From fb59293fe160556eb43e96fe70bb373b905237d7 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Sat, 23 Nov 2019 23:28:31 -0800 Subject: [PATCH 1/2] [Parse] Add a cType key to the convention attribute. For example, one may write a calling convention like convention(c, cType: "void *(void)") for a procedure that takes no arguments. --- include/swift/AST/Attr.h | 30 ++++- include/swift/AST/DiagnosticsParse.def | 7 + include/swift/Parse/Parser.h | 4 + lib/AST/TypeRepr.cpp | 5 +- lib/Parse/ParseDecl.cpp | 140 +++++++++++++------- lib/ParseSIL/ParseSIL.cpp | 3 +- lib/Sema/TypeCheckType.cpp | 15 ++- test/Parse/c_function_pointers.swift | 8 ++ test/SourceKit/CursorInfo/cursor_info.swift | 24 ++-- 9 files changed, 163 insertions(+), 73 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 776b22a25b259..1a3b0e0f8f26d 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -64,8 +64,24 @@ class TypeAttributes { /// AtLoc - This is the location of the first '@' in the attribute specifier. /// If this is an empty attribute specifier, then this will be an invalid loc. SourceLoc AtLoc; - Optional convention = None; - Optional conventionWitnessMethodProtocol = None; + + struct Convention { + StringRef Name = {}; + StringRef WitnessMethodProtocol = {}; + StringRef ClangType = {}; + // Carry the source location for diagnostics. + SourceLoc ClangTypeLoc = {}; + + /// Convenience factory function to create a Swift convention. + /// + /// Don't use this function if you are creating a C convention as you + /// probably need a ClangType field as well. + static Convention makeSwiftConvention(StringRef name) { + return {name, "", "", {}}; + } + }; + + Optional ConventionArguments; // Indicates whether the type's '@differentiable' attribute has a 'linear' // argument. @@ -134,8 +150,14 @@ class TypeAttributes { return true; } - bool hasConvention() const { return convention.hasValue(); } - StringRef getConvention() const { return *convention; } + bool hasConvention() const { return ConventionArguments.hasValue(); } + + /// Returns the primary calling convention string. + /// + /// Note: For C conventions, this may not represent the full convention. + StringRef getConventionName() const { + return ConventionArguments.getValue().Name; + } bool hasOwnership() const { return getOwnership() != ReferenceOwnership::Strong; diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 5d452f42a4224..f6b492a0481b1 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1404,6 +1404,13 @@ ERROR(convention_attribute_expected_name,none, "expected convention name identifier in 'convention' attribute", ()) ERROR(convention_attribute_expected_rparen,none, "expected ')' after convention name for 'convention' attribute", ()) +ERROR(convention_attribute_ctype_expected_label,none, + "expected 'cType' label in 'convention' attribute", ()) +ERROR(convention_attribute_ctype_expected_colon,none, + "expected ':' after 'cType' for 'convention' attribute", ()) +ERROR(convention_attribute_ctype_expected_string,none, + "expected string literal containing clang type for 'cType' in " + "'convention' attribute", ()) ERROR(convention_attribute_witness_method_expected_colon,none, "expected ':' after 'witness_method' for 'convention' attribute", ()) ERROR(convention_attribute_witness_method_expected_protocol,none, diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index abf1af7580048..d3f1f22aca09e 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1017,6 +1017,10 @@ class Parser { bool parseTypeAttributeListPresent(ParamDecl::Specifier &Specifier, SourceLoc &SpecifierLoc, TypeAttributes &Attributes); + + bool parseConventionAttributeInternal(bool justChecking, + TypeAttributes::Convention &convention); + bool parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, bool justChecking = false); diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index c6168a918ac0a..44d54ba5e1633 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -312,10 +312,11 @@ void AttributedTypeRepr::printAttrs(ASTPrinter &Printer, if (hasAttr(TAK_thick)) Printer.printSimpleAttr("@thick") << " "; - if (hasAttr(TAK_convention) && Attrs.convention.hasValue()) { + if (hasAttr(TAK_convention) && Attrs.hasConvention()) { + // TODO: (Varun) Print clang type here! Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); - Printer << "(" << Attrs.convention.getValue() << ")"; + Printer << "(" << Attrs.getConvention() << ")"; Printer.printStructurePost(PrintStructureKind::BuiltinAttribute); Printer << " "; } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 436b9c1bb6b03..ff458957474a3 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2252,6 +2252,91 @@ static bool parseDifferentiableAttributeArgument(Parser &P, return false; } +/// Parse the inside of a convention attribute '(...)'. +/// +/// The '@convention' prefix should've been parsed by the caller. +/// See `Parser::parseTypeAttribute` for the justChecking argument. +/// +/// Returns true if there was an error. +bool Parser::parseConventionAttributeInternal( + bool justChecking, TypeAttributes::Convention &convention) { + SourceLoc LPLoc; + if (!consumeIfNotAtStartOfLine(tok::l_paren)) { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_expected_lparen); + return true; + } + + if (Tok.isNot(tok::identifier)) { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_expected_name); + return true; + } + + convention.Name = Tok.getText(); + consumeToken(tok::identifier); + + // Consume extra (optional) ', cType: " blah blah "' + if (consumeIf(tok::comma)) { + if (Tok.isNot(tok::identifier)) { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_ctype_expected_label); + return true; + } + auto cTypeLabel = Tok.getText(); + consumeToken(tok::identifier); + if (cTypeLabel != "cType") { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_ctype_expected_label); + return true; + } + if (!consumeIf(tok::colon)) { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_ctype_expected_colon); + return true; + } + if (Tok.isNot(tok::string_literal)) { + if (!justChecking) + diagnose(Tok, diag::convention_attribute_ctype_expected_string); + return true; + } + if (auto ty = getStringLiteralIfNotInterpolated(Tok.getLoc(), "(C type)")) { + convention.ClangType = ty.getValue(); + convention.ClangTypeLoc = Tok.getLoc(); + } + consumeToken(tok::string_literal); + } + + if (convention.Name == "witness_method") { + if (!consumeIf(tok::colon)) { + if (!justChecking) + diagnose(Tok, + diag::convention_attribute_witness_method_expected_colon); + return true; + } + if (Tok.isNot(tok::identifier)) { + if (!justChecking) + diagnose(Tok, + diag::convention_attribute_witness_method_expected_protocol); + return true; + } + + convention.WitnessMethodProtocol = Tok.getText(); + consumeToken(tok::identifier); + } + + // Parse the ')'. We can't use parseMatchingToken if we're in + // just-checking mode. + if (justChecking && Tok.isNot(tok::r_paren)) + return true; + + SourceLoc RPLoc; + parseMatchingToken(tok::r_paren, RPLoc, + diag::convention_attribute_expected_rparen, + LPLoc); + return false; +} + /// \verbatim /// attribute-type: /// 'noreturn' @@ -2325,57 +2410,17 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, StringRef Text = Tok.getText(); consumeToken(); - StringRef conventionName; - StringRef witnessMethodProtocol; - + TypeAttributes::Convention convention; if (attr == TAK_convention) { - SourceLoc LPLoc; - if (!consumeIfNotAtStartOfLine(tok::l_paren)) { - if (!justChecking) - diagnose(Tok, diag::convention_attribute_expected_lparen); - return true; - } - - if (Tok.isNot(tok::identifier)) { - if (!justChecking) - diagnose(Tok, diag::convention_attribute_expected_name); + bool failedToParse = + parseConventionAttributeInternal(justChecking, convention); + if (failedToParse) { + if (Tok.is(tok::r_paren)) + consumeToken(); return true; } - - conventionName = Tok.getText(); - consumeToken(tok::identifier); - - if (conventionName == "witness_method") { - if (Tok.isNot(tok::colon)) { - if (!justChecking) - diagnose(Tok, - diag::convention_attribute_witness_method_expected_colon); - return true; - } - consumeToken(tok::colon); - if (Tok.isNot(tok::identifier)) { - if (!justChecking) - diagnose(Tok, - diag::convention_attribute_witness_method_expected_protocol); - return true; - } - - witnessMethodProtocol = Tok.getText(); - consumeToken(tok::identifier); - } - - // Parse the ')'. We can't use parseMatchingToken if we're in - // just-checking mode. - if (justChecking && Tok.isNot(tok::r_paren)) - return true; - - SourceLoc RPLoc; - parseMatchingToken(tok::r_paren, RPLoc, - diag::convention_attribute_expected_rparen, - LPLoc); } - // In just-checking mode, we only need to consume the tokens, and we don't // want to do any other analysis. if (justChecking) @@ -2474,8 +2519,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, SourceLoc AtLoc, // Convention attribute. case TAK_convention: - Attributes.convention = conventionName; - Attributes.conventionWitnessMethodProtocol = witnessMethodProtocol; + Attributes.ConventionArguments = convention; break; case TAK__opaqueReturnTypeOf: { diff --git a/lib/ParseSIL/ParseSIL.cpp b/lib/ParseSIL/ParseSIL.cpp index f4a587686b139..8585b3dae2db0 100644 --- a/lib/ParseSIL/ParseSIL.cpp +++ b/lib/ParseSIL/ParseSIL.cpp @@ -1272,7 +1272,8 @@ bool SILParser::parseSILType(SILType &Result, if (IsFuncDecl && !attrs.has(TAK_convention)) { // Use a random location. attrs.setAttr(TAK_convention, P.PreviousLoc); - attrs.convention = "thin"; + attrs.ConventionArguments = + TypeAttributes::Convention::makeSwiftConvention("thin"); } ParserResult TyR = P.parseType(diag::expected_sil_type, diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index ddcd749cbc010..97e940585f4f9 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -2165,7 +2165,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, if (!attrs.hasConvention()) { rep = SILFunctionType::Representation::Thick; } else { - auto convention = attrs.getConvention(); + auto convention = attrs.getConventionName(); // SIL exposes a greater number of conventions than Swift source. auto parsedRep = llvm::StringSwitch>( @@ -2182,14 +2182,15 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, .Default(None); if (!parsedRep) { diagnose(attrs.getLoc(TAK_convention), - diag::unsupported_sil_convention, attrs.getConvention()); + diag::unsupported_sil_convention, attrs.getConventionName()); rep = SILFunctionType::Representation::Thin; } else { rep = *parsedRep; } if (rep == SILFunctionType::Representation::WitnessMethod) { - auto protocolName = *attrs.conventionWitnessMethodProtocol; + auto protocolName = + attrs.ConventionArguments.getValue().WitnessMethodProtocol; witnessMethodProtocol = new (Context) SimpleIdentTypeRepr( SourceLoc(), Context.getIdentifier(protocolName)); } @@ -2220,7 +2221,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, if (attrs.hasConvention()) { auto parsedRep = llvm::StringSwitch>( - attrs.getConvention()) + attrs.getConventionName()) .Case("swift", FunctionType::Representation::Swift) .Case("block", FunctionType::Representation::Block) .Case("thin", FunctionType::Representation::Thin) @@ -2228,7 +2229,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, .Default(None); if (!parsedRep) { diagnose(attrs.getLoc(TAK_convention), diag::unsupported_convention, - attrs.getConvention()); + attrs.getConventionName()); rep = FunctionType::Representation::Swift; } else { rep = *parsedRep; @@ -2239,7 +2240,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, rep == FunctionType::Representation::Block) { diagnose(attrs.getLoc(TAK_convention), diag::invalid_autoclosure_and_convention_attributes, - attrs.getConvention()); + attrs.getConventionName()); attrs.clearAttribute(TAK_convention); } } @@ -2355,7 +2356,7 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs, // Remove the function attributes from the set so that we don't diagnose. for (auto i : FunctionAttrs) attrs.clearAttribute(i); - attrs.convention = None; + attrs.ConventionArguments = None; } // In SIL, handle @opened (n), which creates an existential archetype. diff --git a/test/Parse/c_function_pointers.swift b/test/Parse/c_function_pointers.swift index 429b255b7d94a..3d51e63098b5f 100644 --- a/test/Parse/c_function_pointers.swift +++ b/test/Parse/c_function_pointers.swift @@ -47,3 +47,11 @@ if true { func genericFunc(_ t: T) -> T { return t } let f: @convention(c) (Int) -> Int = genericFunc // expected-error{{cannot be formed from a reference to a generic function}} + +func ct1() -> () { print("") } + +let ct1ref0 : @convention(c, cType: "void *(void)") () -> () = ct1 +let ct1ref1 : @convention(c, cType: "void *(void)") = ct1 // expected-error{{expected type}} +let ct1ref2 : @convention(c, ) () -> () = ct1 // expected-error{{expected 'cType' label in 'convention' attribute}} +let ct1ref3 : @convention(c, cType) () -> () = ct1 // expected-error{{expected ':' after 'cType' for 'convention' attribute}} +let ct1ref4 : @convention(c, cType: ) () -> () = ct1 // expected-error{{expected string literal containing clang type for 'cType' in 'convention' attribute}} diff --git a/test/SourceKit/CursorInfo/cursor_info.swift b/test/SourceKit/CursorInfo/cursor_info.swift index 2188364c1742c..f95950586a6c1 100644 --- a/test/SourceKit/CursorInfo/cursor_info.swift +++ b/test/SourceKit/CursorInfo/cursor_info.swift @@ -202,6 +202,7 @@ func convention1(_: @convention(thick) ()->()) {} func convention2(_: @convention(thin) ()->()) {} func convention3(_: @convention(block) ()->()) {} func convention4(_: @convention(c) ()->()) {} +func convention4a(_: @convention(c, cType: "void *(void *, int[])") () -> () {} func convention5(_: @convention(method) ()->()) {} func convention6(_: @convention(objc_method) ()->()) {} func convention7(_: @convention(witness_method: P1) ()->()) {} @@ -717,46 +718,47 @@ enum E7: String { // RUN: %sourcekitd-test -req=cursor -pos=205:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s // RUN: %sourcekitd-test -req=cursor -pos=206:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s // RUN: %sourcekitd-test -req=cursor -pos=207:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s +// RUN: %sourcekitd-test -req=cursor -pos=208:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s // CHECK86: @convention({{[a-z_]*}}) -// RUN: %sourcekitd-test -req=cursor -pos=212:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK87 %s -// CHECK87: source.lang.swift.decl.struct (212:8-212:26) +// RUN: %sourcekitd-test -req=cursor -pos=213:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK87 %s +// CHECK87: source.lang.swift.decl.struct (213:8-213:26) // CHECK87-NEXT: HasLocalizationKey // CHECK87-NEXT: s:11cursor_info18HasLocalizationKeyV // CHECK87-NEXT: HasLocalizationKey.Type // CHECK87-NEXT: $s // CHECK87-NEXT: struct HasLocalizationKey // CHECK87-NEXT: struct HasLocalizationKey -// CHECK87-NEXT: HasLocalizationKeys:11cursor_info18HasLocalizationKeyVstruct HasLocalizationKeyBrief. +// CHECK87-NEXT: HasLocalizationKeys:11cursor_info18HasLocalizationKeyVstruct HasLocalizationKeyBrief. // CHECK87-NEXT: ABC -// RUN: %sourcekitd-test -req=cursor -pos=215:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK88 %s -// CHECK88: source.lang.swift.decl.function.free (215:6-215:27) +// RUN: %sourcekitd-test -req=cursor -pos=216:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK88 %s +// CHECK88: source.lang.swift.decl.function.free (216:6-216:27) // CHECK88-NEXT: hasLocalizationKey2 // CHECK88-NEXT: s:11cursor_info19hasLocalizationKey2yyF // CHECK88-NEXT: () -> () // CHECK88-NEXT: $s // CHECK88-NEXT: func hasLocalizationKey2() // CHECK88-NEXT: func hasLocalizationKey2() -// CHECK88-NEXT: hasLocalizationKey2()s:11cursor_info19hasLocalizationKey2yyFfunc hasLocalizationKey2()hasLocalizationKey2()s:11cursor_info19hasLocalizationKey2yyFfunc hasLocalizationKey2()ABC -// RUN: %sourcekitd-test -req=cursor -pos=218:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK89 %s +// RUN: %sourcekitd-test -req=cursor -pos=219:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK89 %s // CHECK89: func funcWithNestedEscaping(a: (@escaping () -> ()) -> ()) // CHECK89-NEXT: (@escaping () -> ()) -> () -// RUN: %sourcekitd-test -req=cursor -pos=221:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK90 %s +// RUN: %sourcekitd-test -req=cursor -pos=222:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK90 %s // CHECK90: typealias typeWithNestedAutoclosure = (@autoclosure () -> ()) -> () // CHECK90-NEXT: @autoclosure () -> () -// RUN: %sourcekitd-test -req=cursor -pos=223:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK91 %s +// RUN: %sourcekitd-test -req=cursor -pos=224:11 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK91 %s // CHECK91: typealias GenericAlias<T, U> = MyAlias<T, U> where T : P1 // CHECK91-NEXT: typealias GenericAlias<T, U> = MyAlias<T, U> where T : P1 -// RUN: %sourcekitd-test -req=cursor -pos=226:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK92 %s +// RUN: %sourcekitd-test -req=cursor -pos=227:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK92 %s // CHECK92: case a = "\u{1B}" // CHECK92-NEXT: case a = "\u{1B}" -// RUN: %sourcekitd-test -req=cursor -pos=227:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK93 %s +// RUN: %sourcekitd-test -req=cursor -pos=228:10 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK93 %s // CHECK93: case b = "f" // CHECK93-NEXT: case b = "f" From 7d297bc678a0ced36b1bfd4fb2769718b6330cf7 Mon Sep 17 00:00:00 2001 From: Varun Gandhi Date: Mon, 25 Nov 2019 16:46:48 -0800 Subject: [PATCH 2/2] Print the full calling convention; include the Clang type if applicable. --- include/swift/AST/Attr.h | 5 +++++ lib/AST/Attr.cpp | 13 +++++++++++++ lib/AST/TypeRepr.cpp | 5 +++-- test/SourceKit/CursorInfo/cursor_info.swift | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 1a3b0e0f8f26d..2c68b18b57421 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -159,6 +159,11 @@ class TypeAttributes { return ConventionArguments.getValue().Name; } + /// Show the string enclosed between @convention(..)'s parentheses. + /// + /// For example, @convention(foo, bar) will give the string "foo, bar". + void getConventionArguments(SmallVectorImpl &buffer) const; + bool hasOwnership() const { return getOwnership() != ReferenceOwnership::Strong; } diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 3fdb973a67805..fd03191cd7c2c 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -26,6 +26,7 @@ #include "swift/AST/Types.h" #include "swift/AST/ParameterList.h" #include "swift/Basic/Defer.h" +#include "swift/Basic/QuotedString.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/ErrorHandling.h" @@ -83,6 +84,18 @@ StringRef swift::getAccessLevelSpelling(AccessLevel value) { llvm_unreachable("Unhandled AccessLevel in switch."); } +void TypeAttributes::getConventionArguments(SmallVectorImpl &buf) const { + llvm::raw_svector_ostream stream(buf); + auto &convention = ConventionArguments.getValue(); + stream << convention.Name; + if (!convention.WitnessMethodProtocol.empty()) { + stream << ": " << convention.WitnessMethodProtocol; + return; + } + if (!convention.ClangType.empty()) + stream << ", cType: " << QuotedString(convention.ClangType); +} + /// Given a name like "autoclosure", return the type attribute ID that /// corresponds to it. This returns TAK_Count on failure. /// diff --git a/lib/AST/TypeRepr.cpp b/lib/AST/TypeRepr.cpp index 44d54ba5e1633..c6252349f4b88 100644 --- a/lib/AST/TypeRepr.cpp +++ b/lib/AST/TypeRepr.cpp @@ -313,10 +313,11 @@ void AttributedTypeRepr::printAttrs(ASTPrinter &Printer, Printer.printSimpleAttr("@thick") << " "; if (hasAttr(TAK_convention) && Attrs.hasConvention()) { - // TODO: (Varun) Print clang type here! Printer.callPrintStructurePre(PrintStructureKind::BuiltinAttribute); Printer.printAttrName("@convention"); - Printer << "(" << Attrs.getConvention() << ")"; + SmallString<32> convention; + Attrs.getConventionArguments(convention); + Printer << "(" << convention << ")"; Printer.printStructurePost(PrintStructureKind::BuiltinAttribute); Printer << " "; } diff --git a/test/SourceKit/CursorInfo/cursor_info.swift b/test/SourceKit/CursorInfo/cursor_info.swift index f95950586a6c1..bcb241b50cd54 100644 --- a/test/SourceKit/CursorInfo/cursor_info.swift +++ b/test/SourceKit/CursorInfo/cursor_info.swift @@ -719,7 +719,7 @@ enum E7: String { // RUN: %sourcekitd-test -req=cursor -pos=206:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s // RUN: %sourcekitd-test -req=cursor -pos=207:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s // RUN: %sourcekitd-test -req=cursor -pos=208:6 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK86 %s -// CHECK86: @convention({{[a-z_]*}}) +// CHECK86: @convention({{[a-z_]*((, [a-zA-Z_]*: "[^&]*")|(: [a-zA-Z0-9_]*))?}}) // RUN: %sourcekitd-test -req=cursor -pos=213:8 %s -- -F %S/../Inputs/libIDE-mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK87 %s // CHECK87: source.lang.swift.decl.struct (213:8-213:26)