[clang] c8b37e4 - [clang] extend external_source_symbol attribute with USR clause

Alex Lorenz via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 23 14:59:38 PST 2023


Author: Alex Lorenz
Date: 2023-02-23T14:59:26-08:00
New Revision: c8b37e48f6f00bb2aa3882ca3cc26082f85ca999

URL: https://github.com/llvm/llvm-project/commit/c8b37e48f6f00bb2aa3882ca3cc26082f85ca999
DIFF: https://github.com/llvm/llvm-project/commit/c8b37e48f6f00bb2aa3882ca3cc26082f85ca999.diff

LOG: [clang] extend external_source_symbol attribute with USR clause

Allow the user to specify a concrete USR in the external_source_symbol attribute.
That will let Clang's indexer to use Swift USRs for Swift declarations that are
represented with C++ declarations.

This new clause is used by Swift when generating a C++ header representation
of a Swift module:
https://github.com/apple/swift/pull/63002

Differential Revision: https://reviews.llvm.org/D141324

Added: 
    clang/test/Index/Core/external-source-symbol-attr-cxx.cpp
    clang/test/Sema/attr-external-source-symbol-cxx.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/Attr.td
    clang/include/clang/Basic/AttrDocs.td
    clang/include/clang/Basic/DiagnosticCommonKinds.td
    clang/include/clang/Basic/DiagnosticParseKinds.td
    clang/include/clang/Parse/Parser.h
    clang/lib/Index/USRGeneration.cpp
    clang/lib/Parse/ParseDecl.cpp
    clang/lib/Parse/Parser.cpp
    clang/lib/Sema/SemaDeclAttr.cpp
    clang/test/AST/ast-dump-attr.cpp
    clang/test/Index/Core/external-source-symbol-attr.m
    clang/test/Parser/attr-external-source-symbol.m
    clang/test/Sema/attr-external-source-symbol.c
    clang/utils/TableGen/ClangAttrEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 2a636ebb00f79..c7204d8615a90 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -127,6 +127,11 @@ Attribute Changes in Clang
 - ``__declspec`` attributes can now be used together with the using keyword. Before
   the attributes on ``__declspec`` was ignored, while now it will be forwarded to the
   point where the alias is used.
+- Introduced a new ``USR`` (unified symbol resolution) clause inside of the
+existing ``__attribute__((external_source_symbol))`` attribute. Clang's indexer
+uses the optional USR value when indexing Clang's AST. This value is expected
+to be generated by an external compiler when generating C++ bindings during
+the compilation of the foreign language sources (e.g. Swift).
 
 Improvements to Clang's diagnostics
 -----------------------------------

diff  --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index fc2c7f7e37f45..8858bb6bec850 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -287,23 +287,22 @@ class VariadicEnumArgument<string name, string type, list<string> values,
 }
 
 // This handles one spelling of an attribute.
-class Spelling<string name, string variety> {
+class Spelling<string name, string variety, int version = 1> {
   string Name = name;
   string Variety = variety;
+  int Version = version;
 }
 
 class GNU<string name> : Spelling<name, "GNU">;
 class Declspec<string name> : Spelling<name, "Declspec">;
 class Microsoft<string name> : Spelling<name, "Microsoft">;
 class CXX11<string namespace, string name, int version = 1>
-    : Spelling<name, "CXX11"> {
+    : Spelling<name, "CXX11", version> {
   string Namespace = namespace;
-  int Version = version;
 }
 class C2x<string namespace, string name, int version = 1>
-    : Spelling<name, "C2x"> {
+    : Spelling<name, "C2x", version> {
   string Namespace = namespace;
-  int Version = version;
 }
 
 class Keyword<string name> : Spelling<name, "Keyword">;
@@ -321,7 +320,8 @@ class GCC<string name, bit allowInC = 1> : Spelling<name, "GCC"> {
 // The Clang spelling implies GNU<name>, CXX11<"clang", name>, and optionally,
 // C2x<"clang", name>. This spelling should be used for any Clang-specific
 // attributes.
-class Clang<string name, bit allowInC = 1> : Spelling<name, "Clang"> {
+class Clang<string name, bit allowInC = 1, int version = 1>
+    : Spelling<name, "Clang", version> {
   bit AllowInC = allowInC;
 }
 
@@ -958,10 +958,12 @@ static llvm::StringRef canonicalizePlatformName(llvm::StringRef Platform) {
 }
 
 def ExternalSourceSymbol : InheritableAttr {
-  let Spellings = [Clang<"external_source_symbol">];
+  let Spellings = [Clang<"external_source_symbol", /*allowInC=*/1,
+                   /*version=*/20230206>];
   let Args = [StringArgument<"language", 1>,
               StringArgument<"definedIn", 1>,
-              BoolArgument<"generatedDeclaration", 1>];
+              BoolArgument<"generatedDeclaration", 1>,
+              StringArgument<"USR", 1>];
   let HasCustomParsing = 1;
   let Subjects = SubjectList<[Named]>;
   let Documentation = [ExternalSourceSymbolDocs];

diff  --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index eebbf6863dd43..cbf28688a7408 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1750,6 +1750,19 @@ defined_in=\ *string-literal*
   source containers are modules, so ``defined_in`` should specify the Swift
   module name.
 
+USR=\ *string-literal*
+  String that specifies a unified symbol resolution (USR) value for this
+  declaration. USR string uniquely identifies this particular declaration, and
+  is typically used when constructing an index of a codebase.
+  The USR value in this attribute is expected to be generated by an external
+  compiler that compiled the native declaration using its original source
+  language. The exact format of the USR string and its other attributes
+  are determined by the specification of this declaration's source language.
+  When not specified, Clang's indexer will use the Clang USR for this symbol.
+  User can query to see if Clang supports the use of the ``USR`` clause in
+  the ``external_source_symbol`` attribute with
+  ``__has_attribute(external_source_symbol) >= 20230206``.
+
 generated_declaration
   This declaration was automatically generated by some tool.
 

diff  --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index c59adcc72a68b..9b8103c97e39c 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -55,7 +55,7 @@ def err_expected_colon_after_setter_name : Error<
 def err_expected_string_literal : Error<"expected string literal "
   "%select{in %1|for diagnostic message in static_assert|"
           "for optional message in 'availability' attribute|"
-          "for %select{language|source container}1 name in "
+          "for %select{language name|source container name|USR}1 in "
           "'external_source_symbol' attribute}0">;
 def err_invalid_string_udl : Error<
   "string literal with user-defined suffix cannot be used here">;

diff  --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 31519f3c04795..e3849c6658d16 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1104,7 +1104,7 @@ def err_availability_query_repeated_star : Error<
 
 // External source symbol attribute
 def err_external_source_symbol_expected_keyword : Error<
-  "expected 'language', 'defined_in', or 'generated_declaration'">;
+  "expected 'language', 'defined_in', 'generated_declaration', or 'USR'">;
 def err_external_source_symbol_duplicate_clause : Error<
   "duplicate %0 clause in an 'external_source_symbol' attribute">;
 

diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 6f9581b9ea1fc..96963fb6aa807 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -157,7 +157,7 @@ class Parser : public CodeCompletionHandler {
 
   /// Identifiers used by the 'external_source_symbol' attribute.
   IdentifierInfo *Ident_language, *Ident_defined_in,
-      *Ident_generated_declaration;
+      *Ident_generated_declaration, *Ident_USR;
 
   /// C++11 contextual keywords.
   mutable IdentifierInfo *Ident_final;

diff  --git a/clang/lib/Index/USRGeneration.cpp b/clang/lib/Index/USRGeneration.cpp
index 77b05b6bae056..06a3717f9ffaf 100644
--- a/clang/lib/Index/USRGeneration.cpp
+++ b/clang/lib/Index/USRGeneration.cpp
@@ -1141,6 +1141,15 @@ bool clang::index::generateUSRForDecl(const Decl *D,
   // C++'s operator new function, can have invalid locations but it is fine to
   // create USRs that can identify them.
 
+  // Check if the declaration has explicit external USR specified.
+  auto *CD = D->getCanonicalDecl();
+  if (auto *ExternalSymAttr = CD->getAttr<ExternalSourceSymbolAttr>()) {
+    if (!ExternalSymAttr->getUSR().empty()) {
+      llvm::raw_svector_ostream Out(Buf);
+      Out << ExternalSymAttr->getUSR();
+      return false;
+    }
+  }
   USRGenerator UG(&D->getASTContext(), Buf);
   UG.Visit(D);
   return UG.ignoreResults();

diff  --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 6465d859b1dab..d654cdff847be 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -1340,6 +1340,7 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability,
 /// keyword-arg:
 ///   'language' '=' <string>
 ///   'defined_in' '=' <string>
+///   'USR' '=' <string>
 ///   'generated_declaration'
 void Parser::ParseExternalSourceSymbolAttribute(
     IdentifierInfo &ExternalSourceSymbol, SourceLocation Loc,
@@ -1355,6 +1356,7 @@ void Parser::ParseExternalSourceSymbolAttribute(
     Ident_language = PP.getIdentifierInfo("language");
     Ident_defined_in = PP.getIdentifierInfo("defined_in");
     Ident_generated_declaration = PP.getIdentifierInfo("generated_declaration");
+    Ident_USR = PP.getIdentifierInfo("USR");
   }
 
   ExprResult Language;
@@ -1362,6 +1364,8 @@ void Parser::ParseExternalSourceSymbolAttribute(
   ExprResult DefinedInExpr;
   bool HasDefinedIn = false;
   IdentifierLoc *GeneratedDeclaration = nullptr;
+  ExprResult USR;
+  bool HasUSR = false;
 
   // Parse the language/defined_in/generated_declaration keywords
   do {
@@ -1383,7 +1387,8 @@ void Parser::ParseExternalSourceSymbolAttribute(
       continue;
     }
 
-    if (Keyword != Ident_language && Keyword != Ident_defined_in) {
+    if (Keyword != Ident_language && Keyword != Ident_defined_in &&
+        Keyword != Ident_USR) {
       Diag(Tok, diag::err_external_source_symbol_expected_keyword);
       SkipUntil(tok::r_paren, StopAtSemi);
       return;
@@ -1396,16 +1401,22 @@ void Parser::ParseExternalSourceSymbolAttribute(
       return;
     }
 
-    bool HadLanguage = HasLanguage, HadDefinedIn = HasDefinedIn;
+    bool HadLanguage = HasLanguage, HadDefinedIn = HasDefinedIn,
+         HadUSR = HasUSR;
     if (Keyword == Ident_language)
       HasLanguage = true;
+    else if (Keyword == Ident_USR)
+      HasUSR = true;
     else
       HasDefinedIn = true;
 
     if (Tok.isNot(tok::string_literal)) {
       Diag(Tok, diag::err_expected_string_literal)
           << /*Source='external_source_symbol attribute'*/ 3
-          << /*language | source container*/ (Keyword != Ident_language);
+          << /*language | source container | USR*/ (
+                 Keyword == Ident_language
+                     ? 0
+                     : (Keyword == Ident_defined_in ? 1 : 2));
       SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
       continue;
     }
@@ -1417,6 +1428,14 @@ void Parser::ParseExternalSourceSymbolAttribute(
         continue;
       }
       Language = ParseStringLiteralExpression();
+    } else if (Keyword == Ident_USR) {
+      if (HadUSR) {
+        Diag(KeywordLoc, diag::err_external_source_symbol_duplicate_clause)
+            << Keyword;
+        ParseStringLiteralExpression();
+        continue;
+      }
+      USR = ParseStringLiteralExpression();
     } else {
       assert(Keyword == Ident_defined_in && "Invalid clause keyword!");
       if (HadDefinedIn) {
@@ -1435,8 +1454,8 @@ void Parser::ParseExternalSourceSymbolAttribute(
   if (EndLoc)
     *EndLoc = T.getCloseLocation();
 
-  ArgsUnion Args[] = {Language.get(), DefinedInExpr.get(),
-                      GeneratedDeclaration};
+  ArgsUnion Args[] = {Language.get(), DefinedInExpr.get(), GeneratedDeclaration,
+                      USR.get()};
   Attrs.addNew(&ExternalSourceSymbol, SourceRange(Loc, T.getCloseLocation()),
                ScopeName, ScopeLoc, Args, std::size(Args), Syntax);
 }

diff  --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 829a187e842b9..e6b1d3fdf70bf 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -523,7 +523,8 @@ void Parser::Initialize() {
   Ident_strict = nullptr;
   Ident_replacement = nullptr;
 
-  Ident_language = Ident_defined_in = Ident_generated_declaration = nullptr;
+  Ident_language = Ident_defined_in = Ident_generated_declaration = Ident_USR =
+      nullptr;
 
   Ident__except = nullptr;
 

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 1a0bfb3d91bcc..5a6e07c81041b 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -2834,7 +2834,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
 
 static void handleExternalSourceSymbolAttr(Sema &S, Decl *D,
                                            const ParsedAttr &AL) {
-  if (!AL.checkAtLeastNumArgs(S, 1) || !AL.checkAtMostNumArgs(S, 3))
+  if (!AL.checkAtLeastNumArgs(S, 1) || !AL.checkAtMostNumArgs(S, 4))
     return;
 
   StringRef Language;
@@ -2844,9 +2844,12 @@ static void handleExternalSourceSymbolAttr(Sema &S, Decl *D,
   if (const auto *SE = dyn_cast_or_null<StringLiteral>(AL.getArgAsExpr(1)))
     DefinedIn = SE->getString();
   bool IsGeneratedDeclaration = AL.getArgAsIdent(2) != nullptr;
+  StringRef USR;
+  if (const auto *SE = dyn_cast_or_null<StringLiteral>(AL.getArgAsExpr(3)))
+    USR = SE->getString();
 
   D->addAttr(::new (S.Context) ExternalSourceSymbolAttr(
-      S.Context, AL, Language, DefinedIn, IsGeneratedDeclaration));
+      S.Context, AL, Language, DefinedIn, IsGeneratedDeclaration, USR));
 }
 
 template <class T>

diff  --git a/clang/test/AST/ast-dump-attr.cpp b/clang/test/AST/ast-dump-attr.cpp
index 25cfa6820b372..15ef878f31573 100644
--- a/clang/test/AST/ast-dump-attr.cpp
+++ b/clang/test/AST/ast-dump-attr.cpp
@@ -175,7 +175,7 @@ __attribute__((external_source_symbol(language="Swift", defined_in="module", gen
 void TestExternalSourceSymbolAttr2()
 __attribute__((external_source_symbol(defined_in="module", language="Swift")));
 // CHECK: FunctionDecl{{.*}} TestExternalSourceSymbolAttr2
-// CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Swift" "module"{{$}}
+// CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Swift" "module" ""{{$}}
 
 void TestExternalSourceSymbolAttr3()
 __attribute__((external_source_symbol(generated_declaration, language="Objective-C++", defined_in="module")));
@@ -192,6 +192,11 @@ __attribute__((external_source_symbol(generated_declaration, defined_in="module"
 // CHECK: FunctionDecl{{.*}} TestExternalSourceSymbolAttr5
 // CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Swift" "module" GeneratedDeclaration
 
+void TestExternalSourceSymbolAttr6()
+__attribute__((external_source_symbol(generated_declaration, defined_in="module", language="Swift", USR="testUSR")));
+// CHECK: FunctionDecl{{.*}} TestExternalSourceSymbolAttr6
+// CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Swift" "module" GeneratedDeclaration "testUSR"
+
 namespace TestNoEscape {
   void noescapeFunc(int *p0, __attribute__((noescape)) int *p1) {}
   // CHECK: `-FunctionDecl{{.*}} noescapeFunc 'void (int *, __attribute__((noescape)) int *)'

diff  --git a/clang/test/Index/Core/external-source-symbol-attr-cxx.cpp b/clang/test/Index/Core/external-source-symbol-attr-cxx.cpp
new file mode 100644
index 0000000000000..8ccf3c7d69496
--- /dev/null
+++ b/clang/test/Index/Core/external-source-symbol-attr-cxx.cpp
@@ -0,0 +1,35 @@
+// RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s
+
+#define GEN_DECL_USR(mod_name, usr) __attribute__((external_source_symbol(language="Swift", defined_in=mod_name, USR=usr, generated_declaration)))
+
+class GEN_DECL_USR("Module", "s:Class") Class {
+public:
+    void method() GEN_DECL_USR("Module", "s:Class_method");
+    void method2() GEN_DECL_USR("Module", "");
+
+    static void staticMethod() GEN_DECL_USR("Module", "s:Class_staticMethod");
+};
+
+template<class T>
+class GEN_DECL_USR("Module", "s:TemplateClass") TemplateClass {
+public:
+    void method() GEN_DECL_USR("Module", "s:TemplateClass_method");
+};
+
+void test() {
+  Class c = Class();
+  // CHECK: [[@LINE-1]]:3 | class/Swift | Class | s:Class |
+  // CHECK: [[@LINE-2]]:13 | class/Swift | Class | s:Class |
+  c.method();
+  // CHECK: [[@LINE-1]]:5 | instance-method/Swift | method | s:Class_method |
+  c.method2();
+  // CHECK: [[@LINE-1]]:5 | instance-method/Swift | method2 | c:@M at Module@S at Class@F at method2# |
+  Class::staticMethod();
+  // CHECK: [[@LINE-1]]:10 | static-method/Swift | staticMethod | s:Class_staticMethod |
+  // CHECK: [[@LINE-2]]:3 | class/Swift | Class | s:Class |
+  TemplateClass<int> c2 = TemplateClass<int>();
+  // CHECK: [[@LINE-1]]:3 | class(Gen)/Swift | TemplateClass | s:TemplateClass |
+  // CHECK: [[@LINE-2]]:27 | class(Gen)/Swift | TemplateClass | s:TemplateClass |
+  c2.method();
+  // CHECK: [[@LINE-1]]:6 | instance-method/Swift | method | s:TemplateClass_method |
+}

diff  --git a/clang/test/Index/Core/external-source-symbol-attr.m b/clang/test/Index/Core/external-source-symbol-attr.m
index d2cef35ffab23..1f907912737b0 100644
--- a/clang/test/Index/Core/external-source-symbol-attr.m
+++ b/clang/test/Index/Core/external-source-symbol-attr.m
@@ -4,6 +4,8 @@
 #define GEN_DECL(mod_name) __attribute__((external_source_symbol(language="Swift", defined_in=mod_name, generated_declaration)))
 #define PUSH_GEN_DECL(mod_name) push(GEN_DECL(mod_name), apply_to=any(enum, objc_interface, objc_category, objc_protocol))
 
+#define GEN_DECL_USR(mod_name, usr) __attribute__((external_source_symbol(language="Swift", defined_in=mod_name, USR=usr, generated_declaration)))
+
 // Forward declarations should not affect module namespacing below
 @class I1;
 @class I2;
@@ -110,3 +112,10 @@ void test3(I3 *i3) {
   [i3 meth_other_mod];
   // CHECK: [[@LINE-1]]:7 | instance-method/Swift | meth_other_mod | c:@CM at other_mod_for_cat@modname at objc(cs)I3(im)meth_other_mod |
 }
+
+void function() GEN_DECL_USR("SwiftMod", "s:8SwiftMod8functionyyF");
+
+void test4() {
+  function();
+  // CHECK: [[@LINE-1]]:3 | function/Swift | function | s:8SwiftMod8functionyyF
+}

diff  --git a/clang/test/Parser/attr-external-source-symbol.m b/clang/test/Parser/attr-external-source-symbol.m
index 752c748be84a7..b94cb3a628145 100644
--- a/clang/test/Parser/attr-external-source-symbol.m
+++ b/clang/test/Parser/attr-external-source-symbol.m
@@ -14,10 +14,14 @@ CaseA __attribute__((external_source_symbol(generated_declaration))),
   CaseB __attribute__((external_source_symbol(generated_declaration, language="Swift")))
 } __attribute__((external_source_symbol(language = "Swift")));
 
+void functionCustomUSR(void) __attribute__((external_source_symbol(language="Swift", defined_in="module", generated_declaration, USR="s:6module17functionCustomUSRyyF")));
+
+void functionCustomUSR2(void) __attribute__((external_source_symbol(language="Swift", defined_in="module", USR="s:6module18functionCustomUSR2yyF", generated_declaration)));
+
 void f2(void)
-__attribute__((external_source_symbol())); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}}
+__attribute__((external_source_symbol())); // expected-error {{expected 'language', 'defined_in', 'generated_declaration', or 'USR'}}
 void f3(void)
-__attribute__((external_source_symbol(invalid))); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}}
+__attribute__((external_source_symbol(invalid))); // expected-error {{expected 'language', 'defined_in', 'generated_declaration', or 'USR'}}
 void f4(void)
 __attribute__((external_source_symbol(language))); // expected-error {{expected '=' after language}}
 void f5(void)
@@ -31,6 +35,8 @@ void f8(void)
 __attribute__((external_source_symbol(language="Swift", language="Swift"))); // expected-error {{duplicate 'language' clause in an 'external_source_symbol' attribute}}
 void f9(void)
 __attribute__((external_source_symbol(defined_in="module", language="Swift", defined_in="foo"))); // expected-error {{duplicate 'defined_in' clause in an 'external_source_symbol' attribute}}
+__attribute__((external_source_symbol(defined_in="module", language="Swift", USR="foo", USR="bar"))); // expected-error {{duplicate 'USR' clause in an 'external_source_symbol' attribute}}
+void f9_1(void);
 
 void f10(void)
 __attribute__((external_source_symbol(generated_declaration, language="Swift", defined_in="foo", generated_declaration, generated_declaration, language="Swift"))); // expected-error {{duplicate 'generated_declaration' clause in an 'external_source_symbol' attribute}}
@@ -45,16 +51,16 @@ void f13(void)
 __attribute__((external_source_symbol(language=Swift))); // expected-error {{expected string literal for language name in 'external_source_symbol' attribute}}
 
 void f14(void)
-__attribute__((external_source_symbol(=))); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}}
+__attribute__((external_source_symbol(=))); // expected-error {{expected 'language', 'defined_in', 'generated_declaration', or 'USR'}}
 
 void f15(void)
-__attribute__((external_source_symbol(="Swift"))); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}}
+__attribute__((external_source_symbol(="Swift"))); // expected-error {{expected 'language', 'defined_in', 'generated_declaration', or 'USR'}}
 
 void f16(void)
-__attribute__((external_source_symbol("Swift", "module", generated_declaration))); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}}
+__attribute__((external_source_symbol("Swift", "module", generated_declaration))); // expected-error {{expected 'language', 'defined_in', 'generated_declaration', or 'USR'}}
 
 void f17(void)
-__attribute__((external_source_symbol(language="Swift", "generated_declaration"))); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}}
+__attribute__((external_source_symbol(language="Swift", "generated_declaration"))); // expected-error {{expected 'language', 'defined_in', 'generated_declaration', or 'USR'}}
 
 void f18(void)
 __attribute__((external_source_symbol(language= =))); // expected-error {{expected string literal for language name in 'external_source_symbol' attribute}}
@@ -81,4 +87,14 @@ void f25(void)
 __attribute__((external_source_symbol(defined_in=123, defined_in="module"))); // expected-error {{expected string literal for source container name in 'external_source_symbol'}} expected-error {{duplicate 'defined_in' clause in an 'external_source_symbol' attribute}}
 
 void f26(void)
-__attribute__((external_source_symbol(language=Swift, language="Swift", error))); // expected-error {{expected string literal for language name in 'external_source_symbol'}} expected-error {{duplicate 'language' clause in an 'external_source_symbol' attribute}} expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}}
+__attribute__((external_source_symbol(language=Swift, language="Swift", error))); // expected-error {{expected string literal for language name in 'external_source_symbol'}} expected-error {{duplicate 'language' clause in an 'external_source_symbol' attribute}} expected-error {{expected 'language', 'defined_in', 'generated_declaration', or 'USR'}}
+
+void f27(void)
+__attribute__((external_source_symbol(USR=f27))); // expected-error {{expected string literal for USR in 'external_source_symbol' attribute}}
+
+void f28(void)
+__attribute__((external_source_symbol(USR="")));
+
+#if __has_attribute(external_source_symbol) != 20230206
+# error "invalid __has_attribute version"
+#endif

diff  --git a/clang/test/Sema/attr-external-source-symbol-cxx.cpp b/clang/test/Sema/attr-external-source-symbol-cxx.cpp
new file mode 100644
index 0000000000000..af5da4909b399
--- /dev/null
+++ b/clang/test/Sema/attr-external-source-symbol-cxx.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -fdouble-square-bracket-attributes %s
+
+template<class T>
+class Class {
+public:
+    [[clang::external_source_symbol(language="Swift", defined_in="module", USR="test", generated_declaration)]]
+    void testExternalSourceSymbol();
+
+    // expected-error at +1 {{expected string literal for USR in 'external_source_symbol' attribute}}
+    [[clang::external_source_symbol(language="Swift", defined_in="module", USR=T, generated_declaration)]]
+    void testExternalSourceSymbol2();
+};
+
+template<class T>
+void Class<T>::testExternalSourceSymbol() {
+}

diff  --git a/clang/test/Sema/attr-external-source-symbol.c b/clang/test/Sema/attr-external-source-symbol.c
index f6d0e0649f035..7301b83ebfdeb 100644
--- a/clang/test/Sema/attr-external-source-symbol.c
+++ b/clang/test/Sema/attr-external-source-symbol.c
@@ -4,7 +4,9 @@ void threeClauses(void) __attribute__((external_source_symbol(language="Swift",
 
 void twoClauses(void) __attribute__((external_source_symbol(language="Swift", defined_in="module")));
 
-void fourClauses(void) __attribute__((external_source_symbol(language="Swift", defined_in="module", generated_declaration, generated_declaration))); // expected-error {{duplicate 'generated_declaration' clause in an 'external_source_symbol' attribute}}
+void fourClauses(void) __attribute__((external_source_symbol(language="Swift", defined_in="module", generated_declaration, USR="test")));
+
+void fiveClauses(void) __attribute__((external_source_symbol(language="Swift", defined_in="module", generated_declaration, generated_declaration, USR="test"))); // expected-error {{duplicate 'generated_declaration' clause in an 'external_source_symbol' attribute}}
 
 void oneClause(void) __attribute__((external_source_symbol(generated_declaration)));
 
@@ -22,8 +24,8 @@ void namedDeclsOnly(void) {
 
 [[clang::external_source_symbol(language="Swift", defined_in="module")]] void twoClauses2(void);
 
-[[clang::external_source_symbol(language="Swift", defined_in="module", generated_declaration, generated_declaration)]] // expected-error {{duplicate 'generated_declaration' clause in an 'external_source_symbol' attribute}}
-void fourClauses2(void);
+[[clang::external_source_symbol(language="Swift", defined_in="module", USR="test", generated_declaration, generated_declaration)]] // expected-error {{duplicate 'generated_declaration' clause in an 'external_source_symbol' attribute}}
+void fiveClauses2(void);
 
 [[clang::external_source_symbol(generated_declaration)]] void oneClause2(void);
 

diff  --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index de608c780f981..063b104984725 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -51,14 +51,18 @@ namespace {
 class FlattenedSpelling {
   std::string V, N, NS;
   bool K = false;
+  const Record &OriginalSpelling;
 
 public:
   FlattenedSpelling(const std::string &Variety, const std::string &Name,
-                    const std::string &Namespace, bool KnownToGCC) :
-    V(Variety), N(Name), NS(Namespace), K(KnownToGCC) {}
+                    const std::string &Namespace, bool KnownToGCC,
+                    const Record &OriginalSpelling)
+      : V(Variety), N(Name), NS(Namespace), K(KnownToGCC),
+        OriginalSpelling(OriginalSpelling) {}
   explicit FlattenedSpelling(const Record &Spelling)
       : V(std::string(Spelling.getValueAsString("Variety"))),
-        N(std::string(Spelling.getValueAsString("Name"))) {
+        N(std::string(Spelling.getValueAsString("Name"))),
+        OriginalSpelling(Spelling) {
     assert(V != "GCC" && V != "Clang" &&
            "Given a GCC spelling, which means this hasn't been flattened!");
     if (V == "CXX11" || V == "C2x" || V == "Pragma")
@@ -69,6 +73,7 @@ class FlattenedSpelling {
   const std::string &name() const { return N; }
   const std::string &nameSpace() const { return NS; }
   bool knownToGCC() const { return K; }
+  const Record &getSpellingRecord() const { return OriginalSpelling; }
 };
 
 } // end anonymous namespace
@@ -82,15 +87,15 @@ GetFlattenedSpellings(const Record &Attr) {
     StringRef Variety = Spelling->getValueAsString("Variety");
     StringRef Name = Spelling->getValueAsString("Name");
     if (Variety == "GCC") {
-      Ret.emplace_back("GNU", std::string(Name), "", true);
-      Ret.emplace_back("CXX11", std::string(Name), "gnu", true);
+      Ret.emplace_back("GNU", std::string(Name), "", true, *Spelling);
+      Ret.emplace_back("CXX11", std::string(Name), "gnu", true, *Spelling);
       if (Spelling->getValueAsBit("AllowInC"))
-        Ret.emplace_back("C2x", std::string(Name), "gnu", true);
+        Ret.emplace_back("C2x", std::string(Name), "gnu", true, *Spelling);
     } else if (Variety == "Clang") {
-      Ret.emplace_back("GNU", std::string(Name), "", false);
-      Ret.emplace_back("CXX11", std::string(Name), "clang", false);
+      Ret.emplace_back("GNU", std::string(Name), "", false, *Spelling);
+      Ret.emplace_back("CXX11", std::string(Name), "clang", false, *Spelling);
       if (Spelling->getValueAsBit("AllowInC"))
-        Ret.emplace_back("C2x", std::string(Name), "clang", false);
+        Ret.emplace_back("C2x", std::string(Name), "clang", false, *Spelling);
     } else
       Ret.push_back(FlattenedSpelling(*Spelling));
   }
@@ -3309,18 +3314,31 @@ static void GenerateHasAttrSpellingStringSwitch(
     // C2x-style attributes have the same kind of version information
     // associated with them. The unscoped attribute version information should
     // be taken from the specification of the attribute in the C Standard.
+    //
+    // Clang-specific attributes have the same kind of version information
+    // associated with them. This version is typically the default value (1).
+    // These version values are clang-specific and should typically be
+    // incremented once the attribute changes its syntax and/or semantics in a
+    // a way that is impactful to the end user.
     int Version = 1;
 
-    if (Variety == "CXX11" || Variety == "C2x") {
-      std::vector<Record *> Spellings = Attr->getValueAsListOfDefs("Spellings");
-      for (const auto &Spelling : Spellings) {
-        if (Spelling->getValueAsString("Variety") == Variety) {
-          Version = static_cast<int>(Spelling->getValueAsInt("Version"));
-          if (Scope.empty() && Version == 1)
-            PrintError(Spelling->getLoc(), "Standard attributes must have "
-                                           "valid version information.");
-          break;
-        }
+    std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr);
+    for (const auto &Spelling : Spellings) {
+      if (Spelling.variety() == Variety &&
+          (Spelling.nameSpace().empty() || Scope == Spelling.nameSpace())) {
+        Version = static_cast<int>(
+            Spelling.getSpellingRecord().getValueAsInt("Version"));
+        // Verify that explicitly specified CXX11 and C2x spellings (i.e.
+        // not inferred from Clang/GCC spellings) have a version that's
+        // 
diff erent than the default (1).
+        bool RequiresValidVersion =
+            (Variety == "CXX11" || Variety == "C2x") &&
+            Spelling.getSpellingRecord().getValueAsString("Variety") == Variety;
+        if (RequiresValidVersion && Scope.empty() && Version == 1)
+          PrintError(Spelling.getSpellingRecord().getLoc(),
+                     "Standard attributes must have "
+                     "valid version information.");
+        break;
       }
     }
 
@@ -3342,9 +3360,9 @@ static void GenerateHasAttrSpellingStringSwitch(
     else if (Variety == "C2x")
       Test = "LangOpts.DoubleSquareBracketAttributes";
 
-    std::string TestStr =
-        !Test.empty() ? Test + " ? " + llvm::itostr(Version) + " : 0" : "1";
-    std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr);
+    std::string TestStr = !Test.empty()
+                              ? Test + " ? " + llvm::itostr(Version) + " : 0"
+                              : llvm::itostr(Version);
     for (const auto &S : Spellings)
       if (Variety.empty() || (Variety == S.variety() &&
                               (Scope.empty() || Scope == S.nameSpace())))


        


More information about the cfe-commits mailing list