[clang] [C] Diagnose use of C++ keywords in C (PR #137234)
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 24 11:57:09 PDT 2025
https://github.com/AaronBallman updated https://github.com/llvm/llvm-project/pull/137234
>From 56a3f3cd282e9bd5ef9014e4125380e0d9685121 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Thu, 24 Apr 2025 14:17:42 -0400
Subject: [PATCH 1/3] [C] Diagnose use of C++ keywords in C
This adds a new diagnostic group, -Widentifier-is-c++-keyword, which is
off by default and grouped under -Wc++-compat. The diagnostic catches
use of C++ keywords in C code.
This change additionally fixes an issue with -Wreserved-identifier not
diagnosing use of reserved identifiers in function parameter lists.
Partially fixes #21898
---
clang/docs/ReleaseNotes.rst | 3 +
clang/include/clang/Basic/DiagnosticGroups.td | 3 +-
.../clang/Basic/DiagnosticSemaKinds.td | 3 +
clang/include/clang/Basic/IdentifierTable.h | 11 +-
clang/lib/Basic/IdentifierTable.cpp | 27 +++
clang/lib/Sema/SemaDecl.cpp | 23 ++
clang/test/Sema/c++-keyword-in-c.c | 213 ++++++++++++++++++
7 files changed, 279 insertions(+), 4 deletions(-)
create mode 100644 clang/test/Sema/c++-keyword-in-c.c
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d1f24fb23d44d..57b604335fcdd 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -143,6 +143,9 @@ C Language Changes
- Added ``-Wimplicit-void-ptr-cast``, grouped under ``-Wc++-compat``, which
diagnoses implicit conversion from ``void *`` to another pointer type as
being incompatible with C++. (#GH17792)
+- Added ``-Widentifier-is-c++-keyword``, grouped under ``-Wc++-compat``, which
+ diagnoses when a C++ keyword is used as an identifier in C. Partially
+ addresses #GH21898.
C2y Feature Support
^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 6441b8049ed8d..84e590a857398 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -155,8 +155,9 @@ def C99Compat : DiagGroup<"c99-compat">;
def C23Compat : DiagGroup<"c23-compat">;
def : DiagGroup<"c2x-compat", [C23Compat]>;
+def CppKeywordInC : DiagGroup<"identifier-is-c++-keyword">;
def ImplicitVoidPtrCast : DiagGroup<"implicit-void-ptr-cast">;
-def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast]>;
+def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast, CppKeywordInC]>;
def ExternCCompat : DiagGroup<"extern-c-compat">;
def KeywordCompat : DiagGroup<"keyword-compat">;
def GNUCaseRange : DiagGroup<"gnu-case-range">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8ff170520aafe..50a960313349a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -496,6 +496,9 @@ def warn_unused_lambda_capture: Warning<"lambda capture %0 is not "
"%select{used|required to be captured for this use}1">,
InGroup<UnusedLambdaCapture>, DefaultIgnore;
+def warn_identifier_is_cpp_keyword : Warning<
+ "identifier %0 conflicts with a C++ keyword">,
+ InGroup<CppKeywordInC>, DefaultIgnore;
def warn_reserved_extern_symbol: Warning<
"identifier %0 is reserved because %select{"
"<ERROR>|" // ReservedIdentifierStatus::NotReserved
diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h
index 1275b056227b5..05c989c1b07d9 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -444,13 +444,18 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
}
bool isCPlusPlusOperatorKeyword() const { return IsCPPOperatorKeyword; }
- /// Return true if this token is a keyword in the specified language.
+ /// Return true if this identifier uses a keyword token and is a keyword in
+ /// the specified language.
bool isKeyword(const LangOptions &LangOpts) const;
- /// Return true if this token is a C++ keyword in the specified
- /// language.
+ /// Return true if this identifier uses a keyword token and is a C++ keyword
+ /// in the specified language.
bool isCPlusPlusKeyword(const LangOptions &LangOpts) const;
+ /// Returns true if the name of this identifier matches a keyword given the
+ /// specified language options.
+ bool isNameKeyword(const LangOptions &LangOpts) const;
+
/// Get and set FETokenInfo. The language front-end is allowed to associate
/// arbitrary metadata with this token.
void *getFETokenInfo() const { return FETokenInfo; }
diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp
index 16151c94464f9..412f0af40861a 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -343,6 +343,14 @@ static KeywordStatus getTokenKwStatus(const LangOptions &LangOpts,
}
}
+static KeywordStatus getNameKwStatus(const LangOptions &LangOpts,
+ StringRef Name) {
+ return llvm::StringSwitch<KeywordStatus>(Name)
+#define KEYWORD(NAME, FLAGS) .Case(#NAME, getKeywordStatus(LangOpts, FLAGS))
+#include "clang/Basic/TokenKinds.def"
+ .Default(KS_Disabled);
+}
+
/// Returns true if the identifier represents a keyword in the
/// specified language.
bool IdentifierInfo::isKeyword(const LangOptions &LangOpts) const {
@@ -355,6 +363,25 @@ bool IdentifierInfo::isKeyword(const LangOptions &LangOpts) const {
}
}
+bool IdentifierInfo::isNameKeyword(const LangOptions &LangOpts) const {
+ // This differs from IdentifierInfo::isCPlusPlusKeyword(). That function
+ // tests if the identifier is a keyword token in C++ mode and then isn't a
+ // keyword token in C modes. In our case, if it was a keyword, we wouldn't
+ // have gotten the identifier for it anyway, so that function will always
+ // return false for us. Instead, check against the token definitions directly.
+ //
+ // FIXME: It would be nice to handle things like char8_t and wchar_t here as
+ // well, but those require looking at the currently selected language options
+ // which would make this interface confusing with isCPlusPlusKeyword.
+ switch (getNameKwStatus(LangOpts, getName())) {
+ case KS_Enabled:
+ case KS_Extension:
+ return true;
+ default:
+ return false;
+ }
+}
+
/// Returns true if the identifier represents a C++ keyword in the
/// specified language.
bool IdentifierInfo::isCPlusPlusKeyword(const LangOptions &LangOpts) const {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d28a2107d58a9..9bb96dbdc1670 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -61,6 +61,7 @@
#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/TargetParser/Triple.h"
#include <algorithm>
@@ -6107,6 +6108,23 @@ static bool isFromSystemHeader(SourceManager &SM, const Decl *D) {
SM.isInSystemMacro(D->getLocation());
}
+static bool isKeywordInCPlusPlus(const Sema &S, const IdentifierInfo* II) {
+ if (!II)
+ return false;
+
+ // Clear all the C language options, set all the C++ language options, but
+ // otherwise leave all the defaults alone. This isn't ideal because some
+ // feature flags are language-specific, but this is a reasonable
+ // approximation.
+ LangOptions LO = S.getLangOpts();
+ LO.CPlusPlus = LO.CPlusPlus11 = LO.CPlusPlus14 = LO.CPlusPlus17 =
+ LO.CPlusPlus20 = LO.CPlusPlus23 = LO.CPlusPlus26 = 1;
+ LO.C99 = LO.C11 = LO.C17 = LO.C23 = LO.C2y = 0;
+ LO.Char8 = 1; // Presume this is always a keyword in C++.
+ LO.WChar = 1; // Presume this is always a keyword in C++.
+ return II->isNameKeyword(LO);
+}
+
void Sema::warnOnReservedIdentifier(const NamedDecl *D) {
// Avoid warning twice on the same identifier, and don't warn on redeclaration
// of system decl.
@@ -6118,6 +6136,10 @@ void Sema::warnOnReservedIdentifier(const NamedDecl *D) {
Diag(D->getLocation(), diag::warn_reserved_extern_symbol)
<< D << static_cast<int>(Status);
}
+ // Diagnose use of C++ keywords in C as being incompatible with C++.
+ if (!getLangOpts().CPlusPlus &&
+ isKeywordInCPlusPlus(*this, D->getIdentifier()))
+ Diag(D->getLocation(), diag::warn_identifier_is_cpp_keyword) << D;
}
Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D) {
@@ -15311,6 +15333,7 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D,
if (D.isInvalidType())
New->setInvalidDecl();
+ warnOnReservedIdentifier(New);
CheckExplicitObjectParameter(*this, New, ExplicitThisLoc);
assert(S->isFunctionPrototypeScope());
diff --git a/clang/test/Sema/c++-keyword-in-c.c b/clang/test/Sema/c++-keyword-in-c.c
new file mode 100644
index 0000000000000..364f115ddcb44
--- /dev/null
+++ b/clang/test/Sema/c++-keyword-in-c.c
@@ -0,0 +1,213 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Widentifier-is-c++-keyword %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wc++-compat %s
+// RUN: %clang_cc1 -fsyntax-only -verify=good %s
+// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ -std=c++2c %s
+// good-no-diagnostics
+
+// 'try', 'this' and 'throw' are not tested as identifiers, but are instead
+// tested as other constructs (otherwise there would be redefinition errors in
+// C).
+int catch; // expected-warning {{identifier 'catch' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int class; // expected-warning {{identifier 'class' conflicts with a C++ keyword}} \
+ cxx-error {{declaration of anonymous class must be a definition}} \
+ cxx-warning {{declaration does not declare anything}}
+int const_cast; // expected-warning {{identifier 'const_cast' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int delete; // expected-warning {{identifier 'delete' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int dynamic_cast; // expected-warning {{identifier 'dynamic_cast' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int explicit; // expected-warning {{identifier 'explicit' conflicts with a C++ keyword}} \
+ cxx-error {{'explicit' can only appear on non-static member functions}} \
+ cxx-warning {{declaration does not declare anything}}
+int export; // expected-warning {{identifier 'export' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int friend; // expected-warning {{identifier 'friend' conflicts with a C++ keyword}} \
+ cxx-error {{'friend' used outside of class}} \
+ cxx-warning {{declaration does not declare anything}}
+int mutable; // expected-warning {{identifier 'mutable' conflicts with a C++ keyword}} \
+ cxx-warning {{declaration does not declare anything}}
+int namespace; // expected-warning {{identifier 'namespace' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int new; // expected-warning {{identifier 'new' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int operator; // expected-warning {{identifier 'operator' conflicts with a C++ keyword}} \
+ cxx-error {{expected a type}}
+int private; // expected-warning {{identifier 'private' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int protected; // expected-warning {{identifier 'protected' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int public; // expected-warning {{identifier 'public' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int reinterpret_cast; // expected-warning {{identifier 'reinterpret_cast' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int static_cast; // expected-warning {{identifier 'static_cast' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int template; // expected-warning {{identifier 'template' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int typename; // expected-warning {{identifier 'typename' conflicts with a C++ keyword}} \
+ cxx-error {{expected a qualified name after 'typename'}}
+int typeid; // expected-warning {{identifier 'typeid' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int using; // expected-warning {{identifier 'using' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int virtual; // expected-warning {{identifier 'virtual' conflicts with a C++ keyword}} \
+ cxx-error {{'virtual' can only appear on non-static member functions}} \
+ cxx-warning {{declaration does not declare anything}}
+int wchar_t; // expected-warning {{identifier 'wchar_t' conflicts with a C++ keyword}} \
+ cxx-error {{cannot combine with previous 'int' declaration specifier}} \
+ cxx-warning {{declaration does not declare anything}}
+int char8_t; // expected-warning {{identifier 'char8_t' conflicts with a C++ keyword}} \
+ cxx-error {{cannot combine with previous 'int' declaration specifier}} \
+ cxx-warning {{declaration does not declare anything}}
+int char16_t; // expected-warning {{identifier 'char16_t' conflicts with a C++ keyword}} \
+ cxx-error {{cannot combine with previous 'int' declaration specifier}} \
+ cxx-warning {{declaration does not declare anything}}
+int char32_t; // expected-warning {{identifier 'char32_t' conflicts with a C++ keyword}} \
+ cxx-error {{cannot combine with previous 'int' declaration specifier}} \
+ cxx-warning {{declaration does not declare anything}}
+int noexcept; // expected-warning {{identifier 'noexcept' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int co_await; // expected-warning {{identifier 'co_await' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int co_return; // expected-warning {{identifier 'co_return' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int co_yield; // expected-warning {{identifier 'co_yield' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int consteval; // expected-warning {{identifier 'consteval' conflicts with a C++ keyword}} \
+ cxx-error {{consteval can only be used in function declarations}}
+int constinit; // expected-warning {{identifier 'constinit' conflicts with a C++ keyword}} \
+ cxx-error {{constinit can only be used in variable declarations}}
+int concept; // expected-warning {{identifier 'concept' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+int requires; // expected-warning {{identifier 'requires' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+
+// Now try the same thing, but as struct members.
+struct S {
+ int catch; // expected-warning {{identifier 'catch' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int class; // expected-warning {{identifier 'class' conflicts with a C++ keyword}} \
+ cxx-error {{declaration of anonymous class must be a definition}} \
+ cxx-warning {{declaration does not declare anything}}
+ int const_cast; // expected-warning {{identifier 'const_cast' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int delete; // expected-warning {{identifier 'delete' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int dynamic_cast; // expected-warning {{identifier 'dynamic_cast' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int explicit; // expected-warning {{identifier 'explicit' conflicts with a C++ keyword}} \
+ cxx-error {{'explicit' can only appear on non-static member functions}} \
+ cxx-warning {{declaration does not declare anything}}
+ int export; // expected-warning {{identifier 'export' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int friend; // expected-warning {{identifier 'friend' conflicts with a C++ keyword}} \
+ cxx-error {{'friend' must appear first in a non-function declaration}}
+ int mutable; // expected-warning {{identifier 'mutable' conflicts with a C++ keyword}} \
+ cxx-warning {{declaration does not declare anything}}
+ int namespace; // expected-warning {{identifier 'namespace' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int new; // expected-warning {{identifier 'new' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int operator; // expected-warning {{identifier 'operator' conflicts with a C++ keyword}} \
+ cxx-error {{expected a type}}
+ int private; // expected-warning {{identifier 'private' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int protected; // expected-warning {{identifier 'protected' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int public; // expected-warning {{identifier 'public' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int reinterpret_cast; // expected-warning {{identifier 'reinterpret_cast' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int static_cast; // expected-warning {{identifier 'static_cast' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int template; // expected-warning {{identifier 'template' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int this; // expected-warning {{identifier 'this' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int throw; // expected-warning {{identifier 'throw' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int try; // expected-warning {{identifier 'try' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int typename; // expected-warning {{identifier 'typename' conflicts with a C++ keyword}} \
+ cxx-error {{expected a qualified name after 'typename'}}
+ int typeid; // expected-warning {{identifier 'typeid' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int using; // expected-warning {{identifier 'using' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int virtual; // expected-warning {{identifier 'virtual' conflicts with a C++ keyword}} \
+ cxx-error {{'virtual' can only appear on non-static member functions}} \
+ cxx-warning {{declaration does not declare anything}}
+ int wchar_t; // expected-warning {{identifier 'wchar_t' conflicts with a C++ keyword}} \
+ cxx-error {{cannot combine with previous 'int' declaration specifier}} \
+ cxx-warning {{declaration does not declare anything}}
+ int char8_t; // expected-warning {{identifier 'char8_t' conflicts with a C++ keyword}} \
+ cxx-error {{cannot combine with previous 'int' declaration specifier}} \
+ cxx-warning {{declaration does not declare anything}}
+ int char16_t; // expected-warning {{identifier 'char16_t' conflicts with a C++ keyword}} \
+ cxx-error {{cannot combine with previous 'int' declaration specifier}} \
+ cxx-warning {{declaration does not declare anything}}
+ int char32_t; // expected-warning {{identifier 'char32_t' conflicts with a C++ keyword}} \
+ cxx-error {{cannot combine with previous 'int' declaration specifier}} \
+ cxx-warning {{declaration does not declare anything}}
+ int noexcept; // expected-warning {{identifier 'noexcept' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int co_await; // expected-warning {{identifier 'co_await' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int co_return; // expected-warning {{identifier 'co_return' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int co_yield; // expected-warning {{identifier 'co_yield' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}}
+ int consteval; // expected-warning {{identifier 'consteval' conflicts with a C++ keyword}} \
+ cxx-error {{consteval can only be used in function declarations}}
+ int constinit; // expected-warning {{identifier 'constinit' conflicts with a C++ keyword}} \
+ cxx-error {{constinit can only be used in variable declarations}}
+ int concept; // expected-warning {{identifier 'concept' conflicts with a C++ keyword}} \
+ cxx-error {{concept declarations may only appear in global or namespace scope}}
+ int requires; // expected-warning {{identifier 'requires' conflicts with a C++ keyword}} \
+ cxx-error {{expected member name or ';' after declaration specifiers}} \
+ cxx-error {{trailing requires clause can only be used when declaring a function}} \
+ cxx-error {{expected expression}}
+};
+
+// Smoke test that we catch a keyword used as an enumerator. If we diagnose
+// one, we'll diagnose them all.
+enum E {
+ throw, // expected-warning {{identifier 'throw' conflicts with a C++ keyword}} \
+ cxx-error {{expected identifier}}
+};
+
+// Smoke test that we catch a keyword used as a tag name.
+struct try { // expected-warning {{identifier 'try' conflicts with a C++ keyword}} \
+ cxx-error {{declaration of anonymous struct must be a definition}} \
+ cxx-warning {{declaration does not declare anything}}
+ int x;
+};
+
+// Smoke test that we catch keyword use in a function name.
+void this(void); // expected-warning {{identifier 'this' conflicts with a C++ keyword}} \
+ cxx-error {{expected unqualified-id}}
+
+// Smoke test that we catch keyword use in function parameters too.
+void func(int private); // expected-warning {{identifier 'private' conflicts with a C++ keyword}} \
+ cxx-error {{invalid parameter name: 'private' is a keyword}}
+
+// These are conditionally a keyword in C++, so they're intentionally not being
+// diagnosed as a keyword.
+int module;
+int import;
+
+// We do not diagnose use of C++ keywords when used as a macro name because
+// that does not conflict with C++ (the macros will be replaced before the
+// keyword is seen by the parser).
+#define this 12
+
+// FIXME: These tests are disabled for C++ because it causes a crash.
+// See GH114815.
+#ifndef __cplusplus
+int decltype; // expected-warning {{identifier 'decltype' conflicts with a C++ keyword}}
+struct T {
+ int decltype; // expected-warning {{identifier 'decltype' conflicts with a C++ keyword}}
+};
+#endif // __cplusplus
>From 3b0732e353d52808d4405ca306b87b85e6dbcd13 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Thu, 24 Apr 2025 14:49:40 -0400
Subject: [PATCH 2/3] Fix the parameter handling
---
clang/docs/ReleaseNotes.rst | 2 ++
clang/lib/Sema/SemaDecl.cpp | 10 +++++++++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 57b604335fcdd..453bdc9870bdb 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -419,6 +419,8 @@ Improvements to Clang's diagnostics
- ``-Winitializer-overrides`` and ``-Wreorder-init-list`` are now grouped under
the ``-Wc99-designator`` diagnostic group, as they also are about the
behavior of the C99 feature as it was introduced into C++20. Fixes #GH47037
+- ``-Wreserved-identifier`` now fires on reserved parameter names in a function
+ declaration which is not a definition.
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 9bb96dbdc1670..74432ea34bd57 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10438,6 +10438,15 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
// Finally, we know we have the right number of parameters, install them.
NewFD->setParams(Params);
+ // If this declarator is a declaration and not a definition, its parameters
+ // will not be pushed onto a scope chain. That means we will not issue any
+ // reserved identifier warnings for the declaration, but we will for the
+ // definition. Handle those here.
+ if (!Params.empty() && !D.isFunctionDefinition()) {
+ for (const ParmVarDecl *PVD : Params)
+ warnOnReservedIdentifier(PVD);
+ }
+
if (D.getDeclSpec().isNoreturnSpecified())
NewFD->addAttr(
C11NoReturnAttr::Create(Context, D.getDeclSpec().getNoreturnSpecLoc()));
@@ -15333,7 +15342,6 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D,
if (D.isInvalidType())
New->setInvalidDecl();
- warnOnReservedIdentifier(New);
CheckExplicitObjectParameter(*this, New, ExplicitThisLoc);
assert(S->isFunctionPrototypeScope());
>From 55350f090fe3af859d0ab8caad4d308b3af56f26 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Thu, 24 Apr 2025 14:56:49 -0400
Subject: [PATCH 3/3] Update comment; NFC
---
clang/lib/Basic/IdentifierTable.cpp | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp
index 412f0af40861a..a8b64364a1745 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -368,11 +368,7 @@ bool IdentifierInfo::isNameKeyword(const LangOptions &LangOpts) const {
// tests if the identifier is a keyword token in C++ mode and then isn't a
// keyword token in C modes. In our case, if it was a keyword, we wouldn't
// have gotten the identifier for it anyway, so that function will always
- // return false for us. Instead, check against the token definitions directly.
- //
- // FIXME: It would be nice to handle things like char8_t and wchar_t here as
- // well, but those require looking at the currently selected language options
- // which would make this interface confusing with isCPlusPlusKeyword.
+ // return false for us. Instead, check against the identifier name directly.
switch (getNameKwStatus(LangOpts, getName())) {
case KS_Enabled:
case KS_Extension:
More information about the cfe-commits
mailing list