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<"clang", "external_source_symbol">]; + let Args = [StringArgument<"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,63 @@ }]; } +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=\ *string-literal* + The name of 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. + +The clauses can be specified in any order. The clauses that are listed above are +all optional, but the attribute has to have at least one clause. + }]; +} 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,9 @@ "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 %select{language|source container}1 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 @@ -859,6 +859,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_duplicate_clause : Error< + "duplicate %0 clause in an 'external_source_symbol' attribute">; + // 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/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2744,7 +2744,8 @@ "|functions, methods, enums, and classes" "|structs, classes, variables, functions, and inline namespaces" "|variables, functions, methods, types, enumerations, enumerators, labels, and non-static data members" - "|classes and enumerations}1">, + "|classes and enumerations" + "|named declarations}1">, InGroup; def err_attribute_wrong_decl_type : Error; def warn_type_attribute_wrong_type : Warning< 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: include/clang/Sema/AttributeList.h =================================================================== --- include/clang/Sema/AttributeList.h +++ include/clang/Sema/AttributeList.h @@ -927,6 +927,7 @@ ExpectedStructClassVariableFunctionOrInlineNamespace, ExpectedForMaybeUnused, ExpectedEnumOrClass, + ExpectedNamedDecl, }; } // end namespace clang 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,119 @@ 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' '=' +/// '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.expectAndConsume()) + 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"); + } + + ExprResult Language; + bool HasLanguage = false; + ExprResult DefinedInExpr; + bool HasDefinedIn = false; + 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; + } + + SourceLocation KeywordLoc = Tok.getLocation(); + IdentifierInfo *Keyword = Tok.getIdentifierInfo(); + if (Keyword == Ident_generated_declaration) { + if (GeneratedDeclaration) { + Diag(Tok, diag::err_external_source_symbol_duplicate_clause) << Keyword; + SkipUntil(tok::r_paren, StopAtSemi); + return; + } + 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; + } + + bool HadLanguage = HasLanguage, HadDefinedIn = HasDefinedIn; + if (Keyword == Ident_language) + HasLanguage = 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); + SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch); + continue; + } + if (Keyword == Ident_language) { + if (HadLanguage) { + Diag(KeywordLoc, diag::err_external_source_symbol_duplicate_clause) + << Keyword; + ParseStringLiteralExpression(); + continue; + } + Language = ParseStringLiteralExpression(); + } else { + assert(Keyword == Ident_defined_in); + if (HadDefinedIn) { + Diag(KeywordLoc, diag::err_external_source_symbol_duplicate_clause) + << Keyword; + ParseStringLiteralExpression(); + continue; + } + DefinedInExpr = ParseStringLiteralExpression(); + } + } while (TryConsumeToken(tok::comma)); + + // Closing ')'. + if (T.consumeClose()) + return; + if (EndLoc) + *EndLoc = T.getCloseLocation(); + + ArgsUnion Args[] = {Language.get(), 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/ParseDeclCXX.cpp =================================================================== --- lib/Parse/ParseDeclCXX.cpp +++ lib/Parse/ParseDeclCXX.cpp @@ -3786,6 +3786,15 @@ } } +/// \return True if the given clang attribute should be parsed like an +/// attribute in the 'gnu' scope. +static bool isClangAttributeWithCustomParsing(IdentifierInfo &AttrName, + IdentifierInfo &ScopeName) { + if (ScopeName.getName() != "clang") + return false; + return AttrName.isStr("external_source_symbol"); +} + /// ParseCXX11AttributeArgs -- Parse a C++11 attribute-argument-clause. /// /// [C++11] attribute-argument-clause: @@ -3819,7 +3828,8 @@ return false; } - if (ScopeName && ScopeName->getName() == "gnu") + if (ScopeName && (ScopeName->getName() == "gnu" || + isClangAttributeWithCustomParsing(*AttrName, *ScopeName))) // GNU-scoped attributes have some special cases to handle GNU-specific // behaviors. ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, 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,32 @@ } } +static void handleExternalSourceSymbolAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) + return; + assert(checkAttributeAtMostNumArgs(S, Attr, 3) && + "Invalid number of arguments in an external_source_symbol attribute"); + + if (!isa(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedNamedDecl; + return; + } + + StringRef Language; + if (const auto *SE = dyn_cast_or_null(Attr.getArgAsExpr(0))) + Language = SE->getString(); + StringRef DefinedIn; + if (const auto *SE = dyn_cast_or_null(Attr.getArgAsExpr(1))) + DefinedIn = SE->getString(); + bool IsGeneratedDeclaration = Attr.getArgAsIdent(2) != nullptr; + + 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 +5830,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/Misc/ast-dump-attr.cpp =================================================================== --- test/Misc/ast-dump-attr.cpp +++ test/Misc/ast-dump-attr.cpp @@ -154,3 +154,28 @@ struct __attribute__((objc_bridge_related(NSParagraphStyle,,))) TestBridgedRef; // CHECK: CXXRecordDecl{{.*}} struct TestBridgedRef // CHECK-NEXT: ObjCBridgeRelatedAttr{{.*}} NSParagraphStyle + +void TestExternalSourceSymbolAttr1() +__attribute__((external_source_symbol(language="Swift", defined_in="module", generated_declaration))); +// CHECK: FunctionDecl{{.*}} TestExternalSourceSymbolAttr1 +// CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Swift" "module" GeneratedDeclaration + +void TestExternalSourceSymbolAttr2() +__attribute__((external_source_symbol(defined_in="module", language="Swift"))); +// CHECK: FunctionDecl{{.*}} TestExternalSourceSymbolAttr2 +// CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Swift" "module"{{$}} + +void TestExternalSourceSymbolAttr3() +__attribute__((external_source_symbol(generated_declaration, language="Objective-C++", defined_in="module"))); +// CHECK: FunctionDecl{{.*}} TestExternalSourceSymbolAttr3 +// CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Objective-C++" "module" GeneratedDeclaration + +void TestExternalSourceSymbolAttr4() +__attribute__((external_source_symbol(defined_in="Some external file.cs", generated_declaration, language="C Sharp"))); +// CHECK: FunctionDecl{{.*}} TestExternalSourceSymbolAttr4 +// CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "C Sharp" "Some external file.cs" GeneratedDeclaration + +void TestExternalSourceSymbolAttr5() +__attribute__((external_source_symbol(generated_declaration, defined_in="module", language="Swift"))); +// CHECK: FunctionDecl{{.*}} TestExternalSourceSymbolAttr5 +// CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Swift" "module" GeneratedDeclaration Index: test/Parser/attr-external-source-symbol-cxx11.cpp =================================================================== --- /dev/null +++ test/Parser/attr-external-source-symbol-cxx11.cpp @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// expected-no-diagnostics + +[[clang::external_source_symbol(language="Swift", defined_in="module")]] +void function() { } Index: test/Parser/attr-external-source-symbol.m =================================================================== --- /dev/null +++ test/Parser/attr-external-source-symbol.m @@ -0,0 +1,84 @@ +// 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 string literal for language name in 'external_source_symbol' attribute}} +void f6() +__attribute__((external_source_symbol(defined_in=20))); // expected-error {{expected string literal for source container name in 'external_source_symbol' attribute}} + +void f7() +__attribute__((external_source_symbol(generated_declaration, generated_declaration))); // expected-error {{duplicate 'generated_declaration' clause in an 'external_source_symbol' attribute}} +void f8() +__attribute__((external_source_symbol(language="Swift", language="Swift"))); // expected-error {{duplicate 'language' clause in an 'external_source_symbol' attribute}} +void f9() +__attribute__((external_source_symbol(defined_in="module", language="Swift", defined_in="foo"))); // expected-error {{duplicate 'defined_in' clause in an 'external_source_symbol' attribute}} + +void f10() +__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}} + +void f11() +__attribute__((external_source_symbol(language="Objective-C++", defined_in="Some file with spaces"))); + +void f12() +__attribute__((external_source_symbol(language="C Sharp", defined_in="file:////Hello world with spaces. cs"))); + +void f13() +__attribute__((external_source_symbol(language=Swift))); // expected-error {{expected string literal for language name in 'external_source_symbol' attribute}} + +void f14() +__attribute__((external_source_symbol(=))); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}} + +void f15() +__attribute__((external_source_symbol(="Swift"))); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}} + +void f16() +__attribute__((external_source_symbol("Swift", "module", generated_declaration))); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}} + +void f17() +__attribute__((external_source_symbol(language="Swift", "generated_declaration"))); // expected-error {{expected 'language', 'defined_in', or 'generated_declaration'}} + +void f18() +__attribute__((external_source_symbol(language= =))); // expected-error {{expected string literal for language name in 'external_source_symbol' attribute}} + +void f19() +__attribute__((external_source_symbol(defined_in="module" language="swift"))); // expected-error {{expected ')'}} expected-note {{to match this '('}} + +void f20() +__attribute__((external_source_symbol(defined_in="module" language="swift" generated_declaration))); // expected-error {{expected ')'}} expected-note {{to match this '('}} + +void f21() +__attribute__((external_source_symbol(defined_in= language="swift"))); // expected-error {{expected string literal for source container name in 'external_source_symbol' attribute}} + +void f22() +__attribute__((external_source_symbol)); // expected-error {{'external_source_symbol' attribute takes at least 1 argument}} + +void f23() +__attribute__((external_source_symbol(defined_in=, language="swift" generated_declaration))); // expected-error {{expected string literal for source container name in 'external_source_symbol' attribute}} expected-error{{expected ')'}} expected-note{{to match this '('}} + +void f24() +__attribute__((external_source_symbol(language = generated_declaration))); // expected-error {{expected string literal for language name in 'external_source_symbol' attribute}} + +void f25() +__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() +__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'}} Index: test/Sema/attr-external-source-symbol.c =================================================================== --- /dev/null +++ test/Sema/attr-external-source-symbol.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -verify %s + +void threeClauses() __attribute__((external_source_symbol(language="Swift", defined_in="module", generated_declaration))); + +void twoClauses() __attribute__((external_source_symbol(language="Swift", defined_in="module"))); + +void fourClauses() __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 oneClause() __attribute__((external_source_symbol(generated_declaration))); + +void noArguments() +__attribute__((external_source_symbol)); // expected-error {{'external_source_symbol' attribute takes at least 1 argument}} + +void namedDeclsOnly() { + int (^block)(void) = ^ (void) + __attribute__((external_source_symbol(language="Swift"))) { // expected-warning {{'external_source_symbol' attribute only applies to named declarations}} + return 1; + }; +}