Add missing diagnostic for use of _reserved name in extern "C"
authorRichard Smith <richard@metafoo.co.uk>
Wed, 6 Oct 2021 22:11:12 +0000 (15:11 -0700)
committerRichard Smith <richard@metafoo.co.uk>
Wed, 6 Oct 2021 22:13:06 +0000 (15:13 -0700)
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.

clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/IdentifierTable.h
clang/lib/AST/Decl.cpp
clang/test/SemaCXX/reserved-identifier.cpp

index 778ff67..128ad04 100644 (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">,
index 803fec8..19c967e 100644 (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.
index 57d84f2..acc0839 100644 (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;
index 56fa387..eaa5fe8 100644 (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}}
+}