diff --git a/include/swift/IDE/CompletionLookup.h b/include/swift/IDE/CompletionLookup.h index 288b912bbe49e..9781f5a40ec46 100644 --- a/include/swift/IDE/CompletionLookup.h +++ b/include/swift/IDE/CompletionLookup.h @@ -519,7 +519,14 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void getPostfixKeywordCompletions(Type ExprType, Expr *ParsedExpr); - void getValueExprCompletions(Type ExprType, ValueDecl *VD = nullptr); + /// Add code completion results after an expression of type \p ExprType. + /// This includes members as well as call patterns if \p ExprType is a + /// function type. + /// If \p IsDeclUnapplied is \c true, we are completing after a refernce to + /// \p VD that hasn't been called yet. Thus, \p VD has type \p ExprType and we + /// can use \p VD to enrich call pattern completions of \p ExprType. + void getValueExprCompletions(Type ExprType, ValueDecl *VD = nullptr, + bool IsDeclUnapplied = false); void collectOperators(SmallVectorImpl &results); @@ -529,7 +536,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void tryPostfixOperator(Expr *expr, PostfixOperatorDecl *op); - void addAssignmentOperator(Type RHSType, Type resultType); + void addAssignmentOperator(Type RHSType); void addInfixOperatorCompletion(OperatorDecl *op, Type resultType, Type RHSType); diff --git a/include/swift/IDE/PostfixCompletion.h b/include/swift/IDE/PostfixCompletion.h index f097f9f4d6b36..aa11705683c78 100644 --- a/include/swift/IDE/PostfixCompletion.h +++ b/include/swift/IDE/PostfixCompletion.h @@ -20,16 +20,41 @@ namespace swift { namespace ide { -/// Used to collect and store information needed to perform member completion -/// (\c CompletionKind::DotExpr ) from the solutions formed during expression -/// type-checking. +/// Used to collect and store information needed to perform postfix completion +/// (either .#^COMPLETE^# or #^COMPLETE^#). class PostfixCompletionCallback : public TypeCheckCompletionCallback { struct Result { + /// The type that we are completing on. Will never be null. Type BaseTy; + + /// The decl that we are completing on. Is \c nullptr if we are completing + /// on an expression. ValueDecl *BaseDecl; + + /// Whether \c BaseDecl refers to a function that has not been called yet. + /// In such cases, we know that \p BaseTy is the type of \p BaseDecl and we + /// can use \p BaseDecl for more detailed call pattern completions. + bool IsBaseDeclUnapplied; + + /// If the expression we are completing on statically refers to a metatype, + /// that is if it's something like 'MyType'. In such cases we want to offer + /// constructor call pattern completions and don't want to suggeste + /// operators that work on metatypes. + bool BaseIsStaticMetaType; + + /// The types that the completion is expected to produce. SmallVector ExpectedTypes; + + /// Whether results that produce 'Void' should be disfavored. This happens + /// if the context is requiring a value. Once a completion produces 'Void', + /// we know that we can't retrieve a value from it anymore. bool ExpectsNonVoid; - bool BaseIsStaticMetaType; + + /// If the code completion expression occurs as a single statement in a + /// single-expression closure. In such cases we don't want to disfavor + /// results that produce 'Void' because the user might intend to make the + /// closure a multi-statment closure, in which case this expression is no + /// longer implicitly returned. bool IsImplicitSingleExpressionReturn; /// Whether the surrounding context is async and thus calling async @@ -40,13 +65,24 @@ class PostfixCompletionCallback : public TypeCheckCompletionCallback { /// haven't been saved to the AST. llvm::DenseMap ClosureActorIsolations; + + /// Checks whether this result has the same \c BaseTy and \c BaseDecl as + /// \p Other and if the two can thus be merged to be one value lookup in + /// \c deliverResults. + bool canBeMergedWith(const Result &Other, DeclContext &DC) const; + + /// Merge this result with \p Other. Assumes that they can be merged. + void merge(const Result &Other, DeclContext &DC); }; CodeCompletionExpr *CompletionExpr; DeclContext *DC; SmallVector Results; - llvm::DenseMap, size_t> BaseToSolutionIdx; + + /// Add a result to \c Results, merging it with an existing result, if + /// possible. + void addResult(const Result &Res); void sawSolutionImpl(const constraints::Solution &solution) override; @@ -58,8 +94,18 @@ class PostfixCompletionCallback : public TypeCheckCompletionCallback { /// \c sawSolution for each solution formed. void fallbackTypeCheck(DeclContext *DC) override; - void deliverResults(Expr *BaseExpr, DeclContext *DC, SourceLoc DotLoc, - bool IsInSelector, CodeCompletionContext &CompletionCtx, + /// Deliver code completion results that were discoverd by \c sawSolution to + /// \p Consumer. + /// \param DotLoc If we are completing after a dot, the location of the dot, + /// otherwise an invalid SourceLoc. + /// \param IsInSelector Whether we are completing in an Objective-C selector. + /// \param IncludeOperators If operators should be suggested. Assumes that + /// \p DotLoc is invalid + /// \param HasLeadingSpace Whether there is a space separating the exiting + /// expression and the code completion token. + void deliverResults(SourceLoc DotLoc, bool IsInSelector, + bool IncludeOperators, bool HasLeadingSpace, + CodeCompletionContext &CompletionCtx, CodeCompletionConsumer &Consumer); }; diff --git a/include/swift/IDE/UnresolvedMemberCompletion.h b/include/swift/IDE/UnresolvedMemberCompletion.h index e39fc4f30034e..364234595c26e 100644 --- a/include/swift/IDE/UnresolvedMemberCompletion.h +++ b/include/swift/IDE/UnresolvedMemberCompletion.h @@ -32,6 +32,14 @@ class UnresolvedMemberTypeCheckCompletionCallback /// Whether the surrounding context is async and thus calling async /// functions is supported. bool IsInAsyncContext; + + /// Checks whether this result has the same \c BaseTy and \c BaseDecl as + /// \p Other and if the two can thus be merged to be one value lookup in + /// \c deliverResults. + bool canBeMergedWith(const Result &Other, DeclContext &DC) const; + + /// Merge this result with \p Other. Assumes that they can be merged. + void merge(const Result &Other, DeclContext &DC); }; CodeCompletionExpr *CompletionExpr; @@ -40,6 +48,10 @@ class UnresolvedMemberTypeCheckCompletionCallback SmallVector ExprResults; SmallVector EnumPatternTypes; + /// Add a result to \c Results, merging it with an existing result, if + /// possible. + void addExprResult(const Result &Res); + void sawSolutionImpl(const constraints::Solution &solution) override; public: diff --git a/include/swift/Parse/IDEInspectionCallbacks.h b/include/swift/Parse/IDEInspectionCallbacks.h index 4b370c6b779dd..e3921e01fbae5 100644 --- a/include/swift/Parse/IDEInspectionCallbacks.h +++ b/include/swift/Parse/IDEInspectionCallbacks.h @@ -147,8 +147,9 @@ class CodeCompletionCallbacks { /// Complete the \c in keyword in a for-each loop. virtual void completeForEachInKeyword(){}; - /// Complete a given expr-postfix. - virtual void completePostfixExpr(Expr *E, bool hasSpace) {}; + /// Complete a expr-postfix. The \c CodeCompletionExpr has the expression it + /// is completing after set as its base. + virtual void completePostfixExpr(CodeCompletionExpr *E, bool hasSpace){}; /// Complete a given expr-postfix, given that there is a following /// left parenthesis. diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index b5c416693a8d3..e1d79e0f26d7f 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -472,6 +472,9 @@ class TypeVariableType::Implementation { /// Determine whether this type variable represents a closure type. bool isClosureType() const; + /// Determine whether this type variable represents a type of tap expression. + bool isTapType() const; + /// Determine whether this type variable represents one of the /// parameter types associated with a closure. bool isClosureParameterType() const; @@ -935,6 +938,9 @@ enum ScoreKind: unsigned int { /// A reference to an @unavailable declaration. SK_Unavailable, /// A reference to an async function in a synchronous context. + /// + /// \note Any score kind after this is considered a conversion that doesn't + /// require fixing the source and will be ignored during code completion. SK_AsyncInSyncMismatch, /// Synchronous function in an asynchronous context or a conversion of /// a synchronous function to an asynchronous one. @@ -3951,6 +3957,20 @@ class ConstraintSystem { /// \returns `true` if pack expansion has been resolved, `false` otherwise. bool resolvePackExpansion(TypeVariableType *typeVar, Type contextualType); + /// Bind tap expression to the given contextual type and generate + /// constraints for its body. + /// + /// \param typeVar The type variable representing the tap expression. + /// \param contextualType The contextual type this tap expression + /// would be bound to. + /// \param locator The locator associated with contextual type. + /// + /// \returns `true` if it was possible to generate constraints for + /// the body and assign fixed type to the tap expression, `false` + /// otherwise. + bool resolveTapBody(TypeVariableType *typeVar, Type contextualType, + ConstraintLocatorBuilder locator); + /// Assign a fixed type to the given type variable. /// /// \param typeVar The type variable to bind. @@ -4354,6 +4374,14 @@ class ConstraintSystem { FreeTypeVariableBinding allowFreeTypeVariables = FreeTypeVariableBinding::Disallow); + /// Generate constraints for the body of the given tap expression. + /// + /// \param tap the tap expression + /// + /// \returns \c true if constraint generation failed, \c false otherwise + [[nodiscard]] + bool generateConstraints(TapExpr *tap); + /// Generate constraints for the body of the given function or closure. /// /// \param fn The function or closure expression @@ -5203,7 +5231,8 @@ class ConstraintSystem { public: /// Increase the score of the given kind for the current (partial) solution /// along the. - void increaseScore(ScoreKind kind, unsigned value = 1); + void increaseScore(ScoreKind kind, ConstraintLocatorBuilder Locator, + unsigned value = 1); /// Determine whether this solution is guaranteed to be worse than the best /// solution found so far. @@ -5285,6 +5314,22 @@ class ConstraintSystem { llvm::Optional(SyntacticElementTarget)> rewriteTarget); + /// Apply the given solution to the given tap expression. + /// + /// \param solution The solution to apply. + /// \param tapExpr The tap expression to which the solution is being applied. + /// \param currentDC The declaration context in which transformations + /// will be applied. + /// \param rewriteTarget Function that performs a rewrite of any + /// solution application target within the context. + /// + /// \returns true if solution cannot be applied. + bool applySolutionToBody(Solution &solution, TapExpr *tapExpr, + DeclContext *¤tDC, + std::function( + SyntacticElementTarget)> + rewriteTarget); + /// Reorder the disjunctive clauses for a given expression to /// increase the likelihood that a favored constraint will be successfully /// resolved before any others. diff --git a/lib/AST/ASTNode.cpp b/lib/AST/ASTNode.cpp index 9a33c040d1734..44130ed4849bc 100644 --- a/lib/AST/ASTNode.cpp +++ b/lib/AST/ASTNode.cpp @@ -41,6 +41,7 @@ SourceRange ASTNode::getSourceRange() const { if (const auto *I = this->dyn_cast()) { return I->getSourceRange(); } + assert(!isNull() && "Null ASTNode doesn't have a source range"); llvm_unreachable("unsupported AST node"); } diff --git a/lib/IDE/ArgumentCompletion.cpp b/lib/IDE/ArgumentCompletion.cpp index 82cb460200a71..e8ceaaf5031b2 100644 --- a/lib/IDE/ArgumentCompletion.cpp +++ b/lib/IDE/ArgumentCompletion.cpp @@ -236,7 +236,18 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) { for (auto Idx : range(0, ParamsToPass.size())) { bool Optional = false; if (Info.ValueRef) { - if (const ParamDecl *DeclParam = getParameterAt(Info.ValueRef, Idx)) { + if (Info.ValueRef.getDecl()->isInstanceMember() && + !doesMemberRefApplyCurriedSelf(Info.BaseTy, + Info.ValueRef.getDecl())) { + // We are completing in an unapplied instance function, eg. + // struct TestStatic { + // func method() -> Void {} + // } + // TestStatic.method(#^STATIC^#) + // The 'self' parameter is never optional, so don't enter the check + // below (which always assumes that self has been applied). + } else if (const ParamDecl *DeclParam = + getParameterAt(Info.ValueRef, Idx)) { Optional |= DeclParam->isDefaultArgument(); Optional |= DeclParam->getType()->is(); } diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 2cc7a2477e979..5dfb622622e83 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -259,7 +259,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks, void completePostfixExprBeginning(CodeCompletionExpr *E) override; void completeForEachSequenceBeginning(CodeCompletionExpr *E) override; void completeForEachInKeyword() override; - void completePostfixExpr(Expr *E, bool hasSpace) override; + void completePostfixExpr(CodeCompletionExpr *E, bool hasSpace) override; void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) override; void completeExprKeyPath(KeyPathExpr *KPE, SourceLoc DotLoc) override; @@ -391,7 +391,8 @@ void CodeCompletionCallbacksImpl::completeForEachInKeyword() { CurDeclContext = P.CurDeclContext; } -void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E, bool hasSpace) { +void CodeCompletionCallbacksImpl::completePostfixExpr(CodeCompletionExpr *E, + bool hasSpace) { assert(P.Tok.is(tok::code_complete)); // Don't produce any results in an enum element. @@ -405,7 +406,8 @@ void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E, bool hasSpace) { CompleteExprSelectorContext = ParseExprSelectorContext; } - ParsedExpr = E; + ParsedExpr = E->getBase(); + CodeCompleteTokenExpr = E; CurDeclContext = P.CurDeclContext; } @@ -1469,6 +1471,7 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) { }; switch (Kind) { + case CompletionKind::PostfixExpr: case CompletionKind::DotExpr: { assert(CodeCompleteTokenExpr); assert(CurDeclContext); @@ -1478,9 +1481,10 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) { addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); - Expr *CheckedBase = CodeCompleteTokenExpr->getBase(); - Lookup.deliverResults(CheckedBase, CurDeclContext, DotLoc, - isInsideObjCSelector(), CompletionContext, Consumer); + bool IncludeOperators = (Kind == CompletionKind::PostfixExpr); + + Lookup.deliverResults(DotLoc, isInsideObjCSelector(), IncludeOperators, + HasSpace, CompletionContext, Consumer); return true; } case CompletionKind::UnresolvedMember: { @@ -1507,6 +1511,7 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) { Lookup.deliverResults(CurDeclContext, DotLoc, CompletionContext, Consumer); return true; } + case CompletionKind::PostfixExprParen: case CompletionKind::CallArg: { assert(CodeCompleteTokenExpr); assert(CurDeclContext); @@ -1692,65 +1697,11 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) { case CompletionKind::AfterPoundExpr: case CompletionKind::AccessorBeginning: case CompletionKind::CaseStmtBeginning: + case CompletionKind::PostfixExprParen: + case CompletionKind::PostfixExpr: llvm_unreachable("should be already handled"); return; - case CompletionKind::PostfixExpr: { - Lookup.setHaveLeadingSpace(HasSpace); - if (isDynamicLookup(*ExprType)) - Lookup.setIsDynamicLookup(); - Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); - /// We set the type of ParsedExpr explicitly above. But we don't want an - /// unresolved type in our AST when we type check again for operator - /// completions. Remove the type of the ParsedExpr and see if we can come up - /// with something more useful based on the the full sequence expression. - if (ParsedExpr->getType()->is()) { - ParsedExpr->setType(nullptr); - } - Lookup.getOperatorCompletions(ParsedExpr, leadingSequenceExprs); - Lookup.getPostfixKeywordCompletions(*ExprType, ParsedExpr); - break; - } - - case CompletionKind::PostfixExprParen: { - Lookup.setHaveLParen(true); - - ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); - - if (ShouldCompleteCallPatternAfterParen) { - ExprContextInfo ParentContextInfo(CurDeclContext, ParsedExpr); - Lookup.setExpectedTypes( - ParentContextInfo.getPossibleTypes(), - ParentContextInfo.isImplicitSingleExpressionReturn()); - if (!ContextInfo.getPossibleCallees().empty()) { - for (auto &typeAndDecl : ContextInfo.getPossibleCallees()) - Lookup.tryFunctionCallCompletions(typeAndDecl.Type, typeAndDecl.Decl, - typeAndDecl.SemanticContext); - } else if (ExprType && ((*ExprType)->is() || - (*ExprType)->is())) { - Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); - } - } else { - // Add argument labels, then fallthrough to get values. - Lookup.addCallArgumentCompletionResults(ContextInfo.getPossibleParams()); - } - - if (!Lookup.FoundFunctionCalls || - (Lookup.FoundFunctionCalls && - Lookup.FoundFunctionsWithoutFirstKeyword)) { - Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), - ContextInfo.isImplicitSingleExpressionReturn()); - Lookup.setHaveLParen(false); - - // Add any keywords that can be used in an argument expr position. - addSuperKeyword(CompletionContext.getResultSink(), CurDeclContext); - addExprKeywords(CompletionContext.getResultSink(), CurDeclContext); - - DoPostfixExprBeginning(); - } - break; - } - case CompletionKind::KeyPathExprObjC: { if (DotLoc.isValid()) Lookup.setHaveDot(DotLoc); @@ -1761,7 +1712,8 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) { if (isDynamicLookup(*ExprType)) Lookup.setIsDynamicLookup(); - Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); + Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl(), + /*IncludeFunctionCallCompletions=*/true); } else { SourceLoc Loc = P.Context.SourceMgr.getIDEInspectionTargetLoc(); Lookup.getValueCompletionsInDeclContext(Loc, KeyPathFilter, @@ -1986,7 +1938,8 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) { if (isDynamicLookup(resultTy)) Lookup.setIsDynamicLookup(); - Lookup.getValueExprCompletions(resultTy, /*VD=*/nullptr); + Lookup.getValueExprCompletions(resultTy, /*VD=*/nullptr, + /*IncludeFunctionCallCompletions=*/true); Lookup.getOperatorCompletions(analyzedExpr, leadingSequenceExprs); Lookup.getPostfixKeywordCompletions(resultTy, analyzedExpr); } diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index a6513136c4b1f..a3107512ca861 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -2307,6 +2307,8 @@ void CompletionLookup::getPostfixKeywordCompletions(Type ExprType, if (IsSuperRefExpr) return; + NeedLeadingDot = !HaveDot; + if (!ExprType->getAs()) { addKeyword(getTokenText(tok::kw_self), ExprType->getRValueType(), SemanticContextKind::CurrentNominal, @@ -2329,7 +2331,8 @@ void CompletionLookup::getPostfixKeywordCompletions(Type ExprType, } } -void CompletionLookup::getValueExprCompletions(Type ExprType, ValueDecl *VD) { +void CompletionLookup::getValueExprCompletions(Type ExprType, ValueDecl *VD, + bool IsDeclUnapplied) { Kind = LookupKind::ValueExpr; NeedLeadingDot = !HaveDot; @@ -2362,7 +2365,7 @@ void CompletionLookup::getValueExprCompletions(Type ExprType, ValueDecl *VD) { // Handle special cases bool isIUO = VD && VD->isImplicitlyUnwrappedOptional(); - if (tryFunctionCallCompletions(ExprType, VD)) + if (tryFunctionCallCompletions(ExprType, IsDeclUnapplied ? VD : nullptr)) return; if (tryModuleCompletions(ExprType, {CodeCompletionFilterFlag::Expr, CodeCompletionFilterFlag::Type})) @@ -2425,7 +2428,7 @@ void CompletionLookup::tryPostfixOperator(Expr *expr, PostfixOperatorDecl *op) { addPostfixOperatorCompletion(op, funcTy->getResult()); } -void CompletionLookup::addAssignmentOperator(Type RHSType, Type resultType) { +void CompletionLookup::addAssignmentOperator(Type RHSType) { CodeCompletionResultBuilder builder = makeResultBuilder( CodeCompletionResultKind::BuiltinOperator, SemanticContextKind::None); @@ -2435,12 +2438,11 @@ void CompletionLookup::addAssignmentOperator(Type RHSType, Type resultType) { builder.addWhitespace(" "); builder.addEqual(); builder.addWhitespace(" "); - assert(RHSType && resultType); + assert(RHSType); Type contextTy; if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) contextTy = typeContext->getDeclaredTypeInContext(); builder.addCallArgument(Identifier(), RHSType, contextTy); - addTypeAnnotation(builder, resultType); } void CompletionLookup::addInfixOperatorCompletion(OperatorDecl *op, @@ -2559,8 +2561,7 @@ void CompletionLookup::getOperatorCompletions( if (leadingSequence.empty() && LHS->getType() && LHS->getType()->hasLValueType()) { - addAssignmentOperator(LHS->getType()->getRValueType(), - CurrDeclContext->getASTContext().TheEmptyTupleType); + addAssignmentOperator(LHS->getType()->getRValueType()); } // FIXME: unify this with the ?.member completions. diff --git a/lib/IDE/ConformingMethodList.cpp b/lib/IDE/ConformingMethodList.cpp index ef1d355695116..e5420539e6e07 100644 --- a/lib/IDE/ConformingMethodList.cpp +++ b/lib/IDE/ConformingMethodList.cpp @@ -16,8 +16,10 @@ #include "swift/AST/GenericEnvironment.h" #include "swift/AST/NameLookup.h" #include "swift/AST/USRGeneration.h" +#include "swift/IDE/TypeCheckCompletionCallback.h" #include "swift/Parse/IDEInspectionCallbacks.h" #include "swift/Sema/IDETypeChecking.h" +#include "swift/Sema/ConstraintSystem.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" @@ -30,7 +32,7 @@ class ConformingMethodListCallbacks : public CodeCompletionCallbacks, ArrayRef ExpectedTypeNames; ConformingMethodListConsumer &Consumer; SourceLoc Loc; - Expr *ParsedExpr = nullptr; + CodeCompletionExpr *CCExpr = nullptr; DeclContext *CurDeclContext = nullptr; void getMatchingMethods(Type T, @@ -47,7 +49,7 @@ class ConformingMethodListCallbacks : public CodeCompletionCallbacks, // Only handle callbacks for suffix completions. // { void completeDotExpr(CodeCompletionExpr *E, SourceLoc DotLoc) override; - void completePostfixExpr(Expr *E, bool hasSpace) override; + void completePostfixExpr(CodeCompletionExpr *E, bool hasSpace) override; // } void doneParsing(SourceFile *SrcFile) override; @@ -56,34 +58,67 @@ class ConformingMethodListCallbacks : public CodeCompletionCallbacks, void ConformingMethodListCallbacks::completeDotExpr(CodeCompletionExpr *E, SourceLoc DotLoc) { CurDeclContext = P.CurDeclContext; - ParsedExpr = E->getBase(); + CCExpr = E; } -void ConformingMethodListCallbacks::completePostfixExpr(Expr *E, +void ConformingMethodListCallbacks::completePostfixExpr(CodeCompletionExpr *E, bool hasSpace) { CurDeclContext = P.CurDeclContext; - ParsedExpr = E; + CCExpr = E; } -void ConformingMethodListCallbacks::doneParsing(SourceFile *SrcFile) { - if (!ParsedExpr) - return; +class ConformingMethodListCallback : public TypeCheckCompletionCallback { +public: + struct Result { + Type Ty; - typeCheckContextAt(TypeCheckASTNodeAtLocContext::declContext(CurDeclContext), - ParsedExpr->getLoc()); + /// Types of variables that were determined in the solution that produced + /// this result. This in particular includes parameters of closures that + /// were type-checked with the code completion expression. + llvm::SmallDenseMap SolutionSpecificVarTypes; + }; +private: + CodeCompletionExpr *CCExpr; - Type T = ParsedExpr->getType(); + SmallVector Results; - // Type check the expression if needed. - if (!T || T->is()) { - ConcreteDeclRef ReferencedDecl = nullptr; - auto optT = getTypeOfCompletionContextExpr(P.Context, CurDeclContext, - CompletionTypeCheckKind::Normal, - ParsedExpr, ReferencedDecl); - if (!optT) + void sawSolutionImpl(const constraints::Solution &S) override { + if (!S.hasType(CCExpr->getBase())) { return; - T = *optT; + } + if (Type T = getTypeForCompletion(S, CCExpr->getBase())) { + llvm::SmallDenseMap SolutionSpecificVarTypes; + getSolutionSpecificVarTypes(S, SolutionSpecificVarTypes); + Results.push_back({T, SolutionSpecificVarTypes}); + } + } + +public: + ConformingMethodListCallback(CodeCompletionExpr *CCExpr) : CCExpr(CCExpr) {} + + ArrayRef getResults() const { return Results; } +}; + +void ConformingMethodListCallbacks::doneParsing(SourceFile *SrcFile) { + if (!CCExpr || !CCExpr->getBase()) + return; + + ConformingMethodListCallback TypeCheckCallback(CCExpr); + { + llvm::SaveAndRestore CompletionCollector( + Context.CompletionCallback, &TypeCheckCallback); + typeCheckContextAt( + TypeCheckASTNodeAtLocContext::declContext(CurDeclContext), + CCExpr->getLoc()); + } + + if (TypeCheckCallback.getResults().size() != 1) { + // Either no results or results were ambiguous, which we cannot handle. + return; } + auto Res = TypeCheckCallback.getResults()[0]; + Type T = Res.Ty; + WithSolutionSpecificVarTypesRAII VarType(Res.SolutionSpecificVarTypes); if (!T || T->is() || T->is()) return; diff --git a/lib/IDE/PostfixCompletion.cpp b/lib/IDE/PostfixCompletion.cpp index 7fae130f0a0e1..c3a54993964ad 100644 --- a/lib/IDE/PostfixCompletion.cpp +++ b/lib/IDE/PostfixCompletion.cpp @@ -21,6 +21,65 @@ using namespace swift; using namespace swift::constraints; using namespace swift::ide; +bool PostfixCompletionCallback::Result::canBeMergedWith(const Result &Other, + DeclContext &DC) const { + if (BaseDecl != Other.BaseDecl) { + return false; + } + if (!BaseTy->isEqual(Other.BaseTy) && + !isConvertibleTo(BaseTy, Other.BaseTy, /*openArchetypes=*/true, DC) && + !isConvertibleTo(Other.BaseTy, BaseTy, /*openArchetypes=*/true, DC)) { + return false; + } + return true; +} + +void PostfixCompletionCallback::Result::merge(const Result &Other, + DeclContext &DC) { + assert(canBeMergedWith(Other, DC)); + // These properties should match if we are talking about the same BaseDecl. + assert(IsBaseDeclUnapplied == Other.IsBaseDeclUnapplied); + assert(BaseIsStaticMetaType == Other.BaseIsStaticMetaType); + + if (!BaseTy->isEqual(Other.BaseTy) && + isConvertibleTo(Other.BaseTy, BaseTy, /*openArchetypes=*/true, DC)) { + // Pick the more specific base type as it will produce more solutions. + BaseTy = Other.BaseTy; + } + + // There could be multiple results that have different actor isolations if the + // closure is an argument to a function that has multiple overloads with + // different isolations for the closure. Producing multiple results for these + // is usually not very enlightning. For now, we just pick the first actor + // isolation that we find. This is good enough in practice. + // What we should really do is probably merge these two actor isolations and + // pick the weakest isolation for each closure. + + for (auto &OtherExpectedTy : Other.ExpectedTypes) { + auto IsEqual = [&](Type Ty) { return Ty->isEqual(OtherExpectedTy); }; + if (llvm::any_of(ExpectedTypes, IsEqual)) { + // We already know if this expected type + continue; + } + ExpectedTypes.push_back(OtherExpectedTy); + } + ExpectsNonVoid &= Other.ExpectsNonVoid; + IsImplicitSingleExpressionReturn |= Other.IsImplicitSingleExpressionReturn; + IsInAsyncContext |= Other.IsInAsyncContext; +} + +void PostfixCompletionCallback::addResult(const Result &Res) { + auto ExistingRes = + llvm::find_if(Results, [&Res, DC = DC](const Result &ExistingResult) { + return ExistingResult.canBeMergedWith(Res, *DC); + }); + if (ExistingRes != Results.end()) { + ExistingRes->merge(Res, *DC); + } else { + Results.push_back(Res); + } +} + void PostfixCompletionCallback::fallbackTypeCheck(DeclContext *DC) { assert(!gotCallback()); @@ -36,6 +95,14 @@ void PostfixCompletionCallback::fallbackTypeCheck(DeclContext *DC) { } } + if (isa(fallbackDC)) { + // If the expression is embedded in a closure, the constraint system tries + // to retrieve that closure's type, which will fail since we won't have + // generated any type variables for it. Thus, fallback type checking isn't + // available in this case. + return; + } + SyntacticElementTarget completionTarget(fallbackExpr, fallbackDC, CTP_Unused, Type(), /*isDiscared=*/true); @@ -68,12 +135,39 @@ getClosureActorIsolation(const Solution &S, AbstractClosureExpr *ACE) { getClosureActorIsolationThunk); } +/// Returns \c true if \p Choice refers to a function that hasn't been called +/// yet. +static bool isUnappliedFunctionRef(const OverloadChoice &Choice) { + if (!Choice.isDecl()) { + return false; + } + switch (Choice.getFunctionRefKind()) { + case FunctionRefKind::Unapplied: + return true; + case FunctionRefKind::SingleApply: + if (auto BaseTy = Choice.getBaseType()) { + // We consider curried member calls as unapplied. E.g. + // MyStruct.someInstanceFunc(theInstance)#^COMPLETE^# + // is unapplied. + return BaseTy->is() && !Choice.getDeclOrNull()->isStatic(); + } else { + return false; + } + default: + return false; + } +} + void PostfixCompletionCallback::sawSolutionImpl( const constraints::Solution &S) { auto &CS = S.getConstraintSystem(); auto *ParsedExpr = CompletionExpr->getBase(); auto *SemanticExpr = ParsedExpr->getSemanticsProvidingExpr(); + if (!S.hasType(ParsedExpr)) { + return; + } + auto BaseTy = getTypeForCompletion(S, ParsedExpr); // If base type couldn't be determined (e.g. because base expression // is an invalid reference), let's not attempt to do a lookup since @@ -89,63 +183,235 @@ void PostfixCompletionCallback::sawSolutionImpl( auto *CalleeLocator = S.getCalleeLocator(Locator); ValueDecl *ReferencedDecl = nullptr; - if (auto SelectedOverload = S.getOverloadChoiceIfAvailable(CalleeLocator)) + bool IsBaseDeclUnapplied = false; + if (auto SelectedOverload = S.getOverloadChoiceIfAvailable(CalleeLocator)) { ReferencedDecl = SelectedOverload->choice.getDeclOrNull(); + IsBaseDeclUnapplied = isUnappliedFunctionRef(SelectedOverload->choice); + } + + bool BaseIsStaticMetaType = S.isStaticallyDerivedMetatype(ParsedExpr); + + SmallVector ExpectedTypes; + if (ExpectedTy) { + ExpectedTypes.push_back(ExpectedTy); + } + + bool ExpectsNonVoid = false; + ExpectsNonVoid |= ExpectedTy && !ExpectedTy->isVoid(); + ExpectsNonVoid |= + !ParentExpr && CS.getContextualTypePurpose(CompletionExpr) != CTP_Unused; + for (auto SAT : S.targets) { + if (ExpectsNonVoid) { + // ExpectsNonVoid is already set. No need to iterate further. + break; + } + if (SAT.second.getAsExpr() == CompletionExpr) { + ExpectsNonVoid |= SAT.second.getExprContextualTypePurpose() != CTP_Unused; + } + } + + bool IsImplicitSingleExpressionReturn = + isImplicitSingleExpressionReturn(CS, CompletionExpr); + + bool IsInAsyncContext = isContextAsync(S, DC); llvm::DenseMap ClosureActorIsolations; - bool IsAsync = isContextAsync(S, DC); for (auto SAT : S.targets) { if (auto ACE = getAsExpr(SAT.second.getAsASTNode())) { ClosureActorIsolations[ACE] = getClosureActorIsolation(S, ACE); } } - auto Key = std::make_pair(BaseTy, ReferencedDecl); - auto Ret = BaseToSolutionIdx.insert({Key, Results.size()}); - if (Ret.second) { - bool ISDMT = S.isStaticallyDerivedMetatype(ParsedExpr); - bool ImplicitReturn = isImplicitSingleExpressionReturn(CS, CompletionExpr); - bool DisallowVoid = false; - DisallowVoid |= ExpectedTy && !ExpectedTy->isVoid(); - DisallowVoid |= !ParentExpr && - CS.getContextualTypePurpose(CompletionExpr) != CTP_Unused; - for (auto SAT : S.targets) { - if (DisallowVoid) { - // DisallowVoid is already set. No need to iterate further. - break; - } - if (SAT.second.getAsExpr() == CompletionExpr) { - DisallowVoid |= SAT.second.getExprContextualTypePurpose() != CTP_Unused; - } + Result Res = { + BaseTy, + ReferencedDecl, + IsBaseDeclUnapplied, + BaseIsStaticMetaType, + ExpectedTypes, + ExpectsNonVoid, + IsImplicitSingleExpressionReturn, + IsInAsyncContext, + ClosureActorIsolations + }; + + addResult(Res); +} + +/// Returns \c true if \p T is '_OptionalNilComparisonType'. +static bool isOptionalNilComparisonType(Type T) { + if (!T) { + return false; + } + auto *nominal = T->getAnyNominal(); + if (!nominal) { + return false; + } + return (nominal->isStdlibDecl() && + nominal->getName() == + nominal->getASTContext().Id_OptionalNilComparisonType); +} + +static DeclRefKind getDeclRefKindOfOperator(OperatorDecl *op) { + switch (op->getKind()) { + case DeclKind::PrefixOperator: + return DeclRefKind::PrefixOperator; + case DeclKind::PostfixOperator: + return DeclRefKind::PostfixOperator; + case DeclKind::InfixOperator: + return DeclRefKind::BinaryOperator; + default: + llvm_unreachable("unexpected operator kind"); + } +} + +/// Return type of \c getOperatorCompletionTypes. +struct OperatorResultTypes { + /// If we are trying to complete a binary operator, the type the operator + /// expects for the RHS. Null for postfix operators. + Type RHSType; + + /// The type the operator returns when called. + Type ResultType; + + bool operator==(const OperatorResultTypes &Other) const { + return nullableTypesEqual(RHSType, Other.RHSType) && + nullableTypesEqual(ResultType, Other.ResultType); + } +}; + +/// Builds a constriant system that tries applying the operator \p op on a LHS +/// of type \p LHSType. If that succeeds, returns the result type of the +/// operator call and (in case of binary operators) the expected type for the +/// RHS. +static SmallVector +getOperatorCompletionTypes(DeclContext *DC, Type LHSType, OperatorDecl *Op) { + ConstraintSystemOptions options; + options |= ConstraintSystemFlags::SuppressDiagnostics; + + ConstraintSystem CS(DC, options); + + // The source loc of the generated expression doesn't matter. + SourceLoc Loc; + + // We represent the LHS and RHS by CodeCompletionExprs because there's no + // other better choice. rhs will have its type set in the constraint system + // below and, in case of binary operators, rhs will be inspected for its type + // when the constraint system has been solved. + CodeCompletionExpr LHS(Loc); + CodeCompletionExpr RHS(Loc); + + UnresolvedDeclRefExpr UDRE(DeclNameRef(Op->getName()), + getDeclRefKindOfOperator(Op), DeclNameLoc(Loc)); + DiagnosticTransaction IgnoreDiags(DC->getASTContext().Diags); + Expr *OpExpr = + resolveDeclRefExpr(&UDRE, DC, /*replaceInvalidRefsWithErrors=*/true); + IgnoreDiags.abort(); + if (isa(OpExpr)) { + // If we couldn't resolve the operator (e.g. because there is only an + // operator definition but no decls that implement it), we can't call the + // operator. + return {}; + } + + Expr *OpCallExpr; + switch (Op->getKind()) { + case DeclKind::PrefixOperator: + // Don't insert prefix operators in postfix position. + return {}; + case DeclKind::PostfixOperator: + OpCallExpr = PostfixUnaryExpr::create(DC->getASTContext(), OpExpr, &LHS); + break; + case DeclKind::InfixOperator: + OpCallExpr = BinaryExpr::create(DC->getASTContext(), &LHS, OpExpr, &RHS, + /*implicit*/ true); + break; + default: + llvm_unreachable("unexpected operator kind"); + } + + CS.preCheckExpression(OpCallExpr, DC, /*replaceInvalidRefsWithErrors=*/true, + /*leaveClosureBodyUnchecked=*/false); + OpCallExpr = CS.generateConstraints(OpCallExpr, DC); + + CS.assignFixedType(CS.getType(&LHS)->getAs(), LHSType); + + SmallVector Solutions; + CS.solve(Solutions); + + SmallVector Results; + for (auto &S : Solutions) { + Type RHSType; + if (Op->getKind() == DeclKind::InfixOperator) { + RHSType = getTypeForCompletion(S, &RHS); + } + Type ResultType = getTypeForCompletion(S, OpCallExpr); + + OperatorResultTypes ResultTypes = {RHSType, ResultType}; + if (llvm::is_contained(Results, ResultTypes)) { + continue; } - Results.push_back({BaseTy, ReferencedDecl, - /*ExpectedTypes=*/{}, DisallowVoid, ISDMT, - ImplicitReturn, IsAsync, ClosureActorIsolations}); - if (ExpectedTy) { - Results.back().ExpectedTypes.push_back(ExpectedTy); + if (S.getFixedScore().Data[SK_ValueToOptional] > 0) { + if (Op->getName().str() == "??" || isOptionalNilComparisonType(RHSType)) { + // Don't suggest optional operators that need to demote the LHS to an + // Optional to become applicable. + continue; + } } - } else if (ExpectedTy) { - auto &ExistingResult = Results[Ret.first->getSecond()]; - ExistingResult.IsInAsyncContext |= IsAsync; - auto IsEqual = [&](Type Ty) { return ExpectedTy->isEqual(Ty); }; - if (!llvm::any_of(ExistingResult.ExpectedTypes, IsEqual)) { - ExistingResult.ExpectedTypes.push_back(ExpectedTy); + + Results.push_back(ResultTypes); + } + + return Results; +} + +/// Adds applicable operator suggestions to \p Lookup. +static void addOperatorResults(Type LHSType, ArrayRef Operators, + DeclContext *DC, CompletionLookup &Lookup) { + for (auto Op : Operators) { + switch (Op->getKind()) { + case DeclKind::PrefixOperator: + break; + case DeclKind::PostfixOperator: + for (auto operatorType : getOperatorCompletionTypes(DC, LHSType, Op)) { + Lookup.addPostfixOperatorCompletion(Op, operatorType.ResultType); + } + break; + case DeclKind::InfixOperator: + for (auto operatorType : getOperatorCompletionTypes(DC, LHSType, Op)) { + Lookup.addInfixOperatorCompletion(Op, operatorType.ResultType, + operatorType.RHSType); + } + break; + default: + llvm_unreachable("unexpected operator kind"); } } + if (LHSType->hasLValueType()) { + Lookup.addAssignmentOperator(LHSType->getRValueType()); + } + if (auto ValueT = LHSType->getRValueType()->getOptionalObjectType()) { + Lookup.addPostfixBang(ValueT); + } } void PostfixCompletionCallback::deliverResults( - Expr *BaseExpr, DeclContext *DC, SourceLoc DotLoc, bool IsInSelector, - CodeCompletionContext &CompletionCtx, CodeCompletionConsumer &Consumer) { + SourceLoc DotLoc, bool IsInSelector, bool IncludeOperators, + bool HasLeadingSpace, CodeCompletionContext &CompletionCtx, + CodeCompletionConsumer &Consumer) { ASTContext &Ctx = DC->getASTContext(); CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC, &CompletionCtx); - if (DotLoc.isValid()) + if (DotLoc.isValid()) { + assert(!IncludeOperators && "We shouldn't be suggesting operators if we " + "are completing after a dot"); Lookup.setHaveDot(DotLoc); + } + Lookup.setHaveLeadingSpace(HasLeadingSpace); + Expr *BaseExpr = CompletionExpr->getBase(); Lookup.setIsSuperRefExpr(isa(BaseExpr)); if (auto *DRE = dyn_cast(BaseExpr)) @@ -158,6 +424,10 @@ void PostfixCompletionCallback::deliverResults( Lookup.includeInstanceMembers(); Lookup.setPreferFunctionReferencesToCalls(); } + SmallVector Operators; + if (IncludeOperators) { + Lookup.collectOperators(Operators); + } Lookup.shouldCheckForDuplicates(Results.size() > 1); for (auto &Result : Results) { @@ -170,7 +440,12 @@ void PostfixCompletionCallback::deliverResults( Result.ExpectsNonVoid); if (isDynamicLookup(Result.BaseTy)) Lookup.setIsDynamicLookup(); - Lookup.getValueExprCompletions(Result.BaseTy, Result.BaseDecl); + Lookup.getValueExprCompletions(Result.BaseTy, Result.BaseDecl, + Result.IsBaseDeclUnapplied); + + if (IncludeOperators && !Result.BaseIsStaticMetaType) { + addOperatorResults(Result.BaseTy, Operators, DC, Lookup); + } } deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer); diff --git a/lib/IDE/TypeCheckCompletionCallback.cpp b/lib/IDE/TypeCheckCompletionCallback.cpp index 9739a53fbd679..1fb4987940010 100644 --- a/lib/IDE/TypeCheckCompletionCallback.cpp +++ b/lib/IDE/TypeCheckCompletionCallback.cpp @@ -28,8 +28,13 @@ void TypeCheckCompletionCallback::fallbackTypeCheck(DeclContext *DC) { return; auto fallback = finder.getFallbackCompletionExpr(); - if (!fallback) + if (!fallback || isa(fallback->DC)) { + // If the expression is embedded in a closure, the constraint system tries + // to retrieve that closure's type, which will fail since we won't have + // generated any type variables for it. Thus, fallback type checking isn't + // available in this case. return; + } SyntacticElementTarget completionTarget(fallback->E, fallback->DC, CTP_Unused, Type(), diff --git a/lib/IDE/UnresolvedMemberCompletion.cpp b/lib/IDE/UnresolvedMemberCompletion.cpp index 41bfe34032050..2006ec7d63e73 100644 --- a/lib/IDE/UnresolvedMemberCompletion.cpp +++ b/lib/IDE/UnresolvedMemberCompletion.cpp @@ -21,6 +21,45 @@ using namespace swift; using namespace swift::constraints; using namespace swift::ide; +bool UnresolvedMemberTypeCheckCompletionCallback::Result::canBeMergedWith( + const Result &Other, DeclContext &DC) const { + if (!isConvertibleTo(ExpectedTy, Other.ExpectedTy, /*openArchetypes=*/true, + DC) && + !isConvertibleTo(Other.ExpectedTy, ExpectedTy, /*openArchetypes=*/true, + DC)) { + return false; + } + return true; +} + +void UnresolvedMemberTypeCheckCompletionCallback::Result::merge( + const Result &Other, DeclContext &DC) { + assert(canBeMergedWith(Other, DC)); + if (!ExpectedTy->isEqual(Other.ExpectedTy) && + isConvertibleTo(ExpectedTy, Other.ExpectedTy, /*openArchetypes=*/true, + DC)) { + // ExpectedTy is more general than Other.ExpectedTy. Complete based on the + // more general type because it offers more completion options. + ExpectedTy = Other.ExpectedTy; + } + + IsImplicitSingleExpressionReturn |= Other.IsImplicitSingleExpressionReturn; + IsInAsyncContext |= Other.IsInAsyncContext; +} + +void UnresolvedMemberTypeCheckCompletionCallback::addExprResult( + const Result &Res) { + auto ExistingRes = + llvm::find_if(ExprResults, [&Res, DC = DC](const Result &ExistingResult) { + return ExistingResult.canBeMergedWith(Res, *DC); + }); + if (ExistingRes != ExprResults.end()) { + ExistingRes->merge(Res, *DC); + } else { + ExprResults.push_back(Res); + } +} + void UnresolvedMemberTypeCheckCompletionCallback::sawSolutionImpl( const constraints::Solution &S) { auto &CS = S.getConstraintSystem(); @@ -32,15 +71,9 @@ void UnresolvedMemberTypeCheckCompletionCallback::sawSolutionImpl( // to derive it from), let's not attempt to do a lookup since it wouldn't // produce any useful results anyway. if (ExpectedTy) { - // If ExpectedTy is a duplicate of any other result, ignore this solution. - auto IsEqual = [&](const Result &R) { - return R.ExpectedTy->isEqual(ExpectedTy); - }; - if (!llvm::any_of(ExprResults, IsEqual)) { - bool SingleExprBody = - isImplicitSingleExpressionReturn(CS, CompletionExpr); - ExprResults.push_back({ExpectedTy, SingleExprBody, IsAsync}); - } + bool SingleExprBody = isImplicitSingleExpressionReturn(CS, CompletionExpr); + Result Res = {ExpectedTy, SingleExprBody, IsAsync}; + addExprResult(Res); } if (auto PatternType = getPatternMatchType(S, CompletionExpr)) { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 652cf4cb871cd..0e7dc92670047 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3681,15 +3681,22 @@ ParserResult Parser::parseCustomAttribute( ArgumentList *argList = nullptr; if (Tok.isFollowingLParen() && isCustomAttributeArgument()) { if (peekToken().is(tok::code_complete)) { - consumeToken(tok::l_paren); + auto lParenLoc = consumeToken(tok::l_paren); + auto typeE = new (Context) TypeExpr(type.get()); + auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc()); if (CodeCompletionCallbacks) { - auto typeE = new (Context) TypeExpr(type.get()); - auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc()); CodeCompletionCallbacks->completePostfixExprParen(typeE, CCE); } consumeToken(tok::code_complete); - skipUntil(tok::r_paren); - consumeIf(tok::r_paren); + skipUntilDeclStmtRBrace(tok::r_paren); + auto rParenLoc = PreviousLoc; + if (Tok.is(tok::r_paren)) { + rParenLoc = consumeToken(tok::r_paren); + } + + argList = ArgumentList::createParsed( + Context, lParenLoc, {Argument::unlabeled(CCE)}, rParenLoc, + /*trailingClosureIdx=*/llvm::None); status.setHasCodeCompletionAndIsError(); } else { // If we have no local context to parse the initial value into, create diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 599935a5ce0ea..12c6431c902b5 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -1491,9 +1491,20 @@ Parser::parseExprPostfixSuffix(ParserResult Result, bool isExprBasic, return Result; } - if (CodeCompletionCallbacks && Result.isNonNull()) { + // FIXME: We should be able to remove the InBindingPattern checks once + // apple/swift#64280 is merged. + if (CodeCompletionCallbacks && Result.isNonNull() && + InBindingPattern != PatternBindingState::ImplicitlyImmutable && + InBindingPattern != PatternBindingState::InVar && + InBindingPattern != PatternBindingState::InLet) { + // Don't do postfix completions when in a binding pattern. It doesn't + // make sense to offer completions eg. in + // if let myVar#^COMPLETE^# = someOptional {} bool hasSpace = Tok.getLoc() != getEndOfPreviousLoc(); - CodeCompletionCallbacks->completePostfixExpr(Result.get(), hasSpace); + auto CCExpr = + new (Context) CodeCompletionExpr(Result.get(), Tok.getLoc()); + CodeCompletionCallbacks->completePostfixExpr(CCExpr, hasSpace); + Result = makeParserResult(Result, CCExpr); } // Eat the code completion token because we handled it. consumeToken(tok::code_complete); diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index aad4a5207baba..3088f5bfc56ad 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -195,6 +195,15 @@ class ResultBuilderTransform // Allocate variable with a placeholder type auto *resultVar = buildPlaceholderVar(stmt->getStartLoc(), newBody); + if (ctx.CompletionCallback && stmt->getSourceRange().isValid() && + !containsIDEInspectionTarget(stmt->getSourceRange(), ctx.SourceMgr) && + !isa(stmt)) { + // A statement that doesn't contain the code completion expression can't + // influence the type of the code completion expression, so we can skip + // it to improve performance. + return llvm::None; + } + auto result = visit(stmt, resultVar); if (!result) return UnsupportedElt(stmt); @@ -223,6 +232,16 @@ class ResultBuilderTransform // to rank code completion items that match the type expected by // buildBlock higher. buildBlockArguments.push_back(expr); + } else if (ctx.CompletionCallback && expr->getSourceRange().isValid() && + !containsIDEInspectionTarget(expr->getSourceRange(), + ctx.SourceMgr)) { + // A statement that doesn't contain the code completion expression can't + // influence the type of the code completion expression. Add a variable + // for it that we can put into the buildBlock call but don't add the + // expression itself into the transformed body to improve performance. + auto *resultVar = buildPlaceholderVar(expr->getStartLoc(), newBody); + buildBlockArguments.push_back( + builder.buildVarRef(resultVar, expr->getStartLoc())); } else { auto *capture = captureExpr(expr, newBody); // A reference to the synthesized variable is passed as an argument @@ -245,7 +264,12 @@ class ResultBuilderTransform for (auto element : braceStmt->getElements()) { if (auto unsupported = transformBraceElement(element, newBody, buildBlockArguments)) { - return failTransform(*unsupported); + // When in code completion mode, simply ignore unsported constructs to + // get results for anything that's unrelated to the unsupported + // constructs. + if (!ctx.CompletionCallback) { + return failTransform(*unsupported); + } } } @@ -984,6 +1008,9 @@ llvm::Optional TypeChecker::applyResultBuilderBodyTransform( // Solve the constraint system. if (cs.getASTContext().CompletionCallback) { SmallVector solutions; + cs.Options |= ConstraintSystemFlags::AllowFixes; + cs.Options |= ConstraintSystemFlags::SuppressDiagnostics; + cs.Options |= ConstraintSystemFlags::ForCodeCompletion; cs.solveForCodeCompletion(solutions); SyntacticElementTarget funcTarget(func); @@ -1152,7 +1179,7 @@ ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType, auto *body = transform.apply(fn.getBody()); if (auto unsupported = transform.getUnsupportedElement()) { - assert(!body); + assert(!body || getASTContext().CompletionCallback); // If we aren't supposed to attempt fixes, fail. if (!shouldAttemptFixes()) { diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 2c9066e3f1d4f..e25f97f071252 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -8570,15 +8570,11 @@ namespace { class ExprWalker : public ASTWalker { ExprRewriter &Rewriter; SmallVector ClosuresToTypeCheck; - SmallVector, 4> TapsToTypeCheck; public: ExprWalker(ExprRewriter &Rewriter) : Rewriter(Rewriter) { } - ~ExprWalker() { - assert(ClosuresToTypeCheck.empty()); - assert(TapsToTypeCheck.empty()); - } + ~ExprWalker() { assert(ClosuresToTypeCheck.empty()); } bool shouldWalkIntoPropertyWrapperPlaceholderValue() override { // Property wrapper placeholder underlying values are filled in @@ -8587,9 +8583,7 @@ namespace { } /// Check if there are any closures or tap expressions left to process separately. - bool hasDelayedTasks() { - return !ClosuresToTypeCheck.empty() || !TapsToTypeCheck.empty(); - } + bool hasDelayedTasks() { return !ClosuresToTypeCheck.empty(); } /// Process delayed closure bodies and `Tap` expressions. /// @@ -8632,17 +8626,6 @@ namespace { hadError |= TypeChecker::typeCheckClosureBody(closure); } - // Tap expressions too; they should or should not be - // type-checked under the same conditions as closure bodies. - { - for (const auto &tuple : TapsToTypeCheck) { - auto tap = std::get<0>(tuple); - auto tapDC = std::get<1>(tuple); - hadError |= TypeChecker::typeCheckTapBody(tap, tapDC); - } - TapsToTypeCheck.clear(); - } - return hadError; } @@ -8667,10 +8650,9 @@ namespace { return Action::SkipChildren(SVE); } - if (auto tap = dyn_cast(expr)) { - // We remember the DeclContext because the code to handle - // single-expression-body closures above changes it. - TapsToTypeCheck.push_back(std::make_pair(tap, Rewriter.dc)); + if (auto tap = dyn_cast_or_null(expr)) { + rewriteTapExpr(tap); + return Action::SkipChildren(tap); } if (auto captureList = dyn_cast(expr)) { @@ -8816,6 +8798,26 @@ namespace { if (auto expr = resultTarget->getAsExpr()) solution.setExprTypes(expr); + return resultTarget; + }); + } + + void rewriteTapExpr(TapExpr *tap) { + auto &solution = Rewriter.solution; + + // First, let's visit the tap expression itself + // and set all of the inferred types. + Rewriter.visitTapExpr(tap); + + // Now, let's apply solution to the body + (void)Rewriter.cs.applySolutionToBody( + solution, tap, Rewriter.dc, [&](SyntacticElementTarget target) { + auto resultTarget = rewriteTarget(target); + if (resultTarget) { + if (auto expr = resultTarget->getAsExpr()) + solution.setExprTypes(expr); + } + return resultTarget; }); } diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index a735a76aa3810..5120be8746690 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -2297,6 +2297,12 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { if (TypeVar->getImpl().getGenericParameter()) return false; + // Don't penalize solutions if we couldn't determine the type of the code + // completion token. We still want to examine the surrounding types in + // that case. + if (TypeVar->getImpl().isCodeCompletionToken()) + return false; + // Don't penalize solutions with holes due to missing arguments after the // code completion position. auto argLoc = srcLocator->findLast(); @@ -2315,7 +2321,7 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { } // Reflect in the score that this type variable couldn't be // resolved and had to be bound to a placeholder "hole" type. - cs.increaseScore(SK_Hole); + cs.increaseScore(SK_Hole, srcLocator); if (auto fix = fixForHole(cs)) { if (cs.recordFix(/*fix=*/fix->first, /*impact=*/fix->second)) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 82c0b79a65f80..50038b77057fc 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -848,6 +848,61 @@ namespace { namespace { +// Collect any variable references whose types involve type variables, +// because there will be a dependency on those type variables once we have +// generated constraints for the closure/tap body. This includes references +// to other closure params such as in `{ x in { x }}` where the inner +// closure is dependent on the outer closure's param type, as well as +// cases like `for i in x where bar({ i })` where there's a dependency on +// the type variable for the pattern `i`. +struct VarRefCollector : public ASTWalker { + ConstraintSystem &cs; + llvm::SmallPtrSet varRefs; + + VarRefCollector(ConstraintSystem &cs) : cs(cs) {} + + bool shouldWalkCaptureInitializerExpressions() override { return true; } + + MacroWalking getMacroWalkingBehavior() const override { + return MacroWalking::Arguments; + } + + PreWalkResult walkToExprPre(Expr *expr) override { + // Retrieve type variables from references to var decls. + if (auto *declRef = dyn_cast(expr)) { + if (auto *varDecl = dyn_cast(declRef->getDecl())) { + if (auto varType = cs.getTypeIfAvailable(varDecl)) { + varType->getTypeVariables(varRefs); + } + } + } + + // FIXME: We can see UnresolvedDeclRefExprs here because we have + // not yet run preCheckExpression() on the entire closure body + // yet. + // + // We could consider pre-checking more eagerly. + if (auto *declRef = dyn_cast(expr)) { + auto name = declRef->getName(); + auto loc = declRef->getLoc(); + if (name.isSimpleName() && loc.isValid()) { + auto *varDecl = + dyn_cast_or_null(ASTScope::lookupSingleLocalDecl( + cs.DC->getParentSourceFile(), name.getFullName(), loc)); + if (varDecl) + if (auto varType = cs.getTypeIfAvailable(varDecl)) + varType->getTypeVariables(varRefs); + } + } + + return Action::Continue(expr); + } + + PreWalkAction walkToDeclPre(Decl *D) override { + return Action::VisitChildrenIf(isa(D)); + } +}; + class ConstraintGenerator : public ExprVisitor { ConstraintSystem &CS; DeclContext *CurDC; @@ -1236,15 +1291,38 @@ namespace { return nullptr; } - auto interpolationTV = DependentMemberType::get(tv, associatedTypeDecl); + auto interpolationTV = + CS.createTypeVariable(locator, TVO_CanBindToNoEscape); + auto interpolationType = + DependentMemberType::get(tv, associatedTypeDecl); + + CS.addConstraint(ConstraintKind::Equal, interpolationTV, + interpolationType, locator); auto appendingExprType = CS.getType(appendingExpr); auto appendingLocator = CS.getConstraintLocator(appendingExpr); - // Must be Conversion; if it's Equal, then in semi-rare cases, the + SmallVector referencedVars; + + if (auto *tap = getAsExpr(appendingExpr)) { + // Collect all of the variable references that appear + // in the tap body, otherwise tap expression is going + // to get disconnected from the context. + if (auto *body = tap->getBody()) { + VarRefCollector refCollector(CS); + + body->walk(refCollector); + + referencedVars.append(refCollector.varRefs.begin(), + refCollector.varRefs.end()); + } + } + + // Must be Conversion; if it's Equal, then in semi-rare cases, the // interpolation temporary variable cannot be @lvalue. - CS.addConstraint(ConstraintKind::Conversion, appendingExprType, - interpolationTV, appendingLocator); + CS.addUnsolvedConstraint(Constraint::create( + CS, ConstraintKind::Conversion, appendingExprType, interpolationTV, + appendingLocator, referencedVars)); } return tv; @@ -2883,68 +2961,14 @@ namespace { auto *locator = CS.getConstraintLocator(closure); auto closureType = CS.createTypeVariable(locator, TVO_CanBindToNoEscape); - // Collect any variable references whose types involve type variables, - // because there will be a dependency on those type variables once we have - // generated constraints for the closure body. This includes references - // to other closure params such as in `{ x in { x }}` where the inner - // closure is dependent on the outer closure's param type, as well as - // cases like `for i in x where bar({ i })` where there's a dependency on - // the type variable for the pattern `i`. - struct CollectVarRefs : public ASTWalker { - ConstraintSystem &cs; - llvm::SmallPtrSet varRefs; - - CollectVarRefs(ConstraintSystem &cs) : cs(cs) { } - - bool shouldWalkCaptureInitializerExpressions() override { return true; } - - MacroWalking getMacroWalkingBehavior() const override { - return MacroWalking::Arguments; - } - - PreWalkResult walkToExprPre(Expr *expr) override { - // Retrieve type variables from references to var decls. - if (auto *declRef = dyn_cast(expr)) { - if (auto *varDecl = dyn_cast(declRef->getDecl())) { - if (auto varType = cs.getTypeIfAvailable(varDecl)) { - varType->getTypeVariables(varRefs); - } - } - } - - // FIXME: We can see UnresolvedDeclRefExprs here because we have - // not yet run preCheckExpression() on the entire closure body - // yet. - // - // We could consider pre-checking more eagerly. - if (auto *declRef = dyn_cast(expr)) { - auto name = declRef->getName(); - auto loc = declRef->getLoc(); - if (name.isSimpleName() && loc.isValid()) { - auto *varDecl = dyn_cast_or_null( - ASTScope::lookupSingleLocalDecl(cs.DC->getParentSourceFile(), - name.getFullName(), loc)); - if (varDecl) - if (auto varType = cs.getTypeIfAvailable(varDecl)) - varType->getTypeVariables(varRefs); - } - } - - return Action::Continue(expr); - } - - PreWalkAction walkToDeclPre(Decl *D) override { - return Action::VisitChildrenIf(isa(D)); - } - } collectVarRefs(CS); - + VarRefCollector refCollector(CS); // Walk the capture list if this closure has one, because it could // reference declarations from the outer closure. if (auto *captureList = getAsExpr(CS.getParentExpr(closure))) { - captureList->walk(collectVarRefs); + captureList->walk(refCollector); } else { - closure->walk(collectVarRefs); + closure->walk(refCollector); } auto inferredType = inferClosureType(closure); @@ -2952,7 +2976,7 @@ namespace { return Type(); SmallVector referencedVars{ - collectVarRefs.varRefs.begin(), collectVarRefs.varRefs.end()}; + refCollector.varRefs.begin(), refCollector.varRefs.end()}; CS.addUnsolvedConstraint( Constraint::create(CS, ConstraintKind::FallbackType, closureType, diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index 6f3e971eda791..282f052ba2cc5 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -32,31 +32,97 @@ using namespace constraints; #define DEBUG_TYPE "Constraint solver overall" STATISTIC(NumDiscardedSolutions, "Number of solutions discarded"); -void ConstraintSystem::increaseScore(ScoreKind kind, unsigned value) { - if (isForCodeCompletion()) { - switch (kind) { - case SK_NonDefaultLiteral: - // Don't increase score for non-default literals in expressions involving - // a code completion. In the below example, members of EnumA and EnumB - // should be ranked equally: - // func overloaded(_ x: Float, _ y: EnumA) {} - // func overloaded(_ x: Int, _ y: EnumB) {} - // func overloaded(_ x: Float) -> EnumA {} - // func overloaded(_ x: Int) -> EnumB {} - // - // overloaded(1, .) {} - // overloaded(1). - return; - default: - break; +/// Returns \c true if \p expr takes a code completion expression as an +/// argument. +static bool exprHasCodeCompletionAsArgument(Expr *expr, ConstraintSystem &cs) { + if (auto args = expr->getArgs()) { + for (auto arg : *args) { + if (isa(arg.getExpr())) { + return true; + } + } + } + return false; +} + +static bool shouldIgnoreScoreIncreaseForCodeCompletion( + ScoreKind kind, ConstraintLocatorBuilder Locator, ConstraintSystem &cs) { + if (kind < SK_SyncInAsync) { + // We don't want to ignore score kinds that make the code invalid. + return false; + } + auto expr = Locator.trySimplifyToExpr(); + if (!expr) { + return false; + } + + // These are a few hand-picked examples in which we don't want to increase the + // score in code completion mode. Technically, to get all valid results, we + // would like to not increase the score if the expression contains the code + // completion token anywhere but that's not possible for performance reasons. + // Thus, just special case the most common cases. + + // The code completion token itself. + if (isa(expr)) { + return true; + } + + // An assignment where the LHS or RHS contains the code completion token (e.g. + // an optional conversion). + // E.g. + // x[#^COMPLETE^#] = foo + // let a = foo(#^COMPLETE^#) + if (auto assign = dyn_cast(expr)) { + if (exprHasCodeCompletionAsArgument(assign->getSrc(), cs)) { + return true; + } else if (exprHasCodeCompletionAsArgument(assign->getDest(), cs)) { + return true; + } + } + + // If the function call takes the code completion token as an argument, the + // call also shouldn't increase the score. + // E.g. `foo` in + // foo(#^COMPLETE^#) + if (exprHasCodeCompletionAsArgument(expr, cs)) { + return true; + } + + // The sibling argument is the code completion expression, this allows e.g. + // non-default literal values in sibling arguments. + // E.g. we allow a 1 to be a double in + // foo(1, #^COMPLETE^#) + if (auto parent = cs.getParentExpr(expr)) { + if (exprHasCodeCompletionAsArgument(parent, cs)) { + return true; } } + return false; +} +void ConstraintSystem::increaseScore(ScoreKind kind, + ConstraintLocatorBuilder Locator, + unsigned value) { + if (isForCodeCompletion() && + shouldIgnoreScoreIncreaseForCodeCompletion(kind, Locator, *this)) { + if (isDebugMode() && value > 0) { + if (solverState) + llvm::errs().indent(solverState->getCurrentIndent()); + llvm::errs() << "(not increasing '" << Score::getNameFor(kind) + << "' score by " << value + << " because of proximity to code completion token"; + Locator.dump(&getASTContext().SourceMgr, llvm::errs()); + llvm::errs() << ")\n"; + } + return; + } if (isDebugMode() && value > 0) { if (solverState) llvm::errs().indent(solverState->getCurrentIndent()); llvm::errs() << "(increasing '" << Score::getNameFor(kind) << "' score by " - << value << ")\n"; + << value << " @ "; + Locator.dump(&getASTContext().SourceMgr, llvm::errs()); + llvm::errs() << ")\n"; } unsigned index = static_cast(kind); @@ -890,6 +956,25 @@ SolutionCompareResult ConstraintSystem::compareSolutions( // FIXME: Along with the FIXME below, this is a hack to work around // problems with restating requirements in protocols. identical = false; + + if (cs.Context.CompletionCallback) { + // Don't rank based on overload choices of function calls that contain the + // code completion token. + ASTNode anchor = simplifyLocatorToAnchor(overload.locator); + if (auto expr = getAsExpr(anchor)) { + // If the anchor is a called function, also don't rank overload choices + // if any of the arguments contain the code completion token. + if (auto apply = dyn_cast_or_null(cs.getParentExpr(expr))) { + if (apply->getFn() == expr) { + anchor = apply; + } + } + } + if (anchor && cs.containsIDEInspectionTarget(anchor)) { + continue; + } + } + bool decl1InSubprotocol = false; bool decl2InSubprotocol = false; if (dc1->getContextKind() == DeclContextKind::GenericTypeDecl && diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index aeadff9ad97bf..8e6401c4d6e31 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1251,7 +1251,7 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { // to identify that there is something going on besides just missing // arguments. if (!MissingArguments.empty() || !ExtraArguments.empty()) { - CS.increaseScore(SK_Fix); + CS.increaseScore(SK_Fix, Locator); return false; } @@ -1272,7 +1272,7 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { // because they'd share a locator path which (currently) means // one fix would overwrite another. if (!ExtraArguments.empty()) { - CS.increaseScore(SK_Fix); + CS.increaseScore(SK_Fix, Locator); return false; } @@ -1727,7 +1727,7 @@ static ConstraintSystem::TypeMatchResult matchCallArguments( // warning for such code. if (trailingClosureMatching && *trailingClosureMatching == TrailingClosureMatching::Forward) - cs.increaseScore(SK_ForwardTrailingClosure); + cs.increaseScore(SK_ForwardTrailingClosure, locator); // Take the parameter bindings we selected. parameterBindings = std::move(callArgumentMatch->parameterBindings); @@ -1865,7 +1865,7 @@ static ConstraintSystem::TypeMatchResult matchCallArguments( // // f { } // OK if (isExpr(argExpr)) { - cs.increaseScore(SK_FunctionToAutoClosureConversion); + cs.increaseScore(SK_FunctionToAutoClosureConversion, loc); } // If the argument is not marked as @autoclosure or @@ -1882,7 +1882,7 @@ static ConstraintSystem::TypeMatchResult matchCallArguments( // Matching @autoclosure argument to @autoclosure parameter // directly would mean introducing a function conversion // in Swift <= 4 mode. - cs.increaseScore(SK_FunctionConversion); + cs.increaseScore(SK_FunctionConversion, loc); matchingAutoClosureResult = false; } } @@ -2004,7 +2004,7 @@ static ConstraintSystem::TypeMatchResult matchCallArguments( auto *locator = typeVar->getImpl().getLocator(); auto *closure = castToExpr(locator->getAnchor()); if (!cs.getClosureType(closure)->isAsync()) - cs.increaseScore(SK_SyncInAsync); + cs.increaseScore(SK_SyncInAsync, locator); } } } @@ -3022,7 +3022,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, // indicate than solution with such a mismatch is always // worse than one with synchronous functions on both sides. if (!forClosureInArgumentPosition) - increaseScore(SK_SyncInAsync); + increaseScore(SK_SyncInAsync, locator); } // A @Sendable function can be a subtype of a non-@Sendable function. @@ -3077,7 +3077,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, // because in these contexts it's valid to both add or remove the actor // from these function types. At least with the score increases, we // can bias the solver to pick the solution with fewer conversions. - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); } else if (MarkGlobalActorFunction::attempt(*this, kind, func1, func2, locator)) { return getTypeMatchFailure(locator); @@ -3092,7 +3092,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, // avoid ambiguity when solver can also match a global actor matching // function type. // FIXME: there may be a better way. see https://github.com/apple/swift/pull/62514 - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); } } @@ -3103,7 +3103,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, auto loc = getConstraintLocator(locator); if (loc->findLast() && func1->getRepresentation() != func2->getRepresentation()) { - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); } if (!matchFunctionRepresentations(func1->getExtInfo(), func2->getExtInfo(), @@ -3249,7 +3249,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, if (isSingleTupleParam(ctx, func2Params) && canImplodeParams(func1Params, /*destFn*/ func2)) { implodeParams(func1Params); - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); } else if (!ctx.isSwiftVersionAtLeast(5) && isSingleTupleParam(ctx, func1Params) && canImplodeParams(func2Params, /*destFn*/ func1)) { @@ -3262,7 +3262,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, isa(simplified) || isa(simplified))) { implodeParams(func2Params); - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); } } } else if (last->is() && @@ -3310,11 +3310,11 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, if (isSingleTupleParam(ctx, func1Params) && canImplodeParams(func2Params, /*destFn*/ func1)) { implodeParams(func2Params); - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); } else if (isSingleTupleParam(ctx, func2Params) && canImplodeParams(func1Params, /*destFn*/ func2)) { implodeParams(func1Params); - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); } } } @@ -3445,7 +3445,7 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2, if (isAutoClosureFunctionMatch(func1Param, func2Param) || isAutoClosureFunctionMatch(func2Param, func1Param)) { - increaseScore(SK_FunctionToAutoClosureConversion); + increaseScore(SK_FunctionToAutoClosureConversion, locator); } // Variadic bit must match. @@ -3778,7 +3778,7 @@ ConstraintSystem::matchDeepEqualityTypes(Type type1, Type type2, if (recordFix(fix)) return getTypeMatchFailure(locator); - increaseScore(SK_Fix, mismatches.size()); + increaseScore(SK_Fix, loc, mismatches.size()); return getTypeMatchSuccess(); } } @@ -4224,7 +4224,7 @@ ConstraintSystem::matchTypesBindTypeVar( // it would let the solver to form a _valid_ solution as if the // constraint between the type variable and the unresolved dependent // member type never existed. - increaseScore(SK_Hole); + increaseScore(SK_Hole, locator); recordPotentialHole(typeVar); return getTypeMatchSuccess(); } @@ -4413,6 +4413,12 @@ ConstraintSystem::matchTypesBindTypeVar( : getTypeMatchFailure(locator); } + if (typeVar->getImpl().isTapType()) { + return resolveTapBody(typeVar, type, locator) + ? getTypeMatchSuccess() + : getTypeMatchFailure(locator); + } + assignFixedType(typeVar, type, /*updateState=*/true, /*notifyInference=*/!flags.contains(TMF_BindingTypeVariable)); @@ -5839,7 +5845,7 @@ bool ConstraintSystem::repairFailures( // a function type itself, let's ignore argument failure but // increase a score. if (hasFixFor(parentLoc)) { - increaseScore(SK_Fix); + increaseScore(SK_Fix, locator); return true; } @@ -6123,7 +6129,7 @@ bool ConstraintSystem::repairFailures( path.pop_back(); auto loc = getConstraintLocator(anchor, path); if (hasFixFor(loc, FixKind::TreatArrayLiteralAsDictionary)) { - increaseScore(SK_Fix); + increaseScore(SK_Fix, loc); return true; } @@ -7369,7 +7375,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // Penalize conversions to Any. if (kind >= ConstraintKind::Conversion && type2->isAny()) - increaseScore(ScoreKind::SK_EmptyExistentialConversion); + increaseScore(ScoreKind::SK_EmptyExistentialConversion, locator); conversionsOrFixes.push_back(ConversionRestrictionKind::Existential); } @@ -7408,7 +7414,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, if (auto meta1 = type1->getAs()) { if (meta1->getInstanceType()->mayHaveSuperclass() && type2->isAnyObject()) { - increaseScore(ScoreKind::SK_UserConversion); + increaseScore(ScoreKind::SK_UserConversion, locator); return addSolvedRestrictedConstraint( ConversionRestrictionKind::ClassMetatypeToAnyObject); } @@ -7430,7 +7436,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, if (auto protoTy = constraintType->getAs()) { if (protoTy->getDecl()->isObjC() && isProtocolClassType(type2)) { - increaseScore(ScoreKind::SK_UserConversion); + increaseScore(ScoreKind::SK_UserConversion, locator); return addSolvedRestrictedConstraint( ConversionRestrictionKind::ProtocolMetatypeToProtocolClass); } @@ -7440,7 +7446,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // Class-constrained existential metatypes can be converted to AnyObject. if (meta1->getInstanceType()->isClassExistentialType() && type2->isAnyObject()) { - increaseScore(ScoreKind::SK_UserConversion); + increaseScore(ScoreKind::SK_UserConversion, locator); return addSolvedRestrictedConstraint( ConversionRestrictionKind::ExistentialMetatypeToAnyObject); } @@ -7549,7 +7555,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // Favor an UnsafeMutablePointer-to-UnsafeMutablePointer // conversion. if (type1PointerKind != pointerKind) - increaseScore(ScoreKind::SK_ValueToPointerConversion); + increaseScore(ScoreKind::SK_ValueToPointerConversion, + locator); conversionsOrFixes.push_back( ConversionRestrictionKind::PointerToPointer); } @@ -7557,7 +7564,8 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, else if (type1PointerKind == PTK_UnsafeMutableRawPointer && pointerKind == PTK_UnsafeRawPointer) { if (type1PointerKind != pointerKind) - increaseScore(ScoreKind::SK_ValueToPointerConversion); + increaseScore(ScoreKind::SK_ValueToPointerConversion, + locator); conversionsOrFixes.push_back( ConversionRestrictionKind::PointerToPointer); } @@ -7690,7 +7698,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // forbid conversion to `Void` and report an error instead to // honor user's intent. if (type1->isUninhabited() || !resultElt->hasExplicitReturn()) { - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); return getTypeMatchSuccess(); } } @@ -7698,7 +7706,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, // Single expression function with implicit `return`. if (auto contextualType = elt->getAs()) { if (contextualType->isFor(CTP_ReturnSingleExpr)) { - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); return getTypeMatchSuccess(); } } @@ -7721,7 +7729,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, break; } if (allowConversion) { - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); return getTypeMatchSuccess(); } } @@ -8223,7 +8231,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( /// requirements if necessary. auto recordConformance = [&](ProtocolConformanceRef conformance) { if (isConformanceUnavailable(conformance, loc)) - increaseScore(SK_Unavailable); + increaseScore(SK_Unavailable, locator); // This conformance may be conditional, in which case we need to consider // those requirements as constraints too. @@ -11508,6 +11516,23 @@ bool ConstraintSystem::resolvePackExpansion(TypeVariableType *typeVar, return true; } +bool ConstraintSystem::resolveTapBody(TypeVariableType *typeVar, + Type contextualType, + ConstraintLocatorBuilder locator) { + auto *tapLoc = typeVar->getImpl().getLocator(); + auto *tapExpr = castToExpr(tapLoc->getAnchor()); + + // Assign a type to tap expression itself. + assignFixedType(typeVar, contextualType, getConstraintLocator(locator)); + // Set type to `$interpolation` variable declared in the body of tap + // expression. + setType(tapExpr->getVar(), contextualType); + + // With all of the contextual information recorded in the constraint system, + // it's time to generate constraints for the body of this tap expression. + return !generateConstraints(tapExpr); +} + ConstraintSystem::SolutionKind ConstraintSystem::simplifyDynamicTypeOfConstraint( Type type1, Type type2, @@ -11629,7 +11654,7 @@ ConstraintSystem::simplifyBridgingConstraint(Type type1, return formUnsolved(); // Update the score. - increaseScore(SK_UserConversion); // FIXME: Use separate score kind? + increaseScore(SK_UserConversion, locator); // FIXME: Use separate score kind? if (worseThanBestSolution()) { return SolutionKind::Error; } @@ -11638,7 +11663,8 @@ ConstraintSystem::simplifyBridgingConstraint(Type type1, // after the bridging conversion. auto countOptionalInjections = [&] { if (numToOptionals > numFromOptionals) - increaseScore(SK_ValueToOptional, numToOptionals - numFromOptionals); + increaseScore(SK_ValueToOptional, locator, + numToOptionals - numFromOptionals); }; // Anything can be explicitly converted to AnyObject using the universal @@ -12200,7 +12226,7 @@ ConstraintSystem::simplifyKeyPathConstraint( auto loc = locator.getBaseLocator(); if (definitelyFunctionType) { - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); return SolutionKind::Solved; } else if (!anyComponentsUnresolved || (definitelyKeyPathType && capability == ReadOnly)) { @@ -13733,7 +13759,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( // This induces conversions to occur within closures instead of // outside of them wherever possible. if (locator.isFunctionConversion()) { - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); } }; @@ -13743,7 +13769,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( [&](llvm::PointerIntPair baseType1, llvm::PointerIntPair baseType2) -> SolutionKind { if (restriction != ConversionRestrictionKind::PointerToPointer) - increaseScore(ScoreKind::SK_ValueToPointerConversion); + increaseScore(ScoreKind::SK_ValueToPointerConversion, locator); auto result = matchTypes(baseType1.getPointer(), baseType2.getPointer(), @@ -13901,7 +13927,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( // T $< U ===> T $< U? case ConversionRestrictionKind::ValueToOptional: { addContextualScore(); - increaseScore(SK_ValueToOptional); + increaseScore(SK_ValueToOptional, locator); assert(matchKind >= ConstraintKind::Subtype); if (auto generic2 = type2->getAs()) { @@ -13971,7 +13997,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( auto baseType1 = getFixedTypeRecursive(obj1->isArrayType(), false); auto ptr2 = getBaseTypeForPointer(t2); - increaseScore(SK_ValueToOptional, ptr2.getInt()); + increaseScore(SK_ValueToOptional, locator, ptr2.getInt()); return matchPointerBaseTypes({baseType1, 0}, ptr2); } @@ -13982,7 +14008,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( auto ptr2 = getBaseTypeForPointer(type2->getDesugaredType()); - increaseScore(SK_ValueToOptional, ptr2.getInt()); + increaseScore(SK_ValueToOptional, locator, ptr2.getInt()); // The pointer element type must be void or a byte-sized type. // TODO: Handle different encodings based on pointer element type, such as @@ -13992,7 +14018,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( // If we haven't resolved the element type, generate constraints. if (baseType2->isTypeVariableOrMember()) { if (flags.contains(TMF_GenerateConstraints)) { - increaseScore(ScoreKind::SK_ValueToPointerConversion); + increaseScore(ScoreKind::SK_ValueToPointerConversion, locator); auto &ctx = getASTContext(); auto int8Con = Constraint::create(*this, ConstraintKind::Bind, @@ -14019,7 +14045,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( return SolutionKind::Error; } - increaseScore(ScoreKind::SK_ValueToPointerConversion); + increaseScore(ScoreKind::SK_ValueToPointerConversion, locator); return SolutionKind::Solved; } @@ -14032,7 +14058,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( auto baseType1 = type1->getInOutObjectType(); auto ptr2 = getBaseTypeForPointer(t2); - increaseScore(SK_ValueToOptional, ptr2.getInt()); + increaseScore(SK_ValueToOptional, locator, ptr2.getInt()); return matchPointerBaseTypes({baseType1, 0}, ptr2); } @@ -14069,7 +14095,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( return SolutionKind::Error; } - increaseScore(SK_ValueToPointerConversion); + increaseScore(SK_ValueToPointerConversion, locator); type1 = getFixedTypeRecursive(type1->getInOutObjectType()->isArrayType(), /*wantRValue=*/false); @@ -14082,7 +14108,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( auto ptr2 = type2->getDesugaredType()->lookThroughAllOptionalTypes(optionals); - increaseScore(SK_ValueToOptional, optionals.size()); + increaseScore(SK_ValueToOptional, locator, optionals.size()); PointerTypeKind pointerKind; (void)ptr2->getAnyPointerElementType(pointerKind); @@ -14110,7 +14136,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( Type baseType1 = type1->isArrayType(); Type baseType2 = type2->isArrayType(); - increaseScore(SK_CollectionUpcastConversion); + increaseScore(SK_CollectionUpcastConversion, locator); return matchTypes(baseType1, baseType2, matchKind, @@ -14131,7 +14157,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( std::tie(key2, value2) = *isDictionaryType(t2); auto subMatchKind = matchKind; // TODO: Restrict this? - increaseScore(SK_CollectionUpcastConversion); + increaseScore(SK_CollectionUpcastConversion, locator); // The source key and value types must be subtypes of the destination // key and value types, respectively. auto result = @@ -14159,7 +14185,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( Type baseType1 = *isSetType(type1); Type baseType2 = *isSetType(type2); - increaseScore(SK_CollectionUpcastConversion); + increaseScore(SK_CollectionUpcastConversion, locator); return matchTypes(baseType1, baseType2, matchKind, @@ -14176,7 +14202,8 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( } addContextualScore(); - increaseScore(SK_UserConversion); // FIXME: Use separate score kind? + increaseScore(SK_UserConversion, + locator); // FIXME: Use separate score kind? if (worseThanBestSolution()) { return SolutionKind::Error; } @@ -14201,7 +14228,8 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( // T' < U and T a toll-free-bridged to T' ===> T' T SolutionKind { // Make sure that solutions with implicit pointer conversions // are always worse than the ones without them. - increaseScore(SK_ImplicitValueConversion); + increaseScore(SK_ImplicitValueConversion, locator); if (inCorrectPosition) return SolutionKind::Solved; @@ -14492,7 +14521,7 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) { // If this should affect the solution score, do so. if (auto impactScoreKind = fix->impact()) - increaseScore(*impactScoreKind, impact); + increaseScore(*impactScoreKind, fix->getLocator(), impact); // If we've made the current solution worse than the best solution we've seen // already, stop now. @@ -14964,7 +14993,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( // are completely disjoint and adjust impact of // the fix accordingly. if (auto *fnType2 = type2->getAs()) { - increaseScore(SK_Fix, 10); + increaseScore(SK_Fix, locator, 10); } else { // If type produced by expression is a function type // with result type matching contextual, it should have @@ -14973,7 +15002,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint( auto result = matchTypes(fnType1->getResult(), type2, matchKind, TMF_ApplyingFix, locator); if (result == SolutionKind::Solved) - increaseScore(SK_Fix); + increaseScore(SK_Fix, locator); } } diff --git a/lib/Sema/CSStep.cpp b/lib/Sema/CSStep.cpp index 7bb7383189ae8..080b4585b2cad 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -838,7 +838,7 @@ bool DisjunctionStep::attempt(const DisjunctionChoice &choice) { kind == ConstraintLocator::DynamicLookupResult) { assert(index == 0 || index == 1); if (index == 1) - CS.increaseScore(SK_ForceUnchecked); + CS.increaseScore(SK_ForceUnchecked, disjunctionLocator); } } } @@ -959,7 +959,7 @@ StepResult ConjunctionStep::resume(bool prevFailed) { if (Solutions.size() == 1) { auto score = Solutions.front().getFixedScore(); - if (score.Data[SK_Fix] > 0) + if (score.Data[SK_Fix] > 0 && !CS.getASTContext().CompletionCallback) Producer.markExhausted(); } } else if (Solutions.size() != 1) { @@ -1037,7 +1037,7 @@ StepResult ConjunctionStep::resume(bool prevFailed) { ++numHoles; } } - CS.increaseScore(SK_Hole, numHoles); + CS.increaseScore(SK_Hole, Conjunction->getLocator(), numHoles); } if (CS.worseThanBestSolution()) diff --git a/lib/Sema/CSStep.h b/lib/Sema/CSStep.h index 56c8f60677a10..469922441e762 100644 --- a/lib/Sema/CSStep.h +++ b/lib/Sema/CSStep.h @@ -1037,8 +1037,10 @@ class ConjunctionStep : public BindingStep { /// Restore best and current scores as they were before conjunction. void restoreCurrentScore(const Score &solutionScore) const { CS.CurrentScore = CurrentScore; - CS.increaseScore(SK_Fix, solutionScore.Data[SK_Fix]); - CS.increaseScore(SK_Hole, solutionScore.Data[SK_Hole]); + CS.increaseScore(SK_Fix, Conjunction->getLocator(), + solutionScore.Data[SK_Fix]); + CS.increaseScore(SK_Hole, Conjunction->getLocator(), + solutionScore.Data[SK_Hole]); } void restoreBestScore() const { CS.solverState->BestScore = BestScore; } diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 4ab0d5f882f74..26465899c3d9c 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -359,6 +359,13 @@ static void createConjunction(ConstraintSystem &cs, isIsolated = true; } + if (locator->directlyAt()) { + // Body of the interpolation is always isolated from its context, only + // its individual elements are allowed access to type information + // from the outside e.g. external declaration references. + isIsolated = true; + } + UnresolvedVarCollector paramCollector(cs); for (const auto &entry : elements) { @@ -410,13 +417,15 @@ ElementInfo makeJoinElement(ConstraintSystem &cs, TypeJoinExpr *join, struct SyntacticElementContext : public llvm::PointerUnion { + SingleValueStmtExpr *, ExprPattern *, TapExpr *> { // Inherit the constructors from PointerUnion. using PointerUnion::PointerUnion; /// A join that should be applied to the elements of a SingleValueStmtExpr. NullablePtr ElementJoin; + static SyntacticElementContext forTapExpr(TapExpr *tap) { return {tap}; } + static SyntacticElementContext forFunctionRef(AnyFunctionRef ref) { if (auto *decl = ref.getAbstractFunctionDecl()) { return {decl}; @@ -454,6 +463,8 @@ struct SyntacticElementContext return SVE->getDeclContext(); } else if (auto *EP = dyn_cast()) { return EP->getDeclContext(); + } else if (auto *tap = this->dyn_cast()) { + return tap->getVar()->getDeclContext(); } else { llvm_unreachable("unsupported kind"); } @@ -489,6 +500,8 @@ struct SyntacticElementContext return closure->getBody(); } else if (auto *SVE = dyn_cast()) { return SVE->getStmt(); + } else if (auto *tap = this->dyn_cast()) { + return tap->getBody(); } else { llvm_unreachable("unsupported kind"); } @@ -1154,17 +1167,26 @@ class SyntacticElementConstraintGenerator for (auto element : braceStmt->getElements()) { if (cs.isForCodeCompletion() && !cs.containsIDEInspectionTarget(element)) { - // Statements and expressions can't influence the expresion that - // contains the code completion token. To improve performance, skip - // type checking them entirely. - if (element.is() && !element.isExpr(ExprKind::TypeJoin)) { - // Type join expressions are not really pure expressions, they kind of - // declare new type variables and are important to a result builder's - // structure. Don't skip them. - continue; - } else if (element.is() && !element.isStmt(StmtKind::Guard)) { + // To improve performance, skip type checking elements that can't + // influence the code completion token. + if (element.is() && !element.isStmt(StmtKind::Guard) && !element.isStmt(StmtKind::Return)) { + // Statements can't influence the expresion that contains the code + // completion token. // Guard statements might define variables that are used in the code // completion expression. Don't skip them. + // Return statements influence the type of the closure itself. Don't + // skip them either. + continue; + } + if (element.isExpr(ExprKind::Assign)) { + // Assignments are also similar to statements and can't influence the + // code completion token. + continue; + } + if (element.isExpr(ExprKind::Error)) { + // ErrorExpr can't influcence the expresssion that contains the code + // completion token. Since they are causing type checking to abort + // early, just skip them. continue; } } @@ -1369,6 +1391,22 @@ class SyntacticElementConstraintGenerator }; } +bool ConstraintSystem::generateConstraints(TapExpr *tap) { + SyntacticElementConstraintGenerator generator( + *this, SyntacticElementContext::forTapExpr(tap), + getConstraintLocator(tap)); + + auto *body = tap->getBody(); + + if (!body) { + assert(tap->getSubExpr()); + return false; + } + + generator.visit(tap->getBody()); + return generator.hadError; +} + bool ConstraintSystem::generateConstraints(AnyFunctionRef fn, BraceStmt *body) { NullablePtr locator; @@ -1542,6 +1580,8 @@ ConstraintSystem::simplifySyntacticElementConstraint( context = SyntacticElementContext::forSingleValueStmtExpr(SVE); } else if (auto *EP = getAsPattern(anchor)) { context = SyntacticElementContext::forExprPattern(EP); + } else if (auto *tap = getAsExpr(anchor)) { + context = SyntacticElementContext::forTapExpr(tap); } else { return SolutionKind::Error; } @@ -1605,7 +1645,6 @@ class SyntacticElementSolutionApplication protected: Solution &solution; SyntacticElementContext context; - Type resultType; RewriteTargetFn rewriteTarget; /// All `func`s declared in the body of the closure. @@ -1618,24 +1657,39 @@ class SyntacticElementSolutionApplication SyntacticElementSolutionApplication(Solution &solution, SyntacticElementContext context, RewriteTargetFn rewriteTarget) - : solution(solution), context(context), rewriteTarget(rewriteTarget) { - if (auto fn = AnyFunctionRef::fromDeclContext(context.getAsDeclContext())) { + : solution(solution), context(context), rewriteTarget(rewriteTarget) {} + + virtual ~SyntacticElementSolutionApplication() {} + +private: + Type getContextualResultType() const { + // Taps do not have a contextual result type. + if (context.is()) { + return Type(); + } + + auto fn = context.getAsAnyFunctionRef(); + + if (context.is()) { + // if/switch expressions can have `return` inside. + fn = AnyFunctionRef::fromDeclContext(context.getAsDeclContext()); + } + + if (fn) { if (auto transform = solution.getAppliedBuilderTransform(*fn)) { - resultType = solution.simplifyType(transform->bodyResultType); + return solution.simplifyType(transform->bodyResultType); } else if (auto *closure = getAsExpr(fn->getAbstractClosureExpr())) { - resultType = solution.getResolvedType(closure) - ->castTo() - ->getResult(); + return solution.getResolvedType(closure) + ->castTo() + ->getResult(); } else { - resultType = fn->getBodyResultType(); + return fn->getBodyResultType(); } } - } - virtual ~SyntacticElementSolutionApplication() {} - -private: + return Type(); + } ASTNode visit(Stmt *S, bool performSyntacticDiagnostics = true) { auto rewritten = ASTVisitor::visit(S); @@ -1991,17 +2045,18 @@ class SyntacticElementSolutionApplication auto closure = context.getAsAbstractClosureExpr(); if (closure && !closure.get()->hasSingleExpressionBody() && closure.get()->getBody() == braceStmt) { + auto resultType = getContextualResultType(); if (resultType->getOptionalObjectType() && resultType->lookThroughAllOptionalTypes()->isVoid() && !braceStmt->getLastElement().isStmt(StmtKind::Return)) { - return addImplicitVoidReturn(braceStmt); + return addImplicitVoidReturn(braceStmt, resultType); } } return braceStmt; } - ASTNode addImplicitVoidReturn(BraceStmt *braceStmt) { + ASTNode addImplicitVoidReturn(BraceStmt *braceStmt, Type contextualResultTy) { auto &cs = solution.getConstraintSystem(); auto &ctx = cs.getASTContext(); @@ -2016,7 +2071,7 @@ class SyntacticElementSolutionApplication // number of times. { SyntacticElementTarget target(resultExpr, context.getAsDeclContext(), - CTP_ReturnStmt, resultType, + CTP_ReturnStmt, contextualResultTy, /*isDiscarded=*/false); cs.setTargetFor(returnStmt, target); @@ -2037,6 +2092,8 @@ class SyntacticElementSolutionApplication ASTNode visitReturnStmt(ReturnStmt *returnStmt) { auto &cs = solution.getConstraintSystem(); + auto resultType = getContextualResultType(); + if (!returnStmt->hasResult()) { // If contextual is not optional, there is nothing to do here. if (resultType->isVoid()) @@ -2530,8 +2587,23 @@ bool ConstraintSystem::applySolutionToBody(Solution &solution, return false; } +bool ConstraintSystem::applySolutionToBody(Solution &solution, TapExpr *tapExpr, + DeclContext *¤tDC, + RewriteTargetFn rewriteTarget) { + SyntacticElementSolutionApplication application( + solution, SyntacticElementContext::forTapExpr(tapExpr), rewriteTarget); + + auto body = application.apply(); + + if (!body || application.hadError) + return true; + + tapExpr->setBody(castToStmt(body)); + return false; +} + bool ConjunctionElement::mightContainCodeCompletionToken( - const ConstraintSystem &cs) const { + const ConstraintSystem &cs) const { if (Element->getKind() == ConstraintKind::SyntacticElement) { if (Element->getSyntacticElement().getSourceRange().isInvalid()) { return true; diff --git a/lib/Sema/ConstantnessSemaDiagnostics.cpp b/lib/Sema/ConstantnessSemaDiagnostics.cpp index 9f39f0bc3cf36..06f2599f4e7ac 100644 --- a/lib/Sema/ConstantnessSemaDiagnostics.cpp +++ b/lib/Sema/ConstantnessSemaDiagnostics.cpp @@ -353,14 +353,7 @@ void swift::diagnoseConstantArgumentRequirement( return walkToClosureExprPre(closureExpr); } - // Interpolated expressions' bodies will be type checked - // separately so exit early to avoid duplicate diagnostics. - // The caveat is that they won't be checked inside closure - // bodies because we manually check all closures to avoid - // duplicate diagnostics. Therefore we must still descend into - // interpolated expressions if we are inside of a closure. - if (!expr || isa(expr) || !expr->getType() || - (isa(expr) && !insideClosure)) + if (!expr || isa(expr) || !expr->getType()) return Action::SkipChildren(expr); if (auto *callExpr = dyn_cast(expr)) { diagnoseConstantArgumentRequirementOfCall(callExpr, DC->getASTContext()); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 9622977e0be9c..ae4d4f39d2e0c 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -225,7 +225,7 @@ void ConstraintSystem::assignFixedType(TypeVariableType *typeVar, Type type, // Check whether the nominal types match. This makes sure that we // properly handle Array vs. Array. if (defaultType->getAnyNominal() != type->getAnyNominal()) { - increaseScore(SK_NonDefaultLiteral); + increaseScore(SK_NonDefaultLiteral, locator); } } @@ -515,7 +515,7 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( // If we have a locator that starts with a key path component element, we // may have a callee given by a property or subscript component. if (auto componentElt = - locator->getFirstElementAs()) { + locator->getFirstElementAs()) { auto *kpExpr = castToExpr(anchor); auto component = kpExpr->getComponents()[componentElt->getIndex()]; @@ -525,7 +525,7 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( case ComponentKind::Subscript: // For a subscript the callee is given by 'component -> subscript member'. return getConstraintLocator( - anchor, {*componentElt, ConstraintLocator::SubscriptMember}); + anchor, {*componentElt, ConstraintLocator::SubscriptMember}); case ComponentKind::UnresolvedProperty: case ComponentKind::Property: // For a property, the choice is just given by the component. @@ -563,14 +563,14 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( if (fnTy->is()) { return getConstraintLocator(anchor, {LocatorPathElt::ApplyFunction(), - LocatorPathElt::ConstructorMember()}); + LocatorPathElt::ConstructorMember()}); } // Handle an apply of a nominal type which supports callAsFunction. if (fnTy->isCallAsFunctionType(DC)) { return getConstraintLocator(anchor, {LocatorPathElt::ApplyFunction(), - LocatorPathElt::ImplicitCallAsFunction()}); + LocatorPathElt::ImplicitCallAsFunction()}); } // Handling an apply for a nominal type that supports @dynamicCallable. @@ -605,13 +605,13 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( UDE->getName().getBaseName() == Context.Id_callAsFunction) { return getConstraintLocator(anchor, {LocatorPathElt::ApplyFunction(), - LocatorPathElt::ImplicitCallAsFunction()}); + LocatorPathElt::ImplicitCallAsFunction()}); } return getConstraintLocator( - anchor, TypeChecker::getSelfForInitDelegationInConstructor(DC, UDE) - ? ConstraintLocator::ConstructorMember - : ConstraintLocator::Member); + anchor, TypeChecker::getSelfForInitDelegationInConstructor(DC, UDE) + ? ConstraintLocator::ConstructorMember + : ConstraintLocator::Member); } if (auto *UME = getAsExpr(anchor)) { @@ -632,6 +632,9 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( getOverloadFor); } + if (auto FVE = getAsExpr(anchor)) + return getConstraintLocator(FVE->getSubExpr(), ConstraintLocator::Member); + return getConstraintLocator(anchor); } @@ -3450,7 +3453,7 @@ void ConstraintSystem::bindOverloadType( if (isSubscriptRef) { // Make sure that regular subscript declarations (if any) are // preferred over key path dynamic member lookup. - increaseScore(SK_KeyPathSubscript); + increaseScore(SK_KeyPathSubscript, locator); auto boundTypeVar = boundType->castTo(); auto constraints = getConstraintGraph().gatherConstraints( @@ -3660,7 +3663,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, auto SE = getAsExpr(locator->getAnchor()); if (!isForCodeCompletion() || (SE && !containsIDEInspectionTarget(SE->getArgs()))) { - increaseScore(SK_KeyPathSubscript); + increaseScore(SK_KeyPathSubscript, locator); } break; } @@ -3676,8 +3679,9 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, if (!Options.contains(ConstraintSystemFlags::IgnoreAsyncSyncMismatch) && !func->hasPolymorphicEffect(EffectKind::Async) && func->isAsyncContext() != isAsynchronousContext(useDC)) { - increaseScore( - func->isAsyncContext() ? SK_AsyncInSyncMismatch : SK_SyncInAsync); + increaseScore(func->isAsyncContext() ? SK_AsyncInSyncMismatch + : SK_SyncInAsync, + locator); } } @@ -3704,7 +3708,7 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, // choices based on the selector kind on the valid code path. if (choice.getFunctionRefKind() == FunctionRefKind::Unapplied && !UnevaluatedRootExprs.contains(getAsExpr(anchor))) { - increaseScore(SK_UnappliedFunction); + increaseScore(SK_UnappliedFunction, locator); } } @@ -3798,15 +3802,15 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, if (auto *decl = choice.getDeclOrNull()) { // If the declaration is unavailable, note that in the score. if (isDeclUnavailable(decl, locator)) - increaseScore(SK_Unavailable); + increaseScore(SK_Unavailable, locator); // If this overload is disfavored, note that. if (decl->getAttrs().hasAttribute()) - increaseScore(SK_DisfavoredOverload); + increaseScore(SK_DisfavoredOverload, locator); } if (choice.isFallbackMemberOnUnwrappedBase()) { - increaseScore(SK_UnresolvedMemberViaOptional); + increaseScore(SK_UnresolvedMemberViaOptional, locator); } } diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index ec048eba14622..31e043be4ebfa 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -134,8 +134,6 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - PreWalkResult walkToExprPre(Expr *E) override { // See through implicit conversions of the expression. We want to be able // to associate the parent of this expression with the ultimate callee. @@ -1482,8 +1480,6 @@ static void diagRecursivePropertyAccess(const Expr *E, const DeclContext *DC) { bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -1774,8 +1770,6 @@ static void diagnoseImplicitSelfUseInClosure(const Expr *E, bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - PreWalkResult walkToExprPre(Expr *E) override { if (auto *CE = dyn_cast(E)) { // If this is a potentially-escaping closure expression, start looking @@ -4245,8 +4239,6 @@ static void checkStmtConditionTrailingClosure(ASTContext &ctx, const Expr *E) { bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -4376,8 +4368,6 @@ class ObjCSelectorWalker : public ASTWalker { bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -5240,8 +5230,6 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E, bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -5321,8 +5309,6 @@ static void diagnoseDeprecatedWritableKeyPath(const Expr *E, bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -5617,8 +5603,6 @@ static void diagUnqualifiedAccessToMethodNamedSelf(const Expr *E, return false; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } @@ -5778,8 +5762,6 @@ diagnoseDictionaryLiteralDuplicateKeyEntries(const Expr *E, return false; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } diff --git a/lib/Sema/TypeCheckAvailability.cpp b/lib/Sema/TypeCheckAvailability.cpp index 4e05a20160840..548f5d52dc365 100644 --- a/lib/Sema/TypeCheckAvailability.cpp +++ b/lib/Sema/TypeCheckAvailability.cpp @@ -3191,8 +3191,6 @@ class ExprAvailabilityWalker : public ASTWalker { return false; } - bool shouldWalkIntoTapExpression() override { return false; } - MacroWalking getMacroWalkingBehavior() const override { // Expanded source should be type checked and diagnosed separately. return MacroWalking::Arguments; diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 996f50992c721..ad16602c65fc2 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -301,6 +301,13 @@ static Type getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, ConcreteDeclRef &referencedDecl, FreeTypeVariableBinding allowFreeTypeVariables) { + if (isa(dc)) { + // If the expression is embedded in a closure, the constraint system tries + // to retrieve that closure's type, which will fail since we won't have + // generated any type variables for it. Thus, fallback type checking isn't + // available in this case. + return Type(); + } auto &Context = dc->getASTContext(); expr = expr->walk(SanitizeExpr(Context)); @@ -586,15 +593,6 @@ bool TypeChecker::typeCheckForCodeCompletion( if (!contextAnalyzer.hasCompletion()) return false; - // Interpolation components are type-checked separately. - if (contextAnalyzer.locatedInStringInterpolation()) - return false; - - // FIXME: There is currently no way to distinguish between - // multi-statement closures which are result builder bodies - // (that are type-checked together with enclosing context) - // and regular closures which are type-checked separately. - if (needsPrecheck) { // First, pre-check the expression, validating any types that occur in the // expression and folding sequence expressions. @@ -656,18 +654,27 @@ bool TypeChecker::typeCheckForCodeCompletion( // Determine the best subexpression to use based on the collected context // of the code completion expression. - if (auto fallback = contextAnalyzer.getFallbackCompletionExpr()) { - if (auto *expr = target.getAsExpr()) { - assert(fallback->E != expr); - (void)expr; - } - SyntacticElementTarget completionTarget(fallback->E, fallback->DC, - CTP_Unused, - /*contextualType=*/Type(), - /*isDiscarded=*/true); - typeCheckForCodeCompletion(completionTarget, fallback->SeparatePrecheck, - callback); + auto fallback = contextAnalyzer.getFallbackCompletionExpr(); + if (!fallback) { + return true; + } + if (isa(fallback->DC)) { + // If the expression is embedded in a closure, the constraint system tries + // to retrieve that closure's type, which will fail since we won't have + // generated any type variables for it. Thus, fallback type checking isn't + // available in this case. + return true; + } + if (auto *expr = target.getAsExpr()) { + assert(fallback->E != expr); + (void)expr; } + SyntacticElementTarget completionTarget(fallback->E, fallback->DC, + CTP_Unused, + /*contextualType=*/Type(), + /*isDiscarded=*/true); + typeCheckForCodeCompletion(completionTarget, fallback->SeparatePrecheck, + callback); return true; } diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 10db167533654..63145dfc294f2 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -2156,8 +2156,6 @@ namespace { bool shouldWalkCaptureInitializerExpressions() override { return true; } - bool shouldWalkIntoTapExpression() override { return true; } - MacroWalking getMacroWalkingBehavior() const override { return MacroWalking::Expansion; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index 80f874ecb2f23..6e17751e20aaf 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -120,6 +120,10 @@ bool TypeVariableType::Implementation::isClosureType() const { return isExpr(locator->getAnchor()) && locator->getPath().empty(); } +bool TypeVariableType::Implementation::isTapType() const { + return locator && locator->directlyAt(); +} + bool TypeVariableType::Implementation::isClosureParameterType() const { if (!(locator && locator->getAnchor())) return false; diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 0e5b6aa9792bf..f1b5ad8927b85 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -2586,19 +2586,23 @@ bool TypeCheckASTNodeAtLocRequest::evaluate( auto optBody = TypeChecker::applyResultBuilderBodyTransform( func, builderType, /*ClosuresInResultBuilderDontParticipateInInference=*/ - ctx.CompletionCallback == nullptr && ctx.SolutionCallback == nullptr); - if (optBody && *optBody) { + ctx.CompletionCallback == nullptr && ctx.SolutionCallback == nullptr); + if ((ctx.CompletionCallback && ctx.CompletionCallback->gotCallback()) || (ctx.SolutionCallback && ctx.SolutionCallback->gotCallback())) { + // We already informed the completion callback of solutions found by + // type checking the entire result builder from + // applyResultBuilderBodyTransform. No need to typecheck the requested + // AST node individually anymore. + return false; + } + if (!ctx.CompletionCallback && !ctx.SolutionCallback && optBody && *optBody) { // Wire up the function body now. func->setBody(*optBody, AbstractFunctionDecl::BodyKind::TypeChecked); return false; } - // FIXME: We failed to apply the result builder transform. Fall back to - // just type checking the node that contains the code completion token. - // This may be missing some context from the result builder but in - // practice it often contains sufficient information to provide a decent - // level of code completion that's better than providing nothing at all. - // The proper solution would be to only partially type check the result - // builder so that this fall back would not be necessary. + // We did not find a solution while applying the result builder, possibly + // because the result builder contained an invalid element and thus the + // transform couldn't be applied. Perform code completion pretending there + // was no result builder to recover. } else if (func->hasSingleExpressionBody() && func->getResultInterfaceType()->isVoid()) { // The function returns void. We don't need an explicit return, no matter diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 16c4d719be063..db3098fe842c0 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -624,7 +624,10 @@ extension P_47606 { let u = rdar33296619().element //expected-error {{cannot find 'rdar33296619' in scope}} [1].forEach { _ in - _ = "\(u)" + _ = "\(u)" // No diagnostic because `u` is already diagnosed and marked as invalid +} + +[1].forEach { _ in _ = 1 + "hi" // expected-error {{binary operator '+' cannot be applied to operands of type 'Int' and 'String'}} // expected-note@-1 {{overloads for '+' exist with these partially matching parameter lists: (Int, Int), (String, String)}} } diff --git a/test/Constraints/interpolation_segments.swift b/test/Constraints/interpolation_segments.swift deleted file mode 100644 index 4c2e02ee7a9c2..0000000000000 --- a/test/Constraints/interpolation_segments.swift +++ /dev/null @@ -1,21 +0,0 @@ -// RUN: %target-typecheck-verify-swift -debug-constraints > %t.dump 2>&1 -// RUN: %FileCheck %s < %t.dump - -// Make sure that the type checker doesn't initially try to solve the appendLiteral and -// appendInterpolation calls. Specifically, we check that the string literal -// has been assigned a type variable, but the calls inside the body have not. - -// CHECK: ---Initial constraints for the given expression--- -// CHECK: (interpolated_string_literal_expr type='$T -// CHECK-NOT: (call_expr implicit type='$T -// CHECK: ---Solution--- - -// We also check that the type checker did not need to evaluate any -// DefaultStringInterpolation overloads in the initial expression. - -// CHECK-NOT: Constraint restrictions: -// CHECK: --- Solution #{{.*}}--- -// CHECK: Overload choices: -// CHECK: Swift.(file).DefaultStringInterpolation.append - -_ = "\(1), \(2), \(3), \(4)" diff --git a/test/IDE/complete_after_pattern_in_closure.swift b/test/IDE/complete_after_pattern_in_closure.swift new file mode 100644 index 0000000000000..769c06cb577e9 --- /dev/null +++ b/test/IDE/complete_after_pattern_in_closure.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -enable-experimental-concurrency + +func makeURL(withExtension ext: Int?) -> Int? { + return nil +} + +func test() { + let calculatorContext: Int? = { + guard let url#^COMPLETE^# = makeURL(withExtension: 1), + let script = url else { + return nil + } + }() + // COMPLETE-NOT: Begin completions +} diff --git a/test/IDE/complete_after_super.swift b/test/IDE/complete_after_super.swift index 25bffeb453089..b63713fef4c9c 100644 --- a/test/IDE/complete_after_super.swift +++ b/test/IDE/complete_after_super.swift @@ -116,8 +116,17 @@ class SuperDerivedA : SuperBaseA { init() { super#^CONSTRUCTOR_SUPER_NO_DOT_1?check=COMMON_BASE_A_NO_DOT;check=CONSTRUCTOR_SUPER_NO_DOT_1^# -// CONSTRUCTOR_SUPER_NO_DOT_1: Begin completions, 8 items -// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[Constructor]/CurrNominal/Flair[SuperChain]: .init()[#SuperBaseA#]{{; name=.+$}} +// CONSTRUCTOR_SUPER_NO_DOT_1: Begin completions, 10 items +// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: .baseInstanceVar[#Int#]; +// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: .baseProp[#Int#]; +// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc0()[#Void#]; +// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc1({#(a): Int#})[#Void#]; +// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[Subscript]/CurrNominal: [{#(i): Int#}][#Double#]; +// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[Constructor]/CurrNominal/Flair[SuperChain]: .init()[#SuperBaseA#]; +// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: .baseExtProp[#Int#]; +// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: .baseExtFunc0()[#Void#]; +// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: !== {#AnyObject?#}[#Bool#]; +// CONSTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: === {#AnyObject?#}[#Bool#]; } init(a: Int) { @@ -136,17 +145,26 @@ class SuperDerivedA : SuperBaseA { init (a: Float) { super.init#^CONSTRUCTOR_SUPER_INIT_1^# -// CONSTRUCTOR_SUPER_INIT_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#SuperBaseA#]; name=() +// CONSTRUCTOR_SUPER_INIT_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ()[#SuperBaseA#]; } init (b: Float) { super.init(#^CONSTRUCTOR_SUPER_INIT_PAREN_1^# // CONSTRUCTOR_SUPER_INIT_PAREN_1: Begin completions, 1 items -// CONSTRUCTOR_SUPER_INIT_PAREN_1: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#SuperBaseA#]; name= +// CONSTRUCTOR_SUPER_INIT_PAREN_1: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#SuperBaseA#]; } deinit { super#^DESTRUCTOR_SUPER_NO_DOT_1?check=COMMON_BASE_A_NO_DOT;check=DESTRUCTOR_SUPER_NO_DOT_1;check=NO_CONSTRUCTORS^# -// DESTRUCTOR_SUPER_NO_DOT_1: Begin completions, 7 items +// DESTRUCTOR_SUPER_NO_DOT_1: Begin completions, 9 items +// DESTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: .baseInstanceVar[#Int#]; +// DESTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: .baseProp[#Int#]; +// DESTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc0()[#Void#]; +// DESTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc1({#(a): Int#})[#Void#]; +// DESTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[Subscript]/CurrNominal: [{#(i): Int#}][#Double#]; +// DESTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: .baseExtProp[#Int#]; +// DESTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: .baseExtFunc0()[#Void#]; +// DESTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: !== {#AnyObject?#}[#Bool#]; +// DESTRUCTOR_SUPER_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: === {#AnyObject?#}[#Bool#]; var resyncParser = 42 @@ -156,7 +174,16 @@ class SuperDerivedA : SuperBaseA { func test1() { super#^FUNC_SUPER_NO_DOT_1?check=COMMON_BASE_A_NO_DOT;check=FUNC_SUPER_NO_DOT_1;check=NO_CONSTRUCTORS^# -// FUNC_SUPER_NO_DOT_1: Begin completions, 7 items +// FUNC_SUPER_NO_DOT_1: Begin completions, 9 items +// FUNC_SUPER_NO_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: .baseInstanceVar[#Int#]; +// FUNC_SUPER_NO_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: .baseProp[#Int#]; +// FUNC_SUPER_NO_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc0()[#Void#]; +// FUNC_SUPER_NO_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc1({#(a): Int#})[#Void#]; +// FUNC_SUPER_NO_DOT_1-DAG: Decl[Subscript]/CurrNominal: [{#(i): Int#}][#Double#]; +// FUNC_SUPER_NO_DOT_1-DAG: Decl[InstanceVar]/CurrNominal: .baseExtProp[#Int#]; +// FUNC_SUPER_NO_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: .baseExtFunc0()[#Void#]; +// FUNC_SUPER_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: !== {#AnyObject?#}[#Bool#]; +// FUNC_SUPER_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: === {#AnyObject?#}[#Bool#]; } func test2() { @@ -271,10 +298,19 @@ class SuperDerivedB : SuperBaseB { init() { super#^CONSTRUCTOR_SUPER_NO_DOT_2?check=COMMON_BASE_B_NO_DOT;check=CONSTRUCTOR_SUPER_NO_DOT_2^# -// CONSTRUCTOR_SUPER_NO_DOT_2: Begin completions, 10 items -// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Constructor]/CurrNominal/Flair[SuperChain]: .init()[#SuperBaseB#]{{; name=.+$}} -// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Constructor]/CurrNominal: .init({#a: Double#})[#SuperBaseB#]{{; name=.+$}} -// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Constructor]/CurrNominal: .init({#int: Int#})[#SuperBaseB#]{{; name=.+$}} +// CONSTRUCTOR_SUPER_NO_DOT_2: Begin completions, 12 items +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: .baseInstanceVar[#Int#]; +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: .baseProp[#Int#]; +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Constructor]/CurrNominal/Flair[SuperChain]: .init()[#SuperBaseB#]; +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Constructor]/CurrNominal: .init({#a: Double#})[#SuperBaseB#]; +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Constructor]/CurrNominal: .init({#int: Int#})[#SuperBaseB#]; +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc0()[#Void#]; +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc1({#(a): Int#})[#Void#]; +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Subscript]/CurrNominal: [{#(i): Int#}][#Double#]; +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: .baseExtProp[#Int#]; +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: .baseExtFunc0()[#Void#]; +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: !== {#AnyObject?#}[#Bool#]; +// CONSTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: === {#AnyObject?#}[#Bool#]; } init(int a: Int) { @@ -287,17 +323,41 @@ class SuperDerivedB : SuperBaseB { deinit { super#^DESTRUCTOR_SUPER_NO_DOT_2?check=COMMON_BASE_B_NO_DOT;check=DESTRUCTOR_SUPER_NO_DOT_2;check=NO_CONSTRUCTORS^# -// DESTRUCTOR_SUPER_NO_DOT_2: Begin completions, 7 items +// DESTRUCTOR_SUPER_NO_DOT_2: Begin completions, 9 items +// DESTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: .baseInstanceVar[#Int#]; +// DESTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: .baseProp[#Int#]; +// DESTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc0()[#Void#]; +// DESTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc1({#(a): Int#})[#Void#]; +// DESTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[Subscript]/CurrNominal: [{#(i): Int#}][#Double#]; +// DESTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: .baseExtProp[#Int#]; +// DESTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: .baseExtFunc0()[#Void#]; +// DESTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: !== {#AnyObject?#}[#Bool#]; +// DESTRUCTOR_SUPER_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: === {#AnyObject?#}[#Bool#]; var resyncParser = 42 super.#^DESTRUCTOR_SUPER_DOT_2?check=COMMON_BASE_B_DOT;check=DESTRUCTOR_SUPER_DOT_2;check=NO_CONSTRUCTORS^# // DESTRUCTOR_SUPER_DOT_2: Begin completions, 6 items +// DESTRUCTOR_SUPER_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: baseInstanceVar[#Int#]; +// DESTRUCTOR_SUPER_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: baseProp[#Int#]; +// DESTRUCTOR_SUPER_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: baseFunc0()[#Void#]; +// DESTRUCTOR_SUPER_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: baseFunc1({#(a): Int#})[#Void#]; +// DESTRUCTOR_SUPER_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: baseExtProp[#Int#]; +// DESTRUCTOR_SUPER_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: baseExtFunc0()[#Void#]; } func test1() { super#^FUNC_SUPER_NO_DOT_2?check=COMMON_BASE_B_NO_DOT;check=FUNC_SUPER_NO_DOT_2;check=NO_CONSTRUCTORS^# -// FUNC_SUPER_NO_DOT_2: Begin completions, 7 items +// FUNC_SUPER_NO_DOT_2: Begin completions, 9 items +// FUNC_SUPER_NO_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: .baseInstanceVar[#Int#]; +// FUNC_SUPER_NO_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: .baseProp[#Int#]; +// FUNC_SUPER_NO_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc0()[#Void#]; +// FUNC_SUPER_NO_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: .baseFunc1({#(a): Int#})[#Void#]; +// FUNC_SUPER_NO_DOT_2-DAG: Decl[Subscript]/CurrNominal: [{#(i): Int#}][#Double#]; +// FUNC_SUPER_NO_DOT_2-DAG: Decl[InstanceVar]/CurrNominal: .baseExtProp[#Int#]; +// FUNC_SUPER_NO_DOT_2-DAG: Decl[InstanceMethod]/CurrNominal: .baseExtFunc0()[#Void#]; +// FUNC_SUPER_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: !== {#AnyObject?#}[#Bool#]; +// FUNC_SUPER_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: === {#AnyObject?#}[#Bool#]; } func test2() { diff --git a/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index d5df091e49eb6..a603940e465e0 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -51,8 +51,9 @@ func testCallAsFunctionDeduplication() { overloaded()#^SKIP_CALLASFUNCTION_DUPLICATES^# } -// FIXME: Update this to check the callAsFunction pattern only appears once when PostfixExpr completion is migrated to the solver-based implementation (which handles ambiguity). -// SKIP_CALLASFUNCTION_DUPLICATES-NOT: Begin completions +// SKIP_CALLASFUNCTION_DUPLICATES: Begin completions +// SKIP_CALLASFUNCTION_DUPLICATES-DAG: Decl[InstanceMethod]/CurrNominal: .callAsFunction({#x: Int#})[#Void#]; +// SKIP_CALLASFUNCTION_DUPLICATES: End completions givenErrorExpr(undefined).#^ERROR_IN_BASE?check=SIMPLE^# @@ -394,8 +395,12 @@ CreateThings { CreateThings { Thing { point in print("hello") - point. // ErrorExpr - point.#^MULTICLOSURE_FUNCBUILDER_ERROR?check=POINT_MEMBER^# + do { + point. // ErrorExpr + } + do { + point.#^MULTICLOSURE_FUNCBUILDER_ERROR?check=POINT_MEMBER^# + } } Thing { point in print("hello") @@ -443,8 +448,8 @@ struct Struct123: Equatable { } func testBestSolutionFilter() { let a = Struct123(); - let b = [Struct123]().first(where: { $0 == a && 1 + 90 * 5 / 8 == 45 * -10 })?.structMem != .#^BEST_SOLUTION_FILTER?xfail=rdar73282163^# - let c = min(10.3, 10 / 10.4) < 6 / 7 ? true : Optional(a)?.structMem != .#^BEST_SOLUTION_FILTER2?check=BEST_SOLUTION_FILTER;xfail=rdar73282163^# + let b = [Struct123]().first(where: { $0 == a && 1 + 90 * 5 / 8 == 45 * -10 })?.structMem != .#^BEST_SOLUTION_FILTER^# + let c = min(10.3, 10 / 10.4) < 6 / 7 ? true : Optional(a)?.structMem != .#^BEST_SOLUTION_FILTER2?check=BEST_SOLUTION_FILTER^# } // BEST_SOLUTION_FILTER-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: enumElem[#Enum123#]{{; name=.+$}} diff --git a/test/IDE/complete_at_top_level.swift b/test/IDE/complete_at_top_level.swift index 2951456b1ea94..9296c767cd8da 100644 --- a/test/IDE/complete_at_top_level.swift +++ b/test/IDE/complete_at_top_level.swift @@ -40,7 +40,7 @@ func resyncParser1() {} fooObject#^TYPE_CHECKED_EXPR_1^# // TYPE_CHECKED_EXPR_1-DAG: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_1-DAG: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} -// TYPE_CHECKED_EXPR_1-DAG: BuiltinOperator/None: = {#FooStruct#}[#Void#]{{; name=.+$}} +// TYPE_CHECKED_EXPR_1-DAG: BuiltinOperator/None: = {#FooStruct#}{{; name=.+$}} // TYPE_CHECKED_EXPR_1-DAG: Keyword[self]/CurrNominal: .self[#FooStruct#]{{; name=.+$}} func resyncParser2() {} @@ -51,7 +51,7 @@ var _tmpVar1 : FooStruct fooObject#^TYPE_CHECKED_EXPR_2^# // TYPE_CHECKED_EXPR_2-DAG: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_2-DAG: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} -// TYPE_CHECKED_EXPR_2-DAG: BuiltinOperator/None: = {#FooStruct#}[#Void#]{{; name=.+$}} +// TYPE_CHECKED_EXPR_2-DAG: BuiltinOperator/None: = {#FooStruct#}{{; name=.+$}} // TYPE_CHECKED_EXPR_2-DAG: Keyword[self]/CurrNominal: .self[#FooStruct#]{{; name=.+$}} func resyncParser3() {} @@ -59,7 +59,7 @@ func resyncParser3() {} fooObject#^TYPE_CHECKED_EXPR_3^#.bar // TYPE_CHECKED_EXPR_3-DAG: Decl[InstanceVar]/CurrNominal: .instanceVar[#Int#]{{; name=.+$}} // TYPE_CHECKED_EXPR_3-DAG: Decl[InstanceMethod]/CurrNominal: .instanceFunc({#(a): Int#})[#Void#]{{; name=.+$}} -// TYPE_CHECKED_EXPR_3-DAG: BuiltinOperator/None: = {#FooStruct#}[#Void#]{{; name=.+$}} +// TYPE_CHECKED_EXPR_3-DAG: BuiltinOperator/None: = {#FooStruct#}{{; name=.+$}} // TYPE_CHECKED_EXPR_3-DAG: Keyword[self]/CurrNominal: .self[#FooStruct#]{{; name=.+$}} func resyncParser4() {} @@ -295,7 +295,7 @@ func resyncParserB14() {} "\(1) \(#^STRING_INTERP_2?check=STRING_INTERP^#) \(2)" var stringInterp = "\(#^STRING_INTERP_3?check=STRING_INTERP^#)" _ = "" + "\(#^STRING_INTERP_4?check=STRING_INTERP^#)" + "" -// STRING_INTERP-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]/IsSystem: ['(']{#(value): T#}[')'][#Void#]; +// STRING_INTERP-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]/IsSystem: ['(']{#(value): any Any.Type#}[')'][#Void#]; // STRING_INTERP-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]; name=FooStruct // STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Invalid]: fooFunc1()[#Void#]; // STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule: optStr()[#String?#]; diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index 88cd522d5dc67..c0f322d1553bd 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -221,7 +221,7 @@ class C3 { // OVERLOAD6-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(a1): C1#}, {#b1: C2#}[')'][#Void#]; name=:b1: // OVERLOAD6-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#a2: C2#}, {#b2: C1#}[')'][#Void#]; name=a2:b2: // OVERLOAD6-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: C1I[#C1#]; name=C1I -// OVERLOAD6-DAG: Decl[InstanceVar]/CurrNominal: C2I[#C2#]; name=C2I +// OVERLOAD6-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: C2I[#C2#]; name=C2I extension C3 { func hasError(a1: C1, b1: TypeInvalid) -> Int {} @@ -234,7 +234,7 @@ extension C3 { } } -// HASERROR1-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#a1: C1#}, {#b1: <>#}[')'][#Int#]; +// HASERROR1-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#a1: C1#}, {#b1: _#}[')'][#Int#]; // HASERROR2-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: C1I[#C1#]; // HASERROR2-DAG: Decl[InstanceVar]/CurrNominal: C2I[#C2#]; @@ -426,8 +426,8 @@ class Bar { func curry(_ f: @escaping (T1, T2) -> R) -> (T1) -> (T2) -> R { return { t1 in { t2 in f(#^NESTED_CLOSURE^#, t2) } } // FIXME: Should be '/TypeRelation[Invalid]: t2[#T2#]' - // NESTED_CLOSURE: Decl[LocalVar]/Local: t2; name=t2 - // NESTED_CLOSURE: Decl[LocalVar]/Local: t1; name=t1 + // NESTED_CLOSURE: Decl[LocalVar]/Local: t2[#T2#]; name=t2 + // NESTED_CLOSURE: Decl[LocalVar]/Local/TypeRelation[Convertible]: t1[#T1#]; name=t1 } func trailingClosureLocal(x: Int, fn: (Int) -> Void) { @@ -554,8 +554,8 @@ func testStaticMemberCall() { // STATIC_METHOD_AFTERPAREN_1: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: let _ = TestStaticMemberCall.create2(#^STATIC_METHOD_AFTERPAREN_2^#) -// STATIC_METHOD_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#(arg1): Int#}[')'][#TestStaticMemberCall#]; -// STATIC_METHOD_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#(arg1): Int#}, {#arg2: Int#}, {#arg3: Int#}, {#arg4: Int#}[')'][#TestStaticMemberCall#]; +// STATIC_METHOD_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(arg1): Int#}[')'][#TestStaticMemberCall#]; +// STATIC_METHOD_AFTERPAREN_2-DAG: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#(arg1): Int#}, {#arg2: Int#}, {#arg3: Int#}, {#arg4: Int#}[')'][#TestStaticMemberCall#]; // STATIC_METHOD_AFTERPAREN_2-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem/TypeRelation[Convertible]: Int[#Int#]; // STATIC_METHOD_AFTERPAREN_2-DAG: Literal[Integer]/None/TypeRelation[Convertible]: 0[#Int#]; @@ -600,9 +600,8 @@ func testImplicitMember() { // IMPLICIT_MEMBER_SKIPPED: Pattern/Local/Flair[ArgLabels]: {#arg4: Int#}[#Int#]; let _: TestStaticMemberCall = .createOverloaded(#^IMPLICIT_MEMBER_OVERLOADED^#) -// IMPLICIT_MEMBER_OVERLOADED: Begin completions, 2 items +// IMPLICIT_MEMBER_OVERLOADED: Begin completions, 1 item // IMPLICIT_MEMBER_OVERLOADED: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#arg1: Int#}[')'][#TestStaticMemberCall#]; name=arg1: -// IMPLICIT_MEMBER_OVERLOADED: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#arg1: String#}[')'][#String#]; name=arg1: } func testImplicitMemberInArrayLiteral() { struct Receiver { @@ -664,7 +663,7 @@ struct TestHasErrorAutoclosureParam { func test() { hasErrorAutoclosureParam(#^PARAM_WITH_ERROR_AUTOCLOSURE^# // PARAM_WITH_ERROR_AUTOCLOSURE: Begin completions, 1 items -// PARAM_WITH_ERROR_AUTOCLOSURE: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#value: <>#}[')'][#Void#]; +// PARAM_WITH_ERROR_AUTOCLOSURE: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#value: _#}[')'][#Void#]; } } @@ -687,7 +686,7 @@ extension MyType where T == Int { func testTypecheckedTypeExpr() { MyType(#^TYPECHECKED_TYPEEXPR^# } -// TYPECHECKED_TYPEEXPR: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg1: String#}, {#arg2: _#}[')'][#MyType<_>#]; name=arg1:arg2: +// TYPECHECKED_TYPEEXPR: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg1: String#}, {#arg2: T#}[')'][#MyType#]; name=arg1:arg2: // TYPECHECKED_TYPEEXPR: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#(intVal): Int#}[')'][#MyType#]; name=: func testPamrameterFlags(_: Int, inoutArg: inout Int, autoclosureArg: @autoclosure () -> Int, iuoArg: Int!, variadicArg: Int...) { @@ -1281,8 +1280,8 @@ extension Rdar89773376 { func testRdar89773376(arry: [Int]) { arry.map { Rdar89773376(#^RDAR89773376^#) } // RDAR89773376: Begin completions, 2 items -// RDAR89773376-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#string: String#}[')'][#Rdar89773376#]; -// RDAR89773376-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#intVal: Int#}[')'][#Rdar89773376#]; +// RDAR89773376-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#string: String#}[')'][#Rdar89773376#]; +// RDAR89773376-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#intVal: Int#}[')'][#Rdar89773376#]; } // This is an incomplete macro definition but it's sufficient to get the signature for code completion purposes @@ -1302,6 +1301,67 @@ func testMacroArg() { // MACRO_CALL_ARG-DAG: Literal[Integer]/None/TypeRelation[Convertible]: 0[#Int#]; name=0 // MACRO_CALL_ARG-DAG: Literal[Boolean]/None: true[#Bool#]; name=true // MACRO_CALL_ARG: End completions + +func testOverloadedWithDefaultedArgument() { + struct Image { + init(_ name: Int) {} + func grame(maxWidth: Double? = nil) {} + func grame() {} + } + + func test() { + Image(0) + .grame(maxWidth: .#^OVERLOADED_WITH_DEFAULT_ARG^#infinity) +// OVERLOADED_WITH_DEFAULT_ARG: Begin completions +// OVERLOADED_WITH_DEFAULT_ARG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/IsSystem/TypeRelation[Convertible]: infinity[#Double#]; +// OVERLOADED_WITH_DEFAULT_ARG: End completions + } +} + +func testSubscriptWithExistingRhs(someString: String) { + var userInfo: [String: Any] = [:] + userInfo[#^SUBSCRIPT_WITH_EXISTING_RHS^#] = message + +// SUBSCRIPT_WITH_EXISTING_RHS: Begin completions +// SUBSCRIPT_WITH_EXISTING_RHS-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath<[String : Any], Value>#}[']'][#Value#]; +// SUBSCRIPT_WITH_EXISTING_RHS-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]/IsSystem/TypeRelation[Convertible]: ['[']{#(key): String#}[']'][#@lvalue Any?#]; +// SUBSCRIPT_WITH_EXISTING_RHS-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]/IsSystem/TypeRelation[Convertible]: ['[']{#(key): String#}, {#default: Any#}[']'][#@lvalue Any#]; +// SUBSCRIPT_WITH_EXISTING_RHS-DAG: Decl[LocalVar]/Local/TypeRelation[Convertible]: someString[#String#]; +// SUBSCRIPT_WITH_EXISTING_RHS: End completions +} + +func testOptionalConversionFromSubscriptToCallArg() { + func takeOptionalInt(_ x: Int?) {} + + func test(savedFilters: [Int], index: Int) { + takeOptionalInt(savedFilters[#^OPTIONAL_CONVERSION_FROM_SUBSCRIPT_TO_CALL_ARG^#index]) +// OPTIONAL_CONVERSION_FROM_SUBSCRIPT_TO_CALL_ARG: Begin completions +// OPTIONAL_CONVERSION_FROM_SUBSCRIPT_TO_CALL_ARG-DAG: Pattern/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['[']{#keyPath: KeyPath<[Int], Value>#}[']'][#Value#]; +// OPTIONAL_CONVERSION_FROM_SUBSCRIPT_TO_CALL_ARG-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]/IsSystem/TypeRelation[Convertible]: ['[']{#(index): Int#}[']'][#Int#]; +// OPTIONAL_CONVERSION_FROM_SUBSCRIPT_TO_CALL_ARG-DAG: Decl[LocalVar]/Local/TypeRelation[Convertible]: index[#Int#]; +// OPTIONAL_CONVERSION_FROM_SUBSCRIPT_TO_CALL_ARG: End completions + } +} + +func testOptionalConversionInSrcOfAssignment(myArray: [Int]) { + var optInt: Int? + optInt = myArray[#^OPTIONAL_CONVERSION_IN_ASSIGNMENT^#] +// OPTIONAL_CONVERSION_IN_ASSIGNMENT: Begin completions +// OPTIONAL_CONVERSION_IN_ASSIGNMENT-DAG: Pattern/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['[']{#keyPath: KeyPath<[Int], Value>#}[']'][#Value#]; name=keyPath: +// OPTIONAL_CONVERSION_IN_ASSIGNMENT-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]/IsSystem/TypeRelation[Convertible]: ['[']{#(index): Int#}[']'][#Int#]; name=: +// OPTIONAL_CONVERSION_IN_ASSIGNMENT-DAG: Literal[Integer]/None/TypeRelation[Convertible]: 0[#Int#]; name=0 +// OPTIONAL_CONVERSION_IN_ASSIGNMENT: End completions +} + +func testAnyConversionInDestOfAssignment(_ message: String) { + var userInfo: [String: Any] = [:] + userInfo[#^ANY_CONVERSION_IN_ASSIGNMENT^#] = message +// ANY_CONVERSION_IN_ASSIGNMENT: Begin completions +// ANY_CONVERSION_IN_ASSIGNMENT-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath<[String : Any], Value>#}[']'][#Value#]; name=keyPath: +// ANY_CONVERSION_IN_ASSIGNMENT-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]/IsSystem/TypeRelation[Convertible]: ['[']{#(key): String#}, {#default: Any#}[']'][#@lvalue Any#]; name=:default: +// ANY_CONVERSION_IN_ASSIGNMENT-DAG: Decl[LocalVar]/Local: userInfo[#[String : Any]#]; name=userInfo +// ANY_CONVERSION_IN_ASSIGNMENT-DAG: Decl[LocalVar]/Local/TypeRelation[Convertible]: message[#String#]; name=message +// ANY_CONVERSION_IN_ASSIGNMENT: End completions } func testParameterPack(intArray: [Int]) { @@ -1310,3 +1370,23 @@ func testParameterPack(intArray: [Int]) { // PARAMETER_PACK_ARG: Pattern/Local/Flair[ArgLabels]: {#otherParam: Int#}[#Int#]; name=otherParam: // PARAMETER_PACK_ARG: Decl[LocalVar]/Local/TypeRelation[Convertible]: intArray[#[Int]#]; name=intArray } + +struct AmbiguousCallInResultBuilder { + @resultBuilder + struct MyResultBuilder { + static func buildBlock(_ value: Int) -> Int { + return value + } + } + + func ttroke(_ content: Int, style: String) -> Int { 41 } + func ttroke(_ content: Int, lineWidth: Int = 1) -> Int { 42 } + + @MyResultBuilder var body: Int { + self.ttroke(1, #^AMBIGUOUS_IN_RESULT_BUILDER^#) +// AMBIGUOUS_IN_RESULT_BUILDER: Begin completions, 2 items +// AMBIGUOUS_IN_RESULT_BUILDER-DAG: Pattern/Local/Flair[ArgLabels]: {#style: String#}[#String#]; +// AMBIGUOUS_IN_RESULT_BUILDER-DAG: Pattern/Local/Flair[ArgLabels]: {#lineWidth: Int#}[#Int#]; +// AMBIGUOUS_IN_RESULT_BUILDER: End completions + } +} diff --git a/test/IDE/complete_call_as_function.swift b/test/IDE/complete_call_as_function.swift index d0ceea806ce6d..de6b26a8da311 100644 --- a/test/IDE/complete_call_as_function.swift +++ b/test/IDE/complete_call_as_function.swift @@ -39,11 +39,13 @@ func testCallAsFunction(add: Adder, addTy: Adder.Type) { // INSTANCE_ARG2: Pattern/Local/Flair[ArgLabels]: {#y: Int#}[#Int#]; let _ = addTy#^METATYPE_NO_DOT^#; -// METATYPE_NO_DOT: Begin completions, 3 items +// METATYPE_NO_DOT: Begin completions, 5 items // METATYPE_NO_DOT-NOT: {#x: Int#}, {#y: Int#} // METATYPE_NO_DOT-DAG: Decl[InstanceMethod]/CurrNominal: .callAsFunction({#(self): Adder#})[#(x: Int, y: Int) -> Int#]; // METATYPE_NO_DOT-DAG: Decl[Constructor]/CurrNominal: .init({#base: Int#})[#Adder#]; // METATYPE_NO_DOT-DAG: Keyword[self]/CurrNominal: .self[#Adder.Type#]; +// METATYPE_NO_DOT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(any Any.Type)?#}[#Bool#]; +// METATYPE_NO_DOT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(any Any.Type)?#}[#Bool#]; let _ = addTy.#^METATYPE_DOT^#; // METATYPE_DOT: Begin completions, 3 items diff --git a/test/IDE/complete_call_pattern_heuristics.swift b/test/IDE/complete_call_pattern_heuristics.swift index d1beda0bbdd8f..e416231d1258d 100644 --- a/test/IDE/complete_call_pattern_heuristics.swift +++ b/test/IDE/complete_call_pattern_heuristics.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -code-complete-call-pattern-heuristics +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t -code-complete-call-pattern-heuristics -disable-objc-attr-requires-foundation-module struct FooStruct { init() {} @@ -33,8 +33,63 @@ func testArg2Name3() { // LABELED_FIRSTARG-NOT: ['(']{#arg1: Int#}, {#arg2: Int#}[')'][#Void#]; // LABELED_FIRSTARG-DAG: Pattern/Local/Flair[ArgLabels]: {#arg1: Int#}[#Int#]; // LABELED_FIRSTARG-NOT: ['(']{#arg1: Int#}, {#arg2: Int#}[')'][#Void#]; +} + +func optionalClosure(optClosure: ((Int) -> Void)?, someArg: Int) { + optClosure?(#^OPTIONAL_CLOSURE^#someArg) + // OPTIONAL_CLOSURE-DAG: Decl[LocalVar]/Local/TypeRelation[Convertible]: someArg[#Int#]; name=someArg +} + +func optionalProtocolMethod() { + @objc protocol Foo { + @objc optional func foo(arg: Int) + } + + func test(foo: Foo) { + foo.foo?(#^OPTIONAL_PROTOCOL_METHOD^#) + // OPTIONAL_PROTOCOL_METHOD-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#arg: Int#}[')'][#Void#]; + } +} func subscriptAccess(info: [String: Int]) { info[#^SUBSCRIPT_ACCESS^#] // SUBSCRIPT_ACCESS: Pattern/Local/Flair[ArgLabels]: {#keyPath: KeyPath<[String : Int], Value>#}[#KeyPath<[String : Int], Value>#]; name=keyPath: } + +struct StaticMethods { + static func before() { + self.after(num)#^AFTER_STATIC_FUNC^# + } + static func after(_ num: Int) -> (() -> Int) {} +// AFTER_STATIC_FUNC: Begin completions, 2 items +// AFTER_STATIC_FUNC-DAG: Keyword[self]/CurrNominal: .self[#(() -> Int)#]; +// AFTER_STATIC_FUNC-DAG: Pattern/CurrModule/Flair[ArgLabels]: ()[#Int#]; +// AFTER_STATIC_FUNC: End completions +} + +struct AmbiguousInResultBuilder { + @resultBuilder + struct MyViewBuilder { + static func buildBlock(_ elt: Text) -> Int { + 53 + } + } + + struct Text { + init(verbatim content: String) {} + init(_ content: S) where S : StringProtocol {} + } + + func foo(@MyViewBuilder content: () -> Int) {} + + func test(myStr: String) { + foo { + Text(#^AMBIGUOUS_IN_RESULT_BUILDER?xfail=TODO^#) +// AMBIGUOUS_IN_RESULT_BUILDER: Begin completions +// AMBIGUOUS_IN_RESULT_BUILDER-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#verbatim: String#}[')'][#Text#]; name=verbatim: +// AMBIGUOUS_IN_RESULT_BUILDER-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#(content): _#}[')'][#Text#]; name=: +// AMBIGUOUS_IN_RESULT_BUILDER-DAG: Decl[LocalVar]/Local/TypeRelation[Convertible]: myStr[#String#]; name=myStr +// AMBIGUOUS_IN_RESULT_BUILDER: End completions + } + } +} diff --git a/test/IDE/complete_constructor.swift b/test/IDE/complete_constructor.swift index 0975c4f4b1838..059e02a4e01f3 100644 --- a/test/IDE/complete_constructor.swift +++ b/test/IDE/complete_constructor.swift @@ -316,8 +316,8 @@ func testDependentTypeInClosure() { // DEPENDENT_IN_CLOSURE_3-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init({#arg: DataType#}, {#fn: () -> Data.Content##() -> Data.Content#})[#DependentTypeInClosure#]; let _ = DependentTypeInClosure(#^DEPENDENT_IN_CLOSURE_1^#) -// DEPENDENT_IN_CLOSURE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#(arg): _#}, {#fn: (_.Content) -> Void##(_.Content) -> Void#}[')'][#DependentTypeInClosure<_>#]; -// DEPENDENT_IN_CLOSURE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg: _#}, {#fn: () -> _.Content##() -> _.Content#}[')'][#DependentTypeInClosure<_>#]; +// DEPENDENT_IN_CLOSURE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#(arg): DataType#}, {#fn: (_) -> Void##(_) -> Void#}[')'][#DependentTypeInClosure#]; +// DEPENDENT_IN_CLOSURE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg: DataType#}, {#fn: () -> _##() -> _#}[')'][#DependentTypeInClosure#]; let _ = DependentTypeInClosure.#^DEPENDENT_IN_CLOSURE_2^# // DEPENDENT_IN_CLOSURE_2: Begin completions, 4 items @@ -335,8 +335,8 @@ extension InitWithUnresolved where Self.Data: Comparable { func testInitWithUnresolved() { let _ = InitWithUnresolved(#^INIT_WITH_UNRESOLVEDTYPE_1^# // INIT_WITH_UNRESOLVEDTYPE_1: Begin completions, 2 items -// INIT_WITH_UNRESOLVEDTYPE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg: _#}, {#fn: (_.Content) -> Void##(_.Content) -> Void#}[')'][#InitWithUnresolved<_>#]; -// INIT_WITH_UNRESOLVEDTYPE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg2: _#}[')'][#InitWithUnresolved<_>#]; +// INIT_WITH_UNRESOLVEDTYPE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg: DataType#}, {#fn: (_) -> Void##(_) -> Void#}[')'][#InitWithUnresolved#]; +// INIT_WITH_UNRESOLVEDTYPE_1-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#arg2: DataType#}[')'][#InitWithUnresolved#]; } func testIgnoreGenericArgsAfterCompletionToken() { diff --git a/test/IDE/complete_crashes.swift b/test/IDE/complete_crashes.swift index 025abcfff917c..509f50121dbe2 100644 --- a/test/IDE/complete_crashes.swift +++ b/test/IDE/complete_crashes.swift @@ -156,7 +156,7 @@ func rdar22834017() { Foo(#^RDAR_22834017^#) } // RDAR_22834017: Begin completions, 1 items -// RDAR_22834017: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#a: <>#}, {#b: <>#}, {#c: <>#}[')'][#Foo#]; +// RDAR_22834017: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#a: _#}, {#b: _#}, {#c: _#}[')'][#Foo#]; // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RDAR_23173692 | %FileCheck %s -check-prefix=RDAR_23173692 func rdar23173692() { @@ -213,8 +213,6 @@ func foo_38149042(bar: Bar_38149042) { } // RDAR_38149042-DAG: Decl[InstanceVar]/CurrNominal: .x[#Int#]; name=x // RDAR_38149042-DAG: Keyword[self]/CurrNominal: .self[#Baz_38149042#]; name=self -// RDAR_38149042-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']=== {#AnyObject?#}[#Bool#]; name==== -// RDAR_38149042-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']!== {#AnyObject?#}[#Bool#]; name=!== // rdar://problem/38272904 // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=RDAR_38272904 | %FileCheck %s -check-prefix=RDAR_38272904 @@ -239,7 +237,7 @@ func foo_38272904(a: A_38272904) { // rdar://problem/41159258 // RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=RDAR41159258_1 | %FileCheck %s -check-prefix=RDAR_41159258 -// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=RDAR41159258_2 | %FileCheck %s -check-prefix=RDAR_41159258 +// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=RDAR41159258_2 public func ==(lhs: RDAR41159258_MyResult1, rhs: RDAR41159258_MyResult1) -> Bool { fatalError() } @@ -278,15 +276,15 @@ public final class IntStore { _ = true ? 1 : provider.nextInt() #^RDAR41232519_2^# } } -// RDAR_41232519: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']+ {#Int#}[#Int#]; name=+ +// RDAR_41232519: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem/TypeRelation[Convertible]: [' ']+ {#Int#}[#Int#]; name=+ // rdar://problem/28188259 // RUN: %target-swift-ide-test -code-completion -code-completion-token=RDAR_28188259 -source-filename=%s | %FileCheck %s -check-prefix=RDAR_28188259 func test_28188259(x: ((Int) -> Void) -> Void) { x({_ in }#^RDAR_28188259^#) } -// RDAR_28188259-DAG: Pattern/CurrModule/Flair[ArgLabels]: ({#Int#})[#Void#]; name=() -// RDAR_28188259-DAG: Keyword[self]/CurrNominal: .self[#(Int) -> ()#]; name=self +// RDAR_28188259-DAG: Pattern/CurrModule/Flair[ArgLabels]/TypeRelation[Invalid]: ({#_#})[#Void#]; name=() +// RDAR_28188259-DAG: Keyword[self]/CurrNominal: .self[#(_) -> ()#]; name=self // rdar://problem/40956846 // RUN: %target-swift-ide-test -code-completion -code-completion-token=RDAR_40956846 -source-filename=%s | %FileCheck %s -check-prefix=RDAR_40956846 @@ -341,14 +339,13 @@ extension Foo { // RDAR_41234606-DAG: Decl[AssociatedType]/CurrNominal/IsSystem: .Iterator; name=Iterator // rdar://problem/41071587 -// RUN: %target-swift-ide-test -code-completion -code-completion-token=RDAR_41071587 -source-filename=%s | %FileCheck %s -check-prefix=RDAR_41071587 +// RUN: %target-swift-ide-test -code-completion -code-completion-token=RDAR_41071587 -source-filename=%s func test_41071587(x: Any) { switch x { case (let (_, _)) #^RDAR_41071587^#: () } } -// RDAR_41071587: Begin completions // rdar://problem/54215016 // RUN: %target-swift-ide-test -code-completion -code-completion-token=RDAR_54215016 -source-filename=%s | %FileCheck %s -check-prefix=RDAR_54215016 diff --git a/test/IDE/complete_diagnostics_concurrency.swift b/test/IDE/complete_diagnostics_concurrency.swift index cbac561004610..f8fb559b5776f 100644 --- a/test/IDE/complete_diagnostics_concurrency.swift +++ b/test/IDE/complete_diagnostics_concurrency.swift @@ -29,7 +29,7 @@ func testActor(obj: MyActor) async { func testClosure(obj: (Int) async -> Void) { obj(#^CLOSURE_CALL^#) -// CLOSURE_CALL-DAG: Pattern/CurrModule/Flair[ArgLabels]/NotRecommended: ['(']{#Int#}[')'][' async'][#Void#]; name=; diagnostics=error:async function used in a context that does not support concurrency +// CLOSURE_CALL-DAG: Pattern/Local/Flair[ArgLabels]/NotRecommended: ['(']{#Int#}[')'][' async'][#Void#]; name=; diagnostics=error:async function used in a context that does not support concurrency } func test() { diff --git a/test/IDE/complete_dont_filter_overloads_with_cc_token.swift b/test/IDE/complete_dont_filter_overloads_with_cc_token.swift new file mode 100644 index 0000000000000..f561a2e5250d6 --- /dev/null +++ b/test/IDE/complete_dont_filter_overloads_with_cc_token.swift @@ -0,0 +1,25 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t + +func buildView(@MyViewBuilder _ x: () -> T) {} + +@resultBuilder struct MyViewBuilder { + static func buildBlock(_ content: MyText) -> MyText { content } +} + +struct MyText : Equatable { + init(verbatim content: String) {} + init(_ content: S) where S : StringProtocol {} +} + +func test(text: String) { + buildView { + MyText(#^COMPLETE^#text) + } +} + +// COMPLETE: Begin completions +// COMPLETE-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#verbatim: String#}[')'][#MyText#]; +// COMPLETE-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#(content): StringProtocol#}[')'][#MyText#]; +// COMPLETE-DAG: Decl[LocalVar]/Local/TypeRelation[Convertible]: text[#String#]; +// COMPLETE: End completions diff --git a/test/IDE/complete_enum_elements.swift b/test/IDE/complete_enum_elements.swift index 71d955cb42030..216a1d07ff8f9 100644 --- a/test/IDE/complete_enum_elements.swift +++ b/test/IDE/complete_enum_elements.swift @@ -99,7 +99,7 @@ enum BarEnum { // BAR_ENUM_NO_DOT-DAG: Decl[EnumElement]/CurrNominal: .Bar12({#Int#}, {#(Float, Double)#})[#BarEnum#]{{; name=.+$}} // BAR_ENUM_NO_DOT-DAG: Decl[InstanceMethod]/CurrNominal: .barInstanceFunc({#(self): &BarEnum#})[#() -> Void#]{{; name=.+$}} // BAR_ENUM_NO_DOT-DAG: Decl[StaticVar]/CurrNominal: .staticVar[#Int#]{{; name=.+$}} -// BAR_ENUM_NO_DOT-DAG: Decl[StaticMethod]/CurrNominal: .barStaticFunc()[#Void#]{{; name=.+$}} +// BAR_ENUM_NO_DOT-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Invalid]: .barStaticFunc()[#Void#]{{; name=.+$}} // BAR_ENUM_NO_DOT-DAG: Keyword[self]/CurrNominal: .self[#BarEnum.Type#]; name=self // BAR_ENUM_NO_DOT-DAG: Keyword/CurrNominal: .Type[#BarEnum.Type#]; name=Type @@ -141,7 +141,7 @@ enum BazEnum { // BAZ_INT_ENUM_NO_DOT-DAG: Decl[InstanceMethod]/CurrNominal: .bazInstanceFunc({#(self): &BazEnum#})[#() -> Void#]{{; name=.+$}} // BAZ_INT_ENUM_NO_DOT-DAG: Decl[StaticVar]/CurrNominal: .staticVar[#Int#]{{; name=.+$}} // BAZ_INT_ENUM_NO_DOT-DAG: Decl[StaticVar]/CurrNominal: .staticVarT[#Int#]{{; name=.+$}} -// BAZ_INT_ENUM_NO_DOT-DAG: Decl[StaticMethod]/CurrNominal: .bazStaticFunc()[#Void#]{{; name=.+$}} +// BAZ_INT_ENUM_NO_DOT-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Invalid]: .bazStaticFunc()[#Void#]{{; name=.+$}} // BAZ_INT_ENUM_NO_DOT-DAG: Keyword[self]/CurrNominal: .self[#BazEnum.Type#]; name=self // BAZ_INT_ENUM_NO_DOT-DAG: Keyword/CurrNominal: .Type[#BazEnum.Type#]; name=Type @@ -150,7 +150,7 @@ enum BazEnum { // BAZ_T_ENUM_NO_DOT-DAG: Decl[InstanceMethod]/CurrNominal: .bazInstanceFunc({#(self): &BazEnum<_>#})[#() -> Void#]{{; name=.+$}} // BAZ_T_ENUM_NO_DOT-DAG: Decl[StaticVar]/CurrNominal: .staticVar[#Int#]{{; name=.+$}} // BAZ_T_ENUM_NO_DOT-DAG: Decl[StaticVar]/CurrNominal: .staticVarT[#_#]{{; name=.+$}} -// BAZ_T_ENUM_NO_DOT-DAG: Decl[StaticMethod]/CurrNominal: .bazStaticFunc()[#Void#]{{; name=.+$}} +// BAZ_T_ENUM_NO_DOT-DAG: Decl[StaticMethod]/CurrNominal/TypeRelation[Invalid]: .bazStaticFunc()[#Void#]{{; name=.+$}} // BAZ_T_ENUM_NO_DOT-DAG: Keyword[self]/CurrNominal: .self[#BazEnum<_>.Type#]; name=self // BAZ_T_ENUM_NO_DOT-DAG: Keyword/CurrNominal: .Type[#BazEnum<_>.Type#]; name=Type @@ -343,13 +343,13 @@ func testUnqualified1(x: QuxEnum) { // UNRESOLVED_2-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: Qux1[#QuxEnum#]; name=Qux1 // UNRESOLVED_2-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: Qux2[#QuxEnum#]; name=Qux2 // UNRESOLVED_2-DAG: Decl[TypeAlias]/CurrNominal: RawValue[#Int#]; name=RawValue - // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal: init({#rawValue: Int#})[#QuxEnum?#]; name=init(rawValue:) + // UNRESOLVED_2-DAG: Decl[Constructor]/CurrNominal/TypeRelation[Convertible]: init({#rawValue: Int#})[#QuxEnum?#]; name=init(rawValue:) // UNRESOLVED_2-DAG: Decl[InstanceMethod]/Super/IsSystem/TypeRelation[Invalid]: hash({#(self): QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(:) _ = (x == .Qux1#^UNRESOLVED_3^#) // UNRESOLVED_3-DAG: Decl[InstanceVar]/CurrNominal: .rawValue[#Int#]; name=rawValue // UNRESOLVED_3-DAG: Decl[InstanceVar]/Super/IsSystem: .hashValue[#Int#]; name=hashValue -// UNRESOLVED_3-DAG: Decl[InstanceMethod]/Super/IsSystem: .hash({#into: &Hasher#})[#Void#]; name=hash(into:) +// UNRESOLVED_3-DAG: Decl[InstanceMethod]/Super/IsSystem/TypeRelation[Invalid]: .hash({#into: &Hasher#})[#Void#]; name=hash(into:) // UNRESOLVED_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: ~= {#QuxEnum#}[#Bool#]; name=~= // UNRESOLVED_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#QuxEnum#}[#Bool#]; name=!= // UNRESOLVED_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#QuxEnum#}[#Bool#]; name=== @@ -395,7 +395,7 @@ func ~=(pattern: OtherEnum, value: EnumWithCustomPatternMatchingOperator) -> Boo func completeEnumWithCustomPatternMatchingOperator(x: EnumWithCustomPatternMatchingOperator) { switch x { case .#^PATTERN_MATCH_ENUM_WITH_CUSTOM_PATTERN_MATCHING^# -// We should be suggesting static members of `OtherEnum`, because we can match it to `EnumWithCustomPatternMatchingOperator` using the custom pattern match operator. +// We should be suggesting static members of `OtherEnum`, because we can match it to `EnumWithCustomPatternMatchingOperator` using the custom pattern match operator. // We should also suggest enum cases from `EnumWithCustomPatternMatchingOperator` whose pattern matching doesn't go through any `~=` operator. // We shouldn't suggest `staticMember` because `EnumWithCustomPatternMatchingOperator` doesn`t have `~=` defined between two of its instances. // PATTERN_MATCH_ENUM_WITH_CUSTOM_PATTERN_MATCHING: Begin completions, 4 items diff --git a/test/IDE/complete_enum_unresolved_dot_argument_labels.swift b/test/IDE/complete_enum_unresolved_dot_argument_labels.swift index 90fbd2900a26d..b3d720b5438a4 100644 --- a/test/IDE/complete_enum_unresolved_dot_argument_labels.swift +++ b/test/IDE/complete_enum_unresolved_dot_argument_labels.swift @@ -11,7 +11,7 @@ func testInit() { var state = DragState.inactive state = .dragging(#^SIGNATURE^#) // SIGNATURE: Begin completions, 1 item - // SIGNATURE: Pattern/CurrModule/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#translationX: Int#}, {#translationY: Int#}[')'][#DragState#]; + // SIGNATURE: Pattern/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#translationX: Int#}, {#translationY: Int#}[')'][#DragState#]; state = .dragging(translationX: 2, #^ARGUMENT^#) // ARGUMENT: Begin completions, 1 item @@ -19,7 +19,7 @@ func testInit() { state = .defaulted(#^DEFAULTED^#) // DEFAULTED: Begin completions, 1 items - // DEFAULTED: Pattern/CurrModule/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#x: Int#}, {#y: Int#}, {#z: Int#}, {#extra: Int#}[')'][#DragState#]; + // DEFAULTED: Pattern/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#x: Int#}, {#y: Int#}, {#z: Int#}, {#extra: Int#}[')'][#DragState#]; state = .defaulted(x: 1, #^DEFAULTEDARG^#) state = .defaulted(x: "Wrong type", #^ERRORTYPE?check=DEFAULTEDARG^#) diff --git a/test/IDE/complete_expr_postfix_begin.swift b/test/IDE/complete_expr_postfix_begin.swift index bc04d1bab6cf2..e2939b7eb43f5 100644 --- a/test/IDE/complete_expr_postfix_begin.swift +++ b/test/IDE/complete_expr_postfix_begin.swift @@ -36,14 +36,14 @@ typealias FooTypealias = Int // COMMON-DAG: Decl[TypeAlias]/CurrModule{{(/TypeRelation\[Convertible\])?}}: FooTypealias[#Int#]{{; name=.+$}} // COMMON-DAG: Decl[GlobalVar]/CurrModule: fooObject[#FooStruct#]{{; name=.+$}} // COMMON-DAG: Keyword[try]/None: try{{; name=.+$}} -// COMMON-DAG: Literal[Boolean]/None: true[#Bool#]{{; name=.+$}} -// COMMON-DAG: Literal[Boolean]/None: false[#Bool#]{{; name=.+$}} +// COMMON-DAG: Literal[Boolean]/None{{(/TypeRelation\[Convertible\])?}}: true[#Bool#]{{; name=.+$}} +// COMMON-DAG: Literal[Boolean]/None{{(/TypeRelation\[Convertible\])?}}: false[#Bool#]{{; name=.+$}} // COMMON-DAG: Literal[Nil]/None: nil{{; name=.+$}} -// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem: Int8[#Int8#]{{; name=.+$}} -// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem: Int16[#Int16#]{{; name=.+$}} -// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem: Int32[#Int32#]{{; name=.+$}} -// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem: Int64[#Int64#]{{; name=.+$}} -// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem: Bool[#Bool#]{{; name=.+$}} +// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem{{(/TypeRelation\[Convertible\])?}}: Int8[#Int8#]{{; name=.+$}} +// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem{{(/TypeRelation\[Convertible\])?}}: Int16[#Int16#]{{; name=.+$}} +// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem{{(/TypeRelation\[Convertible\])?}}: Int32[#Int32#]{{; name=.+$}} +// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem{{(/TypeRelation\[Convertible\])?}}: Int64[#Int64#]{{; name=.+$}} +// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem{{(/TypeRelation\[Convertible\])?}}: Bool[#Bool#]{{; name=.+$}} // NO_SELF-NOT: {{[[:<:]][Ss]elf[[:>:]]}} @@ -63,7 +63,7 @@ func testExprPostfixBegin3(fooParam: FooStruct) { } func testExprPostfixBegin4(fooParam: FooStruct) { - "\(#^EXPR_POSTFIX_BEGIN_4?xfail=FIXME-64812321;check=COMMON^#)" + "\(#^EXPR_POSTFIX_BEGIN_4?check=COMMON^#)" } func testExprPostfixBegin5(fooParam: FooStruct) { diff --git a/test/IDE/complete_expr_tuple.swift b/test/IDE/complete_expr_tuple.swift index c509be861e09a..5e179b76cc839 100644 --- a/test/IDE/complete_expr_tuple.swift +++ b/test/IDE/complete_expr_tuple.swift @@ -36,8 +36,8 @@ func testTupleNoDot1() { // TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_1-DAG: BuiltinOperator/None: = {#(Int, Double)#}[#Void#]{{; name=.+$}} -// TUPLE_NO_DOT_1-NEXT: Keyword[self]/CurrNominal: .self[#(Int, Double)#]; name=self +// TUPLE_NO_DOT_1-DAG: BuiltinOperator/None: = {#(Int, Double)#}{{; name=.+$}} +// TUPLE_NO_DOT_1-DAG: Keyword[self]/CurrNominal: .self[#(Int, Double)#]; name=self func testTupleNoDot2() { var t = (foo: 1, bar: 2.0) @@ -52,7 +52,7 @@ func testTupleNoDot2() { // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_2-DAG: BuiltinOperator/None: = {#(foo: Int, bar: Double)#}[#Void#]{{; name=.+$}} +// TUPLE_NO_DOT_2-DAG: BuiltinOperator/None: = {#(foo: Int, bar: Double)#}{{; name=.+$}} // TUPLE_NO_DOT_2-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, bar: Double)#]; name=self func testTupleNoDot3() { @@ -68,7 +68,7 @@ func testTupleNoDot3() { // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(Int, Double)#}[#Bool#]{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#(Int, Double)#}[#Bool#]{{; name=.+$}} -// TUPLE_NO_DOT_3-DAG: BuiltinOperator/None: = {#(foo: Int, Double)#}[#Void#]{{; name=.+$}} +// TUPLE_NO_DOT_3-DAG: BuiltinOperator/None: = {#(foo: Int, Double)#}{{; name=.+$}} // TUPLE_NO_DOT_3-DAG: Keyword[self]/CurrNominal: .self[#(foo: Int, Double)#]; name=self func testTupleDot1() { diff --git a/test/IDE/complete_from_stdlib.swift b/test/IDE/complete_from_stdlib.swift index fe25b63d0c190..de35aa28046d3 100644 --- a/test/IDE/complete_from_stdlib.swift +++ b/test/IDE/complete_from_stdlib.swift @@ -185,10 +185,16 @@ func testInfixOperator3(_ x: String) { func testInfixOperator4(_ x: String) { x == ""#^INFIX_EXT_STRING_1?check=INFIX_EXT_STRING^# } -// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: + {#String#}[#String#] -// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: || {#Bool#}[#Bool#] -// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: && {#Bool#}[#Bool#] -// INFIX_EXT_STRING-NOT: == +// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: >= {#String#}[#Bool#]; name=>= +// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: ... {#String#}[#ClosedRange#]; name=... +// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: ..< {#String#}[#Range#]; name=..< +// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: <= {#String#}[#Bool#]; name=<= +// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: ~= {#Substring#}[#Bool#]; name=~= +// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#String#}[#Bool#]; name=!= +// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem/TypeRelation[Convertible]: + {#String#}[#String#]; name=+ +// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#String#}[#Bool#]; name=== +// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: < {#String#}[#Bool#]; name=< +// INFIX_EXT_STRING-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: > {#String#}[#Bool#]; name=> class TestSequence : Sequence { #^CONFORM_SEQUENCE^# diff --git a/test/IDE/complete_in_closures.swift b/test/IDE/complete_in_closures.swift index 203a9214cb3dc..27328d53b5130 100644 --- a/test/IDE/complete_in_closures.swift +++ b/test/IDE/complete_in_closures.swift @@ -336,7 +336,7 @@ var foo = { func testWithMemoryRebound(_ bar: UnsafePointer) { _ = bar.withMemoryRebound(to: Int64.self, capacity: 3) { ptr in return ptr #^SINGLE_EXPR_CLOSURE_CONTEXT^# - // SINGLE_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: .deallocate()[#Void#]; name=deallocate() + // SINGLE_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Invalid]: .deallocate()[#Void#]; name=deallocate() // SINGLE_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .pointee[#Int64#]; name=pointee } } @@ -344,14 +344,14 @@ func testWithMemoryRebound(_ bar: UnsafePointer) { func testInsideTernaryClosureReturn(test: Bool) -> [String] { return "hello".map { thing in test ? String(thing #^SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT^#).uppercased() : String(thing).lowercased() - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .utf8[#Character.UTF8View#]; name=utf8 - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .description[#String#]; name=description - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .isWhitespace[#Bool#]; name=isWhitespace - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem: .uppercased()[#String#]; name=uppercased() - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']... {#String.Element#}[#ClosedRange#]; name=... - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']< {#Character#}[#Bool#]; name=< - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']>= {#String.Element#}[#Bool#]; name=>= - // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']== {#Character#}[#Bool#]; name=== + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem/TypeRelation[Convertible]: .utf8[#Character.UTF8View#]; name=utf8 + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem/TypeRelation[Convertible]: .description[#String#]; name=description + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceVar]/CurrNominal/IsSystem/TypeRelation[Convertible]: .isWhitespace[#Bool#]; name=isWhitespace + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InstanceMethod]/CurrNominal/IsSystem/TypeRelation[Convertible]: .uppercased()[#String#]; name=uppercased() + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem/TypeRelation[Convertible]: [' ']... {#String.Element#}[#ClosedRange#]; name=... + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem/TypeRelation[Convertible]: [' ']< {#Character#}[#Bool#]; name=< + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem/TypeRelation[Convertible]: [' ']>= {#String.Element#}[#Bool#]; name=>= + // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem/TypeRelation[Convertible]: [' ']== {#Character#}[#Bool#]; name=== // SINGLE_TERNARY_EXPR_CLOSURE_CONTEXT-DAG: Keyword[self]/CurrNominal: .self[#String.Element#]; name=self } } @@ -470,7 +470,7 @@ func testCompleteInMatchOfAssociatedValueInSwitchCase() { } }) -// IN_ASSOC_OF_CASE_IN_CLOSURE-DAG: Decl[LocalVar]/Local: str[#String#]; name=str +// IN_ASSOC_OF_CASE_IN_CLOSURE-DAG: Decl[LocalVar]/Local/TypeRelation[Convertible]: str[#String#]; name=str } } @@ -485,3 +485,78 @@ func testReferenceToVariableDefinedInClosure() { // VARIABLE_DEFINED_IN_CLOSURE: Decl[LocalVar]/Local: item[#String#]; name=item } +func testBinaryOperatorWithEnum() { + func closureWithEnum(completionHandler: (SomeEnum) -> Void) {} + + closureWithEnum { foo in + if foo != .#^BINARY_OPERATOR_WITH_ENUM^# { + } + } +// BINARY_OPERATOR_WITH_ENUM: Begin completions +// BINARY_OPERATOR_WITH_ENUM-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: north[#SomeEnum#] +// BINARY_OPERATOR_WITH_ENUM-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: south[#SomeEnum#] +// BINARY_OPERATOR_WITH_ENUM: End completions + +} + +func testPreviousSyntacticElementHasError() { + struct MyStruct {} + + class MyClass { + var myMember: Int = 1 + var myString: String = "1" + } + + @resultBuilder struct ViewBuilder { + static func buildBlock(_ content: Content) -> Content { content } + } + + func buildView(@ViewBuilder content: () -> Content) -> Int { 0 } + + func takeClosure(_ action: () -> Void) {} + + func test(x: MyClass) { + // Not that the previous syntactic element (let a) has an error because we + // skip MyStruct inside the result builder for performance reasons. + takeClosure { + let a = buildView { + MyStruct() + } + x.#^PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR^#myMember = 1234 + } + } +// PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR: Begin completions +// PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR-DAG: Keyword[self]/CurrNominal: self[#MyClass#]; name=self +// PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: myMember[#Int#]; name=myMember +// PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR-DAG: Decl[InstanceVar]/CurrNominal: myString[#String#]; name=myString +// PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR: End completions +} + +func testCompleteAfterClosureWithIfExprThatContainErrors() { + _ = { + if true { + invalid(1) + } else if true { + invalid(2) + } else { + invalid(3) + } + }#^AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS^# + + // AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS: Begin completions, 2 items + // AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS-DAG: Keyword[self]/CurrNominal: .self[#() -> _#]; name=self + // AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS-DAG: Pattern/CurrModule/Flair[ArgLabels]: ()[#_#]; name=() + // AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS: End completions +} + +func testVariableInClosureHasArchetype(_ key: K) { + func takeClosure(_ x: () -> Void) {} + + takeClosure { + var varWithArchetype = key + #^VAR_WITH_ARCHETYPE^# + // VAR_WITH_ARCHETYPE: Begin completions + // VAR_WITH_ARCHETYPE: Decl[LocalVar]/Local: varWithArchetype[#K#]; + // VAR_WITH_ARCHETYPE: End completions + } +} diff --git a/test/IDE/complete_in_result_builder.swift b/test/IDE/complete_in_result_builder.swift index 41db06c8ec9a2..12924357e055e 100644 --- a/test/IDE/complete_in_result_builder.swift +++ b/test/IDE/complete_in_result_builder.swift @@ -40,20 +40,20 @@ func testGlobalLookup() { @TupleBuilder var x2 { if true { - #^GLOBAL_LOOKUP_IN_IF_BODY?check=GLOBAL_LOOKUP_NO_TYPE_RELATION^# -// GLOBAL_LOOKUP_NO_TYPE_RELATION: Decl[GlobalVar]/CurrModule: MyConstantString[#String#]; + #^GLOBAL_LOOKUP_IN_IF_BODY^# +// GLOBAL_LOOKUP_IN_IF_BODY: Decl[GlobalVar]/CurrModule: MyConstantString[#String#]; } } @TupleBuilder var x3 { if { - #^GLOBAL_LOOKUP_IN_IF_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP_NO_TYPE_RELATION^# + #^GLOBAL_LOOKUP_IN_IF_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP_IN_IF_BODY^# } } @TupleBuilder var x4 { guard else { - #^GLOBAL_LOOKUP_IN_GUARD_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP_NO_TYPE_RELATION^# + #^GLOBAL_LOOKUP_IN_GUARD_BODY_WITHOUT_CONDITION?check=GLOBAL_LOOKUP_IN_IF_BODY^# } } @@ -85,7 +85,10 @@ func testStaticMemberLookup() { } @TupleBuilder var x3 { - "hello: \(StringFactory.#^COMPLETE_STATIC_MEMBER_IN_STRING_LITERAL?check=COMPLETE_STATIC_MEMBER;xfail=rdar78015646^#)" + "hello: \(StringFactory.#^COMPLETE_STATIC_MEMBER_IN_STRING_LITERAL^#)" +// COMPLETE_STATIC_MEMBER_IN_STRING_LITERAL: Begin completions +// COMPLETE_STATIC_MEMBER_IN_STRING_LITERAL: Decl[StaticMethod]/CurrNominal/TypeRelation[Convertible]: makeString({#x: String#})[#String#]; +// COMPLETE_STATIC_MEMBER_IN_STRING_LITERAL: End completions } } @@ -132,7 +135,7 @@ func testCompleteFunctionArgumentLabels() { @TupleBuilder var x1 { StringFactory.makeString(#^FUNCTION_ARGUMENT_LABEL^#) // FUNCTION_ARGUMENT_LABEL: Begin completions, 1 item - // FUNCTION_ARGUMENT_LABEL: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]: ['(']{#x: String#}[')'][#String#]; + // FUNCTION_ARGUMENT_LABEL: Decl[StaticMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#x: String#}[')'][#String#]; } } @@ -327,13 +330,54 @@ func testSwitchInResultBuilder() { Reduce2() Reduce2 { action in switch action { - case .#^SWITCH_IN_RESULT_BUILDER^# alertDismissed: + case .#^SWITCH_IN_RESULT_BUILDER?xfail=rdar106720462^# alertDismissed: return 0 } } } } -// SWITCH_IN_RESULT_BUILDER: Begin completions, 2 items +// SWITCH_IN_RESULT_BUILDER: Begin completions, 1 item // SWITCH_IN_RESULT_BUILDER-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: alertDismissed[#Action#]; -// SWITCH_IN_RESULT_BUILDER-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Action#})[#(into: inout Hasher) -> Void#]; +} + +func testCompleteIfLetInResultBuilder() { + func takeClosure(_ x: () -> Void) -> Int { + return 0 + } + + @resultBuilder struct MyResultBuilder { + static func buildBlock() -> Int { return 0 } + static func buildBlock(_ content: Int) -> Int { content } + } + + @MyResultBuilder func test(integer: Int?) -> Int { + takeClosure { + if let #^IF_LET_IN_RESULT_BUILDER^#integer = integer { + } + } + // IF_LET_IN_RESULT_BUILDER: Begin completions, 1 items + // IF_LET_IN_RESULT_BUILDER: Decl[LocalVar]/Local: integer[#Int?#]; name=integer + // IF_LET_IN_RESULT_BUILDER: End completions + } +} + +func testOverloadedCallArgs() { + func overloaded(single: Int) -> Int {} + func overloaded(_ first: Int, second: Int) -> Int {} + + @resultBuilder struct ViewBuilder { + static func buildBlock(_ content: Int) -> Int { content } + } + + struct Test { + @ViewBuilder var body: Int { + overloaded(#^OVERLOADED_CALL_ARG^#, second: 1) + // OVERLOADED_CALL_ARG: Begin completions + // OVERLOADED_CALL_ARG-DAG: Decl[FreeFunction]/Local/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#single: Int#}[')'][#Int#]; + // OVERLOADED_CALL_ARG-DAG: Decl[FreeFunction]/Local/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#(first): Int#}, {#second: Int#}[')'][#Int#]; + // OVERLOADED_CALL_ARG-DAG: Literal[Integer]/None/TypeRelation[Convertible]: 0[#Int#]; + // OVERLOADED_CALL_ARG: End completions + } + } + } diff --git a/test/IDE/complete_init_inherited.swift b/test/IDE/complete_init_inherited.swift index 8c71466fc51f1..45f0d7b6901ef 100644 --- a/test/IDE/complete_init_inherited.swift +++ b/test/IDE/complete_init_inherited.swift @@ -70,7 +70,7 @@ class D : C { // TEST_D_PAREN: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#d: D#}[')'][#D#]; name=d: // TEST_D_PAREN-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#int: Int#}[')'][#D#]; name=int: -// TEST_D_PAREN-DAG: Decl[Constructor]/Super/Flair[ArgLabels]: ['(']{#c: C#}[')'][#C#]; name=c: +// TEST_D_PAREN-DAG: Decl[Constructor]/Super/Flair[ArgLabels]: ['(']{#c: C#}[')'][#D#]; name=c: func testA() { A#^TEST_A^# @@ -101,5 +101,5 @@ func testR74233797() { R74233797Derived(#^METATYPE_CONVERSION^#) // METATYPE_CONVERSION-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#sub: Bool#}[')'][#R74233797Derived#]; // METATYPE_CONVERSION-DAG: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['('][')'][#R74233797Derived#]; -// METATYPE_CONVERSION-DAG: Decl[Constructor]/Super/Flair[ArgLabels]: ['(']{#(test): Bool#}[')'][#R74233797Base#]; +// METATYPE_CONVERSION-DAG: Decl[Constructor]/Super/Flair[ArgLabels]: ['(']{#(test): Bool#}[')'][#R74233797Derived#]; } diff --git a/test/IDE/complete_literal.swift b/test/IDE/complete_literal.swift index 13554eee3b539..3f186b0b92adb 100644 --- a/test/IDE/complete_literal.swift +++ b/test/IDE/complete_literal.swift @@ -71,7 +71,7 @@ func testArray(f1: Float) { _ = [1, 2, f1] #^LITERAL8^# } // LITERAL8-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .count[#Int#]; name=count -// LITERAL8-DAG: Decl[InstanceVar]/Super/IsSystem: .first[#Float?#]; name=first +// LITERAL8-DAG: Decl[InstanceVar]/Super/IsSystem: .first[#Any?#]; name=first func testDict(f1: Float) { _ = ["foo": f1, "bar": "baz"] #^LITERAL9^# diff --git a/test/IDE/complete_multiple_trailingclosure_signatures.swift b/test/IDE/complete_multiple_trailingclosure_signatures.swift index 788b2820df27a..cd17fe6196141 100644 --- a/test/IDE/complete_multiple_trailingclosure_signatures.swift +++ b/test/IDE/complete_multiple_trailingclosure_signatures.swift @@ -24,3 +24,17 @@ func test() { // GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn7: (inout Int) -> Void {<#inout Int#> in|}#}[#(inout Int) -> Void#]; // GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn8: (Int...) -> Void {<#Int...#> in|}#}[#(Int...) -> Void#]; } + +func testStringAndMulipleTrailingClosures() { + func stringAndClosure(_ key: String, _ body: () -> Void) {} + + func takeClosure(_ x: () -> Void) {} + + takeClosure { + stringAndClosure("\(1)") { }#^STRING_AND_MULTIPLE_TRAILING_CLOSURES^# + } + // STRING_AND_MULTIPLE_TRAILING_CLOSURES: Begin completions + // STRING_AND_MULTIPLE_TRAILING_CLOSURES-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#()#}[#Bool#]; + // STRING_AND_MULTIPLE_TRAILING_CLOSURES-DAG: Keyword[self]/CurrNominal: .self[#Void#]; name=self + // STRING_AND_MULTIPLE_TRAILING_CLOSURES: End completions +} diff --git a/test/IDE/complete_opaque_result.swift b/test/IDE/complete_opaque_result.swift index 6d5f22388c282..560e318ed99ea 100644 --- a/test/IDE/complete_opaque_result.swift +++ b/test/IDE/complete_opaque_result.swift @@ -167,7 +167,7 @@ func postfixExpr() { // POSTFIX_TestProtocol_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .foo({#arg1: TestProtocol.Assoc1#}, {#arg2: (Comparable) -> TestProtocol.Assoc1##(Comparable) -> TestProtocol.Assoc1#})[#Comparable#]; name={{.*$}} // POSTFIX_TestProtocol_NODOT-DAG: Decl[Subscript]/CurrNominal: [{#(idx): TestProtocol.Assoc1#}, {#(idx2): Comparable#}][#TestProtocol#]; name={{.*$}} // POSTFIX_TestProtocol_NODOT-DAG: Decl[InstanceVar]/CurrNominal: .value[#(TestProtocol.Assoc1, Comparable)#]; name={{.*$}} -// POSTFIX_TestProtocol_NODOT-DAG: BuiltinOperator/None: = {#TestProtocol#}[#Void#]; name={{.*$}} +// POSTFIX_TestProtocol_NODOT-DAG: BuiltinOperator/None: = {#TestProtocol#}; name={{.*$}} // POSTFIX_TestProtocol_NODOT-DAG: Keyword[self]/CurrNominal: .self[#TestProtocol#]; name={{.*$}} protocol TestProtocol2 { diff --git a/test/IDE/complete_operators.swift b/test/IDE/complete_operators.swift index e242850895a02..4fa856575bba5 100644 --- a/test/IDE/complete_operators.swift +++ b/test/IDE/complete_operators.swift @@ -45,7 +45,7 @@ postfix func ***(x: G) -> G { return x } func testPostfix6() { 1 + 2 * 3#^POSTFIX_6^# } -// POSTFIX_6: Decl[PostfixOperatorFunction]/CurrModule: ***[#Int#] +// POSTFIX_6: Decl[PostfixOperatorFunction]/CurrModule/TypeRelation[Convertible]: ***[#Int#] func testPostfix7() { 1 + 2 * 3.0#^POSTFIX_7^# @@ -119,7 +119,7 @@ func testInfix2(x: inout S2) { // S2_INFIX_LVALUE-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: + {#S2#}[#S2#] // S2_INFIX_LVALUE-DAG: Decl[InfixOperatorFunction]/CurrModule: ** {#Int#}[#S2#] // S2_INFIX_LVALUE-DAG: Decl[InfixOperatorFunction]/CurrModule: **= {#Int#}[#Void#] -// S2_INFIX_LVALUE-DAG: BuiltinOperator/None: = {#S2#}[#Void#] +// S2_INFIX_LVALUE-DAG: BuiltinOperator/None: = {#S2#} // NEGATIVE_S2_INFIX_LVALUE-NOT: += // NEGATIVE_S2_INFIX_LVALUE-NOT: \* {#Int#} // NEGATIVE_S2_INFIX_LVALUE-NOT: ?? @@ -205,11 +205,13 @@ func testInfix14() { func testInfix15() { T#^INFIX_15^# } -// INFIX_15: Begin completions, 4 items -// INFIX_15-NEXT: Decl[AssociatedType]/CurrNominal: .T; name=T -// INFIX_15-NEXT: Decl[InstanceMethod]/CurrNominal: .foo({#(self): P#})[#() -> S2#]; name=foo(:) -// INFIX_15-NEXT: Keyword[self]/CurrNominal: .self[#T.Type#]; name=self -// INFIX_15-NEXT: Keyword/CurrNominal: .Type[#T.Type#]; name=Type +// INFIX_15: Begin completions, 6 items +// INFIX_15-DAG: Decl[AssociatedType]/CurrNominal: .T; name=T +// INFIX_15-DAG: Decl[InstanceMethod]/CurrNominal: .foo({#(self): P#})[#() -> S2#]; name=foo(:) +// INFIX_15-DAG: Keyword[self]/CurrNominal: .self[#T.Type#]; name=self +// INFIX_15-DAG: Keyword/CurrNominal: .Type[#T.Type#]; name=Type +// INFIX_15-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: != {#(any Any.Type)?#}[#Bool#]; +// INFIX_15-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#(any Any.Type)?#}[#Bool#]; func testInfix16() { T.foo#^INFIX_16^# @@ -264,8 +266,12 @@ func testSpace(x: S2) { // S2_INFIX_SPACE-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: [' ']+ {#S2#}[#S2#] func testExtInfix1(x: inout S2) { - x + S2() + x + S2() + x + S2() + x#^EXT_INFIX_1?check=S2_INFIX^# + x + S2() + x + S2() + x + S2() + x#^EXT_INFIX_1^# } +// EXT_INFIX_1: Begin completions +// EXT_INFIX_1-DAG: Decl[InfixOperatorFunction]/CurrModule/TypeRelation[Convertible]: ** {#Int#}[#S2#] +// EXT_INFIX_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem/TypeRelation[Convertible]: + {#S2#}[#S2#] +// EXT_INFIX_1: End completions struct S4 {} func +(x: S4, y: S4) -> S4 { return x } @@ -285,26 +291,27 @@ precedencegroup ReallyHighPrecedence { func &&&(x: Bool, y: Bool) -> S4 { return x } func testExtInfix2(x: S4) { - x + x == x + x#^EXT_INFIX_2?check=S4_EXT_INFIX;check=S4_EXT_INFIX_NEG^# + x + x == x + x#^EXT_INFIX_2^# } -// S4_EXT_INFIX-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: + {#S4#}[#S4#] -// S4_EXT_INFIX-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: && {#Bool#}[#Bool#] -// S4_EXT_INFIX-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: || {#Bool#}[#Bool#] - -// S4_EXT_INFIX-NEG-NOT: != -// S4_EXT_INFIX-NEG-NOT: == -// S4_EXT_INFIX_NEG-NOT: +++ -// S4_EXT_INFIX_NEG-NOT: &&& +// EXT_INFIX_2: Begin completions, 4 items +// EXT_INFIX_2-DAG: Keyword[self]/CurrNominal: .self[#S4#]; +// EXT_INFIX_2-DAG: Decl[InfixOperatorFunction]/CurrModule/TypeRelation[Convertible]: +++ {#S4#}[#S4#]; +// EXT_INFIX_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem/TypeRelation[Convertible]: + {#S4#}[#S4#]; +// EXT_INFIX_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#S4#}[#Bool#]; func testExtInfix3(x: S4) { - x + x#^EXT_INFIX_3?check=S4_EXT_INFIX_SIMPLE^# + x + x#^EXT_INFIX_3^# } -// S4_EXT_INFIX_SIMPLE-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: + {#S4#}[#S4#] -// S4_EXT_INFIX_SIMPLE-DAG: Decl[InfixOperatorFunction]/CurrModule: +++ {#S4#}[#S4#] +// EXT_INFIX_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem/TypeRelation[Convertible]: + {#S4#}[#S4#] +// EXT_INFIX_3-DAG: Decl[InfixOperatorFunction]/CurrModule/TypeRelation[Convertible]: +++ {#S4#}[#S4#] func testExtInfix4(x: S4) { - 1 + 1.0 + x#^EXT_INFIX_4?check=S4_EXT_INFIX_SIMPLE^# + 1 + 1.0 + x#^EXT_INFIX_4^# } +// EXT_INFIX_4: Begin completions +// EXT_INFIX_4-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: + {#S4#}[#S4#] +// EXT_INFIX_4-DAG: Decl[InfixOperatorFunction]/CurrModule: +++ {#S4#}[#S4#] +// EXT_INFIX_4: End completions func testAssignTuple1() { ()#^ASSIGN_TUPLE_1^# @@ -323,7 +330,7 @@ func testAssignTuple2() { var y: S2 (x, y)#^ASSIGN_TUPLE_2^# } -// ASSIGN_TUPLE_2: BuiltinOperator/None: = {#(S2, S2)#}[#Void#]; +// ASSIGN_TUPLE_2: BuiltinOperator/None: = {#(S2, S2)#}; infix operator ====: ComparisonPrecedence @@ -336,12 +343,18 @@ func ||||(x: Boolish, y: @autoclosure ()->Boolish) -> Boolish { return x } func testAutoclosure(x: Boolish, y: Boolish) { if x #^INFIX_AUTOCLOSURE_1^# {} - if x &&&& y #^INFIX_AUTOCLOSURE_2?check=INFIX_AUTOCLOSURE_1^# {} - if x |||| y #^INFIX_AUTOCLOSURE_3?check=INFIX_AUTOCLOSURE_1^# {} - if x &&&& x |||| y #^INFIX_AUTOCLOSURE_4?check=INFIX_AUTOCLOSURE_1^# {} + if x &&&& y #^INFIX_AUTOCLOSURE_2^# {} + if x |||| y #^INFIX_AUTOCLOSURE_3?check=INFIX_AUTOCLOSURE_2^# {} + if x &&&& x |||| y #^INFIX_AUTOCLOSURE_4?check=INFIX_AUTOCLOSURE_2^# {} } // INFIX_AUTOCLOSURE_1-DAG: Decl[InfixOperatorFunction]/CurrModule: [' ']&&&& {#Boolish#}[#Boolish#]; // INFIX_AUTOCLOSURE_1-DAG: Decl[InfixOperatorFunction]/CurrModule: [' ']==== {#Boolish#}[#Boolish#]; // INFIX_AUTOCLOSURE_1-DAG: Decl[InfixOperatorFunction]/CurrModule: [' ']|||| {#Boolish#}[#Boolish#]; +// INFIX_AUTOCLOSURE_2: Begin completions +// INFIX_AUTOCLOSURE_2-DAG: Decl[InfixOperatorFunction]/CurrModule/TypeRelation[Convertible]: [' ']&&&& {#Boolish#}[#Boolish#]; +// INFIX_AUTOCLOSURE_2-DAG: Decl[InfixOperatorFunction]/CurrModule/TypeRelation[Convertible]: [' ']==== {#Boolish#}[#Boolish#]; +// INFIX_AUTOCLOSURE_2-DAG: Decl[InfixOperatorFunction]/CurrModule/TypeRelation[Convertible]: [' ']|||| {#Boolish#}[#Boolish#]; +// INFIX_AUTOCLOSURE_2: End completions + diff --git a/test/IDE/complete_pattern.swift b/test/IDE/complete_pattern.swift index a3dd213a65c37..1c7ad288bcfc3 100644 --- a/test/IDE/complete_pattern.swift +++ b/test/IDE/complete_pattern.swift @@ -206,3 +206,32 @@ func test_cc_in_pattern(subject: IntHolder, i1: Int) { } // CC_IN_PATTERN_1-DAG: Decl[LocalVar]/Local/TypeRelation[Convertible]: i1[#Int#]; name=i1 + +func testCompleteAfterPatternInClosure() { + func takeClosure(_ x: () -> Void) {} + + enum MyEnum { + case failure(Int) + } + + func test(value: MyEnum) { + takeClosure { + switch value { + case let .failure(error)#^AFTER_PATTERN_IN_CLOSURE^#: + break + } + } + } + + // AFTER_PATTERN_IN_CLOSURE-NOT: Begin completions +} + +func testIfLetInClosure(foo: Int?) { + func takeClosure(_ x: () -> Void) {} + + takeClosure { + if let items#^IF_LET_IN_CLOSURE^# = foo { + } + } + // IF_LET_IN_CLOSURE-NOT: Begin completions +} diff --git a/test/IDE/complete_rdar63965160.swift b/test/IDE/complete_rdar63965160.swift index e4ebfe1a95323..23e29e6a43028 100644 --- a/test/IDE/complete_rdar63965160.swift +++ b/test/IDE/complete_rdar63965160.swift @@ -1,5 +1,5 @@ -// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=STRINGLITERAL | %FileCheck %s -// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=NORMAL | %FileCheck %s +// RUN: %empty-directory(%t) +// RUN: %swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t protocol View {} @@ -25,11 +25,11 @@ struct Value { func test(values: [Value]) { _ = ForEach(values) { value in Text("foobar") - Text("value \(value.#^STRINGLITERAL^#)") + Text("value \(value.#^STRINGLITERAL?check=CHECK^#)") } _ = ForEach(values) { value in Text("foobar") - Text(value.#^NORMAL^#) + Text(value.#^NORMAL?check=CHECK^#) } } // CHECK: Begin completions, 2 items diff --git a/test/IDE/complete_repl_identifier_prefix_1.swift b/test/IDE/complete_repl_identifier_prefix_1.swift index 32fd306509b8a..4699fba7618c6 100644 --- a/test/IDE/complete_repl_identifier_prefix_1.swift +++ b/test/IDE/complete_repl_identifier_prefix_1.swift @@ -1,6 +1,5 @@ // RUN: %target-swift-ide-test -repl-code-completion -source-filename %s | %FileCheck %s -// CHECK-DAG: .self: _ // CHECK-DAG: {{^}}true: Bool{{$}} tru diff --git a/test/IDE/complete_result_builder.swift b/test/IDE/complete_result_builder.swift index 6d729dec1055b..96bcde48cd8e1 100644 --- a/test/IDE/complete_result_builder.swift +++ b/test/IDE/complete_result_builder.swift @@ -72,7 +72,7 @@ func testAcceptColorTagged(paramIntVal: Int, paramStringVal: String) { acceptColorTagged { color in paramIntVal.tag(#^IN_CLOSURE_COLOR_CONTEXT^#) -// IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[LocalVar]/Local: color; name=color +// IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[LocalVar]/Local: color[#Color#]; name=color // IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[LocalVar]/Local: taggedValue[#Tagged#]; name=taggedValue // IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[LocalVar]/Local: paramIntVal[#Int#]; name=paramIntVal // IN_CLOSURE_COLOR_CONTEXT-DAG: Decl[LocalVar]/Local: paramStringVal[#String#]; name=paramStringVal diff --git a/test/IDE/complete_trailing_if_expr.swift b/test/IDE/complete_trailing_if_expr.swift new file mode 100644 index 0000000000000..8b2f9f7e20d23 --- /dev/null +++ b/test/IDE/complete_trailing_if_expr.swift @@ -0,0 +1,24 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t + +struct MyStruct { + func takeAnotherClosure(_ y: () -> Void) {} +} + +func takeClosure(_ x: () -> Void) -> MyStruct {} + +func foo() { + takeClosure { + #^COMPLETE^# + }.takeAnotherClosure { + if true { + 1 + } else { + 1 + } + } +} + +// COMPLETE: Begin completions +// COMPLETE: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct +// COMPLETE: End completions diff --git a/test/IDE/complete_unapplied_static_ref_to_func_with_error.swift b/test/IDE/complete_unapplied_static_ref_to_func_with_error.swift index 82fd81fd38363..15d41e60291dd 100644 --- a/test/IDE/complete_unapplied_static_ref_to_func_with_error.swift +++ b/test/IDE/complete_unapplied_static_ref_to_func_with_error.swift @@ -1,5 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t +// Check that we don't crash func myGlobalFunction() -> Invalid {} @@ -7,21 +8,18 @@ struct Test { func myInstanceMethod() -> Invalid {} func testInstanceMethod() { - Test.myInstanceMethod#^INSTANCE_METHOD^# - // Check that we don't crash - // INSTANCE_METHOD-NOT: Begin completions + Test.myInstanceMethod#^INSTANCE_METHOD?check=NO_RESULTS^# } func testGlobalFunctionMethod() { - myGlobalFunction#^GLOBAL_FUNCTION^# + myGlobalFunction#^GLOBAL_FUNCTION?check=NO_RESULTS^# // Check that we don't crash - // GLOBAL_FUNCTION: Keyword[self]/CurrNominal: .self[#_#] } func testLocalFunction() { func myLocalFunction() -> Invalid {} - myLocalFunction#^LOCAL_FUNCTION^# - // LOCAL_FUNCTION: Keyword[self]/CurrNominal: .self[#_#] + myLocalFunction#^LOCAL_FUNCTION?check=NO_RESULTS^# } } +// NO_RESULTS-NOT: Begin completions diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index f81f9a6c31ade..9cabfbd9f20d9 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -262,7 +262,7 @@ class C6 { class C6 { func f1(e: SomeEnum1) { - if let x = Optional(e) where x == .#^UNRESOLVED_25?check=UNRESOLVED_3^# + if let x = Optional(e) where x == .#^UNRESOLVED_25?check=UNRESOLVED_3_OPT^# } } class C7 {} diff --git a/test/IDE/complete_value_expr.swift b/test/IDE/complete_value_expr.swift index e9c961e02cc98..623e524a971b7 100644 --- a/test/IDE/complete_value_expr.swift +++ b/test/IDE/complete_value_expr.swift @@ -5,8 +5,6 @@ // NOCRASH: Token -// NORESULT: Token - struct FooStruct { lazy var lazyInstanceVar = 0 var instanceVar = 0 @@ -352,8 +350,12 @@ struct NoMetaCompletions { typealias Foo = Int } func testMetatypeCompletions() { - NoMetaCompletions.Type.#^FOO_STRUCT_META_1?check=FOO_STRUCT_META^# + NoMetaCompletions.Type.#^FOO_STRUCT_META_1^# } +// FOO_STRUCT_META_1: Begin completions, 2 items +// FOO_STRUCT_META_1-DAG: Keyword[self]/CurrNominal: self[#NoMetaCompletions.Type.Type#]; name=self +// FOO_STRUCT_META_1-DAG: Keyword/CurrNominal: Type[#NoMetaCompletions.Type.Type#]; name=Type +// FOO_STRUCT_META_1: End completions func testMetatypeCompletionsWithoutDot() { NoMetaCompletions.Type#^FOO_STRUCT_META_2?check=FOO_STRUCT_META^# } @@ -388,11 +390,23 @@ func testImplicitlyCurriedFunc(_ fs: inout FooStruct) { // This call is ambiguous, and the expression is invalid. // Ensure that we don't suggest to call the result. - FooStruct.overloadedInstanceFunc1(&fs)#^IMPLICITLY_CURRIED_OVERLOADED_FUNC_1?check=NORESULT^# + FooStruct.overloadedInstanceFunc1(&fs)#^IMPLICITLY_CURRIED_OVERLOADED_FUNC_1^# +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_1: Begin completions, 4 items +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_1-DAG: Keyword[self]/CurrNominal: .self[#() -> Int#]; +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_1-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ()[#Int#]; +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_1-DAG: Keyword[self]/CurrNominal: .self[#() -> Double#]; +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_1-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ()[#Double#]; +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_1: End completions // This call is ambiguous, and the expression is invalid. // Ensure that we don't suggest to call the result. - FooStruct.overloadedInstanceFunc2(&fs)#^IMPLICITLY_CURRIED_OVERLOADED_FUNC_2?check=NORESULT^# + FooStruct.overloadedInstanceFunc2(&fs)#^IMPLICITLY_CURRIED_OVERLOADED_FUNC_2^# +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_2: Begin completions, 4 items +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_2-DAG: Keyword[self]/CurrNominal: .self[#(Int) -> Int#]; +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_2-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#(x): Int#})[#Int#]; +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_2-DAG: Keyword[self]/CurrNominal: .self[#(Double) -> Int#]; +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_2-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]: ({#(x): Double#})[#Int#]; +// IMPLICITLY_CURRIED_OVERLOADED_FUNC_2: End completions } //===--- @@ -619,13 +633,15 @@ class FuncTypeVars { var funcTypeVarsObject: FuncTypeVars func testFuncTypeVars() { funcTypeVarsObject.funcVar1#^VF1^# +// VF1: Begin completions, 3 items // VF1-DAG: Pattern/CurrModule/Flair[ArgLabels]: ()[#Double#]{{; name=.+$}} -// VF1-DAG: BuiltinOperator/None: = {#() -> Double##() -> Double#}[#Void#] +// VF1-DAG: BuiltinOperator/None: = {#() -> Double##() -> Double#} // VF1-DAG: Keyword[self]/CurrNominal: .self[#() -> Double#]; name=self funcTypeVarsObject.funcVar2#^VF2^# +// VF2: Begin completions, 3 items // VF2-DAG: Pattern/CurrModule/Flair[ArgLabels]: ({#Int#})[#Double#]{{; name=.+$}} -// VF2-DAG: BuiltinOperator/None: = {#(Int) -> Double##(_ a: Int) -> Double#}[#Void#] +// VF2-DAG: BuiltinOperator/None: = {#(Int) -> Double##(_ a: Int) -> Double#} // VF2-DAG: Keyword[self]/CurrNominal: .self[#(Int) -> Double#]; name=self } @@ -1103,18 +1119,18 @@ func testResolveModules1() { //===--- Check that we can complete inside interpolated string literals func testInterpolatedString1() { - "\(fooObject.#^INTERPOLATED_STRING_1?check=FOO_OBJECT_DOT1^#)" + "\(fooObject.#^INTERPOLATED_STRING_1^#)" } -// FOO_OBJECT_DOT1-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: lazyInstanceVar[#Int#]{{; name=.+$}} -// FOO_OBJECT_DOT1-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: instanceVar[#Int#]{{; name=.+$}} -// FOO_OBJECT_DOT1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc0()[#Void#]{{; name=.+$}} -// FOO_OBJECT_DOT1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} -// FOO_OBJECT_DOT1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc2({#(a): Int#}, {#b: &Double#})[#Void#]{{; name=.+$}} -// FOO_OBJECT_DOT1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc3({#(a): Int#}, {#(Float, Double)#})[#Void#]{{; name=.+$}} -// FOO_OBJECT_DOT1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc4({#(a): Int?#}, {#b: Int!#}, {#c: &Int?#}, {#d: &Int!#})[#Void#]{{; name=.+$}} -// FOO_OBJECT_DOT1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc5()[#Int?#]{{; name=.+$}} -// FOO_OBJECT_DOT1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc6()[#Int!#]{{; name=.+$}} +// INTERPOLATED_STRING_1-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: lazyInstanceVar[#Int#]{{; name=.+$}} +// INTERPOLATED_STRING_1-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: instanceVar[#Int#]{{; name=.+$}} +// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc0()[#Void#]{{; name=.+$}} +// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}} +// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc2({#(a): Int#}, {#b: &Double#})[#Void#]{{; name=.+$}} +// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc3({#(a): Int#}, {#(Float, Double)#})[#Void#]{{; name=.+$}} +// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc4({#(a): Int?#}, {#b: Int!#}, {#c: &Int?#}, {#d: &Int!#})[#Void#]{{; name=.+$}} +// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc5()[#Int?#]{{; name=.+$}} +// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc6()[#Int!#]{{; name=.+$}} //===--- Check protocol extensions @@ -1685,7 +1701,7 @@ func testKeyword(cat: Cat) { let _ = cat.class#^KEYWORD_2^# // KEYWORD_2-DAG: Decl[InstanceVar]/CurrNominal: .prop1[#String#]; name=prop1 // KEYWORD_2-DAG: Decl[InstanceVar]/CurrNominal: .prop2[#String#]; name=prop2 -// KEYWORD_2-DAG: BuiltinOperator/None: = {#Cat.Inner#}[#Void#]; name== +// KEYWORD_2-DAG: BuiltinOperator/None: = {#Cat.Inner#}; name== let _ = cat.class.#^KEYWORD_3^# // KEYWORD_3-DAG: Decl[InstanceVar]/CurrNominal: prop1[#String#]; name=prop1 diff --git a/test/IDE/complete_vararg.swift b/test/IDE/complete_vararg.swift index c3cf5757cd42e..193e599fcea87 100644 --- a/test/IDE/complete_vararg.swift +++ b/test/IDE/complete_vararg.swift @@ -45,8 +45,10 @@ func testObjDot1() { // OBJ_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: method6({#class: Int...#})[#Void#]{{; name=.+$}} // OBJ_DOT_1-DAG: Decl[InstanceMethod]/CurrNominal: method7({#`inout`: Int...#})[#Void#]{{; name=.+$}} -func testFreeFunc() { +func testFreeFunc1() { freeFunc1(#^FREE_FUNC_1^# +} +func testFreeFunc2() { freeFunc2(#^FREE_FUNC_2^# } // FREE_FUNC_1: Begin completions, 1 items @@ -60,8 +62,10 @@ func testInit() { // INIT_1: Begin completions, 1 items // INIT_1: Decl[Constructor]/CurrNominal/Flair[ArgLabels]: ['(']{#x: Int...#}[')'][#C#]{{; name=.+$}} -func testMethod() { +func testMethod1() { obj.method1(#^METHOD_1^# +} +func testMethod2() { obj.method2(#^METHOD_2^# } // METHOD_1: Begin completions, 1 items diff --git a/test/IDE/complete_with_adjacent_string_literal.swift b/test/IDE/complete_with_adjacent_string_literal.swift new file mode 100644 index 0000000000000..e369c7e7644ad --- /dev/null +++ b/test/IDE/complete_with_adjacent_string_literal.swift @@ -0,0 +1,20 @@ +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t + +func takeClosure(x: () -> Void) {} +func takeString(_ a: String) -> MyStruct {} + +struct MyStruct { + func style(arg: Int) {} +} + +func foo() { + takeClosure { + takeString("\(1)") + .style(#^COMPLETE^#) + } +} + +// COMPLETE: Begin completions, 1 items +// COMPLETE: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]/TypeRelation[Convertible]: ['(']{#arg: Int#}[')'][#Void#]; +// COMPLETE: End completions + diff --git a/test/IDE/conforming-methods-after-var-in-closure.swift b/test/IDE/conforming-methods-after-var-in-closure.swift new file mode 100644 index 0000000000000..b8ac7a0e56759 --- /dev/null +++ b/test/IDE/conforming-methods-after-var-in-closure.swift @@ -0,0 +1,11 @@ +// Check that we don't crash +// RUN: %target-swift-ide-test -conforming-methods -source-filename %s -code-completion-token=TOK + +func takeClosure(_ x: () -> Void) {} + +func test(name: String?) { + takeClosure { + guard let url#^TOK^# = name else { + } + } +} diff --git a/test/Sema/string_interpolations.swift b/test/Sema/string_interpolations.swift new file mode 100644 index 0000000000000..94f82c529bfab --- /dev/null +++ b/test/Sema/string_interpolations.swift @@ -0,0 +1,58 @@ +// RUN: %target-typecheck-verify-swift + +do { + enum E: String, CaseIterable { + static var allCases: [E] { + [.one, .two] + } + + case one + case two + + func test(id: String) { + for c in Self.allCases where id == "\(c)" { // Ok + } + } + } +} + +do { + struct Data { + var text: String + + static func fn(_: (inout Data) -> Void) {} + static func fn(_: (inout Int) -> Void) {} + } + + func test(_: @autoclosure () -> T) { + } + + test((1...3).map { number in Data.fn { $0.text = "\(number)" } }) + + func test_multi(_: () -> T) { + } + + test_multi { + let x = 1...3 + _ = x.map { number in + Data.fn { + if $0.text == "\(number)" { + $0.text = "\(x)" + } + } + } + } +} + +// This case is (currently) interesting because "\(query)" is type-checked +// separately as part of ~= operator application. +func test_interpolation_in_case(query: String) { + _ = { (request: String) in + switch request { + case "\(query)": // Ok + break + default: + break + } + } +} diff --git a/test/SourceKit/CodeComplete/complete_inner.swift b/test/SourceKit/CodeComplete/complete_inner.swift index 1c11f13ca57b7..e65edd35c647b 100644 --- a/test/SourceKit/CodeComplete/complete_inner.swift +++ b/test/SourceKit/CodeComplete/complete_inner.swift @@ -37,6 +37,9 @@ func test010(x: E1, y: FooBar) { // INNER_POSTFIX_0b-NOT: key.description: "one{{.+}}" // INNER_POSTFIX_0b: key.description: "one",{{$}} // INNER_POSTFIX_0b: key.description: "one.",{{$}} +// INNER_POSTFIX_0b: key.description: "one==",{{$}} +// INNER_POSTFIX_0b: key.description: "one!=",{{$}} +// INNER_POSTFIX_0b: key.description: "one~=",{{$}} // INNER_POSTFIX_0b-NOT: key.description: "one{{.+}}" // RUN: %sourcekitd-test -req=complete.open -pos=27:9 -req-opts=filtertext=pro %s -- %s | %FileCheck %s -check-prefix=INNER_POSTFIX_1 diff --git a/test/SourceKit/CodeComplete/complete_sort_order.swift b/test/SourceKit/CodeComplete/complete_sort_order.swift index 30851bde8c05f..c562789474149 100644 --- a/test/SourceKit/CodeComplete/complete_sort_order.swift +++ b/test/SourceKit/CodeComplete/complete_sort_order.swift @@ -236,7 +236,7 @@ func test8() { } // CALLARG: (arg: String) // CALLARG: (label: Int) +// CALLARG: intVal // CALLARG: stringVal // CALLARG: String -// CALLARG: intVal } diff --git a/validation-test/IDE/crashers_2_fixed/0036-conformingmethodlist-already-typechecked.swift b/validation-test/IDE/crashers_2_fixed/0036-conformingmethodlist-already-typechecked.swift new file mode 100644 index 0000000000000..6d95d985713fd --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0036-conformingmethodlist-already-typechecked.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-ide-test -conforming-methods -source-filename %s -code-completion-token COMPLETE + +// This test used to crash while PostfixExpr completion was migrated to solver-based. + +struct MyPublisher { + func removeDuplicates() {} +} + +func handleEvents(receiveOutput: ((String) -> Void)? = nil) -> MyPublisher {} + +protocol AnyCancellable {} + +class CategoriesSearchViewModel { + func foo() { + var searchCancellable: AnyCancellable = handleEvents(receiveOutput: { [weak self] _ in }).removeDuplicates #^COMPLETE^# + } +} diff --git a/validation-test/IDE/crashers_2_fixed/0037-constructor-with-error-type.swift b/validation-test/IDE/crashers_2_fixed/0037-constructor-with-error-type.swift index 76b6e36877d49..b76ba80992c96 100644 --- a/validation-test/IDE/crashers_2_fixed/0037-constructor-with-error-type.swift +++ b/validation-test/IDE/crashers_2_fixed/0037-constructor-with-error-type.swift @@ -1,5 +1,5 @@ -// Make sure we don't crash -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t public struct AnyError { @@ -10,3 +10,8 @@ extension AnyError { return AnyError(error)#^COMPLETE^# } } + +// COMPLETE: Begin completions, 1 items +// COMPLETE: Keyword[self]/CurrNominal: .self[#AnyError#]; +// COMPLETE: End completions + diff --git a/validation-test/IDE/crashers_fixed/subexpr-literal-in-sequence-expr.swift b/validation-test/IDE/crashers_fixed/subexpr-literal-in-sequence-expr.swift index 2d68d98ee60af..7c484c04a3004 100644 --- a/validation-test/IDE/crashers_fixed/subexpr-literal-in-sequence-expr.swift +++ b/validation-test/IDE/crashers_fixed/subexpr-literal-in-sequence-expr.swift @@ -1,11 +1,12 @@ -// RUN: %target-swift-ide-test -code-completion -code-completion-token=A -source-filename=%s | %FileCheck %s -// RUN: %target-swift-ide-test -code-completion -code-completion-token=B -source-filename=%s | %FileCheck %s +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename=%s -filecheck %raw-FileCheck -completion-output-dir %t func test1() { 1 + [0]#^A^# +// A: Decl[InstanceVar]/CurrNominal/IsSystem/TypeRelation[Convertible]: .startIndex[#Int#]; name=startIndex } func test2() { "" + [""]#^B^# +// B: Decl[InstanceVar]/CurrNominal/IsSystem: .startIndex[#Int#]; name=startIndex } -// Sanity check results. -// CHECK: Decl[InstanceVar]/CurrNominal/IsSystem: .startIndex[#Int#]; name=startIndex +