[clang] [clang][Sema] Merge GNU diagnostic attributes by semantic spelling (PR #214523)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 6 09:36:12 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Matthias Görgens (matthiasgoergens)
<details>
<summary>Changes</summary>
Fixes llvm/llvm-project#<!-- -->212467.
## Cause
`mergeErrorAttr` used `AttributeCommonInfo::getNormalizedFullName()` to
decide whether a new diagnostic attribute was an error or a warning.
Contrary to the helper's assertion, a C++11 namespaced GNU spelling produces
`gnu::warning` or `gnu::error`, rather than the unqualified string.
## Trigger (how the bug manifests)
Reproduced on current main (2026-08-03): an assertions build from the
unmodified base exits 134 (assertion abort in `SemaDeclAttr.cpp`) when two
`[[gnu::warning]]` attributes are placed on one declaration. Even a release
Clang rejects identical C23 attributes as incompatible, so the bug also
changes non-asserting-build semantics — not merely a debug-build crash.
## Fix
Construct the generated `ErrorAttr` first and use its semantic `isError()`
and `isWarning()` predicates. These deliberately group GNU, C++11, and C23
spellings of the same attribute while retaining the existing same-kind,
differing-message, and error-versus-warning merge behaviour. A second commit
adds a C23 regression test.
## Verification
- The unmodified compiler exits 134 at the reported assertion; the patched
compiler accepts the same declaration.
- The C++ regression covers duplicate warning and error attributes,
differing messages, an incompatible error/warning pair, and both orders of
mixed GNU and C++11 syntax. The added C23 regression covers both
diagnostic kinds and both mixed GNU/C23 orders.
- Both focused tests pass in a fresh current-main assertions build. The
patched compiler also accepts the release-build C23 negative control
without diagnostics.
- All 3,059 tests in `clang/test/Sema`, `clang/test/SemaCXX`, and
`clang/test/Frontend` pass, apart from the suite's supported skips and
expected failure.
- `git diff --check` passes.
- Independent adversarial review checked all six generated spellings,
incompatible attributes, redeclarations, inheritance, allocation lifetime,
and diagnostic preservation, and found no blocker.
---
Full diff: https://github.com/llvm/llvm-project/pull/214523.diff
3 Files Affected:
- (modified) clang/lib/Sema/SemaDeclAttr.cpp (+4-6)
- (added) clang/test/Sema/gh212467.c (+16)
- (added) clang/test/SemaCXX/gh212467.cpp (+23)
``````````diff
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 0645f99492433..09ee3988baf3e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -4056,12 +4056,10 @@ static void handleInitPriorityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
ErrorAttr *Sema::mergeErrorAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef NewUserDiagnostic) {
+ auto *NewAttr = ::new (Context) ErrorAttr(Context, CI, NewUserDiagnostic);
if (const auto *EA = D->getAttr<ErrorAttr>()) {
- std::string NewAttr = CI.getNormalizedFullName();
- assert((NewAttr == "error" || NewAttr == "warning") &&
- "unexpected normalized full name");
- bool Match = (EA->isError() && NewAttr == "error") ||
- (EA->isWarning() && NewAttr == "warning");
+ bool Match = (EA->isError() && NewAttr->isError()) ||
+ (EA->isWarning() && NewAttr->isWarning());
if (!Match) {
Diag(EA->getLocation(), diag::err_attributes_are_not_compatible)
<< CI << EA
@@ -4076,7 +4074,7 @@ ErrorAttr *Sema::mergeErrorAttr(Decl *D, const AttributeCommonInfo &CI,
}
D->dropAttr<ErrorAttr>();
}
- return ::new (Context) ErrorAttr(Context, CI, NewUserDiagnostic);
+ return NewAttr;
}
FormatAttr *Sema::mergeFormatAttr(Decl *D, const AttributeCommonInfo &CI,
diff --git a/clang/test/Sema/gh212467.c b/clang/test/Sema/gh212467.c
new file mode 100644
index 0000000000000..c57d87f13a2db
--- /dev/null
+++ b/clang/test/Sema/gh212467.c
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c23 -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+[[gnu::warning("same")]]
+[[gnu::warning("same")]]
+void same_warning(void);
+
+[[gnu::error("same")]]
+[[gnu::error("same")]]
+void same_error(void);
+
+[[gnu::warning("same")]]
+__attribute__((warning("same"))) void mixed_warning(void);
+
+__attribute__((error("same")))
+[[gnu::error("same")]] void reverse_mixed_error(void);
diff --git a/clang/test/SemaCXX/gh212467.cpp b/clang/test/SemaCXX/gh212467.cpp
new file mode 100644
index 0000000000000..38c760f55a084
--- /dev/null
+++ b/clang/test/SemaCXX/gh212467.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
+
+[[gnu::warning("same")]]
+[[gnu::warning("same")]]
+void same_warning();
+
+[[gnu::warning("one")]] // expected-note {{previous attribute is here}}
+[[gnu::warning("two")]] // expected-warning {{attribute 'gnu::warning' is already applied with different arguments}}
+void different_warning();
+
+[[gnu::error("same")]]
+[[gnu::error("same")]]
+void same_error();
+
+[[gnu::error("one")]] // expected-error {{'gnu::warning' and 'gnu::error' attributes are not compatible}}
+[[gnu::warning("two")]] // expected-note {{conflicting attribute is here}}
+void conflicting();
+
+[[gnu::warning("same")]]
+__attribute__((warning("same"))) void mixed_syntax();
+
+__attribute__((warning("same")))
+[[gnu::warning("same")]] void reverse_mixed_syntax();
``````````
</details>
https://github.com/llvm/llvm-project/pull/214523
More information about the cfe-commits
mailing list