From 0c9a37fcf45bfd8b56760df74991af6c42b218fa Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 7 Feb 2020 13:40:36 -0800 Subject: [PATCH 01/37] [AST] Extend TupleExpr to support multiple trailing closures --- include/swift/AST/Expr.h | 41 ++++++++++++++++++++++++------- lib/AST/Expr.cpp | 52 +++++++++++++++++++++++++++++----------- 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 94bda5f12cf07..bfecada3c645b 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -200,10 +200,7 @@ class alignas(8) Expr { FieldNo : 32 ); - SWIFT_INLINE_BITFIELD_FULL(TupleExpr, Expr, 1+1+1+32, - /// Whether this tuple has a trailing closure. - HasTrailingClosure : 1, - + SWIFT_INLINE_BITFIELD_FULL(TupleExpr, Expr, 1+1+32, /// Whether this tuple has any labels. HasElementNames : 1, @@ -2069,6 +2066,11 @@ class TupleExpr final : public Expr, SourceLoc LParenLoc; SourceLoc RParenLoc; + SourceLoc TrailingBlockLBrace; + SourceLoc TrailingBlockRBrace; + + Optional FirstTrailingArgumentAt; + size_t numTrailingObjects(OverloadToken) const { return getNumElements(); } @@ -2095,11 +2097,13 @@ class TupleExpr final : public Expr, return { getTrailingObjects(), getNumElements() }; } - TupleExpr(SourceLoc LParenLoc, ArrayRef SubExprs, - ArrayRef ElementNames, + TupleExpr(SourceLoc LParenLoc, SourceLoc RParenLoc, + ArrayRef SubExprs, + ArrayRef ElementNames, ArrayRef ElementNameLocs, - SourceLoc RParenLoc, bool HasTrailingClosure, bool Implicit, - Type Ty); + SourceLoc TrailingLBrace, + SourceLoc TrailingRBrace, + Optional FirstTrailingArgumentAt, bool Implicit, Type Ty); public: /// Create a tuple. @@ -2111,6 +2115,17 @@ class TupleExpr final : public Expr, SourceLoc RParenLoc, bool HasTrailingClosure, bool Implicit, Type Ty = Type()); + static TupleExpr *create(ASTContext &ctx, + SourceLoc LParenLoc, + SourceLoc RParenLoc, + ArrayRef SubExprs, + ArrayRef ElementNames, + ArrayRef ElementNameLocs, + SourceLoc TrailingLBrace, + SourceLoc TrailingRBrace, + Optional FirstTrailingArgumentAt, + bool Implicit, Type Ty = Type()); + /// Create an empty tuple. static TupleExpr *createEmpty(ASTContext &ctx, SourceLoc LParenLoc, SourceLoc RParenLoc, bool Implicit); @@ -2125,7 +2140,15 @@ class TupleExpr final : public Expr, SourceRange getSourceRange() const; /// Whether this expression has a trailing closure as its argument. - bool hasTrailingClosure() const { return Bits.TupleExpr.HasTrailingClosure; } + bool hasTrailingClosure() const { + return FirstTrailingArgumentAt + ? *FirstTrailingArgumentAt == getNumElements() - 1 + : false; + } + + bool hasMultipleTrailingClosures() const { + return FirstTrailingArgumentAt ? !hasTrailingClosure() : false; + } /// Retrieve the elements of this tuple. MutableArrayRef getElements() { diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 62c8301fbccea..ff6913e34115b 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1267,14 +1267,18 @@ SourceRange TupleExpr::getSourceRange() const { } } -TupleExpr::TupleExpr(SourceLoc LParenLoc, ArrayRef SubExprs, +TupleExpr::TupleExpr(SourceLoc LParenLoc, SourceLoc RParenLoc, + ArrayRef SubExprs, ArrayRef ElementNames, ArrayRef ElementNameLocs, - SourceLoc RParenLoc, bool HasTrailingClosure, + SourceLoc TrailingLBrace, + SourceLoc TrailingRBrace, + Optional FirstTrailingArgumentAt, bool Implicit, Type Ty) : Expr(ExprKind::Tuple, Implicit, Ty), - LParenLoc(LParenLoc), RParenLoc(RParenLoc) { - Bits.TupleExpr.HasTrailingClosure = HasTrailingClosure; + LParenLoc(LParenLoc), RParenLoc(RParenLoc), + TrailingBlockLBrace(TrailingLBrace), TrailingBlockRBrace(TrailingRBrace), + FirstTrailingArgumentAt(FirstTrailingArgumentAt) { Bits.TupleExpr.HasElementNames = !ElementNames.empty(); Bits.TupleExpr.HasElementNameLocations = !ElementNameLocs.empty(); Bits.TupleExpr.NumElements = SubExprs.size(); @@ -1303,11 +1307,30 @@ TupleExpr::TupleExpr(SourceLoc LParenLoc, ArrayRef SubExprs, } TupleExpr *TupleExpr::create(ASTContext &ctx, - SourceLoc LParenLoc, + SourceLoc LParenLoc, ArrayRef SubExprs, - ArrayRef ElementNames, + ArrayRef ElementNames, ArrayRef ElementNameLocs, - SourceLoc RParenLoc, bool HasTrailingClosure, + SourceLoc RParenLoc, + bool HasTrailingClosure, + bool Implicit, Type Ty) { + Optional FirstTrailingArgumentAt = + HasTrailingClosure ? SubExprs.size() - 1 : Optional(); + + return create(ctx, LParenLoc, RParenLoc, SubExprs, ElementNames, + ElementNameLocs, SourceLoc(), SourceLoc(), + FirstTrailingArgumentAt, Implicit, Ty); +} + +TupleExpr *TupleExpr::create(ASTContext &ctx, + SourceLoc LParenLoc, + SourceLoc RParenLoc, + ArrayRef SubExprs, + ArrayRef ElementNames, + ArrayRef ElementNameLocs, + SourceLoc TrailingLBrace, + SourceLoc TrailingRBrace, + Optional FirstTrailingArgumentAt, bool Implicit, Type Ty) { assert(!Ty || isa(Ty.getPointer())); auto hasNonEmptyIdentifier = [](ArrayRef Ids) -> bool { @@ -1327,24 +1350,25 @@ TupleExpr *TupleExpr::create(ASTContext &ctx, ElementNames.size(), ElementNameLocs.size()); void *mem = ctx.Allocate(size, alignof(TupleExpr)); - return new (mem) TupleExpr(LParenLoc, SubExprs, ElementNames, ElementNameLocs, - RParenLoc, HasTrailingClosure, Implicit, Ty); + return new (mem) TupleExpr(LParenLoc, RParenLoc, SubExprs, ElementNames, + ElementNameLocs, TrailingLBrace, TrailingRBrace, + FirstTrailingArgumentAt, Implicit, Ty); } TupleExpr *TupleExpr::createEmpty(ASTContext &ctx, SourceLoc LParenLoc, SourceLoc RParenLoc, bool Implicit) { - return create(ctx, LParenLoc, { }, { }, { }, RParenLoc, - /*HasTrailingClosure=*/false, Implicit, + return create(ctx, LParenLoc, RParenLoc, {}, {}, {}, SourceLoc(), SourceLoc(), + /*FirstTrailingArgumentAt=*/None, Implicit, TupleType::getEmpty(ctx)); } TupleExpr *TupleExpr::createImplicit(ASTContext &ctx, ArrayRef SubExprs, ArrayRef ElementNames) { - return create(ctx, SourceLoc(), SubExprs, ElementNames, { }, SourceLoc(), - /*HasTrailingClosure=*/false, /*Implicit=*/true, Type()); + return create(ctx, SourceLoc(), SourceLoc(), SubExprs, ElementNames, {}, + SourceLoc(), SourceLoc(), /*FirstTrailingArgumentAt=*/None, + /*Implicit=*/true, Type()); } - ArrayExpr *ArrayExpr::create(ASTContext &C, SourceLoc LBracketLoc, ArrayRef Elements, ArrayRef CommaLocs, From 4650091efbeb0453aabe65a122f1e9b714b7259f Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 7 Feb 2020 14:52:51 -0800 Subject: [PATCH 02/37] [AST] Remove unused "trailing closure" argument from `totalSizeToAlloc` and `initializeCallArguments` --- include/swift/AST/TrailingCallArguments.h | 6 +-- lib/AST/Attr.cpp | 6 +-- lib/AST/Expr.cpp | 48 ++++++++++------------- 3 files changed, 25 insertions(+), 35 deletions(-) diff --git a/include/swift/AST/TrailingCallArguments.h b/include/swift/AST/TrailingCallArguments.h index 30383b68a9aa7..1c88c9b69db64 100644 --- a/include/swift/AST/TrailingCallArguments.h +++ b/include/swift/AST/TrailingCallArguments.h @@ -82,16 +82,14 @@ class TrailingCallArguments protected: /// Determine the total size to allocate. static size_t totalSizeToAlloc(ArrayRef argLabels, - ArrayRef argLabelLocs, - bool hasTrailingClosure) { + ArrayRef argLabelLocs) { return TrailingObjects::template totalSizeToAlloc( argLabels.size(), argLabelLocs.size()); } /// Initialize the actual call arguments. void initializeCallArguments(ArrayRef argLabels, - ArrayRef argLabelLocs, - bool hasTrailingClosure) { + ArrayRef argLabelLocs) { if (!argLabels.empty()) { std::uninitialized_copy(argLabels.begin(), argLabels.end(), this->template getTrailingObjects()); diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index dcb98d3ab31bd..7f6aa8d731ae4 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1850,8 +1850,7 @@ CustomAttr::CustomAttr(SourceLoc atLoc, SourceRange range, TypeLoc type, initContext(initContext) { hasArgLabelLocs = !argLabelLocs.empty(); numArgLabels = argLabels.size(); - initializeCallArguments(argLabels, argLabelLocs, - /*hasTrailingClosure=*/false); + initializeCallArguments(argLabels, argLabelLocs); } CustomAttr *CustomAttr::create(ASTContext &ctx, SourceLoc atLoc, TypeLoc type, @@ -1876,8 +1875,7 @@ CustomAttr *CustomAttr::create(ASTContext &ctx, SourceLoc atLoc, TypeLoc type, if (arg) range.End = arg->getEndLoc(); - size_t size = totalSizeToAlloc(argLabels, argLabelLocs, - /*hasTrailingClosure=*/false); + size_t size = totalSizeToAlloc(argLabels, argLabelLocs); void *mem = ctx.Allocate(size, alignof(CustomAttr)); return new (mem) CustomAttr(atLoc, range, type, initContext, arg, argLabels, argLabelLocs, implicit); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index ff6913e34115b..ce930382828d7 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1074,7 +1074,7 @@ ObjectLiteralExpr::ObjectLiteralExpr(SourceLoc PoundLoc, LiteralKind LitKind, Bits.ObjectLiteralExpr.NumArgLabels = argLabels.size(); Bits.ObjectLiteralExpr.HasArgLabelLocs = !argLabelLocs.empty(); Bits.ObjectLiteralExpr.HasTrailingClosure = hasTrailingClosure; - initializeCallArguments(argLabels, argLabelLocs, hasTrailingClosure); + initializeCallArguments(argLabels, argLabelLocs); } ObjectLiteralExpr * @@ -1091,7 +1091,7 @@ ObjectLiteralExpr::create(ASTContext &ctx, SourceLoc poundLoc, LiteralKind kind, &hasTrailingClosure, getType); - size_t size = totalSizeToAlloc(argLabels, argLabelLocs, hasTrailingClosure); + size_t size = totalSizeToAlloc(argLabels, argLabelLocs); void *memory = ctx.Allocate(size, alignof(ObjectLiteralExpr)); return new (memory) ObjectLiteralExpr(poundLoc, kind, arg, argLabels, @@ -1115,8 +1115,7 @@ ObjectLiteralExpr *ObjectLiteralExpr::create(ASTContext &ctx, rParenLoc, trailingClosure, implicit, argLabelsScratch, argLabelLocsScratch); - size_t size = totalSizeToAlloc(argLabels, argLabelLocs, - trailingClosure != nullptr); + size_t size = totalSizeToAlloc(argLabels, argLabelLocs); void *memory = ctx.Allocate(size, alignof(ObjectLiteralExpr)); return new (memory) ObjectLiteralExpr(poundLoc, kind, arg, argLabels, @@ -1479,7 +1478,7 @@ SubscriptExpr::SubscriptExpr(Expr *base, Expr *index, Bits.SubscriptExpr.NumArgLabels = argLabels.size(); Bits.SubscriptExpr.HasArgLabelLocs = !argLabelLocs.empty(); Bits.SubscriptExpr.HasTrailingClosure = hasTrailingClosure; - initializeCallArguments(argLabels, argLabelLocs, hasTrailingClosure); + initializeCallArguments(argLabels, argLabelLocs); } SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, Expr *index, @@ -1496,7 +1495,7 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, Expr *index, &hasTrailingClosure, getType); - size_t size = totalSizeToAlloc(argLabels, argLabelLocs, hasTrailingClosure); + size_t size = totalSizeToAlloc(argLabels, argLabelLocs); void *memory = ctx.Allocate(size, alignof(SubscriptExpr)); return new (memory) SubscriptExpr(base, index, argLabels, argLabelLocs, @@ -1522,8 +1521,7 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, indexArgLabelsScratch, indexArgLabelLocsScratch); - size_t size = totalSizeToAlloc(indexArgLabels, indexArgLabelLocs, - trailingClosure != nullptr); + size_t size = totalSizeToAlloc(indexArgLabels, indexArgLabelLocs); void *memory = ctx.Allocate(size, alignof(SubscriptExpr)); return new (memory) SubscriptExpr(base, index, indexArgLabels, @@ -1543,7 +1541,7 @@ DynamicSubscriptExpr::DynamicSubscriptExpr(Expr *base, Expr *index, Bits.DynamicSubscriptExpr.NumArgLabels = argLabels.size(); Bits.DynamicSubscriptExpr.HasArgLabelLocs = !argLabelLocs.empty(); Bits.DynamicSubscriptExpr.HasTrailingClosure = hasTrailingClosure; - initializeCallArguments(argLabels, argLabelLocs, hasTrailingClosure); + initializeCallArguments(argLabels, argLabelLocs); if (implicit) setImplicit(implicit); } @@ -1561,7 +1559,7 @@ DynamicSubscriptExpr::create(ASTContext &ctx, Expr *base, Expr *index, &hasTrailingClosure, getType); - size_t size = totalSizeToAlloc(argLabels, argLabelLocs, hasTrailingClosure); + size_t size = totalSizeToAlloc(argLabels, argLabelLocs); void *memory = ctx.Allocate(size, alignof(DynamicSubscriptExpr)); return new (memory) DynamicSubscriptExpr(base, index, argLabels, argLabelLocs, @@ -1581,7 +1579,7 @@ UnresolvedMemberExpr::UnresolvedMemberExpr(SourceLoc dotLoc, Bits.UnresolvedMemberExpr.NumArgLabels = argLabels.size(); Bits.UnresolvedMemberExpr.HasArgLabelLocs = !argLabelLocs.empty(); Bits.UnresolvedMemberExpr.HasTrailingClosure = hasTrailingClosure; - initializeCallArguments(argLabels, argLabelLocs, hasTrailingClosure); + initializeCallArguments(argLabels, argLabelLocs); } UnresolvedMemberExpr *UnresolvedMemberExpr::create(ASTContext &ctx, @@ -1589,7 +1587,7 @@ UnresolvedMemberExpr *UnresolvedMemberExpr::create(ASTContext &ctx, DeclNameLoc nameLoc, DeclNameRef name, bool implicit) { - size_t size = totalSizeToAlloc({ }, { }, /*hasTrailingClosure=*/false); + size_t size = totalSizeToAlloc({ }, { }); void *memory = ctx.Allocate(size, alignof(UnresolvedMemberExpr)); return new (memory) UnresolvedMemberExpr(dotLoc, nameLoc, name, nullptr, @@ -1616,8 +1614,7 @@ UnresolvedMemberExpr::create(ASTContext &ctx, SourceLoc dotLoc, argLabelsScratch, argLabelLocsScratch); - size_t size = totalSizeToAlloc(argLabels, argLabelLocs, - trailingClosure != nullptr); + size_t size = totalSizeToAlloc(argLabels, argLabelLocs); void *memory = ctx.Allocate(size, alignof(UnresolvedMemberExpr)); return new (memory) UnresolvedMemberExpr(dotLoc, nameLoc, name, arg, @@ -1660,26 +1657,25 @@ bool ApplyExpr::hasTrailingClosure() const { CallExpr::CallExpr(Expr *fn, Expr *arg, bool Implicit, ArrayRef argLabels, ArrayRef argLabelLocs, - bool hasTrailingClosure, Type ty) : ApplyExpr(ExprKind::Call, fn, arg, Implicit, ty) { Bits.CallExpr.NumArgLabels = argLabels.size(); Bits.CallExpr.HasArgLabelLocs = !argLabelLocs.empty(); - Bits.CallExpr.HasTrailingClosure = hasTrailingClosure; - initializeCallArguments(argLabels, argLabelLocs, hasTrailingClosure); + initializeCallArguments(argLabels, argLabelLocs); } CallExpr *CallExpr::create(ASTContext &ctx, Expr *fn, Expr *arg, ArrayRef argLabels, ArrayRef argLabelLocs, - bool hasTrailingClosure, bool implicit, Type type, + bool implicit, Type type, llvm::function_ref getType) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; if (argLabels.empty()) { // Inspect the argument to dig out the argument labels, their location, and // whether there is a trailing closure. + bool hasTrailingClosure; argLabels = getArgumentLabelsFromArgument(arg, argLabelsScratch, &argLabelLocsScratch, &hasTrailingClosure, @@ -1687,33 +1683,31 @@ CallExpr *CallExpr::create(ASTContext &ctx, Expr *fn, Expr *arg, argLabelLocs = argLabelLocsScratch; } - size_t size = totalSizeToAlloc(argLabels, argLabelLocs, hasTrailingClosure); + size_t size = totalSizeToAlloc(argLabels, argLabelLocs); void *memory = ctx.Allocate(size, alignof(CallExpr)); - return new (memory) CallExpr(fn, arg, implicit, argLabels, argLabelLocs, - hasTrailingClosure, type); + return new (memory) CallExpr(fn, arg, implicit, argLabels, argLabelLocs, type); } CallExpr *CallExpr::create(ASTContext &ctx, Expr *fn, SourceLoc lParenLoc, ArrayRef args, ArrayRef argLabels, ArrayRef argLabelLocs, - SourceLoc rParenLoc, Expr *trailingClosure, + SourceLoc rParenLoc, + ArrayRef trailingClosures, bool implicit, llvm::function_ref getType) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingClosure, implicit, + rParenLoc, trailingClosures, implicit, argLabelsScratch, argLabelLocsScratch, getType); - size_t size = totalSizeToAlloc(argLabels, argLabelLocs, - trailingClosure != nullptr); + size_t size = totalSizeToAlloc(argLabels, argLabelLocs); void *memory = ctx.Allocate(size, alignof(CallExpr)); - return new (memory) CallExpr(fn, arg, implicit, argLabels, argLabelLocs, - trailingClosure != nullptr, Type()); + return new (memory) CallExpr(fn, arg, implicit, argLabels, argLabelLocs, Type()); } Expr *CallExpr::getDirectCallee() const { From 5cea9b9849c44edb5ca4f5aadf3d8dae854c8642 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 7 Feb 2020 18:48:24 -0800 Subject: [PATCH 03/37] [AST] Add support for multiple trailing closures to the parser/expressions --- include/swift/AST/Expr.h | 18 ++++---- include/swift/Parse/Parser.h | 2 +- lib/AST/Expr.cpp | 61 +++++++++++++++------------ lib/Parse/ParseDecl.cpp | 6 +-- lib/Parse/ParseExpr.cpp | 47 ++++++++++----------- lib/Parse/ParsePattern.cpp | 4 +- lib/Parse/ParseStmt.cpp | 6 +-- lib/Sema/BuilderTransform.cpp | 2 +- lib/Sema/CSApply.cpp | 6 +-- lib/Sema/TypeCheckConstraints.cpp | 2 +- lib/Sema/TypeCheckPropertyWrapper.cpp | 4 +- 11 files changed, 82 insertions(+), 76 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index bfecada3c645b..9d10d6ec0363d 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -1195,7 +1195,7 @@ class ObjectLiteralExpr final ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, bool implicit); LiteralKind getLiteralKind() const { @@ -1845,7 +1845,7 @@ class UnresolvedMemberExpr final ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, bool implicit); DeclNameRef getName() const { return Name; } @@ -2397,7 +2397,7 @@ class SubscriptExpr final : public LookupExpr, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, ConcreteDeclRef decl = ConcreteDeclRef(), bool implicit = false, AccessSemantics semantics @@ -4311,7 +4311,7 @@ class CallExpr final : public ApplyExpr, return E->getType(); }) { return create(ctx, fn, SourceLoc(), args, argLabels, { }, SourceLoc(), - /*trailingClosure=*/nullptr, /*implicit=*/true, getType); + /*trailingClosures=*/{}, /*implicit=*/true, getType); } /// Create a new call expression. @@ -4322,11 +4322,11 @@ class CallExpr final : public ApplyExpr, /// or which must be empty. /// \param argLabelLocs The locations of the argument labels, whose size must /// equal args.size() or which must be empty. - /// \param trailingClosure The trailing closure, if any. + /// \param trailingClosures The list of trailing closures, if any. static CallExpr *create( ASTContext &ctx, Expr *fn, SourceLoc lParenLoc, ArrayRef args, ArrayRef argLabels, ArrayRef argLabelLocs, - SourceLoc rParenLoc, Expr *trailingClosure, bool implicit, + SourceLoc rParenLoc, ArrayRef trailingClosures, bool implicit, llvm::function_ref getType = [](Expr *E) -> Type { return E->getType(); }); @@ -4348,7 +4348,7 @@ class CallExpr final : public ApplyExpr, unsigned getNumArguments() const { return Bits.CallExpr.NumArgLabels; } bool hasArgumentLabelLocs() const { return Bits.CallExpr.HasArgLabelLocs; } - /// Whether this call with written with a trailing closure. + /// Whether this call with written with a single trailing closure. bool hasTrailingClosure() const { return Bits.CallExpr.HasTrailingClosure; } using TrailingCallArguments::getArgumentLabels; @@ -5214,7 +5214,7 @@ class KeyPathExpr : public Expr { ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - Expr *trailingClosure); + ArrayRef trailingClosures); /// Create an unresolved component for a subscript. /// @@ -5654,7 +5654,7 @@ inline const SourceLoc *CollectionExpr::getTrailingSourceLocs() const { Expr *packSingleArgument( ASTContext &ctx, SourceLoc lParenLoc, ArrayRef args, ArrayRef &argLabels, ArrayRef &argLabelLocs, - SourceLoc rParenLoc, Expr *trailingClosure, bool implicit, + SourceLoc rParenLoc, ArrayRef trailingClosures, bool implicit, SmallVectorImpl &argLabelsScratch, SmallVectorImpl &argLabelLocsScratch, llvm::function_ref getType = [](Expr *E) -> Type { diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 883dae550812f..3a28922ff1a0b 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1567,7 +1567,7 @@ class Parser { SmallVectorImpl &exprLabels, SmallVectorImpl &exprLabelLocs, SourceLoc &rightLoc, - Expr *&trailingClosure, + SmallVectorImpl &trailingClosures, syntax::SyntaxKind Kind); ParserResult parseTrailingClosure(SourceRange calleeRange); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index ce930382828d7..9476c9803fadd 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -974,7 +974,8 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, ArrayRef args, ArrayRef &argLabels, ArrayRef &argLabelLocs, - SourceLoc rParenLoc, Expr *trailingClosure, + SourceLoc rParenLoc, + ArrayRef trailingClosures, bool implicit, SmallVectorImpl &argLabelsScratch, SmallVectorImpl &argLabelLocsScratch, @@ -984,7 +985,7 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, argLabelLocsScratch.clear(); // Construct a TupleExpr or ParenExpr, as appropriate, for the argument. - if (!trailingClosure) { + if (trailingClosures.empty()) { // Do we have a single, unlabeled argument? if (args.size() == 1 && (argLabels.empty() || argLabels[0].empty())) { auto arg = new (ctx) ParenExpr(lParenLoc, args[0], rParenLoc, @@ -1015,11 +1016,12 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, return arg; } - // If we have no other arguments, represent the trailing closure as a + // If we have no other arguments, represent the a single trailing closure as a // parenthesized expression. - if (args.empty()) { - auto arg = new (ctx) ParenExpr(lParenLoc, trailingClosure, rParenLoc, - /*hasTrailingClosure=*/true); + if (args.empty() && trailingClosures.size() == 1) { + auto arg = + new (ctx) ParenExpr(lParenLoc, trailingClosures.front(), rParenLoc, + /*hasTrailingClosure=*/true); computeSingleArgumentType(ctx, arg, implicit, getType); argLabelsScratch.push_back(Identifier()); argLabels = argLabelsScratch; @@ -1033,7 +1035,7 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, SmallVector argsScratch; argsScratch.reserve(args.size() + 1); argsScratch.append(args.begin(), args.end()); - argsScratch.push_back(trailingClosure); + argsScratch.append(trailingClosures.begin(), trailingClosures.end()); args = argsScratch; argLabelsScratch.reserve(args.size()); @@ -1041,23 +1043,24 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, argLabelsScratch.assign(args.size(), Identifier()); } else { argLabelsScratch.append(argLabels.begin(), argLabels.end()); - argLabelsScratch.push_back(Identifier()); + if (trailingClosures.size() == 1) + argLabelsScratch.push_back(Identifier()); } argLabels = argLabelsScratch; if (!argLabelLocs.empty()) { argLabelLocsScratch.reserve(argLabelLocs.size() + 1); argLabelLocsScratch.append(argLabelLocs.begin(), argLabelLocs.end()); - argLabelLocsScratch.push_back(SourceLoc()); + if (trailingClosures.size() == 1) + argLabelLocsScratch.push_back(SourceLoc()); argLabelLocs = argLabelLocsScratch; } - auto arg = TupleExpr::create(ctx, lParenLoc, args, argLabels, - argLabelLocs, rParenLoc, - /*HasTrailingClosure=*/true, + auto arg = TupleExpr::create(ctx, lParenLoc, rParenLoc, args, argLabels, + argLabelLocs, SourceLoc(), SourceLoc(), + args.size() - trailingClosures.size(), /*Implicit=*/false); computeSingleArgumentType(ctx, arg, implicit, getType); - return arg; } @@ -1107,12 +1110,12 @@ ObjectLiteralExpr *ObjectLiteralExpr::create(ASTContext &ctx, ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, bool implicit) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingClosure, implicit, + rParenLoc, trailingClosures, implicit, argLabelsScratch, argLabelLocsScratch); size_t size = totalSizeToAlloc(argLabels, argLabelLocs); @@ -1120,7 +1123,7 @@ ObjectLiteralExpr *ObjectLiteralExpr::create(ASTContext &ctx, void *memory = ctx.Allocate(size, alignof(ObjectLiteralExpr)); return new (memory) ObjectLiteralExpr(poundLoc, kind, arg, argLabels, argLabelLocs, - trailingClosure != nullptr, implicit); + trailingClosures.size() == 1, implicit); } StringRef ObjectLiteralExpr::getLiteralKindRawName() const { @@ -1509,7 +1512,7 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, ConcreteDeclRef decl, bool implicit, AccessSemantics semantics) { @@ -1517,7 +1520,7 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, SmallVector indexArgLabelLocsScratch; Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, - trailingClosure, implicit, + trailingClosures, implicit, indexArgLabelsScratch, indexArgLabelLocsScratch); @@ -1526,7 +1529,7 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, void *memory = ctx.Allocate(size, alignof(SubscriptExpr)); return new (memory) SubscriptExpr(base, index, indexArgLabels, indexArgLabelLocs, - trailingClosure != nullptr, + trailingClosures.size() == 1, decl, implicit, semantics); } @@ -1604,13 +1607,13 @@ UnresolvedMemberExpr::create(ASTContext &ctx, SourceLoc dotLoc, ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, bool implicit) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingClosure, implicit, + trailingClosures, implicit, argLabelsScratch, argLabelLocsScratch); @@ -1619,7 +1622,7 @@ UnresolvedMemberExpr::create(ASTContext &ctx, SourceLoc dotLoc, void *memory = ctx.Allocate(size, alignof(UnresolvedMemberExpr)); return new (memory) UnresolvedMemberExpr(dotLoc, nameLoc, name, arg, argLabels, argLabelLocs, - trailingClosure != nullptr, + trailingClosures.size() == 1, implicit); } @@ -1657,17 +1660,20 @@ bool ApplyExpr::hasTrailingClosure() const { CallExpr::CallExpr(Expr *fn, Expr *arg, bool Implicit, ArrayRef argLabels, ArrayRef argLabelLocs, + bool hasTrailingClosure, Type ty) : ApplyExpr(ExprKind::Call, fn, arg, Implicit, ty) { Bits.CallExpr.NumArgLabels = argLabels.size(); Bits.CallExpr.HasArgLabelLocs = !argLabelLocs.empty(); + Bits.CallExpr.HasTrailingClosure = hasTrailingClosure; initializeCallArguments(argLabels, argLabelLocs); } CallExpr *CallExpr::create(ASTContext &ctx, Expr *fn, Expr *arg, ArrayRef argLabels, ArrayRef argLabelLocs, + bool hasTrailingClosure, bool implicit, Type type, llvm::function_ref getType) { SmallVector argLabelsScratch; @@ -1675,7 +1681,6 @@ CallExpr *CallExpr::create(ASTContext &ctx, Expr *fn, Expr *arg, if (argLabels.empty()) { // Inspect the argument to dig out the argument labels, their location, and // whether there is a trailing closure. - bool hasTrailingClosure; argLabels = getArgumentLabelsFromArgument(arg, argLabelsScratch, &argLabelLocsScratch, &hasTrailingClosure, @@ -1686,7 +1691,8 @@ CallExpr *CallExpr::create(ASTContext &ctx, Expr *fn, Expr *arg, size_t size = totalSizeToAlloc(argLabels, argLabelLocs); void *memory = ctx.Allocate(size, alignof(CallExpr)); - return new (memory) CallExpr(fn, arg, implicit, argLabels, argLabelLocs, type); + return new (memory) CallExpr(fn, arg, implicit, argLabels, argLabelLocs, + hasTrailingClosure, type); } CallExpr *CallExpr::create(ASTContext &ctx, Expr *fn, SourceLoc lParenLoc, @@ -1707,7 +1713,8 @@ CallExpr *CallExpr::create(ASTContext &ctx, Expr *fn, SourceLoc lParenLoc, size_t size = totalSizeToAlloc(argLabels, argLabelLocs); void *memory = ctx.Allocate(size, alignof(CallExpr)); - return new (memory) CallExpr(fn, arg, implicit, argLabels, argLabelLocs, Type()); + return new (memory) CallExpr(fn, arg, implicit, argLabels, argLabelLocs, + trailingClosures.size() == 1, Type()); } Expr *CallExpr::getDirectCallee() const { @@ -2200,12 +2207,12 @@ KeyPathExpr::Component::forUnresolvedSubscript(ASTContext &ctx, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - Expr *trailingClosure) { + ArrayRef trailingClosures) { SmallVector indexArgLabelsScratch; SmallVector indexArgLabelLocsScratch; Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, - trailingClosure, /*implicit*/ false, + trailingClosures, /*implicit*/ false, indexArgLabelsScratch, indexArgLabelLocsScratch); return forUnresolvedSubscriptWithPrebuiltIndexExpr(ctx, index, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 359e5c8899177..5413d7e623761 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2539,7 +2539,7 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - Expr *trailingClosure = nullptr; + SmallVector trailingClosures; bool hasInitializer = false; ParserStatus status; @@ -2577,9 +2577,9 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At status |= parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/false, /*isExprBasic=*/true, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingClosure, + rParenLoc, trailingClosures, SyntaxKind::TupleExprElementList); - assert(!trailingClosure && "Cannot parse a trailing closure here"); + assert(trailingClosures.empty() && "Cannot parse a trailing closure here"); hasInitializer = true; } } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 8f2601f7ed05f..5f147f0a0cf38 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1184,18 +1184,18 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, SmallVector indexArgs; SmallVector indexArgLabels; SmallVector indexArgLabelLocs; - Expr *trailingClosure; + SmallVector trailingClosures; ParserStatus status = parseExprList( tok::l_square, tok::r_square, /*isPostfix=*/true, isExprBasic, lSquareLoc, indexArgs, - indexArgLabels, indexArgLabelLocs, rSquareLoc, trailingClosure, + indexArgLabels, indexArgLabelLocs, rSquareLoc, trailingClosures, SyntaxKind::TupleExprElementList); Result = makeParserResult( status | Result, SubscriptExpr::create(Context, Result.get(), lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, - trailingClosure, ConcreteDeclRef(), + trailingClosures, ConcreteDeclRef(), /*implicit=*/false)); SyntaxContext->createNodeInPlace(SyntaxKind::SubscriptExpr); continue; @@ -1603,14 +1603,14 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - Expr *trailingClosure; + SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, isExprBasic, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingClosure, + trailingClosures, SyntaxKind::TupleExprElementList); SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); return makeParserResult( @@ -1618,7 +1618,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingClosure, + trailingClosures, /*implicit=*/false)); } @@ -3006,7 +3006,7 @@ Parser::parseExprList(tok leftTok, tok rightTok, SyntaxKind Kind) { SmallVector subExprs; SmallVector subExprNames; SmallVector subExprNameLocs; - Expr *trailingClosure = nullptr; + SmallVector trailingClosures; SourceLoc leftLoc, rightLoc; ParserStatus status = parseExprList(leftTok, rightTok, /*isPostfix=*/false, @@ -3016,7 +3016,7 @@ Parser::parseExprList(tok leftTok, tok rightTok, SyntaxKind Kind) { subExprNames, subExprNameLocs, rightLoc, - trailingClosure, + trailingClosures, Kind); // A tuple with a single, unlabeled element is just parentheses. @@ -3052,10 +3052,8 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, SmallVectorImpl &exprLabels, SmallVectorImpl &exprLabelLocs, SourceLoc &rightLoc, - Expr *&trailingClosure, + SmallVectorImpl &trailingClosures, SyntaxKind Kind) { - trailingClosure = nullptr; - StructureMarkerRAII ParsingExprList(*this, Tok); if (ParsingExprList.isFailed()) { @@ -3143,7 +3141,7 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, return status; // Record the trailing closure. - trailingClosure = closure.get(); + trailingClosures.push_back(closure.get()); return status; } @@ -3201,14 +3199,14 @@ Parser::parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LitKind, SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - Expr *trailingClosure; + SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, isExprBasic, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingClosure, + trailingClosures, SyntaxKind::TupleExprElementList); if (status.hasCodeCompletion()) return makeParserCodeCompletionResult(); @@ -3218,7 +3216,7 @@ Parser::parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LitKind, return makeParserResult( ObjectLiteralExpr::create(Context, PoundLoc, LitKind, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingClosure, /*implicit=*/false)); + trailingClosures, /*implicit=*/false)); } /// Parse and diagnose unknown pound expression @@ -3243,13 +3241,13 @@ ParserResult Parser::parseExprPoundUnknown(SourceLoc LSquareLoc) { SmallVector argLabelLocs; SmallVector args; SmallVector argLabels; - Expr *trailingClosure; + SmallVector trailingClosures; if (Tok.isFollowingLParen()) { // Parse arguments. ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, /*isExprBasic*/ true, LParenLoc, - args, argLabels, argLabelLocs, RParenLoc, trailingClosure, + args, argLabels, argLabelLocs, RParenLoc, trailingClosures, SyntaxKind::TupleExprElementList); if (status.hasCodeCompletion()) return makeParserCodeCompletionResult(); @@ -3344,7 +3342,7 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { { Identifier() }, { }, SourceLoc(), - /*trailingClosure=*/nullptr, + /*trailingClosures=*/{}, /*implicit=*/false)); CodeCompletion->completePostfixExprParen(fn.get(), CCE); // Eat the code completion token because we handled it. @@ -3358,21 +3356,22 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - Expr *trailingClosure; + SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, isExprBasic, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingClosure, + trailingClosures, SyntaxKind::TupleExprElementList); // Form the call. - return makeParserResult( - status | fn, CallExpr::create(Context, fn.get(), lParenLoc, args, - argLabels, argLabelLocs, rParenLoc, - trailingClosure, /*implicit=*/false)); + return makeParserResult(status | fn, + CallExpr::create(Context, fn.get(), lParenLoc, args, + argLabels, argLabelLocs, rParenLoc, + trailingClosures, + /*implicit=*/false)); } /// parseExprCollection - Parse a collection literal expression. diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index f6b28b9c56413..1b42538f5d808 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -917,13 +917,13 @@ ParserResult Parser::parseTypedPattern() { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - Expr *trailingClosure; + SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, /*isExprBasic=*/false, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingClosure, + trailingClosures, SyntaxKind::Unknown); if (status.isSuccess()) { backtrack.cancelBacktrack(); diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 80a5ab62d56de..18b6ba511efcd 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -845,7 +845,7 @@ ParserResult Parser::parseStmtYield(SourceLoc tryLoc) { SmallVector yieldLabels; SmallVector yieldLabelLocs; - Expr *trailingClosure = nullptr; + SmallVector trailingClosures; status = parseExprList(tok::l_paren, tok::r_paren, /*postfix (allow trailing closure)*/ false, @@ -853,9 +853,9 @@ ParserResult Parser::parseStmtYield(SourceLoc tryLoc) { lpLoc, yields, yieldLabels, yieldLabelLocs, rpLoc, - trailingClosure, + trailingClosures, SyntaxKind::ExprList); - assert(trailingClosure == nullptr); + assert(trailingClosures.empty()); assert(yieldLabels.empty()); assert(yieldLabelLocs.empty()); } else { diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 03d8366fc14f0..edd71fbacd154 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -101,7 +101,7 @@ class BuilderClosureVisitor SourceLoc closeLoc = args.empty() ? loc : args.back()->getEndLoc(); Expr *result = CallExpr::create(ctx, memberRef, openLoc, args, argLabels, argLabelLocs, closeLoc, - /*trailing closure*/ nullptr, + /*trailing closures*/{}, /*implicit*/true); return result; diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1d22f82b49344..e1a925adff358 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2001,14 +2001,14 @@ namespace { arguments.push_back(SE->getIndex()->getSemanticsProvidingExpr()); } - Expr *trailingClosure = nullptr; + SmallVector trailingClosures; if (SE->hasTrailingClosure()) - trailingClosure = arguments.back(); + trailingClosures.push_back(arguments.back()); componentExpr = SubscriptExpr::create( ctx, dotExpr, SE->getStartLoc(), arguments, SE->getArgumentLabels(), SE->getArgumentLabelLocs(), - SE->getEndLoc(), trailingClosure, + SE->getEndLoc(), trailingClosures, SE->hasDecl() ? SE->getDecl() : ConcreteDeclRef(), /*implicit=*/true, SE->getAccessSemantics()); } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 901fb50eb2569..8c57b244177b2 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -995,7 +995,7 @@ namespace { E = CallExpr::create(Context, newCallee, lParen, {newArg}, {Identifier()}, {SourceLoc()}, rParen, - /*trailingClosure=*/nullptr, + /*trailingClosures=*/{}, /*implicit=*/false); } } diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 5be91691a2100..4a0c2b110324b 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -750,7 +750,7 @@ Expr *swift::buildPropertyWrapperWrappedValueCall( auto *init = CallExpr::create( ctx, typeExpr, startLoc, {initializer}, {argName}, {initializer->getStartLoc()}, endLoc, - nullptr, /*implicit=*/true); + /*trailingClosures=*/{}, /*implicit=*/true); initializer = init; if (!innermostInit) @@ -785,7 +785,7 @@ Expr *swift::buildPropertyWrapperWrappedValueCall( auto *init = CallExpr::create( ctx, typeExpr, startLoc, elements, elementNames, elementLocs, - endLoc, nullptr, /*implicit=*/true); + endLoc, /*trailingClosures=*/{}, /*implicit=*/true); initializer = init; if (!innermostInit) From 45ac1bcf1790d1c0c8a11303e1265cb9c5861d07 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 10 Feb 2020 00:24:07 -0800 Subject: [PATCH 04/37] [Parser] Adjust `parseExprList` to return multiple trailing closures Also extend returned object from simplify being an expression to `TrailingClosure` which has a label, label's source location and associated closure expression. --- include/swift/AST/Expr.h | 27 ++++++++++++++++++++------- include/swift/Parse/Parser.h | 2 +- lib/AST/Attr.cpp | 4 ++-- lib/AST/Expr.cpp | 32 +++++++++++++++++--------------- lib/Parse/ParseDecl.cpp | 2 +- lib/Parse/ParseExpr.cpp | 20 ++++++++++---------- lib/Parse/ParsePattern.cpp | 2 +- lib/Parse/ParseStmt.cpp | 2 +- lib/Sema/CSApply.cpp | 4 ++-- lib/Sema/TypeCheckStorage.cpp | 2 +- 10 files changed, 56 insertions(+), 41 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 9d10d6ec0363d..99217c710798f 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -67,6 +67,18 @@ namespace swift { class KeyPathExpr; class CaptureListExpr; +struct TrailingClosure { + Identifier Label; + SourceLoc LabelLoc; + Expr *ClosureExpr; + + TrailingClosure(Expr *closure) + : TrailingClosure(Identifier(), SourceLoc(), closure) {} + + TrailingClosure(Identifier label, SourceLoc labelLoc, Expr *closure) + : Label(label), LabelLoc(labelLoc), ClosureExpr(closure) {} +}; + enum class ExprKind : uint8_t { #define EXPR(Id, Parent) Id, #define LAST_EXPR(Id) Last_Expr = Id, @@ -1195,7 +1207,7 @@ class ObjectLiteralExpr final ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - ArrayRef trailingClosures, + ArrayRef trailingClosures, bool implicit); LiteralKind getLiteralKind() const { @@ -1845,7 +1857,7 @@ class UnresolvedMemberExpr final ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - ArrayRef trailingClosures, + ArrayRef trailingClosures, bool implicit); DeclNameRef getName() const { return Name; } @@ -2397,7 +2409,7 @@ class SubscriptExpr final : public LookupExpr, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - ArrayRef trailingClosures, + ArrayRef trailingClosures, ConcreteDeclRef decl = ConcreteDeclRef(), bool implicit = false, AccessSemantics semantics @@ -4326,7 +4338,7 @@ class CallExpr final : public ApplyExpr, static CallExpr *create( ASTContext &ctx, Expr *fn, SourceLoc lParenLoc, ArrayRef args, ArrayRef argLabels, ArrayRef argLabelLocs, - SourceLoc rParenLoc, ArrayRef trailingClosures, bool implicit, + SourceLoc rParenLoc, ArrayRef trailingClosures, bool implicit, llvm::function_ref getType = [](Expr *E) -> Type { return E->getType(); }); @@ -5214,7 +5226,7 @@ class KeyPathExpr : public Expr { ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - ArrayRef trailingClosures); + ArrayRef trailingClosures); /// Create an unresolved component for a subscript. /// @@ -5266,7 +5278,7 @@ class KeyPathExpr : public Expr { ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, Type elementType, ArrayRef indexHashables); @@ -5654,7 +5666,8 @@ inline const SourceLoc *CollectionExpr::getTrailingSourceLocs() const { Expr *packSingleArgument( ASTContext &ctx, SourceLoc lParenLoc, ArrayRef args, ArrayRef &argLabels, ArrayRef &argLabelLocs, - SourceLoc rParenLoc, ArrayRef trailingClosures, bool implicit, + SourceLoc rParenLoc, ArrayRef trailingClosures, + bool implicit, SmallVectorImpl &argLabelsScratch, SmallVectorImpl &argLabelLocsScratch, llvm::function_ref getType = [](Expr *E) -> Type { diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 3a28922ff1a0b..a3854c9965e76 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1567,7 +1567,7 @@ class Parser { SmallVectorImpl &exprLabels, SmallVectorImpl &exprLabelLocs, SourceLoc &rightLoc, - SmallVectorImpl &trailingClosures, + SmallVectorImpl &trailingClosures, syntax::SyntaxKind Kind); ParserResult parseTrailingClosure(SourceRange calleeRange); diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 7f6aa8d731ae4..2b339f12fb75a 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1867,8 +1867,8 @@ CustomAttr *CustomAttr::create(ASTContext &ctx, SourceLoc atLoc, TypeLoc type, Expr *arg = nullptr; if (hasInitializer) { arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, nullptr, implicit, argLabelsScratch, - argLabelLocsScratch); + rParenLoc, /*trailingClosures=*/{}, implicit, + argLabelsScratch, argLabelLocsScratch); } SourceRange range(atLoc, type.getSourceRange().End); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 9476c9803fadd..3f57d0544a384 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -975,7 +975,7 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, ArrayRef &argLabels, ArrayRef &argLabelLocs, SourceLoc rParenLoc, - ArrayRef trailingClosures, + ArrayRef trailingClosures, bool implicit, SmallVectorImpl &argLabelsScratch, SmallVectorImpl &argLabelLocsScratch, @@ -1019,8 +1019,9 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, // If we have no other arguments, represent the a single trailing closure as a // parenthesized expression. if (args.empty() && trailingClosures.size() == 1) { + auto &trailingClosure = trailingClosures.front(); auto arg = - new (ctx) ParenExpr(lParenLoc, trailingClosures.front(), rParenLoc, + new (ctx) ParenExpr(lParenLoc, trailingClosure.ClosureExpr, rParenLoc, /*hasTrailingClosure=*/true); computeSingleArgumentType(ctx, arg, implicit, getType); argLabelsScratch.push_back(Identifier()); @@ -1035,7 +1036,8 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, SmallVector argsScratch; argsScratch.reserve(args.size() + 1); argsScratch.append(args.begin(), args.end()); - argsScratch.append(trailingClosures.begin(), trailingClosures.end()); + for (const auto &closure : trailingClosures) + argsScratch.push_back(closure.ClosureExpr); args = argsScratch; argLabelsScratch.reserve(args.size()); @@ -1043,16 +1045,16 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, argLabelsScratch.assign(args.size(), Identifier()); } else { argLabelsScratch.append(argLabels.begin(), argLabels.end()); - if (trailingClosures.size() == 1) - argLabelsScratch.push_back(Identifier()); + for (const auto &closure : trailingClosures) + argLabelsScratch.push_back(closure.Label); } argLabels = argLabelsScratch; if (!argLabelLocs.empty()) { - argLabelLocsScratch.reserve(argLabelLocs.size() + 1); + argLabelLocsScratch.reserve(argLabelLocs.size() + trailingClosures.size()); argLabelLocsScratch.append(argLabelLocs.begin(), argLabelLocs.end()); - if (trailingClosures.size() == 1) - argLabelLocsScratch.push_back(SourceLoc()); + for (const auto &closure : trailingClosures) + argLabelLocsScratch.push_back(closure.LabelLoc); argLabelLocs = argLabelLocsScratch; } @@ -1110,7 +1112,7 @@ ObjectLiteralExpr *ObjectLiteralExpr::create(ASTContext &ctx, ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - ArrayRef trailingClosures, + ArrayRef trailingClosures, bool implicit) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; @@ -1512,7 +1514,7 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - ArrayRef trailingClosures, + ArrayRef trailingClosures, ConcreteDeclRef decl, bool implicit, AccessSemantics semantics) { @@ -1607,7 +1609,7 @@ UnresolvedMemberExpr::create(ASTContext &ctx, SourceLoc dotLoc, ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - ArrayRef trailingClosures, + ArrayRef trailingClosures, bool implicit) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; @@ -1700,7 +1702,7 @@ CallExpr *CallExpr::create(ASTContext &ctx, Expr *fn, SourceLoc lParenLoc, ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - ArrayRef trailingClosures, + ArrayRef trailingClosures, bool implicit, llvm::function_ref getType) { SmallVector argLabelsScratch; @@ -2183,14 +2185,14 @@ KeyPathExpr::Component::forSubscript(ASTContext &ctx, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, Type elementType, ArrayRef indexHashables) { SmallVector indexArgLabelsScratch; SmallVector indexArgLabelLocsScratch; Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, - trailingClosure, /*implicit*/ false, + trailingClosures, /*implicit*/ false, indexArgLabelsScratch, indexArgLabelLocsScratch); return forSubscriptWithPrebuiltIndexExpr(subscript, index, @@ -2207,7 +2209,7 @@ KeyPathExpr::Component::forUnresolvedSubscript(ASTContext &ctx, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - ArrayRef trailingClosures) { + ArrayRef trailingClosures) { SmallVector indexArgLabelsScratch; SmallVector indexArgLabelLocsScratch; Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 5413d7e623761..fcff951712a26 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2539,7 +2539,7 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - SmallVector trailingClosures; + SmallVector trailingClosures; bool hasInitializer = false; ParserStatus status; diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 5f147f0a0cf38..153f3bee4b551 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1184,7 +1184,7 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, SmallVector indexArgs; SmallVector indexArgLabels; SmallVector indexArgLabelLocs; - SmallVector trailingClosures; + SmallVector trailingClosures; ParserStatus status = parseExprList( tok::l_square, tok::r_square, @@ -1227,7 +1227,7 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, Result = makeParserResult( ParserStatus(closure) | ParserStatus(Result), CallExpr::create(Context, Result.get(), SourceLoc(), {}, {}, {}, - SourceLoc(), closure.get(), /*implicit=*/false)); + SourceLoc(), {closure.get()}, /*implicit=*/false)); SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); // We only allow a single trailing closure on a call. This could be @@ -1603,7 +1603,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - SmallVector trailingClosures; + SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, isExprBasic, @@ -1641,7 +1641,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { ParserStatus(closure), UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, SourceLoc(), { }, { }, { }, - SourceLoc(), closure.get(), + SourceLoc(), {closure.get()}, /*implicit=*/false)); } @@ -3006,7 +3006,7 @@ Parser::parseExprList(tok leftTok, tok rightTok, SyntaxKind Kind) { SmallVector subExprs; SmallVector subExprNames; SmallVector subExprNameLocs; - SmallVector trailingClosures; + SmallVector trailingClosures; SourceLoc leftLoc, rightLoc; ParserStatus status = parseExprList(leftTok, rightTok, /*isPostfix=*/false, @@ -3052,7 +3052,7 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, SmallVectorImpl &exprLabels, SmallVectorImpl &exprLabelLocs, SourceLoc &rightLoc, - SmallVectorImpl &trailingClosures, + SmallVectorImpl &trailingClosures, SyntaxKind Kind) { StructureMarkerRAII ParsingExprList(*this, Tok); @@ -3141,7 +3141,7 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, return status; // Record the trailing closure. - trailingClosures.push_back(closure.get()); + trailingClosures.push_back({closure.get()}); return status; } @@ -3199,7 +3199,7 @@ Parser::parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LitKind, SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - SmallVector trailingClosures; + SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, isExprBasic, @@ -3241,7 +3241,7 @@ ParserResult Parser::parseExprPoundUnknown(SourceLoc LSquareLoc) { SmallVector argLabelLocs; SmallVector args; SmallVector argLabels; - SmallVector trailingClosures; + SmallVector trailingClosures; if (Tok.isFollowingLParen()) { // Parse arguments. ParserStatus status = @@ -3356,7 +3356,7 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - SmallVector trailingClosures; + SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, isExprBasic, diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 1b42538f5d808..8b29e0e10d365 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -917,7 +917,7 @@ ParserResult Parser::parseTypedPattern() { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - SmallVector trailingClosures; + SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, /*isExprBasic=*/false, diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 18b6ba511efcd..5c6dfdc92d8c2 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -845,7 +845,7 @@ ParserResult Parser::parseStmtYield(SourceLoc tryLoc) { SmallVector yieldLabels; SmallVector yieldLabelLocs; - SmallVector trailingClosures; + SmallVector trailingClosures; status = parseExprList(tok::l_paren, tok::r_paren, /*postfix (allow trailing closure)*/ false, diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index e1a925adff358..89e567b34a111 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2001,9 +2001,9 @@ namespace { arguments.push_back(SE->getIndex()->getSemanticsProvidingExpr()); } - SmallVector trailingClosures; + SmallVector trailingClosures; if (SE->hasTrailingClosure()) - trailingClosures.push_back(arguments.back()); + trailingClosures.push_back({arguments.back()}); componentExpr = SubscriptExpr::create( ctx, dotExpr, SE->getStartLoc(), arguments, diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index dcd44b00c95d4..6550213718039 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -891,7 +891,7 @@ static Expr *buildStorageReference(AccessorDecl *accessor, lookupExpr = SubscriptExpr::create( ctx, wrapperMetatype, SourceLoc(), args, subscriptDecl->getName().getArgumentNames(), { }, SourceLoc(), - nullptr, subscriptDecl, /*Implicit=*/true); + /*trailingClosures=*/{}, subscriptDecl, /*Implicit=*/true); // FIXME: Since we're not resolving overloads or anything, we should be // building fully type-checked AST above; we already have all the From 07cb6b8ad231d6ca4879c5fec5118acdf8d0e7c0 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 10 Feb 2020 10:28:10 -0800 Subject: [PATCH 05/37] [Parse] Account that there could be multiple trailing closures which parsing --- include/swift/Parse/Parser.h | 4 +++- lib/Parse/ParseExpr.cpp | 46 +++++++++++++++++------------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index a3854c9965e76..97187101a7887 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1570,7 +1570,9 @@ class Parser { SmallVectorImpl &trailingClosures, syntax::SyntaxKind Kind); - ParserResult parseTrailingClosure(SourceRange calleeRange); + ParserStatus + parseTrailingClosures(SourceRange calleeRange, + SmallVectorImpl &closures); /// Parse an object literal. /// diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 153f3bee4b551..5758533453149 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1218,16 +1218,17 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, leadingTriviaLoc(), *SyntaxContext)); } - ParserResult closure = - parseTrailingClosure(callee->getSourceRange()); - if (closure.isNull()) + SmallVector trailingClosures; + auto trailingResult = + parseTrailingClosures(callee->getSourceRange(), trailingClosures); + if (trailingClosures.empty()) return nullptr; // Trailing closure implicitly forms a call. Result = makeParserResult( - ParserStatus(closure) | ParserStatus(Result), + ParserStatus(Result) | trailingResult, CallExpr::create(Context, Result.get(), SourceLoc(), {}, {}, {}, - SourceLoc(), {closure.get()}, /*implicit=*/false)); + SourceLoc(), trailingClosures, /*implicit=*/false)); SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); // We only allow a single trailing closure on a call. This could be @@ -1631,18 +1632,19 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { leadingTriviaLoc(), *SyntaxContext)); } - ParserResult closure = - parseTrailingClosure(NameLoc.getSourceRange()); - if (closure.isNull()) return nullptr; + SmallVector trailingClosures; + auto result = + parseTrailingClosures(NameLoc.getSourceRange(), trailingClosures); + if (trailingClosures.empty()) + return nullptr; SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); // Handle .foo by just making an AST node. return makeParserResult( - ParserStatus(closure), - UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, - SourceLoc(), { }, { }, { }, - SourceLoc(), {closure.get()}, - /*implicit=*/false)); + result, UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, + SourceLoc(), {}, {}, {}, + SourceLoc(), trailingClosures, + /*implicit=*/false)); } // Handle .foo by just making an AST node. @@ -3134,19 +3136,14 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, return status; // Parse the closure. - ParserResult closure = - parseTrailingClosure(SourceRange(leftLoc, rightLoc)); - status |= closure; - if (closure.isNull()) - return status; - - // Record the trailing closure. - trailingClosures.push_back({closure.get()}); - + status |= + parseTrailingClosures(SourceRange(leftLoc, rightLoc), trailingClosures); return status; } -ParserResult Parser::parseTrailingClosure(SourceRange calleeRange) { +ParserStatus +Parser::parseTrailingClosures(SourceRange calleeRange, + SmallVectorImpl &closures) { SourceLoc braceLoc = Tok.getLoc(); // Record the line numbers for the diagnostics below. @@ -3175,9 +3172,10 @@ ParserResult Parser::parseTrailingClosure(SourceRange calleeRange) { } } + closures.push_back({closure.get()}); return closure; } - + /// Parse an object literal expression. /// /// expr-literal: From d06126da3b31f2d3d24a19cf6947f6e4c8b0b13f Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 10 Feb 2020 12:57:44 -0800 Subject: [PATCH 06/37] [Parser] Add support for multiple trailing closures syntax Accept trailing closures in following form: ```swift foo { : { ... } : { ... } ... : { ... } } ``` Consider each labeled block to be a regular argument to a call or subscript, so the result of parser looks like this: ```swift foo(: { ... }, ..., : { ... }) ``` Note that in this example parens surrounding parameter list are implicit and for the cases when they are given by the user e.g. ```swift foo(bar) { : { ... } ... } ``` location of `)` is changed to a location of `}` to make sure that call "covers" all of the transformed arguments and parser result would look like this: ```swift foo(bar, : { ... } ) ``` Resolves: rdar://problem/59203764 --- include/swift/AST/Expr.h | 29 +++-- include/swift/Parse/Parser.h | 9 +- lib/AST/Attr.cpp | 3 +- lib/AST/Expr.cpp | 89 ++++++++++----- lib/Parse/ParseDecl.cpp | 4 +- lib/Parse/ParseExpr.cpp | 156 ++++++++++++++++++++++---- lib/Parse/ParsePattern.cpp | 2 + lib/Parse/ParseStmt.cpp | 2 + lib/Sema/BuilderTransform.cpp | 2 + lib/Sema/CSApply.cpp | 11 +- lib/Sema/TypeCheckConstraints.cpp | 1 + lib/Sema/TypeCheckPropertyWrapper.cpp | 18 +-- lib/Sema/TypeCheckStorage.cpp | 1 + test/Constraints/closures.swift | 1 + 14 files changed, 257 insertions(+), 71 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 99217c710798f..bbe01dc4c05f9 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -1207,6 +1207,8 @@ class ObjectLiteralExpr final ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit); @@ -1857,6 +1859,8 @@ class UnresolvedMemberExpr final ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit); @@ -2078,8 +2082,8 @@ class TupleExpr final : public Expr, SourceLoc LParenLoc; SourceLoc RParenLoc; - SourceLoc TrailingBlockLBrace; - SourceLoc TrailingBlockRBrace; + SourceLoc TrailingLBraceLoc; + SourceLoc TrailingRBraceLoc; Optional FirstTrailingArgumentAt; @@ -2151,6 +2155,9 @@ class TupleExpr final : public Expr, SourceRange getSourceRange() const; + SourceLoc getTrailingLBraceLoc() const { return TrailingLBraceLoc; } + SourceLoc getTrailingRBraceLoc() const { return TrailingRBraceLoc; } + /// Whether this expression has a trailing closure as its argument. bool hasTrailingClosure() const { return FirstTrailingArgumentAt @@ -2409,6 +2416,8 @@ class SubscriptExpr final : public LookupExpr, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures, ConcreteDeclRef decl = ConcreteDeclRef(), bool implicit = false, @@ -4322,8 +4331,9 @@ class CallExpr final : public ApplyExpr, llvm::function_ref getType = [](Expr *E) -> Type { return E->getType(); }) { - return create(ctx, fn, SourceLoc(), args, argLabels, { }, SourceLoc(), - /*trailingClosures=*/{}, /*implicit=*/true, getType); + return create(ctx, fn, SourceLoc(), args, argLabels, {}, SourceLoc(), + SourceLoc(), SourceLoc(), /*trailingClosures=*/{}, + /*implicit=*/true, getType); } /// Create a new call expression. @@ -4338,7 +4348,8 @@ class CallExpr final : public ApplyExpr, static CallExpr *create( ASTContext &ctx, Expr *fn, SourceLoc lParenLoc, ArrayRef args, ArrayRef argLabels, ArrayRef argLabelLocs, - SourceLoc rParenLoc, ArrayRef trailingClosures, bool implicit, + SourceLoc rParenLoc, SourceLoc trailingLBrace, SourceLoc trailingRBrace, + ArrayRef trailingClosures, bool implicit, llvm::function_ref getType = [](Expr *E) -> Type { return E->getType(); }); @@ -5226,6 +5237,8 @@ class KeyPathExpr : public Expr { ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures); /// Create an unresolved component for a subscript. @@ -5278,6 +5291,8 @@ class KeyPathExpr : public Expr { ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures, Type elementType, ArrayRef indexHashables); @@ -5666,8 +5681,8 @@ inline const SourceLoc *CollectionExpr::getTrailingSourceLocs() const { Expr *packSingleArgument( ASTContext &ctx, SourceLoc lParenLoc, ArrayRef args, ArrayRef &argLabels, ArrayRef &argLabelLocs, - SourceLoc rParenLoc, ArrayRef trailingClosures, - bool implicit, + SourceLoc rParenLoc, SourceLoc trailingLBrace, SourceLoc trailingRBrace, + ArrayRef trailingClosures, bool implicit, SmallVectorImpl &argLabelsScratch, SmallVectorImpl &argLabelLocsScratch, llvm::function_ref getType = [](Expr *E) -> Type { diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 97187101a7887..1c67f0a62f54d 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1567,13 +1567,20 @@ class Parser { SmallVectorImpl &exprLabels, SmallVectorImpl &exprLabelLocs, SourceLoc &rightLoc, + SourceLoc &trailingLBrace, + SourceLoc &trailingRBrace, SmallVectorImpl &trailingClosures, syntax::SyntaxKind Kind); ParserStatus - parseTrailingClosures(SourceRange calleeRange, + parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, + SourceLoc &LBrace, SourceLoc &RBrace, SmallVectorImpl &closures); + ParserStatus + parseMultipleTrailingClosures(SourceLoc &LBrace, SourceLoc &RBrace, + SmallVectorImpl &closures); + /// Parse an object literal. /// /// \param LK The literal kind as determined by the first token. diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 2b339f12fb75a..5c9eb437db1a1 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1867,7 +1867,8 @@ CustomAttr *CustomAttr::create(ASTContext &ctx, SourceLoc atLoc, TypeLoc type, Expr *arg = nullptr; if (hasInitializer) { arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, /*trailingClosures=*/{}, implicit, + rParenLoc, SourceLoc(), SourceLoc(), + /*trailingClosures=*/{}, implicit, argLabelsScratch, argLabelLocsScratch); } diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 3f57d0544a384..b6d72f473bfd5 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -975,6 +975,8 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, ArrayRef &argLabels, ArrayRef &argLabelLocs, SourceLoc rParenLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit, SmallVectorImpl &argLabelsScratch, @@ -1032,34 +1034,44 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, assert(argLabels.empty() || args.size() == argLabels.size()); + unsigned numRegularArgs = args.size(); + // Form a tuple, including the trailing closure. SmallVector argsScratch; - argsScratch.reserve(args.size() + 1); + argsScratch.reserve(numRegularArgs + trailingClosures.size()); argsScratch.append(args.begin(), args.end()); for (const auto &closure : trailingClosures) argsScratch.push_back(closure.ClosureExpr); args = argsScratch; - argLabelsScratch.reserve(args.size()); - if (argLabels.empty()) { - argLabelsScratch.assign(args.size(), Identifier()); - } else { - argLabelsScratch.append(argLabels.begin(), argLabels.end()); + { + if (argLabels.empty()) { + argLabelsScratch.resize(numRegularArgs); + } else { + argLabelsScratch.append(argLabels.begin(), argLabels.end()); + } + for (const auto &closure : trailingClosures) argLabelsScratch.push_back(closure.Label); + + argLabels = argLabelsScratch; } - argLabels = argLabelsScratch; - if (!argLabelLocs.empty()) { - argLabelLocsScratch.reserve(argLabelLocs.size() + trailingClosures.size()); - argLabelLocsScratch.append(argLabelLocs.begin(), argLabelLocs.end()); + { + if (argLabelLocs.empty()) { + argLabelLocsScratch.resize(numRegularArgs); + } else { + argLabelLocsScratch.append(argLabelLocs.begin(), argLabelLocs.end()); + } + for (const auto &closure : trailingClosures) argLabelLocsScratch.push_back(closure.LabelLoc); + argLabelLocs = argLabelLocsScratch; } auto arg = TupleExpr::create(ctx, lParenLoc, rParenLoc, args, argLabels, - argLabelLocs, SourceLoc(), SourceLoc(), + argLabelLocs, trailingLBrace, trailingRBrace, args.size() - trailingClosures.size(), /*Implicit=*/false); computeSingleArgumentType(ctx, arg, implicit, getType); @@ -1112,13 +1124,16 @@ ObjectLiteralExpr *ObjectLiteralExpr::create(ASTContext &ctx, ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingClosures, implicit, - argLabelsScratch, argLabelLocsScratch); + rParenLoc, trailingLBrace, trailingRBrace, + trailingClosures, implicit, argLabelsScratch, + argLabelLocsScratch); size_t size = totalSizeToAlloc(argLabels, argLabelLocs); @@ -1238,6 +1253,8 @@ SourceRange TupleExpr::getSourceRange() const { start = LParenLoc; } else if (getNumElements() == 0) { return { SourceLoc(), SourceLoc() }; + } else if (TrailingLBraceLoc.isValid()) { + start = TrailingLBraceLoc; } else { // Scan forward for the first valid source loc. for (Expr *expr : getElements()) { @@ -1248,9 +1265,12 @@ SourceRange TupleExpr::getSourceRange() const { } } - if (hasTrailingClosure() || RParenLoc.isInvalid()) { + if (hasTrailingClosure() || hasMultipleTrailingClosures() || + RParenLoc.isInvalid()) { if (getNumElements() == 0) { return { SourceLoc(), SourceLoc() }; + } else if (TrailingRBraceLoc.isValid()) { + end = TrailingRBraceLoc; } else { // Scan backwards for a valid source loc. for (Expr *expr : llvm::reverse(getElements())) { @@ -1281,7 +1301,7 @@ TupleExpr::TupleExpr(SourceLoc LParenLoc, SourceLoc RParenLoc, bool Implicit, Type Ty) : Expr(ExprKind::Tuple, Implicit, Ty), LParenLoc(LParenLoc), RParenLoc(RParenLoc), - TrailingBlockLBrace(TrailingLBrace), TrailingBlockRBrace(TrailingRBrace), + TrailingLBraceLoc(TrailingLBrace), TrailingRBraceLoc(TrailingRBrace), FirstTrailingArgumentAt(FirstTrailingArgumentAt) { Bits.TupleExpr.HasElementNames = !ElementNames.empty(); Bits.TupleExpr.HasElementNameLocations = !ElementNameLocs.empty(); @@ -1514,6 +1534,8 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures, ConcreteDeclRef decl, bool implicit, @@ -1522,6 +1544,7 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, SmallVector indexArgLabelLocsScratch; Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, + trailingLBrace, trailingRBrace, trailingClosures, implicit, indexArgLabelsScratch, indexArgLabelLocsScratch); @@ -1609,14 +1632,15 @@ UnresolvedMemberExpr::create(ASTContext &ctx, SourceLoc dotLoc, ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; - Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, - argLabelLocs, rParenLoc, - trailingClosures, implicit, - argLabelsScratch, + Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, + rParenLoc, trailingLBrace, trailingRBrace, + trailingClosures, implicit, argLabelsScratch, argLabelLocsScratch); size_t size = totalSizeToAlloc(argLabels, argLabelLocs); @@ -1702,15 +1726,17 @@ CallExpr *CallExpr::create(ASTContext &ctx, Expr *fn, SourceLoc lParenLoc, ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit, llvm::function_ref getType) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingClosures, implicit, - argLabelsScratch, argLabelLocsScratch, - getType); + rParenLoc, trailingLBrace, trailingRBrace, + trailingClosures, implicit, argLabelsScratch, + argLabelLocsScratch, getType); size_t size = totalSizeToAlloc(argLabels, argLabelLocs); @@ -2185,6 +2211,8 @@ KeyPathExpr::Component::forSubscript(ASTContext &ctx, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures, Type elementType, ArrayRef indexHashables) { @@ -2192,6 +2220,7 @@ KeyPathExpr::Component::forSubscript(ASTContext &ctx, SmallVector indexArgLabelLocsScratch; Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, + trailingLBrace, trailingRBrace, trailingClosures, /*implicit*/ false, indexArgLabelsScratch, indexArgLabelLocsScratch); @@ -2209,17 +2238,17 @@ KeyPathExpr::Component::forUnresolvedSubscript(ASTContext &ctx, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, + SourceLoc trailingLBrace, + SourceLoc trailingRBrace, ArrayRef trailingClosures) { SmallVector indexArgLabelsScratch; SmallVector indexArgLabelLocsScratch; - Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, - indexArgLabelLocs, rSquareLoc, - trailingClosures, /*implicit*/ false, - indexArgLabelsScratch, - indexArgLabelLocsScratch); - return forUnresolvedSubscriptWithPrebuiltIndexExpr(ctx, index, - indexArgLabels, - lSquareLoc); + Expr *index = packSingleArgument( + ctx, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, + trailingLBrace, trailingRBrace, trailingClosures, /*implicit*/ false, + indexArgLabelsScratch, indexArgLabelLocsScratch); + return forUnresolvedSubscriptWithPrebuiltIndexExpr(ctx, index, indexArgLabels, + lSquareLoc); } KeyPathExpr::Component::Component(ASTContext *ctxForCopyingLabels, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index fcff951712a26..d31907688dc91 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2539,6 +2539,7 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; + SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; bool hasInitializer = false; ParserStatus status; @@ -2577,7 +2578,8 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At status |= parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/false, /*isExprBasic=*/true, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingClosures, + rParenLoc, trailingLBrace, trailingRBrace, + trailingClosures, SyntaxKind::TupleExprElementList); assert(trailingClosures.empty() && "Cannot parse a trailing closure here"); hasInitializer = true; diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 5758533453149..4b44a8b6db18e 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1184,17 +1184,20 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, SmallVector indexArgs; SmallVector indexArgLabels; SmallVector indexArgLabelLocs; + SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; ParserStatus status = parseExprList( tok::l_square, tok::r_square, /*isPostfix=*/true, isExprBasic, lSquareLoc, indexArgs, - indexArgLabels, indexArgLabelLocs, rSquareLoc, trailingClosures, + indexArgLabels, indexArgLabelLocs, rSquareLoc, + trailingLBrace, trailingRBrace, trailingClosures, SyntaxKind::TupleExprElementList); Result = makeParserResult( status | Result, SubscriptExpr::create(Context, Result.get(), lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, + trailingLBrace, trailingRBrace, trailingClosures, ConcreteDeclRef(), /*implicit=*/false)); SyntaxContext->createNodeInPlace(SyntaxKind::SubscriptExpr); @@ -1218,9 +1221,11 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, leadingTriviaLoc(), *SyntaxContext)); } + SourceLoc LBrace, RBrace; SmallVector trailingClosures; auto trailingResult = - parseTrailingClosures(callee->getSourceRange(), trailingClosures); + parseTrailingClosures(isExprBasic, callee->getSourceRange(), LBrace, + RBrace, trailingClosures); if (trailingClosures.empty()) return nullptr; @@ -1228,7 +1233,8 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, Result = makeParserResult( ParserStatus(Result) | trailingResult, CallExpr::create(Context, Result.get(), SourceLoc(), {}, {}, {}, - SourceLoc(), trailingClosures, /*implicit=*/false)); + SourceLoc(), LBrace, RBrace, trailingClosures, + /*implicit=*/false)); SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); // We only allow a single trailing closure on a call. This could be @@ -1604,6 +1610,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; + SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, @@ -1611,6 +1618,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { lParenLoc, args, argLabels, argLabelLocs, rParenLoc, + trailingLBrace, trailingRBrace, trailingClosures, SyntaxKind::TupleExprElementList); SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); @@ -1619,6 +1627,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, + trailingLBrace, trailingRBrace, trailingClosures, /*implicit=*/false)); } @@ -1632,9 +1641,10 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { leadingTriviaLoc(), *SyntaxContext)); } + SourceLoc LBrace, RBrace; SmallVector trailingClosures; - auto result = - parseTrailingClosures(NameLoc.getSourceRange(), trailingClosures); + auto result = parseTrailingClosures(isExprBasic, NameLoc.getSourceRange(), + LBrace, RBrace, trailingClosures); if (trailingClosures.empty()) return nullptr; @@ -1643,7 +1653,8 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { return makeParserResult( result, UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, SourceLoc(), {}, {}, {}, - SourceLoc(), trailingClosures, + SourceLoc(), LBrace, RBrace, + trailingClosures, /*implicit=*/false)); } @@ -3011,6 +3022,7 @@ Parser::parseExprList(tok leftTok, tok rightTok, SyntaxKind Kind) { SmallVector trailingClosures; SourceLoc leftLoc, rightLoc; + SourceLoc trailingLBrace, trailingRBrace; ParserStatus status = parseExprList(leftTok, rightTok, /*isPostfix=*/false, /*isExprBasic=*/true, leftLoc, @@ -3018,6 +3030,8 @@ Parser::parseExprList(tok leftTok, tok rightTok, SyntaxKind Kind) { subExprNames, subExprNameLocs, rightLoc, + trailingLBrace, + trailingRBrace, trailingClosures, Kind); @@ -3054,6 +3068,8 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, SmallVectorImpl &exprLabels, SmallVectorImpl &exprLabelLocs, SourceLoc &rightLoc, + SourceLoc &trailingLBrace, + SourceLoc &trailingRBrace, SmallVectorImpl &trailingClosures, SyntaxKind Kind) { StructureMarkerRAII ParsingExprList(*this, Tok); @@ -3137,12 +3153,86 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, // Parse the closure. status |= - parseTrailingClosures(SourceRange(leftLoc, rightLoc), trailingClosures); + parseTrailingClosures(isExprBasic, SourceRange(leftLoc, rightLoc), + trailingLBrace, trailingRBrace, trailingClosures); return status; } +/// Check whether upcoming trailing closure actually represents a block of +/// labeled trailing closures e.g. +/// foo() { +/// : { ... } +/// ... +/// } +static bool isBlockOfMultipleTrailingClosures(bool isExprBasic, Parser &P) { + assert(isValidTrailingClosure(isExprBasic, P)); + Parser::BacktrackingScope backtrack(P); + P.consumeToken(tok::l_brace); + + if (!P.Tok.canBeArgumentLabel() || P.Tok.is(tok::kw__)) + return false; + + auto text = P.Tok.getText(); + if (text[0] == '$') + return false; + + P.consumeToken(); // Consume Label text. + + if (!P.Tok.is(tok::colon)) + return false; + + P.consumeToken(); // Consume `:` + + // If the next token after the label is not a left brace + // that means what follows is not a valid closure, which + // in turn means it can't be a labeled block. + if (!P.Tok.is(tok::l_brace)) + return false; + + return isValidTrailingClosure(isExprBasic, P); +} + +ParserStatus Parser::parseMultipleTrailingClosures( + SourceLoc &LBrace, SourceLoc &RBrace, + SmallVectorImpl &closures) { + // Consume '{' of the trailing closure + LBrace = consumeToken(); + + // There could N labeled closures depending on the number of arguments. + do { + if (!(Tok.canBeArgumentLabel() && peekToken().is(tok::colon))) + return makeParserError(); + + Identifier label; + auto labelLoc = consumeArgumentLabel(label); + if (!labelLoc.isValid()) + return makeParserError(); + + consumeToken(); // Consume ':' + + auto closure = parseExprClosure(); + if (closure.isNull()) + return makeParserError(); + + closures.push_back({label, labelLoc, closure.get()}); + + // Recover if blocks are separated by comma instead of newline. + if (Tok.is(tok::comma)) { + diagnose(Tok.getLoc(), diag::unexpected_separator, ",") + .fixItRemove(Tok.getLoc()); + consumeToken(); + } + } while (!Tok.is(tok::r_brace)); + + // Consume `}` of the trailing closure. + RBrace = consumeToken(); + + return makeParserSuccess(); +} + ParserStatus -Parser::parseTrailingClosures(SourceRange calleeRange, +Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, + SourceLoc &LBrace, SourceLoc &RBrace, SmallVectorImpl &closures) { SourceLoc braceLoc = Tok.getLoc(); @@ -3152,10 +3242,22 @@ Parser::parseTrailingClosures(SourceRange calleeRange, auto origLine = SourceMgr.getLineNumber(calleeRange.End); auto braceLine = SourceMgr.getLineNumber(braceLoc); - // Parse the closure. - ParserResult closure = parseExprClosure(); - if (closure.isNull()) - return makeParserError(); + ParserStatus result; + + if (isBlockOfMultipleTrailingClosures(isExprBasic, *this)) { + result |= parseMultipleTrailingClosures(LBrace, RBrace, closures); + } else { + // Parse the closure. + ParserResult closure = parseExprClosure(); + if (closure.isNull()) + return makeParserError(); + + result |= closure; + + LBrace = braceLoc; + RBrace = closure.get()->getEndLoc(); + closures.push_back({closure.get()}); + } // Warn if the trailing closure is separated from its callee by more than // one line. A single-line separation is acceptable for a trailing closure @@ -3163,17 +3265,18 @@ Parser::parseTrailingClosures(SourceRange calleeRange, if (braceLine > origLine + 1) { diagnose(braceLoc, diag::trailing_closure_after_newlines); diagnose(calleeRange.Start, diag::trailing_closure_callee_here); - - auto *CE = dyn_cast(closure.get()); - if (CE && CE->hasAnonymousClosureVars() && - CE->getParameters()->size() == 0) { - diagnose(braceLoc, diag::brace_stmt_suggest_do) - .fixItInsert(braceLoc, "do "); + + if (closures.size() == 1) { + auto *CE = dyn_cast(closures[0].ClosureExpr); + if (CE && CE->hasAnonymousClosureVars() && + CE->getParameters()->size() == 0) { + diagnose(braceLoc, diag::brace_stmt_suggest_do) + .fixItInsert(braceLoc, "do "); + } } } - closures.push_back({closure.get()}); - return closure; + return result; } /// Parse an object literal expression. @@ -3197,6 +3300,7 @@ Parser::parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LitKind, SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; + SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, @@ -3204,6 +3308,8 @@ Parser::parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LitKind, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, + trailingLBrace, + trailingRBrace, trailingClosures, SyntaxKind::TupleExprElementList); if (status.hasCodeCompletion()) @@ -3214,6 +3320,7 @@ Parser::parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LitKind, return makeParserResult( ObjectLiteralExpr::create(Context, PoundLoc, LitKind, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, + trailingLBrace, trailingRBrace, trailingClosures, /*implicit=*/false)); } @@ -3239,13 +3346,15 @@ ParserResult Parser::parseExprPoundUnknown(SourceLoc LSquareLoc) { SmallVector argLabelLocs; SmallVector args; SmallVector argLabels; + SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; if (Tok.isFollowingLParen()) { // Parse arguments. ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, /*isExprBasic*/ true, LParenLoc, - args, argLabels, argLabelLocs, RParenLoc, trailingClosures, + args, argLabels, argLabelLocs, RParenLoc, + trailingLBrace, trailingRBrace, trailingClosures, SyntaxKind::TupleExprElementList); if (status.hasCodeCompletion()) return makeParserCodeCompletionResult(); @@ -3340,6 +3449,7 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { { Identifier() }, { }, SourceLoc(), + SourceLoc(), SourceLoc(), /*trailingClosures=*/{}, /*implicit=*/false)); CodeCompletion->completePostfixExprParen(fn.get(), CCE); @@ -3354,6 +3464,7 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; + SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, @@ -3361,6 +3472,8 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { lParenLoc, args, argLabels, argLabelLocs, rParenLoc, + trailingLBrace, + trailingRBrace, trailingClosures, SyntaxKind::TupleExprElementList); @@ -3368,6 +3481,7 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { return makeParserResult(status | fn, CallExpr::create(Context, fn.get(), lParenLoc, args, argLabels, argLabelLocs, rParenLoc, + trailingLBrace, trailingRBrace, trailingClosures, /*implicit=*/false)); } diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 8b29e0e10d365..3f951c7f72f17 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -917,12 +917,14 @@ ParserResult Parser::parseTypedPattern() { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; + SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, /*isExprBasic=*/false, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, + trailingLBrace, trailingRBrace, trailingClosures, SyntaxKind::Unknown); if (status.isSuccess()) { diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 5c6dfdc92d8c2..ca1646e853503 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -845,6 +845,7 @@ ParserResult Parser::parseStmtYield(SourceLoc tryLoc) { SmallVector yieldLabels; SmallVector yieldLabelLocs; + SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; status = parseExprList(tok::l_paren, tok::r_paren, @@ -853,6 +854,7 @@ ParserResult Parser::parseStmtYield(SourceLoc tryLoc) { lpLoc, yields, yieldLabels, yieldLabelLocs, rpLoc, + trailingLBrace, trailingRBrace, trailingClosures, SyntaxKind::ExprList); assert(trailingClosures.empty()); diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index edd71fbacd154..49600997108e1 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -101,6 +101,8 @@ class BuilderClosureVisitor SourceLoc closeLoc = args.empty() ? loc : args.back()->getEndLoc(); Expr *result = CallExpr::create(ctx, memberRef, openLoc, args, argLabels, argLabelLocs, closeLoc, + /*trailingLBrace=*/SourceLoc(), + /*trailingRBrace=*/SourceLoc(), /*trailing closures*/{}, /*implicit*/true); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 89e567b34a111..a15ddaf4877ff 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2001,14 +2001,19 @@ namespace { arguments.push_back(SE->getIndex()->getSemanticsProvidingExpr()); } + SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; - if (SE->hasTrailingClosure()) - trailingClosures.push_back({arguments.back()}); + if (SE->hasTrailingClosure()) { + auto *closure = arguments.back(); + trailingLBrace = closure->getStartLoc(); + trailingRBrace = closure->getEndLoc(); + trailingClosures.push_back({closure}); + } componentExpr = SubscriptExpr::create( ctx, dotExpr, SE->getStartLoc(), arguments, SE->getArgumentLabels(), SE->getArgumentLabelLocs(), - SE->getEndLoc(), trailingClosures, + SE->getEndLoc(), trailingLBrace, trailingRBrace, trailingClosures, SE->hasDecl() ? SE->getDecl() : ConcreteDeclRef(), /*implicit=*/true, SE->getAccessSemantics()); } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 8c57b244177b2..93d744b8b07d5 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -995,6 +995,7 @@ namespace { E = CallExpr::create(Context, newCallee, lParen, {newArg}, {Identifier()}, {SourceLoc()}, rParen, + SourceLoc(), SourceLoc(), /*trailingClosures=*/{}, /*implicit=*/false); } diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 4a0c2b110324b..1b5ff0625df15 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -747,10 +747,12 @@ Expr *swift::buildPropertyWrapperWrappedValueCall( if (endLoc.isInvalid() && startLoc.isValid()) endLoc = wrapperAttrs[i]->getTypeLoc().getSourceRange().End; - auto *init = CallExpr::create( - ctx, typeExpr, startLoc, {initializer}, {argName}, - {initializer->getStartLoc()}, endLoc, - /*trailingClosures=*/{}, /*implicit=*/true); + auto *init = + CallExpr::create(ctx, typeExpr, startLoc, {initializer}, {argName}, + {initializer->getStartLoc()}, endLoc, + /*trailingLBrace=*/SourceLoc(), + /*trailingRBrace=*/SourceLoc(), + /*trailingClosures=*/{}, /*implicit=*/true); initializer = init; if (!innermostInit) @@ -783,9 +785,11 @@ Expr *swift::buildPropertyWrapperWrappedValueCall( if (endLoc.isInvalid() && startLoc.isValid()) endLoc = wrapperAttrs[i]->getTypeLoc().getSourceRange().End; - auto *init = CallExpr::create( - ctx, typeExpr, startLoc, elements, elementNames, elementLocs, - endLoc, /*trailingClosures=*/{}, /*implicit=*/true); + auto *init = CallExpr::create(ctx, typeExpr, startLoc, elements, + elementNames, elementLocs, endLoc, + /*trailingLBrace=*/SourceLoc(), + /*trailingRBrace=*/SourceLoc(), + /*trailingClosures=*/{}, /*implicit=*/true); initializer = init; if (!innermostInit) diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 6550213718039..72bd61443fb7b 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -891,6 +891,7 @@ static Expr *buildStorageReference(AccessorDecl *accessor, lookupExpr = SubscriptExpr::create( ctx, wrapperMetatype, SourceLoc(), args, subscriptDecl->getName().getArgumentNames(), { }, SourceLoc(), + /*trailingLBrace=*/SourceLoc(), /*trailingRBrace=*/SourceLoc(), /*trailingClosures=*/{}, subscriptDecl, /*Implicit=*/true); // FIXME: Since we're not resolving overloads or anything, we should be diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index f9b6e23a298a1..2eacf3e1b4497 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -972,6 +972,7 @@ func test_correct_inference_of_closure_result_in_presence_of_optionals() { } } + // rdar://problem/59741308 - inference fails with tuple element has to joined to supertype func rdar_59741308() { class Base { From 93c0d85a96ad97bb4d6485259688b7aba892e95d Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 3 Mar 2020 15:31:31 -0800 Subject: [PATCH 07/37] [Parser] NFC: Move multiple trailing closures tests into a separate file --- test/Parse/multiple_trailing_closures.swift | 73 +++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 test/Parse/multiple_trailing_closures.swift diff --git a/test/Parse/multiple_trailing_closures.swift b/test/Parse/multiple_trailing_closures.swift new file mode 100644 index 0000000000000..9aec5a09c3229 --- /dev/null +++ b/test/Parse/multiple_trailing_closures.swift @@ -0,0 +1,73 @@ +// RUN: %target-typecheck-verify-swift + +func foo(a: () -> T, b: () -> U) {} + +foo { + a: { 42 } + b: { "" } +} + +foo { a: { 42 } b: { "" } } + +foo { + a: { 42 }, // expected-error {{unexpected ',' separator}} {{12-13=}} + b: { "" } +} + +func when(_ condition: @autoclosure () -> Bool, + `then` trueBranch: () -> T, + `else` falseBranch: () -> T) -> T { + return condition() ? trueBranch() : falseBranch() +} + +let _ = when (2 < 3) { + then: { 3 } + else: { 4 } +} + +struct S { + static func foo(a: Int = 42, b: (inout Int) -> Void) -> S { + return S() + } + + subscript(v v: () -> Int) -> Int { + get { return v() } + } + + subscript(cond: Bool, v v: () -> Int) -> Int { + get { return cond ? 0 : v() } + } +} + +let _: S = .foo { + b: { $0 = $0 + 1 } +} + +func bar(_ s: S) { + _ = s[] { + v: { 42 } + } + + _ = s[true] { + v: { 42 } + } +} + +func multiple_trailing_with_defaults( + duration: Int, + animations: (() -> Void)? = nil, + completion: (() -> Void)? = nil) {} + +multiple_trailing_with_defaults(duration: 42) { + animations: {} +} + +multiple_trailing_with_defaults(duration: 42) { + completion: {} +} + +multiple_trailing_with_defaults(duration: 42) { + animations: {} + completion: {} +} + From ed5b6958f37f696a2cadb47c95016966e36aef13 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 3 Mar 2020 15:37:45 -0800 Subject: [PATCH 08/37] [Parse] Introduce tailored diagnostics for an invalid trailing closures block If block is not just a list of labeled closures produce a tailored diagnostic and skip over invalid code. --- include/swift/AST/DiagnosticsParse.def | 9 +++++++++ lib/Parse/ParseExpr.cpp | 13 +++++++++---- test/Parse/multiple_trailing_closures.swift | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 1222a70cd3ade..26b0746c7050f 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1750,5 +1750,14 @@ ERROR(availability_query_repeated_platform, none, ERROR(unknown_syntax_entity, PointsToFirstBadToken, "unknown %0 syntax exists in the source", (StringRef)) +//------------------------------------------------------------------------------ +// MARK: multiple trailing closures diagnostics +//------------------------------------------------------------------------------ +ERROR(expected_argument_label_followed_by_closure_literal,none, + "expected an argument label followed by a closure literal", ()) + +ERROR(expected_multiple_closures_block_rbrace,none, + "expected '}' at the end of a trailing closures block", ()) + #define UNDEFINE_DIAGNOSTIC_MACROS #include "DefineDiagnosticMacros.h" diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 4b44a8b6db18e..2416f2d6f1900 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3196,12 +3196,16 @@ ParserStatus Parser::parseMultipleTrailingClosures( SourceLoc &LBrace, SourceLoc &RBrace, SmallVectorImpl &closures) { // Consume '{' of the trailing closure - LBrace = consumeToken(); + LBrace = consumeToken(tok::l_brace); // There could N labeled closures depending on the number of arguments. do { - if (!(Tok.canBeArgumentLabel() && peekToken().is(tok::colon))) - return makeParserError(); + if (!(Tok.canBeArgumentLabel() && peekToken().is(tok::colon))) { + diagnose(Tok.getLoc(), + diag::expected_argument_label_followed_by_closure_literal); + skipUntilDeclStmtRBrace(tok::r_brace); + break; + } Identifier label; auto labelLoc = consumeArgumentLabel(label); @@ -3225,7 +3229,8 @@ ParserStatus Parser::parseMultipleTrailingClosures( } while (!Tok.is(tok::r_brace)); // Consume `}` of the trailing closure. - RBrace = consumeToken(); + parseMatchingToken(tok::r_brace, RBrace, + diag::expected_multiple_closures_block_rbrace, LBrace); return makeParserSuccess(); } diff --git a/test/Parse/multiple_trailing_closures.swift b/test/Parse/multiple_trailing_closures.swift index 9aec5a09c3229..7e2a46af6a0ca 100644 --- a/test/Parse/multiple_trailing_closures.swift +++ b/test/Parse/multiple_trailing_closures.swift @@ -71,3 +71,18 @@ multiple_trailing_with_defaults(duration: 42) { completion: {} } +foo { + a: { 42 } + b: { 42 } + + _ = 1 + 2 + // expected-error@-1 {{expected an argument label followed by a closure literal}} +} + +foo { // expected-note {{to match this opening '{'}} + a: { 42 } + b: { "" } + + func foo() {} // expected-error {{expected an argument label followed by a closure literal}} + // expected-error@-1 {{expected '}' at the end of a trailing closures block}} +} // expected-error {{extraneous '}' at top level}} From 08a29e8da6aebe4ec084b67d96b9dc78ff3ac7ec Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 3 Mar 2020 16:10:16 -0800 Subject: [PATCH 09/37] [Parser] Check for first label in trailing closure block without backtracking --- lib/Parse/ParseExpr.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 2416f2d6f1900..707073b4d59b0 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3166,16 +3166,18 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, /// } static bool isBlockOfMultipleTrailingClosures(bool isExprBasic, Parser &P) { assert(isValidTrailingClosure(isExprBasic, P)); - Parser::BacktrackingScope backtrack(P); - P.consumeToken(tok::l_brace); - if (!P.Tok.canBeArgumentLabel() || P.Tok.is(tok::kw__)) - return false; - - auto text = P.Tok.getText(); - if (text[0] == '$') - return false; + // If closure doesn't start from a label there is no reason + // to look any farther. + { + const auto &nextTok = P.peekToken(); + if (!nextTok.canBeArgumentLabel() || nextTok.is(tok::kw__) || + nextTok.getText()[0] == '$') + return false; + } + Parser::BacktrackingScope backtrack(P); + P.consumeToken(tok::l_brace); P.consumeToken(); // Consume Label text. if (!P.Tok.is(tok::colon)) From c8a8b890f35d362c7c37b3820f9c28ced8ebdce1 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 3 Mar 2020 17:07:32 -0800 Subject: [PATCH 10/37] [Parser] Propagate status information from each closure in the trailing block --- lib/Parse/ParseExpr.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 707073b4d59b0..eef5149084ae8 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3180,11 +3180,9 @@ static bool isBlockOfMultipleTrailingClosures(bool isExprBasic, Parser &P) { P.consumeToken(tok::l_brace); P.consumeToken(); // Consume Label text. - if (!P.Tok.is(tok::colon)) + if (!P.consumeIf(tok::colon)) // Consume `:` return false; - P.consumeToken(); // Consume `:` - // If the next token after the label is not a left brace // that means what follows is not a valid closure, which // in turn means it can't be a labeled block. @@ -3200,9 +3198,11 @@ ParserStatus Parser::parseMultipleTrailingClosures( // Consume '{' of the trailing closure LBrace = consumeToken(tok::l_brace); + ParserStatus Status; // There could N labeled closures depending on the number of arguments. do { if (!(Tok.canBeArgumentLabel() && peekToken().is(tok::colon))) { + Status.setIsParseError(); diagnose(Tok.getLoc(), diag::expected_argument_label_followed_by_closure_literal); skipUntilDeclStmtRBrace(tok::r_brace); @@ -3220,6 +3220,8 @@ ParserStatus Parser::parseMultipleTrailingClosures( if (closure.isNull()) return makeParserError(); + Status |= closure; + closures.push_back({label, labelLoc, closure.get()}); // Recover if blocks are separated by comma instead of newline. @@ -3231,10 +3233,13 @@ ParserStatus Parser::parseMultipleTrailingClosures( } while (!Tok.is(tok::r_brace)); // Consume `}` of the trailing closure. - parseMatchingToken(tok::r_brace, RBrace, - diag::expected_multiple_closures_block_rbrace, LBrace); + if (parseMatchingToken(tok::r_brace, RBrace, + diag::expected_multiple_closures_block_rbrace, + LBrace)) { + Status.setIsParseError(); + } - return makeParserSuccess(); + return Status; } ParserStatus From 150df80a75346b9d6ea683e8b3c9478d729fedd7 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 3 Mar 2020 16:23:01 -0800 Subject: [PATCH 11/37] [Syntax] Add declaration for multiple trailing closure --- lib/Parse/ParseExpr.cpp | 6 +++ .../round_trip_parse_gen.swift.withkinds | 20 ++++++++- test/Syntax/round_trip_parse_gen.swift | 18 ++++++++ utils/gyb_syntax_support/ExprNodes.py | 42 +++++++++++++++++-- .../NodeSerializationCodes.py | 3 ++ 5 files changed, 84 insertions(+), 5 deletions(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index eef5149084ae8..c8c1e23191870 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3195,6 +3195,9 @@ static bool isBlockOfMultipleTrailingClosures(bool isExprBasic, Parser &P) { ParserStatus Parser::parseMultipleTrailingClosures( SourceLoc &LBrace, SourceLoc &RBrace, SmallVectorImpl &closures) { + SyntaxParsingContext ClauseCtx(SyntaxContext, + SyntaxKind::MultipleTrailingClosureClause); + // Consume '{' of the trailing closure LBrace = consumeToken(tok::l_brace); @@ -3209,6 +3212,8 @@ ParserStatus Parser::parseMultipleTrailingClosures( break; } + SyntaxParsingContext ElementCtx(SyntaxContext, + SyntaxKind::MultipleTrailingClosureElement); Identifier label; auto labelLoc = consumeArgumentLabel(label); if (!labelLoc.isValid()) @@ -3231,6 +3236,7 @@ ParserStatus Parser::parseMultipleTrailingClosures( consumeToken(); } } while (!Tok.is(tok::r_brace)); + SyntaxContext->collectNodesInPlace(SyntaxKind::MultipleTrailingClosureElementList); // Consume `}` of the trailing closure. if (parseMatchingToken(tok::r_brace, RBrace, diff --git a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index 253bd6948c54e..34fa092078366 100644 --- a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds +++ b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -94,6 +94,9 @@ class C { _ = .foo _ = .foo(x: 12) _ = .foo { 12 } + _ = .foo { + arg1: { 12 } + } _ = .foo[12] _ = .foo.bar } @@ -257,14 +260,29 @@ func closure() () { foo() foo() {} + foo {} + foo { + arg1: {} + } + foo() { + arg1: {} + arg2: {} + } foo {} foo.bar() foo.bar() {} + foo.bar() { + arg1: {} + } foo.bar {} foo[] foo[1] foo[] {} - foo[1] {} + foo[1] {} + foo[1] { + arg1: {} + arg2: {} + } foo[1][2,x:3] foo?++.bar!(baz).self foo().0 diff --git a/test/Syntax/round_trip_parse_gen.swift b/test/Syntax/round_trip_parse_gen.swift index 55526163f7615..271d537a52719 100644 --- a/test/Syntax/round_trip_parse_gen.swift +++ b/test/Syntax/round_trip_parse_gen.swift @@ -94,6 +94,9 @@ class C { _ = .foo _ = .foo(x: 12) _ = .foo { 12 } + _ = .foo { + arg1: { 12 } + } _ = .foo[12] _ = .foo.bar } @@ -258,13 +261,28 @@ func postfix() { foo() foo() {} foo {} + foo { + arg1: {} + } + foo() { + arg1: {} + arg2: {} + } + foo {} foo.bar() foo.bar() {} + foo.bar() { + arg1: {} + } foo.bar {} foo[] foo[1] foo[] {} foo[1] {} + foo[1] { + arg1: {} + arg2: {} + } foo[1][2,x:3] foo?++.bar!(baz).self foo().0 diff --git a/utils/gyb_syntax_support/ExprNodes.py b/utils/gyb_syntax_support/ExprNodes.py index 8f9835970008f..424f8981f39c3 100644 --- a/utils/gyb_syntax_support/ExprNodes.py +++ b/utils/gyb_syntax_support/ExprNodes.py @@ -394,6 +394,32 @@ Child('Pattern', kind='Pattern'), ]), + # trailing-closure-element -> identifier ':' closure-expression + Node('MultipleTrailingClosureElement', kind='Syntax', + children=[ + Child('Label', kind='Token', + token_choices=[ + 'IdentifierToken', + 'WildcardToken' + ]), + Child('Colon', kind='ColonToken'), + Child('Closure', kind='ClosureExpr'), + ]), + + Node('MultipleTrailingClosureElementList', kind='SyntaxCollection', + element='MultipleTrailingClosureElement'), + + # multiple-trailing-closure-clause -> + # '{' multiple-trailing-closure-element-list '}' + Node('MultipleTrailingClosureClause', kind='Syntax', + traits=['Braced'], + children=[ + Child('LeftBrace', kind='LeftBraceToken'), + Child('Elements', kind='MultipleTrailingClosureElementList', + collection_element_name='Element'), + Child('RightBrace', kind='RightBraceToken'), + ]), + # call-expr -> expr '(' call-argument-list ')' closure-expr? # | expr closure-expr Node('FunctionCallExpr', kind='Expr', @@ -405,8 +431,12 @@ collection_element_name='Argument'), Child('RightParen', kind='RightParenToken', is_optional=True), - Child('TrailingClosure', kind='ClosureExpr', - is_optional=True), + Child('TrailingClosure', kind='Syntax', is_optional=True, + node_choices=[ + Child('SingleClosure', kind='ClosureExpr'), + Child('MultipleTrailingClosures', + kind='MultipleTrailingClosureClause'), + ]), ]), # subscript-expr -> expr '[' call-argument-list ']' closure-expr? @@ -417,8 +447,12 @@ Child('ArgumentList', kind='TupleExprElementList', collection_element_name='Argument'), Child('RightBracket', kind='RightSquareBracketToken'), - Child('TrailingClosure', kind='ClosureExpr', - is_optional=True), + Child('TrailingClosure', kind='Syntax', is_optional=True, + node_choices=[ + Child('SingleClosure', kind='ClosureExpr'), + Child('MultipleTrailingClosures', + kind='MultipleTrailingClosureClause'), + ]), ]), # optional-chaining-expr -> expr '?' diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 5647189cd397f..d542eb2e07921 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -246,6 +246,9 @@ 'QualifiedDeclName': 242, 'CatchItem': 243, 'CatchItemList': 244, + 'MultipleTrailingClosureClause': 245, + 'MultipleTrailingClosureElementList': 246, + 'MultipleTrailingClosureElement': 247, } From 4c4990ead4d8e91fc851ce4c43abc23e3322d608 Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Sat, 14 Mar 2020 13:36:14 -0700 Subject: [PATCH 12/37] Multiple trailing closure fixes - Diagnose non-closure-literals after label + colon - Form a TupleExpr, rather than a ParenExpr for a single labelled trailing closure. --- include/swift/AST/DiagnosticsParse.def | 2 ++ lib/AST/Expr.cpp | 3 ++- lib/Parse/ParseExpr.cpp | 17 +++++++++++++---- test/Parse/multiple_trailing_closures.swift | 13 +++++++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 26b0746c7050f..1a79bcc0da34d 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1755,6 +1755,8 @@ ERROR(unknown_syntax_entity, PointsToFirstBadToken, //------------------------------------------------------------------------------ ERROR(expected_argument_label_followed_by_closure_literal,none, "expected an argument label followed by a closure literal", ()) +ERROR(expected_closure_literal,none, + "expected a closure literal", ()) ERROR(expected_multiple_closures_block_rbrace,none, "expected '}' at the end of a trailing closures block", ()) diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index b6d72f473bfd5..cc58fd6a03b5b 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1020,7 +1020,8 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, // If we have no other arguments, represent the a single trailing closure as a // parenthesized expression. - if (args.empty() && trailingClosures.size() == 1) { + if (args.empty() && trailingClosures.size() == 1 && + trailingClosures.front().LabelLoc.isInvalid()) { auto &trailingClosure = trailingClosures.front(); auto arg = new (ctx) ParenExpr(lParenLoc, trailingClosure.ClosureExpr, rParenLoc, diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index c8c1e23191870..7a837d9c07dca 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3221,12 +3221,21 @@ ParserStatus Parser::parseMultipleTrailingClosures( consumeToken(); // Consume ':' - auto closure = parseExprClosure(); - if (closure.isNull()) - return makeParserError(); + SourceLoc closureLoc = Tok.getLoc(); + auto closure = parseExpr(diag::expected_closure_literal); + if (closure.isNull()) { + Status.setIsParseError(); + skipUntilDeclStmtRBrace(tok::r_brace); + break; + } - Status |= closure; + // Allow any expression, but diagnose if not a closure literal. + if (!isa(closure.get()) && + !isa(closure.get())) { + diagnose(closureLoc, diag::expected_closure_literal); + } + Status |= closure; closures.push_back({label, labelLoc, closure.get()}); // Recover if blocks are separated by comma instead of newline. diff --git a/test/Parse/multiple_trailing_closures.swift b/test/Parse/multiple_trailing_closures.swift index 7e2a46af6a0ca..0e3fdbb9bf567 100644 --- a/test/Parse/multiple_trailing_closures.swift +++ b/test/Parse/multiple_trailing_closures.swift @@ -79,6 +79,19 @@ foo { // expected-error@-1 {{expected an argument label followed by a closure literal}} } +foo { + a: { 42 } + b: { 45 } + c: +} // expected-error {{expected a closure literal}} + +foo { + a: { 42 } + b: { 45 } + c: 67 // expected-error {{expected a closure literal}} + // expected-error@-1 {{extra argument 'c' in call}} +} + foo { // expected-note {{to match this opening '{'}} a: { 42 } b: { "" } From c5c8c584a1069e0926cf88c77ce2e5fa787dcecb Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Sat, 14 Mar 2020 15:02:55 -0700 Subject: [PATCH 13/37] Add indentation support for multiple trailing closures. Resolves rdar://problem/60250267 --- include/swift/AST/Expr.h | 6 + lib/IDE/Formatting.cpp | 75 +++++++++--- .../multiple-trailing-closures.swift | 107 ++++++++++++++++++ 3 files changed, 171 insertions(+), 17 deletions(-) create mode 100644 test/swift-indent/multiple-trailing-closures.swift diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index bbe01dc4c05f9..04a4a0f46b81d 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -2180,6 +2180,12 @@ class TupleExpr final : public Expr, } unsigned getNumElements() const { return Bits.TupleExpr.NumElements; } + + unsigned getNumTrailingElements() const { + return FirstTrailingArgumentAt + ? getNumElements() - *FirstTrailingArgumentAt + : 0; + } Expr *getElement(unsigned i) const { return getElements()[i]; diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index be632aced8e39..22836cb2d6110 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -637,10 +637,9 @@ class RangeWalker: protected ASTWalker { return Stop; } if (PE->hasTrailingClosure()) { - if (auto *Last = PE->getSubExpr()) { - TC = dyn_cast(Last); - TCL = dyn_cast(Last); - } + auto *LastElem = PE->getSubExpr(); + TC = dyn_cast_or_null(LastElem); + TCL = dyn_cast_or_null(LastElem); } } else if (auto *TE = dyn_cast_or_null(Arg)) { if (isa(E)) { @@ -650,10 +649,15 @@ class RangeWalker: protected ASTWalker { if (!handleParens(TE->getLParenLoc(), TE->getRParenLoc(), ContextLoc)) return Stop; } - if (TE->hasTrailingClosure()) { - if (auto *Last = TE->getElements().back()) { - TC = dyn_cast(Last); - TCL = dyn_cast(Last); + if (TE->hasTrailingClosure() || TE->hasMultipleTrailingClosures()) { + if (TE->getTrailingLBraceLoc().isValid()) { + if (!handleBraces(TE->getTrailingLBraceLoc(), + TE->getTrailingRBraceLoc(), ContextLoc)) + return Stop; + } else { + Expr *LastElem = TE->getElements().back(); + TC = dyn_cast_or_null(LastElem); + TCL = dyn_cast_or_null(LastElem); } } } @@ -2327,13 +2331,18 @@ class FormatWalker : public ASTWalker { TCL = dyn_cast_or_null(Last); } } else if (auto *TE = dyn_cast_or_null(Arg)) { - if (auto Ctx = getIndentContextFrom(TE, ContextLoc)) { + if (auto Ctx = getIndentContextFrom(TE, ContextLoc)) return Ctx; - } - if (TE->hasTrailingClosure()) { - Expr *Last = TE->getElements().back(); - TCE = dyn_cast_or_null(Last); - TCL = dyn_cast_or_null(Last); + if (TE->hasTrailingClosure() || TE->hasMultipleTrailingClosures()) { + if (TE->getTrailingLBraceLoc().isValid()) { + if (auto Ctx = getIndentContextFromTrailingClosureGroup(TE, + ContextLoc)) + return Ctx; + } else { + Expr *LastElem = TE->getElements().back(); + TCE = dyn_cast_or_null(LastElem); + TCL = dyn_cast_or_null(LastElem); + } } } @@ -2485,9 +2494,7 @@ class FormatWalker : public ASTWalker { } ListAligner Aligner(SM, TargetLocation, ContextLoc, L, R); - auto NumElems = TE->getNumElements(); - if (TE->hasTrailingClosure()) - --NumElems; + auto NumElems = TE->getNumElements() - TE->getNumTrailingElements(); for (auto I : range(NumElems)) { SourceRange ElemRange = TE->getElementNameLoc(I); if (Expr *Elem = TE->getElement(I)) @@ -2506,6 +2513,40 @@ class FormatWalker : public ASTWalker { return Aligner.getContextAndSetAlignment(CtxOverride); } + Optional + getIndentContextFromTrailingClosureGroup(TupleExpr *TE, + SourceLoc ContextLoc) { + SourceLoc L = TE->getTrailingLBraceLoc(); + SourceLoc R = getLocIfKind(SM, TE->getTrailingRBraceLoc(), tok::r_brace); + + if (L.isInvalid() || !overlapsTarget(L, R)) + return None; + + ContextLoc = CtxOverride.propagateContext(SM, ContextLoc, + IndentContext::LineStart, + L, R); + auto NumElems = TE->getNumElements(); + auto FirstTrailing = NumElems - TE->getNumTrailingElements(); + for (auto I: range(FirstTrailing, NumElems)) { + SourceRange ElemRange = TE->getElementNameLoc(I); + if (Expr *Elem = TE->getElement(I)) + widenOrSet(ElemRange, Elem->getSourceRange()); + assert(ElemRange.isValid()); + if (isTargetContext(ElemRange)) { + return IndentContext { + ElemRange.Start, + !OutdentChecker::hasOutdent(SM, ElemRange, TE) + }; + } + } + SourceRange Range = SourceRange(L, TE->getEndLoc()); + return IndentContext { + ContextLoc, + containsTarget(L, R) && + !OutdentChecker::hasOutdent(SM, Range, TE, RangeKind::Open) + }; + } + Optional getIndentContextFrom(ParenExpr *PE, SourceLoc ContextLoc = SourceLoc()) { if (ContextLoc.isValid()) diff --git a/test/swift-indent/multiple-trailing-closures.swift b/test/swift-indent/multiple-trailing-closures.swift new file mode 100644 index 0000000000000..0e3dd404dba57 --- /dev/null +++ b/test/swift-indent/multiple-trailing-closures.swift @@ -0,0 +1,107 @@ +// RUN: %swift-indent %s >%t.response +// RUN: diff -u %s %t.response + +foo(c: 12, d: 34) { + a: { print("foo") } +} + +foo { + a: { print("foo") } +} + +foo { + a: { print("foo") } + b +} + +foo (c: 12, d: 34) { + a: { print("foo") } + b: +} + +// Invalid, but we should still indent correctly. +foo { + a: { + print("foo") + } + b: bar(a: 1, + b: 2) { + print("bar") + } +} + +foo { + a: { + print("foo") + } + b: { + print("bar") + } +} + +foo(c: 12, d: 34) { + a: { + print("foo") + } + b: { + print("bar") + } +} + +foo(c: 12, d: 34) { a: { + print("foo") +} b: { + print("bar") +}} + +foo(c: 12, d: 34) { a: {print("foo")} + b: {print("bar")} } + +foo(c: 12, d: 34) { a: {print("foo")} + /*comment*/b: { + print("bar") + }} + +foobar(c: 12, + d: 34) { + a: { + print("foo") + } + b: { + print("bar") + } +} + +foo(c: 12, d: 34) +{ + a: { + print("foo") + } + b: { + print("bar") + } +} + +foobar[c: 12, + d: 34] { + a: { + print("foo") + } + b: { + print("bar") + } +} + +foo(c: 12, d: 34) +{ + a: { + print("foo") + } + // comment + b: { + print("bar") + } + +func invalidCode() { + print("blah") +} \ No newline at end of file From d6dcd44e868e2082f55c53ca3ddab6dac7547ba7 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 13 Mar 2020 12:26:12 -0700 Subject: [PATCH 14/37] [Parse] Report "success" if '}' is found for multiple trailing closures --- lib/Parse/ParseExpr.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 7a837d9c07dca..8e78e35368afa 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3252,6 +3252,9 @@ ParserStatus Parser::parseMultipleTrailingClosures( diag::expected_multiple_closures_block_rbrace, LBrace)) { Status.setIsParseError(); + } else { + if (!Status.hasCodeCompletion()) + Status = makeParserSuccess(); } return Status; From 6bd20ca768ea82c927f0aedb3b429558460691a1 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 17 Mar 2020 18:02:18 -0700 Subject: [PATCH 15/37] [Parse] Allow multiple trailing closures to be delimited by `_:` Syntax like `foo { _: { ... } }` is going to be parsed as a single trailing closure. --- lib/Parse/ParseExpr.cpp | 5 +- test/Parse/multiple_trailing_closures.swift | 54 +++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 8e78e35368afa..36704472e27dc 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3170,9 +3170,8 @@ static bool isBlockOfMultipleTrailingClosures(bool isExprBasic, Parser &P) { // If closure doesn't start from a label there is no reason // to look any farther. { - const auto &nextTok = P.peekToken(); - if (!nextTok.canBeArgumentLabel() || nextTok.is(tok::kw__) || - nextTok.getText()[0] == '$') + const auto nextTok = P.peekToken(); + if (!nextTok.canBeArgumentLabel() || nextTok.getText()[0] == '$') return false; } diff --git a/test/Parse/multiple_trailing_closures.swift b/test/Parse/multiple_trailing_closures.swift index 0e3fdbb9bf567..a7b2b03d9926b 100644 --- a/test/Parse/multiple_trailing_closures.swift +++ b/test/Parse/multiple_trailing_closures.swift @@ -71,6 +71,60 @@ multiple_trailing_with_defaults(duration: 42) { completion: {} } +func test_multiple_trailing_syntax_without_labels() { + func fn(f: () -> Void) {} + + fn { + _: {} // Ok + } + + fn { + f: {} // Ok + } + + func multiple(_: () -> Void, _: () -> Void) {} + + multiple { + _: { } + _: { } + } + + func mixed_args_1(a: () -> Void, _: () -> Void) {} + func mixed_args_2(_: () -> Void, a: () -> Void, _: () -> Void) {} // expected-note 2 {{'mixed_args_2(_:a:_:)' declared here}} + + mixed_args_1 { + a: {} + _: {} + } + + mixed_args_1 { + _: {} + a: {} // expected-error {{argument 'a' must precede unnamed argument #1}} + } + + mixed_args_2 { + _: {} + a: {} + _: {} + } + + mixed_args_2 { + _: {} // expected-error {{missing argument for parameter 'a' in call}} + _: {} + } + + mixed_args_2 { + a: {} + _: {} + _: {} // expected-error {{unnamed argument #3 must precede argument 'a'}} + } + + mixed_args_2 { + a: {} // expected-error {{missing argument for parameter #1 in call}} + _: {} + } +} + foo { a: { 42 } b: { 42 } From fd68f092f21288d51d3c6477767cb12bd1adf6c6 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 17 Mar 2020 15:25:26 -0700 Subject: [PATCH 16/37] [CodeCompletion] Completion inside multiple trailing closure rdar://problem/59688477 --- lib/IDE/CodeCompletion.cpp | 14 ++- lib/IDE/CodeCompletionResultBuilder.h | 4 +- lib/IDE/ExprContextAnalysis.cpp | 96 +++++++++++++++++-- lib/Parse/ParseExpr.cpp | 50 +++++++--- .../complete_multiple_trailingclosure.swift | 85 ++++++++++++++++ 5 files changed, 223 insertions(+), 26 deletions(-) create mode 100644 test/IDE/complete_multiple_trailingclosure.swift diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 0919eb622376f..2cef1361e00c7 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -869,7 +869,8 @@ void CodeCompletionResultBuilder::addCallParameter(Identifier Name, bool IsVarArg, bool IsInOut, bool IsIUO, - bool isAutoClosure) { + bool isAutoClosure, + bool useUnderscoreLabel) { CurrentNestingLevel++; using ChunkKind = CodeCompletionString::Chunk::ChunkKind; @@ -910,6 +911,11 @@ void CodeCompletionResultBuilder::addCallParameter(Identifier Name, escapeKeyword(Name.str(), false, EscapedKeyword)); addChunkWithTextNoCopy( CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": "); + } else if (useUnderscoreLabel) { + addChunkWithTextNoCopy( + CodeCompletionString::Chunk::ChunkKind::CallParameterName, "_"); + addChunkWithTextNoCopy( + CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": "); } else if (!LocalName.empty()) { // Use local (non-API) parameter name if we have nothing else. llvm::SmallString<16> EscapedKeyword; @@ -2512,7 +2518,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { contextTy = typeContext->getDeclaredTypeInContext(); Builder.addCallParameter(argName, bodyName, paramTy, contextTy, - isVariadic, isInOut, isIUO, isAutoclosure); + isVariadic, isInOut, isIUO, isAutoclosure, + /*useUnderscoreLabel=*/false); modifiedBuilder = true; NeedComma = true; @@ -4160,7 +4167,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Builder.addCallParameter(Arg->getLabel(), Identifier(), Arg->getPlainType(), ContextType, Arg->isVariadic(), Arg->isInOut(), - /*isIUO=*/false, Arg->isAutoClosure()); + /*isIUO=*/false, Arg->isAutoClosure(), + /*useUnderscoreLabel=*/true); auto Ty = Arg->getPlainType(); if (Arg->isInOut()) { Ty = InOutType::get(Ty); diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index 67c19da19d871..cc70304bb6a7d 100644 --- a/lib/IDE/CodeCompletionResultBuilder.h +++ b/lib/IDE/CodeCompletionResultBuilder.h @@ -378,12 +378,12 @@ class CodeCompletionResultBuilder { void addCallParameter(Identifier Name, Identifier LocalName, Type Ty, Type ContextTy, bool IsVarArg, bool IsInOut, bool IsIUO, - bool isAutoClosure); + bool isAutoClosure, bool useUnderscoreLabel); void addCallParameter(Identifier Name, Type Ty, Type ContextTy = Type()) { addCallParameter(Name, Identifier(), Ty, ContextTy, /*IsVarArg=*/false, /*IsInOut=*/false, /*isIUO=*/false, - /*isAutoClosure=*/false); + /*isAutoClosure=*/false, /*useUnderscoreLabel=*/false); } void addGenericParameter(StringRef Name) { diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 4bfe04bc73517..a092e3b3f4262 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -561,6 +561,47 @@ static bool getPositionInArgs(DeclContext &DC, Expr *Args, Expr *CCExpr, return false; } +enum class ArgPositionKind { + /// The argument is in normal calling parenthesis. + /// i.e. foo(args..., ) { ... } + NormalArgument, + /// The argument is inside multiple trailing closure block. + /// i.e. foo(args...) { arg2: { ... } } + InClosureBlock, + /// The argument is inside multiple trailing closure block, also it is the + /// sole element. + /// foo(args...) { } + InEmptyClosureBlock, +}; + +static ArgPositionKind getArgPositionKind(DeclContext &DC, Expr *Args, + unsigned Position) { + SourceManager &SM = DC.getASTContext().SourceMgr; + + if (auto tuple = dyn_cast(Args)) { + SourceLoc argPos = tuple->getElement(Position)->getStartLoc(); + SourceLoc rParenLoc = tuple->getRParenLoc(); + if (rParenLoc.isInvalid() || SM.isBeforeInBuffer(rParenLoc, argPos)) { + // Invariant: If the token is at the label position, the label location is + // invalid, and the value is a CodeCompletionExpr. + if (Position == tuple->getNumElements() - 1 && + tuple->getNumTrailingElements() == 1 && + tuple->getElementNameLoc(Position).isInvalid()) { + return ArgPositionKind::InEmptyClosureBlock; + } + return ArgPositionKind::InClosureBlock; + } + } else if (auto paren = dyn_cast(Args)) { + SourceLoc argLoc = paren->getSubExpr()->getStartLoc(); + SourceLoc rParenLoc = paren->getRParenLoc(); + // We don't have a way to distingish between 'foo { _: }' and + // 'foo { }'. For now, consider it latter one. + if (rParenLoc.isInvalid() || SM.isBeforeInBuffer(rParenLoc, argLoc)) + return ArgPositionKind::InEmptyClosureBlock; + } + return ArgPositionKind::NormalArgument; +} + /// Given an expression and its context, the analyzer tries to figure out the /// expected type of the expression by analyzing its context. class ExprContextAnalyzer { @@ -607,6 +648,7 @@ class ExprContextAnalyzer { } else { llvm_unreachable("unexpected expression kind"); } + assert(!Candidates.empty()); PossibleCallees.assign(Candidates.begin(), Candidates.end()); // Determine the position of code completion token in call argument. @@ -615,6 +657,8 @@ class ExprContextAnalyzer { if (!getPositionInArgs(*DC, Arg, ParsedExpr, Position, HasName)) return false; + ArgPositionKind positionKind = getArgPositionKind(*DC, Arg, Position); + // Collect possible types (or labels) at the position. { bool MayNeedName = !HasName && !E->isImplicit() && @@ -637,20 +681,52 @@ class ExprContextAnalyzer { if (paramList && paramList->size() != Params.size()) paramList = nullptr; } + + // Determine the index of the parameter that can be a single trailing + // closure. + unsigned singleTrailingClosureIdx = Params.size(); + for (int idx = Params.size() - 1; idx >= 0; --idx) { + if (Params[idx].getPlainType()->is()) { + singleTrailingClosureIdx = idx; + break; + } + if (!paramList || !paramList->get(idx)->isDefaultArgument()) + break; + } + for (auto Pos = Position; Pos < Params.size(); ++Pos) { - const auto &Param = Params[Pos]; - Type ty = Param.getPlainType(); + const auto ¶mType = Params[Pos]; + Type ty = paramType.getPlainType(); if (memberDC && ty->hasTypeParameter()) ty = memberDC->mapTypeIntoContext(ty); - if (Param.hasLabel() && MayNeedName) { - if (seenArgs.insert({Param.getLabel(), ty.getPointer()}).second) - recordPossibleParam(Param); + if (paramType.hasLabel() && MayNeedName) { + // In trailing closure block, don't suggest non-closure arguments. + if (positionKind >= ArgPositionKind::InClosureBlock) { + Type argTy = ty; + if (paramType.isAutoClosure() && ty->is()) + argTy = ty->castTo()->getResult(); + if (!argTy->is()) + continue; + + // If the token is the only element in the closure block. It might + // be a single trailing closure. We should perform global + // completion as well. + if (positionKind == ArgPositionKind::InEmptyClosureBlock && + Pos == singleTrailingClosureIdx) { + auto resultTy = argTy->castTo()->getResult(); + if (seenTypes.insert(resultTy.getPointer()).second) + recordPossibleType(resultTy); + } + } + + if (seenArgs.insert({paramType.getLabel(), ty.getPointer()}).second) + recordPossibleParam(paramType); if (paramList && paramList->get(Position)->isDefaultArgument()) continue; } else { auto argTy = ty; - if (Param.isInOut()) + if (paramType.isInOut()) argTy = InOutType::get(argTy); if (seenTypes.insert(argTy.getPointer()).second) recordPossibleType(argTy); @@ -942,8 +1018,12 @@ class ExprContextAnalyzer { switch (E->getKind()) { case ExprKind::Call: { // Iff the cursor is in argument position. - auto argsRange = cast(E)->getArg()->getSourceRange(); - return SM.rangeContains(argsRange, ParsedExpr->getSourceRange()); + auto call = cast(E); + auto fnRange = call->getFn()->getSourceRange(); + auto argsRange = call->getArg()->getSourceRange(); + auto exprRange = ParsedExpr->getSourceRange(); + return !SM.rangeContains(fnRange, exprRange) && + SM.rangeContains(argsRange, exprRange); } case ExprKind::Subscript: { // Iff the cursor is in index position. diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 36704472e27dc..b28a12dfcdcd4 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3170,13 +3170,26 @@ static bool isBlockOfMultipleTrailingClosures(bool isExprBasic, Parser &P) { // If closure doesn't start from a label there is no reason // to look any farther. { - const auto nextTok = P.peekToken(); - if (!nextTok.canBeArgumentLabel() || nextTok.getText()[0] == '$') + const auto &nextTok = P.peekToken(); + if (!(nextTok.canBeArgumentLabel() || nextTok.is(tok::code_complete)) || + nextTok.getText()[0] == '$') return false; } Parser::BacktrackingScope backtrack(P); P.consumeToken(tok::l_brace); + + if (P.Tok.is(tok::code_complete)) { + // Parse '{ }' as a multiple trailing closure block. + if (P.peekToken().is(tok::r_brace)) + return true; + + // Otherwise, look through the code completion token. + P.consumeToken(tok::code_complete); + if (!P.Tok.canBeArgumentLabel() || P.Tok.getText()[0] == '$') + return false; + } + P.consumeToken(); // Consume Label text. if (!P.consumeIf(tok::colon)) // Consume `:` @@ -3204,6 +3217,18 @@ ParserStatus Parser::parseMultipleTrailingClosures( // There could N labeled closures depending on the number of arguments. do { if (!(Tok.canBeArgumentLabel() && peekToken().is(tok::colon))) { + + if (Tok.is(tok::code_complete)) { + auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc()); + closures.emplace_back(Identifier(), SourceLoc(), CCE); + if (CodeCompletion) + CodeCompletion->completeCallArg(CCE, /*isFirst=*/false); + + consumeToken(tok::code_complete); + Status.setHasCodeCompletion(); + continue; + } + Status.setIsParseError(); diagnose(Tok.getLoc(), diag::expected_argument_label_followed_by_closure_literal); @@ -3465,19 +3490,25 @@ ParserResult Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { assert(Tok.isFollowingLParen() && "Not a call suffix?"); - // Parse the first argument. + SourceLoc lParenLoc, rParenLoc; + SmallVector args; + SmallVector argLabels; + SmallVector argLabelLocs; + SourceLoc trailingLBrace, trailingRBrace; + SmallVector trailingClosures; // If there is a code completion token right after the '(', do a special case // callback. if (peekToken().is(tok::code_complete) && CodeCompletion) { - consumeToken(tok::l_paren); + lParenLoc = consumeToken(tok::l_paren); auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc()); + rParenLoc = Tok.getLoc(); auto Result = makeParserResult(fn, - CallExpr::create(Context, fn.get(), SourceLoc(), + CallExpr::create(Context, fn.get(), lParenLoc, { CCE }, { Identifier() }, { }, - SourceLoc(), + rParenLoc, SourceLoc(), SourceLoc(), /*trailingClosures=*/{}, /*implicit=*/false)); @@ -3489,13 +3520,6 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { } // Parse the argument list. - SourceLoc lParenLoc, rParenLoc; - SmallVector args; - SmallVector argLabels; - SmallVector argLabelLocs; - SourceLoc trailingLBrace, trailingRBrace; - SmallVector trailingClosures; - ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, isExprBasic, lParenLoc, args, argLabels, diff --git a/test/IDE/complete_multiple_trailingclosure.swift b/test/IDE/complete_multiple_trailingclosure.swift new file mode 100644 index 0000000000000..efad51de753b3 --- /dev/null +++ b/test/IDE/complete_multiple_trailingclosure.swift @@ -0,0 +1,85 @@ +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBAL_FUNC_1 | %FileCheck %s -check-prefix=GLOBAL_FUNC_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBAL_FUNC_2 | %FileCheck %s -check-prefix=GLOBAL_FUNC_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBAL_FUNC_3 | %FileCheck %s -check-prefix=GLOBAL_FUNC_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBAL_FUNC_4 | %FileCheck %s -check-prefix=GLOBAL_FUNC_4 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBAL_FUNC_5 | %FileCheck %s -check-prefix=GLOBAL_FUNC_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_1 | %FileCheck %s -check-prefix=METHOD_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_2 | %FileCheck %s -check-prefix=METHOD_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_3 | %FileCheck %s -check-prefix=METHOD_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_4 | %FileCheck %s -check-prefix=METHOD_4 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_5 | %FileCheck %s -check-prefix=METHOD_1 + + +func globalFunc1(fn1: () -> Int, fn2: () -> String) {} +func testGlobalFunc() { + globalFunc1 { + #^GLOBAL_FUNC_1^# +// GLOBAL_FUNC_1: Begin completions, 1 items +// GLOBAL_FUNC_1-DAG: Pattern/ExprSpecific: {#fn1: () -> Int##() -> Int#}[#() -> Int#]; +// GLOBAL_FUNC_1: End completions + } + globalFunc1() { + #^GLOBAL_FUNC_2^# +// Same as GLOBAL_FUNC_1 + } + globalFunc1() { + fn1: { 1 } + #^GLOBAL_FUNC_3^# +// GLOBAL_FUNC_3: Begin completions, 1 items +// GLOBAL_FUNC_3-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; name=fn2: () -> String +// GLOBAL_FUNC_3: End completions + } + + globalFunc1(fn1: { 1 }) { + #^GLOBAL_FUNC_4^# +// GLOBAL_FUNC_4: Begin completions +// GLOBAL_FUNC_4-NOT: Decl[Struct]/OtherModule[Swift]/TypeRelation[Identical]: Int[#Int#]; +// GLOBAL_FUNC_4-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// GLOBAL_FUNC_4-DAG: Decl[Struct]/OtherModule[Swift]/TypeRelation[Identical]: String[#String#]; +// GLOBAL_FUNC_4: End completions + } + + globalFunc1() { + #^GLOBAL_FUNC_5^# +// Same as GLOBAL_FUNC_1 + fn2: { "" } + } +} + +struct MyStruct { + func method1(fn1: () -> Int, fn2: () -> String) {} +} +func testMethod(value: MyStruct) { + value.method1 { + #^METHOD_1^# +// METHOD_1: Begin completions, 1 items +// METHOD_1-DAG: Pattern/ExprSpecific: {#fn1: () -> Int##() -> Int#}[#() -> Int#]; +// METHOD_1: End completions + } + value.method1() { + #^METHOD_2^# +// Same as METHOD_1 + } + value.method1() { + fn1: { 1 } + #^METHOD_3^# +// METHOD_3: Begin completions, 1 items +// METHOD_3-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; name=fn2: () -> String +// METHOD_3: End completions + } + + value.method1(fn1: { 1 }) { + #^METHOD_4^# +// METHOD_4: Begin completions +// METHOD_4-NOT: Decl[Struct]/OtherModule[Swift]/TypeRelation[Identical]: Int[#Int#]; +// METHOD_4-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// METHOD_4-DAG: Decl[Struct]/OtherModule[Swift]/TypeRelation[Identical]: String[#String#]; +// METHOD_4: End completions + } + + value.method1 { + #^METHOD_5^# +// Same as METHOD_1 + fn2: { "" } + } +} From a518e759d980874c7d923b900de74a18bfe6591d Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 9 Apr 2020 02:43:35 -0400 Subject: [PATCH 17/37] WIP for a different syntax for multiple trailing closures that allows arbitrary `label: {}` suffixes after an initial unlabeled closure. Type-checking is not yet correct, as well as code-completion and other kinds of tooling. --- include/swift/AST/Expr.h | 84 +++++++---- include/swift/Parse/Parser.h | 7 - lib/AST/Attr.cpp | 3 +- lib/AST/Expr.cpp | 77 +++++----- lib/IDE/Formatting.cpp | 113 +++++--------- lib/Parse/ParseDecl.cpp | 3 +- lib/Parse/ParseExpr.cpp | 206 +++++--------------------- lib/Parse/ParsePattern.cpp | 2 - lib/Parse/ParseStmt.cpp | 2 - lib/Sema/BuilderTransform.cpp | 2 - lib/Sema/CSApply.cpp | 7 +- lib/Sema/CSGen.cpp | 26 ++-- lib/Sema/CSSimplify.cpp | 18 ++- lib/Sema/ConstraintSystem.h | 7 +- lib/Sema/TypeCheckConstraints.cpp | 1 - lib/Sema/TypeCheckPropertyWrapper.cpp | 4 - lib/Sema/TypeCheckStorage.cpp | 1 - 17 files changed, 204 insertions(+), 359 deletions(-) diff --git a/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 04a4a0f46b81d..3c13b029ac9aa 100644 --- a/include/swift/AST/Expr.h +++ b/include/swift/AST/Expr.h @@ -542,6 +542,11 @@ class alignas(8) Expr { bool isSelfExprOf(const AbstractFunctionDecl *AFD, bool sameBase = false) const; + /// Given that this is a packed argument expression of the sort that + /// would be produced from packSingleArgument, return the index of the + /// unlabeled trailing closure, if there is one. + Optional getUnlabeledTrailingClosureIndexOfPackedArgument() const; + /// Produce a mapping from each subexpression to its parent /// expression, with the provided expression serving as the root of /// the parent map. @@ -1207,8 +1212,6 @@ class ObjectLiteralExpr final ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit); @@ -1231,6 +1234,11 @@ class ObjectLiteralExpr final return Bits.ObjectLiteralExpr.HasTrailingClosure; } + /// Return the index of the unlabeled trailing closure argument. + Optional getUnlabeledTrailingClosureIndex() const { + return getArg()->getUnlabeledTrailingClosureIndexOfPackedArgument(); + } + SourceLoc getSourceLoc() const { return PoundLoc; } SourceRange getSourceRange() const { return SourceRange(PoundLoc, Arg->getEndLoc()); @@ -1817,6 +1825,11 @@ class DynamicSubscriptExpr final return Bits.DynamicSubscriptExpr.HasTrailingClosure; } + /// Return the index of the unlabeled trailing closure argument. + Optional getUnlabeledTrailingClosureIndex() const { + return Index->getUnlabeledTrailingClosureIndexOfPackedArgument(); + } + SourceLoc getLoc() const { return Index->getStartLoc(); } SourceLoc getStartLoc() const { return getBase()->getStartLoc(); } @@ -1859,8 +1872,6 @@ class UnresolvedMemberExpr final ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit); @@ -1888,6 +1899,11 @@ class UnresolvedMemberExpr final return Bits.UnresolvedMemberExpr.HasTrailingClosure; } + /// Return the index of the unlabeled trailing closure argument. + Optional getUnlabeledTrailingClosureIndex() const { + return getArgument()->getUnlabeledTrailingClosureIndexOfPackedArgument(); + } + SourceLoc getLoc() const { return NameLoc.getBaseNameLoc(); } SourceLoc getStartLoc() const { return DotLoc; } @@ -2069,6 +2085,11 @@ class ParenExpr : public IdentityExpr { /// Whether this expression has a trailing closure as its argument. bool hasTrailingClosure() const { return Bits.ParenExpr.HasTrailingClosure; } + Optional + getUnlabeledTrailingClosureIndexOfPackedArgument() const { + return hasTrailingClosure() ? Optional(0) : None; + } + static bool classof(const Expr *E) { return E->getKind() == ExprKind::Paren; } }; @@ -2082,9 +2103,6 @@ class TupleExpr final : public Expr, SourceLoc LParenLoc; SourceLoc RParenLoc; - SourceLoc TrailingLBraceLoc; - SourceLoc TrailingRBraceLoc; - Optional FirstTrailingArgumentAt; size_t numTrailingObjects(OverloadToken) const { @@ -2117,8 +2135,6 @@ class TupleExpr final : public Expr, ArrayRef SubExprs, ArrayRef ElementNames, ArrayRef ElementNameLocs, - SourceLoc TrailingLBrace, - SourceLoc TrailingRBrace, Optional FirstTrailingArgumentAt, bool Implicit, Type Ty); public: @@ -2137,8 +2153,6 @@ class TupleExpr final : public Expr, ArrayRef SubExprs, ArrayRef ElementNames, ArrayRef ElementNameLocs, - SourceLoc TrailingLBrace, - SourceLoc TrailingRBrace, Optional FirstTrailingArgumentAt, bool Implicit, Type Ty = Type()); @@ -2155,8 +2169,9 @@ class TupleExpr final : public Expr, SourceRange getSourceRange() const; - SourceLoc getTrailingLBraceLoc() const { return TrailingLBraceLoc; } - SourceLoc getTrailingRBraceLoc() const { return TrailingRBraceLoc; } + bool hasAnyTrailingClosures() const { + return (bool) FirstTrailingArgumentAt; + } /// Whether this expression has a trailing closure as its argument. bool hasTrailingClosure() const { @@ -2169,6 +2184,11 @@ class TupleExpr final : public Expr, return FirstTrailingArgumentAt ? !hasTrailingClosure() : false; } + Optional + getUnlabeledTrailingClosureIndexOfPackedArgument() const { + return FirstTrailingArgumentAt; + } + /// Retrieve the elements of this tuple. MutableArrayRef getElements() { return { getTrailingObjects(), getNumElements() }; @@ -2178,6 +2198,14 @@ class TupleExpr final : public Expr, ArrayRef getElements() const { return { getTrailingObjects(), getNumElements() }; } + + MutableArrayRef getTrailingElements() { + return getElements().take_back(getNumTrailingElements()); + } + + ArrayRef getTrailingElements() const { + return getElements().take_back(getNumTrailingElements()); + } unsigned getNumElements() const { return Bits.TupleExpr.NumElements; } @@ -2422,8 +2450,6 @@ class SubscriptExpr final : public LookupExpr, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures, ConcreteDeclRef decl = ConcreteDeclRef(), bool implicit = false, @@ -2448,6 +2474,11 @@ class SubscriptExpr final : public LookupExpr, return Bits.SubscriptExpr.HasTrailingClosure; } + /// Return the index of the unlabeled trailing closure argument. + Optional getUnlabeledTrailingClosureIndex() const { + return getIndex()->getUnlabeledTrailingClosureIndexOfPackedArgument(); + } + /// Determine whether this subscript reference should bypass the /// ordinary accessors. AccessSemantics getAccessSemantics() const { @@ -4293,6 +4324,9 @@ class ApplyExpr : public Expr { /// Whether this application was written using a trailing closure. bool hasTrailingClosure() const; + /// Return the index of the unlabeled trailing closure argument. + Optional getUnlabeledTrailingClosureIndex() const; + static bool classof(const Expr *E) { return E->getKind() >= ExprKind::First_ApplyExpr && E->getKind() <= ExprKind::Last_ApplyExpr; @@ -4338,8 +4372,7 @@ class CallExpr final : public ApplyExpr, return E->getType(); }) { return create(ctx, fn, SourceLoc(), args, argLabels, {}, SourceLoc(), - SourceLoc(), SourceLoc(), /*trailingClosures=*/{}, - /*implicit=*/true, getType); + /*trailingClosures=*/{}, /*implicit=*/true, getType); } /// Create a new call expression. @@ -4354,8 +4387,8 @@ class CallExpr final : public ApplyExpr, static CallExpr *create( ASTContext &ctx, Expr *fn, SourceLoc lParenLoc, ArrayRef args, ArrayRef argLabels, ArrayRef argLabelLocs, - SourceLoc rParenLoc, SourceLoc trailingLBrace, SourceLoc trailingRBrace, - ArrayRef trailingClosures, bool implicit, + SourceLoc rParenLoc, ArrayRef trailingClosures, + bool implicit, llvm::function_ref getType = [](Expr *E) -> Type { return E->getType(); }); @@ -4380,6 +4413,11 @@ class CallExpr final : public ApplyExpr, /// Whether this call with written with a single trailing closure. bool hasTrailingClosure() const { return Bits.CallExpr.HasTrailingClosure; } + /// Return the index of the unlabeled trailing closure argument. + Optional getUnlabeledTrailingClosureIndex() const { + return getArg()->getUnlabeledTrailingClosureIndexOfPackedArgument(); + } + using TrailingCallArguments::getArgumentLabels; /// Retrieve the expression that directly represents the callee. @@ -5243,8 +5281,6 @@ class KeyPathExpr : public Expr { ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures); /// Create an unresolved component for a subscript. @@ -5297,8 +5333,6 @@ class KeyPathExpr : public Expr { ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures, Type elementType, ArrayRef indexHashables); @@ -5687,8 +5721,8 @@ inline const SourceLoc *CollectionExpr::getTrailingSourceLocs() const { Expr *packSingleArgument( ASTContext &ctx, SourceLoc lParenLoc, ArrayRef args, ArrayRef &argLabels, ArrayRef &argLabelLocs, - SourceLoc rParenLoc, SourceLoc trailingLBrace, SourceLoc trailingRBrace, - ArrayRef trailingClosures, bool implicit, + SourceLoc rParenLoc, ArrayRef trailingClosures, + bool implicit, SmallVectorImpl &argLabelsScratch, SmallVectorImpl &argLabelLocsScratch, llvm::function_ref getType = [](Expr *E) -> Type { diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 1c67f0a62f54d..219bfd88fcc2f 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1567,20 +1567,13 @@ class Parser { SmallVectorImpl &exprLabels, SmallVectorImpl &exprLabelLocs, SourceLoc &rightLoc, - SourceLoc &trailingLBrace, - SourceLoc &trailingRBrace, SmallVectorImpl &trailingClosures, syntax::SyntaxKind Kind); ParserStatus parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, - SourceLoc &LBrace, SourceLoc &RBrace, SmallVectorImpl &closures); - ParserStatus - parseMultipleTrailingClosures(SourceLoc &LBrace, SourceLoc &RBrace, - SmallVectorImpl &closures); - /// Parse an object literal. /// /// \param LK The literal kind as determined by the first token. diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 5c9eb437db1a1..2b339f12fb75a 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1867,8 +1867,7 @@ CustomAttr *CustomAttr::create(ASTContext &ctx, SourceLoc atLoc, TypeLoc type, Expr *arg = nullptr; if (hasInitializer) { arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, SourceLoc(), SourceLoc(), - /*trailingClosures=*/{}, implicit, + rParenLoc, /*trailingClosures=*/{}, implicit, argLabelsScratch, argLabelLocsScratch); } diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index cc58fd6a03b5b..3dd7586713141 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -975,8 +975,6 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, ArrayRef &argLabels, ArrayRef &argLabelLocs, SourceLoc rParenLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit, SmallVectorImpl &argLabelsScratch, @@ -1071,14 +1069,27 @@ Expr *swift::packSingleArgument(ASTContext &ctx, SourceLoc lParenLoc, argLabelLocs = argLabelLocsScratch; } + Optional unlabeledTrailingClosureIndex; + if (!trailingClosures.empty() && trailingClosures[0].Label.empty()) + unlabeledTrailingClosureIndex = args.size() - trailingClosures.size(); + auto arg = TupleExpr::create(ctx, lParenLoc, rParenLoc, args, argLabels, - argLabelLocs, trailingLBrace, trailingRBrace, - args.size() - trailingClosures.size(), + argLabelLocs, + unlabeledTrailingClosureIndex, /*Implicit=*/false); computeSingleArgumentType(ctx, arg, implicit, getType); return arg; } +Optional +Expr::getUnlabeledTrailingClosureIndexOfPackedArgument() const { + if (auto PE = dyn_cast(this)) + return PE->getUnlabeledTrailingClosureIndexOfPackedArgument(); + if (auto TE = dyn_cast(this)) + return TE->getUnlabeledTrailingClosureIndexOfPackedArgument(); + return None; +} + ObjectLiteralExpr::ObjectLiteralExpr(SourceLoc PoundLoc, LiteralKind LitKind, Expr *Arg, ArrayRef argLabels, @@ -1125,14 +1136,12 @@ ObjectLiteralExpr *ObjectLiteralExpr::create(ASTContext &ctx, ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingLBrace, trailingRBrace, + rParenLoc, trailingClosures, implicit, argLabelsScratch, argLabelLocsScratch); @@ -1254,8 +1263,6 @@ SourceRange TupleExpr::getSourceRange() const { start = LParenLoc; } else if (getNumElements() == 0) { return { SourceLoc(), SourceLoc() }; - } else if (TrailingLBraceLoc.isValid()) { - start = TrailingLBraceLoc; } else { // Scan forward for the first valid source loc. for (Expr *expr : getElements()) { @@ -1266,12 +1273,9 @@ SourceRange TupleExpr::getSourceRange() const { } } - if (hasTrailingClosure() || hasMultipleTrailingClosures() || - RParenLoc.isInvalid()) { + if (hasAnyTrailingClosures() || RParenLoc.isInvalid()) { if (getNumElements() == 0) { return { SourceLoc(), SourceLoc() }; - } else if (TrailingRBraceLoc.isValid()) { - end = TrailingRBraceLoc; } else { // Scan backwards for a valid source loc. for (Expr *expr : llvm::reverse(getElements())) { @@ -1296,13 +1300,10 @@ TupleExpr::TupleExpr(SourceLoc LParenLoc, SourceLoc RParenLoc, ArrayRef SubExprs, ArrayRef ElementNames, ArrayRef ElementNameLocs, - SourceLoc TrailingLBrace, - SourceLoc TrailingRBrace, Optional FirstTrailingArgumentAt, bool Implicit, Type Ty) : Expr(ExprKind::Tuple, Implicit, Ty), LParenLoc(LParenLoc), RParenLoc(RParenLoc), - TrailingLBraceLoc(TrailingLBrace), TrailingRBraceLoc(TrailingRBrace), FirstTrailingArgumentAt(FirstTrailingArgumentAt) { Bits.TupleExpr.HasElementNames = !ElementNames.empty(); Bits.TupleExpr.HasElementNameLocations = !ElementNameLocs.empty(); @@ -1343,8 +1344,7 @@ TupleExpr *TupleExpr::create(ASTContext &ctx, HasTrailingClosure ? SubExprs.size() - 1 : Optional(); return create(ctx, LParenLoc, RParenLoc, SubExprs, ElementNames, - ElementNameLocs, SourceLoc(), SourceLoc(), - FirstTrailingArgumentAt, Implicit, Ty); + ElementNameLocs, FirstTrailingArgumentAt, Implicit, Ty); } TupleExpr *TupleExpr::create(ASTContext &ctx, @@ -1353,8 +1353,6 @@ TupleExpr *TupleExpr::create(ASTContext &ctx, ArrayRef SubExprs, ArrayRef ElementNames, ArrayRef ElementNameLocs, - SourceLoc TrailingLBrace, - SourceLoc TrailingRBrace, Optional FirstTrailingArgumentAt, bool Implicit, Type Ty) { assert(!Ty || isa(Ty.getPointer())); @@ -1376,13 +1374,13 @@ TupleExpr *TupleExpr::create(ASTContext &ctx, ElementNameLocs.size()); void *mem = ctx.Allocate(size, alignof(TupleExpr)); return new (mem) TupleExpr(LParenLoc, RParenLoc, SubExprs, ElementNames, - ElementNameLocs, TrailingLBrace, TrailingRBrace, + ElementNameLocs, FirstTrailingArgumentAt, Implicit, Ty); } TupleExpr *TupleExpr::createEmpty(ASTContext &ctx, SourceLoc LParenLoc, SourceLoc RParenLoc, bool Implicit) { - return create(ctx, LParenLoc, RParenLoc, {}, {}, {}, SourceLoc(), SourceLoc(), + return create(ctx, LParenLoc, RParenLoc, {}, {}, {}, /*FirstTrailingArgumentAt=*/None, Implicit, TupleType::getEmpty(ctx)); } @@ -1390,8 +1388,7 @@ TupleExpr *TupleExpr::createEmpty(ASTContext &ctx, SourceLoc LParenLoc, TupleExpr *TupleExpr::createImplicit(ASTContext &ctx, ArrayRef SubExprs, ArrayRef ElementNames) { return create(ctx, SourceLoc(), SourceLoc(), SubExprs, ElementNames, {}, - SourceLoc(), SourceLoc(), /*FirstTrailingArgumentAt=*/None, - /*Implicit=*/true, Type()); + /*FirstTrailingArgumentAt=*/None, /*Implicit=*/true, Type()); } ArrayExpr *ArrayExpr::create(ASTContext &C, SourceLoc LBracketLoc, @@ -1535,8 +1532,6 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures, ConcreteDeclRef decl, bool implicit, @@ -1545,7 +1540,6 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, SmallVector indexArgLabelLocsScratch; Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, - trailingLBrace, trailingRBrace, trailingClosures, implicit, indexArgLabelsScratch, indexArgLabelLocsScratch); @@ -1633,16 +1627,13 @@ UnresolvedMemberExpr::create(ASTContext &ctx, SourceLoc dotLoc, ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingLBrace, trailingRBrace, - trailingClosures, implicit, argLabelsScratch, - argLabelLocsScratch); + rParenLoc, trailingClosures, implicit, + argLabelsScratch, argLabelLocsScratch); size_t size = totalSizeToAlloc(argLabels, argLabelLocs); @@ -1684,6 +1675,13 @@ bool ApplyExpr::hasTrailingClosure() const { return false; } +Optional ApplyExpr::getUnlabeledTrailingClosureIndex() const { + if (auto call = dyn_cast(this)) + return call->getUnlabeledTrailingClosureIndex(); + + return None; +} + CallExpr::CallExpr(Expr *fn, Expr *arg, bool Implicit, ArrayRef argLabels, ArrayRef argLabelLocs, @@ -1727,17 +1725,15 @@ CallExpr *CallExpr::create(ASTContext &ctx, Expr *fn, SourceLoc lParenLoc, ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures, bool implicit, llvm::function_ref getType) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingLBrace, trailingRBrace, - trailingClosures, implicit, argLabelsScratch, - argLabelLocsScratch, getType); + rParenLoc, trailingClosures, implicit, + argLabelsScratch, argLabelLocsScratch, + getType); size_t size = totalSizeToAlloc(argLabels, argLabelLocs); @@ -2212,8 +2208,6 @@ KeyPathExpr::Component::forSubscript(ASTContext &ctx, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures, Type elementType, ArrayRef indexHashables) { @@ -2221,7 +2215,6 @@ KeyPathExpr::Component::forSubscript(ASTContext &ctx, SmallVector indexArgLabelLocsScratch; Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, - trailingLBrace, trailingRBrace, trailingClosures, /*implicit*/ false, indexArgLabelsScratch, indexArgLabelLocsScratch); @@ -2239,14 +2232,12 @@ KeyPathExpr::Component::forUnresolvedSubscript(ASTContext &ctx, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - SourceLoc trailingLBrace, - SourceLoc trailingRBrace, ArrayRef trailingClosures) { SmallVector indexArgLabelsScratch; SmallVector indexArgLabelLocsScratch; Expr *index = packSingleArgument( ctx, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, - trailingLBrace, trailingRBrace, trailingClosures, /*implicit*/ false, + trailingClosures, /*implicit*/ false, indexArgLabelsScratch, indexArgLabelLocsScratch); return forUnresolvedSubscriptWithPrebuiltIndexExpr(ctx, index, indexArgLabels, lSquareLoc); diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 22836cb2d6110..3093dc910e714 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -128,6 +128,13 @@ static bool hasExplicitAccessors(VarDecl *VD) { Getter->getAccessorKeywordLoc().isValid()); } +static ClosureExpr *findTrailingClosureFromArgument(Expr *arg) { + if (auto TC = dyn_cast_or_null(arg)) + return TC; + if (auto TCL = dyn_cast_or_null(arg)) + return TCL->getClosureBody(); + return nullptr; +} /// An indentation context of the target location struct IndentContext { @@ -626,8 +633,13 @@ class RangeWalker: protected ASTWalker { } else { Arg = cast(E)->getIndex(); } - ClosureExpr *TC = nullptr; - CaptureListExpr *TCL = nullptr; + + auto handleTrailingClosure = [&](Expr *arg) -> bool { + if (auto CE = findTrailingClosureFromArgument(arg)) + return handleBraceStmt(CE->getBody(), ContextLoc); + return true; + }; + if (auto *PE = dyn_cast_or_null(Arg)) { if (isa(E)) { if (!handleSquares(PE->getLParenLoc(), PE->getRParenLoc(), ContextLoc)) @@ -637,9 +649,8 @@ class RangeWalker: protected ASTWalker { return Stop; } if (PE->hasTrailingClosure()) { - auto *LastElem = PE->getSubExpr(); - TC = dyn_cast_or_null(LastElem); - TCL = dyn_cast_or_null(LastElem); + if (!handleTrailingClosure(PE->getSubExpr())) + return Stop; } } else if (auto *TE = dyn_cast_or_null(Arg)) { if (isa(E)) { @@ -649,22 +660,11 @@ class RangeWalker: protected ASTWalker { if (!handleParens(TE->getLParenLoc(), TE->getRParenLoc(), ContextLoc)) return Stop; } - if (TE->hasTrailingClosure() || TE->hasMultipleTrailingClosures()) { - if (TE->getTrailingLBraceLoc().isValid()) { - if (!handleBraces(TE->getTrailingLBraceLoc(), - TE->getTrailingRBraceLoc(), ContextLoc)) - return Stop; - } else { - Expr *LastElem = TE->getElements().back(); - TC = dyn_cast_or_null(LastElem); - TCL = dyn_cast_or_null(LastElem); - } + for (auto closure : TE->getTrailingElements()) { + if (!handleTrailingClosure(closure)) + return Stop; } } - if (TC && !handleBraceStmt(TC->getBody(), ContextLoc)) - return Stop; - if (TCL && !handleBraceStmt(TCL->getClosureBody()->getBody(), ContextLoc)) - return Stop; } return Continue; } @@ -2320,41 +2320,34 @@ class FormatWalker : public ASTWalker { Arg = cast(E)->getIndex(); } - ClosureExpr *TCE = nullptr; - CaptureListExpr *TCL = nullptr; + auto getIndentContextFromTrailingClosure = + [&](Expr *arg) -> Optional { + if (auto CE = findTrailingClosureFromArgument(arg)) { + SourceRange Range = CE->getSourceRange(); + if (Range.isValid() && (TrailingTarget || overlapsTarget(Range))) { + if (auto CLE = dyn_cast(arg)) + return getIndentContextFrom(CLE, ContextLoc); + return getIndentContextFrom(CE, ContextLoc); + } + } + return None; + }; + if (auto *PE = dyn_cast_or_null(Arg)) { if (auto Ctx = getIndentContextFrom(PE, ContextLoc)) return Ctx; if (PE->hasTrailingClosure()) { - Expr *Last = PE->getSubExpr(); - TCE = dyn_cast_or_null(Last); - TCL = dyn_cast_or_null(Last); + if (auto Ctx = getIndentContextFromTrailingClosure(PE->getSubExpr())) + return Ctx; } } else if (auto *TE = dyn_cast_or_null(Arg)) { if (auto Ctx = getIndentContextFrom(TE, ContextLoc)) return Ctx; - if (TE->hasTrailingClosure() || TE->hasMultipleTrailingClosures()) { - if (TE->getTrailingLBraceLoc().isValid()) { - if (auto Ctx = getIndentContextFromTrailingClosureGroup(TE, - ContextLoc)) + for (auto arg : TE->getTrailingElements()) { + if (auto Ctx = getIndentContextFromTrailingClosure(arg)) return Ctx; - } else { - Expr *LastElem = TE->getElements().back(); - TCE = dyn_cast_or_null(LastElem); - TCL = dyn_cast_or_null(LastElem); - } } } - - if (TCL) { - SourceRange Range = TCL->getSourceRange(); - if (Range.isValid() && (TrailingTarget || overlapsTarget(Range))) - return getIndentContextFrom(TCL, ContextLoc); - } else if (TCE) { - SourceRange Range = TCE->getSourceRange(); - if (Range.isValid() && (TrailingTarget || overlapsTarget(Range))) - return getIndentContextFrom(TCE, ContextLoc); - } } return None; @@ -2513,40 +2506,6 @@ class FormatWalker : public ASTWalker { return Aligner.getContextAndSetAlignment(CtxOverride); } - Optional - getIndentContextFromTrailingClosureGroup(TupleExpr *TE, - SourceLoc ContextLoc) { - SourceLoc L = TE->getTrailingLBraceLoc(); - SourceLoc R = getLocIfKind(SM, TE->getTrailingRBraceLoc(), tok::r_brace); - - if (L.isInvalid() || !overlapsTarget(L, R)) - return None; - - ContextLoc = CtxOverride.propagateContext(SM, ContextLoc, - IndentContext::LineStart, - L, R); - auto NumElems = TE->getNumElements(); - auto FirstTrailing = NumElems - TE->getNumTrailingElements(); - for (auto I: range(FirstTrailing, NumElems)) { - SourceRange ElemRange = TE->getElementNameLoc(I); - if (Expr *Elem = TE->getElement(I)) - widenOrSet(ElemRange, Elem->getSourceRange()); - assert(ElemRange.isValid()); - if (isTargetContext(ElemRange)) { - return IndentContext { - ElemRange.Start, - !OutdentChecker::hasOutdent(SM, ElemRange, TE) - }; - } - } - SourceRange Range = SourceRange(L, TE->getEndLoc()); - return IndentContext { - ContextLoc, - containsTarget(L, R) && - !OutdentChecker::hasOutdent(SM, Range, TE, RangeKind::Open) - }; - } - Optional getIndentContextFrom(ParenExpr *PE, SourceLoc ContextLoc = SourceLoc()) { if (ContextLoc.isValid()) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index d31907688dc91..99b540e8854c8 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2539,7 +2539,6 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; bool hasInitializer = false; ParserStatus status; @@ -2578,7 +2577,7 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc At status |= parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/false, /*isExprBasic=*/true, lParenLoc, args, argLabels, argLabelLocs, - rParenLoc, trailingLBrace, trailingRBrace, + rParenLoc, trailingClosures, SyntaxKind::TupleExprElementList); assert(trailingClosures.empty() && "Cannot parse a trailing closure here"); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index b28a12dfcdcd4..87203f97d3a66 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1184,20 +1184,17 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, SmallVector indexArgs; SmallVector indexArgLabels; SmallVector indexArgLabelLocs; - SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; ParserStatus status = parseExprList( tok::l_square, tok::r_square, /*isPostfix=*/true, isExprBasic, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, - trailingLBrace, trailingRBrace, trailingClosures, - SyntaxKind::TupleExprElementList); + trailingClosures, SyntaxKind::TupleExprElementList); Result = makeParserResult( status | Result, SubscriptExpr::create(Context, Result.get(), lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, - trailingLBrace, trailingRBrace, trailingClosures, ConcreteDeclRef(), /*implicit=*/false)); SyntaxContext->createNodeInPlace(SyntaxKind::SubscriptExpr); @@ -1221,11 +1218,10 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, leadingTriviaLoc(), *SyntaxContext)); } - SourceLoc LBrace, RBrace; SmallVector trailingClosures; auto trailingResult = - parseTrailingClosures(isExprBasic, callee->getSourceRange(), LBrace, - RBrace, trailingClosures); + parseTrailingClosures(isExprBasic, callee->getSourceRange(), + trailingClosures); if (trailingClosures.empty()) return nullptr; @@ -1233,7 +1229,7 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, Result = makeParserResult( ParserStatus(Result) | trailingResult, CallExpr::create(Context, Result.get(), SourceLoc(), {}, {}, {}, - SourceLoc(), LBrace, RBrace, trailingClosures, + SourceLoc(), trailingClosures, /*implicit=*/false)); SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); @@ -1610,7 +1606,6 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, @@ -1618,7 +1613,6 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingLBrace, trailingRBrace, trailingClosures, SyntaxKind::TupleExprElementList); SyntaxContext->createNodeInPlace(SyntaxKind::FunctionCallExpr); @@ -1627,7 +1621,6 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingLBrace, trailingRBrace, trailingClosures, /*implicit=*/false)); } @@ -1641,10 +1634,9 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { leadingTriviaLoc(), *SyntaxContext)); } - SourceLoc LBrace, RBrace; SmallVector trailingClosures; auto result = parseTrailingClosures(isExprBasic, NameLoc.getSourceRange(), - LBrace, RBrace, trailingClosures); + trailingClosures); if (trailingClosures.empty()) return nullptr; @@ -1653,8 +1645,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { return makeParserResult( result, UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, SourceLoc(), {}, {}, {}, - SourceLoc(), LBrace, RBrace, - trailingClosures, + SourceLoc(), trailingClosures, /*implicit=*/false)); } @@ -3022,7 +3013,6 @@ Parser::parseExprList(tok leftTok, tok rightTok, SyntaxKind Kind) { SmallVector trailingClosures; SourceLoc leftLoc, rightLoc; - SourceLoc trailingLBrace, trailingRBrace; ParserStatus status = parseExprList(leftTok, rightTok, /*isPostfix=*/false, /*isExprBasic=*/true, leftLoc, @@ -3030,8 +3020,6 @@ Parser::parseExprList(tok leftTok, tok rightTok, SyntaxKind Kind) { subExprNames, subExprNameLocs, rightLoc, - trailingLBrace, - trailingRBrace, trailingClosures, Kind); @@ -3068,8 +3056,6 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, SmallVectorImpl &exprLabels, SmallVectorImpl &exprLabelLocs, SourceLoc &rightLoc, - SourceLoc &trailingLBrace, - SourceLoc &trailingRBrace, SmallVectorImpl &trailingClosures, SyntaxKind Kind) { StructureMarkerRAII ParsingExprList(*this, Tok); @@ -3154,139 +3140,25 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, // Parse the closure. status |= parseTrailingClosures(isExprBasic, SourceRange(leftLoc, rightLoc), - trailingLBrace, trailingRBrace, trailingClosures); + trailingClosures); return status; } -/// Check whether upcoming trailing closure actually represents a block of -/// labeled trailing closures e.g. -/// foo() { -/// : { ... } -/// ... -/// } -static bool isBlockOfMultipleTrailingClosures(bool isExprBasic, Parser &P) { - assert(isValidTrailingClosure(isExprBasic, P)); - - // If closure doesn't start from a label there is no reason - // to look any farther. - { - const auto &nextTok = P.peekToken(); - if (!(nextTok.canBeArgumentLabel() || nextTok.is(tok::code_complete)) || - nextTok.getText()[0] == '$') - return false; - } - - Parser::BacktrackingScope backtrack(P); - P.consumeToken(tok::l_brace); - - if (P.Tok.is(tok::code_complete)) { - // Parse '{ }' as a multiple trailing closure block. - if (P.peekToken().is(tok::r_brace)) - return true; - - // Otherwise, look through the code completion token. - P.consumeToken(tok::code_complete); - if (!P.Tok.canBeArgumentLabel() || P.Tok.getText()[0] == '$') - return false; - } - - P.consumeToken(); // Consume Label text. - - if (!P.consumeIf(tok::colon)) // Consume `:` +static bool isStartOfLabelledTrailingClosure(Parser &P) { + // Fast path: the next two tokens are a label and a colon. + // TODO: recognize a code-completion token here and record it in the AST. + if (!P.Tok.canBeArgumentLabel() || !P.peekToken().is(tok::colon)) return false; - // If the next token after the label is not a left brace - // that means what follows is not a valid closure, which - // in turn means it can't be a labeled block. - if (!P.Tok.is(tok::l_brace)) - return false; - - return isValidTrailingClosure(isExprBasic, P); -} - -ParserStatus Parser::parseMultipleTrailingClosures( - SourceLoc &LBrace, SourceLoc &RBrace, - SmallVectorImpl &closures) { - SyntaxParsingContext ClauseCtx(SyntaxContext, - SyntaxKind::MultipleTrailingClosureClause); - - // Consume '{' of the trailing closure - LBrace = consumeToken(tok::l_brace); - - ParserStatus Status; - // There could N labeled closures depending on the number of arguments. - do { - if (!(Tok.canBeArgumentLabel() && peekToken().is(tok::colon))) { - - if (Tok.is(tok::code_complete)) { - auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc()); - closures.emplace_back(Identifier(), SourceLoc(), CCE); - if (CodeCompletion) - CodeCompletion->completeCallArg(CCE, /*isFirst=*/false); - - consumeToken(tok::code_complete); - Status.setHasCodeCompletion(); - continue; - } - - Status.setIsParseError(); - diagnose(Tok.getLoc(), - diag::expected_argument_label_followed_by_closure_literal); - skipUntilDeclStmtRBrace(tok::r_brace); - break; - } - - SyntaxParsingContext ElementCtx(SyntaxContext, - SyntaxKind::MultipleTrailingClosureElement); - Identifier label; - auto labelLoc = consumeArgumentLabel(label); - if (!labelLoc.isValid()) - return makeParserError(); - - consumeToken(); // Consume ':' - - SourceLoc closureLoc = Tok.getLoc(); - auto closure = parseExpr(diag::expected_closure_literal); - if (closure.isNull()) { - Status.setIsParseError(); - skipUntilDeclStmtRBrace(tok::r_brace); - break; - } - - // Allow any expression, but diagnose if not a closure literal. - if (!isa(closure.get()) && - !isa(closure.get())) { - diagnose(closureLoc, diag::expected_closure_literal); - } - - Status |= closure; - closures.push_back({label, labelLoc, closure.get()}); - - // Recover if blocks are separated by comma instead of newline. - if (Tok.is(tok::comma)) { - diagnose(Tok.getLoc(), diag::unexpected_separator, ",") - .fixItRemove(Tok.getLoc()); - consumeToken(); - } - } while (!Tok.is(tok::r_brace)); - SyntaxContext->collectNodesInPlace(SyntaxKind::MultipleTrailingClosureElementList); - - // Consume `}` of the trailing closure. - if (parseMatchingToken(tok::r_brace, RBrace, - diag::expected_multiple_closures_block_rbrace, - LBrace)) { - Status.setIsParseError(); - } else { - if (!Status.hasCodeCompletion()) - Status = makeParserSuccess(); - } - - return Status; + // Do some tentative parsing to distinguish `label: { ... }` and + // `label: switch x { ... }`. + Parser::BacktrackingScope backtrack(P); + P.consumeToken(); + return P.peekToken().is(tok::l_brace); } ParserStatus Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, - SourceLoc &LBrace, SourceLoc &RBrace, SmallVectorImpl &closures) { SourceLoc braceLoc = Tok.getLoc(); @@ -3298,20 +3170,14 @@ Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, ParserStatus result; - if (isBlockOfMultipleTrailingClosures(isExprBasic, *this)) { - result |= parseMultipleTrailingClosures(LBrace, RBrace, closures); - } else { - // Parse the closure. - ParserResult closure = parseExprClosure(); - if (closure.isNull()) - return makeParserError(); + // Parse the closure. + ParserResult closure = parseExprClosure(); + if (closure.isNull()) + return makeParserError(); - result |= closure; + result |= closure; - LBrace = braceLoc; - RBrace = closure.get()->getEndLoc(); - closures.push_back({closure.get()}); - } + closures.push_back({closure.get()}); // Warn if the trailing closure is separated from its callee by more than // one line. A single-line separation is acceptable for a trailing closure @@ -3330,6 +3196,21 @@ Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, } } + // Parse labeled trailing closures. + while (isStartOfLabelledTrailingClosure(*this)) { + Identifier label; + auto labelLoc = consumeArgumentLabel(label); + consumeToken(tok::colon); + ParserResult closure = parseExprClosure(); + if (closure.isNull()) + return makeParserError(); + + result |= closure; + closures.push_back({label, labelLoc, closure.get()}); + + // Don't diagnose whitespace gaps before labelled closures. + } + return result; } @@ -3354,7 +3235,6 @@ Parser::parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LitKind, SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, @@ -3362,8 +3242,6 @@ Parser::parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LitKind, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingLBrace, - trailingRBrace, trailingClosures, SyntaxKind::TupleExprElementList); if (status.hasCodeCompletion()) @@ -3374,7 +3252,6 @@ Parser::parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LitKind, return makeParserResult( ObjectLiteralExpr::create(Context, PoundLoc, LitKind, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingLBrace, trailingRBrace, trailingClosures, /*implicit=*/false)); } @@ -3400,7 +3277,6 @@ ParserResult Parser::parseExprPoundUnknown(SourceLoc LSquareLoc) { SmallVector argLabelLocs; SmallVector args; SmallVector argLabels; - SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; if (Tok.isFollowingLParen()) { // Parse arguments. @@ -3408,8 +3284,7 @@ ParserResult Parser::parseExprPoundUnknown(SourceLoc LSquareLoc) { parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, /*isExprBasic*/ true, LParenLoc, args, argLabels, argLabelLocs, RParenLoc, - trailingLBrace, trailingRBrace, trailingClosures, - SyntaxKind::TupleExprElementList); + trailingClosures, SyntaxKind::TupleExprElementList); if (status.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (status.isError()) @@ -3494,7 +3369,6 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; // If there is a code completion token right after the '(', do a special case @@ -3509,7 +3383,6 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { { Identifier() }, { }, rParenLoc, - SourceLoc(), SourceLoc(), /*trailingClosures=*/{}, /*implicit=*/false)); CodeCompletion->completePostfixExprParen(fn.get(), CCE); @@ -3525,8 +3398,6 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingLBrace, - trailingRBrace, trailingClosures, SyntaxKind::TupleExprElementList); @@ -3534,7 +3405,6 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { return makeParserResult(status | fn, CallExpr::create(Context, fn.get(), lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingLBrace, trailingRBrace, trailingClosures, /*implicit=*/false)); } diff --git a/lib/Parse/ParsePattern.cpp b/lib/Parse/ParsePattern.cpp index 3f951c7f72f17..8b29e0e10d365 100644 --- a/lib/Parse/ParsePattern.cpp +++ b/lib/Parse/ParsePattern.cpp @@ -917,14 +917,12 @@ ParserResult Parser::parseTypedPattern() { SmallVector args; SmallVector argLabels; SmallVector argLabelLocs; - SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; ParserStatus status = parseExprList(tok::l_paren, tok::r_paren, /*isPostfix=*/true, /*isExprBasic=*/false, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingLBrace, trailingRBrace, trailingClosures, SyntaxKind::Unknown); if (status.isSuccess()) { diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index ca1646e853503..5c6dfdc92d8c2 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -845,7 +845,6 @@ ParserResult Parser::parseStmtYield(SourceLoc tryLoc) { SmallVector yieldLabels; SmallVector yieldLabelLocs; - SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; status = parseExprList(tok::l_paren, tok::r_paren, @@ -854,7 +853,6 @@ ParserResult Parser::parseStmtYield(SourceLoc tryLoc) { lpLoc, yields, yieldLabels, yieldLabelLocs, rpLoc, - trailingLBrace, trailingRBrace, trailingClosures, SyntaxKind::ExprList); assert(trailingClosures.empty()); diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 49600997108e1..edd71fbacd154 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -101,8 +101,6 @@ class BuilderClosureVisitor SourceLoc closeLoc = args.empty() ? loc : args.back()->getEndLoc(); Expr *result = CallExpr::create(ctx, memberRef, openLoc, args, argLabels, argLabelLocs, closeLoc, - /*trailingLBrace=*/SourceLoc(), - /*trailingRBrace=*/SourceLoc(), /*trailing closures*/{}, /*implicit*/true); diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index a15ddaf4877ff..2a2673dfbce2b 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2001,19 +2001,16 @@ namespace { arguments.push_back(SE->getIndex()->getSemanticsProvidingExpr()); } - SourceLoc trailingLBrace, trailingRBrace; SmallVector trailingClosures; if (SE->hasTrailingClosure()) { auto *closure = arguments.back(); - trailingLBrace = closure->getStartLoc(); - trailingRBrace = closure->getEndLoc(); trailingClosures.push_back({closure}); } componentExpr = SubscriptExpr::create( ctx, dotExpr, SE->getStartLoc(), arguments, SE->getArgumentLabels(), SE->getArgumentLabelLocs(), - SE->getEndLoc(), trailingLBrace, trailingRBrace, trailingClosures, + SE->getEndLoc(), trailingClosures, SE->hasDecl() ? SE->getDecl() : ConcreteDeclRef(), /*implicit=*/true, SE->getAccessSemantics()); } @@ -5518,7 +5515,7 @@ Expr *ExprRewriter::coerceCallArguments(Expr *arg, AnyFunctionType *funcType, SmallVector parameterBindings; bool failed = constraints::matchCallArguments(args, params, paramInfo, - hasTrailingClosure, + arg->getUnlabeledTrailingClosureIndexOfPackedArgument(), /*allowFixes=*/false, listener, parameterBindings); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index ec951a3474d8b..3642eb2f2ede9 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -994,7 +994,8 @@ namespace { Type addSubscriptConstraints( Expr *anchor, Type baseTy, Expr *index, ValueDecl *declOrNull, ArrayRef argLabels, - bool hasTrailingClosure, ConstraintLocator *locator = nullptr, + Optional unlabeledTrailingClosure, + ConstraintLocator *locator = nullptr, SmallVectorImpl *addedTypeVars = nullptr) { // Locators used in this expression. if (locator == nullptr) @@ -1010,7 +1011,8 @@ namespace { CS.getConstraintLocator(locator, ConstraintLocator::FunctionResult); - associateArgumentLabels(memberLocator, {argLabels, hasTrailingClosure}); + associateArgumentLabels(memberLocator, + {argLabels, unlabeledTrailingClosure}); Type outputTy; @@ -1303,7 +1305,8 @@ namespace { Type visitObjectLiteralExpr(ObjectLiteralExpr *expr) { auto *exprLoc = CS.getConstraintLocator(expr); associateArgumentLabels( - exprLoc, {expr->getArgumentLabels(), expr->hasTrailingClosure()}); + exprLoc, {expr->getArgumentLabels(), + expr->getUnlabeledTrailingClosureIndex()}); // If the expression has already been assigned a type; just use that type. if (expr->getType()) @@ -1587,7 +1590,8 @@ namespace { associateArgumentLabels( CS.getConstraintLocator(expr), - {expr->getArgumentLabels(), expr->hasTrailingClosure()}); + {expr->getArgumentLabels(), + expr->getUnlabeledTrailingClosureIndex()}); return baseTy; } @@ -1829,7 +1833,7 @@ namespace { return addSubscriptConstraints(expr, CS.getType(base), expr->getIndex(), decl, expr->getArgumentLabels(), - expr->hasTrailingClosure()); + expr->getUnlabeledTrailingClosureIndex()); } Type visitArrayExpr(ArrayExpr *expr) { @@ -2121,7 +2125,7 @@ namespace { return addSubscriptConstraints(expr, CS.getType(expr->getBase()), expr->getIndex(), /*decl*/ nullptr, expr->getArgumentLabels(), - expr->hasTrailingClosure()); + expr->getUnlabeledTrailingClosureIndex()); } Type visitTupleElementExpr(TupleElementExpr *expr) { @@ -2866,7 +2870,8 @@ namespace { SmallVector scratch; associateArgumentLabels( CS.getConstraintLocator(expr), - {expr->getArgumentLabels(scratch), expr->hasTrailingClosure()}, + {expr->getArgumentLabels(scratch), + expr->getUnlabeledTrailingClosureIndex()}, /*labelsArePermanent=*/isa(expr)); if (auto *UDE = dyn_cast(fnExpr)) { @@ -3414,10 +3419,13 @@ namespace { // Subscript should only appear in resolved ASTs, but we may need to // re-type-check the constraints during failure diagnosis. case KeyPathExpr::Component::Kind::Subscript: { - base = addSubscriptConstraints(E, base, component.getIndexExpr(), + auto index = component.getIndexExpr(); + auto unlabeledTrailingClosureIndex = + index->getUnlabeledTrailingClosureIndexOfPackedArgument(); + base = addSubscriptConstraints(E, base, index, /*decl*/ nullptr, component.getSubscriptLabels(), - /*hasTrailingClosure*/ false, + unlabeledTrailingClosureIndex, memberLocator, &componentTypeVars); break; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index f6b7c02dc5d0d..ba220d39a335c 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -125,7 +125,7 @@ bool constraints::doesMemberRefApplyCurriedSelf(Type baseTy, static bool areConservativelyCompatibleArgumentLabels( OverloadChoice choice, SmallVectorImpl &args, - bool hasTrailingClosure) { + Optional unlabeledTrailingClosureArgIndex) { ValueDecl *decl = nullptr; switch (choice.getKind()) { case OverloadChoiceKind::Decl: @@ -166,7 +166,8 @@ static bool areConservativelyCompatibleArgumentLabels( MatchCallArgumentListener listener; SmallVector unusedParamBindings; - return !matchCallArguments(args, params, paramInfo, hasTrailingClosure, + return !matchCallArguments(args, params, paramInfo, + unlabeledTrailingClosureArgIndex, /*allow fixes*/ false, listener, unusedParamBindings); } @@ -218,11 +219,13 @@ bool constraints:: matchCallArguments(SmallVectorImpl &args, ArrayRef params, const ParameterListInfo ¶mInfo, - bool hasTrailingClosure, + Optional unlabeledTrailingClosureArgIndex, bool allowFixes, MatchCallArgumentListener &listener, SmallVectorImpl ¶meterBindings) { assert(params.size() == paramInfo.size() && "Default map does not match"); + assert(!unlabeledTrailingClosureArgIndex || + *unlabeledTrailingClosureArgIndex < args.size()); // Keep track of the parameter we're matching and what argument indices // got bound to each parameter. @@ -430,7 +433,8 @@ matchCallArguments(SmallVectorImpl &args, }; // If we have a trailing closure, it maps to the last parameter. - if (hasTrailingClosure && numParams > 0) { + // TODO: generalize this correctly for the closure arg index. + if (unlabeledTrailingClosureArgIndex && numParams > 0) { unsigned lastParamIdx = numParams - 1; bool lastAcceptsTrailingClosure = acceptsTrailingClosure(params[lastParamIdx]); @@ -1037,7 +1041,8 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments( ArgumentFailureTracker listener(cs, argsWithLabels, params, parameterBindings, locator); if (constraints::matchCallArguments( - argsWithLabels, params, paramInfo, argInfo->HasTrailingClosure, + argsWithLabels, params, paramInfo, + argInfo->UnlabeledTrailingClosureIndex, cs.shouldAttemptFixes(), listener, parameterBindings)) return cs.getTypeMatchFailure(locator); @@ -7987,7 +7992,8 @@ bool ConstraintSystem::simplifyAppliedOverloadsImpl( FunctionType::relabelParams(argsWithLabels, argumentInfo->Labels); if (!areConservativelyCompatibleArgumentLabels( - choice, argsWithLabels, argumentInfo->HasTrailingClosure)) { + choice, argsWithLabels, + argumentInfo->UnlabeledTrailingClosureIndex)) { labelMismatch = true; return false; } diff --git a/lib/Sema/ConstraintSystem.h b/lib/Sema/ConstraintSystem.h index c43b1fa2638ec..c0fa1adcc7d75 100644 --- a/lib/Sema/ConstraintSystem.h +++ b/lib/Sema/ConstraintSystem.h @@ -2176,7 +2176,7 @@ class ConstraintSystem { struct ArgumentInfo { ArrayRef Labels; - bool HasTrailingClosure; + Optional UnlabeledTrailingClosureIndex; }; /// A mapping from the constraint locators for references to various @@ -4893,7 +4893,8 @@ class MatchCallArgumentListener { /// \param args The arguments. /// \param params The parameters. /// \param paramInfo Declaration-level information about the parameters. -/// \param hasTrailingClosure Whether the last argument is a trailing closure. +/// \param unlabeledTrailingClosureIndex The index of an unlabeled trailing closure, +/// if any. /// \param allowFixes Whether to allow fixes when matching arguments. /// /// \param listener Listener that will be notified when certain problems occur, @@ -4905,7 +4906,7 @@ class MatchCallArgumentListener { bool matchCallArguments(SmallVectorImpl &args, ArrayRef params, const ParameterListInfo ¶mInfo, - bool hasTrailingClosure, + Optional unlabeledTrailingClosureIndex, bool allowFixes, MatchCallArgumentListener &listener, SmallVectorImpl ¶meterBindings); diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 93d744b8b07d5..8c57b244177b2 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -995,7 +995,6 @@ namespace { E = CallExpr::create(Context, newCallee, lParen, {newArg}, {Identifier()}, {SourceLoc()}, rParen, - SourceLoc(), SourceLoc(), /*trailingClosures=*/{}, /*implicit=*/false); } diff --git a/lib/Sema/TypeCheckPropertyWrapper.cpp b/lib/Sema/TypeCheckPropertyWrapper.cpp index 1b5ff0625df15..f79cf4b4d8113 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -750,8 +750,6 @@ Expr *swift::buildPropertyWrapperWrappedValueCall( auto *init = CallExpr::create(ctx, typeExpr, startLoc, {initializer}, {argName}, {initializer->getStartLoc()}, endLoc, - /*trailingLBrace=*/SourceLoc(), - /*trailingRBrace=*/SourceLoc(), /*trailingClosures=*/{}, /*implicit=*/true); initializer = init; @@ -787,8 +785,6 @@ Expr *swift::buildPropertyWrapperWrappedValueCall( auto *init = CallExpr::create(ctx, typeExpr, startLoc, elements, elementNames, elementLocs, endLoc, - /*trailingLBrace=*/SourceLoc(), - /*trailingRBrace=*/SourceLoc(), /*trailingClosures=*/{}, /*implicit=*/true); initializer = init; diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 72bd61443fb7b..6550213718039 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -891,7 +891,6 @@ static Expr *buildStorageReference(AccessorDecl *accessor, lookupExpr = SubscriptExpr::create( ctx, wrapperMetatype, SourceLoc(), args, subscriptDecl->getName().getArgumentNames(), { }, SourceLoc(), - /*trailingLBrace=*/SourceLoc(), /*trailingRBrace=*/SourceLoc(), /*trailingClosures=*/{}, subscriptDecl, /*Implicit=*/true); // FIXME: Since we're not resolving overloads or anything, we should be From 882035f0033bfd6e341ff376b9a7605ccef363be Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 9 Apr 2020 17:53:31 -0400 Subject: [PATCH 18/37] Implement the conservative option for typechecking multiple trailing closures. The current approach for type-checking single trailing closures is to scan backwards from the end of the parameter list, looking for something that can be passed a closure. This generalizes that to perform label-matching in reverse on any later trailing closures, then pick up from that point when trying to place the unlabeled trailing closure. The current approach is really not a good language rule, but changing it as much as we'd really like will require evolution approval and a source break, so we have to be cautious. --- lib/Sema/CSSimplify.cpp | 131 ++++++++++++------ test/Parse/multiple_trailing_closures.swift | 139 +++++++------------- 2 files changed, 144 insertions(+), 126 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index ba220d39a335c..ff841c9c63023 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -432,59 +432,114 @@ matchCallArguments(SmallVectorImpl &args, haveUnfulfilledParams = true; }; - // If we have a trailing closure, it maps to the last parameter. - // TODO: generalize this correctly for the closure arg index. - if (unlabeledTrailingClosureArgIndex && numParams > 0) { - unsigned lastParamIdx = numParams - 1; - bool lastAcceptsTrailingClosure = - acceptsTrailingClosure(params[lastParamIdx]); - - // If the last parameter is defaulted, this might be - // an attempt to use a trailing closure with previous - // parameter that accepts a function type e.g. - // - // func foo(_: () -> Int, _ x: Int = 0) {} - // foo { 42 } - if (!lastAcceptsTrailingClosure && numParams > 1 && - paramInfo.hasDefaultArgument(lastParamIdx)) { - auto paramType = params[lastParamIdx - 1].getPlainType(); - // If the parameter before defaulted last accepts. - if (paramType->is()) { - lastAcceptsTrailingClosure = true; - lastParamIdx -= 1; + // If we have an unlabeled trailing closure, we match trailing closure + // labels from the end, then match the trailing closure arg. + if (unlabeledTrailingClosureArgIndex) { + unsigned unlabeledArgIdx = *unlabeledTrailingClosureArgIndex; + + // One past the next parameter index to look at. + unsigned prevParamIdx = numParams; + + // Scan backwards to match any labeled trailing closures. + for (unsigned argIdx = numArgs - 1; argIdx != unlabeledArgIdx; --argIdx) { + bool claimed = false; + + // We do this scan on a copy of prevParamIdx so that, if it fails, + // we'll restart at this same point for the next trailing closure. + unsigned prevParamIdxForArg = prevParamIdx; + while (prevParamIdxForArg > 0) { + prevParamIdxForArg -= 1; + unsigned paramIdx = prevParamIdxForArg; + + // Check for an exact label match. + const auto ¶m = params[paramIdx]; + if (param.getLabel() == args[argIdx].getLabel()) { + parameterBindings[paramIdx].push_back(argIdx); + claim(param.getLabel(), argIdx); + + // Future arguments should search prior to this parameter. + prevParamIdx = prevParamIdxForArg; + claimed = true; + break; + } + } + + // If we ran out of parameters, there's no match for this argument. + // TODO: somehow fall through to report out-of-order arguments? + if (!claimed) { + if (listener.extraArgument(argIdx)) + return true; } } - bool isExtraClosure = false; - // If there is no suitable last parameter to accept the trailing closure, + // Okay, we've matched all the labeled closures; scan backwards from + // there to match the unlabeled trailing closure. + Optional unlabeledParamIdx; + if (prevParamIdx > 0) { + unsigned paramIdx = prevParamIdx - 1; + + bool lastAcceptsTrailingClosure = + acceptsTrailingClosure(params[paramIdx]); + + // If the last parameter is defaulted, this might be + // an attempt to use a trailing closure with previous + // parameter that accepts a function type e.g. + // + // func foo(_: () -> Int, _ x: Int = 0) {} + // foo { 42 } + // + // FIXME: shouldn't this skip multiple arguments and look for + // something that acceptsTrailingClosure rather than just something + // with a function type? + if (!lastAcceptsTrailingClosure && paramIdx > 0 && + paramInfo.hasDefaultArgument(paramIdx)) { + auto paramType = params[paramIdx - 1].getPlainType(); + // If the parameter before defaulted last accepts. + if (paramType->is()) { + lastAcceptsTrailingClosure = true; + paramIdx -= 1; + } + } + + if (lastAcceptsTrailingClosure) + unlabeledParamIdx = paramIdx; + } + + // If there's no suitable last parameter to accept the trailing closure, // notify the listener and bail if we need to. - if (!lastAcceptsTrailingClosure) { - if (numArgs > numParams) { + if (!unlabeledParamIdx) { + bool isExtraClosure = false; + + if (prevParamIdx == 0) { + isExtraClosure = true; + } else if (unlabeledArgIdx > 0) { // Argument before the trailing closure. - unsigned prevArg = numArgs - 2; + unsigned prevArg = unlabeledArgIdx - 1; auto &arg = args[prevArg]; // If the argument before trailing closure matches // last parameter, this is just a special case of // an extraneous argument. - const auto param = params[numParams - 1]; + const auto param = params[prevParamIdx - 1]; if (param.hasLabel() && param.getLabel() == arg.getLabel()) { isExtraClosure = true; - if (listener.extraArgument(numArgs - 1)) - return true; } } - if (!isExtraClosure && - listener.trailingClosureMismatch(lastParamIdx, numArgs - 1)) - return true; - } + if (isExtraClosure) { + if (listener.extraArgument(unlabeledArgIdx)) + return true; + } else { + if (listener.trailingClosureMismatch(prevParamIdx - 1, + unlabeledArgIdx)) + return true; + } - // Claim the parameter/argument pair. - claim(params[lastParamIdx].getLabel(), numArgs - 1, - /*ignoreNameClash=*/true); - // Let's claim the trailing closure unless it's an extra argument. - if (!isExtraClosure) - parameterBindings[lastParamIdx].push_back(numArgs - 1); + } else { + // Claim the parameter/argument pair. + claim(params[*unlabeledParamIdx].getLabel(), unlabeledArgIdx, + /*ignoreNameClash=*/true); + parameterBindings[*unlabeledParamIdx].push_back(unlabeledArgIdx); + } } { diff --git a/test/Parse/multiple_trailing_closures.swift b/test/Parse/multiple_trailing_closures.swift index a7b2b03d9926b..389ca2dc0f0f6 100644 --- a/test/Parse/multiple_trailing_closures.swift +++ b/test/Parse/multiple_trailing_closures.swift @@ -2,17 +2,10 @@ func foo(a: () -> T, b: () -> U) {} -foo { - a: { 42 } - b: { "" } -} - -foo { a: { 42 } b: { "" } } +foo { 42 } +b: { "" } -foo { - a: { 42 }, // expected-error {{unexpected ',' separator}} {{12-13=}} - b: { "" } -} +foo { 42 } b: { "" } func when(_ condition: @autoclosure () -> Bool, `then` trueBranch: () -> T, @@ -20,36 +13,58 @@ func when(_ condition: @autoclosure () -> Bool, return condition() ? trueBranch() : falseBranch() } -let _ = when (2 < 3) { - then: { 3 } - else: { 4 } -} +let _ = when (2 < 3) { 3 } else: { 4 } struct S { static func foo(a: Int = 42, b: (inout Int) -> Void) -> S { return S() } + static func foo(a: Int = 42, ab: () -> Void, b: (inout Int) -> Void) -> S { + return S() + } + subscript(v v: () -> Int) -> Int { get { return v() } } + subscript(u u: () -> Int, v v: () -> Int) -> Int { + get { return u() + v() } + } + subscript(cond: Bool, v v: () -> Int) -> Int { get { return cond ? 0 : v() } } + subscript(cond: Bool, u u: () -> Int, v v: () -> Int) -> Int { + get { return cond ? u() : v() } + } } let _: S = .foo { - b: { $0 = $0 + 1 } + $0 = $0 + 1 } +let _: S = .foo {} b: { $0 = $0 + 1 } + func bar(_ s: S) { _ = s[] { - v: { 42 } + 42 + } + + _ = s[] { + 21 + } v: { + 42 + } + + _ = s[true] { + 42 } _ = s[true] { - v: { 42 } + 21 + } v: { + 42 } } @@ -58,98 +73,46 @@ func multiple_trailing_with_defaults( animations: (() -> Void)? = nil, completion: (() -> Void)? = nil) {} -multiple_trailing_with_defaults(duration: 42) { - animations: {} -} +multiple_trailing_with_defaults(duration: 42) {} -multiple_trailing_with_defaults(duration: 42) { - completion: {} -} - -multiple_trailing_with_defaults(duration: 42) { - animations: {} - completion: {} -} +multiple_trailing_with_defaults(duration: 42) {} completion: {} func test_multiple_trailing_syntax_without_labels() { - func fn(f: () -> Void) {} + func fn(f: () -> Void, g: () -> Void) {} - fn { - _: {} // Ok - } + fn {} g: {} // Ok - fn { - f: {} // Ok - } + fn {} _: {} // expected-error {{extra argument in call}} func multiple(_: () -> Void, _: () -> Void) {} - multiple { - _: { } - _: { } - } + multiple {} _: { } func mixed_args_1(a: () -> Void, _: () -> Void) {} func mixed_args_2(_: () -> Void, a: () -> Void, _: () -> Void) {} // expected-note 2 {{'mixed_args_2(_:a:_:)' declared here}} - mixed_args_1 { - a: {} + mixed_args_1 {} _: {} - } - mixed_args_1 { - _: {} - a: {} // expected-error {{argument 'a' must precede unnamed argument #1}} - } + // FIXME: not a good diagnostic + mixed_args_1 {} // expected-error {{extra argument in call}} + a: {} - mixed_args_2 { - _: {} + mixed_args_2 {} a: {} _: {} - } - mixed_args_2 { - _: {} // expected-error {{missing argument for parameter 'a' in call}} + mixed_args_2 + {} // expected-error {{missing argument for parameter #1 in call}} _: {} - } - mixed_args_2 { - a: {} + // FIXME: not a good diagnostic + mixed_args_2 + {} // expected-error {{extra argument in call}} _: {} - _: {} // expected-error {{unnamed argument #3 must precede argument 'a'}} - } - - mixed_args_2 { - a: {} // expected-error {{missing argument for parameter #1 in call}} _: {} - } -} - -foo { - a: { 42 } - b: { 42 } - _ = 1 + 2 - // expected-error@-1 {{expected an argument label followed by a closure literal}} -} - -foo { - a: { 42 } - b: { 45 } - c: -} // expected-error {{expected a closure literal}} - -foo { - a: { 42 } - b: { 45 } - c: 67 // expected-error {{expected a closure literal}} - // expected-error@-1 {{extra argument 'c' in call}} + mixed_args_2 + {} // expected-error {{missing argument for parameter #1 in call}} + _: {} } - -foo { // expected-note {{to match this opening '{'}} - a: { 42 } - b: { "" } - - func foo() {} // expected-error {{expected an argument label followed by a closure literal}} - // expected-error@-1 {{expected '}' at the end of a trailing closures block}} -} // expected-error {{extraneous '}' at top level}} From 67838cd60906aea8bca179d02b9ed6a4a31df5eb Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 9 Apr 2020 23:17:15 -0400 Subject: [PATCH 19/37] Call out another bad diagnostic, delete a redundant test. --- test/Parse/multiple_trailing_closures.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/Parse/multiple_trailing_closures.swift b/test/Parse/multiple_trailing_closures.swift index 389ca2dc0f0f6..ab1a38d07aaea 100644 --- a/test/Parse/multiple_trailing_closures.swift +++ b/test/Parse/multiple_trailing_closures.swift @@ -89,19 +89,23 @@ func test_multiple_trailing_syntax_without_labels() { multiple {} _: { } func mixed_args_1(a: () -> Void, _: () -> Void) {} - func mixed_args_2(_: () -> Void, a: () -> Void, _: () -> Void) {} // expected-note 2 {{'mixed_args_2(_:a:_:)' declared here}} + func mixed_args_2(_: () -> Void, a: () -> Void, _: () -> Void) {} // expected-note {{'mixed_args_2(_:a:_:)' declared here}} - mixed_args_1 {} + mixed_args_1 + {} _: {} // FIXME: not a good diagnostic - mixed_args_1 {} // expected-error {{extra argument in call}} + mixed_args_1 + {} // expected-error {{extra argument in call}} a: {} - mixed_args_2 {} + mixed_args_2 + {} a: {} _: {} + // FIXME: not a good diagnostic mixed_args_2 {} // expected-error {{missing argument for parameter #1 in call}} _: {} @@ -111,8 +115,4 @@ func test_multiple_trailing_syntax_without_labels() { {} // expected-error {{extra argument in call}} _: {} _: {} - - mixed_args_2 - {} // expected-error {{missing argument for parameter #1 in call}} - _: {} } From 015d83844136ed0d2d21bb11aefc1a40a7679042 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 15 Apr 2020 18:45:45 -0400 Subject: [PATCH 20/37] Claim trailing closure arguments like we used to in error cases. --- lib/Sema/CSSimplify.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index ff841c9c63023..314597aba46b9 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -508,8 +508,8 @@ matchCallArguments(SmallVectorImpl &args, // If there's no suitable last parameter to accept the trailing closure, // notify the listener and bail if we need to. if (!unlabeledParamIdx) { + // Try to use a specialized diagnostic for an extra closure. bool isExtraClosure = false; - if (prevParamIdx == 0) { isExtraClosure = true; } else if (unlabeledArgIdx > 0) { @@ -534,7 +534,16 @@ matchCallArguments(SmallVectorImpl &args, return true; } - } else { + if (isExtraClosure) { + // Claim the unlabeled trailing closure without an associated + // parameter to suppress further complaints about it. + claim(Identifier(), unlabeledArgIdx, /*ignoreNameClash=*/true); + } else { + unlabeledParamIdx = prevParamIdx - 1; + } + } + + if (unlabeledParamIdx) { // Claim the parameter/argument pair. claim(params[*unlabeledParamIdx].getLabel(), unlabeledArgIdx, /*ignoreNameClash=*/true); From a2fac90284c79dbfa5a32ce6aad153c65d103166 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 15 Apr 2020 18:46:23 -0400 Subject: [PATCH 21/37] Revise test for the new multiple-closure syntax. This isn't really correct --- we're missing nodes for the call. But it gets the test passing. --- .../round_trip_parse_gen.swift.withkinds | 29 ++++++------------- test/Syntax/round_trip_parse_gen.swift | 19 +++--------- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index 34fa092078366..433629b13c940 100644 --- a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds +++ b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -94,9 +94,6 @@ class C { _ = .foo _ = .foo(x: 12) _ = .foo { 12 } - _ = .foo { - arg1: { 12 } - } _ = .foo[12] _ = .foo.bar } @@ -260,29 +257,21 @@ func closure() () { foo() foo() {} - foo {} - foo { - arg1: {} - } - foo() { - arg1: {} - arg2: {} - } + foo {} + foo() {} + arg2: {} foo {} foo.bar() - foo.bar() {} - foo.bar() { - arg1: {} - } + foo.bar() {} + foo.bar() {} + arg2: {} foo.bar {} foo[] foo[1] foo[] {} - foo[1] {} - foo[1] { - arg1: {} - arg2: {} - } + foo[1] {} + foo[1] {} + arg2: {} foo[1][2,x:3] foo?++.bar!(baz).self foo().0 diff --git a/test/Syntax/round_trip_parse_gen.swift b/test/Syntax/round_trip_parse_gen.swift index 271d537a52719..71cdde0d9b7a3 100644 --- a/test/Syntax/round_trip_parse_gen.swift +++ b/test/Syntax/round_trip_parse_gen.swift @@ -94,9 +94,6 @@ class C { _ = .foo _ = .foo(x: 12) _ = .foo { 12 } - _ = .foo { - arg1: { 12 } - } _ = .foo[12] _ = .foo.bar } @@ -261,28 +258,20 @@ func postfix() { foo() foo() {} foo {} - foo { - arg1: {} - } - foo() { - arg1: {} + foo() {} arg2: {} - } foo {} foo.bar() foo.bar() {} - foo.bar() { - arg1: {} - } + foo.bar() {} + arg2: {} foo.bar {} foo[] foo[1] foo[] {} foo[1] {} - foo[1] { - arg1: {} + foo[1] {} arg2: {} - } foo[1][2,x:3] foo?++.bar!(baz).self foo().0 From 1e1e7f37001b4475a652996bb7a554b6e88ef6ee Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 15 Apr 2020 18:47:48 -0400 Subject: [PATCH 22/37] XFAIL a pair of tooling tests that are specific to multiple trailing closures. --- test/IDE/complete_multiple_trailingclosure.swift | 1 + test/swift-indent/multiple-trailing-closures.swift | 2 ++ 2 files changed, 3 insertions(+) diff --git a/test/IDE/complete_multiple_trailingclosure.swift b/test/IDE/complete_multiple_trailingclosure.swift index efad51de753b3..6bc5fb44c55cd 100644 --- a/test/IDE/complete_multiple_trailingclosure.swift +++ b/test/IDE/complete_multiple_trailingclosure.swift @@ -9,6 +9,7 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_4 | %FileCheck %s -check-prefix=METHOD_4 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_5 | %FileCheck %s -check-prefix=METHOD_1 +// XFAIL: * func globalFunc1(fn1: () -> Int, fn2: () -> String) {} func testGlobalFunc() { diff --git a/test/swift-indent/multiple-trailing-closures.swift b/test/swift-indent/multiple-trailing-closures.swift index 0e3dd404dba57..4e04fd834e8cc 100644 --- a/test/swift-indent/multiple-trailing-closures.swift +++ b/test/swift-indent/multiple-trailing-closures.swift @@ -1,6 +1,8 @@ // RUN: %swift-indent %s >%t.response // RUN: diff -u %s %t.response +// XFAIL: * + foo(c: 12, d: 34) { a: { print("foo") } } From 8df09a0a1d98edfd91c5f01b5fcf35aad4331eca Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 15 Apr 2020 18:54:52 -0400 Subject: [PATCH 23/37] Remove unnecessary check for a single closure. At an earlier point, we were doing this check after parsing the labeled closures, but that never made much sense, since it's a good diagnostic regardless. --- lib/Parse/ParseExpr.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 87203f97d3a66..5a8a8b4dc45ac 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3186,13 +3186,11 @@ Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, diagnose(braceLoc, diag::trailing_closure_after_newlines); diagnose(calleeRange.Start, diag::trailing_closure_callee_here); - if (closures.size() == 1) { - auto *CE = dyn_cast(closures[0].ClosureExpr); - if (CE && CE->hasAnonymousClosureVars() && - CE->getParameters()->size() == 0) { - diagnose(braceLoc, diag::brace_stmt_suggest_do) - .fixItInsert(braceLoc, "do "); - } + auto *CE = dyn_cast(closures[0].ClosureExpr); + if (CE && CE->hasAnonymousClosureVars() && + CE->getParameters()->size() == 0) { + diagnose(braceLoc, diag::brace_stmt_suggest_do) + .fixItInsert(braceLoc, "do "); } } From 2e094a556dea23910a9f60d869ebf2ddf185dd4a Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Tue, 14 Apr 2020 15:31:01 -0700 Subject: [PATCH 24/37] [Syntax] Update for braceless multiple trailing closure syntax --- lib/Parse/ParseExpr.cpp | 4 +++ .../round_trip_parse_gen.swift.withkinds | 21 +++++------ test/Syntax/round_trip_parse_gen.swift | 5 +-- unittests/Syntax/ExprSyntaxTests.cpp | 8 ++--- utils/gyb_syntax_support/ExprNodes.py | 35 +++++++------------ .../NodeSerializationCodes.py | 5 ++- 6 files changed, 36 insertions(+), 42 deletions(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 5a8a8b4dc45ac..5ac11f87f7bc2 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3196,6 +3196,8 @@ Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, // Parse labeled trailing closures. while (isStartOfLabelledTrailingClosure(*this)) { + SyntaxParsingContext ClosureCtx(SyntaxContext, + SyntaxKind::MultipleTrailingClosureElement); Identifier label; auto labelLoc = consumeArgumentLabel(label); consumeToken(tok::colon); @@ -3208,6 +3210,8 @@ Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, // Don't diagnose whitespace gaps before labelled closures. } + SyntaxContext->collectNodesInPlace( + SyntaxKind::MultipleTrailingClosureElementList); return result; } diff --git a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index 433629b13c940..43bd7aa16e71e 100644 --- a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds +++ b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -93,7 +93,7 @@ class C { func implictMember() { _ = .foo _ = .foo(x: 12) - _ = .foo { 12 } + _ = .foo() { 12 } _ = .foo[12] _ = .foo.bar } @@ -257,21 +257,22 @@ func closure() () { foo() foo() {} - foo {} - foo() {} - arg2: {} + foo {} + foo { } + arg2: {} foo {} foo.bar() - foo.bar() {} - foo.bar() {} - arg2: {} + foo.bar() {} + foo.bar() {} + arg2: {} + in: {} foo.bar {} foo[] foo[1] foo[] {} - foo[1] {} - foo[1] {} - arg2: {} + foo[1] {} + foo[1] {} + arg2: {} foo[1][2,x:3] foo?++.bar!(baz).self foo().0 diff --git a/test/Syntax/round_trip_parse_gen.swift b/test/Syntax/round_trip_parse_gen.swift index 71cdde0d9b7a3..8c4c998fb49cb 100644 --- a/test/Syntax/round_trip_parse_gen.swift +++ b/test/Syntax/round_trip_parse_gen.swift @@ -93,7 +93,7 @@ class C { func implictMember() { _ = .foo _ = .foo(x: 12) - _ = .foo { 12 } + _ = .foo() { 12 } _ = .foo[12] _ = .foo.bar } @@ -258,13 +258,14 @@ func postfix() { foo() foo() {} foo {} - foo() {} + foo { } arg2: {} foo {} foo.bar() foo.bar() {} foo.bar() {} arg2: {} + in: {} foo.bar {} foo[] foo[1] diff --git a/unittests/Syntax/ExprSyntaxTests.cpp b/unittests/Syntax/ExprSyntaxTests.cpp index 4d103c7f0281d..45f08f2c4f78b 100644 --- a/unittests/Syntax/ExprSyntaxTests.cpp +++ b/unittests/Syntax/ExprSyntaxTests.cpp @@ -400,8 +400,8 @@ TEST(ExprSyntaxTests, FunctionCallExprGetAPIs) { auto ArgList = getFullArgumentList(); auto RightParen = SyntaxFactory::makeRightParenToken({}, {}); - auto Call = SyntaxFactory::makeFunctionCallExpr(SymbolicRef, LeftParen, - ArgList, RightParen, None); + auto Call = SyntaxFactory::makeFunctionCallExpr( + SymbolicRef, LeftParen, ArgList, RightParen, None, None); { auto GottenExpression1 = Call.getCalledExpression(); @@ -435,8 +435,8 @@ TEST(ExprSyntaxTests, FunctionCallExprMakeAPIs) { auto RightParen = SyntaxFactory::makeRightParenToken({}, {}); { - auto Call = SyntaxFactory::makeFunctionCallExpr(SymbolicRef, LeftParen, - ArgList, RightParen, None); + auto Call = SyntaxFactory::makeFunctionCallExpr( + SymbolicRef, LeftParen, ArgList, RightParen, None, None); llvm::SmallString<64> Scratch; llvm::raw_svector_ostream OS(Scratch); Call.print(OS); diff --git a/utils/gyb_syntax_support/ExprNodes.py b/utils/gyb_syntax_support/ExprNodes.py index 424f8981f39c3..e746211f7bead 100644 --- a/utils/gyb_syntax_support/ExprNodes.py +++ b/utils/gyb_syntax_support/ExprNodes.py @@ -409,17 +409,6 @@ Node('MultipleTrailingClosureElementList', kind='SyntaxCollection', element='MultipleTrailingClosureElement'), - # multiple-trailing-closure-clause -> - # '{' multiple-trailing-closure-element-list '}' - Node('MultipleTrailingClosureClause', kind='Syntax', - traits=['Braced'], - children=[ - Child('LeftBrace', kind='LeftBraceToken'), - Child('Elements', kind='MultipleTrailingClosureElementList', - collection_element_name='Element'), - Child('RightBrace', kind='RightBraceToken'), - ]), - # call-expr -> expr '(' call-argument-list ')' closure-expr? # | expr closure-expr Node('FunctionCallExpr', kind='Expr', @@ -431,12 +420,12 @@ collection_element_name='Argument'), Child('RightParen', kind='RightParenToken', is_optional=True), - Child('TrailingClosure', kind='Syntax', is_optional=True, - node_choices=[ - Child('SingleClosure', kind='ClosureExpr'), - Child('MultipleTrailingClosures', - kind='MultipleTrailingClosureClause'), - ]), + Child('TrailingClosure', kind='ClosureExpr', + is_optional=True), + Child('AdditionalTrailingClosures', + kind='MultipleTrailingClosureElementList', + collection_element_name='AdditionalTralingClosure', + is_optional=True), ]), # subscript-expr -> expr '[' call-argument-list ']' closure-expr? @@ -447,12 +436,12 @@ Child('ArgumentList', kind='TupleExprElementList', collection_element_name='Argument'), Child('RightBracket', kind='RightSquareBracketToken'), - Child('TrailingClosure', kind='Syntax', is_optional=True, - node_choices=[ - Child('SingleClosure', kind='ClosureExpr'), - Child('MultipleTrailingClosures', - kind='MultipleTrailingClosureClause'), - ]), + Child('TrailingClosure', kind='ClosureExpr', + is_optional=True), + Child('AdditionalTrailingClosures', + kind='MultipleTrailingClosureElementList', + collection_element_name='AdditionalTralingClosure', + is_optional=True), ]), # optional-chaining-expr -> expr '?' diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index d542eb2e07921..eeea90df395eb 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -246,9 +246,8 @@ 'QualifiedDeclName': 242, 'CatchItem': 243, 'CatchItemList': 244, - 'MultipleTrailingClosureClause': 245, - 'MultipleTrailingClosureElementList': 246, - 'MultipleTrailingClosureElement': 247, + 'MultipleTrailingClosureElementList': 245, + 'MultipleTrailingClosureElement': 246, } From 1cbb1e76d43d547bebde9e2e028d8fdaf5bfa06a Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Wed, 15 Apr 2020 17:10:55 -0700 Subject: [PATCH 25/37] [CodeCompletion] Update for braceless multiple trailing closure --- include/swift/IDE/CodeCompletion.h | 1 + include/swift/Parse/CodeCompletionCallbacks.h | 3 + lib/IDE/CodeCompletion.cpp | 73 +++++- lib/IDE/ExprContextAnalysis.cpp | 97 ++------ lib/IDE/ExprContextAnalysis.h | 21 +- lib/Parse/ParseExpr.cpp | 36 ++- .../complete_multiple_trailingclosure.swift | 229 ++++++++++++------ 7 files changed, 295 insertions(+), 165 deletions(-) diff --git a/include/swift/IDE/CodeCompletion.h b/include/swift/IDE/CodeCompletion.h index 8deabfd7a205a..b494d74aafe8f 100644 --- a/include/swift/IDE/CodeCompletion.h +++ b/include/swift/IDE/CodeCompletion.h @@ -525,6 +525,7 @@ enum class CompletionKind { AttributeDeclParen, PoundAvailablePlatform, CallArg, + LabeledTrailingClosure, ReturnStmtExpr, YieldStmtExpr, ForEachSequence, diff --git a/include/swift/Parse/CodeCompletionCallbacks.h b/include/swift/Parse/CodeCompletionCallbacks.h index c503fd7ed35b4..b8c5fe151fe83 100644 --- a/include/swift/Parse/CodeCompletionCallbacks.h +++ b/include/swift/Parse/CodeCompletionCallbacks.h @@ -198,6 +198,9 @@ class CodeCompletionCallbacks { virtual void completeCallArg(CodeCompletionExpr *E, bool isFirst) {}; + virtual void completeLabeledTrailingClosure(CodeCompletionExpr *E, + bool isAtStartOfLine) {}; + virtual void completeReturnStmt(CodeCompletionExpr *E) {}; /// Complete a yield statement. A missing yield index means that the diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 2cef1361e00c7..49de32eae7082 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1446,6 +1446,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { bool ShouldCompleteCallPatternAfterParen = true; bool PreferFunctionReferencesToCalls = false; bool AttTargetIsIndependent = false; + bool IsAtStartOfLine = false; Optional AttTargetDK; Optional ParentStmtKind; @@ -1572,6 +1573,9 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { void completeUnresolvedMember(CodeCompletionExpr *E, SourceLoc DotLoc) override; void completeCallArg(CodeCompletionExpr *E, bool isFirst) override; + void completeLabeledTrailingClosure(CodeCompletionExpr *E, + bool isAtStartOfLine) override; + void completeReturnStmt(CodeCompletionExpr *E) override; void completeYieldStmt(CodeCompletionExpr *E, Optional yieldIndex) override; @@ -4155,12 +4159,15 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } void addCallArgumentCompletionResults( - ArrayRef Args) { + ArrayRef ParamInfos) { Type ContextType; if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) ContextType = typeContext->getDeclaredTypeInContext(); - for (auto *Arg : Args) { + for (auto Info : ParamInfos) { + const auto *Arg = Info.Param; + if (!Arg) + continue; CodeCompletionResultBuilder Builder( Sink, CodeCompletionResult::ResultKind::Pattern, SemanticContextKind::ExpressionSpecific, {}); @@ -5150,6 +5157,14 @@ void CodeCompletionCallbacksImpl::completeCallArg(CodeCompletionExpr *E, } } +void CodeCompletionCallbacksImpl::completeLabeledTrailingClosure( + CodeCompletionExpr *E, bool isAtStartOfLine) { + CurDeclContext = P.CurDeclContext; + CodeCompleteTokenExpr = E; + Kind = CompletionKind::LabeledTrailingClosure; + IsAtStartOfLine = isAtStartOfLine; +} + void CodeCompletionCallbacksImpl::completeReturnStmt(CodeCompletionExpr *E) { CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; @@ -5332,6 +5347,7 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, case CompletionKind::Import: case CompletionKind::UnresolvedMember: case CompletionKind::CallArg: + case CompletionKind::LabeledTrailingClosure: case CompletionKind::AfterPoundExpr: case CompletionKind::AfterPoundDirective: case CompletionKind::PlatformConditon: @@ -5875,9 +5891,14 @@ void CodeCompletionCallbacksImpl::doneParsing() { (Lookup.FoundFunctionCalls && Lookup.FoundFunctionsWithoutFirstKeyword); } else if (!ContextInfo.getPossibleParams().empty()) { - Lookup.addCallArgumentCompletionResults(ContextInfo.getPossibleParams()); + auto params = ContextInfo.getPossibleParams(); + Lookup.addCallArgumentCompletionResults(params); shouldPerformGlobalCompletion = !ContextInfo.getPossibleTypes().empty(); + // Fallback to global completion if the position is out of number. It's + // better than suggest nothing. + shouldPerformGlobalCompletion |= llvm::all_of( + params, [](const PossibleParamInfo &P) { return !P.Param; }); } if (shouldPerformGlobalCompletion) { @@ -5888,6 +5909,52 @@ void CodeCompletionCallbacksImpl::doneParsing() { break; } + case CompletionKind::LabeledTrailingClosure: { + ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); + + SmallVector params; + // Only complete function type parameters + llvm::copy_if(ContextInfo.getPossibleParams(), std::back_inserter(params), + [](const PossibleParamInfo &P) { + // nullptr indicates out of bounds. + if (!P.Param) + return true; + return P.Param->getPlainType() + ->lookThroughAllOptionalTypes() + ->is(); + }); + + bool allRequired = false; + if (!params.empty()) { + Lookup.addCallArgumentCompletionResults(params); + allRequired = llvm::all_of( + params, [](const PossibleParamInfo &P) { return P.IsRequired; }); + } + + // If there're optional parameters, do global completion or member + // completion depending on the completion is happening at the start of line. + if (!allRequired) { + if (IsAtStartOfLine) { + // foo() {} + // + // Global completion. + auto &Sink = CompletionContext.getResultSink(); + addDeclKeywords(Sink); + addStmtKeywords(Sink, MaybeFuncBody); + addSuperKeyword(Sink); + addLetVarKeywords(Sink); + addExprKeywords(Sink); + addAnyTypeKeyword(Sink); + DoPostfixExprBeginning(); + } else { + // foo() {} + // Member completion + // TODO: Member completion. + } + } + break; + } + case CompletionKind::ReturnStmtExpr : { SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); Lookup.setExpectedTypes(getReturnTypeFromContext(CurDeclContext), diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index a092e3b3f4262..b935f248693a5 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -561,47 +561,6 @@ static bool getPositionInArgs(DeclContext &DC, Expr *Args, Expr *CCExpr, return false; } -enum class ArgPositionKind { - /// The argument is in normal calling parenthesis. - /// i.e. foo(args..., ) { ... } - NormalArgument, - /// The argument is inside multiple trailing closure block. - /// i.e. foo(args...) { arg2: { ... } } - InClosureBlock, - /// The argument is inside multiple trailing closure block, also it is the - /// sole element. - /// foo(args...) { } - InEmptyClosureBlock, -}; - -static ArgPositionKind getArgPositionKind(DeclContext &DC, Expr *Args, - unsigned Position) { - SourceManager &SM = DC.getASTContext().SourceMgr; - - if (auto tuple = dyn_cast(Args)) { - SourceLoc argPos = tuple->getElement(Position)->getStartLoc(); - SourceLoc rParenLoc = tuple->getRParenLoc(); - if (rParenLoc.isInvalid() || SM.isBeforeInBuffer(rParenLoc, argPos)) { - // Invariant: If the token is at the label position, the label location is - // invalid, and the value is a CodeCompletionExpr. - if (Position == tuple->getNumElements() - 1 && - tuple->getNumTrailingElements() == 1 && - tuple->getElementNameLoc(Position).isInvalid()) { - return ArgPositionKind::InEmptyClosureBlock; - } - return ArgPositionKind::InClosureBlock; - } - } else if (auto paren = dyn_cast(Args)) { - SourceLoc argLoc = paren->getSubExpr()->getStartLoc(); - SourceLoc rParenLoc = paren->getRParenLoc(); - // We don't have a way to distingish between 'foo { _: }' and - // 'foo { }'. For now, consider it latter one. - if (rParenLoc.isInvalid() || SM.isBeforeInBuffer(rParenLoc, argLoc)) - return ArgPositionKind::InEmptyClosureBlock; - } - return ArgPositionKind::NormalArgument; -} - /// Given an expression and its context, the analyzer tries to figure out the /// expected type of the expression by analyzing its context. class ExprContextAnalyzer { @@ -612,7 +571,7 @@ class ExprContextAnalyzer { // Results populated by Analyze() SmallVectorImpl &PossibleTypes; - SmallVectorImpl &PossibleParams; + SmallVectorImpl &PossibleParams; SmallVectorImpl &PossibleCallees; bool &singleExpressionBody; @@ -623,8 +582,8 @@ class ExprContextAnalyzer { PossibleTypes.push_back(ty->getRValueType()); } - void recordPossibleParam(const AnyFunctionType::Param &arg) { - PossibleParams.push_back(&arg); + void recordPossibleParam(const AnyFunctionType::Param *arg, bool isRequired) { + PossibleParams.emplace_back(arg, isRequired); } /// Collect context information at call argument position. @@ -657,9 +616,9 @@ class ExprContextAnalyzer { if (!getPositionInArgs(*DC, Arg, ParsedExpr, Position, HasName)) return false; - ArgPositionKind positionKind = getArgPositionKind(*DC, Arg, Position); - // Collect possible types (or labels) at the position. + // FIXME: Take variadic and optional parameters into account. We need to do + // something equivalent to 'constraints::matchCallArguments' { bool MayNeedName = !HasName && !E->isImplicit() && (isa(E) | isa(E) || @@ -681,19 +640,6 @@ class ExprContextAnalyzer { if (paramList && paramList->size() != Params.size()) paramList = nullptr; } - - // Determine the index of the parameter that can be a single trailing - // closure. - unsigned singleTrailingClosureIdx = Params.size(); - for (int idx = Params.size() - 1; idx >= 0; --idx) { - if (Params[idx].getPlainType()->is()) { - singleTrailingClosureIdx = idx; - break; - } - if (!paramList || !paramList->get(idx)->isDefaultArgument()) - break; - } - for (auto Pos = Position; Pos < Params.size(); ++Pos) { const auto ¶mType = Params[Pos]; Type ty = paramType.getPlainType(); @@ -701,28 +647,11 @@ class ExprContextAnalyzer { ty = memberDC->mapTypeIntoContext(ty); if (paramType.hasLabel() && MayNeedName) { - // In trailing closure block, don't suggest non-closure arguments. - if (positionKind >= ArgPositionKind::InClosureBlock) { - Type argTy = ty; - if (paramType.isAutoClosure() && ty->is()) - argTy = ty->castTo()->getResult(); - if (!argTy->is()) - continue; - - // If the token is the only element in the closure block. It might - // be a single trailing closure. We should perform global - // completion as well. - if (positionKind == ArgPositionKind::InEmptyClosureBlock && - Pos == singleTrailingClosureIdx) { - auto resultTy = argTy->castTo()->getResult(); - if (seenTypes.insert(resultTy.getPointer()).second) - recordPossibleType(resultTy); - } - } - + bool isDefaulted = paramList && + paramList->get(Pos)->isDefaultArgument(); if (seenArgs.insert({paramType.getLabel(), ty.getPointer()}).second) - recordPossibleParam(paramType); - if (paramList && paramList->get(Position)->isDefaultArgument()) + recordPossibleParam(¶mType, !isDefaulted); + if (isDefaulted) continue; } else { auto argTy = ty; @@ -733,6 +662,12 @@ class ExprContextAnalyzer { } break; } + // If the argument position is out of expeceted number, indicate that + // with optional nullptr param. + if (Position >= Params.size()) { + if (seenArgs.insert({Identifier(), nullptr}).second) + recordPossibleParam(nullptr, /*isRequired=*/false); + } } } return !PossibleTypes.empty() || !PossibleParams.empty(); @@ -999,7 +934,7 @@ class ExprContextAnalyzer { public: ExprContextAnalyzer( DeclContext *DC, Expr *ParsedExpr, SmallVectorImpl &PossibleTypes, - SmallVectorImpl &PossibleArgs, + SmallVectorImpl &PossibleArgs, SmallVectorImpl &PossibleCallees, bool &singleExpressionBody) : DC(DC), ParsedExpr(ParsedExpr), SM(DC->getASTContext().SourceMgr), diff --git a/lib/IDE/ExprContextAnalysis.h b/lib/IDE/ExprContextAnalysis.h index bab0306057b9a..c7748f8fc54ba 100644 --- a/lib/IDE/ExprContextAnalysis.h +++ b/lib/IDE/ExprContextAnalysis.h @@ -50,11 +50,28 @@ struct FunctionTypeAndDecl { : Type(Type), Decl(Decl), SemanticContext(SemanticContext) {} }; +struct PossibleParamInfo { + /// Expected parameter. + /// + /// 'nullptr' indicates that the code completion position is at out of + /// expected argument position. E.g. + /// func foo(x: Int) {} + /// foo(x: 1, ) + const AnyFunctionType::Param *Param; + bool IsRequired; + + PossibleParamInfo(const AnyFunctionType::Param *Param, bool IsRequired) + : Param(Param), IsRequired(IsRequired) { + assert((Param || !IsRequired) && + "nullptr with required flag is not allowed"); + }; +}; + /// Given an expression and its decl context, the analyzer tries to figure out /// the expected type of the expression by analyzing its context. class ExprContextInfo { SmallVector PossibleTypes; - SmallVector PossibleParams; + SmallVector PossibleParams; SmallVector PossibleCallees; bool singleExpressionBody = false; @@ -73,7 +90,7 @@ class ExprContextInfo { // Returns a list of possible argument label names. // Valid only if \c getKind() is \c CallArgument. - ArrayRef getPossibleParams() const { + ArrayRef getPossibleParams() const { return PossibleParams; } diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 5ac11f87f7bc2..c698017694b33 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3146,7 +3146,6 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, static bool isStartOfLabelledTrailingClosure(Parser &P) { // Fast path: the next two tokens are a label and a colon. - // TODO: recognize a code-completion token here and record it in the AST. if (!P.Tok.canBeArgumentLabel() || !P.peekToken().is(tok::colon)) return false; @@ -3154,7 +3153,12 @@ static bool isStartOfLabelledTrailingClosure(Parser &P) { // `label: switch x { ... }`. Parser::BacktrackingScope backtrack(P); P.consumeToken(); - return P.peekToken().is(tok::l_brace); + if (P.peekToken().is(tok::l_brace)) + return true; + // Consider `label: ` that the user is trying to write a closure. + if (P.peekToken().is(tok::code_complete) && !P.peekToken().isAtStartOfLine()) + return true; + return false; } ParserStatus @@ -3195,13 +3199,37 @@ Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, } // Parse labeled trailing closures. - while (isStartOfLabelledTrailingClosure(*this)) { + while (true) { + if (!isStartOfLabelledTrailingClosure(*this)) { + if (!Tok.is(tok::code_complete)) + break; + + // foo() {} + auto CCExpr = new (Context) CodeCompletionExpr(Tok.getLoc()); + if (CodeCompletion) + CodeCompletion->completeLabeledTrailingClosure(CCExpr, Tok.isAtStartOfLine()); + consumeToken(tok::code_complete); + result.hasCodeCompletion(); + closures.push_back({Identifier(), SourceLoc(), CCExpr}); + continue; + } + SyntaxParsingContext ClosureCtx(SyntaxContext, SyntaxKind::MultipleTrailingClosureElement); Identifier label; auto labelLoc = consumeArgumentLabel(label); consumeToken(tok::colon); - ParserResult closure = parseExprClosure(); + ParserResult closure; + if (Tok.is(tok::l_brace)) { + closure = parseExprClosure(); + } else if (Tok.is(tok::code_complete)) { + assert(!Tok.isAtStartOfLine() && + "isStartOfLabelledTrailingClosure() should return false"); + // Swallow code completion token after the label. + // FIXME: Closure literal completion. + consumeToken(tok::code_complete); + return makeParserCodeCompletionStatus(); + } if (closure.isNull()) return makeParserError(); diff --git a/test/IDE/complete_multiple_trailingclosure.swift b/test/IDE/complete_multiple_trailingclosure.swift index 6bc5fb44c55cd..a440f0f4b98b7 100644 --- a/test/IDE/complete_multiple_trailingclosure.swift +++ b/test/IDE/complete_multiple_trailingclosure.swift @@ -1,86 +1,165 @@ -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBAL_FUNC_1 | %FileCheck %s -check-prefix=GLOBAL_FUNC_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBAL_FUNC_2 | %FileCheck %s -check-prefix=GLOBAL_FUNC_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBAL_FUNC_3 | %FileCheck %s -check-prefix=GLOBAL_FUNC_3 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBAL_FUNC_4 | %FileCheck %s -check-prefix=GLOBAL_FUNC_4 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBAL_FUNC_5 | %FileCheck %s -check-prefix=GLOBAL_FUNC_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_1 | %FileCheck %s -check-prefix=METHOD_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_2 | %FileCheck %s -check-prefix=METHOD_1 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_3 | %FileCheck %s -check-prefix=METHOD_3 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_4 | %FileCheck %s -check-prefix=METHOD_4 -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_5 | %FileCheck %s -check-prefix=METHOD_1 - -// XFAIL: * +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBALFUNC_SAMELINE | %FileCheck %s -check-prefix=GLOBALFUNC_SAMELINE +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBALFUNC_NEWLINE | %FileCheck %s -check-prefix=GLOBALFUNC_NEWLINE +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBALFUNC_AFTERLABEL | %FileCheck %s -check-prefix=GLOBALFUNC_AFTERLABEL +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_SAMELINE | %FileCheck %s -check-prefix=METHOD_SAMELINE +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=METHOD_NEWLINE | %FileCheck %s -check-prefix=METHOD_NEWLINE +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_OVERLOADED_SAMELINE | %FileCheck %s -check-prefix=INIT_OVERLOADED_SAMELINE +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_OVERLOADED_NEWLINE | %FileCheck %s -check-prefix=INIT_OVERLOADED_NEWLINE +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_OPTIONAL_SAMELINE | %FileCheck %s -check-prefix=INIT_OPTIONAL_SAMELINE +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_OPTIONAL_NEWLINE | %FileCheck %s -check-prefix=INIT_OPTIONAL_NEWLINE +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_REQUIRED_SAMELINE_1 | %FileCheck %s -check-prefix=INIT_REQUIRED_SAMELINE_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_REQUIRED_NEWLINE_1 | %FileCheck %s -check-prefix=INIT_REQUIRED_NEWLINE_1 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_REQUIRED_SAMELINE_2 | %FileCheck %s -check-prefix=INIT_REQUIRED_SAMELINE_2 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_REQUIRED_NEWLINE_2 | %FileCheck %s -check-prefix=INIT_REQUIRED_NEWLINE_2 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_REQUIRED_SAMELINE_3 | %FileCheck %s -check-prefix=INIT_REQUIRED_SAMELINE_3 +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_REQUIRED_NEWLINE_3 | %FileCheck %s -check-prefix=INIT_REQUIRED_NEWLINE_3 func globalFunc1(fn1: () -> Int, fn2: () -> String) {} func testGlobalFunc() { - globalFunc1 { - #^GLOBAL_FUNC_1^# -// GLOBAL_FUNC_1: Begin completions, 1 items -// GLOBAL_FUNC_1-DAG: Pattern/ExprSpecific: {#fn1: () -> Int##() -> Int#}[#() -> Int#]; -// GLOBAL_FUNC_1: End completions - } - globalFunc1() { - #^GLOBAL_FUNC_2^# -// Same as GLOBAL_FUNC_1 - } - globalFunc1() { - fn1: { 1 } - #^GLOBAL_FUNC_3^# -// GLOBAL_FUNC_3: Begin completions, 1 items -// GLOBAL_FUNC_3-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; name=fn2: () -> String -// GLOBAL_FUNC_3: End completions - } - - globalFunc1(fn1: { 1 }) { - #^GLOBAL_FUNC_4^# -// GLOBAL_FUNC_4: Begin completions -// GLOBAL_FUNC_4-NOT: Decl[Struct]/OtherModule[Swift]/TypeRelation[Identical]: Int[#Int#]; -// GLOBAL_FUNC_4-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; -// GLOBAL_FUNC_4-DAG: Decl[Struct]/OtherModule[Swift]/TypeRelation[Identical]: String[#String#]; -// GLOBAL_FUNC_4: End completions - } - - globalFunc1() { - #^GLOBAL_FUNC_5^# -// Same as GLOBAL_FUNC_1 - fn2: { "" } - } + globalFunc1() + { 1 } #^GLOBALFUNC_SAMELINE^# + #^GLOBALFUNC_NEWLINE^# +// GLOBALFUNC_SAMELINE: Begin completions, 1 items +// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// GLOBALFUNC_SAMELINE: End completions + +// GLOBALFUNC_NEWLINE: Begin completions, 1 items +// GLOBALFUNC_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// GLOBALFUNC_NEWLINE: End completions + + globalFunc1() + { 1 } fn2: #^GLOBALFUNC_AFTERLABEL^# +// FIXME: Closure literal completion. +// GLOBALFUNC_AFTERLABEL-NOT: Begin completions } struct MyStruct { - func method1(fn1: () -> Int, fn2: () -> String) {} + func method1(fn1: () -> Int, fn2: (() -> String)? = nil) {} + func method1(fn1: () -> Int, fn2: Int = nil) {} } func testMethod(value: MyStruct) { value.method1 { - #^METHOD_1^# -// METHOD_1: Begin completions, 1 items -// METHOD_1-DAG: Pattern/ExprSpecific: {#fn1: () -> Int##() -> Int#}[#() -> Int#]; -// METHOD_1: End completions - } - value.method1() { - #^METHOD_2^# -// Same as METHOD_1 - } - value.method1() { - fn1: { 1 } - #^METHOD_3^# -// METHOD_3: Begin completions, 1 items -// METHOD_3-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; name=fn2: () -> String -// METHOD_3: End completions - } - - value.method1(fn1: { 1 }) { - #^METHOD_4^# -// METHOD_4: Begin completions -// METHOD_4-NOT: Decl[Struct]/OtherModule[Swift]/TypeRelation[Identical]: Int[#Int#]; -// METHOD_4-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; -// METHOD_4-DAG: Decl[Struct]/OtherModule[Swift]/TypeRelation[Identical]: String[#String#]; -// METHOD_4: End completions - } + } #^METHOD_SAMELINE^# + #^METHOD_NEWLINE^# +// METHOD_SAMELINE: Begin completions, 1 items +// METHOD_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: (() -> String)?##() -> String#}[#(() -> String)?#]; +// METHOD_SAMELINE: End completions - value.method1 { - #^METHOD_5^# -// Same as METHOD_1 - fn2: { "" } - } +// METHOD_NEWLINE: Begin completions +// METHOD_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: (() -> String)?##() -> String#}[#(() -> String)?#]; +// METHOD_NEWLINE-DAG: Keyword[class]/None: class; +// METHOD_NEWLINE-DAG: Keyword[if]/None: if; +// METHOD_NEWLINE-DAG: Keyword[try]/None: try; +// METHOD_NEWLINE-DAG: Decl[LocalVar]/Local: value[#MyStruct#]; name=value +// METHOD_NEWLINE: End completions +} + +struct TestStruct { + init(fn1: () -> Int, fn2: () -> String, fn3: () -> String) {} + init(fn1: () -> Int) {} + init(fn1: () -> Int, fn2: () -> String) {} + init(fn1: () -> Int, fn3: () -> String) {} +} + +func testOverloadedInit() { + TestStruct { + 1 + } #^INIT_OVERLOADED_SAMELINE^# + #^INIT_OVERLOADED_NEWLINE^# + +// INIT_OVERLOADED_SAMELINE: Begin completions, 2 items +// INIT_OVERLOADED_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// INIT_OVERLOADED_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_OVERLOADED_SAMELINE: End completions + +// INIT_OVERLOADED_NEWLINE: Begin completions +// INIT_OVERLOADED_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// INIT_OVERLOADED_NEWLINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_OVERLOADED_NEWLINE-DAG: Keyword[class]/None: class; +// INIT_OVERLOADED_NEWLINE-DAG: Keyword[if]/None: if; +// INIT_OVERLOADED_NEWLINE-DAG: Keyword[try]/None: try; +// INIT_OVERLOADED_NEWLINE-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct +// INIT_OVERLOADED_NEWLINE: End completions +} + +struct TestStruct2 { + init(fn1: () -> Int, fn2: () -> String = {}, fn3: () -> String = {}) {} +} +func testOptionalInit() { + TestStruct2 { + 2 + } #^INIT_OPTIONAL_SAMELINE^# + #^INIT_OPTIONAL_NEWLINE^# + +// INIT_OPTIONAL_SAMELINE: Begin completions, 2 items +// INIT_OPTIONAL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// INIT_OPTIONAL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_OPTIONAL_SAMELINE: End completions + +// INIT_OPTIONAL_NEWLINE: Begin completions +// INIT_OPTIONAL_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// INIT_OPTIONAL_NEWLINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_OPTIONAL_NEWLINE-DAG: Keyword[class]/None: class; +// INIT_OPTIONAL_NEWLINE-DAG: Keyword[if]/None: if; +// INIT_OPTIONAL_NEWLINE-DAG: Keyword[try]/None: try; +// INIT_OPTIONAL_NEWLINE-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct +// INIT_OPTIONAL_NEWLINE: End completions +} + +struct TestStruct3 { + init(fn1: () -> Int, fn2: () -> String, fn3: () -> String) {} +} +func testOptionalInit() { + // missing 'fn2' and 'fn3'. + TestStruct3 { + 2 + } #^INIT_REQUIRED_SAMELINE_1^# + #^INIT_REQUIRED_NEWLINE_1^# + +// INIT_REQUIRED_SAMELINE_1: Begin completions, 1 items +// INIT_REQUIRED_SAMELINE_1-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// INIT_REQUIRED_SAMELINE_1: End completions + +// INIT_REQUIRED_NEWLINE_1: Begin completions, 1 items +// INIT_REQUIRED_NEWLINE_1-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// INIT_REQUIRED_NEWLINE_1: End completions + + // missing 'fn3'. + TestStruct3 { + 2 + } fn2: { + "test" + } #^INIT_REQUIRED_SAMELINE_2^# + #^INIT_REQUIRED_NEWLINE_2^# + +// INIT_REQUIRED_SAMELINE_2: Begin completions, 1 items +// INIT_REQUIRED_SAMELINE_2-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_REQUIRED_SAMELINE_2: End completions + +// INIT_REQUIRED_NEWLINE_2: Begin completions, 1 items +// INIT_REQUIRED_NEWLINE_2-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_REQUIRED_NEWLINE_2: End completions + + // Call is completed. + TestStruct3 { + 2 + } fn2: { + "test" + } fn3: { + "test" + } #^INIT_REQUIRED_SAMELINE_3^# + #^INIT_REQUIRED_NEWLINE_3^# + +// INIT_REQUIRED_SAMELINE_3-NOT: Begin completions + +// INIT_REQUIRED_NEWLINE_3: Begin completions +// INIT_REQUIRED_NEWLINE_3-NOT: name=fn2 +// INIT_REQUIRED_NEWLINE_3-NOT: name=fn3 +// INIT_REQUIRED_NEWLINE_3-DAG: Keyword[class]/None: class; +// INIT_REQUIRED_NEWLINE_3-DAG: Keyword[if]/None: if; +// INIT_REQUIRED_NEWLINE_3-DAG: Keyword[try]/None: try; +// INIT_REQUIRED_NEWLINE_3-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct +// INIT_REQUIRED_NEWLINE_3-NOT: name=fn2 +// INIT_REQUIRED_NEWLINE_3-NOT: name=fn3 +// INIT_REQUIRED_NEWLINE_3: End completions } From 938c84f5f724636834a9d803fded9eed7c6d38eb Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 16 Apr 2020 00:59:30 -0400 Subject: [PATCH 26/37] Fix test. --- test/Parse/multiple_trailing_closures.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Parse/multiple_trailing_closures.swift b/test/Parse/multiple_trailing_closures.swift index ab1a38d07aaea..e441b517b3ce9 100644 --- a/test/Parse/multiple_trailing_closures.swift +++ b/test/Parse/multiple_trailing_closures.swift @@ -88,8 +88,8 @@ func test_multiple_trailing_syntax_without_labels() { multiple {} _: { } - func mixed_args_1(a: () -> Void, _: () -> Void) {} - func mixed_args_2(_: () -> Void, a: () -> Void, _: () -> Void) {} // expected-note {{'mixed_args_2(_:a:_:)' declared here}} + func mixed_args_1(a: () -> Void, _: () -> Void) {} // expected-note {{'mixed_args_1(a:_:)' declared here}} + func mixed_args_2(_: () -> Void, a: () -> Void, _: () -> Void) {} // expected-note 2 {{'mixed_args_2(_:a:_:)' declared here}} mixed_args_1 {} @@ -97,7 +97,7 @@ func test_multiple_trailing_syntax_without_labels() { // FIXME: not a good diagnostic mixed_args_1 - {} // expected-error {{extra argument in call}} + {} // expected-error {{extra argument in call}} expected-error {{missing argument for parameter #2 in call}} a: {} mixed_args_2 @@ -112,7 +112,7 @@ func test_multiple_trailing_syntax_without_labels() { // FIXME: not a good diagnostic mixed_args_2 - {} // expected-error {{extra argument in call}} + {} // expected-error {{extra argument in call}} expected-error {{missing argument for parameter 'a' in call}} _: {} _: {} } From 40836a0045f9cc794a05a0b02fc4ebd36322fd7c Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 16 Apr 2020 00:59:41 -0400 Subject: [PATCH 27/37] Resolve an ambiguity with the multiple-trailing-closure syntax in favor of parsing `default:` labels. --- lib/Parse/ParseExpr.cpp | 8 ++++++-- test/Parse/multiple_trailing_closures.swift | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index c698017694b33..e3abe56834e5e 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3145,8 +3145,12 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok, } static bool isStartOfLabelledTrailingClosure(Parser &P) { - // Fast path: the next two tokens are a label and a colon. - if (!P.Tok.canBeArgumentLabel() || !P.peekToken().is(tok::colon)) + // Fast path: the next two tokens must be a label and a colon. + // But 'default:' is ambiguous with switch cases and we disallow it + // (unless escaped) even outside of switches. + if (!P.Tok.canBeArgumentLabel() || + P.Tok.is(tok::kw_default) || + !P.peekToken().is(tok::colon)) return false; // Do some tentative parsing to distinguish `label: { ... }` and diff --git a/test/Parse/multiple_trailing_closures.swift b/test/Parse/multiple_trailing_closures.swift index e441b517b3ce9..33cec90d4c79c 100644 --- a/test/Parse/multiple_trailing_closures.swift +++ b/test/Parse/multiple_trailing_closures.swift @@ -116,3 +116,10 @@ func test_multiple_trailing_syntax_without_labels() { _: {} _: {} } + +func produce(fn: () -> Int?, default d: () -> Int) -> Int { // expected-note {{declared here}} + return fn() ?? d() +} +// TODO: The diagnostics here are perhaps a little overboard. +_ = produce { 0 } default: { 1 } // expected-error {{missing argument for parameter 'fn' in call}} expected-error {{consecutive statements}} expected-error {{'default' label can only appear inside a 'switch' statement}} expected-error {{top-level statement cannot begin with a closure expression}} expected-error {{closure expression is unused}} expected-note {{did you mean to use a 'do' statement?}} +_ = produce { 2 } `default`: { 3 } From 7cb5066ba82d21b39d60787a453819b187413bdb Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 16 Apr 2020 09:54:57 -0700 Subject: [PATCH 28/37] [CodeCompletion] Temporarily XFAIL a test case TODO: Postfix completion after trailing closures --- validation-test/IDE/slow/rdar45511835.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/validation-test/IDE/slow/rdar45511835.swift b/validation-test/IDE/slow/rdar45511835.swift index 7028c898e5f01..6ba80a42e88ff 100644 --- a/validation-test/IDE/slow/rdar45511835.swift +++ b/validation-test/IDE/slow/rdar45511835.swift @@ -1,4 +1,6 @@ // RUN: %target-swift-ide-test -code-completion -code-completion-token=COMPLETE -source-filename=%s | %FileCheck %s +// TODO: Postfix completion after trailing closure. +// XFAIL: * // REQUIRES: long_test From 7407a8092d430d133a947243444ca5135910f466 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 16 Apr 2020 15:33:56 -0700 Subject: [PATCH 29/37] [CodeCompletion] Postfix expr completion after trailing closures --- lib/IDE/CodeCompletion.cpp | 33 +++++++++++++++++-- lib/IDE/ExprContextAnalysis.cpp | 8 +++-- lib/IDE/ExprContextAnalysis.h | 5 +++ .../complete_multiple_trailingclosure.swift | 33 +++++++++++++++---- validation-test/IDE/slow/rdar45511835.swift | 2 -- 5 files changed, 69 insertions(+), 12 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 49de32eae7082..d67d5b8df9934 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -5948,8 +5948,37 @@ void CodeCompletionCallbacksImpl::doneParsing() { DoPostfixExprBeginning(); } else { // foo() {} - // Member completion - // TODO: Member completion. + // Member completion. + Expr *analyzedExpr = ContextInfo.getAnalyzedExpr(); + if (!analyzedExpr) + break; + + // Only if the completion token is the last token in the call. + if (analyzedExpr->getEndLoc() != CodeCompleteTokenExpr->getLoc()) + break; + + // If the call expression doesn't have a type, infer it from the + // possible callee info. + Type resultTy = analyzedExpr->getType(); + if (!resultTy) { + if (ContextInfo.getPossibleCallees().empty()) + break; + auto calleeInfo = ContextInfo.getPossibleCallees()[0]; + resultTy = calleeInfo.Type->getResult(); + analyzedExpr->setType(resultTy); + } + + auto &SM = CurDeclContext->getASTContext().SourceMgr; + auto leadingChar = + SM.extractText({SM.getCodeCompletionLoc().getAdvancedLoc(-1), 1}); + Lookup.setHaveLeadingSpace(leadingChar.find_first_of(" \t\f\v") != + StringRef::npos); + + if (isDynamicLookup(resultTy)) + Lookup.setIsDynamicLookup(); + Lookup.getValueExprCompletions(resultTy, /*VD=*/nullptr); + Lookup.getOperatorCompletions(analyzedExpr, leadingSequenceExprs); + Lookup.getPostfixKeywordCompletions(resultTy, analyzedExpr); } } break; diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index b935f248693a5..86ed8e5c262b7 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -573,6 +573,7 @@ class ExprContextAnalyzer { SmallVectorImpl &PossibleTypes; SmallVectorImpl &PossibleParams; SmallVectorImpl &PossibleCallees; + Expr *&AnalyzedExpr; bool &singleExpressionBody; void recordPossibleType(Type ty) { @@ -674,6 +675,7 @@ class ExprContextAnalyzer { } void analyzeExpr(Expr *Parent) { + AnalyzedExpr = Parent; switch (Parent->getKind()) { case ExprKind::Call: case ExprKind::Subscript: @@ -936,10 +938,11 @@ class ExprContextAnalyzer { DeclContext *DC, Expr *ParsedExpr, SmallVectorImpl &PossibleTypes, SmallVectorImpl &PossibleArgs, SmallVectorImpl &PossibleCallees, - bool &singleExpressionBody) + Expr *&AnalyzedExpr, bool &singleExpressionBody) : DC(DC), ParsedExpr(ParsedExpr), SM(DC->getASTContext().SourceMgr), Context(DC->getASTContext()), PossibleTypes(PossibleTypes), PossibleParams(PossibleArgs), PossibleCallees(PossibleCallees), + AnalyzedExpr(AnalyzedExpr), singleExpressionBody(singleExpressionBody) {} void Analyze() { @@ -1048,7 +1051,8 @@ class ExprContextAnalyzer { ExprContextInfo::ExprContextInfo(DeclContext *DC, Expr *TargetExpr) { ExprContextAnalyzer Analyzer(DC, TargetExpr, PossibleTypes, PossibleParams, - PossibleCallees, singleExpressionBody); + PossibleCallees, AnalyzedExpr, + singleExpressionBody); Analyzer.Analyze(); } diff --git a/lib/IDE/ExprContextAnalysis.h b/lib/IDE/ExprContextAnalysis.h index c7748f8fc54ba..039d28c60643d 100644 --- a/lib/IDE/ExprContextAnalysis.h +++ b/lib/IDE/ExprContextAnalysis.h @@ -73,6 +73,7 @@ class ExprContextInfo { SmallVector PossibleTypes; SmallVector PossibleParams; SmallVector PossibleCallees; + Expr *AnalyzedExpr = nullptr; bool singleExpressionBody = false; public: @@ -99,6 +100,10 @@ class ExprContextInfo { ArrayRef getPossibleCallees() const { return PossibleCallees; } + + Expr *getAnalyzedExpr() const { + return AnalyzedExpr; + } }; /// Returns whether \p VD is referenceable with implicit member expression. diff --git a/test/IDE/complete_multiple_trailingclosure.swift b/test/IDE/complete_multiple_trailingclosure.swift index a440f0f4b98b7..a82c91e2a2777 100644 --- a/test/IDE/complete_multiple_trailingclosure.swift +++ b/test/IDE/complete_multiple_trailingclosure.swift @@ -33,16 +33,26 @@ func testGlobalFunc() { // GLOBALFUNC_AFTERLABEL-NOT: Begin completions } +struct SimpleEnum { + case foo, bar + + func enumFunc() {} + static func + (lhs: SimpleEnum, rhs: SimpleEnum) -> SimpleEnum {} +} + struct MyStruct { - func method1(fn1: () -> Int, fn2: (() -> String)? = nil) {} - func method1(fn1: () -> Int, fn2: Int = nil) {} + func method1(fn1: () -> Int, fn2: (() -> String)? = nil) -> SimpleEnum {} + func method1(fn1: () -> Int, fn2: Int = nil) -> SimpleEnum {} } func testMethod(value: MyStruct) { value.method1 { } #^METHOD_SAMELINE^# #^METHOD_NEWLINE^# -// METHOD_SAMELINE: Begin completions, 1 items +// METHOD_SAMELINE: Begin completions, 4 items // METHOD_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: (() -> String)?##() -> String#}[#(() -> String)?#]; +// METHOD_SAMELINE-DAG: Decl[InstanceMethod]/CurrNominal: .enumFunc()[#Void#]; +// METHOD_SAMELINE-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: [' ']+ {#SimpleEnum#}[#SimpleEnum#]; +// METHOD_SAMELINE-DAG: Keyword[self]/CurrNominal: .self[#SimpleEnum#]; // METHOD_SAMELINE: End completions // METHOD_NEWLINE: Begin completions @@ -59,6 +69,8 @@ struct TestStruct { init(fn1: () -> Int) {} init(fn1: () -> Int, fn2: () -> String) {} init(fn1: () -> Int, fn3: () -> String) {} + + func testStructMethod() {} } func testOverloadedInit() { @@ -67,9 +79,11 @@ func testOverloadedInit() { } #^INIT_OVERLOADED_SAMELINE^# #^INIT_OVERLOADED_NEWLINE^# -// INIT_OVERLOADED_SAMELINE: Begin completions, 2 items +// INIT_OVERLOADED_SAMELINE: Begin completions, 4 items // INIT_OVERLOADED_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; // INIT_OVERLOADED_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_OVERLOADED_SAMELINE-DAG: Decl[InstanceMethod]/CurrNominal: .testStructMethod()[#Void#]; +// INIT_OVERLOADED_SAMELINE-DAG: Keyword[self]/CurrNominal: .self[#TestStruct#]; // INIT_OVERLOADED_SAMELINE: End completions // INIT_OVERLOADED_NEWLINE: Begin completions @@ -84,6 +98,7 @@ func testOverloadedInit() { struct TestStruct2 { init(fn1: () -> Int, fn2: () -> String = {}, fn3: () -> String = {}) {} + func testStructMethod() {} } func testOptionalInit() { TestStruct2 { @@ -91,9 +106,11 @@ func testOptionalInit() { } #^INIT_OPTIONAL_SAMELINE^# #^INIT_OPTIONAL_NEWLINE^# -// INIT_OPTIONAL_SAMELINE: Begin completions, 2 items +// INIT_OPTIONAL_SAMELINE: Begin completions, 4 items // INIT_OPTIONAL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; // INIT_OPTIONAL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_OPTIONAL_SAMELINE-DAG: Decl[InstanceMethod]/CurrNominal: .testStructMethod()[#Void#]; +// INIT_OPTIONAL_SAMELINE-DAG: Keyword[self]/CurrNominal: .self[#TestStruct2#]; // INIT_OPTIONAL_SAMELINE: End completions // INIT_OPTIONAL_NEWLINE: Begin completions @@ -108,6 +125,7 @@ func testOptionalInit() { struct TestStruct3 { init(fn1: () -> Int, fn2: () -> String, fn3: () -> String) {} + func testStructMethod() {} } func testOptionalInit() { // missing 'fn2' and 'fn3'. @@ -150,7 +168,10 @@ func testOptionalInit() { } #^INIT_REQUIRED_SAMELINE_3^# #^INIT_REQUIRED_NEWLINE_3^# -// INIT_REQUIRED_SAMELINE_3-NOT: Begin completions +// INIT_REQUIRED_SAMELINE_3: Begin completions, 2 items +// INIT_REQUIRED_SAMELINE_3-DAG: Decl[InstanceMethod]/CurrNominal: .testStructMethod()[#Void#]; +// INIT_REQUIRED_SAMELINE_3-DAG: Keyword[self]/CurrNominal: .self[#TestStruct3#]; +// INIT_REQIORED_SAMELINE_3: End completions // INIT_REQUIRED_NEWLINE_3: Begin completions // INIT_REQUIRED_NEWLINE_3-NOT: name=fn2 diff --git a/validation-test/IDE/slow/rdar45511835.swift b/validation-test/IDE/slow/rdar45511835.swift index 6ba80a42e88ff..7028c898e5f01 100644 --- a/validation-test/IDE/slow/rdar45511835.swift +++ b/validation-test/IDE/slow/rdar45511835.swift @@ -1,6 +1,4 @@ // RUN: %target-swift-ide-test -code-completion -code-completion-token=COMPLETE -source-filename=%s | %FileCheck %s -// TODO: Postfix completion after trailing closure. -// XFAIL: * // REQUIRES: long_test From c0bf473cb6939ee56d57a3e1101fff7a962fefc9 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Thu, 16 Apr 2020 22:30:37 -0700 Subject: [PATCH 30/37] [CodeCompletion] Fix a crash regression --- lib/IDE/ExprContextAnalysis.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/IDE/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 86ed8e5c262b7..f34fa82665b14 100644 --- a/lib/IDE/ExprContextAnalysis.cpp +++ b/lib/IDE/ExprContextAnalysis.cpp @@ -418,21 +418,26 @@ static bool collectPossibleCalleesForApply( if (auto *DRE = dyn_cast(fnExpr)) { if (auto *decl = DRE->getDecl()) { - Type fnType = fnExpr->getType(); - if ((!fnType || fnType->hasError() || fnType->hasUnresolvedType()) && - decl->hasInterfaceType()) - fnType = decl->getInterfaceType(); - if (fnType) { - fnType = fnType->getWithoutSpecifierType(); - if (auto *funcTy = fnType->getAs()) + Type declTy = fnExpr->getType(); + if ((!declTy || declTy->hasError() || declTy->hasUnresolvedType()) && + decl->hasInterfaceType()) { + declTy = decl->getInterfaceType(); + declTy = decl->getInnermostDeclContext()->mapTypeIntoContext(declTy); + } + if (declTy) { + declTy = declTy->getWithoutSpecifierType(); + if (auto *funcTy = declTy->getAs()) candidates.emplace_back(funcTy, decl); } } } else if (auto *OSRE = dyn_cast(fnExpr)) { for (auto *decl : OSRE->getDecls()) { - if (decl->hasInterfaceType()) - if (auto *funcType = decl->getInterfaceType()->getAs()) + if (decl->hasInterfaceType()) { + auto declTy = decl->getInterfaceType(); + declTy = decl->getInnermostDeclContext()->mapTypeIntoContext(declTy); + if (auto *funcType = declTy->getAs()) candidates.emplace_back(funcType, decl); + } } } else if (auto *UDE = dyn_cast(fnExpr)) { collectPossibleCalleesByQualifiedLookup(DC, UDE->getBase(), UDE->getName(), From 0355a87eea9caf7cd3ec7a8a112bb54e17d33e81 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Sun, 19 Apr 2020 22:17:18 -0700 Subject: [PATCH 31/37] [Parse] Parse editor placeholder as a labeled trailng closure So that SourceKit can expand them to closure literals. --- lib/Parse/ParseExpr.cpp | 13 +++++++++++++ test/Parse/multiple_trailing_closures.swift | 2 ++ 2 files changed, 15 insertions(+) diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index e3abe56834e5e..0b3252d489c60 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -3159,6 +3159,11 @@ static bool isStartOfLabelledTrailingClosure(Parser &P) { P.consumeToken(); if (P.peekToken().is(tok::l_brace)) return true; + // Parse editor placeholder as trailing closure so SourceKit can expand it to + // closure literal. + if (P.peekToken().is(tok::identifier) && + Identifier::isEditorPlaceholder(P.peekToken().getText())) + return true; // Consider `label: ` that the user is trying to write a closure. if (P.peekToken().is(tok::code_complete) && !P.peekToken().isAtStartOfLine()) return true; @@ -3226,6 +3231,14 @@ Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, ParserResult closure; if (Tok.is(tok::l_brace)) { closure = parseExprClosure(); + } else if (Tok.is(tok::identifier)) { + // Parse editor placeholder as a closure literal. + assert(Identifier::isEditorPlaceholder(Tok.getText())); + SyntaxParsingContext IdCtx(SyntaxContext, + SyntaxKind::EditorPlaceholderExpr); + Identifier name = Context.getIdentifier(Tok.getText()); + closure = makeParserResult(parseExprEditorPlaceholder(Tok, name)); + consumeToken(tok::identifier); } else if (Tok.is(tok::code_complete)) { assert(!Tok.isAtStartOfLine() && "isStartOfLabelledTrailingClosure() should return false"); diff --git a/test/Parse/multiple_trailing_closures.swift b/test/Parse/multiple_trailing_closures.swift index 33cec90d4c79c..0fb3976f9d493 100644 --- a/test/Parse/multiple_trailing_closures.swift +++ b/test/Parse/multiple_trailing_closures.swift @@ -84,6 +84,8 @@ func test_multiple_trailing_syntax_without_labels() { fn {} _: {} // expected-error {{extra argument in call}} + fn {} g: <#T##() -> Void#> // expected-error {{editor placeholder in source file}} + func multiple(_: () -> Void, _: () -> Void) {} multiple {} _: { } From 58859f56992e2ba891a136c0280559a8bfe64c5c Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Sun, 19 Apr 2020 22:41:35 -0700 Subject: [PATCH 32/37] [SourceKit/CodeFormat] Update indentation for braceless multiple trailing closures. --- lib/IDE/Formatting.cpp | 55 +++- .../trailing-closure-multiple.swift | 7 + .../multiple-trailing-closures.swift | 240 +++++++++++++----- 3 files changed, 227 insertions(+), 75 deletions(-) create mode 100644 test/SourceKit/CodeFormat/indent-trailing/trailing-closure-multiple.swift diff --git a/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index 3093dc910e714..f86117f64ac13 100644 --- a/lib/IDE/Formatting.cpp +++ b/lib/IDE/Formatting.cpp @@ -388,9 +388,15 @@ class RangeWalker: protected ASTWalker { public: explicit RangeWalker(SourceManager &SM) : SM(SM) {} + /// Called for every range bounded by a pair of parens, braces, square + /// brackets, or angle brackets. + /// /// \returns true to continue walking. virtual bool handleRange(SourceLoc L, SourceLoc R, SourceLoc ContextLoc) = 0; + /// Called for ranges that have a separate ContextLoc but no bounding tokens. + virtual void handleImplicitRange(SourceRange Range, SourceLoc ContextLoc) = 0; + private: bool handleBraces(SourceLoc L, SourceLoc R, SourceLoc ContextLoc) { L = getLocIfKind(SM, L, tok::l_brace); @@ -634,12 +640,6 @@ class RangeWalker: protected ASTWalker { Arg = cast(E)->getIndex(); } - auto handleTrailingClosure = [&](Expr *arg) -> bool { - if (auto CE = findTrailingClosureFromArgument(arg)) - return handleBraceStmt(CE->getBody(), ContextLoc); - return true; - }; - if (auto *PE = dyn_cast_or_null(Arg)) { if (isa(E)) { if (!handleSquares(PE->getLParenLoc(), PE->getRParenLoc(), ContextLoc)) @@ -649,8 +649,9 @@ class RangeWalker: protected ASTWalker { return Stop; } if (PE->hasTrailingClosure()) { - if (!handleTrailingClosure(PE->getSubExpr())) - return Stop; + if (auto CE = findTrailingClosureFromArgument(PE->getSubExpr())) + if (!handleBraceStmt(CE->getBody(), ContextLoc)) + return Stop; } } else if (auto *TE = dyn_cast_or_null(Arg)) { if (isa(E)) { @@ -660,9 +661,10 @@ class RangeWalker: protected ASTWalker { if (!handleParens(TE->getLParenLoc(), TE->getRParenLoc(), ContextLoc)) return Stop; } - for (auto closure : TE->getTrailingElements()) { - if (!handleTrailingClosure(closure)) - return Stop; + if (TE->hasAnyTrailingClosures()) { + SourceRange Range(TE->getTrailingElements().front()->getStartLoc(), + TE->getEndLoc()); + handleImplicitRange(Range, ContextLoc); } } } @@ -723,6 +725,16 @@ class OutdentChecker: protected RangeWalker { assert(CheckRange.isValid()); } + void handleImplicitRange(SourceRange Range, SourceLoc ContextLoc) override { + assert(Range.isValid() && ContextLoc.isValid()); + + // Ignore ranges outside of the open/closed check range. + if (!isInCheckRange(Range.Start, Range.End)) + return; + + propagateContextLocs(ContextLoc, Range.Start, Range.End); + } + bool handleRange(SourceLoc L, SourceLoc R, SourceLoc ContextLoc) override { assert(L.isValid() && ContextLoc.isValid()); @@ -2343,9 +2355,24 @@ class FormatWalker : public ASTWalker { } else if (auto *TE = dyn_cast_or_null(Arg)) { if (auto Ctx = getIndentContextFrom(TE, ContextLoc)) return Ctx; - for (auto arg : TE->getTrailingElements()) { - if (auto Ctx = getIndentContextFromTrailingClosure(arg)) - return Ctx; + + if (TE->hasAnyTrailingClosures()) { + Expr *Unlabeled = TE->getTrailingElements().front(); + SourceRange ClosuresRange(Unlabeled->getStartLoc(), TE->getEndLoc()); + + if (overlapsTarget(ClosuresRange) || TrailingTarget) { + SourceRange ContextToEnd(ContextLoc, ClosuresRange.End); + ContextLoc = CtxOverride.propagateContext(SM, ContextLoc, + IndentContext::LineStart, + ClosuresRange.Start, + SourceLoc()); + if (!TrailingTarget) { + return IndentContext { + ContextLoc, + !OutdentChecker::hasOutdent(SM, ContextToEnd, E) + }; + } + } } } } diff --git a/test/SourceKit/CodeFormat/indent-trailing/trailing-closure-multiple.swift b/test/SourceKit/CodeFormat/indent-trailing/trailing-closure-multiple.swift new file mode 100644 index 0000000000000..5163d25dd1e3f --- /dev/null +++ b/test/SourceKit/CodeFormat/indent-trailing/trailing-closure-multiple.swift @@ -0,0 +1,7 @@ +func foo() { + bar + .fooooooooooo(first: 3, + second)[x: 10] {} other: { + +// RUN: %sourcekitd-test -req=format -line=5 -length=1 %s | %FileCheck --strict-whitespace %s +// CHECK: key.sourcetext: " " diff --git a/test/swift-indent/multiple-trailing-closures.swift b/test/swift-indent/multiple-trailing-closures.swift index 4e04fd834e8cc..908e1864b2874 100644 --- a/test/swift-indent/multiple-trailing-closures.swift +++ b/test/swift-indent/multiple-trailing-closures.swift @@ -1,109 +1,227 @@ // RUN: %swift-indent %s >%t.response // RUN: diff -u %s %t.response -// XFAIL: * - foo(c: 12, d: 34) { - a: { print("foo") } + print("foo") } -foo { - a: { print("foo") } +foo(c: 12, d: 34) +{ + print("foo") } -foo { - a: { print("foo") } - b + +someCall(c: 12, d: 34) { + print("foo") +} e: { + print("foo") +} f: { + print("bar") } -foo (c: 12, d: 34) { - a: { print("foo") } - b: +someCall(c: 12, d: 34) { + print("foo") +} +e: { + print("foo") +} +f: { + print("bar") } -// Invalid, but we should still indent correctly. -foo { - a: { - print("foo") - } - b: bar(a: 1, - b: 2) { - print("bar") - } +someCall(c: 12, d: 34) +{ + print("foo") +} +e: +{ + print("foo") +} +f: +{ + print("bar") } -foo { - a: { - print("foo") - } - b: { +someCall(c: 12, d: 34) { print("foo") } + e: { print("foo") } + f: { print("bar") } -} -foo(c: 12, d: 34) { - a: { - print("foo") +someCall(c: 12, + d: 34) { print("foo") } + e: { print("bar") } + .other { print("foo") } + a: { print("foo") } + .next() { + print("bar") } - b: { + +base + .someCall(c: 12, + d: 34) { print("foo") } + e: { print("bar") } + .other { print("foo") } + a: { print("foo") } + .next() { print("bar") } + +someCall( + c: 12, + d: 34 +) { print("foo") } +e: { print("bar") } + +base + .someCall( + x: 10, + y: 10 + ) { $0 } + next: { $0 } + +base + .foo( + x: 10, + y: 10) { } next: { + + } + other: {} + +base + .someCall(x: 120, + y: 3030) { print("foo") } next: + { + print("foo") + } + other: {} + + +someCall { + print("foo") +} e: { + print("foo") +} f: { + print("bar") } -foo(c: 12, d: 34) { a: { +someCall { print("foo") -} b: { +} +e: { + print("foo") +} +f: { print("bar") -}} +} -foo(c: 12, d: 34) { a: {print("foo")} - b: {print("bar")} } +someCall +{ + print("foo") +} +e: +{ + print("foo") +} +f: +{ + print("bar") +} -foo(c: 12, d: 34) { a: {print("foo")} - /*comment*/b: { +someCall { print("foo") } + e: { print("foo") } + f: { print("bar") - }} + } -foobar(c: 12, - d: 34) { - a: { +doSomethingWith(someCall { print("foo") } + e: { print("foo") } + f: { + print("bar") + }) + +doSomethingWith(someCall { + print("foo") +} +e: { print("foo") } +f: { + print("bar") +}) + +func containIncomplete1() { + someCall { print("foo") } - b: { - print("bar") - } + e } -foo(c: 12, d: 34) -{ - a: { +func containIncomplete2() { + someCall { print("foo") } - b: { - print("bar") + e: +} + +func containIncomplete3() { + someCall + { + print("foo") } + e } -foobar[c: 12, - d: 34] { - a: { +func containIncomplete4() { + someCall + { print("foo") } + e: +} + +func containIncomplete5() { + someCall { print("foo") } + e +} + +func containIncomplete6() { + someCall { print("foo") } + e: +} + +someCall(c: 12, d: 34) {{ + print("foo") +}()} b: {{ + print("bar") +}()} + +someCall(c: 12, d: 34) { print("foo") } + /*comment*/e: { print("bar") } + /*comment*/f: { + print("hi") + } + +someCall[c: 12, + d: 34] { print("foo") } b: { print("bar") } + +someSub[c: 12, + d: 34] { + print("foo") +} b: { + print("bar") } -foo(c: 12, d: 34) -{ - a: { - print("foo") - } +someSub[c: 12, + d: 34] { + print("foo") +} b: { + print("bar") +} + +someCall(c: 12, d: 34) { print("foo") } // comment b: { print("bar") } - -func invalidCode() { - print("blah") -} \ No newline at end of file From 78b7bce3a08c69ceab4d8bf1a8be0bc774b4a95c Mon Sep 17 00:00:00 2001 From: Nathan Hawes Date: Mon, 20 Apr 2020 19:18:25 -0700 Subject: [PATCH 33/37] [IDE][Refactoring] Update syntactic rename to support braceless multiple trailing closures. --- include/swift/IDE/Utils.h | 12 +-- lib/IDE/Refactoring.cpp | 53 ++++++++++++- lib/IDE/SwiftSourceDocInfo.cpp | 75 +++++++++++-------- lib/Migrator/APIDiffMigratorPass.cpp | 11 ++- .../callsites/defaults.swift.expected | 7 +- .../callsites/trailing.swift.expected | 7 +- .../callsites/trailing_only.swift.expected | 7 +- ...trailing-closures-defaulted.swift.expected | 9 +++ .../multiple-trailing-closures.swift.expected | 8 ++ .../Outputs/callsites/defaults.swift.expected | 7 +- .../Outputs/callsites/mixed.swift.expected | 7 +- .../Outputs/callsites/trailing.swift.expected | 7 +- .../callsites/trailing_only.swift.expected | 7 +- .../Outputs/callsites/varargs.swift.expected | 7 +- .../Outputs/callsites/varargs2.swift.expected | 7 +- .../SyntacticRename/callsites.swift | 7 +- ...multiple-trailing-closures-defaulted.swift | 12 +++ .../multiple-trailing-closures.swift | 11 +++ 18 files changed, 178 insertions(+), 83 deletions(-) create mode 100644 test/refactoring/SyntacticRename/FindRangeOutputs/multiple-trailing-closures-defaulted.swift.expected create mode 100644 test/refactoring/SyntacticRename/FindRangeOutputs/multiple-trailing-closures.swift.expected create mode 100644 test/refactoring/SyntacticRename/multiple-trailing-closures-defaulted.swift create mode 100644 test/refactoring/SyntacticRename/multiple-trailing-closures.swift diff --git a/include/swift/IDE/Utils.h b/include/swift/IDE/Utils.h index 87a2b71978e60..6fa05dececa47 100644 --- a/include/swift/IDE/Utils.h +++ b/include/swift/IDE/Utils.h @@ -222,6 +222,7 @@ struct ResolvedLoc { ASTWalker::ParentTy Node; CharSourceRange Range; std::vector LabelRanges; + Optional FirstTrailingLabel; LabelRangeType LabelType; bool IsActive; bool IsInSelector; @@ -268,7 +269,8 @@ class NameMatcher: public ASTWalker { bool tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc); bool tryResolve(ASTWalker::ParentTy Node, DeclNameLoc NameLoc, Expr *Arg); bool tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc, LabelRangeType RangeType, - ArrayRef LabelLocs); + ArrayRef LabelLocs, + Optional FirstTrailingLabel); bool handleCustomAttrs(Decl *D); Expr *getApplicableArgFor(Expr* E); @@ -579,10 +581,10 @@ struct CallArgInfo { std::vector getCallArgInfo(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind); -// Get the ranges of argument labels from an Arg, either tuple or paren. -// This includes empty ranges for any unlabelled arguments, and excludes -// trailing closures. -std::vector +// Get the ranges of argument labels from an Arg, either tuple or paren, and +// the index of the first trailing closure argument, if any. This includes empty +// ranges for any unlabelled arguments, including the first trailing closure. +std::pair, Optional> getCallArgLabelRanges(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind); /// Whether a decl is defined from clang source. diff --git a/lib/IDE/Refactoring.cpp b/lib/IDE/Refactoring.cpp index 4cdf2248c1da0..469fb6bef7fa7 100644 --- a/lib/IDE/Refactoring.cpp +++ b/lib/IDE/Refactoring.cpp @@ -108,10 +108,12 @@ class Renamer { /// Adds replacements to rename the given label ranges /// \return true if the label ranges do not match the old name bool renameLabels(ArrayRef LabelRanges, + Optional FirstTrailingLabel, LabelRangeType RangeType, bool isCallSite) { if (isCallSite) - return renameLabelsLenient(LabelRanges, RangeType); + return renameLabelsLenient(LabelRanges, FirstTrailingLabel, RangeType); + assert(!FirstTrailingLabel); ArrayRef OldLabels = Old.args(); if (OldLabels.size() != LabelRanges.size()) @@ -261,10 +263,56 @@ class Renamer { } bool renameLabelsLenient(ArrayRef LabelRanges, + Optional FirstTrailingLabel, LabelRangeType RangeType) { ArrayRef OldNames = Old.args(); + // First, match trailing closure arguments in reverse + if (FirstTrailingLabel) { + auto TrailingLabels = LabelRanges.drop_front(*FirstTrailingLabel); + LabelRanges = LabelRanges.take_front(*FirstTrailingLabel); + + for (auto LabelIndex: llvm::reverse(indices(TrailingLabels))) { + CharSourceRange Label = TrailingLabels[LabelIndex]; + + if (Label.getByteLength()) { + if (OldNames.empty()) + return true; + + while (!labelRangeMatches(Label, LabelRangeType::Selector, + OldNames.back())) { + if ((OldNames = OldNames.drop_back()).empty()) + return true; + } + splitAndRenameLabel(Label, LabelRangeType::Selector, + OldNames.size() - 1); + OldNames = OldNames.drop_back(); + continue; + } + + // empty labelled trailing closure label + if (LabelIndex) { + if (OldNames.empty()) + return true; + + while (!OldNames.back().empty()) { + if ((OldNames = OldNames.drop_back()).empty()) + return true; + } + splitAndRenameLabel(Label, LabelRangeType::Selector, + OldNames.size() - 1); + OldNames = OldNames.drop_back(); + continue; + } + + // unlabelled trailing closure label + OldNames = OldNames.drop_back(); + continue; + } + } + + // Next, match the non-trailing arguments. size_t NameIndex = 0; for (CharSourceRange Label : LabelRanges) { @@ -399,7 +447,8 @@ class Renamer { (Config.Usage != NameUsage::Reference || IsSubscript) && Resolved.LabelType == LabelRangeType::CallArg; - if (renameLabels(Resolved.LabelRanges, Resolved.LabelType, isCallSite)) + if (renameLabels(Resolved.LabelRanges, Resolved.FirstTrailingLabel, + Resolved.LabelType, isCallSite)) return Config.Usage == NameUsage::Unknown ? RegionType::Unmatched : RegionType::Mismatch; } diff --git a/lib/IDE/SwiftSourceDocInfo.cpp b/lib/IDE/SwiftSourceDocInfo.cpp index f34d63e445fd5..f8211c437795a 100644 --- a/lib/IDE/SwiftSourceDocInfo.cpp +++ b/lib/IDE/SwiftSourceDocInfo.cpp @@ -102,7 +102,7 @@ std::vector NameMatcher::resolve(ArrayRef Locs, Arra // handle any unresolved locs past the end of the last AST node or comment std::vector Remaining(Locs.size() - ResolvedLocs.size(), { - ASTWalker::ParentTy(), CharSourceRange(), {}, LabelRangeType::None, + ASTWalker::ParentTy(), CharSourceRange(), {}, None, LabelRangeType::None, /*isActice*/true, /*isInSelector*/false}); ResolvedLocs.insert(ResolvedLocs.end(), Remaining.begin(), Remaining.end()); @@ -238,15 +238,15 @@ bool NameMatcher::walkToDeclPre(Decl *D) { LabelRanges = getLabelRanges(ParamList, getSourceMgr()); } tryResolve(ASTWalker::ParentTy(D), D->getLoc(), LabelRangeType::Param, - LabelRanges); + LabelRanges, None); } else if (SubscriptDecl *SD = dyn_cast(D)) { tryResolve(ASTWalker::ParentTy(D), D->getLoc(), LabelRangeType::NoncollapsibleParam, - getLabelRanges(SD->getIndices(), getSourceMgr())); + getLabelRanges(SD->getIndices(), getSourceMgr()), None); } else if (EnumElementDecl *EED = dyn_cast(D)) { if (auto *ParamList = EED->getParameterList()) { auto LabelRanges = getEnumParamListInfo(getSourceMgr(), ParamList); tryResolve(ASTWalker::ParentTy(D), D->getLoc(), LabelRangeType::CallArg, - LabelRanges); + LabelRanges, None); } else { tryResolve(ASTWalker::ParentTy(D), D->getLoc()); } @@ -362,7 +362,8 @@ std::pair NameMatcher::walkToExprPre(Expr *E) { auto Labels = getCallArgLabelRanges(getSourceMgr(), SubExpr->getIndex(), LabelRangeEndAt::BeforeElemStart); - tryResolve(ASTWalker::ParentTy(E), E->getLoc(), LabelRangeType::CallArg, Labels); + tryResolve(ASTWalker::ParentTy(E), E->getLoc(), LabelRangeType::CallArg, + Labels.first, Labels.second); if (isDone()) break; if (!SubExpr->getIndex()->walk(*this)) @@ -379,7 +380,7 @@ std::pair NameMatcher::walkToExprPre(Expr *E) { auto Labels = getCallArgLabelRanges(getSourceMgr(), P, LabelRangeEndAt::BeforeElemStart); tryResolve(ASTWalker::ParentTy(E), P->getLParenLoc(), - LabelRangeType::CallArg, Labels); + LabelRangeType::CallArg, Labels.first, Labels.second); break; } case ExprKind::Tuple: { @@ -388,7 +389,7 @@ std::pair NameMatcher::walkToExprPre(Expr *E) { auto Labels = getCallArgLabelRanges(getSourceMgr(), T, LabelRangeEndAt::BeforeElemStart); tryResolve(ASTWalker::ParentTy(E), T->getLParenLoc(), - LabelRangeType::CallArg, Labels); + LabelRangeType::CallArg, Labels.first, Labels.second); if (isDone()) break; @@ -485,8 +486,10 @@ bool NameMatcher::walkToTypeReprPre(TypeRepr *T) { // If we're walking a CustomAttr's type we may have an associated call // argument to resolve with from its semantic initializer. if (CustomAttrArg.hasValue() && CustomAttrArg->Loc == T->getLoc()) { + auto Labels = getCallArgLabelRanges(getSourceMgr(), CustomAttrArg->Item, + LabelRangeEndAt::BeforeElemStart); tryResolve(ASTWalker::ParentTy(T), T->getLoc(), LabelRangeType::CallArg, - getCallArgLabelRanges(getSourceMgr(), CustomAttrArg->Item, LabelRangeEndAt::BeforeElemStart)); + Labels.first, Labels.second); } else { tryResolve(ASTWalker::ParentTy(T), T->getLoc()); } @@ -527,7 +530,7 @@ void NameMatcher::skipLocsBefore(SourceLoc Start) { if (!checkComments()) { LocsToResolve.pop_back(); ResolvedLocs.push_back({ASTWalker::ParentTy(), CharSourceRange(), {}, - LabelRangeType::None, isActive(), isInSelector()}); + None, LabelRangeType::None, isActive(), isInSelector()}); } } } @@ -592,7 +595,7 @@ bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, DeclNameLoc NameLoc, if (NameLoc.isCompound()) { auto Labels = getSelectorLabelRanges(getSourceMgr(), NameLoc); bool Resolved = tryResolve(Node, NameLoc.getBaseNameLoc(), - LabelRangeType::Selector, Labels); + LabelRangeType::Selector, Labels, None); if (!isDone()) { for (auto Label: Labels) { if (tryResolve(Node, Label.getStart())) { @@ -606,10 +609,12 @@ bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, DeclNameLoc NameLoc, } if (LocsToResolve.back().ResolveArgLocs) { - if (Arg) + if (Arg) { + auto Labels = getCallArgLabelRanges(getSourceMgr(), Arg, + LabelRangeEndAt::BeforeElemStart); return tryResolve(Node, NameLoc.getBaseNameLoc(), LabelRangeType::CallArg, - getCallArgLabelRanges(getSourceMgr(), Arg, - LabelRangeEndAt::BeforeElemStart)); + Labels.first, Labels.second); + } } return tryResolve(Node, NameLoc.getBaseNameLoc()); @@ -617,12 +622,13 @@ bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, DeclNameLoc NameLoc, bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc) { assert(!isDone()); - return tryResolve(Node, NameLoc, LabelRangeType::None, None); + return tryResolve(Node, NameLoc, LabelRangeType::None, None, None); } bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc, LabelRangeType RangeType, - ArrayRef LabelRanges) { + ArrayRef LabelRanges, + Optional FirstTrailingLabel) { skipLocsBefore(NameLoc); if (isDone()) return false; @@ -634,8 +640,8 @@ bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc, if (Range.isValid()) { if (NameLoc == Next.Loc) { LocsToResolve.pop_back(); - ResolvedLocs.push_back({Node, Range, LabelRanges, RangeType, - isActive(), isInSelector()}); + ResolvedLocs.push_back({Node, Range, LabelRanges, FirstTrailingLabel, + RangeType, isActive(), isInSelector()}); if (isDone()) return true; WasResolved = true; @@ -649,7 +655,7 @@ bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc, Range.getByteLength() - 1); if (NewRange.getStart() == Next.Loc) { LocsToResolve.pop_back(); - ResolvedLocs.push_back({Node, NewRange, {}, LabelRangeType::None, + ResolvedLocs.push_back({Node, NewRange, {}, None, LabelRangeType::None, isActive(), isInSelector()}); WasResolved = true; } @@ -881,6 +887,7 @@ std::vector swift::ide:: getCallArgInfo(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind) { std::vector InfoVec; if (auto *TE = dyn_cast(Arg)) { + auto FirstTrailing = TE->getUnlabeledTrailingClosureIndexOfPackedArgument(); for (size_t ElemIndex: range(TE->getNumElements())) { Expr *Elem = TE->getElement(ElemIndex); if (isa(Elem)) @@ -889,14 +896,14 @@ getCallArgInfo(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind) { SourceLoc LabelStart(Elem->getStartLoc()); SourceLoc LabelEnd(LabelStart); - auto NameIdentifier = TE->getElementName(ElemIndex); - if (!NameIdentifier.empty()) { - LabelStart = TE->getElementNameLoc(ElemIndex); - if (EndKind == LabelRangeEndAt::LabelNameOnly) - LabelEnd = LabelStart.getAdvancedLoc(NameIdentifier.getLength()); + bool IsTrailingClosure = FirstTrailing && ElemIndex >= *FirstTrailing; + SourceLoc NameLoc = TE->getElementNameLoc(ElemIndex); + if (NameLoc.isValid()) { + LabelStart = NameLoc; + if (EndKind == LabelRangeEndAt::LabelNameOnly || IsTrailingClosure) { + LabelEnd = Lexer::getLocForEndOfToken(SM, NameLoc); + } } - bool IsTrailingClosure = TE->hasTrailingClosure() && - ElemIndex == TE->getNumElements() - 1; InfoVec.push_back({getSingleNonImplicitChild(Elem), CharSourceRange(SM, LabelStart, LabelEnd), IsTrailingClosure}); } @@ -911,17 +918,19 @@ getCallArgInfo(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind) { return InfoVec; } -std::vector swift::ide:: +std::pair, Optional> swift::ide:: getCallArgLabelRanges(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind) { std::vector Ranges; auto InfoVec = getCallArgInfo(SM, Arg, EndKind); - auto EndWithoutTrailing = std::remove_if(InfoVec.begin(), InfoVec.end(), - [](CallArgInfo &Info) { - return Info.IsTrailingClosure; - }); - std::transform(InfoVec.begin(), EndWithoutTrailing, - std::back_inserter(Ranges), + Optional FirstTrailing; + auto I = std::find_if(InfoVec.begin(), InfoVec.end(), [](CallArgInfo &Info) { + return Info.IsTrailingClosure; + }); + if (I != InfoVec.end()) + FirstTrailing = std::distance(InfoVec.begin(), I); + + std::transform(InfoVec.begin(), InfoVec.end(), std::back_inserter(Ranges), [](CallArgInfo &Info) { return Info.LabelRange; }); - return Ranges; + return {Ranges, FirstTrailing}; } diff --git a/lib/Migrator/APIDiffMigratorPass.cpp b/lib/Migrator/APIDiffMigratorPass.cpp index 35a30946f83d1..f99b28e07dc37 100644 --- a/lib/Migrator/APIDiffMigratorPass.cpp +++ b/lib/Migrator/APIDiffMigratorPass.cpp @@ -505,11 +505,16 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { auto Ranges = getCallArgLabelRanges(SM, Arg, LabelRangeEndAt::LabelNameOnly); llvm::SmallVector ToRemoveIndices; - for (unsigned I = 0; I < Ranges.size(); I ++) { + for (unsigned I = 0; I < Ranges.first.size(); I ++) { if (std::any_of(IgnoreArgIndex.begin(), IgnoreArgIndex.end(), [I](unsigned Ig) { return Ig == I; })) continue; - auto LR = Ranges[I]; + + // Ignore the first trailing closure label + if (Ranges.second && I == Ranges.second) + continue; + + auto LR = Ranges.first[I]; if (Idx < NewName.argSize()) { auto Label = NewName.args()[Idx++]; @@ -528,7 +533,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker { auto Ranges = getCallArgLabelRanges(SM, Arg, LabelRangeEndAt::BeforeElemStart); for (auto I : ToRemoveIndices) { - Editor.remove(Ranges[I]); + Editor.remove(Ranges.first[I]); } } } diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/defaults.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/defaults.swift.expected index c2b6ef77b1453..b9a844c5bf41b 100644 --- a/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/defaults.swift.expected +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/defaults.swift.expected @@ -21,12 +21,11 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {} // valid /*trailing:call*/withTrailingClosure(x: 2, y: { return 1}) /*trailing:call*/withTrailingClosure(x: 2) { return 1} - -// false positives -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} /*trailing:call*/withTrailingClosure(x: 2) { return 1} +// invalid +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} func /*trailing-only:def*/trailingOnly(a: () -> ()) {} /*trailing-only:call*/trailingOnly(a: {}) diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/trailing.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/trailing.swift.expected index 97a23f001360f..2f8c1ec6e791d 100644 --- a/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/trailing.swift.expected +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/trailing.swift.expected @@ -21,12 +21,11 @@ func /*trailing:def*/withTrailingClosure(xwithTrailingClosure(x: 2, y: { return 1}) /*trailing:call*/withTrailingClosure(x: 2) { return 1} - -// false positives -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} /*trailing:call*/withTrailingClosure(x: 2) { return 1} +// invalid +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} func /*trailing-only:def*/trailingOnly(a: () -> ()) {} /*trailing-only:call*/trailingOnly(a: {}) diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/trailing_only.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/trailing_only.swift.expected index d027c004e2d9c..a8cfead01a35b 100644 --- a/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/trailing_only.swift.expected +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/callsites/trailing_only.swift.expected @@ -21,12 +21,11 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {} // valid /*trailing:call*/withTrailingClosure(x: 2, y: { return 1}) /*trailing:call*/withTrailingClosure(x: 2) { return 1} - -// false positives -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} /*trailing:call*/withTrailingClosure(x: 2) { return 1} +// invalid +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} func /*trailing-only:def*/trailingOnly(a: () -> ()) {} /*trailing-only:call*/trailingOnly(a: {}) diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/multiple-trailing-closures-defaulted.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/multiple-trailing-closures-defaulted.swift.expected new file mode 100644 index 0000000000000..eadc804207f3e --- /dev/null +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/multiple-trailing-closures-defaulted.swift.expected @@ -0,0 +1,9 @@ +func /*test:def*/test(x: () -> () = {}, _ xx: () -> () = {}, y: () -> () = {}, z: ()->() = {}) {} + +/*test:call*/test(x: {}, {}) {} +/*test:call*/test(x: {}) {} z: {} +/*test:call*/test(x: {}, {}) {} +/*test:call*/test {} y: {} +/*test:call*/test(y: {}) {} + + diff --git a/test/refactoring/SyntacticRename/FindRangeOutputs/multiple-trailing-closures.swift.expected b/test/refactoring/SyntacticRename/FindRangeOutputs/multiple-trailing-closures.swift.expected new file mode 100644 index 0000000000000..cab10b18e0902 --- /dev/null +++ b/test/refactoring/SyntacticRename/FindRangeOutputs/multiple-trailing-closures.swift.expected @@ -0,0 +1,8 @@ +func /*test:def*/test(x: () -> (), _ xx: () -> (), y: () -> (), z: ()->()) {} + +/*test:call*/test(x: {}, {}, y: {}) {} +/*test:call*/test(x: {}, {}) {} z: {} +/*test:call*/test(x: {}) {} y: {} z: {} +/*test:call*/test {} _: {} y: {} z: {} + + diff --git a/test/refactoring/SyntacticRename/Outputs/callsites/defaults.swift.expected b/test/refactoring/SyntacticRename/Outputs/callsites/defaults.swift.expected index 80b9f74019da8..e11d90978b5a0 100644 --- a/test/refactoring/SyntacticRename/Outputs/callsites/defaults.swift.expected +++ b/test/refactoring/SyntacticRename/Outputs/callsites/defaults.swift.expected @@ -21,12 +21,11 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {} // valid /*trailing:call*/withTrailingClosure(x: 2, y: { return 1}) /*trailing:call*/withTrailingClosure(x: 2) { return 1} - -// false positives -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} /*trailing:call*/withTrailingClosure(x: 2) { return 1} +// invalid +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} func /*trailing-only:def*/trailingOnly(a: () -> ()) {} /*trailing-only:call*/trailingOnly(a: {}) diff --git a/test/refactoring/SyntacticRename/Outputs/callsites/mixed.swift.expected b/test/refactoring/SyntacticRename/Outputs/callsites/mixed.swift.expected index 717bea356c83a..cd31731d7d04b 100644 --- a/test/refactoring/SyntacticRename/Outputs/callsites/mixed.swift.expected +++ b/test/refactoring/SyntacticRename/Outputs/callsites/mixed.swift.expected @@ -21,12 +21,11 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {} // valid /*trailing:call*/withTrailingClosure(x: 2, y: { return 1}) /*trailing:call*/withTrailingClosure(x: 2) { return 1} - -// false positives -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} /*trailing:call*/withTrailingClosure(x: 2) { return 1} +// invalid +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} func /*trailing-only:def*/trailingOnly(a: () -> ()) {} /*trailing-only:call*/trailingOnly(a: {}) diff --git a/test/refactoring/SyntacticRename/Outputs/callsites/trailing.swift.expected b/test/refactoring/SyntacticRename/Outputs/callsites/trailing.swift.expected index 567c8817b2510..5f51cab687146 100644 --- a/test/refactoring/SyntacticRename/Outputs/callsites/trailing.swift.expected +++ b/test/refactoring/SyntacticRename/Outputs/callsites/trailing.swift.expected @@ -21,12 +21,11 @@ func /*trailing:def*/betterName(a: Int, b: () -> Int) {} // valid /*trailing:call*/betterName(a: 2, b: { return 1}) /*trailing:call*/betterName(a: 2) { return 1} - -// false positives -/*trailing:call*/betterName(a: 1, b: 2) { return 1} -/*trailing:call*/betterName(a: 1, b: 2) { return 1} /*trailing:call*/betterName(a: 2) { return 1} +// invalid +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} func /*trailing-only:def*/trailingOnly(a: () -> ()) {} /*trailing-only:call*/trailingOnly(a: {}) diff --git a/test/refactoring/SyntacticRename/Outputs/callsites/trailing_only.swift.expected b/test/refactoring/SyntacticRename/Outputs/callsites/trailing_only.swift.expected index bb9eda3995593..f26f802687191 100644 --- a/test/refactoring/SyntacticRename/Outputs/callsites/trailing_only.swift.expected +++ b/test/refactoring/SyntacticRename/Outputs/callsites/trailing_only.swift.expected @@ -21,12 +21,11 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {} // valid /*trailing:call*/withTrailingClosure(x: 2, y: { return 1}) /*trailing:call*/withTrailingClosure(x: 2) { return 1} - -// false positives -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} /*trailing:call*/withTrailingClosure(x: 2) { return 1} +// invalid +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} func /*trailing-only:def*/betterName(b: () -> ()) {} /*trailing-only:call*/betterName(b: {}) diff --git a/test/refactoring/SyntacticRename/Outputs/callsites/varargs.swift.expected b/test/refactoring/SyntacticRename/Outputs/callsites/varargs.swift.expected index 1a74dab20cf42..ddc395b8472c7 100644 --- a/test/refactoring/SyntacticRename/Outputs/callsites/varargs.swift.expected +++ b/test/refactoring/SyntacticRename/Outputs/callsites/varargs.swift.expected @@ -21,12 +21,11 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {} // valid /*trailing:call*/withTrailingClosure(x: 2, y: { return 1}) /*trailing:call*/withTrailingClosure(x: 2) { return 1} - -// false positives -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} /*trailing:call*/withTrailingClosure(x: 2) { return 1} +// invalid +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} func /*trailing-only:def*/trailingOnly(a: () -> ()) {} /*trailing-only:call*/trailingOnly(a: {}) diff --git a/test/refactoring/SyntacticRename/Outputs/callsites/varargs2.swift.expected b/test/refactoring/SyntacticRename/Outputs/callsites/varargs2.swift.expected index 2f587488451ad..f195d768a91c8 100644 --- a/test/refactoring/SyntacticRename/Outputs/callsites/varargs2.swift.expected +++ b/test/refactoring/SyntacticRename/Outputs/callsites/varargs2.swift.expected @@ -21,12 +21,11 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {} // valid /*trailing:call*/withTrailingClosure(x: 2, y: { return 1}) /*trailing:call*/withTrailingClosure(x: 2) { return 1} - -// false positives -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} /*trailing:call*/withTrailingClosure(x: 2) { return 1} +// invalid +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} func /*trailing-only:def*/trailingOnly(a: () -> ()) {} /*trailing-only:call*/trailingOnly(a: {}) diff --git a/test/refactoring/SyntacticRename/callsites.swift b/test/refactoring/SyntacticRename/callsites.swift index bae528bf2def8..ba44be8ab6bd4 100644 --- a/test/refactoring/SyntacticRename/callsites.swift +++ b/test/refactoring/SyntacticRename/callsites.swift @@ -21,12 +21,11 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {} // valid /*trailing:call*/withTrailingClosure(x: 2, y: { return 1}) /*trailing:call*/withTrailingClosure(x: 2) { return 1} - -// false positives -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} -/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} /*trailing:call*/withTrailingClosure(x: 2) { return 1} +// invalid +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} +/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1} func /*trailing-only:def*/trailingOnly(a: () -> ()) {} /*trailing-only:call*/trailingOnly(a: {}) diff --git a/test/refactoring/SyntacticRename/multiple-trailing-closures-defaulted.swift b/test/refactoring/SyntacticRename/multiple-trailing-closures-defaulted.swift new file mode 100644 index 0000000000000..4b48229e580b9 --- /dev/null +++ b/test/refactoring/SyntacticRename/multiple-trailing-closures-defaulted.swift @@ -0,0 +1,12 @@ +func /*test:def*/test(x: () -> () = {}, _ xx: () -> () = {}, y: () -> () = {}, z: ()->() = {}) {} + +/*test:call*/test(x: {}, {}) {} +/*test:call*/test(x: {}) {} z: {} +/*test:call*/test(x: {}, {}) {} +/*test:call*/test {} y: {} +/*test:call*/test(y: {}) {} + + +// RUN: %empty-directory(%t.ranges) +// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "test(x:_:y:z:)" >> %t.ranges/multiple-trailing-closures-defaulted.swift.expected +// RUN: diff -u %S/FindRangeOutputs/multiple-trailing-closures-defaulted.swift.expected %t.ranges/multiple-trailing-closures-defaulted.swift.expected diff --git a/test/refactoring/SyntacticRename/multiple-trailing-closures.swift b/test/refactoring/SyntacticRename/multiple-trailing-closures.swift new file mode 100644 index 0000000000000..bd97ef7c0a741 --- /dev/null +++ b/test/refactoring/SyntacticRename/multiple-trailing-closures.swift @@ -0,0 +1,11 @@ +func /*test:def*/test(x: () -> (), _ xx: () -> (), y: () -> (), z: ()->()) {} + +/*test:call*/test(x: {}, {}, y: {}) {} +/*test:call*/test(x: {}, {}) {} z: {} +/*test:call*/test(x: {}) {} y: {} z: {} +/*test:call*/test {} _: {} y: {} z: {} + + +// RUN: %empty-directory(%t.ranges) +// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "test(x:_:y:z:)" >> %t.ranges/multiple-trailing-closures.swift.expected +// RUN: diff -u %S/FindRangeOutputs/multiple-trailing-closures.swift.expected %t.ranges/multiple-trailing-closures.swift.expected From feaaf392065c67a5971d6eeae5b2ff9e32712b97 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Mon, 9 Mar 2020 13:47:06 -0700 Subject: [PATCH 34/37] [sourcekitd-test] Make expand-placeholder iterative Instead of getting all edits up front using the same source code, apply each replacement before calculating the next. Placeholder expansion is sensitive the surrounding code, so expanding multiple closures separately can give different results from doing so in order. To allow testing that, add a magic placeholder identifier __skip__ to skip expansion during testing. This is also required for handling multiple trailing closures. --- test/SourceKit/CodeExpand/code-expand.swift | 29 +++-- .../tools/sourcekitd-test/sourcekitd-test.cpp | 100 ++++++++++++------ 2 files changed, 93 insertions(+), 36 deletions(-) diff --git a/test/SourceKit/CodeExpand/code-expand.swift b/test/SourceKit/CodeExpand/code-expand.swift index 1313be88fd642..425b49e9d6a59 100644 --- a/test/SourceKit/CodeExpand/code-expand.swift +++ b/test/SourceKit/CodeExpand/code-expand.swift @@ -67,24 +67,41 @@ func f1() { // CHECK-NEXT: <#code#> // CHECK-NEXT: } +// FIXME: whether we get a trailing closure or not depends on the order we +// expand the placeholders. func f1() { - bar(<#T##d: () -> ()##() -> ()#>, <#T##d: () -> ()##() -> ()#>) + bar(<#T##__skip__: () -> ()##() -> ()#>, <#T##d: () -> ()##() -> ()#>) } -// CHECK: bar({ -// CHECK-NEXT: <#code#> -// CHECK-NEXT: }, { +// CHECK: bar(<#T##__skip__: () -> ()##() -> ()#>, { // CHECK-NEXT: <#code#> // CHECK-NEXT: }) +func f1() { + bar(<#T##d: () -> ()##() -> ()#>, <#T##d: () -> ()##() -> ()#>) +} +// CHECK: bar({ +// CHECK-NEXT: <#code#> +// CHECK-NEXT: }) { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +// FIXME: whether we get a trailing closure or not depends on the order we +// expand the placeholders. +func f1() { + bar(a : <#T##__skip__: () -> ()##() -> ()#>, b : <#T##d: () -> ()##() -> ()#>) +} +// CHECK: bar(a : <#T##__skip__: () -> ()##() -> ()#>, b : { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: }) func f1() { bar(a : <#T##d: () -> ()##() -> ()#>, b : <#T##d: () -> ()##() -> ()#>) } // CHECK: bar(a : { // CHECK-NEXT: <#code#> -// CHECK-NEXT: }, b : { +// CHECK-NEXT: }) { // CHECK-NEXT: <#code#> -// CHECK-NEXT: }) +// CHECK-NEXT: } func f1() { diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 5bbc258a859b4..21683a1f976a6 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -15,7 +15,6 @@ #include "SourceKit/Support/Concurrency.h" #include "TestOptions.h" #include "swift/Demangling/ManglingMacros.h" -#include "clang/Rewrite/Core/RewriteBuffer.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/Optional.h" @@ -2088,53 +2087,85 @@ static void printStatistics(sourcekitd_variant_t Info, raw_ostream &OS) { }); } -static void initializeRewriteBuffer(StringRef Input, - clang::RewriteBuffer &RewriteBuf) { - RewriteBuf.Initialize(Input); - StringRef CheckStr = "CHECK"; - size_t Pos = 0; - while (true) { - Pos = Input.find(CheckStr, Pos); - if (Pos == StringRef::npos) - break; - Pos = Input.substr(0, Pos).rfind("//"); - assert(Pos != StringRef::npos); - size_t EndLine = Input.find('\n', Pos); - assert(EndLine != StringRef::npos); - ++EndLine; - RewriteBuf.RemoveText(Pos, EndLine-Pos); - Pos = EndLine; +static std::string initializeSource(StringRef Input) { + std::string result; + { + llvm::raw_string_ostream OS(result); + StringRef CheckStr = "CHECK"; + size_t Pos = 0; + while (true) { + auto checkPos = Input.find(CheckStr, Pos); + if (checkPos == StringRef::npos) + break; + checkPos = Input.substr(0, checkPos).rfind("//"); + assert(checkPos != StringRef::npos); + size_t EndLine = Input.find('\n', checkPos); + assert(EndLine != StringRef::npos); + ++EndLine; + OS << Input.slice(Pos, checkPos); + Pos = EndLine; + } + + OS << Input.slice(Pos, StringRef::npos); } + return result; } -static std::vector> -getPlaceholderRanges(StringRef Source) { +static Optional> +firstPlaceholderRange(StringRef Source, unsigned from) { const char *StartPtr = Source.data(); - std::vector> Ranges; + Source = Source.drop_front(from); + while (true) { size_t Pos = Source.find("<#"); if (Pos == StringRef::npos) break; unsigned OffsetStart = Source.data() + Pos - StartPtr; Source = Source.substr(Pos+2); + if (Source.startswith("__skip__") || Source.startswith("T##__skip__")) + continue; Pos = Source.find("#>"); if (Pos == StringRef::npos) break; unsigned OffsetEnd = Source.data() + Pos + 2 - StartPtr; Source = Source.substr(Pos+2); - Ranges.emplace_back(OffsetStart, OffsetEnd-OffsetStart); + return std::make_pair(OffsetStart, OffsetEnd-OffsetStart); } - return Ranges; + return llvm::None; } static void expandPlaceholders(llvm::MemoryBuffer *SourceBuf, llvm::raw_ostream &OS) { - clang::RewriteBuffer RewriteBuf; - initializeRewriteBuffer(SourceBuf->getBuffer(), RewriteBuf); - auto Ranges = getPlaceholderRanges(SourceBuf->getBuffer()); - for (auto Range : Ranges) { - unsigned Offset = Range.first; - unsigned Length = Range.second; + auto syncEdit = [=](unsigned offset, unsigned length, const char *text) { + auto SourceBufID = SourceBuf->getBufferIdentifier(); + auto req = sourcekitd_request_dictionary_create(nullptr, nullptr, 0); + sourcekitd_request_dictionary_set_uid(req, KeyRequest, + RequestEditorReplaceText); + sourcekitd_request_dictionary_set_stringbuf(req, KeyName, + SourceBufID.data(), + SourceBufID.size()); + sourcekitd_request_dictionary_set_int64(req, KeyOffset, offset); + sourcekitd_request_dictionary_set_int64(req, KeyLength, length); + sourcekitd_request_dictionary_set_string(req, KeySourceText, text); + + sourcekitd_response_t resp = sourcekitd_send_request_sync(req); + if (sourcekitd_response_is_error(resp)) { + sourcekitd_response_description_dump(resp); + exit(1); + } + sourcekitd_request_release(req); + sourcekitd_response_dispose(resp); + }; + + std::string source = initializeSource(SourceBuf->getBuffer()); + // Sync contents with modified source. + syncEdit(0, SourceBuf->getBuffer().size(), source.c_str()); + + unsigned cursor = 0; + + while (auto Range = firstPlaceholderRange(source, cursor)) { + unsigned Offset = Range->first; + unsigned Length = Range->second; sourcekitd_object_t Exp = sourcekitd_request_dictionary_create(nullptr, nullptr, 0); sourcekitd_request_dictionary_set_uid(Exp, KeyRequest, @@ -2156,16 +2187,25 @@ static void expandPlaceholders(llvm::MemoryBuffer *SourceBuf, sourcekitd_variant_t Info = sourcekitd_response_get_value(Resp); const char *Text = sourcekitd_variant_dictionary_get_string(Info, KeySourceText); if (!Text) { + cursor = Offset + Length; sourcekitd_response_dispose(Resp); continue; } unsigned EditOffset = sourcekitd_variant_dictionary_get_int64(Info, KeyOffset); unsigned EditLength = sourcekitd_variant_dictionary_get_int64(Info, KeyLength); - RewriteBuf.ReplaceText(EditOffset, EditLength, Text); + + // Apply edit locally. + source.replace(EditOffset, EditLength, Text); + + // Apply edit on server. + syncEdit(EditOffset, EditLength, Text); + + // Adjust cursor to after the edit (we do not expand recursively). + cursor = EditOffset + strlen(Text); sourcekitd_response_dispose(Resp); } - RewriteBuf.write(OS); + OS << source; } static std::pair From 2bf014dc74c98eb59f5726b9a0161179c214b893 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Mon, 9 Mar 2020 16:00:34 -0700 Subject: [PATCH 35/37] [expand-placeholder] Add support for multiple-trailing closures Since placeholder expansion works with a single placeholder, which is somewhat at odds with multiple-trailing closures, we eagerly attempt to expand all consecutive placeholders of closure type. That is, if the API has multiple closure parameters at the end, expanding any one of them will transform all of them to the new syntax. Example ``` foo(a: <#T##()->()#>, b: <#T##()->()#>) ``` expanding *either* parameter will produce the following: ``` foo { <#code#> } b: { <#code#> } ``` (caveat: the indentation is not part of placeholder expansion, but it's added here for clarity) At least for now we do not attempt to corral an existing closure into the new syntax, so for ``` foo(a: { bar() }, b: <#T##()->()#>) ``` The exansion will be ``` foo(a: { bar() }) { <#code#> } ``` as it was before. rdar://59688632 --- ...de-expand-multiple-trailing-closures.swift | 121 ++++++++ test/SourceKit/CodeExpand/code-expand.swift | 25 +- tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp | 291 ++++++++++++------ 3 files changed, 337 insertions(+), 100 deletions(-) create mode 100644 test/SourceKit/CodeExpand/code-expand-multiple-trailing-closures.swift diff --git a/test/SourceKit/CodeExpand/code-expand-multiple-trailing-closures.swift b/test/SourceKit/CodeExpand/code-expand-multiple-trailing-closures.swift new file mode 100644 index 0000000000000..e1a2428e3a537 --- /dev/null +++ b/test/SourceKit/CodeExpand/code-expand-multiple-trailing-closures.swift @@ -0,0 +1,121 @@ +// RUN: %sourcekitd-test -req=expand-placeholder %s | %FileCheck %s + +withMulti1Labeled(a: <#T##() -> ()#>, b: <#T##() -> ()#>) +// CHECK: withMulti1Labeled { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } b: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +withMulti2UnlabeledFirst(<#T##() -> ()#>, b: <#T##() -> ()#>) +// CHECK: withMulti2UnlabeledFirst { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } b: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +withMulti2UnlabeledFirst(_: <#T##() -> ()#>, b: <#T##() -> ()#>) +// CHECK: withMulti2UnlabeledFirst { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } b: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +// FIXME: we may ban second argument unlabeled. +withMulti2Unlabled(_: <#T##() -> ()#>, _: <#T##() -> ()#>) +// CHECK: withMulti2Unlabled { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } _: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +// FIXME: we may ban second argument unlabeled. +withMulti2Unlabled(<#T##() -> ()#>, <#T##() -> ()#>) +// CHECK: withMulti2Unlabled { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } _: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +withMulti3SecondArg(a: <#T##__skip__##() -> ()#>, b: <#T##() -> ()#>) +// CHECK: withMulti3SecondArg { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } b: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +withMulti4MiddleArg(a: <#T##() -> ()#>, b: <#T##__skip__##() -> ()#>, c: <#T##() -> ()#>) +// CHECK: withMulti4MiddleArg { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } b: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } c: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +singleAlreadyExpand(a: { print("hi") }, b: <#T##() -> ()#>) +// CHECK: singleAlreadyExpand(a: { print("hi") }) { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +nonTrailing1(a: <#T##() -> ()#>, b: { print("hi") }) +// CHECK: nonTrailing1(a: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: }, b: { print("hi") }) + +nonTrailingAndTrailing1(a: <#T##() -> ()#>, b: { print("hi") }, c: <#T##() -> ()#>) +// CHECK: nonTrailingAndTrailing1(a: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: }, b: { print("hi") }) { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +singleNonClosure(a: <#T##Int#>, b: <#T##() -> ()#>) +// CHECK: singleNonClosure(a: Int) { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +nonTrailing2(a: <#T##() -> ()#>, b: <#T##Int#>) +// CHECK: nonTrailing2(a: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: }, b: Int) + +nonTrailingAndTrailing2(a: <#T##() -> ()#>, b: <#T##Int#> c: <#T##() -> ()#>) +// CHECK: nonTrailingAndTrailing2(a: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: }, b: Int) { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + + +withTypesAndLabels1(a: <#T##(_ booly: Bool, inty: Int) -> ()#>, b: <#T##(solo: Xyz) -> ()#>) +// CHECK: withTypesAndLabels1 { (booly, inty) in +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } b: { (solo) in +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +func reset_parser1() {} + +withTypes1(a: <#T##(Bool, Int) -> ()#>, b: <#T##() -> Int#>) +// CHECK: withTypes1 { (<#Bool#>, <#Int#>) in +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } b: { () -> Int in +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +func reset_parser2() {} + +mixedFull1(arg1: <#T##Something#>, arg2: <#T##Other#>, callback1: <#T##() -> Void#>, callback2: <#T##() -> Void#>) +// CHECK: mixedFull1(arg1: Something, arg2: Other) { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } callback2: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +mixedFull2(arg1: 1, arg2: "2"/*comment*/, callback1: <#T##() -> Void#>, callback2: <#T##() -> Void#>) +// CHECK: mixedFull2(arg1: 1, arg2: "2") { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } callback2: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } diff --git a/test/SourceKit/CodeExpand/code-expand.swift b/test/SourceKit/CodeExpand/code-expand.swift index 425b49e9d6a59..ab5f00fcbec62 100644 --- a/test/SourceKit/CodeExpand/code-expand.swift +++ b/test/SourceKit/CodeExpand/code-expand.swift @@ -67,43 +67,42 @@ func f1() { // CHECK-NEXT: <#code#> // CHECK-NEXT: } -// FIXME: whether we get a trailing closure or not depends on the order we -// expand the placeholders. func f1() { bar(<#T##__skip__: () -> ()##() -> ()#>, <#T##d: () -> ()##() -> ()#>) } -// CHECK: bar(<#T##__skip__: () -> ()##() -> ()#>, { +// CHECK: bar { // CHECK-NEXT: <#code#> -// CHECK-NEXT: }) +// CHECK-NEXT: } _: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } func f1() { bar(<#T##d: () -> ()##() -> ()#>, <#T##d: () -> ()##() -> ()#>) } -// CHECK: bar({ +// CHECK: bar { // CHECK-NEXT: <#code#> -// CHECK-NEXT: }) { +// CHECK-NEXT: } _: { // CHECK-NEXT: <#code#> // CHECK-NEXT: } -// FIXME: whether we get a trailing closure or not depends on the order we -// expand the placeholders. func f1() { bar(a : <#T##__skip__: () -> ()##() -> ()#>, b : <#T##d: () -> ()##() -> ()#>) } -// CHECK: bar(a : <#T##__skip__: () -> ()##() -> ()#>, b : { +// CHECK: bar { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } b: { // CHECK-NEXT: <#code#> -// CHECK-NEXT: }) +// CHECK-NEXT: } func f1() { bar(a : <#T##d: () -> ()##() -> ()#>, b : <#T##d: () -> ()##() -> ()#>) } -// CHECK: bar(a : { +// CHECK: bar { // CHECK-NEXT: <#code#> -// CHECK-NEXT: }) { +// CHECK-NEXT: } b: { // CHECK-NEXT: <#code#> // CHECK-NEXT: } - func f1() { bar(a : {}}, <#T##d: () -> ()##() -> ()#>) } diff --git a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp index 963f14ff654b2..32902979906c0 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp @@ -1468,16 +1468,13 @@ class PlaceholderExpansionScanner { :NameRange(NameRange), TypeRange(TypeRange) { } }; -private: - struct ClosureInfo { std::vector Params; CharSourceRange ReturnTypeRange; }; +private: SourceManager &SM; - ClosureInfo TargetClosureInfo; - EditorPlaceholderExpr *PHE = nullptr; class PlaceholderFinder: public ASTWalker { SourceLoc PlaceholderLoc; @@ -1571,12 +1568,11 @@ class PlaceholderExpansionScanner { return ClosureWalker.FoundFunctionTypeRepr; } - bool scanClosureType(SourceFile &SF, SourceLoc PlaceholderLoc) { + bool scanClosureType(EditorPlaceholderExpr *PHE, + ClosureInfo &TargetClosureInfo) { TargetClosureInfo.Params.clear(); TargetClosureInfo.ReturnTypeRange = CharSourceRange(); - PlaceholderFinder Finder(PlaceholderLoc, PHE); - SF.walk(Finder); - if (!PHE || !PHE->getTypeForExpansion()) + if (!PHE->getTypeForExpansion()) return false; ClosureTypeWalker PW(SM, TargetClosureInfo); PHE->getTypeForExpansion()->walk(PW); @@ -1699,33 +1695,61 @@ class PlaceholderExpansionScanner { return std::make_pair(CE, true); } - bool shouldUseTrailingClosureInTuple(TupleExpr *TE, - SourceLoc PlaceHolderStartLoc, - bool &isWrappedWithBraces) { + struct ParamClosureInfo { + Optional placeholderClosure; + bool isNonPlacholderClosure = false; + bool isWrappedWithBraces = false; + }; + + /// Scan the given TupleExpr collecting parameter closure information and + /// returning the index of the given target placeholder (if found). + Optional scanTupleExpr(TupleExpr *TE, SourceLoc targetPlacholderLoc, + std::vector &outParams) { if (TE->getElements().empty()) - return false; + return llvm::None; - for (unsigned I = 0, N = TE->getNumElements(); I < N; ++ I) { - bool IsLast = I == N - 1; - Expr *E = TE->getElement(I); + outParams.clear(); + outParams.reserve(TE->getNumElements()); + + Optional targetPlacholderIndex; + + for (Expr *E : TE->getElements()) { + outParams.emplace_back(); + auto &outParam = outParams.back(); - // Placeholders wrapped in braces {<#T##() -> Int#>} can also - // be valid for trailing syntax. if (auto CE = dyn_cast(E)) { if (CE->hasSingleExpressionBody() && - CE->getSingleExpressionBody()->getStartLoc() - == PlaceHolderStartLoc) { - // We found the placeholder. - isWrappedWithBraces = true; - return IsLast; + CE->getSingleExpressionBody()->getStartLoc() == + targetPlacholderLoc) { + targetPlacholderIndex = outParams.size() - 1; + if (auto *PHE = dyn_cast( + CE->getSingleExpressionBody())) { + outParam.isWrappedWithBraces = true; + ClosureInfo info; + if (scanClosureType(PHE, info)) + outParam.placeholderClosure = info; + continue; + } } - } else if (IsLast) { - return E->getStartLoc() == PlaceHolderStartLoc; + // else... + outParam.isNonPlacholderClosure = true; + continue; + } + + if (auto *PHE = dyn_cast(E)) { + ClosureInfo info; + if (scanClosureType(PHE, info)) + outParam.placeholderClosure = info; } else if (containClosure(E)) { - return false; + outParam.isNonPlacholderClosure = true; + } + + if (E->getStartLoc() == targetPlacholderLoc) { + targetPlacholderIndex = outParams.size() - 1; } } - return false; + + return targetPlacholderIndex; } public: @@ -1734,45 +1758,91 @@ class PlaceholderExpansionScanner { /// Retrieves the parameter list, return type and context info for /// a typed completion placeholder in a function call. /// For example: foo.bar(aaa, <#T##(Int, Int) -> Bool#>). - bool scan(SourceFile &SF, unsigned BufID, unsigned Offset, - unsigned Length, std::function, - CharSourceRange)> Callback, - std::function NonClosureCallback) { + bool scan(SourceFile &SF, unsigned BufID, unsigned Offset, unsigned Length, + std::function + OneClosureCallback, + std::function trailingClosures)> + MultiClosureCallback, + std::function NonClosureCallback) { SourceLoc PlaceholderStartLoc = SM.getLocForOffset(BufID, Offset); // See if the placeholder is encapsulated with an EditorPlaceholderExpr - // and retrieve parameter and return type ranges. - if (!scanClosureType(SF, PlaceholderStartLoc)) { + EditorPlaceholderExpr *PHE = nullptr; + PlaceholderFinder Finder(PlaceholderStartLoc, PHE); + SF.walk(Finder); + if (!PHE) + return NonClosureCallback(PHE); + + // Retrieve parameter and return type ranges. + ClosureInfo TargetClosureInfo; + if (!scanClosureType(PHE, TargetClosureInfo)) return NonClosureCallback(PHE); - } // Now we need to see if we can suggest trailing closure expansion, // and if the call parens can be removed in that case. // We'll first find the enclosing CallExpr, and then do further analysis. - bool UseTrailingClosure = false; - bool isWrappedWithBraces = false; + std::vector params; + Optional targetPlacholderIndex; auto ECE = enclosingCallExprArg(SF, PlaceholderStartLoc); Expr *Args = ECE.first; if (Args && ECE.second) { if (isa(Args)) { - UseTrailingClosure = true; + params.emplace_back(); + params.back().placeholderClosure = TargetClosureInfo; + targetPlacholderIndex = 0; } else if (auto *TE = dyn_cast(Args)) { - UseTrailingClosure = shouldUseTrailingClosureInTuple( - TE, PlaceholderStartLoc, - isWrappedWithBraces); + targetPlacholderIndex = scanTupleExpr(TE, PlaceholderStartLoc, params); } } - Callback(Args, UseTrailingClosure, isWrappedWithBraces, - TargetClosureInfo.Params, - TargetClosureInfo.ReturnTypeRange); + // If there was no appropriate parent call expression, it's non-trailing. + if (!targetPlacholderIndex.hasValue()) { + OneClosureCallback(Args, /*useTrailingClosure=*/false, + /*isWrappedWithBraces=*/false, TargetClosureInfo); + return true; + } + + const unsigned end = params.size(); + unsigned firstTrailingIndex = end; + + // Find the first parameter eligible to be trailing. + while (firstTrailingIndex != 0) { + unsigned i = firstTrailingIndex - 1; + if (params[i].isNonPlacholderClosure || + !params[i].placeholderClosure.hasValue()) + break; + firstTrailingIndex = i; + } + + if (firstTrailingIndex > targetPlacholderIndex) { + // Target comes before the eligible trailing closures. + OneClosureCallback(Args, /*isTrailing=*/false, + params[*targetPlacholderIndex].isWrappedWithBraces, + TargetClosureInfo); + return true; + } else if (targetPlacholderIndex == end - 1 && + firstTrailingIndex == end - 1) { + // Target is the only eligible trailing closure. + OneClosureCallback(Args, /*isTrailing=*/true, + params[*targetPlacholderIndex].isWrappedWithBraces, + TargetClosureInfo); + return true; + } + + // There are multiple trailing closures. + SmallVector trailingClosures; + trailingClosures.reserve(params.size() - firstTrailingIndex); + for (const auto ¶m : + llvm::makeArrayRef(params).slice(firstTrailingIndex)) { + trailingClosures.push_back(*param.placeholderClosure); + } + MultiClosureCallback(cast(Args), firstTrailingIndex, + trailingClosures); return true; } - }; } // anonymous namespace @@ -2059,13 +2129,57 @@ void SwiftEditorDocument::formatText(unsigned Line, unsigned Length, Consumer.recordAffectedLineRange(LineRange.startLine(), LineRange.lineCount()); } -bool isReturningVoid(SourceManager &SM, CharSourceRange Range) { +bool isReturningVoid(const SourceManager &SM, CharSourceRange Range) { if (Range.isInvalid()) return false; StringRef Text = SM.extractText(Range); return "()" == Text || "Void" == Text; } +static void +printClosureBody(const PlaceholderExpansionScanner::ClosureInfo &closure, + llvm::raw_ostream &OS, const SourceManager &SM) { + bool ReturningVoid = isReturningVoid(SM, closure.ReturnTypeRange); + + bool HasSignature = !closure.Params.empty() || + (closure.ReturnTypeRange.isValid() && !ReturningVoid); + bool FirstParam = true; + if (HasSignature) + OS << "("; + for (auto &Param : closure.Params) { + if (!FirstParam) + OS << ", "; + FirstParam = false; + if (Param.NameRange.isValid()) { + // If we have a parameter name, just output the name as is and skip + // the type. For example: + // <#(arg1: Int, arg2: Int)#> turns into (arg1, arg2). + OS << SM.extractText(Param.NameRange); + } else { + // If we only have the parameter type, output the type as a + // placeholder. For example: + // <#(Int, Int)#> turns into (<#Int#>, <#Int#>). + OS << "<#"; + OS << SM.extractText(Param.TypeRange); + OS << "#>"; + } + } + if (HasSignature) + OS << ") "; + if (closure.ReturnTypeRange.isValid()) { + auto ReturnTypeText = SM.extractText(closure.ReturnTypeRange); + + // We need return type if it is not Void. + if (!ReturningVoid) { + OS << "-> "; + OS << ReturnTypeText << " "; + } + } + if (HasSignature) + OS << "in"; + OS << "\n" << getCodePlaceholder() << "\n"; +} + void SwiftEditorDocument::expandPlaceholder(unsigned Offset, unsigned Length, EditorConsumer &Consumer) { auto SyntaxInfo = Impl.getSyntaxInfo(); @@ -2086,8 +2200,7 @@ void SwiftEditorDocument::expandPlaceholder(unsigned Offset, unsigned Length, Scanner.scan(SF, BufID, Offset, Length, [&](Expr *Args, bool UseTrailingClosure, bool isWrappedWithBraces, - ArrayRef ClosureParams, - CharSourceRange ClosureReturnTypeRange) { + const PlaceholderExpansionScanner::ClosureInfo &closure) { unsigned EffectiveOffset = Offset; unsigned EffectiveLength = Length; @@ -2131,54 +2244,58 @@ void SwiftEditorDocument::expandPlaceholder(unsigned Offset, unsigned Length, } // Trailing closure syntax handling will replace braces anyway. bool printBraces = !isWrappedWithBraces || UseTrailingClosure; + if (printBraces) OS << "{ "; + printClosureBody(closure, OS, SM); + if (printBraces) + OS << "}"; + } + Consumer.handleSourceText(ExpansionStr); + Consumer.recordAffectedRange(EffectiveOffset, EffectiveLength); - bool ReturningVoid = isReturningVoid(SM, ClosureReturnTypeRange); - - bool HasSignature = !ClosureParams.empty() || - (ClosureReturnTypeRange.isValid() && !ReturningVoid); - bool FirstParam = true; - if (HasSignature) - OS << "("; - for (auto &Param: ClosureParams) { - if (!FirstParam) - OS << ", "; - FirstParam = false; - if (Param.NameRange.isValid()) { - // If we have a parameter name, just output the name as is and skip - // the type. For example: - // <#(arg1: Int, arg2: Int)#> turns into (arg1, arg2). - OS << SM.extractText(Param.NameRange); - } - else { - // If we only have the parameter type, output the type as a - // placeholder. For example: - // <#(Int, Int)#> turns into (<#Int#>, <#Int#>). - OS << "<#"; - OS << SM.extractText(Param.TypeRange); - OS << "#>"; - } - } - if (HasSignature) + },[&](TupleExpr *args, unsigned firstTrailingIndex, + ArrayRef trailingClosures) { + unsigned EffectiveOffset = Offset; + unsigned EffectiveLength = Length; + llvm::SmallString<128> ExpansionStr; + { + llvm::raw_svector_ostream OS(ExpansionStr); + + assert(args->getNumElements() - firstTrailingIndex == trailingClosures.size()); + if (firstTrailingIndex == 0) { + // foo(<....>) -> foo { <...> } + EffectiveOffset = SM.getLocOffsetInBuffer(args->getStartLoc(), BufID); + OS << " "; + } else { + // foo(blah, <....>) -> foo(blah) { <...> } + SourceLoc beforeTrailingLoc = Lexer::getLocForEndOfToken(SM, + args->getElements()[firstTrailingIndex - 1]->getEndLoc()); + EffectiveOffset = SM.getLocOffsetInBuffer(beforeTrailingLoc, BufID); OS << ") "; - if (ClosureReturnTypeRange.isValid()) { - auto ReturnTypeText = SM.extractText(ClosureReturnTypeRange); + } + + unsigned End = SM.getLocOffsetInBuffer(args->getEndLoc(), BufID); + EffectiveLength = (End + 1) - EffectiveOffset; - // We need return type if it is not Void. - if (!ReturningVoid) { - OS << "-> "; - OS << ReturnTypeText << " "; + unsigned argI = firstTrailingIndex; + for (unsigned i = 0; argI != args->getNumElements(); ++i, ++argI) { + const auto &closure = trailingClosures[i]; + if (i == 0) { + OS << "{ "; + } else { + auto label = args->getElementName(argI); + OS << " " << (label.empty() ? "_" : label.str()) << ": { "; } - } - if (HasSignature) - OS << "in"; - OS << "\n" << getCodePlaceholder() << "\n"; - if (printBraces) + printClosureBody(closure, OS, SM); OS << "}"; + } + OS << "\n"; } + Consumer.handleSourceText(ExpansionStr); Consumer.recordAffectedRange(EffectiveOffset, EffectiveLength); + }, [&](EditorPlaceholderExpr *PHE) { if (!PHE) return false; From 37b98af4e61dad68d52edd8b008928016548b4f0 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Fri, 1 May 2020 12:11:40 -0700 Subject: [PATCH 36/37] [CodeCompletion] Pre-expand closures in argument completion When completing a single argument for a trailing closure, pre-expand the closure expression syntax instead of using a placeholder. It's not valid to pass a non-closure anyway. rdar://62189182 --- include/swift/IDE/CodeCompletion.h | 7 ++ lib/IDE/CodeCompletion.cpp | 71 ++++++++++++++++--- lib/IDE/CodeCompletionResultBuilder.h | 6 +- lib/IDE/CodeCompletionResultPrinter.cpp | 2 + lib/IDE/REPLCodeCompletion.cpp | 4 ++ .../complete_multiple_trailingclosure.swift | 32 ++++----- ..._multiple_trailingclosure_signatures.swift | 28 ++++++++ ...multiple_trailing_closure_signatures.swift | 29 ++++++++ .../lib/SwiftLang/CodeCompletionOrganizer.cpp | 1 + .../lib/SwiftLang/SwiftCompletion.cpp | 17 ++++- 10 files changed, 168 insertions(+), 29 deletions(-) create mode 100644 test/IDE/complete_multiple_trailingclosure_signatures.swift create mode 100644 test/SourceKit/CodeComplete/multiple_trailing_closure_signatures.swift diff --git a/include/swift/IDE/CodeCompletion.h b/include/swift/IDE/CodeCompletion.h index b494d74aafe8f..89a1ea7572416 100644 --- a/include/swift/IDE/CodeCompletion.h +++ b/include/swift/IDE/CodeCompletion.h @@ -156,6 +156,12 @@ class CodeCompletionStringChunk { /// closure type if CallParameterType is a TypeAliasType. CallParameterClosureType, + /// An expanded closure expression for the value of a parameter, including + /// the left and right braces and possible signature. The preferred + /// position to put the cursor after the completion result is inserted + /// into the editor buffer is between the braces. + CallParameterClosureExpr, + /// A placeholder for \c ! or \c ? in a call to a method found by dynamic /// lookup. /// @@ -224,6 +230,7 @@ class CodeCompletionStringChunk { Kind == ChunkKind::DeclAttrParamKeyword || Kind == ChunkKind::CallParameterType || Kind == ChunkKind::CallParameterClosureType || + Kind == ChunkKind::CallParameterClosureExpr || Kind == ChunkKind::GenericParameterName || Kind == ChunkKind::DynamicLookupMethodCallTail || Kind == ChunkKind::OptionalMethodCallTail || diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index d67d5b8df9934..8f8f7e873a218 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -431,6 +431,9 @@ void CodeCompletionString::print(raw_ostream &OS) const { OS << C.getText(); OS << "#]"; break; + case ChunkKind::CallParameterClosureExpr: + OS << " {" << C.getText() << "|}"; + break; case ChunkKind::BraceStmtWithCursor: OS << " {|}"; break; @@ -870,7 +873,8 @@ void CodeCompletionResultBuilder::addCallParameter(Identifier Name, bool IsInOut, bool IsIUO, bool isAutoClosure, - bool useUnderscoreLabel) { + bool useUnderscoreLabel, + bool isLabeledTrailingClosure) { CurrentNestingLevel++; using ChunkKind = CodeCompletionString::Chunk::ChunkKind; @@ -967,7 +971,8 @@ void CodeCompletionResultBuilder::addCallParameter(Identifier Name, // function type. Ty = Ty->lookThroughAllOptionalTypes(); if (auto AFT = Ty->getAs()) { - // If this is a closure type, add ChunkKind::CallParameterClosureType. + // If this is a closure type, add ChunkKind::CallParameterClosureType or + // ChunkKind::CallParameterClosureExpr for labeled trailing closures. PrintOptions PO; PO.PrintFunctionRepresentationAttrs = PrintOptions::FunctionRepresentationMode::None; @@ -976,9 +981,51 @@ void CodeCompletionResultBuilder::addCallParameter(Identifier Name, PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; if (ContextTy) PO.setBaseType(ContextTy); - addChunkWithText( - CodeCompletionString::Chunk::ChunkKind::CallParameterClosureType, - AFT->getString(PO)); + + if (isLabeledTrailingClosure) { + // Expand the closure body. + SmallString<32> buffer; + llvm::raw_svector_ostream OS(buffer); + + bool returnsVoid = AFT->getResult()->isVoid(); + bool hasSignature = !returnsVoid || !AFT->getParams().empty(); + if (hasSignature) + OS << "("; + bool firstParam = true; + for (const auto ¶m : AFT->getParams()) { + if (!firstParam) + OS << ", "; + firstParam = false; + + if (param.hasLabel()) { + OS << param.getLabel(); + } else { + OS << "<#"; + if (param.isInOut()) + OS << "inout "; + OS << param.getPlainType()->getString(PO); + if (param.isVariadic()) + OS << "..."; + OS << "#>"; + } + } + if (hasSignature) + OS << ")"; + if (!returnsVoid) + OS << " -> " << AFT->getResult()->getString(PO); + + if (hasSignature) + OS << " in"; + + addChunkWithText( + CodeCompletionString::Chunk::ChunkKind::CallParameterClosureExpr, + OS.str()); + } else { + // Add the closure type. + addChunkWithText( + CodeCompletionString::Chunk::ChunkKind::CallParameterClosureType, + AFT->getString(PO)); + } } if (IsVarArg) @@ -1337,6 +1384,7 @@ Optional CodeCompletionString::getFirstTextChunkIndex( case ChunkKind::DeclAttrParamColon: case ChunkKind::CallParameterType: case ChunkKind::CallParameterClosureType: + case ChunkKind::CallParameterClosureExpr: case ChunkKind::OptionalBegin: case ChunkKind::GenericParameterBegin: case ChunkKind::DynamicLookupMethodCallTail: @@ -1370,6 +1418,7 @@ void CodeCompletionString::getName(raw_ostream &OS) const { switch (C.getKind()) { case ChunkKind::TypeAnnotation: case ChunkKind::CallParameterClosureType: + case ChunkKind::CallParameterClosureExpr: case ChunkKind::DeclAttrParamColon: case ChunkKind::OptionalMethodCallTail: continue; @@ -2523,7 +2572,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Builder.addCallParameter(argName, bodyName, paramTy, contextTy, isVariadic, isInOut, isIUO, isAutoclosure, - /*useUnderscoreLabel=*/false); + /*useUnderscoreLabel=*/false, + /*isLabeledTrailingClosure=*/false); modifiedBuilder = true; NeedComma = true; @@ -4159,7 +4209,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } void addCallArgumentCompletionResults( - ArrayRef ParamInfos) { + ArrayRef ParamInfos, + bool isLabeledTrailingClosure = false) { Type ContextType; if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) ContextType = typeContext->getDeclaredTypeInContext(); @@ -4175,7 +4226,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { Arg->getPlainType(), ContextType, Arg->isVariadic(), Arg->isInOut(), /*isIUO=*/false, Arg->isAutoClosure(), - /*useUnderscoreLabel=*/true); + /*useUnderscoreLabel=*/true, + isLabeledTrailingClosure); auto Ty = Arg->getPlainType(); if (Arg->isInOut()) { Ty = InOutType::get(Ty); @@ -5926,7 +5978,8 @@ void CodeCompletionCallbacksImpl::doneParsing() { bool allRequired = false; if (!params.empty()) { - Lookup.addCallArgumentCompletionResults(params); + Lookup.addCallArgumentCompletionResults( + params, /*isLabeledTrailingClosure=*/true); allRequired = llvm::all_of( params, [](const PossibleParamInfo &P) { return P.IsRequired; }); } diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index cc70304bb6a7d..bbf6955b11692 100644 --- a/lib/IDE/CodeCompletionResultBuilder.h +++ b/lib/IDE/CodeCompletionResultBuilder.h @@ -378,12 +378,14 @@ class CodeCompletionResultBuilder { void addCallParameter(Identifier Name, Identifier LocalName, Type Ty, Type ContextTy, bool IsVarArg, bool IsInOut, bool IsIUO, - bool isAutoClosure, bool useUnderscoreLabel); + bool isAutoClosure, bool useUnderscoreLabel, + bool isLabeledTrailingClosure); void addCallParameter(Identifier Name, Type Ty, Type ContextTy = Type()) { addCallParameter(Name, Identifier(), Ty, ContextTy, /*IsVarArg=*/false, /*IsInOut=*/false, /*isIUO=*/false, - /*isAutoClosure=*/false, /*useUnderscoreLabel=*/false); + /*isAutoClosure=*/false, /*useUnderscoreLabel=*/false, + /*isLabeledTrailingClosure=*/false); } void addGenericParameter(StringRef Name) { diff --git a/lib/IDE/CodeCompletionResultPrinter.cpp b/lib/IDE/CodeCompletionResultPrinter.cpp index 5b9f656a78914..9d3a5e76ee340 100644 --- a/lib/IDE/CodeCompletionResultPrinter.cpp +++ b/lib/IDE/CodeCompletionResultPrinter.cpp @@ -39,6 +39,7 @@ void swift::ide::printCodeCompletionResultDescription( if (C.is(ChunkKind::TypeAnnotation) || C.is(ChunkKind::CallParameterClosureType) || + C.is(ChunkKind::CallParameterClosureExpr) || C.is(ChunkKind::Whitespace)) continue; @@ -117,6 +118,7 @@ class AnnotatingDescriptionPrinter { break; case ChunkKind::TypeAnnotation: case ChunkKind::CallParameterClosureType: + case ChunkKind::CallParameterClosureExpr: case ChunkKind::Whitespace: // ignore; break; diff --git a/lib/IDE/REPLCodeCompletion.cpp b/lib/IDE/REPLCodeCompletion.cpp index 2378ef913dc4f..f1c43b033e61a 100644 --- a/lib/IDE/REPLCodeCompletion.cpp +++ b/lib/IDE/REPLCodeCompletion.cpp @@ -83,6 +83,10 @@ static std::string toInsertableString(CodeCompletionResult *Result) { case CodeCompletionString::Chunk::ChunkKind::TypeAnnotation: return Str; + case CodeCompletionString::Chunk::ChunkKind::CallParameterClosureExpr: + Str += " {"; + Str += C.getText(); + break; case CodeCompletionString::Chunk::ChunkKind::BraceStmtWithCursor: Str += " {"; break; diff --git a/test/IDE/complete_multiple_trailingclosure.swift b/test/IDE/complete_multiple_trailingclosure.swift index a82c91e2a2777..5fa03207a0169 100644 --- a/test/IDE/complete_multiple_trailingclosure.swift +++ b/test/IDE/complete_multiple_trailingclosure.swift @@ -20,11 +20,11 @@ func testGlobalFunc() { { 1 } #^GLOBALFUNC_SAMELINE^# #^GLOBALFUNC_NEWLINE^# // GLOBALFUNC_SAMELINE: Begin completions, 1 items -// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {() -> String in|}#}[#() -> String#]; // GLOBALFUNC_SAMELINE: End completions // GLOBALFUNC_NEWLINE: Begin completions, 1 items -// GLOBALFUNC_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// GLOBALFUNC_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {() -> String in|}#}[#() -> String#]; // GLOBALFUNC_NEWLINE: End completions globalFunc1() @@ -49,14 +49,14 @@ func testMethod(value: MyStruct) { } #^METHOD_SAMELINE^# #^METHOD_NEWLINE^# // METHOD_SAMELINE: Begin completions, 4 items -// METHOD_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: (() -> String)?##() -> String#}[#(() -> String)?#]; +// METHOD_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: (() -> String)? {() -> String in|}#}[#(() -> String)?#]; // METHOD_SAMELINE-DAG: Decl[InstanceMethod]/CurrNominal: .enumFunc()[#Void#]; // METHOD_SAMELINE-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: [' ']+ {#SimpleEnum#}[#SimpleEnum#]; // METHOD_SAMELINE-DAG: Keyword[self]/CurrNominal: .self[#SimpleEnum#]; // METHOD_SAMELINE: End completions // METHOD_NEWLINE: Begin completions -// METHOD_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: (() -> String)?##() -> String#}[#(() -> String)?#]; +// METHOD_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: (() -> String)? {() -> String in|}#}[#(() -> String)?#]; // METHOD_NEWLINE-DAG: Keyword[class]/None: class; // METHOD_NEWLINE-DAG: Keyword[if]/None: if; // METHOD_NEWLINE-DAG: Keyword[try]/None: try; @@ -80,15 +80,15 @@ func testOverloadedInit() { #^INIT_OVERLOADED_NEWLINE^# // INIT_OVERLOADED_SAMELINE: Begin completions, 4 items -// INIT_OVERLOADED_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; -// INIT_OVERLOADED_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_OVERLOADED_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {() -> String in|}#}[#() -> String#]; +// INIT_OVERLOADED_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String {() -> String in|}#}[#() -> String#]; // INIT_OVERLOADED_SAMELINE-DAG: Decl[InstanceMethod]/CurrNominal: .testStructMethod()[#Void#]; // INIT_OVERLOADED_SAMELINE-DAG: Keyword[self]/CurrNominal: .self[#TestStruct#]; // INIT_OVERLOADED_SAMELINE: End completions // INIT_OVERLOADED_NEWLINE: Begin completions -// INIT_OVERLOADED_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; -// INIT_OVERLOADED_NEWLINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_OVERLOADED_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {() -> String in|}#}[#() -> String#]; +// INIT_OVERLOADED_NEWLINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String {() -> String in|}#}[#() -> String#]; // INIT_OVERLOADED_NEWLINE-DAG: Keyword[class]/None: class; // INIT_OVERLOADED_NEWLINE-DAG: Keyword[if]/None: if; // INIT_OVERLOADED_NEWLINE-DAG: Keyword[try]/None: try; @@ -107,15 +107,15 @@ func testOptionalInit() { #^INIT_OPTIONAL_NEWLINE^# // INIT_OPTIONAL_SAMELINE: Begin completions, 4 items -// INIT_OPTIONAL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; -// INIT_OPTIONAL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_OPTIONAL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {() -> String in|}#}[#() -> String#]; +// INIT_OPTIONAL_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String {() -> String in|}#}[#() -> String#]; // INIT_OPTIONAL_SAMELINE-DAG: Decl[InstanceMethod]/CurrNominal: .testStructMethod()[#Void#]; // INIT_OPTIONAL_SAMELINE-DAG: Keyword[self]/CurrNominal: .self[#TestStruct2#]; // INIT_OPTIONAL_SAMELINE: End completions // INIT_OPTIONAL_NEWLINE: Begin completions -// INIT_OPTIONAL_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; -// INIT_OPTIONAL_NEWLINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_OPTIONAL_NEWLINE-DAG: Pattern/ExprSpecific: {#fn2: () -> String {() -> String in|}#}[#() -> String#]; +// INIT_OPTIONAL_NEWLINE-DAG: Pattern/ExprSpecific: {#fn3: () -> String {() -> String in|}#}[#() -> String#]; // INIT_OPTIONAL_NEWLINE-DAG: Keyword[class]/None: class; // INIT_OPTIONAL_NEWLINE-DAG: Keyword[if]/None: if; // INIT_OPTIONAL_NEWLINE-DAG: Keyword[try]/None: try; @@ -135,11 +135,11 @@ func testOptionalInit() { #^INIT_REQUIRED_NEWLINE_1^# // INIT_REQUIRED_SAMELINE_1: Begin completions, 1 items -// INIT_REQUIRED_SAMELINE_1-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// INIT_REQUIRED_SAMELINE_1-DAG: Pattern/ExprSpecific: {#fn2: () -> String {() -> String in|}#}[#() -> String#]; // INIT_REQUIRED_SAMELINE_1: End completions // INIT_REQUIRED_NEWLINE_1: Begin completions, 1 items -// INIT_REQUIRED_NEWLINE_1-DAG: Pattern/ExprSpecific: {#fn2: () -> String##() -> String#}[#() -> String#]; +// INIT_REQUIRED_NEWLINE_1-DAG: Pattern/ExprSpecific: {#fn2: () -> String {() -> String in|}#}[#() -> String#]; // INIT_REQUIRED_NEWLINE_1: End completions // missing 'fn3'. @@ -151,11 +151,11 @@ func testOptionalInit() { #^INIT_REQUIRED_NEWLINE_2^# // INIT_REQUIRED_SAMELINE_2: Begin completions, 1 items -// INIT_REQUIRED_SAMELINE_2-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_REQUIRED_SAMELINE_2-DAG: Pattern/ExprSpecific: {#fn3: () -> String {() -> String in|}#}[#() -> String#]; // INIT_REQUIRED_SAMELINE_2: End completions // INIT_REQUIRED_NEWLINE_2: Begin completions, 1 items -// INIT_REQUIRED_NEWLINE_2-DAG: Pattern/ExprSpecific: {#fn3: () -> String##() -> String#}[#() -> String#]; +// INIT_REQUIRED_NEWLINE_2-DAG: Pattern/ExprSpecific: {#fn3: () -> String {() -> String in|}#}[#() -> String#]; // INIT_REQUIRED_NEWLINE_2: End completions // Call is completed. diff --git a/test/IDE/complete_multiple_trailingclosure_signatures.swift b/test/IDE/complete_multiple_trailingclosure_signatures.swift new file mode 100644 index 0000000000000..ca07b2d53065d --- /dev/null +++ b/test/IDE/complete_multiple_trailingclosure_signatures.swift @@ -0,0 +1,28 @@ +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GLOBALFUNC_SAMELINE | %FileCheck %s -check-prefix=GLOBALFUNC_SAMELINE + +func func1( + fn1: () -> Int, + fn2: () -> Void = {}, + fn3: (Int) -> Void = {_ in}, + fn4: (Int, String) -> Void = {_,_ in}, + fn5: (Int, String) -> Int = {_,_ in 1}, + fn6: (_ a: Int, _ b: String) -> Int = {_,_ in 1}, + fn7: (inout Int) -> Void = {_ in}, + fn8: (Int...) -> Void = { (_:Int...) in}) +{} + +func test() { + func1() + { 1 } #^GLOBALFUNC_SAMELINE^# + +// GLOBALFUNC_SAMELINE: Begin completions +// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn2: () -> Void {|}#}[#() -> Void#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn3: (Int) -> Void {(<#Int#>) in|}#}[#(Int) -> Void#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn4: (Int, String) -> Void {(<#Int#>, <#String#>) in|}#}[#(Int, String) -> Void#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn5: (Int, String) -> Int {(<#Int#>, <#String#>) -> Int in|}#}[#(Int, String) -> Int#]; +// FIXME: recover names +// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn6: (Int, String) -> Int {(<#Int#>, <#String#>) -> Int in|}#}[#(Int, String) -> Int#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn7: (inout Int) -> Void {(<#inout Int#>) in|}#}[#(inout Int) -> Void#]; +// GLOBALFUNC_SAMELINE-DAG: Pattern/ExprSpecific: {#fn8: (Int...) -> Void {(<#Int...#>) in|}#}[#(Int...) -> Void#]; +// GLOBALFUNC_SAMELINE: End completions +} diff --git a/test/SourceKit/CodeComplete/multiple_trailing_closure_signatures.swift b/test/SourceKit/CodeComplete/multiple_trailing_closure_signatures.swift new file mode 100644 index 0000000000000..31d0b944aa348 --- /dev/null +++ b/test/SourceKit/CodeComplete/multiple_trailing_closure_signatures.swift @@ -0,0 +1,29 @@ +func test() { + func1() + { 1 } +} + +func func1( + fn1: () -> Int, + fn2: () -> Void = {}, + fn3: (Int) -> Void = {_ in}, + fn4: (Int, String) -> Void = {_,_ in}, + fn5: (Int, String) -> Int = {_,_ in 1}, + fn7: (inout Int) -> Void = {_ in}, + fn8: (Int...) -> Void = { (_:Int...) in}) +{} + +// RUN: %sourcekitd-test -req=complete -pos=3:11 %s -- %s > %t +// RUN: %FileCheck %s < %t +// RUN: %FileCheck %s --check-prefix=DESCRIPTION < %t + +// CHECK: key.results: [ +// CHECK-DAG: key.sourcetext: "fn2: {\n<#code#>\n}" +// CHECK-DAG: key.sourcetext: "fn3: { (<#Int#>) in\n<#code#>\n}" +// CHECK-DAG: key.sourcetext: "fn4: { (<#Int#>, <#String#>) in\n<#code#>\n}", +// CHECK-DAG: key.sourcetext: "fn5: { (<#Int#>, <#String#>) -> Int in\n<#code#>\n}", +// CHECK-DAG: key.sourcetext: "fn7: { (<#inout Int#>) in\n<#code#>\n}", +// CHECK-DAG: key.sourcetext: "fn8: { (<#Int...#>) in\n<#code#>\n}", +// CHECK: ] + +// DESCRIPTION-NOT: key.description: "fn{{[0-9]*}}: { diff --git a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp index 56cca8de290cb..dde9cd6029d26 100644 --- a/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp +++ b/tools/SourceKit/lib/SwiftLang/CodeCompletionOrganizer.cpp @@ -1231,6 +1231,7 @@ void CompletionBuilder::getFilterName(CodeCompletionString *str, case ChunkKind::TypeAnnotation: case ChunkKind::CallParameterInternalName: case ChunkKind::CallParameterClosureType: + case ChunkKind::CallParameterClosureExpr: case ChunkKind::CallParameterType: case ChunkKind::DeclAttrParamColon: case ChunkKind::Comma: diff --git a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp index 73a1e7b7aecc6..7ba24a9906b0a 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp @@ -248,6 +248,7 @@ static void getResultStructure( auto C = chunks[i]; if (C.is(ChunkKind::TypeAnnotation) || C.is(ChunkKind::CallParameterClosureType) || + C.is(ChunkKind::CallParameterClosureExpr) || C.is(ChunkKind::Whitespace)) continue; @@ -268,6 +269,7 @@ static void getResultStructure( auto &C = chunks[i]; if (C.is(ChunkKind::TypeAnnotation) || C.is(ChunkKind::CallParameterClosureType) || + C.is(ChunkKind::CallParameterClosureExpr) || C.is(ChunkKind::Whitespace)) continue; @@ -289,7 +291,8 @@ static void getResultStructure( for (; i < chunks.size(); ++i) { if (chunks[i].endsPreviousNestedGroup(C.getNestingLevel())) break; - if (chunks[i].is(ChunkKind::CallParameterClosureType)) + if (chunks[i].is(ChunkKind::CallParameterClosureType) || + chunks[i].is(ChunkKind::CallParameterClosureExpr)) continue; if (isOperator && chunks[i].is(ChunkKind::CallParameterType)) continue; @@ -610,7 +613,8 @@ static void constructTextForCallParam( continue; if (C.is(ChunkKind::CallParameterInternalName) || C.is(ChunkKind::CallParameterType) || - C.is(ChunkKind::CallParameterTypeBegin)) { + C.is(ChunkKind::CallParameterTypeBegin) || + C.is(ChunkKind::CallParameterClosureExpr)) { break; } if (!C.hasText()) @@ -648,6 +652,15 @@ static void constructTextForCallParam( assert(TypeString.empty()); TypeString = C.getText(); } + if (C.is(ChunkKind::CallParameterClosureExpr)) { + // We have a closure expression, so provide it directly instead of in + // a placeholder. + OS << "{"; + if (!C.getText().empty()) + OS << " " << C.getText(); + OS << "\n" << getCodePlaceholder() << "\n}"; + return; + } if (C.isAnnotation() || !C.hasText()) continue; DisplayString += C.getText(); From 5597b2f23c9aae3a8f90a093703a7441dd26c9bd Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 6 May 2020 12:24:13 -0400 Subject: [PATCH 37/37] Fix for new use of CallExpr::Create --- lib/Sema/BuilderTransform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index edd71fbacd154..09aba51a554a5 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -823,7 +823,7 @@ class BuilderClosureVisitor auto bodyVarRef = buildVarRef(bodyVar, endLoc); Expr *arrayAppendCall = CallExpr::create( ctx, arrayAppendRef, endLoc, { bodyVarRef } , { Identifier() }, - { endLoc }, endLoc, /*trailingClosure=*/nullptr, /*implicit=*/true); + { endLoc }, endLoc, /*trailingClosures=*/{}, /*implicit=*/true); arrayAppendCall = cs->generateConstraints(arrayAppendCall, dc); if (!arrayAppendCall) { hadError = true;