"identifier %0 is reserved because %select{"
"<ERROR>|" // 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">,
enum class ReservedIdentifierStatus {
NotReserved = 0,
StartsWithUnderscoreAtGlobalScope,
+ StartsWithUnderscoreAndIsExternC,
StartsWithDoubleUnderscore,
StartsWithUnderscoreFollowedByCapitalLetter,
ContainsDoubleUnderscore,
/// 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.
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<ParmVarDecl>(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<VarDecl>(this))
+ if (VD->isExternC())
+ return ReservedIdentifierStatus::StartsWithUnderscoreAndIsExternC;
+ if (auto *FD = dyn_cast<FunctionDecl>(this))
+ if (FD->isExternC())
+ return ReservedIdentifierStatus::StartsWithUnderscoreAndIsExternC;
+ return ReservedIdentifierStatus::NotReserved;
}
return Status;
#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}}
+}