diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 778ff67ce174..128ad047072e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -391,6 +391,7 @@ def warn_reserved_extern_symbol: Warning< "identifier %0 is reserved because %select{" "|" // ReservedIdentifierStatus::NotReserved "it starts with '_' at global scope|" + "it starts with '_' and has C language linkage|" "it starts with '__'|" "it starts with '_' followed by a capital letter|" "it contains '__'}1">, diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h index 803fec8df30c..19c967efcc42 100644 --- a/clang/include/clang/Basic/IdentifierTable.h +++ b/clang/include/clang/Basic/IdentifierTable.h @@ -43,6 +43,7 @@ class SourceLocation; enum class ReservedIdentifierStatus { NotReserved = 0, StartsWithUnderscoreAtGlobalScope, + StartsWithUnderscoreAndIsExternC, StartsWithDoubleUnderscore, StartsWithUnderscoreFollowedByCapitalLetter, ContainsDoubleUnderscore, @@ -60,7 +61,8 @@ inline bool isReservedAtGlobalScope(ReservedIdentifierStatus Status) { /// example. inline bool isReservedInAllContexts(ReservedIdentifierStatus Status) { return Status != ReservedIdentifierStatus::NotReserved && - Status != ReservedIdentifierStatus::StartsWithUnderscoreAtGlobalScope; + Status != ReservedIdentifierStatus::StartsWithUnderscoreAtGlobalScope && + Status != ReservedIdentifierStatus::StartsWithUnderscoreAndIsExternC; } /// A simple pair of identifier info and location. diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 57d84f2c3439..acc0839dba75 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1089,12 +1089,28 @@ NamedDecl::isReserved(const LangOptions &LangOpts) const { ReservedIdentifierStatus Status = II->isReserved(LangOpts); if (isReservedAtGlobalScope(Status) && !isReservedInAllContexts(Status)) { - // Check if we're at TU level or not. + // This name is only reserved at global scope. Check if this declaration + // conflicts with a global scope declaration. if (isa(this) || isTemplateParameter()) return ReservedIdentifierStatus::NotReserved; + + // C++ [dcl.link]/7: + // Two declarations [conflict] if [...] one declares a function or + // variable with C language linkage, and the other declares [...] a + // variable that belongs to the global scope. + // + // Therefore names that are reserved at global scope are also reserved as + // names of variables and functions with C language linkage. const DeclContext *DC = getDeclContext()->getRedeclContext(); - if (!DC->isTranslationUnit()) - return ReservedIdentifierStatus::NotReserved; + if (DC->isTranslationUnit()) + return Status; + if (auto *VD = dyn_cast(this)) + if (VD->isExternC()) + return ReservedIdentifierStatus::StartsWithUnderscoreAndIsExternC; + if (auto *FD = dyn_cast(this)) + if (FD->isExternC()) + return ReservedIdentifierStatus::StartsWithUnderscoreAndIsExternC; + return ReservedIdentifierStatus::NotReserved; } return Status; diff --git a/clang/test/SemaCXX/reserved-identifier.cpp b/clang/test/SemaCXX/reserved-identifier.cpp index 56fa3873359b..eaa5fe833033 100644 --- a/clang/test/SemaCXX/reserved-identifier.cpp +++ b/clang/test/SemaCXX/reserved-identifier.cpp @@ -105,3 +105,10 @@ struct Any { #define _Reserved // expected-warning {{macro name is a reserved identifier}} #undef _not_reserved #undef _Reserved // expected-warning {{macro name is a reserved identifier}} + +namespace N { + int _namespace_a; + extern "C" int _namespace_b; // expected-warning {{identifier '_namespace_b' is reserved because it starts with '_' and has C language linkage}} + void _namespace_c(); + extern "C" void _namespace_d(); // expected-warning {{identifier '_namespace_d' is reserved because it starts with '_' and has C language linkage}} +}