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