arphaman created this revision.

This patch adds an `external_source_symbol` attribute to Clang. This attribute 
specifies that a declaration originates
from an external source and describes the nature of that source.

This attribute is useful for mixed-language projects or project that use 
auto-generated code. For instance, Xcode can use this attribute to provide a 
correct 'jump-to-definition' feature.  For a concrete example, consider a 
protocol that's defined in a Swift file:

  @objc public protocol SwiftProtocol {
    func method()
  }

This protocol can be used from Objective-C code by including a header file that 
was generated by the Swift compiler. The declarations in that header can use 
the ``external_source_symbol`` attribute to make Clang aware of the fact that 
``SwiftProtocol`` actually originates from a Swift module:

  __attribute__((external_source_symbol(language=Swift,defined_in="module")))
  @protocol SwiftProtocol
  @required
  - (void) method;
  @end

Consequently, when 'jump-to-definition' is performed at a location that 
references `SwiftProtocol`, Xcode can jump to the original definition in the 
Swift source file rather than jumping to the Objective-C declaration in the 
auto-generated header file.

his attribute uses custom parsing to specify a varying number of clauses such 
as 'language', 'defined_in' or 'generated_declaration'. The 'language' clause 
specifies the source language from which the declaration originates. The 
'defined_in' clause specifies the name of the source container in which the 
declaration was defined (the exact definition of source container is 
language-specific, e.g. Swift's source containers are modules, so 
``defined_in`` should specify the Swift module name). The 
'generated_declaration' is a flag that specifies that whether this declaration 
was automatically generated by some tool or not.


Repository:
  rL LLVM

https://reviews.llvm.org/D29819

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticCommonKinds.td
  include/clang/Basic/DiagnosticParseKinds.td
  include/clang/Parse/Parser.h
  lib/Parse/ParseDecl.cpp
  lib/Parse/Parser.cpp
  lib/Sema/SemaDeclAttr.cpp
  test/Parser/attr-external-source-symbol-cxx11.cpp
  test/Parser/attr-external-source-symbol.m
  test/Sema/attr-external-source-symbol.c

Index: test/Sema/attr-external-source-symbol.c
===================================================================
--- /dev/null
+++ test/Sema/attr-external-source-symbol.c
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -verify %s
+
+void f1() __attribute__((external_source_symbol(language=Swift, defined_in="module", generated_declaration)));
+
+void f2() __attribute__((external_source_symbol(generated_declaration)));
+
+void f3()
+__attribute__((external_source_symbol)); // expected-error {{'external_source_symbol' attribute takes at least 1 argument}}
+
+void f4() {
+  int (^block)(void) = ^ (void)
+    __attribute__((external_source_symbol(language=Swift))) { // expected-warning {{'external_source_symbol' attribute ignored}}
+      return 1;
+  };
+}
Index: test/Parser/attr-external-source-symbol.m
===================================================================
--- /dev/null
+++ test/Parser/attr-external-source-symbol.m
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+void function() __attribute__((external_source_symbol(language=Swift, defined_in="module", generated_declaration)));
+
+__attribute__((external_source_symbol(language=Swift, defined_in="module")))
+@interface I
+
+- (void)method __attribute__((external_source_symbol(defined_in= "module")));
+
+@end
+
+enum E {
+  CaseA __attribute__((external_source_symbol(generated_declaration))),
+  CaseB __attribute__((external_source_symbol(generated_declaration, language=Swift)))
+} __attribute__((external_source_symbol(language = Swift)));
+
+void f2()
+__attribute__((external_source_symbol())); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}}
+void f3()
+__attribute__((external_source_symbol(invalid))); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}}
+void f4()
+__attribute__((external_source_symbol(language))); // expected-error {{expected '=' after language}}
+void f5()
+__attribute__((external_source_symbol(language=))); // expected-error {{expected a source language , e.g., 'Swift'}}
+void f6()
+__attribute__((external_source_symbol(defined_in=20))); // expected-error {{expected string literal for source container name in 'external_source_symbol' attribute}}
Index: test/Parser/attr-external-source-symbol-cxx11.cpp
===================================================================
--- /dev/null
+++ test/Parser/attr-external-source-symbol-cxx11.cpp
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+
+[[gnu::external_source_symbol(language=Swift, defined_in="module")]]
+void function() { }
+
+// expected-no-diagnostics
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -2409,6 +2409,29 @@
   }
 }
 
+static void handleExternalSourceSymbolAttr(Sema &S, Decl *D,
+                                           const AttributeList &Attr) {
+  if (!checkAttributeAtLeastNumArgs(S, Attr, 1))
+    return;
+
+  if (!isa<NamedDecl>(D)) {
+    S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName();
+    return;
+  }
+
+  IdentifierInfo *Language = nullptr;
+  if (const IdentifierLoc *LanguageIdent = Attr.getArgAsIdent(0))
+    Language = LanguageIdent->Ident;
+  StringRef DefinedIn;
+  if (const auto *SE = dyn_cast_or_null<StringLiteral>(Attr.getArgAsExpr(1)))
+    DefinedIn = SE->getString();
+  bool IsGeneratedDeclaration = Attr.getArgAsIdent(2);
+
+  D->addAttr(::new (S.Context) ExternalSourceSymbolAttr(
+      Attr.getRange(), S.Context, Language, DefinedIn, IsGeneratedDeclaration,
+      Attr.getAttributeSpellingListIndex()));
+}
+
 template <class T>
 static T *mergeVisibilityAttr(Sema &S, Decl *D, SourceRange range,
                               typename T::VisibilityType value,
@@ -5804,6 +5827,9 @@
   case AttributeList::AT_ExtVectorType:
     handleExtVectorTypeAttr(S, scope, D, Attr);
     break;
+  case AttributeList::AT_ExternalSourceSymbol:
+    handleExternalSourceSymbolAttr(S, D, Attr);
+    break;
   case AttributeList::AT_MinSize:
     handleMinSizeAttr(S, D, Attr);
     break;
Index: lib/Parse/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp
+++ lib/Parse/Parser.cpp
@@ -493,6 +493,8 @@
   Ident_strict = nullptr;
   Ident_replacement = nullptr;
 
+  Ident_language = Ident_defined_in = Ident_generated_declaration = nullptr;
+
   Ident__except = nullptr;
 
   Ident__exception_code = Ident__exception_info = nullptr;
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -356,6 +356,10 @@
     ParseAvailabilityAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
                                ScopeLoc, Syntax);
     return;
+  } else if (AttrKind == AttributeList::AT_ExternalSourceSymbol) {
+    ParseExternalSourceSymbolAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
+                                       ScopeName, ScopeLoc, Syntax);
+    return;
   } else if (AttrKind == AttributeList::AT_ObjCBridgeRelated) {
     ParseObjCBridgeRelatedAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
                                     ScopeName, ScopeLoc, Syntax);
@@ -1064,6 +1068,98 @@
                Syntax, StrictLoc, ReplacementExpr.get());
 }
 
+/// \brief Parse the contents of the "external_source_symbol" attribute.
+///
+/// external-source-symbol-attribute:
+///   'external_source_symbol' '(' keyword-arg-list ')'
+///
+/// keyword-arg-list:
+///   keyword-arg
+///   keyword-arg ',' keyword-arg-list
+///
+/// keyword-arg:
+///   'language' '=' language
+///   'defined_in' '=' <string>
+///   'generated_declaration'
+void Parser::ParseExternalSourceSymbolAttribute(
+    IdentifierInfo &ExternalSourceSymbol, SourceLocation Loc,
+    ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName,
+    SourceLocation ScopeLoc, AttributeList::Syntax Syntax) {
+  // Opening '('.
+  BalancedDelimiterTracker T(*this, tok::l_paren);
+  if (T.consumeOpen()) {
+    Diag(Tok, diag::err_expected) << tok::l_paren;
+    return;
+  }
+
+  // Initialize the pointers for the keyword identifiers when required.
+  if (!Ident_language) {
+    Ident_language = PP.getIdentifierInfo("language");
+    Ident_defined_in = PP.getIdentifierInfo("defined_in");
+    Ident_generated_declaration = PP.getIdentifierInfo("generated_declaration");
+  }
+
+  IdentifierLoc *Language = nullptr;
+  ExprResult DefinedInExpr;
+  IdentifierLoc *GeneratedDeclaration = nullptr;
+
+  // Parse the language/defined_in/generated_declaration keywords
+  do {
+    if (Tok.isNot(tok::identifier)) {
+      Diag(Tok, diag::err_external_source_symbol_expected_keyword);
+      SkipUntil(tok::r_paren, StopAtSemi);
+      return;
+    }
+
+    IdentifierInfo *Keyword = Tok.getIdentifierInfo();
+    if (Keyword == Ident_generated_declaration) {
+      GeneratedDeclaration = ParseIdentifierLoc();
+      continue;
+    }
+
+    if (Keyword != Ident_language && Keyword != Ident_defined_in) {
+      Diag(Tok, diag::err_external_source_symbol_expected_keyword);
+      SkipUntil(tok::r_paren, StopAtSemi);
+      return;
+    }
+
+    ConsumeToken();
+    if (ExpectAndConsume(tok::equal, diag::err_expected_after,
+                         Keyword->getName())) {
+      SkipUntil(tok::r_paren, StopAtSemi);
+      return;
+    }
+
+    if (Keyword == Ident_language) {
+      if (Tok.isNot(tok::identifier)) {
+        Diag(Tok, diag::err_external_source_symbol_expected_language);
+        SkipUntil(tok::r_paren, StopAtSemi);
+        return;
+      }
+      Language = ParseIdentifierLoc();
+    } else {
+      // Keyword must be 'Ident_defined_in'.
+      if (Tok.isNot(tok::string_literal)) {
+        Diag(Tok, diag::err_expected_string_literal)
+            << /*Source='external_source_symbol attribute'*/ 3;
+        SkipUntil(tok::r_paren, StopAtSemi);
+        return;
+      }
+      DefinedInExpr = ParseStringLiteralExpression();
+    }
+  } while (TryConsumeToken(tok::comma));
+
+  // Closing ')'.
+  if (T.consumeClose())
+    return;
+  if (EndLoc)
+    *EndLoc = T.getCloseLocation();
+
+  ArgsUnion Args[] = {Language, DefinedInExpr.get(), GeneratedDeclaration};
+  Attrs.addNew(&ExternalSourceSymbol, SourceRange(Loc, T.getCloseLocation()),
+               ScopeName, ScopeLoc, Args, llvm::array_lengthof(Args), Syntax);
+}
+
 /// \brief Parse the contents of the "objc_bridge_related" attribute.
 /// objc_bridge_related '(' related_class ',' opt-class_method ',' opt-instance_method ')'
 /// related_class:
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -142,6 +142,10 @@
   /// \brief Identifier for "replacement".
   IdentifierInfo *Ident_replacement;
 
+  /// Identifiers used by the 'external_source_symbol' attribute.
+  IdentifierInfo *Ident_language, *Ident_defined_in,
+      *Ident_generated_declaration;
+
   /// C++0x contextual keywords.
   mutable IdentifierInfo *Ident_final;
   mutable IdentifierInfo *Ident_GNU_final;
@@ -2286,6 +2290,14 @@
   Optional<AvailabilitySpec> ParseAvailabilitySpec();
   ExprResult ParseAvailabilityCheckExpr(SourceLocation StartLoc);
 
+  void ParseExternalSourceSymbolAttribute(IdentifierInfo &ExternalSourceSymbol,
+                                          SourceLocation Loc,
+                                          ParsedAttributes &Attrs,
+                                          SourceLocation *EndLoc,
+                                          IdentifierInfo *ScopeName,
+                                          SourceLocation ScopeLoc,
+                                          AttributeList::Syntax Syntax);
+
   void ParseObjCBridgeRelatedAttribute(IdentifierInfo &ObjCBridgeRelated,
                                        SourceLocation ObjCBridgeRelatedLoc,
                                        ParsedAttributes &attrs,
Index: include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- include/clang/Basic/DiagnosticParseKinds.td
+++ include/clang/Basic/DiagnosticParseKinds.td
@@ -856,6 +856,12 @@
 def err_availability_query_repeated_star : Error<
   "'*' query has already been specified">;
 
+// External source symbol attribute
+def err_external_source_symbol_expected_keyword : Error<
+  "expected 'language', 'defined_in', or 'generated_declaration'">;
+def err_external_source_symbol_expected_language : Error<
+  "expected a source language , e.g., 'Swift'">;
+
 // Type safety attributes
 def err_type_safety_unknown_flag : Error<
   "invalid comparison flag %0; use 'layout_compatible' or 'must_be_null'">;
Index: include/clang/Basic/DiagnosticCommonKinds.td
===================================================================
--- include/clang/Basic/DiagnosticCommonKinds.td
+++ include/clang/Basic/DiagnosticCommonKinds.td
@@ -45,7 +45,8 @@
   "must end with ':'">;
 def err_expected_string_literal : Error<"expected string literal "
   "%select{in %1|for diagnostic message in static_assert|"
-          "for optional message in 'availability' attribute}0">;
+          "for optional message in 'availability' attribute|"
+          "for source container name in 'external_source_symbol' attribute}0">;
 def err_invalid_string_udl : Error<
   "string literal with user-defined suffix cannot be used here">;
 def err_invalid_character_udl : Error<
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -960,6 +960,60 @@
   }];
 }
 
+def ExternalSourceSymbolDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+The ``external_source_symbol`` attribute specifies that a declaration originates
+from an external source and describes the nature of that source.
+
+The fact that Clang is capable of recognizing declarations that were defined
+externally can be used to provide better tooling support for mixed-language
+projects or projects that rely on auto-generated code. For instance, an IDE that
+uses Clang and that supports mixed-language projects can use this attribute to
+provide a correct 'jump-to-definition' feature. For a concrete example,
+consider a protocol that's defined in a Swift file:
+
+.. code-block:: swift
+
+  @objc public protocol SwiftProtocol {
+    func method()
+  }
+
+This protocol can be used from Objective-C code by including a header file that
+was generated by the Swift compiler. The declarations in that header can use
+the ``external_source_symbol`` attribute to make Clang aware of the fact
+that ``SwiftProtocol`` actually originates from a Swift module:
+
+.. code-block:: objc
+
+  __attribute__((external_source_symbol(language=Swift,defined_in="module")))
+  @protocol SwiftProtocol
+  @required
+  - (void) method;
+  @end
+
+Consequently, when 'jump-to-definition' is performed at a location that
+references ``SwiftProtocol``, the IDE can jump to the original definition in
+the Swift source file rather than jumping to the Objective-C declaration in the
+auto-generated header file.
+
+The ``external_source_symbol`` attribute is a comma-separated list that includes
+clauses that describe the origin and the nature of the particular declaration.
+Those clauses can be:
+
+language=\ *identifier*
+  The source language in which this declaration was defined.
+
+defined_in=\ *string-literal*
+  The name of the source container in which the declaration was defined. The
+  exact definition of source container is language-specific, e.g. Swift's
+  source containers are modules, so ``defined_in`` should specify the Swift
+  module name.
+
+generated_declaration
+  This declaration was automatically generated by some tool.
+  }];
+}
 
 def RequireConstantInitDocs : Documentation {
   let Category = DocCatVariable;
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -527,6 +527,17 @@
   let Documentation = [AvailabilityDocs];
 }
 
+def ExternalSourceSymbol : InheritableAttr {
+  let Spellings = [GNU<"external_source_symbol">,
+                   CXX11<"gnu", "external_source_symbol">];
+  let Args = [IdentifierArgument<"language", 1>,
+              StringArgument<"definedIn", 1>,
+              BoolArgument<"generatedDeclaration", 1>];
+  let HasCustomParsing = 1;
+//  let Subjects = SubjectList<[Named]>;
+  let Documentation = [ExternalSourceSymbolDocs];
+}
+
 def Blocks : InheritableAttr {
   let Spellings = [GNU<"blocks">];
   let Args = [EnumArgument<"Type", "BlockType", ["byref"], ["ByRef"]>];
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to