diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index d762bcd789bf5..d770d513f108a 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2099,6 +2099,12 @@ static void handleStandardNoReturnAttr(Sema &S, Decl *D, const ParsedAttr &A) { S.getSourceManager().isInSystemMacro(A.getLoc()))) S.Diag(A.getLoc(), diag::warn_deprecated_noreturn_spelling) << A.getRange(); + // Check for duplicate attribute - warn if already applied. + if (D->hasAttr()) { + S.Diag(A.getLoc(), diag::warn_duplicate_attribute_exact) << A; + return; + } + D->addAttr(::new (S.Context) CXX11NoReturnAttr(S.Context, A)); } @@ -2230,6 +2236,11 @@ static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!S.getLangOpts().CPlusPlus17 && IsCXX17Attr) S.Diag(AL.getLoc(), diag::ext_cxx17_attr) << AL; + // Check for duplicate attribute - warn if already applied. + if (D->hasAttr()) { + S.Diag(AL.getLoc(), diag::warn_duplicate_attribute_exact) << AL; + return; + } D->addAttr(::new (S.Context) UnusedAttr(S.Context, AL)); } diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index 27fd5563cc40e..c440ec3fe7735 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -16,6 +16,7 @@ #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/ParsedAttr.h" #include "clang/Sema/ScopeInfo.h" +#include "llvm/ADT/SmallPtrSet.h" #include using namespace clang; @@ -740,7 +741,19 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A, void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs, SmallVectorImpl &OutAttrs) { + // Track standard attributes without arguments to detect duplicates. + llvm::SmallPtrSet SeenStdAttrs; + for (const ParsedAttr &AL : InAttrs) { + // Check for duplicate standard attributes without arguments. + if (AL.isStandardAttributeSyntax() && !AL.getScopeName() && + AL.getNumArgs() == 0 && AL.getAttrName()) { + if (!SeenStdAttrs.insert(AL.getAttrName()).second) { + Diag(AL.getLoc(), diag::warn_duplicate_attribute_exact) << AL; + continue; + } + } + if (const Attr *A = ProcessStmtAttribute(*this, S, AL, InAttrs.Range)) OutAttrs.push_back(A); } diff --git a/clang/test/SemaCXX/cxx17-duplicate-attrs.cpp b/clang/test/SemaCXX/cxx17-duplicate-attrs.cpp new file mode 100644 index 0000000000000..67bea30e30f35 --- /dev/null +++ b/clang/test/SemaCXX/cxx17-duplicate-attrs.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s + +int foo([[maybe_unused, maybe_unused]] int a) { // expected-warning {{attribute 'maybe_unused' is already applied}} + return 1; +} + +[[noreturn, noreturn]] void g() { // expected-warning {{attribute 'noreturn' is already applied}} + __builtin_unreachable(); +} + +int h(int n) { + switch (n) { + case 1: + [[fallthrough, fallthrough]]; // expected-warning {{attribute 'fallthrough' is already applied}} + case 2: + return 1; + } + return 0; +} \ No newline at end of file