diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 1222a70cd3ade..1a79bcc0da34d 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1750,5 +1750,16 @@ 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_closure_literal,none, + "expected 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/include/swift/AST/Expr.h b/include/swift/AST/Expr.h index 94bda5f12cf07..3c13b029ac9aa 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, @@ -200,10 +212,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, @@ -533,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. @@ -1198,7 +1212,7 @@ class ObjectLiteralExpr final ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, bool implicit); LiteralKind getLiteralKind() const { @@ -1220,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()); @@ -1806,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(); } @@ -1848,7 +1872,7 @@ class UnresolvedMemberExpr final ArrayRef argLabels, ArrayRef argLabelLocs, SourceLoc rParenLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, bool implicit); DeclNameRef getName() const { return Name; } @@ -1875,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; } @@ -2056,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; } }; @@ -2069,6 +2103,8 @@ class TupleExpr final : public Expr, SourceLoc LParenLoc; SourceLoc RParenLoc; + Optional FirstTrailingArgumentAt; + size_t numTrailingObjects(OverloadToken) const { return getNumElements(); } @@ -2095,11 +2131,11 @@ 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); + Optional FirstTrailingArgumentAt, bool Implicit, Type Ty); public: /// Create a tuple. @@ -2111,6 +2147,15 @@ 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, + Optional FirstTrailingArgumentAt, + bool Implicit, Type Ty = Type()); + /// Create an empty tuple. static TupleExpr *createEmpty(ASTContext &ctx, SourceLoc LParenLoc, SourceLoc RParenLoc, bool Implicit); @@ -2124,8 +2169,25 @@ class TupleExpr final : public Expr, SourceRange getSourceRange() const; + bool hasAnyTrailingClosures() const { + return (bool) FirstTrailingArgumentAt; + } + /// 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; + } + + Optional + getUnlabeledTrailingClosureIndexOfPackedArgument() const { + return FirstTrailingArgumentAt; + } /// Retrieve the elements of this tuple. MutableArrayRef getElements() { @@ -2136,8 +2198,22 @@ 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; } + + unsigned getNumTrailingElements() const { + return FirstTrailingArgumentAt + ? getNumElements() - *FirstTrailingArgumentAt + : 0; + } Expr *getElement(unsigned i) const { return getElements()[i]; @@ -2374,7 +2450,7 @@ class SubscriptExpr final : public LookupExpr, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, ConcreteDeclRef decl = ConcreteDeclRef(), bool implicit = false, AccessSemantics semantics @@ -2398,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 { @@ -4243,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; @@ -4287,8 +4371,8 @@ class CallExpr final : public ApplyExpr, llvm::function_ref getType = [](Expr *E) -> Type { return E->getType(); }) { - return create(ctx, fn, SourceLoc(), args, argLabels, { }, SourceLoc(), - /*trailingClosure=*/nullptr, /*implicit=*/true, getType); + return create(ctx, fn, SourceLoc(), args, argLabels, {}, SourceLoc(), + /*trailingClosures=*/{}, /*implicit=*/true, getType); } /// Create a new call expression. @@ -4299,11 +4383,12 @@ 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(); }); @@ -4325,9 +4410,14 @@ 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; } + /// 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. @@ -5191,7 +5281,7 @@ class KeyPathExpr : public Expr { ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - Expr *trailingClosure); + ArrayRef trailingClosures); /// Create an unresolved component for a subscript. /// @@ -5243,7 +5333,7 @@ class KeyPathExpr : public Expr { ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, Type elementType, ArrayRef indexHashables); @@ -5631,7 +5721,8 @@ 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/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/include/swift/IDE/CodeCompletion.h b/include/swift/IDE/CodeCompletion.h index 8deabfd7a205a..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 || @@ -525,6 +532,7 @@ enum class CompletionKind { AttributeDeclParen, PoundAvailablePlatform, CallArg, + LabeledTrailingClosure, ReturnStmtExpr, YieldStmtExpr, ForEachSequence, 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/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/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 883dae550812f..219bfd88fcc2f 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1567,10 +1567,12 @@ class Parser { SmallVectorImpl &exprLabels, SmallVectorImpl &exprLabelLocs, SourceLoc &rightLoc, - Expr *&trailingClosure, + SmallVectorImpl &trailingClosures, syntax::SyntaxKind Kind); - ParserResult parseTrailingClosure(SourceRange calleeRange); + ParserStatus + parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, + SmallVectorImpl &closures); /// Parse an object literal. /// diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index dcb98d3ab31bd..2b339f12fb75a 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, @@ -1868,16 +1867,15 @@ 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); 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 62c8301fbccea..3dd7586713141 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,14 @@ 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 && + trailingClosures.front().LabelLoc.isInvalid()) { + auto &trailingClosure = trailingClosures.front(); + auto arg = + new (ctx) ParenExpr(lParenLoc, trailingClosure.ClosureExpr, rParenLoc, + /*hasTrailingClosure=*/true); computeSingleArgumentType(ctx, arg, implicit, getType); argLabelsScratch.push_back(Identifier()); argLabels = argLabelsScratch; @@ -1029,38 +1033,63 @@ 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()); - argsScratch.push_back(trailingClosure); + 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()); - argLabelsScratch.push_back(Identifier()); + { + 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() + 1); - argLabelLocsScratch.append(argLabelLocs.begin(), argLabelLocs.end()); - argLabelLocsScratch.push_back(SourceLoc()); + { + 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, args, argLabels, - argLabelLocs, rParenLoc, - /*HasTrailingClosure=*/true, + Optional unlabeledTrailingClosureIndex; + if (!trailingClosures.empty() && trailingClosures[0].Label.empty()) + unlabeledTrailingClosureIndex = args.size() - trailingClosures.size(); + + auto arg = TupleExpr::create(ctx, lParenLoc, rParenLoc, args, argLabels, + 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, @@ -1074,7 +1103,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 +1120,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, @@ -1107,21 +1136,21 @@ 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, - argLabelsScratch, argLabelLocsScratch); + rParenLoc, + trailingClosures, 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, argLabelLocs, - trailingClosure != nullptr, implicit); + trailingClosures.size() == 1, implicit); } StringRef ObjectLiteralExpr::getLiteralKindRawName() const { @@ -1244,7 +1273,7 @@ SourceRange TupleExpr::getSourceRange() const { } } - if (hasTrailingClosure() || RParenLoc.isInvalid()) { + if (hasAnyTrailingClosures() || RParenLoc.isInvalid()) { if (getNumElements() == 0) { return { SourceLoc(), SourceLoc() }; } else { @@ -1267,14 +1296,15 @@ 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, + Optional FirstTrailingArgumentAt, bool Implicit, Type Ty) : Expr(ExprKind::Tuple, Implicit, Ty), - LParenLoc(LParenLoc), RParenLoc(RParenLoc) { - Bits.TupleExpr.HasTrailingClosure = HasTrailingClosure; + LParenLoc(LParenLoc), RParenLoc(RParenLoc), + FirstTrailingArgumentAt(FirstTrailingArgumentAt) { Bits.TupleExpr.HasElementNames = !ElementNames.empty(); Bits.TupleExpr.HasElementNameLocations = !ElementNameLocs.empty(); Bits.TupleExpr.NumElements = SubExprs.size(); @@ -1303,11 +1333,27 @@ 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, FirstTrailingArgumentAt, Implicit, Ty); +} + +TupleExpr *TupleExpr::create(ASTContext &ctx, + SourceLoc LParenLoc, + SourceLoc RParenLoc, + ArrayRef SubExprs, + ArrayRef ElementNames, + ArrayRef ElementNameLocs, + Optional FirstTrailingArgumentAt, bool Implicit, Type Ty) { assert(!Ty || isa(Ty.getPointer())); auto hasNonEmptyIdentifier = [](ArrayRef Ids) -> bool { @@ -1327,24 +1373,24 @@ 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, + 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, {}, {}, {}, + /*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, {}, + /*FirstTrailingArgumentAt=*/None, /*Implicit=*/true, Type()); } - ArrayExpr *ArrayExpr::create(ASTContext &C, SourceLoc LBracketLoc, ArrayRef Elements, ArrayRef CommaLocs, @@ -1455,7 +1501,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, @@ -1472,7 +1518,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, @@ -1486,7 +1532,7 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, ArrayRef indexArgLabels, ArrayRef indexArgLabelLocs, SourceLoc rSquareLoc, - Expr *trailingClosure, + ArrayRef trailingClosures, ConcreteDeclRef decl, bool implicit, AccessSemantics semantics) { @@ -1494,17 +1540,16 @@ SubscriptExpr *SubscriptExpr::create(ASTContext &ctx, Expr *base, SmallVector indexArgLabelLocsScratch; Expr *index = packSingleArgument(ctx, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, - trailingClosure, implicit, + trailingClosures, implicit, 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, indexArgLabelLocs, - trailingClosure != nullptr, + trailingClosures.size() == 1, decl, implicit, semantics); } @@ -1519,7 +1564,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); } @@ -1537,7 +1582,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, @@ -1557,7 +1602,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, @@ -1565,7 +1610,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, @@ -1582,23 +1627,20 @@ 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, - argLabelsScratch, - argLabelLocsScratch); + Expr *arg = packSingleArgument(ctx, lParenLoc, args, argLabels, argLabelLocs, + rParenLoc, trailingClosures, implicit, + 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, argLabels, argLabelLocs, - trailingClosure != nullptr, + trailingClosures.size() == 1, implicit); } @@ -1633,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, @@ -1643,13 +1692,14 @@ CallExpr::CallExpr(Expr *fn, Expr *arg, bool Implicit, 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 hasTrailingClosure, + bool implicit, Type type, llvm::function_ref getType) { SmallVector argLabelsScratch; SmallVector argLabelLocsScratch; @@ -1663,7 +1713,7 @@ 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, @@ -1674,22 +1724,22 @@ 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()); + trailingClosures.size() == 1, Type()); } Expr *CallExpr::getDirectCallee() const { @@ -2158,14 +2208,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, @@ -2182,17 +2232,15 @@ 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, - indexArgLabelsScratch, - indexArgLabelLocsScratch); - return forUnresolvedSubscriptWithPrebuiltIndexExpr(ctx, index, - indexArgLabels, - lSquareLoc); + Expr *index = packSingleArgument( + ctx, lSquareLoc, indexArgs, indexArgLabels, indexArgLabelLocs, rSquareLoc, + trailingClosures, /*implicit*/ false, + indexArgLabelsScratch, indexArgLabelLocsScratch); + return forUnresolvedSubscriptWithPrebuiltIndexExpr(ctx, index, indexArgLabels, + lSquareLoc); } KeyPathExpr::Component::Component(ASTContext *ctxForCopyingLabels, diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 0919eb622376f..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; @@ -869,7 +872,9 @@ void CodeCompletionResultBuilder::addCallParameter(Identifier Name, bool IsVarArg, bool IsInOut, bool IsIUO, - bool isAutoClosure) { + bool isAutoClosure, + bool useUnderscoreLabel, + bool isLabeledTrailingClosure) { CurrentNestingLevel++; using ChunkKind = CodeCompletionString::Chunk::ChunkKind; @@ -910,6 +915,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; @@ -961,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; @@ -970,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) @@ -1331,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: @@ -1364,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; @@ -1440,6 +1495,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { bool ShouldCompleteCallPatternAfterParen = true; bool PreferFunctionReferencesToCalls = false; bool AttTargetIsIndependent = false; + bool IsAtStartOfLine = false; Optional AttTargetDK; Optional ParentStmtKind; @@ -1566,6 +1622,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; @@ -2512,7 +2571,9 @@ 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, + /*isLabeledTrailingClosure=*/false); modifiedBuilder = true; NeedComma = true; @@ -4148,19 +4209,25 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { } void addCallArgumentCompletionResults( - ArrayRef Args) { + ArrayRef ParamInfos, + bool isLabeledTrailingClosure = false) { 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, {}); Builder.addCallParameter(Arg->getLabel(), Identifier(), Arg->getPlainType(), ContextType, Arg->isVariadic(), Arg->isInOut(), - /*isIUO=*/false, Arg->isAutoClosure()); + /*isIUO=*/false, Arg->isAutoClosure(), + /*useUnderscoreLabel=*/true, + isLabeledTrailingClosure); auto Ty = Arg->getPlainType(); if (Arg->isInOut()) { Ty = InOutType::get(Ty); @@ -5142,6 +5209,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; @@ -5324,6 +5399,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: @@ -5867,9 +5943,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) { @@ -5880,6 +5961,82 @@ 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, /*isLabeledTrailingClosure=*/true); + 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. + 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; + } + case CompletionKind::ReturnStmtExpr : { SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); Lookup.setExpectedTypes(getReturnTypeFromContext(CurDeclContext), diff --git a/lib/IDE/CodeCompletionResultBuilder.h b/lib/IDE/CodeCompletionResultBuilder.h index 67c19da19d871..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 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); + /*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/ExprContextAnalysis.cpp b/lib/IDE/ExprContextAnalysis.cpp index 4bfe04bc73517..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(), @@ -571,8 +576,9 @@ class ExprContextAnalyzer { // Results populated by Analyze() SmallVectorImpl &PossibleTypes; - SmallVectorImpl &PossibleParams; + SmallVectorImpl &PossibleParams; SmallVectorImpl &PossibleCallees; + Expr *&AnalyzedExpr; bool &singleExpressionBody; void recordPossibleType(Type ty) { @@ -582,8 +588,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. @@ -607,6 +613,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. @@ -616,6 +623,8 @@ class ExprContextAnalyzer { return false; // 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) || @@ -638,31 +647,40 @@ class ExprContextAnalyzer { paramList = nullptr; } 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 (paramList && paramList->get(Position)->isDefaultArgument()) + if (paramType.hasLabel() && MayNeedName) { + bool isDefaulted = paramList && + paramList->get(Pos)->isDefaultArgument(); + if (seenArgs.insert({paramType.getLabel(), ty.getPointer()}).second) + recordPossibleParam(¶mType, !isDefaulted); + if (isDefaulted) continue; } else { auto argTy = ty; - if (Param.isInOut()) + if (paramType.isInOut()) argTy = InOutType::get(argTy); if (seenTypes.insert(argTy.getPointer()).second) recordPossibleType(argTy); } 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(); } void analyzeExpr(Expr *Parent) { + AnalyzedExpr = Parent; switch (Parent->getKind()) { case ExprKind::Call: case ExprKind::Subscript: @@ -923,12 +941,13 @@ class ExprContextAnalyzer { public: ExprContextAnalyzer( DeclContext *DC, Expr *ParsedExpr, SmallVectorImpl &PossibleTypes, - SmallVectorImpl &PossibleArgs, + 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() { @@ -942,8 +961,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. @@ -1033,7 +1056,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 bab0306057b9a..039d28c60643d 100644 --- a/lib/IDE/ExprContextAnalysis.h +++ b/lib/IDE/ExprContextAnalysis.h @@ -50,12 +50,30 @@ 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; + Expr *AnalyzedExpr = nullptr; bool singleExpressionBody = false; public: @@ -73,7 +91,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; } @@ -82,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/lib/IDE/Formatting.cpp b/lib/IDE/Formatting.cpp index be632aced8e39..f86117f64ac13 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 { @@ -381,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); @@ -626,8 +639,7 @@ class RangeWalker: protected ASTWalker { } else { Arg = cast(E)->getIndex(); } - ClosureExpr *TC = nullptr; - CaptureListExpr *TCL = nullptr; + if (auto *PE = dyn_cast_or_null(Arg)) { if (isa(E)) { if (!handleSquares(PE->getLParenLoc(), PE->getRParenLoc(), ContextLoc)) @@ -637,10 +649,9 @@ class RangeWalker: protected ASTWalker { return Stop; } if (PE->hasTrailingClosure()) { - if (auto *Last = PE->getSubExpr()) { - TC = dyn_cast(Last); - TCL = dyn_cast(Last); - } + 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)) { @@ -650,17 +661,12 @@ 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->hasAnyTrailingClosures()) { + SourceRange Range(TE->getTrailingElements().front()->getStartLoc(), + TE->getEndLoc()); + handleImplicitRange(Range, ContextLoc); } } - if (TC && !handleBraceStmt(TC->getBody(), ContextLoc)) - return Stop; - if (TCL && !handleBraceStmt(TCL->getClosureBody()->getBody(), ContextLoc)) - return Stop; } return Continue; } @@ -719,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()); @@ -2316,35 +2332,48 @@ 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)) { + 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 (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); + 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) + }; + } + } + } } } @@ -2485,9 +2514,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)) 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/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/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 359e5c8899177..99b540e8854c8 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,10 @@ 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..0b3252d489c60 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, - SyntaxKind::TupleExprElementList); + 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; @@ -1218,16 +1218,19 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, leadingTriviaLoc(), *SyntaxContext)); } - ParserResult closure = - parseTrailingClosure(callee->getSourceRange()); - if (closure.isNull()) + SmallVector trailingClosures; + auto trailingResult = + parseTrailingClosures(isExprBasic, 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 @@ -1603,14 +1606,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 +1621,7 @@ ParserResult Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) { UnresolvedMemberExpr::create(Context, DotLoc, NameLoc, Name, lParenLoc, args, argLabels, argLabelLocs, rParenLoc, - trailingClosure, + trailingClosures, /*implicit=*/false)); } @@ -1631,18 +1634,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(isExprBasic, 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. @@ -3006,7 +3010,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 +3020,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 +3056,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()) { @@ -3136,19 +3138,41 @@ 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; + status |= + parseTrailingClosures(isExprBasic, SourceRange(leftLoc, rightLoc), + trailingClosures); + return status; +} - // Record the trailing closure. - trailingClosure = closure.get(); +static bool isStartOfLabelledTrailingClosure(Parser &P) { + // 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; - return status; + // Do some tentative parsing to distinguish `label: { ... }` and + // `label: switch x { ... }`. + Parser::BacktrackingScope backtrack(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; + return false; } -ParserResult Parser::parseTrailingClosure(SourceRange calleeRange) { +ParserStatus +Parser::parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, + SmallVectorImpl &closures) { SourceLoc braceLoc = Tok.getLoc(); // Record the line numbers for the diagnostics below. @@ -3157,19 +3181,25 @@ ParserResult Parser::parseTrailingClosure(SourceRange calleeRange) { auto origLine = SourceMgr.getLineNumber(calleeRange.End); auto braceLine = SourceMgr.getLineNumber(braceLoc); + ParserStatus result; + // Parse the closure. ParserResult closure = parseExprClosure(); if (closure.isNull()) return makeParserError(); + result |= closure; + + 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 // call, and will be diagnosed later only if the call fails to typecheck. 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()); + + auto *CE = dyn_cast(closures[0].ClosureExpr); if (CE && CE->hasAnonymousClosureVars() && CE->getParameters()->size() == 0) { diagnose(braceLoc, diag::brace_stmt_suggest_do) @@ -3177,9 +3207,60 @@ ParserResult Parser::parseTrailingClosure(SourceRange calleeRange) { } } - return closure; + // Parse labeled trailing closures. + 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; + 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"); + // Swallow code completion token after the label. + // FIXME: Closure literal completion. + consumeToken(tok::code_complete); + return makeParserCodeCompletionStatus(); + } + if (closure.isNull()) + return makeParserError(); + + result |= closure; + closures.push_back({label, labelLoc, closure.get()}); + + // Don't diagnose whitespace gaps before labelled closures. + } + SyntaxContext->collectNodesInPlace( + SyntaxKind::MultipleTrailingClosureElementList); + + return result; } - + /// Parse an object literal expression. /// /// expr-literal: @@ -3201,14 +3282,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 +3299,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,14 +3324,14 @@ 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, - SyntaxKind::TupleExprElementList); + args, argLabels, argLabelLocs, RParenLoc, + trailingClosures, SyntaxKind::TupleExprElementList); if (status.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (status.isError()) @@ -3331,20 +3412,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; + 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(), - /*trailingClosure=*/nullptr, + rParenLoc, + /*trailingClosures=*/{}, /*implicit=*/false)); CodeCompletion->completePostfixExprParen(fn.get(), CCE); // Eat the code completion token because we handled it. @@ -3354,25 +3440,20 @@ Parser::parseExprCallSuffix(ParserResult fn, bool isExprBasic) { } // Parse the argument list. - SourceLoc lParenLoc, rParenLoc; - SmallVector args; - SmallVector argLabels; - SmallVector argLabelLocs; - Expr *trailingClosure; - 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..8b29e0e10d365 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..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; - 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..09aba51a554a5 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; @@ -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; diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1d22f82b49344..2a2673dfbce2b 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2001,14 +2001,16 @@ namespace { arguments.push_back(SE->getIndex()->getSemanticsProvidingExpr()); } - Expr *trailingClosure = nullptr; - if (SE->hasTrailingClosure()) - trailingClosure = arguments.back(); + SmallVector trailingClosures; + if (SE->hasTrailingClosure()) { + auto *closure = arguments.back(); + trailingClosures.push_back({closure}); + } 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()); } @@ -5513,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..314597aba46b9 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. @@ -429,58 +432,123 @@ matchCallArguments(SmallVectorImpl &args, haveUnfulfilledParams = true; }; - // If we have a trailing closure, it maps to the last parameter. - if (hasTrailingClosure && numParams > 0) { - unsigned lastParamIdx = numParams - 1; - bool lastAcceptsTrailingClosure = - acceptsTrailingClosure(params[lastParamIdx]); + // 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 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 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; + } + } + + // 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; } - bool isExtraClosure = false; - // If there is no suitable last parameter to accept the trailing closure, + // 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) { + // Try to use a specialized diagnostic for an extra closure. + 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; + } + + 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; + } } - // 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); + if (unlabeledParamIdx) { + // Claim the parameter/argument pair. + claim(params[*unlabeledParamIdx].getLabel(), unlabeledArgIdx, + /*ignoreNameClash=*/true); + parameterBindings[*unlabeledParamIdx].push_back(unlabeledArgIdx); + } } { @@ -1037,7 +1105,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 +8056,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 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..f79cf4b4d8113 100644 --- a/lib/Sema/TypeCheckPropertyWrapper.cpp +++ b/lib/Sema/TypeCheckPropertyWrapper.cpp @@ -747,10 +747,10 @@ 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, - nullptr, /*implicit=*/true); + auto *init = + CallExpr::create(ctx, typeExpr, startLoc, {initializer}, {argName}, + {initializer->getStartLoc()}, endLoc, + /*trailingClosures=*/{}, /*implicit=*/true); initializer = init; if (!innermostInit) @@ -783,9 +783,9 @@ 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, nullptr, /*implicit=*/true); + auto *init = CallExpr::create(ctx, typeExpr, startLoc, elements, + elementNames, elementLocs, endLoc, + /*trailingClosures=*/{}, /*implicit=*/true); initializer = init; if (!innermostInit) 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 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 { diff --git a/test/IDE/complete_multiple_trailingclosure.swift b/test/IDE/complete_multiple_trailingclosure.swift new file mode 100644 index 0000000000000..5fa03207a0169 --- /dev/null +++ b/test/IDE/complete_multiple_trailingclosure.swift @@ -0,0 +1,186 @@ +// 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() + { 1 } #^GLOBALFUNC_SAMELINE^# + #^GLOBALFUNC_NEWLINE^# +// GLOBALFUNC_SAMELINE: Begin completions, 1 items +// 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 in|}#}[#() -> String#]; +// GLOBALFUNC_NEWLINE: End completions + + globalFunc1() + { 1 } fn2: #^GLOBALFUNC_AFTERLABEL^# +// FIXME: Closure literal completion. +// 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) -> SimpleEnum {} + func method1(fn1: () -> Int, fn2: Int = nil) -> SimpleEnum {} +} +func testMethod(value: MyStruct) { + value.method1 { + } #^METHOD_SAMELINE^# + #^METHOD_NEWLINE^# +// METHOD_SAMELINE: Begin completions, 4 items +// 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 in|}#}[#(() -> 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 testStructMethod() {} +} + +func testOverloadedInit() { + TestStruct { + 1 + } #^INIT_OVERLOADED_SAMELINE^# + #^INIT_OVERLOADED_NEWLINE^# + +// INIT_OVERLOADED_SAMELINE: Begin completions, 4 items +// 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 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; +// 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 testStructMethod() {} +} +func testOptionalInit() { + TestStruct2 { + 2 + } #^INIT_OPTIONAL_SAMELINE^# + #^INIT_OPTIONAL_NEWLINE^# + +// INIT_OPTIONAL_SAMELINE: Begin completions, 4 items +// 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 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; +// 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 testStructMethod() {} +} +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 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 in|}#}[#() -> 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 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 in|}#}[#() -> 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: 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 +// 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 +} 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/Parse/multiple_trailing_closures.swift b/test/Parse/multiple_trailing_closures.swift new file mode 100644 index 0000000000000..0fb3976f9d493 --- /dev/null +++ b/test/Parse/multiple_trailing_closures.swift @@ -0,0 +1,127 @@ +// RUN: %target-typecheck-verify-swift + +func foo(a: () -> T, b: () -> U) {} + +foo { 42 } +b: { "" } + +foo { 42 } b: { "" } + +func when(_ condition: @autoclosure () -> Bool, + `then` trueBranch: () -> T, + `else` falseBranch: () -> T) -> T { + return condition() ? trueBranch() : falseBranch() +} + +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 { + $0 = $0 + 1 +} + +let _: S = .foo {} b: { $0 = $0 + 1 } + +func bar(_ s: S) { + _ = s[] { + 42 + } + + _ = s[] { + 21 + } v: { + 42 + } + + _ = s[true] { + 42 + } + + _ = s[true] { + 21 + } v: { + 42 + } +} + +func multiple_trailing_with_defaults( + duration: Int, + animations: (() -> Void)? = nil, + completion: (() -> Void)? = nil) {} + +multiple_trailing_with_defaults(duration: 42) {} + +multiple_trailing_with_defaults(duration: 42) {} completion: {} + +func test_multiple_trailing_syntax_without_labels() { + func fn(f: () -> Void, g: () -> Void) {} + + fn {} g: {} // Ok + + fn {} _: {} // expected-error {{extra argument in call}} + + fn {} g: <#T##() -> Void#> // expected-error {{editor placeholder in source file}} + + func multiple(_: () -> Void, _: () -> Void) {} + + multiple {} _: { } + + 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 + {} + _: {} + + // FIXME: not a good diagnostic + mixed_args_1 + {} // expected-error {{extra argument in call}} expected-error {{missing argument for parameter #2 in call}} + a: {} + + mixed_args_2 + {} + a: {} + _: {} + + // FIXME: not a good diagnostic + mixed_args_2 + {} // expected-error {{missing argument for parameter #1 in call}} + _: {} + + // FIXME: not a good diagnostic + mixed_args_2 + {} // expected-error {{extra argument in call}} expected-error {{missing argument for parameter 'a' in call}} + _: {} + _: {} +} + +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 } 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/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 1313be88fd642..ab5f00fcbec62 100644 --- a/test/SourceKit/CodeExpand/code-expand.swift +++ b/test/SourceKit/CodeExpand/code-expand.swift @@ -68,24 +68,40 @@ func f1() { // CHECK-NEXT: } func f1() { - bar(<#T##d: () -> ()##() -> ()#>, <#T##d: () -> ()##() -> ()#>) + bar(<#T##__skip__: () -> ()##() -> ()#>, <#T##d: () -> ()##() -> ()#>) } -// CHECK: bar({ -// CHECK-NEXT: <#code#> -// CHECK-NEXT: }, { +// CHECK: bar { // CHECK-NEXT: <#code#> -// CHECK-NEXT: }) +// CHECK-NEXT: } _: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } + +func f1() { + bar(<#T##d: () -> ()##() -> ()#>, <#T##d: () -> ()##() -> ()#>) +} +// CHECK: bar { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } _: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } +func f1() { + bar(a : <#T##__skip__: () -> ()##() -> ()#>, b : <#T##d: () -> ()##() -> ()#>) +} +// CHECK: bar { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } b: { +// CHECK-NEXT: <#code#> +// CHECK-NEXT: } func f1() { bar(a : <#T##d: () -> ()##() -> ()#>, b : <#T##d: () -> ()##() -> ()#>) } -// CHECK: bar(a : { +// CHECK: bar { // CHECK-NEXT: <#code#> -// CHECK-NEXT: }, b : { +// CHECK-NEXT: } b: { // CHECK-NEXT: <#code#> -// CHECK-NEXT: }) - +// CHECK-NEXT: } func f1() { bar(a : {}}, <#T##d: () -> ()##() -> ()#>) 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/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index 253bd6948c54e..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,14 +257,22 @@ func closure() () { foo() foo() {} + foo {} + foo { } + arg2: {} foo {} foo.bar() foo.bar() {} + foo.bar() {} + arg2: {} + in: {} foo.bar {} foo[] foo[1] foo[] {} - foo[1] {} + 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 55526163f7615..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,21 @@ func postfix() { foo() foo() {} foo {} + foo { } + arg2: {} + foo {} foo.bar() foo.bar() {} + foo.bar() {} + arg2: {} + in: {} foo.bar {} foo[] foo[1] foo[] {} foo[1] {} + foo[1] {} + arg2: {} foo[1][2,x:3] foo?++.bar!(baz).self foo().0 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 diff --git a/test/swift-indent/multiple-trailing-closures.swift b/test/swift-indent/multiple-trailing-closures.swift new file mode 100644 index 0000000000000..908e1864b2874 --- /dev/null +++ b/test/swift-indent/multiple-trailing-closures.swift @@ -0,0 +1,227 @@ +// RUN: %swift-indent %s >%t.response +// RUN: diff -u %s %t.response + +foo(c: 12, d: 34) { + print("foo") +} + +foo(c: 12, d: 34) +{ + print("foo") +} + + +someCall(c: 12, d: 34) { + print("foo") +} e: { + print("foo") +} f: { + print("bar") +} + +someCall(c: 12, d: 34) { + print("foo") +} +e: { + print("foo") +} +f: { + print("bar") +} + +someCall(c: 12, d: 34) +{ + print("foo") +} +e: +{ + print("foo") +} +f: +{ + print("bar") +} + +someCall(c: 12, d: 34) { print("foo") } + e: { print("foo") } + f: { + print("bar") + } + +someCall(c: 12, + d: 34) { print("foo") } + e: { print("bar") } + .other { print("foo") } + a: { print("foo") } + .next() { + print("bar") + } + +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") +} + +someCall { + print("foo") +} +e: { + print("foo") +} +f: { + print("bar") +} + +someCall +{ + print("foo") +} +e: +{ + print("foo") +} +f: +{ + print("bar") +} + +someCall { print("foo") } + e: { print("foo") } + f: { + print("bar") + } + +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") + } + e +} + +func containIncomplete2() { + someCall { + print("foo") + } + e: +} + +func containIncomplete3() { + someCall + { + print("foo") + } + e +} + +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") +} + +someSub[c: 12, + d: 34] { + print("foo") +} b: { + print("bar") +} + +someCall(c: 12, d: 34) { print("foo") } + // comment + b: { + print("bar") + } 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(); 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; 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 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 8f9835970008f..e746211f7bead 100644 --- a/utils/gyb_syntax_support/ExprNodes.py +++ b/utils/gyb_syntax_support/ExprNodes.py @@ -394,6 +394,21 @@ 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'), + # call-expr -> expr '(' call-argument-list ')' closure-expr? # | expr closure-expr Node('FunctionCallExpr', kind='Expr', @@ -407,6 +422,10 @@ is_optional=True), 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? @@ -419,6 +438,10 @@ Child('RightBracket', kind='RightSquareBracketToken'), 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 5647189cd397f..eeea90df395eb 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -246,6 +246,8 @@ 'QualifiedDeclName': 242, 'CatchItem': 243, 'CatchItemList': 244, + 'MultipleTrailingClosureElementList': 245, + 'MultipleTrailingClosureElement': 246, }