Index: clang/include/clang/AST/Decl.h =================================================================== --- clang/include/clang/AST/Decl.h +++ clang/include/clang/AST/Decl.h @@ -356,6 +356,10 @@ /// a C++ class. bool isCXXInstanceMember() const; + /// Determine if the declaration obeys the reserved identifier rules of the + /// given language. + bool isReserved(const LangOptions &) const; + /// Determine what kind of linkage this entity has. /// /// This is not the linkage as defined by the standard or the codegen notion Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -786,6 +786,9 @@ def DuplicateArgDecl : DiagGroup<"duplicate-method-arg">; def SignedEnumBitfield : DiagGroup<"signed-enum-bitfield">; + +def ReservedIdentifier : DiagGroup<"reserved-identifier", [ReservedIdAsMacro]>; + // Unreachable code warning groups. // // The goal is make -Wunreachable-code on by default, in -Wall, or at Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -378,6 +378,10 @@ "%select{used|required to be captured for this use}1">, InGroup, DefaultIgnore; +def warn_reserved_identifier: Warning< + "%0 is a reserved identifier">, + InGroup, DefaultIgnore; + def warn_parameter_size: Warning< "%0 is a large (%1 bytes) pass-by-value argument; " "pass it by reference instead ?">, InGroup; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -2575,6 +2575,8 @@ SourceLocation Less, SourceLocation Greater); + void warnOnReservedIdentifier(const NamedDecl *D); + Decl *ActOnDeclarator(Scope *S, Declarator &D); NamedDecl *HandleDeclarator(Scope *S, Declarator &D, Index: clang/lib/AST/Decl.cpp =================================================================== --- clang/lib/AST/Decl.cpp +++ clang/lib/AST/Decl.cpp @@ -1078,6 +1078,40 @@ return L == getCachedLinkage(); } +bool NamedDecl::isReserved(const LangOptions &LangOpts) const { + IdentifierInfo *II = getIdentifier(); + if (!II) + return false; + + StringRef Name = II->getName(); + // '_' is a reserved identifier, but it's use is so common (e.g. to store + // ignored values) that we don't warn on it. + if (Name.size() <= 1) + return false; + + // [lex.name] p3 + if (Name[0] == '_') { + + // Walk up the lexical parents to determine if we're at TU level or not. + const DeclContext * DC = getLexicalDeclContext(); + while(DC->isTransparentContext()) + DC = DC->getLexicalParent(); + if (isa(DC)) + return true; + + // Each name that begins with an underscore followed by an uppercase letter + // or another underscore is reserved. + if (Name[1] == '_' || ('A' <= Name[1] && Name[1] <= 'Z')) + return true; + } + + // Each name that contains a double underscore (__) is reserved. + if (LangOpts.CPlusPlus && Name.contains("__")) + return true; + + return false; +} + ObjCStringFormatFamily NamedDecl::getObjCFStringFormattingFamily() const { StringRef name = getName(); if (name.empty()) return SFF_None; Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -5554,6 +5554,16 @@ return false; } +void Sema::warnOnReservedIdentifier(const NamedDecl *D) { + // Avoid warning twice on the same identifier, and don't warn on redeclaration + // of system decl + if (D->getPreviousDecl()) + return; + if (!Context.getSourceManager().isInSystemHeader(D->getLocation()) && + D->isReserved(getLangOpts())) + Diag(D->getLocation(), diag::warn_reserved_identifier) << D; +} + Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D) { D.setFunctionDefinitionKind(FunctionDefinitionKind::Declaration); Decl *Dcl = HandleDeclarator(S, D, MultiTemplateParamsArg()); @@ -5899,6 +5909,7 @@ if (isInOpenMPDeclareTargetContext()) checkDeclIsAllowedInOpenMPTarget(nullptr, New); + warnOnReservedIdentifier(New); return New; } @@ -13654,6 +13665,8 @@ if (getLangOpts().OpenCL) deduceOpenCLAddressSpace(New); + warnOnReservedIdentifier(New); + return New; } @@ -16308,6 +16321,7 @@ } else if (SkipBody && SkipBody->ShouldSkip) { return SkipBody->Previous; } else { + warnOnReservedIdentifier(New); return New; } } @@ -17120,6 +17134,8 @@ i != end; ++i) { FieldDecl *FD = cast(*i); + warnOnReservedIdentifier(FD); + // Get the type for the field. const Type *FDTy = FD->getType().getTypePtr(); @@ -17845,6 +17861,8 @@ ActOnDocumentableDecl(New); + warnOnReservedIdentifier(New); + return New; } Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -10027,8 +10027,9 @@ DeclContext *LookupDC = dyn_cast(D); if (DeclaratorDecl *DD = dyn_cast(D)) { - for (unsigned i = 0; i < DD->getNumTemplateParameterLists(); ++i) + for (unsigned i = 0; i < DD->getNumTemplateParameterLists(); ++i) { ParameterLists.push_back(DD->getTemplateParameterList(i)); + } if (FunctionDecl *FD = dyn_cast(D)) { if (FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate()) @@ -11000,6 +11001,9 @@ // for the namespace has the declarations that showed up in that particular // namespace definition. PushDeclContext(NamespcScope, Namespc); + + warnOnReservedIdentifier(Namespc); + return Namespc; } @@ -12674,6 +12678,9 @@ PushOnScopeChains(NewND, S); ActOnDocumentableDecl(NewND); + + warnOnReservedIdentifier(NewND); + return NewND; } Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -541,6 +541,10 @@ return SubStmt; } + if (!Context.getSourceManager().isInSystemHeader(IdentLoc) && + TheDecl->isReserved(getLangOpts())) + Diag(IdentLoc, diag::warn_reserved_identifier) << TheDecl; + // Otherwise, things are good. Fill in the declaration and return it. LabelStmt *LS = new (Context) LabelStmt(IdentLoc, TheDecl, SubStmt); TheDecl->setStmt(LS); Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -1677,6 +1677,9 @@ if (ExportLoc.isValid()) Diag(ExportLoc, diag::warn_template_export_unsupported); + for (NamedDecl *P : Params) + warnOnReservedIdentifier(P); + return TemplateParameterList::Create( Context, TemplateLoc, LAngleLoc, llvm::makeArrayRef(Params.data(), Params.size()), @@ -8628,6 +8631,7 @@ } ActOnDocumentableDecl(NewDecl); + warnOnReservedIdentifier(NewDecl); PushOnScopeChains(NewDecl, S); return NewDecl; } Index: clang/test/Sema/reserved-identifier.c =================================================================== --- /dev/null +++ clang/test/Sema/reserved-identifier.c @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wreserved-identifier -Wno-visibility %s + +#define __oof foo__ // expected-warning {{macro name is a reserved identifier}} + +int foo__bar() { return 0; } // no-warning +static int _bar() { return 0; } // expected-warning {{'_bar' is a reserved identifier}} +static int _Bar() { return 0; } // expected-warning {{'_Bar' is a reserved identifier}} +int _foo() { return 0; } // expected-warning {{'_foo' is a reserved identifier}} + +// This one is explicitly skipped by -Wreserved-identifier +void *_; // no-warning + +void foo(unsigned int _Reserved) { // expected-warning {{'_Reserved' is a reserved identifier}} + unsigned int __1 = // expected-warning {{'__1' is a reserved identifier}} + _Reserved; // no-warning + goto __reserved; +__reserved: // expected-warning {{'__reserved' is a reserved identifier}} + ; +} + +enum __menu { // expected-warning {{'__menu' is a reserved identifier}} + __some, // expected-warning {{'__some' is a reserved identifier}} + _Other, // expected-warning {{'_Other' is a reserved identifier}} + _other // expected-warning {{'_other' is a reserved identifier}} +}; + +struct __babar { // expected-warning {{'__babar' is a reserved identifier}} +}; + +struct _Zebulon; // expected-warning {{'_Zebulon' is a reserved identifier}} +struct _Zebulon2 {}* p; // expected-warning {{'_Zebulon2' is a reserved identifier}} + +typedef struct { + int _Field; // expected-warning {{'_Field' is a reserved identifier}} + int _field; // no-warning +} _Typedef; // expected-warning {{'_Typedef' is a reserved identifier}} + +int foobar() { + return foo__bar(); // no-warning +} + +struct _reserved { // expected-warning {{'_reserved' is a reserved identifier}} + int a; +} cunf(void) { + return (struct _reserved){1}; +} + +// FIXME: According to clang declaration context layering, _preserved belongs to +// the translation unit, so we emit a warning. It's unclear that's what the +// standard mandate, but it's such a corner case we can live with it. +void func(struct _preserved { int a; } r) {} // expected-warning {{'_preserved' is a reserved identifier}} + +extern char *_strdup(const char *); // expected-warning {{'_strdup' is a reserved identifier}} + +// Don't warn on redecleration +extern char *_strdup(const char *); // no-warning Index: clang/test/Sema/reserved-identifier.cpp =================================================================== --- /dev/null +++ clang/test/Sema/reserved-identifier.cpp @@ -0,0 +1,82 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify -Wreserved-identifier %s + +int foo__bar() { return 0; } // expected-warning {{'foo__bar' is a reserved identifier}} +static int _bar() { return 0; } // expected-warning {{'_bar' is a reserved identifier}} +static int _Bar() { return 0; } // expected-warning {{'_Bar' is a reserved identifier}} +int _barbouille() { return 0; } // expected-warning {{'_barbouille' is a reserved identifier}} + +void foo(unsigned int _Reserved) { // expected-warning {{'_Reserved' is a reserved identifier}} + unsigned int __1 = // expected-warning {{'__1' is a reserved identifier}} + _Reserved; // no-warning +} + +// This one is explicitly skipped by -Wreserved-identifier +void *_; // no-warning + +template constexpr bool __toucan = true; // expected-warning {{'__toucan' is a reserved identifier}} + +template +concept _Barbotine = __toucan; // expected-warning {{'_Barbotine' is a reserved identifier}} + +template // expected-warning {{'__' is a reserved identifier}} +struct BarbeNoire {}; + +template // expected-warning {{'__' is a reserved identifier}} +void BarbeRousse() {} + +namespace _Barbidur { // expected-warning {{'_Barbidur' is a reserved identifier}} + +struct __barbidou {}; // expected-warning {{'__barbidou' is a reserved identifier}} +struct _barbidou {}; // no-warning + +int __barbouille; // expected-warning {{'__barbouille' is a reserved identifier}} +int _barbouille; // no-warning + +int __babar() { return 0; } // expected-warning {{'__babar' is a reserved identifier}} +int _babar() { return 0; } // no-warning + +} // namespace _Barbidur + +class __barbapapa { // expected-warning {{'__barbapapa' is a reserved identifier}} + void _barbabelle() {} // no-warning + int _Barbalala; // expected-warning {{'_Barbalala' is a reserved identifier}} +}; + +enum class __menu { // expected-warning {{'__menu' is a reserved identifier}} + __some, // expected-warning {{'__some' is a reserved identifier}} + _Other, // expected-warning {{'_Other' is a reserved identifier}} + _other // no-warning +}; + +enum _Menu { // expected-warning {{'_Menu' is a reserved identifier}} + _OtheR_, // expected-warning {{'_OtheR_' is a reserved identifier}} + _other_ // expected-warning {{'_other_' is a reserved identifier}} +}; + +enum { + __some, // expected-warning {{'__some' is a reserved identifier}} + _Other, // expected-warning {{'_Other' is a reserved identifier}} + _other // expected-warning {{'_other' is a reserved identifier}} +}; + +static union { + int _barbeFleurie; // no-warning +}; + +using _Barbamama = __barbapapa; // expected-warning {{'_Barbamama' is a reserved identifier}} + +int foobar() { + return foo__bar(); // no-warning +} + +namespace { +int _barbatruc; // no-warning +} + +long double operator"" _BarbeBleue(long double) // no-warning +{ + return 0.; +} + +struct _BarbeRouge {} p; // expected-warning {{'_BarbeRouge' is a reserved identifier}} +struct _BarbeNoire {}* q; // expected-warning {{'_BarbeNoire' is a reserved identifier}}