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"]>]; 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/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/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/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 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: 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' '=' +/// '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: 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/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(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(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 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: 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: 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/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; + }; +}