llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Aaron Ballman (AaronBallman)

<details>
<summary>Changes</summary>

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 in a 
function declaration which is not a definition.

Partially fixes https://github.com/llvm/llvm-project/issues/21898

---

Patch is 25.28 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/137234.diff


7 Files Affected:

- (modified) clang/docs/ReleaseNotes.rst (+5) 
- (modified) clang/include/clang/Basic/DiagnosticGroups.td (+2-1) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3) 
- (modified) clang/include/clang/Basic/IdentifierTable.h (+8-3) 
- (modified) clang/lib/Basic/IdentifierTable.cpp (+27) 
- (modified) clang/lib/Sema/SemaDecl.cpp (+31) 
- (added) clang/test/Sema/c++-keyword-in-c.c (+213) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d1f24fb23d44d..453bdc9870bdb 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
 ^^^^^^^^^^^^^^^^^^^
@@ -416,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/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..74432ea34bd57 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) {
@@ -10416,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()));
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 {{exp...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/137234
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to