Add missing diagnostic for use of _reserved name in extern "C"

declaration.

Names starting with an underscore are reserved at the global scope, so
cannot be used as the name of an extern "C" symbol in any scope because
such usages conflict with a name at global scope.
This commit is contained in:
Richard Smith 2021-10-06 15:11:12 -07:00
parent 7063b76b02
commit 141df74456
4 changed files with 30 additions and 4 deletions

View File

@ -391,6 +391,7 @@ def warn_reserved_extern_symbol: Warning<
"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">,

View File

@ -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.

View File

@ -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<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;

View File

@ -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}}
}