Author: Aaron Ballman
Date: 2025-05-02T07:20:02-04:00
New Revision: 85c810060e1a2a90dc4ec184b4c4275db17ef28f

URL: 
https://github.com/llvm/llvm-project/commit/85c810060e1a2a90dc4ec184b4c4275db17ef28f
DIFF: 
https://github.com/llvm/llvm-project/commit/85c810060e1a2a90dc4ec184b4c4275db17ef28f.diff

LOG: [C] Diagnose use of C++ keywords in C (#137234)

This adds a new diagnostic group, -Wc++-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.

Fixes https://github.com/llvm/llvm-project/issues/21898

Added: 
    clang/test/Sema/c++-keyword-in-c.c
    clang/test/Sema/c++-keyword-in-objc.m

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/DiagnosticGroups.td
    clang/include/clang/Basic/DiagnosticLexKinds.td
    clang/include/clang/Basic/IdentifierTable.h
    clang/include/clang/Basic/TokenKinds.def
    clang/lib/Basic/IdentifierTable.cpp
    clang/lib/Lex/Preprocessor.cpp
    clang/lib/Sema/SemaDecl.cpp
    clang/test/OpenMP/assumes_messages.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 95e0574562a2d..2c65fc4667562 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -154,6 +154,8 @@ 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 ``-Wc++-keyword``, grouped under ``-Wc++-compat``, which diagnoses when
+  a C++ keyword is used as an identifier in C. (#GH21898)
 - Added ``-Wc++-hidden-decl``, grouped under ``-Wc++-compat``, which diagnoses
   use of tag types which are visible in C but not visible in C++ due to scoping
   rules. e.g.,
@@ -482,6 +484,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 4f235fecf4cf9..58439553d41c9 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -157,6 +157,7 @@ def C99Compat : DiagGroup<"c99-compat">;
 def C23Compat : DiagGroup<"c23-compat">;
 def : DiagGroup<"c2x-compat", [C23Compat]>;
 
+def CppKeywordInC : DiagGroup<"c++-keyword">;
 def DuplicateDeclSpecifier : DiagGroup<"duplicate-decl-specifier">;
 def InitStringTooLongMissingNonString :
   DiagGroup<"unterminated-string-initialization">;
@@ -178,9 +179,9 @@ def ImplicitIntToEnumCast : 
DiagGroup<"implicit-int-enum-cast",
                                       [ImplicitEnumEnumCast]>;
 def TentativeDefnCompat : DiagGroup<"tentative-definition-compat">;
 def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast, DefaultConstInit,
-                                        ImplicitIntToEnumCast, HiddenCppDecl,
-                                        InitStringTooLongForCpp,
-                                        TentativeDefnCompat,
+                                        ImplicitIntToEnumCast, CppKeywordInC,
+                                        HiddenCppDecl, TentativeDefnCompat,
+                                        InitStringTooLongForCpp,               
                         
                                         DuplicateDeclSpecifier]>;
 
 def ExternCCompat : DiagGroup<"extern-c-compat">;

diff  --git a/clang/include/clang/Basic/DiagnosticLexKinds.td 
b/clang/include/clang/Basic/DiagnosticLexKinds.td
index f29edfa835d42..a3c32107596f0 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -421,6 +421,9 @@ def warn_pp_macro_is_reserved_attribute_id : Warning<
 def warn_pp_objc_macro_redef_ignored : Warning<
   "ignoring redefinition of Objective-C qualifier macro">,
   InGroup<DiagGroup<"objc-macro-redefinition">>;
+def warn_pp_identifier_is_cpp_keyword : Warning<
+  "identifier %0 conflicts with a C++ keyword">,
+  InGroup<CppKeywordInC>, DefaultIgnore;
 
 def pp_invalid_string_literal : Warning<
   "invalid string literal, ignoring final '\\'">;

diff  --git a/clang/include/clang/Basic/IdentifierTable.h 
b/clang/include/clang/Basic/IdentifierTable.h
index 1275b056227b5..54540193cfcc0 100644
--- a/clang/include/clang/Basic/IdentifierTable.h
+++ b/clang/include/clang/Basic/IdentifierTable.h
@@ -195,7 +195,11 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
   LLVM_PREFERRED_TYPE(bool)
   unsigned IsFinal : 1;
 
-  // 22 bits left in a 64-bit word.
+  // True if this identifier would be a keyword in C++ mode.
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned IsKeywordInCpp : 1;
+
+  // 21 bits left in a 64-bit word.
 
   // Managed by the language front-end.
   void *FETokenInfo = nullptr;
@@ -212,7 +216,7 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
         IsFromAST(false), ChangedAfterLoad(false), FEChangedAfterLoad(false),
         RevertedTokenID(false), OutOfDate(false), IsModulesImport(false),
         IsMangledOpenMPVariantName(false), IsDeprecatedMacro(false),
-        IsRestrictExpansion(false), IsFinal(false) {}
+        IsRestrictExpansion(false), IsFinal(false), IsKeywordInCpp(false) {}
 
 public:
   IdentifierInfo(const IdentifierInfo &) = delete;
@@ -444,6 +448,10 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
   }
   bool isCPlusPlusOperatorKeyword() const { return IsCPPOperatorKeyword; }
 
+  /// Return true if this identifier would be a keyword in C++ mode.
+  bool IsKeywordInCPlusPlus() const { return IsKeywordInCpp; }
+  void setIsKeywordInCPlusPlus(bool Val = true) { IsKeywordInCpp = Val; }
+
   /// Return true if this token is a keyword in the specified language.
   bool isKeyword(const LangOptions &LangOpts) const;
 
@@ -462,6 +470,7 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
   /// If this returns false, we know that HandleIdentifier will not affect
   /// the token.
   bool isHandleIdentifierCase() const { return NeedsHandleIdentifier; }
+  void setHandleIdentifierCase(bool Val = true) { NeedsHandleIdentifier = Val; 
}
 
   /// Return true if the identifier in its current state was loaded
   /// from an AST file.

diff  --git a/clang/include/clang/Basic/TokenKinds.def 
b/clang/include/clang/Basic/TokenKinds.def
index 868e851342eb8..fb53d88deea4a 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -1042,6 +1042,7 @@ ANNOTATION(embed)
 #undef TYPE_TRAIT_2
 #undef TYPE_TRAIT_1
 #undef TYPE_TRAIT
+#undef MODULES_KEYWORD
 #undef CXX20_KEYWORD
 #undef CXX11_KEYWORD
 #undef KEYWORD

diff  --git a/clang/lib/Basic/IdentifierTable.cpp 
b/clang/lib/Basic/IdentifierTable.cpp
index 16151c94464f9..cc472c9044c82 100644
--- a/clang/lib/Basic/IdentifierTable.cpp
+++ b/clang/lib/Basic/IdentifierTable.cpp
@@ -250,6 +250,32 @@ static KeywordStatus getKeywordStatus(const LangOptions 
&LangOpts,
   return CurStatus;
 }
 
+static bool IsKeywordInCpp(unsigned Flags) {
+  while (Flags != 0) {
+    unsigned CurFlag = Flags & ~(Flags - 1);
+    Flags = Flags & ~CurFlag;
+    switch (static_cast<TokenKey>(CurFlag)) {
+    case KEYCXX:
+    case KEYCXX11:
+    case KEYCXX20:
+    case BOOLSUPPORT:
+    case WCHARSUPPORT:
+    case CHAR8SUPPORT:
+      return true;
+    default:
+      break; // Go to the next flag, try again.
+    }
+  }
+  return false;
+}
+
+static void MarkIdentifierAsKeywordInCpp(IdentifierTable &Table,
+                                         StringRef Name) {
+  IdentifierInfo &II = Table.get(Name, tok::identifier);
+  II.setIsKeywordInCPlusPlus();
+  II.setHandleIdentifierCase();
+}
+
 /// AddKeyword - This method is used to associate a token ID with specific
 /// identifiers because they are language keywords.  This causes the lexer to
 /// automatically map matching identifiers to specialized token codes.
@@ -258,8 +284,18 @@ static void AddKeyword(StringRef Keyword,
                        const LangOptions &LangOpts, IdentifierTable &Table) {
   KeywordStatus AddResult = getKeywordStatus(LangOpts, Flags);
 
-  // Don't add this keyword if disabled in this language.
-  if (AddResult == KS_Disabled) return;
+  // Don't add this keyword if disabled in this language and isn't otherwise
+  // special.
+  if (AddResult == KS_Disabled) {
+    // We do not consider any identifiers to be C++ keywords when in
+    // Objective-C because @ effectively introduces a custom grammar where C++
+    // keywords can be used (and similar for selectors). We could enable this
+    // for Objective-C, but it would require more logic to ensure we do not
+    // issue compatibility diagnostics in these cases.
+    if (!LangOpts.ObjC && IsKeywordInCpp(Flags))
+      MarkIdentifierAsKeywordInCpp(Table, Keyword);
+    return;
+  }
 
   IdentifierInfo &Info =
       Table.get(Keyword, AddResult == KS_Future ? tok::identifier : TokenCode);
@@ -304,9 +340,11 @@ void IdentifierTable::AddKeywords(const LangOptions 
&LangOpts) {
 #define ALIAS(NAME, TOK, FLAGS) \
   AddKeyword(StringRef(NAME), tok::kw_ ## TOK,  \
              FLAGS, LangOpts, *this);
-#define CXX_KEYWORD_OPERATOR(NAME, ALIAS) \
-  if (LangOpts.CXXOperatorNames)          \
-    AddCXXOperatorKeyword(StringRef(#NAME), tok::ALIAS, *this);
+#define CXX_KEYWORD_OPERATOR(NAME, ALIAS)                                      
\
+  if (LangOpts.CXXOperatorNames)                                               
\
+    AddCXXOperatorKeyword(StringRef(#NAME), tok::ALIAS, *this);                
\
+  else                                                                         
\
+    MarkIdentifierAsKeywordInCpp(*this, StringRef(#NAME));
 #define OBJC_AT_KEYWORD(NAME)  \
   if (LangOpts.ObjC)           \
     AddObjCKeyword(StringRef(#NAME), tok::objc_##NAME, *this);

diff  --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
index 4c050bf1f5bb2..9ea7b95622c76 100644
--- a/clang/lib/Lex/Preprocessor.cpp
+++ b/clang/lib/Lex/Preprocessor.cpp
@@ -834,6 +834,11 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
     II.setIsFutureCompatKeyword(false);
   }
 
+  // If this identifier would be a keyword in C++, diagnose as a compatibility
+  // issue.
+  if (II.IsKeywordInCPlusPlus() && !DisableMacroExpansion)
+    Diag(Identifier, diag::warn_pp_identifier_is_cpp_keyword) << &II;
+
   // If this is an extension token, diagnose its use.
   // We avoid diagnosing tokens that originate from macro definitions.
   // FIXME: This warning is disabled in cases where it shouldn't be,

diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index e1d7fc6d60f3c..d7421934032cf 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -59,8 +59,10 @@
 #include "clang/Sema/SemaWasm.h"
 #include "clang/Sema/Template.h"
 #include "llvm/ADT/STLForwardCompat.h"
+#include "llvm/ADT/SmallPtrSet.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>
@@ -10424,6 +10426,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 (!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/OpenMP/assumes_messages.c 
b/clang/test/OpenMP/assumes_messages.c
index 9bbedf59b77fc..4d63cc2e8aca5 100644
--- a/clang/test/OpenMP/assumes_messages.c
+++ b/clang/test/OpenMP/assumes_messages.c
@@ -59,8 +59,14 @@
 #pragma omp begin assumes ext // expected-warning {{valid begin assumes 
clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 
'no_openmp_routines', 'no_openmp_constructs', 'no_parallelism'; token will be 
ignored}}
 #pragma omp end assumes
 
-#pragma omp assumes ext_123(not allowed) // expected-warning {{'ext_123' 
clause should not be followed by arguments; tokens will be ignored}} 
expected-note {{the ignored tokens spans until here}}
-#pragma omp begin assumes ext_123(not allowed) // expected-warning {{'ext_123' 
clause should not be followed by arguments; tokens will be ignored}} 
expected-note {{the ignored tokens spans until here}}
+// FIXME: We should be getting an expected note about where the span of ignored
+// tokens ends. However, error recovery ends up lexing the 'not' token,
+// emitting a (silenced) diagnostic about use of a C++ keyword in C, and the
+// note gets associated with *that* (silenced) diagnostic. This is an existing
+// issue that also happens with error recovery of reserved identifiers or
+// extension tokens, but is unfortunate nonetheless.
+#pragma omp assumes ext_123(not allowed) // expected-warning {{'ext_123' 
clause should not be followed by arguments; tokens will be ignored}}
+#pragma omp begin assumes ext_123(not allowed) // expected-warning {{'ext_123' 
clause should not be followed by arguments; tokens will be ignored}}
 #pragma omp end assumes
 
 #pragma omp end assumes // expected-error {{'#pragma omp end assumes' with no 
matching '#pragma omp begin assumes'}}

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..f417469441114
--- /dev/null
+++ b/clang/test/Sema/c++-keyword-in-c.c
@@ -0,0 +1,239 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wc++-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;
+int override;
+int final;
+
+// 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
+
+// Check alternative operator names.
+int and;      // expected-warning {{identifier 'and' conflicts with a C++ 
keyword}} \
+                 cxx-error {{expected unqualified-id}}
+int and_eq;   // expected-warning {{identifier 'and_eq' conflicts with a C++ 
keyword}} \
+                 cxx-error {{expected unqualified-id}}
+int bitand;   // expected-warning {{identifier 'bitand' conflicts with a C++ 
keyword}} \
+                 cxx-error {{expected unqualified-id}}
+int bitor;    // expected-warning {{identifier 'bitor' conflicts with a C++ 
keyword}} \
+                 cxx-error {{expected unqualified-id}}
+int compl;    // expected-warning {{identifier 'compl' conflicts with a C++ 
keyword}} \
+                 cxx-error {{expected a class name after '~' to name a 
destructor}}
+int not;      // expected-warning {{identifier 'not' conflicts with a C++ 
keyword}} \
+                 cxx-error {{expected unqualified-id}}
+int not_eq;   // expected-warning {{identifier 'not_eq' conflicts with a C++ 
keyword}} \
+                 cxx-error {{expected unqualified-id}}
+int or;       // expected-warning {{identifier 'or' conflicts with a C++ 
keyword}} \
+                 cxx-error {{expected unqualified-id}}
+int or_eq;    // expected-warning {{identifier 'or_eq' conflicts with a C++ 
keyword}} \
+                 cxx-error {{expected unqualified-id}}
+int xor;      // expected-warning {{identifier 'xor' conflicts with a C++ 
keyword}} \
+                 cxx-error {{expected unqualified-id}}
+int xor_eq;   // expected-warning {{identifier 'xor_eq' conflicts with a C++ 
keyword}} \
+                 cxx-error {{expected unqualified-id}}

diff  --git a/clang/test/Sema/c++-keyword-in-objc.m 
b/clang/test/Sema/c++-keyword-in-objc.m
new file mode 100644
index 0000000000000..2a6c366991ba5
--- /dev/null
+++ b/clang/test/Sema/c++-keyword-in-objc.m
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wc++-keyword %s
+// expected-no-diagnostics
+
+// Show that we do not diagnose any C++ keywords in Objective-C.
+
+@class Foo; // Okay, Objective-C @ keyword, not a regular identifier
+
+// FIXME: it would be nice to diagnose this, but it is intentionally allowed
+// due to @ and selectors allowing C++ keywords in ways that are supposed to be
+// contextually compatible with C++.
+int class = 12;
+


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to