From 2ba70dff88fd778ab0c6bc3078f42265de33d09e Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 10 Oct 2022 20:54:19 +0100 Subject: [PATCH 01/32] [CSClosure] Add `TapExpr` as a member of `SyntacticElementContext` --- lib/Sema/CSSyntacticElement.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 4ab0d5f882f74..a6e6fb845e455 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -410,13 +410,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 +456,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 +493,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"); } @@ -1542,6 +1548,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; } From e337a3947d0cfac9dc3a118e1151d2fcb442d469 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 25 Oct 2022 22:40:22 -0700 Subject: [PATCH 02/32] [ConstraintSystem] Implement tap expression checking in the solver Generate a conjunction for each tap expression body as soon as it gets a contextual type instead of separate post-factum type-checking via `typeCheckTapBody`. --- include/swift/Sema/ConstraintSystem.h | 40 ++++++++++++++++++++++++ lib/Sema/CSApply.cpp | 12 +++++++- lib/Sema/CSGen.cpp | 27 +++++++++++++--- lib/Sema/CSSimplify.cpp | 23 ++++++++++++++ lib/Sema/CSSyntacticElement.cpp | 44 ++++++++++++++++++++++++++- lib/Sema/TypeCheckConstraints.cpp | 4 +++ 6 files changed, 144 insertions(+), 6 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index b5c416693a8d3..c14ef7307c061 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; @@ -3951,6 +3954,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 +4371,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 + LLVM_NODISCARD + bool generateConstraints(TapExpr *tap); + /// Generate constraints for the body of the given function or closure. /// /// \param fn The function or closure expression @@ -5285,6 +5310,21 @@ 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/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 2c9066e3f1d4f..c84f904e3a782 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -8638,7 +8638,17 @@ namespace { for (const auto &tuple : TapsToTypeCheck) { auto tap = std::get<0>(tuple); auto tapDC = std::get<1>(tuple); - hadError |= TypeChecker::typeCheckTapBody(tap, tapDC); + + hadError |= cs.applySolutionToBody( + solution, tap, tapDC, [&](SolutionApplicationTarget target) { + auto resultTarget = rewriteTarget(target); + if (resultTarget) { + if (auto expr = resultTarget->getAsExpr()) + solution.setExprTypes(expr); + } + + return resultTarget; + }); } TapsToTypeCheck.clear(); } diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 82c0b79a65f80..9b527e8571f0f 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1236,15 +1236,34 @@ 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 tap expression is located in a closure, let's connect them + // because interpolation could use parameters. + if (auto *tap = getAsExpr(appendingExpr)) { + auto *tapDC = tap->getVar()->getDeclContext(); + if (auto *closure = dyn_cast(tapDC)) { + referencedVars.push_back( + CS.getType(closure)->castTo()); + } + } + + // 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; diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index aeadff9ad97bf..6d022498923d7 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -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)); @@ -11508,6 +11514,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, diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index a6e6fb845e455..3292d6b95eb8e 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -359,6 +359,17 @@ static void createConjunction(ConstraintSystem &cs, isIsolated = true; } + if (locator->directlyAt()) { + auto *tap = castToExpr(locator->getAnchor()); + auto *tapDC = tap->getVar()->getDeclContext(); + + referencedVars.push_back(cs.getType(tap)->castTo()); + + if (auto *closure = dyn_cast(tapDC)) { + referencedVars.push_back(cs.getType(closure)->castTo()); + } + } + UnresolvedVarCollector paramCollector(cs); for (const auto &entry : elements) { @@ -1375,6 +1386,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; @@ -2538,8 +2565,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/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; From 096f6f8bcd2a14ad9cd64d789181b8d595c1d2ce Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 26 Oct 2022 08:55:46 -0700 Subject: [PATCH 03/32] [CSApply] Don't delay tap expression solution application --- lib/Sema/CSApply.cpp | 58 +++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index c84f904e3a782..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,27 +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 |= cs.applySolutionToBody( - solution, tap, tapDC, [&](SolutionApplicationTarget target) { - auto resultTarget = rewriteTarget(target); - if (resultTarget) { - if (auto expr = resultTarget->getAsExpr()) - solution.setExprTypes(expr); - } - - return resultTarget; - }); - } - TapsToTypeCheck.clear(); - } - return hadError; } @@ -8677,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)) { @@ -8826,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; }); } From 60fa90938790f818990d4160f480821984a0ea11 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 23 Dec 2022 16:35:06 -0800 Subject: [PATCH 04/32] [CSSyntacticElement] Make sure that body of tap is solved in isolation It doesn't need access to anything besides external declarations which could be brought into the scope by context analyzer. --- lib/Sema/CSSyntacticElement.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 3292d6b95eb8e..8644b6ea02777 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -368,6 +368,11 @@ static void createConjunction(ConstraintSystem &cs, if (auto *closure = dyn_cast(tapDC)) { referencedVars.push_back(cs.getType(closure)->castTo()); } + + // 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); From 773513a5c7dd4820c2d84cf7d0b78ff8183802a8 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 23 Dec 2022 16:37:31 -0800 Subject: [PATCH 05/32] [Sema] Run structural/availability diagnostics on tap bodies Since interpolations are now type-checked using conjunctions these diagnostics could be rank as part of the root expression or statement verification. --- lib/Sema/ConstantnessSemaDiagnostics.cpp | 9 +-------- lib/Sema/MiscDiagnostics.cpp | 18 ------------------ lib/Sema/TypeCheckAvailability.cpp | 2 -- lib/Sema/TypeCheckConcurrency.cpp | 2 -- 4 files changed, 1 insertion(+), 30 deletions(-) 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/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/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; } From 4b84391953ced72209390ed035a2944134cf6ef6 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 14 Feb 2023 21:34:21 -0800 Subject: [PATCH 06/32] [Tests] Adjust a couple of string interpolation test-cases `test/Constraints/interpolation_segments.swift` has been removed because interpolations are now type-checked together with their context, so the separate type-checking test no longer applies. --- test/Constraints/closures.swift | 5 ++++- test/Constraints/interpolation_segments.swift | 21 ------------------- 2 files changed, 4 insertions(+), 22 deletions(-) delete mode 100644 test/Constraints/interpolation_segments.swift 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)" From d55f737cc5f7877694fa2407b0cf87297fade1f3 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 10 Mar 2023 13:16:11 -0800 Subject: [PATCH 07/32] [ConstraintSystem] NFC: Replace LLVM_NODISCARD with [[nodiscard]] on newly added method --- include/swift/Sema/ConstraintSystem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index c14ef7307c061..9243ac863f0dd 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4376,7 +4376,7 @@ class ConstraintSystem { /// \param tap the tap expression /// /// \returns \c true if constraint generation failed, \c false otherwise - LLVM_NODISCARD + [[nodiscard]] bool generateConstraints(TapExpr *tap); /// Generate constraints for the body of the given function or closure. From ac6ea62cec23627e37ee71a214c1ce136a9ba02c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 15 Mar 2023 10:45:40 -0700 Subject: [PATCH 08/32] [CSGen] NFC: Extract var reference collector to be used in more places --- lib/Sema/CSGen.cpp | 117 +++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 9b527e8571f0f..c99bac2145bfc 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; @@ -2902,68 +2957,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); @@ -2971,7 +2972,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, From 2fd5b5fb0457d35cab21776359a9ba3b22a18d4a Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 15 Mar 2023 10:46:31 -0700 Subject: [PATCH 09/32] [CSGen] Interpolations: Collect all vars referenced in the body to preserve connection to context Since "tap" bodies are now type-checked together with the context, it's imperative that the variable that represents an interpolation never gets disconnected from its context in the constraint system, otherwise it wouldn't be possible to determine types for the references. --- lib/Sema/CSGen.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index c99bac2145bfc..39430ba522800 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1304,14 +1304,26 @@ struct VarRefCollector : public ASTWalker { SmallVector referencedVars; - // If tap expression is located in a closure, let's connect them - // because interpolation could use parameters. if (auto *tap = getAsExpr(appendingExpr)) { auto *tapDC = tap->getVar()->getDeclContext(); + // If tap expression is located in a closure, let's + // connect them because interpolation could use parameters. if (auto *closure = dyn_cast(tapDC)) { referencedVars.push_back( CS.getType(closure)->castTo()); } + + // 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 From 4b091be1b9cdea02d9c030043bb919988b58dd89 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 15 Mar 2023 10:51:18 -0700 Subject: [PATCH 10/32] [Tests/Sema] NFC: Add a couple string interpolation test-cases --- test/Sema/string_interpolations.swift | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 test/Sema/string_interpolations.swift diff --git a/test/Sema/string_interpolations.swift b/test/Sema/string_interpolations.swift new file mode 100644 index 0000000000000..18bddf3d16fae --- /dev/null +++ b/test/Sema/string_interpolations.swift @@ -0,0 +1,45 @@ +// 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)" + } + } + } + } +} From ff0c942aa9ef42687307ded920209830e3f7c23c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 27 Mar 2023 10:24:49 -0700 Subject: [PATCH 11/32] [CSSyntacticElement] Fix solution applicator to identify context correctly `TapExpr` do not form their own declaration context which means that using `context.getAsDeclContext` is incorrect for them. --- lib/Sema/CSSyntacticElement.cpp | 47 ++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 8644b6ea02777..2bc5dab213d58 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -1645,7 +1645,6 @@ class SyntacticElementSolutionApplication protected: Solution &solution; SyntacticElementContext context; - Type resultType; RewriteTargetFn rewriteTarget; /// All `func`s declared in the body of the closure. @@ -1658,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); @@ -2031,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(); @@ -2056,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); @@ -2077,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()) From 77bd8ad746dd08678b9213bcbaf9154e2859f6d6 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 27 Mar 2023 11:02:35 -0700 Subject: [PATCH 12/32] [CSGen/SyntaticElement] Remove logic that connects tap expressions to closures Both single- and multi-statement closures now use variable reference collector to identify variables used in the interpolation body, which means that it's not longer necessary to connect to the closure explicitly (if interpolation is contained in one). --- lib/Sema/CSGen.cpp | 8 -------- lib/Sema/CSSyntacticElement.cpp | 9 --------- test/Sema/string_interpolations.swift | 13 +++++++++++++ 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 39430ba522800..50038b77057fc 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1305,14 +1305,6 @@ struct VarRefCollector : public ASTWalker { SmallVector referencedVars; if (auto *tap = getAsExpr(appendingExpr)) { - auto *tapDC = tap->getVar()->getDeclContext(); - // If tap expression is located in a closure, let's - // connect them because interpolation could use parameters. - if (auto *closure = dyn_cast(tapDC)) { - referencedVars.push_back( - CS.getType(closure)->castTo()); - } - // Collect all of the variable references that appear // in the tap body, otherwise tap expression is going // to get disconnected from the context. diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 2bc5dab213d58..5cf9028607ac7 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -360,15 +360,6 @@ static void createConjunction(ConstraintSystem &cs, } if (locator->directlyAt()) { - auto *tap = castToExpr(locator->getAnchor()); - auto *tapDC = tap->getVar()->getDeclContext(); - - referencedVars.push_back(cs.getType(tap)->castTo()); - - if (auto *closure = dyn_cast(tapDC)) { - referencedVars.push_back(cs.getType(closure)->castTo()); - } - // 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. diff --git a/test/Sema/string_interpolations.swift b/test/Sema/string_interpolations.swift index 18bddf3d16fae..94f82c529bfab 100644 --- a/test/Sema/string_interpolations.swift +++ b/test/Sema/string_interpolations.swift @@ -43,3 +43,16 @@ do { } } } + +// 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 + } + } +} From 6cec68e302477f30264387a1b0533214aa5d6f8e Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 23 Feb 2023 11:39:23 +0100 Subject: [PATCH 13/32] [IDE] Ignore score kinds that represent implicit conversions when solving for code completion Ignore conversion score increases during code completion to make sure we don't filter solutions that might start receiving the best score based on a choice of the code completion token. --- include/swift/IDE/PostfixCompletion.h | 36 +++++- .../swift/IDE/UnresolvedMemberCompletion.h | 12 ++ include/swift/Sema/ConstraintSystem.h | 6 +- lib/IDE/PostfixCompletion.cpp | 120 +++++++++++++----- lib/IDE/UnresolvedMemberCompletion.cpp | 51 ++++++-- lib/Sema/CSBindings.cpp | 2 +- lib/Sema/CSRanking.cpp | 102 ++++++++++++--- lib/Sema/CSSimplify.cpp | 112 ++++++++-------- lib/Sema/CSStep.cpp | 4 +- lib/Sema/CSStep.h | 6 +- lib/Sema/ConstraintSystem.cpp | 19 +-- test/IDE/complete_call_arg.swift | 61 +++++++++ test/IDE/complete_enum_elements.swift | 2 +- test/IDE/complete_expr_postfix_begin.swift | 16 +-- test/IDE/complete_in_result_builder.swift | 10 +- test/IDE/complete_rdar63965160.swift | 4 +- test/IDE/complete_unresolved_members.swift | 2 +- 17 files changed, 423 insertions(+), 142 deletions(-) diff --git a/include/swift/IDE/PostfixCompletion.h b/include/swift/IDE/PostfixCompletion.h index f097f9f4d6b36..0d8d0cf51585a 100644 --- a/include/swift/IDE/PostfixCompletion.h +++ b/include/swift/IDE/PostfixCompletion.h @@ -25,11 +25,32 @@ namespace ide { /// type-checking. 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; + + /// 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 +61,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; 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/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 9243ac863f0dd..3347729204b4e 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -938,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. @@ -5228,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. diff --git a/lib/IDE/PostfixCompletion.cpp b/lib/IDE/PostfixCompletion.cpp index 7fae130f0a0e1..3bd8964b9b363 100644 --- a/lib/IDE/PostfixCompletion.cpp +++ b/lib/IDE/PostfixCompletion.cpp @@ -21,6 +21,64 @@ 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(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()); @@ -101,39 +159,43 @@ void PostfixCompletionCallback::sawSolutionImpl( } } - 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; - } - } + bool BaseIsStaticMetaType = S.isStaticallyDerivedMetatype(ParsedExpr); + + SmallVector ExpectedTypes; + if (ExpectedTy) { + ExpectedTypes.push_back(ExpectedTy); + } - Results.push_back({BaseTy, ReferencedDecl, - /*ExpectedTypes=*/{}, DisallowVoid, ISDMT, - ImplicitReturn, IsAsync, ClosureActorIsolations}); - if (ExpectedTy) { - Results.back().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; } - } 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); + if (SAT.second.getAsExpr() == CompletionExpr) { + ExpectsNonVoid |= SAT.second.getExprContextualTypePurpose() != CTP_Unused; } } + + bool IsImplicitSingleExpressionReturn = + isImplicitSingleExpressionReturn(CS, CompletionExpr); + + Result Res = { + BaseTy, + ReferencedDecl, + BaseIsStaticMetaType, + ExpectedTypes, + ExpectsNonVoid, + IsImplicitSingleExpressionReturn, + IsAsync, + ClosureActorIsolations + }; + + addResult(Res); } void PostfixCompletionCallback::deliverResults( 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/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index a735a76aa3810..41a1bda1aa951 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -2315,7 +2315,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/CSRanking.cpp b/lib/Sema/CSRanking.cpp index 6f3e971eda791..189e1681d6d1e 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); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 6d022498923d7..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(); } @@ -5845,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; } @@ -6129,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; } @@ -7375,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); } @@ -7414,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); } @@ -7436,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); } @@ -7446,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); } @@ -7555,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); } @@ -7563,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); } @@ -7696,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(); } } @@ -7704,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(); } } @@ -7727,7 +7729,7 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, break; } if (allowConversion) { - increaseScore(SK_FunctionConversion); + increaseScore(SK_FunctionConversion, locator); return getTypeMatchSuccess(); } } @@ -8229,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. @@ -11652,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; } @@ -11661,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 @@ -12223,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)) { @@ -13756,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); } }; @@ -13766,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(), @@ -13924,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()) { @@ -13994,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); } @@ -14005,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 @@ -14015,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, @@ -14042,7 +14045,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( return SolutionKind::Error; } - increaseScore(ScoreKind::SK_ValueToPointerConversion); + increaseScore(ScoreKind::SK_ValueToPointerConversion, locator); return SolutionKind::Solved; } @@ -14055,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); } @@ -14092,7 +14095,7 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( return SolutionKind::Error; } - increaseScore(SK_ValueToPointerConversion); + increaseScore(SK_ValueToPointerConversion, locator); type1 = getFixedTypeRecursive(type1->getInOutObjectType()->isArrayType(), /*wantRValue=*/false); @@ -14105,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); @@ -14133,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, @@ -14154,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 = @@ -14182,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, @@ -14199,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; } @@ -14224,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; @@ -14515,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. @@ -14987,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 @@ -14996,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..10249677c88c1 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); } } } @@ -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/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 9622977e0be9c..8ffe4f1373f19 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); } } @@ -3450,7 +3450,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 +3660,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 +3676,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 +3705,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 +3799,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/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index 88cd522d5dc67..472bd70485030 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -1302,6 +1302,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]) { diff --git a/test/IDE/complete_enum_elements.swift b/test/IDE/complete_enum_elements.swift index 71d955cb42030..d00c091a84ff9 100644 --- a/test/IDE/complete_enum_elements.swift +++ b/test/IDE/complete_enum_elements.swift @@ -343,7 +343,7 @@ 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^#) 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_in_result_builder.swift b/test/IDE/complete_in_result_builder.swift index 41db06c8ec9a2..7eb233578b7c0 100644 --- a/test/IDE/complete_in_result_builder.swift +++ b/test/IDE/complete_in_result_builder.swift @@ -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?xfail=rdar106720628^#)" +// 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 } } @@ -327,13 +330,12 @@ 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#]; } diff --git a/test/IDE/complete_rdar63965160.swift b/test/IDE/complete_rdar63965160.swift index e4ebfe1a95323..d81539efc0728 100644 --- a/test/IDE/complete_rdar63965160.swift +++ b/test/IDE/complete_rdar63965160.swift @@ -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_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 {} From 4c186c0a9aab5a688640db7221b41f698f4a4fad Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 14 Feb 2023 16:02:54 +0100 Subject: [PATCH 14/32] [CodeCompletion] Fix code completion issues related to type checking tap expressions in the cosntraint system --- lib/Sema/TypeCheckCodeCompletion.cpp | 9 ------- test/IDE/complete_in_result_builder.swift | 2 +- test/IDE/complete_rdar63965160.swift | 14 +++++++---- test/IDE/complete_value_expr.swift | 24 +++++++++---------- .../issue-57058.swift | 2 +- 5 files changed, 23 insertions(+), 28 deletions(-) diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 996f50992c721..f431c5a67dba2 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -586,15 +586,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. diff --git a/test/IDE/complete_in_result_builder.swift b/test/IDE/complete_in_result_builder.swift index 7eb233578b7c0..fe5d879454751 100644 --- a/test/IDE/complete_in_result_builder.swift +++ b/test/IDE/complete_in_result_builder.swift @@ -197,7 +197,7 @@ func testCompleteInStringLiteral() { } // STRING_LITERAL_VAR: Begin completions, 2 items // STRING_LITERAL_VAR-DAG: Keyword[self]/CurrNominal: self[#Island#]; name=self -// STRING_LITERAL_VAR-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: turnipPrice[#String#]; name=turnipPrice +// STRING_LITERAL_VAR-DAG: Decl[InstanceVar]/CurrNominal: turnipPrice[#String#]; name=turnipPrice } func bar(island: Island) { diff --git a/test/IDE/complete_rdar63965160.swift b/test/IDE/complete_rdar63965160.swift index d81539efc0728..2865bd5a05f98 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 {} @@ -32,6 +32,10 @@ func test(values: [Value]) { Text(value.#^NORMAL?check=CHECK^#) } } -// CHECK: Begin completions, 2 items -// CHECK-DAG: Keyword[self]/CurrNominal: self[#Value#]; -// CHECK-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: name[#String#]; +// STRINGLITERAL: Begin completions, 2 items +// STRINGLITERAL-DAG: Keyword[self]/CurrNominal: self[#Value#]; +// STRINGLITERAL-DAG: Decl[InstanceVar]/CurrNominal: name[#String#]; + +// NORMAL: Begin completions, 2 items +// NORMAL-DAG: Keyword[self]/CurrNominal: self[#Value#]; +// NORMAL-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: name[#String#]; diff --git a/test/IDE/complete_value_expr.swift b/test/IDE/complete_value_expr.swift index e9c961e02cc98..b7cf6d40f3bcd 100644 --- a/test/IDE/complete_value_expr.swift +++ b/test/IDE/complete_value_expr.swift @@ -1103,18 +1103,18 @@ func testResolveModules1() { //===--- Check that we can complete inside interpolated string literals func testInterpolatedString1() { - "\(fooObject.#^INTERPOLATED_STRING_1?check=FOO_OBJECT_DOT1^#)" -} - -// 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=.+$}} + "\(fooObject.#^INTERPOLATED_STRING_1^#)" +} + +// INTERPOLATED_STRING_1-DAG: Decl[InstanceVar]/CurrNominal: lazyInstanceVar[#Int#]{{; name=.+$}} +// INTERPOLATED_STRING_1-DAG: Decl[InstanceVar]/CurrNominal: 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 diff --git a/validation-test/IDE/stress_tester_issues_fixed/issue-57058.swift b/validation-test/IDE/stress_tester_issues_fixed/issue-57058.swift index 79202f8fe84ca..0a18981537422 100644 --- a/validation-test/IDE/stress_tester_issues_fixed/issue-57058.swift +++ b/validation-test/IDE/stress_tester_issues_fixed/issue-57058.swift @@ -32,4 +32,4 @@ struct MysteryIslandDetail { // CHECK: Begin completions, 2 items // CHECK-DAG: Keyword[self]/CurrNominal: self[#MysteryIsland2#]; name=self -// CHECK-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: chance[#Int#]; name=chance +// CHECK-DAG: Decl[InstanceVar]/CurrNominal: chance[#Int#]; name=chance From 48dd99b12d86a2fb96b4d247b3438118b1bfb68e Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 14 Mar 2023 16:05:32 -0700 Subject: [PATCH 15/32] [IDE] Don't rank based on overload choices of function calls that contain the code completion token --- lib/Sema/CSRanking.cpp | 19 +++++++++++++++++++ test/IDE/complete_ambiguous.swift | 4 ++-- test/IDE/complete_in_closures.swift | 13 +++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index 189e1681d6d1e..282f052ba2cc5 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -956,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/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index d5df091e49eb6..288fb08320a74 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -443,8 +443,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_in_closures.swift b/test/IDE/complete_in_closures.swift index 203a9214cb3dc..04ac410e0c7f6 100644 --- a/test/IDE/complete_in_closures.swift +++ b/test/IDE/complete_in_closures.swift @@ -485,3 +485,16 @@ 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 + +} From 0c413109d30d1b49df849e8f2d97a80e62f3f532 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 14 Mar 2023 16:26:05 -0700 Subject: [PATCH 16/32] [CodeComplete] Skip conjunction elements unrelated to the code completion token --- lib/Sema/BuilderTransform.cpp | 19 +++++++++++++ lib/Sema/CSSyntacticElement.cpp | 27 ++++++++++++------- test/IDE/complete_trailing_if_expr.swift | 24 +++++++++++++++++ ...omplete_with_adjacent_string_literal.swift | 15 +++++++++++ 4 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 test/IDE/complete_trailing_if_expr.swift create mode 100644 test/IDE/complete_with_adjacent_string_literal.swift diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index aad4a5207baba..143bb39c1f1ea 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 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 diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index 5cf9028607ac7..26465899c3d9c 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -1167,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; } } 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_with_adjacent_string_literal.swift b/test/IDE/complete_with_adjacent_string_literal.swift new file mode 100644 index 0000000000000..9148784f61d2c --- /dev/null +++ b/test/IDE/complete_with_adjacent_string_literal.swift @@ -0,0 +1,15 @@ +// 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() {} +} + +func foo() { + takeClosure { + takeString("\(1)") + .style(#^COMPLETE^#) + } +} From 1e723ddbc2955a1504b06da0012daaad358fa8d5 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 23 Feb 2023 12:20:19 +0100 Subject: [PATCH 17/32] [CodeCompletion] Don't increase the score for holes at the code completion token --- lib/Sema/CSBindings.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 41a1bda1aa951..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(); From c385fe5e872748872d1e63c6db8c4ea0924aaaac Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 16 Feb 2023 13:57:02 +0100 Subject: [PATCH 18/32] [CodeCompletion] Migrate PostfixExprParen to solver-based --- lib/IDE/CodeCompletion.cpp | 41 +------------------ lib/Parse/ParseDecl.cpp | 17 +++++--- test/IDE/complete_at_top_level.swift | 2 +- test/IDE/complete_call_arg.swift | 21 +++++----- test/IDE/complete_constructor.swift | 8 ++-- test/IDE/complete_crashes.swift | 2 +- .../complete_diagnostics_concurrency.swift | 2 +- ..._enum_unresolved_dot_argument_labels.swift | 4 +- test/IDE/complete_in_result_builder.swift | 12 +++--- test/IDE/complete_init_inherited.swift | 4 +- test/IDE/complete_result_builder.swift | 2 +- test/IDE/complete_vararg.swift | 8 +++- 12 files changed, 48 insertions(+), 75 deletions(-) diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 2cc7a2477e979..df69421768d9a 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -1507,6 +1507,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,6 +1693,7 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) { case CompletionKind::AfterPoundExpr: case CompletionKind::AccessorBeginning: case CompletionKind::CaseStmtBeginning: + case CompletionKind::PostfixExprParen: llvm_unreachable("should be already handled"); return; @@ -1712,45 +1714,6 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) { 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); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 652cf4cb871cd..4f03a067d8700 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=*/None); status.setHasCodeCompletionAndIsError(); } else { // If we have no local context to parse the initial value into, create diff --git a/test/IDE/complete_at_top_level.swift b/test/IDE/complete_at_top_level.swift index 2951456b1ea94..884543853bcce 100644 --- a/test/IDE/complete_at_top_level.swift +++ b/test/IDE/complete_at_top_level.swift @@ -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.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 472bd70485030..909b1a3227628 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#]; } } @@ -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 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..aa22f162de46f 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() { 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_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_in_result_builder.swift b/test/IDE/complete_in_result_builder.swift index fe5d879454751..55bb3e62296b7 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,7 @@ func testStaticMemberLookup() { } @TupleBuilder var x3 { - "hello: \(StringFactory.#^COMPLETE_STATIC_MEMBER_IN_STRING_LITERAL?xfail=rdar106720628^#)" + "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 @@ -135,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#]; } } 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_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_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 From 00eaed3af955ba6d84485706f612bc10c8e711cc Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 14 Mar 2023 16:37:14 -0700 Subject: [PATCH 19/32] [CodeCompletion] Migrate postfix expr completion to solver-based --- include/swift/IDE/CompletionLookup.h | 11 +- include/swift/IDE/PostfixCompletion.h | 24 +- include/swift/Parse/IDEInspectionCallbacks.h | 5 +- lib/IDE/CodeCompletion.cpp | 40 ++- lib/IDE/CompletionLookup.cpp | 15 +- lib/IDE/ConformingMethodList.cpp | 6 +- lib/IDE/PostfixCompletion.cpp | 229 ++++++++++++++++-- lib/Parse/ParseExpr.cpp | 15 +- lib/Sema/ConstraintSystem.cpp | 19 +- test/IDE/complete_after_super.swift | 84 ++++++- test/IDE/complete_ambiguous.swift | 5 +- test/IDE/complete_at_top_level.swift | 6 +- test/IDE/complete_call_as_function.swift | 4 +- test/IDE/complete_crashes.swift | 13 +- test/IDE/complete_enum_elements.swift | 8 +- test/IDE/complete_expr_tuple.swift | 8 +- test/IDE/complete_from_stdlib.swift | 14 +- test/IDE/complete_in_closures.swift | 18 +- test/IDE/complete_literal.swift | 3 +- test/IDE/complete_opaque_result.swift | 2 +- test/IDE/complete_operators.swift | 63 +++-- test/IDE/complete_pattern.swift | 19 ++ ...pplied_static_ref_to_func_with_error.swift | 12 +- test/IDE/complete_value_expr.swift | 32 ++- .../subexpr-literal-in-sequence-expr.swift | 9 +- 25 files changed, 503 insertions(+), 161 deletions(-) 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 0d8d0cf51585a..aa11705683c78 100644 --- a/include/swift/IDE/PostfixCompletion.h +++ b/include/swift/IDE/PostfixCompletion.h @@ -20,9 +20,8 @@ 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. @@ -32,6 +31,11 @@ class PostfixCompletionCallback : public TypeCheckCompletionCallback { /// 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 @@ -90,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/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/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index df69421768d9a..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: { @@ -1694,26 +1698,10 @@ void CodeCompletionCallbacksImpl::doneParsing(SourceFile *SrcFile) { 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::KeyPathExprObjC: { if (DotLoc.isValid()) Lookup.setHaveDot(DotLoc); @@ -1724,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, @@ -1949,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..ff81c194651a0 100644 --- a/lib/IDE/ConformingMethodList.cpp +++ b/lib/IDE/ConformingMethodList.cpp @@ -47,7 +47,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; @@ -59,10 +59,10 @@ void ConformingMethodListCallbacks::completeDotExpr(CodeCompletionExpr *E, ParsedExpr = E->getBase(); } -void ConformingMethodListCallbacks::completePostfixExpr(Expr *E, +void ConformingMethodListCallbacks::completePostfixExpr(CodeCompletionExpr *E, bool hasSpace) { CurDeclContext = P.CurDeclContext; - ParsedExpr = E; + ParsedExpr = E->getBase(); } void ConformingMethodListCallbacks::doneParsing(SourceFile *SrcFile) { diff --git a/lib/IDE/PostfixCompletion.cpp b/lib/IDE/PostfixCompletion.cpp index 3bd8964b9b363..68a900c2946c0 100644 --- a/lib/IDE/PostfixCompletion.cpp +++ b/lib/IDE/PostfixCompletion.cpp @@ -38,6 +38,7 @@ 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) && @@ -126,6 +127,29 @@ 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(); + } else { + return false; + } + default: + return false; + } +} + void PostfixCompletionCallback::sawSolutionImpl( const constraints::Solution &S) { auto &CS = S.getConstraintSystem(); @@ -147,16 +171,10 @@ 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(); - - 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); - } + IsBaseDeclUnapplied = isUnappliedFunctionRef(SelectedOverload->choice); } bool BaseIsStaticMetaType = S.isStaticallyDerivedMetatype(ParsedExpr); @@ -184,30 +202,204 @@ void PostfixCompletionCallback::sawSolutionImpl( bool IsImplicitSingleExpressionReturn = isImplicitSingleExpressionReturn(CS, CompletionExpr); + bool IsInAsyncContext = isContextAsync(S, DC); + llvm::DenseMap + ClosureActorIsolations; + for (auto SAT : S.targets) { + if (auto ACE = getAsExpr(SAT.second.getAsASTNode())) { + ClosureActorIsolations[ACE] = getClosureActorIsolation(S, ACE); + } + } + Result Res = { BaseTy, ReferencedDecl, + IsBaseDeclUnapplied, BaseIsStaticMetaType, ExpectedTypes, ExpectsNonVoid, IsImplicitSingleExpressionReturn, - IsAsync, + 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; + } + + 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; + } + } + + 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)) @@ -220,6 +412,10 @@ void PostfixCompletionCallback::deliverResults( Lookup.includeInstanceMembers(); Lookup.setPreferFunctionReferencesToCalls(); } + SmallVector Operators; + if (IncludeOperators) { + Lookup.collectOperators(Operators); + } Lookup.shouldCheckForDuplicates(Results.size() > 1); for (auto &Result : Results) { @@ -232,7 +428,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/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/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 8ffe4f1373f19..ae4d4f39d2e0c 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -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); } 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 288fb08320a74..3e7f938891716 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^# diff --git a/test/IDE/complete_at_top_level.swift b/test/IDE/complete_at_top_level.swift index 884543853bcce..5298d6f04f4ad 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() {} diff --git a/test/IDE/complete_call_as_function.swift b/test/IDE/complete_call_as_function.swift index d0ceea806ce6d..10b86c6d850b6 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.Type?#}[#Bool#]; +// METATYPE_NO_DOT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#Any.Type?#}[#Bool#]; let _ = addTy.#^METATYPE_DOT^#; // METATYPE_DOT: Begin completions, 3 items diff --git a/test/IDE/complete_crashes.swift b/test/IDE/complete_crashes.swift index aa22f162de46f..509f50121dbe2 100644 --- a/test/IDE/complete_crashes.swift +++ b/test/IDE/complete_crashes.swift @@ -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_enum_elements.swift b/test/IDE/complete_enum_elements.swift index d00c091a84ff9..03de56d2b8b98 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 @@ -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_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 04ac410e0c7f6..010b4528dd3be 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 } } diff --git a/test/IDE/complete_literal.swift b/test/IDE/complete_literal.swift index 13554eee3b539..02a16134897dc 100644 --- a/test/IDE/complete_literal.swift +++ b/test/IDE/complete_literal.swift @@ -76,7 +76,8 @@ func testArray(f1: Float) { func testDict(f1: Float) { _ = ["foo": f1, "bar": "baz"] #^LITERAL9^# } -// LITERAL9-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .keys[#Dictionary.Keys#]; name=keys +// LITERAL9-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .keys[#Dictionary.Keys#]; name=keys +// LITERAL9-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .keys[#Dictionary.Keys#]; name=keys // LITERAL9-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .isEmpty[#Bool#]; name=isEmpty func testEditorPlaceHolder() { 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..e54c9820880ee 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.Type?#}[#Bool#]; +// INFIX_15-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#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..9b332580fa7e9 100644 --- a/test/IDE/complete_pattern.swift +++ b/test/IDE/complete_pattern.swift @@ -206,3 +206,22 @@ 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 +} 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_value_expr.swift b/test/IDE/complete_value_expr.swift index b7cf6d40f3bcd..290ae38216f94 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 } @@ -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/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 + From 4d1e44fbf1f1ffb9a0baedeb3df07c7530b588ee Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 20 Feb 2023 17:45:58 +0100 Subject: [PATCH 20/32] [CodeCompletion] Don't typecheck expression if result builder transform was applied --- lib/Sema/TypeCheckStmt.cpp | 20 ++++++++++++-------- test/IDE/complete_in_result_builder.swift | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 0e5b6aa9792bf..5dba4c2d3d4de 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -2587,18 +2587,22 @@ bool TypeCheckASTNodeAtLocRequest::evaluate( func, builderType, /*ClosuresInResultBuilderDontParticipateInInference=*/ ctx.CompletionCallback == nullptr && ctx.SolutionCallback == nullptr); - if (optBody && *optBody) { + if (ctx.CompletionCallback && ctx.CompletionCallback->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/IDE/complete_in_result_builder.swift b/test/IDE/complete_in_result_builder.swift index 55bb3e62296b7..66c8e11fb5feb 100644 --- a/test/IDE/complete_in_result_builder.swift +++ b/test/IDE/complete_in_result_builder.swift @@ -339,3 +339,24 @@ func testSwitchInResultBuilder() { // SWITCH_IN_RESULT_BUILDER: Begin completions, 1 item // SWITCH_IN_RESULT_BUILDER-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Convertible]: alertDismissed[#Action#]; } + +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 + } +} From 928a03a2e003afa5b757702709bdbc3b52448dcd Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 30 Jun 2022 15:57:01 +0200 Subject: [PATCH 21/32] [CodeCompletion] Migrate conforming methods list to solver-based --- lib/IDE/ConformingMethodList.cpp | 69 ++++++++++++++----- lib/Sema/TypeCheckStmt.cpp | 4 +- ...ormingmethodlist-already-typechecked.swift | 17 +++++ 3 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 validation-test/IDE/crashers_2_fixed/0036-conformingmethodlist-already-typechecked.swift diff --git a/lib/IDE/ConformingMethodList.cpp b/lib/IDE/ConformingMethodList.cpp index ff81c194651a0..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, @@ -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(CodeCompletionExpr *E, bool hasSpace) { CurDeclContext = P.CurDeclContext; - ParsedExpr = E->getBase(); + 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/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 5dba4c2d3d4de..f1b5ad8927b85 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -2586,8 +2586,8 @@ bool TypeCheckASTNodeAtLocRequest::evaluate( auto optBody = TypeChecker::applyResultBuilderBodyTransform( func, builderType, /*ClosuresInResultBuilderDontParticipateInInference=*/ - ctx.CompletionCallback == nullptr && ctx.SolutionCallback == nullptr); - if (ctx.CompletionCallback && ctx.CompletionCallback->gotCallback()) { + 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 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^# + } +} From d3270c1476aada75810b5395c37fd5903522e2e9 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 30 Jun 2022 17:00:07 +0200 Subject: [PATCH 22/32] [CodeCompletion] Calling a static function on a type is not unapplied --- lib/IDE/PostfixCompletion.cpp | 2 +- test/IDE/complete_call_pattern_heuristics.swift | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/IDE/PostfixCompletion.cpp b/lib/IDE/PostfixCompletion.cpp index 68a900c2946c0..c01fe51ae2f5d 100644 --- a/lib/IDE/PostfixCompletion.cpp +++ b/lib/IDE/PostfixCompletion.cpp @@ -141,7 +141,7 @@ static bool isUnappliedFunctionRef(const OverloadChoice &Choice) { // We consider curried member calls as unapplied. E.g. // MyStruct.someInstanceFunc(theInstance)#^COMPLETE^# // is unapplied. - return BaseTy->is(); + return BaseTy->is() && !Choice.getDeclOrNull()->isStatic(); } else { return false; } diff --git a/test/IDE/complete_call_pattern_heuristics.swift b/test/IDE/complete_call_pattern_heuristics.swift index d1beda0bbdd8f..f3a813ac5e563 100644 --- a/test/IDE/complete_call_pattern_heuristics.swift +++ b/test/IDE/complete_call_pattern_heuristics.swift @@ -38,3 +38,14 @@ 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 +} From c7e0bfae02849ee9ca7b3a3b46767bfe5b656bbd Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 15 Feb 2023 15:06:28 +0100 Subject: [PATCH 23/32] [IDE] Adjust test cases for migrating all completion kinds to solver-based --- test/IDE/complete_ambiguous.swift | 8 +++- test/IDE/complete_call_arg.swift | 22 ++++++++- .../complete_call_pattern_heuristics.swift | 46 ++++++++++++++++++- test/IDE/complete_enum_elements.swift | 2 +- test/IDE/complete_in_result_builder.swift | 4 +- test/IDE/complete_issue-56811.swift | 2 +- test/IDE/complete_literal.swift | 5 +- .../complete_repl_identifier_prefix_1.swift | 1 - ...omplete_with_adjacent_string_literal.swift | 7 ++- .../CodeComplete/complete_inner.swift | 3 ++ .../CodeComplete/complete_sort_order.swift | 2 +- .../0037-constructor-with-error-type.swift | 9 +++- .../IDE/issues_fixed/issue-57041.swift | 4 +- 13 files changed, 97 insertions(+), 18 deletions(-) diff --git a/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index 3e7f938891716..a603940e465e0 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -395,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") diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index 909b1a3227628..5c1e523980ce8 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -686,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...) { @@ -1370,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?xfail=TODO^#) +// 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_pattern_heuristics.swift b/test/IDE/complete_call_pattern_heuristics.swift index f3a813ac5e563..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,6 +33,23 @@ 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^#] @@ -49,3 +66,30 @@ struct StaticMethods { // 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_enum_elements.swift b/test/IDE/complete_enum_elements.swift index 03de56d2b8b98..216a1d07ff8f9 100644 --- a/test/IDE/complete_enum_elements.swift +++ b/test/IDE/complete_enum_elements.swift @@ -349,7 +349,7 @@ func testUnqualified1(x: QuxEnum) { _ = (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=== diff --git a/test/IDE/complete_in_result_builder.swift b/test/IDE/complete_in_result_builder.swift index 66c8e11fb5feb..94758ffeb7d63 100644 --- a/test/IDE/complete_in_result_builder.swift +++ b/test/IDE/complete_in_result_builder.swift @@ -59,7 +59,7 @@ func testGlobalLookup() { @TupleBuilder var x5 { "hello: \(#^GLOBAL_LOOKUP_IN_STRING_LITERAL^#)" -// GLOBAL_LOOKUP_IN_STRING_LITERAL: Decl[GlobalVar]/CurrModule/TypeRelation[Convertible]: MyConstantString[#String#]; +// GLOBAL_LOOKUP_IN_STRING_LITERAL: Decl[GlobalVar]/CurrModule: MyConstantString[#String#]; } @TupleBuilder var x5 { @@ -87,7 +87,7 @@ func testStaticMemberLookup() { @TupleBuilder var x3 { "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: Decl[StaticMethod]/CurrNominal: makeString({#x: String#})[#String#]; // COMPLETE_STATIC_MEMBER_IN_STRING_LITERAL: End completions } } diff --git a/test/IDE/complete_issue-56811.swift b/test/IDE/complete_issue-56811.swift index f617a0935b5ad..052347b732991 100644 --- a/test/IDE/complete_issue-56811.swift +++ b/test/IDE/complete_issue-56811.swift @@ -14,4 +14,4 @@ public enum Endpoint {     }   } } -// CHECK: Decl[LocalVar]/Local/TypeRelation[Convertible]: myInt[#Int#]; +// CHECK: Decl[LocalVar]/Local: myInt[#Int#]; diff --git a/test/IDE/complete_literal.swift b/test/IDE/complete_literal.swift index 02a16134897dc..3f186b0b92adb 100644 --- a/test/IDE/complete_literal.swift +++ b/test/IDE/complete_literal.swift @@ -71,13 +71,12 @@ 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^# } -// LITERAL9-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .keys[#Dictionary.Keys#]; name=keys -// LITERAL9-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .keys[#Dictionary.Keys#]; name=keys +// LITERAL9-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .keys[#Dictionary.Keys#]; name=keys // LITERAL9-DAG: Decl[InstanceVar]/CurrNominal/IsSystem: .isEmpty[#Bool#]; name=isEmpty func testEditorPlaceHolder() { 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_with_adjacent_string_literal.swift b/test/IDE/complete_with_adjacent_string_literal.swift index 9148784f61d2c..e369c7e7644ad 100644 --- a/test/IDE/complete_with_adjacent_string_literal.swift +++ b/test/IDE/complete_with_adjacent_string_literal.swift @@ -4,7 +4,7 @@ func takeClosure(x: () -> Void) {} func takeString(_ a: String) -> MyStruct {} struct MyStruct { - func style() {} + func style(arg: Int) {} } func foo() { @@ -13,3 +13,8 @@ func foo() { .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/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/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/issues_fixed/issue-57041.swift b/validation-test/IDE/issues_fixed/issue-57041.swift index 86f69d6adf6d7..bed4d66b26c25 100644 --- a/validation-test/IDE/issues_fixed/issue-57041.swift +++ b/validation-test/IDE/issues_fixed/issue-57041.swift @@ -71,5 +71,5 @@ struct SplitView: View2 { } } -// COMPLETE-DAG: Decl[InstanceMethod]/Super/TypeRelation[Convertible]: frame()[#Never#]; name=frame() -// COMPLETE-DAG: Decl[InstanceMethod]/Super/TypeRelation[Convertible]: frame({#width: Int?#}, {#height: Int?#})[#Never#]; name=frame(width:height:) +// COMPLETE-DAG: Decl[InstanceMethod]/Super: frame()[#Never#]; name=frame() +// COMPLETE-DAG: Decl[InstanceMethod]/Super: frame({#width: Int?#}, {#height: Int?#})[#Never#]; name=frame(width:height:) From 71937667f26b5bef21075c3de0eaa5357d8e06c2 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 17 Feb 2023 15:53:17 +0100 Subject: [PATCH 24/32] =?UTF-8?q?[IDE]=20Don=E2=80=99t=20return=20any=20re?= =?UTF-8?q?sult=20if=20the=20base=20expression=E2=80=99s=20type=20in=20pos?= =?UTF-8?q?tfix=20completion=20couldn=E2=80=99t=20be=20inferred?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/IDE/PostfixCompletion.cpp | 4 ++++ test/IDE/complete_pattern.swift | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/IDE/PostfixCompletion.cpp b/lib/IDE/PostfixCompletion.cpp index c01fe51ae2f5d..dfadcfe8f7775 100644 --- a/lib/IDE/PostfixCompletion.cpp +++ b/lib/IDE/PostfixCompletion.cpp @@ -156,6 +156,10 @@ void PostfixCompletionCallback::sawSolutionImpl( 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 diff --git a/test/IDE/complete_pattern.swift b/test/IDE/complete_pattern.swift index 9b332580fa7e9..1c7ad288bcfc3 100644 --- a/test/IDE/complete_pattern.swift +++ b/test/IDE/complete_pattern.swift @@ -225,3 +225,13 @@ func testCompleteAfterPatternInClosure() { // 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 +} From 1bacfe9cd4789861f0e37a5c83ed0331fa392966 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 14 Mar 2023 16:44:15 -0700 Subject: [PATCH 25/32] [IDE] Ignore unspported constructs in result builders in code completion mode --- lib/Sema/BuilderTransform.cpp | 9 +++++++-- lib/Sema/CSStep.cpp | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 143bb39c1f1ea..aa9e05bb298e3 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -264,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); + } } } @@ -1171,7 +1176,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/CSStep.cpp b/lib/Sema/CSStep.cpp index 10249677c88c1..080b4585b2cad 100644 --- a/lib/Sema/CSStep.cpp +++ b/lib/Sema/CSStep.cpp @@ -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) { From f2017b82ac01f0d9e0a153cfab91fdb39c4374c8 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 14 Mar 2023 16:44:19 -0700 Subject: [PATCH 26/32] [IDE] Don't fallback type check if the completion expression is inside a closure --- lib/IDE/PostfixCompletion.cpp | 8 +++ lib/IDE/TypeCheckCompletionCallback.cpp | 7 ++- lib/Sema/TypeCheckCodeCompletion.cpp | 38 +++++++++---- test/IDE/complete_in_closures.swift | 53 +++++++++++++++++++ ..._multiple_trailingclosure_signatures.swift | 14 +++++ 5 files changed, 108 insertions(+), 12 deletions(-) diff --git a/lib/IDE/PostfixCompletion.cpp b/lib/IDE/PostfixCompletion.cpp index dfadcfe8f7775..c3a54993964ad 100644 --- a/lib/IDE/PostfixCompletion.cpp +++ b/lib/IDE/PostfixCompletion.cpp @@ -95,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); 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/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index f431c5a67dba2..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)); @@ -647,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/test/IDE/complete_in_closures.swift b/test/IDE/complete_in_closures.swift index 010b4528dd3be..510d0d565aee8 100644 --- a/test/IDE/complete_in_closures.swift +++ b/test/IDE/complete_in_closures.swift @@ -498,3 +498,56 @@ func testBinaryOperatorWithEnum() { // 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^# + + // FIXME: We shouldn't be suggesting 'self' and '()' twice here + // AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS: Begin completions, 4 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: 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-DAG: Pattern/CurrModule/Flair[ArgLabels]: ()[#Void#]; name=() + // AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS: End completions +} 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 +} From c00428a1f45dacd1adcbbb6131e2c6b8bbc50c6d Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 20 Feb 2023 16:02:07 +0100 Subject: [PATCH 27/32] [IDE] New test cases that were failing at some point during solver-based migration --- lib/AST/ASTNode.cpp | 1 + .../complete_after_pattern_in_closure.swift | 16 ++++++++++++ ..._dont_filter_overloads_with_cc_token.swift | 25 +++++++++++++++++++ test/IDE/complete_in_closures.swift | 12 +++++++++ ...forming-methods-after-var-in-closure.swift | 11 ++++++++ 5 files changed, 65 insertions(+) create mode 100644 test/IDE/complete_after_pattern_in_closure.swift create mode 100644 test/IDE/complete_dont_filter_overloads_with_cc_token.swift create mode 100644 test/IDE/conforming-methods-after-var-in-closure.swift 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/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_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_in_closures.swift b/test/IDE/complete_in_closures.swift index 510d0d565aee8..704df7b75b3f0 100644 --- a/test/IDE/complete_in_closures.swift +++ b/test/IDE/complete_in_closures.swift @@ -551,3 +551,15 @@ func testCompleteAfterClosureWithIfExprThatContainErrors() { // AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS-DAG: Pattern/CurrModule/Flair[ArgLabels]: ()[#Void#]; 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/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 { + } + } +} From 527a79e9f856adf24023973b97456688d1016176 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 10 Mar 2023 16:02:57 -0800 Subject: [PATCH 28/32] [IDE] Set constraint system options from `solveForCodeCompletion` in `applyResultBuilderBodyTransform` when solving for code completion --- lib/Sema/BuilderTransform.cpp | 3 +++ test/IDE/complete_call_arg.swift | 2 +- test/IDE/complete_in_result_builder.swift | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index aa9e05bb298e3..8a1f196073360 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1008,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); diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index 5c1e523980ce8..c0f322d1553bd 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -1383,7 +1383,7 @@ struct AmbiguousCallInResultBuilder { func ttroke(_ content: Int, lineWidth: Int = 1) -> Int { 42 } @MyResultBuilder var body: Int { - self.ttroke(1, #^AMBIGUOUS_IN_RESULT_BUILDER?xfail=TODO^#) + 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#]; diff --git a/test/IDE/complete_in_result_builder.swift b/test/IDE/complete_in_result_builder.swift index 94758ffeb7d63..2efffc52663de 100644 --- a/test/IDE/complete_in_result_builder.swift +++ b/test/IDE/complete_in_result_builder.swift @@ -360,3 +360,24 @@ func testCompleteIfLetInResultBuilder() { // 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 + } + } + +} From 4f5743c8c652787c31b90a337f0da4b80147159a Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 10 Mar 2023 16:28:06 -0800 Subject: [PATCH 29/32] [IDE] Adjust more test cases --- test/IDE/complete_in_closures.swift | 5 +---- test/IDE/complete_in_result_builder.swift | 6 +++--- test/IDE/complete_issue-56811.swift | 2 +- test/IDE/complete_rdar63965160.swift | 10 +++------- test/IDE/complete_value_expr.swift | 4 ++-- validation-test/IDE/issues_fixed/issue-57041.swift | 4 ++-- .../IDE/stress_tester_issues_fixed/issue-57058.swift | 2 +- 7 files changed, 13 insertions(+), 20 deletions(-) diff --git a/test/IDE/complete_in_closures.swift b/test/IDE/complete_in_closures.swift index 704df7b75b3f0..b6f4df8b5468c 100644 --- a/test/IDE/complete_in_closures.swift +++ b/test/IDE/complete_in_closures.swift @@ -543,12 +543,9 @@ func testCompleteAfterClosureWithIfExprThatContainErrors() { } }#^AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS^# - // FIXME: We shouldn't be suggesting 'self' and '()' twice here - // AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS: Begin completions, 4 items + // 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: 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-DAG: Pattern/CurrModule/Flair[ArgLabels]: ()[#Void#]; name=() // AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS: End completions } diff --git a/test/IDE/complete_in_result_builder.swift b/test/IDE/complete_in_result_builder.swift index 2efffc52663de..12924357e055e 100644 --- a/test/IDE/complete_in_result_builder.swift +++ b/test/IDE/complete_in_result_builder.swift @@ -59,7 +59,7 @@ func testGlobalLookup() { @TupleBuilder var x5 { "hello: \(#^GLOBAL_LOOKUP_IN_STRING_LITERAL^#)" -// GLOBAL_LOOKUP_IN_STRING_LITERAL: Decl[GlobalVar]/CurrModule: MyConstantString[#String#]; +// GLOBAL_LOOKUP_IN_STRING_LITERAL: Decl[GlobalVar]/CurrModule/TypeRelation[Convertible]: MyConstantString[#String#]; } @TupleBuilder var x5 { @@ -87,7 +87,7 @@ func testStaticMemberLookup() { @TupleBuilder var x3 { "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: makeString({#x: String#})[#String#]; +// COMPLETE_STATIC_MEMBER_IN_STRING_LITERAL: Decl[StaticMethod]/CurrNominal/TypeRelation[Convertible]: makeString({#x: String#})[#String#]; // COMPLETE_STATIC_MEMBER_IN_STRING_LITERAL: End completions } } @@ -197,7 +197,7 @@ func testCompleteInStringLiteral() { } // STRING_LITERAL_VAR: Begin completions, 2 items // STRING_LITERAL_VAR-DAG: Keyword[self]/CurrNominal: self[#Island#]; name=self -// STRING_LITERAL_VAR-DAG: Decl[InstanceVar]/CurrNominal: turnipPrice[#String#]; name=turnipPrice +// STRING_LITERAL_VAR-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: turnipPrice[#String#]; name=turnipPrice } func bar(island: Island) { diff --git a/test/IDE/complete_issue-56811.swift b/test/IDE/complete_issue-56811.swift index 052347b732991..f617a0935b5ad 100644 --- a/test/IDE/complete_issue-56811.swift +++ b/test/IDE/complete_issue-56811.swift @@ -14,4 +14,4 @@ public enum Endpoint {     }   } } -// CHECK: Decl[LocalVar]/Local: myInt[#Int#]; +// CHECK: Decl[LocalVar]/Local/TypeRelation[Convertible]: myInt[#Int#]; diff --git a/test/IDE/complete_rdar63965160.swift b/test/IDE/complete_rdar63965160.swift index 2865bd5a05f98..23e29e6a43028 100644 --- a/test/IDE/complete_rdar63965160.swift +++ b/test/IDE/complete_rdar63965160.swift @@ -32,10 +32,6 @@ func test(values: [Value]) { Text(value.#^NORMAL?check=CHECK^#) } } -// STRINGLITERAL: Begin completions, 2 items -// STRINGLITERAL-DAG: Keyword[self]/CurrNominal: self[#Value#]; -// STRINGLITERAL-DAG: Decl[InstanceVar]/CurrNominal: name[#String#]; - -// NORMAL: Begin completions, 2 items -// NORMAL-DAG: Keyword[self]/CurrNominal: self[#Value#]; -// NORMAL-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: name[#String#]; +// CHECK: Begin completions, 2 items +// CHECK-DAG: Keyword[self]/CurrNominal: self[#Value#]; +// CHECK-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: name[#String#]; diff --git a/test/IDE/complete_value_expr.swift b/test/IDE/complete_value_expr.swift index 290ae38216f94..623e524a971b7 100644 --- a/test/IDE/complete_value_expr.swift +++ b/test/IDE/complete_value_expr.swift @@ -1122,8 +1122,8 @@ func testInterpolatedString1() { "\(fooObject.#^INTERPOLATED_STRING_1^#)" } -// INTERPOLATED_STRING_1-DAG: Decl[InstanceVar]/CurrNominal: lazyInstanceVar[#Int#]{{; name=.+$}} -// INTERPOLATED_STRING_1-DAG: Decl[InstanceVar]/CurrNominal: instanceVar[#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=.+$}} diff --git a/validation-test/IDE/issues_fixed/issue-57041.swift b/validation-test/IDE/issues_fixed/issue-57041.swift index bed4d66b26c25..86f69d6adf6d7 100644 --- a/validation-test/IDE/issues_fixed/issue-57041.swift +++ b/validation-test/IDE/issues_fixed/issue-57041.swift @@ -71,5 +71,5 @@ struct SplitView: View2 { } } -// COMPLETE-DAG: Decl[InstanceMethod]/Super: frame()[#Never#]; name=frame() -// COMPLETE-DAG: Decl[InstanceMethod]/Super: frame({#width: Int?#}, {#height: Int?#})[#Never#]; name=frame(width:height:) +// COMPLETE-DAG: Decl[InstanceMethod]/Super/TypeRelation[Convertible]: frame()[#Never#]; name=frame() +// COMPLETE-DAG: Decl[InstanceMethod]/Super/TypeRelation[Convertible]: frame({#width: Int?#}, {#height: Int?#})[#Never#]; name=frame(width:height:) diff --git a/validation-test/IDE/stress_tester_issues_fixed/issue-57058.swift b/validation-test/IDE/stress_tester_issues_fixed/issue-57058.swift index 0a18981537422..79202f8fe84ca 100644 --- a/validation-test/IDE/stress_tester_issues_fixed/issue-57058.swift +++ b/validation-test/IDE/stress_tester_issues_fixed/issue-57058.swift @@ -32,4 +32,4 @@ struct MysteryIslandDetail { // CHECK: Begin completions, 2 items // CHECK-DAG: Keyword[self]/CurrNominal: self[#MysteryIsland2#]; name=self -// CHECK-DAG: Decl[InstanceVar]/CurrNominal: chance[#Int#]; name=chance +// CHECK-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: chance[#Int#]; name=chance From c453f2d89de270c1be3c295110612a8a1fae0f22 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 21 Jun 2023 00:15:22 -0700 Subject: [PATCH 30/32] [Tests] NFC: Adjust new code completion tests to account for `any` --- test/IDE/complete_at_top_level.swift | 2 +- test/IDE/complete_call_as_function.swift | 4 ++-- test/IDE/complete_in_closures.swift | 2 +- test/IDE/complete_operators.swift | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/IDE/complete_at_top_level.swift b/test/IDE/complete_at_top_level.swift index 5298d6f04f4ad..9296c767cd8da 100644 --- a/test/IDE/complete_at_top_level.swift +++ b/test/IDE/complete_at_top_level.swift @@ -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): Any.Type#}[')'][#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_as_function.swift b/test/IDE/complete_call_as_function.swift index 10b86c6d850b6..de6b26a8da311 100644 --- a/test/IDE/complete_call_as_function.swift +++ b/test/IDE/complete_call_as_function.swift @@ -44,8 +44,8 @@ func testCallAsFunction(add: Adder, addTy: Adder.Type) { // 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.Type?#}[#Bool#]; -// METATYPE_NO_DOT-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#Any.Type?#}[#Bool#]; +// 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_in_closures.swift b/test/IDE/complete_in_closures.swift index b6f4df8b5468c..27328d53b5130 100644 --- a/test/IDE/complete_in_closures.swift +++ b/test/IDE/complete_in_closures.swift @@ -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 } } diff --git a/test/IDE/complete_operators.swift b/test/IDE/complete_operators.swift index e54c9820880ee..4fa856575bba5 100644 --- a/test/IDE/complete_operators.swift +++ b/test/IDE/complete_operators.swift @@ -210,8 +210,8 @@ func testInfix15() { // 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.Type?#}[#Bool#]; -// INFIX_15-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#Any.Type?#}[#Bool#]; +// 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^# From ed4dc1becd7b555054a0904a7f860f9e75af3bdf Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 7 Jul 2023 21:16:33 +0200 Subject: [PATCH 31/32] Change `Optional` -> `llvm::Optional` --- include/swift/Sema/ConstraintSystem.h | 9 +++++---- lib/Parse/ParseDecl.cpp | 2 +- lib/Sema/BuilderTransform.cpp | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 3347729204b4e..e1d79e0f26d7f 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -5324,10 +5324,11 @@ class ConstraintSystem { /// 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); + 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 diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 4f03a067d8700..0e7dc92670047 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3696,7 +3696,7 @@ ParserResult Parser::parseCustomAttribute( argList = ArgumentList::createParsed( Context, lParenLoc, {Argument::unlabeled(CCE)}, rParenLoc, - /*trailingClosureIdx=*/None); + /*trailingClosureIdx=*/llvm::None); status.setHasCodeCompletionAndIsError(); } else { // If we have no local context to parse the initial value into, create diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 8a1f196073360..3088f5bfc56ad 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -201,7 +201,7 @@ class ResultBuilderTransform // 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 None; + return llvm::None; } auto result = visit(stmt, resultVar); From 597c0dc90aef87b0a587d4c1c6f2cd2d71ebfe57 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 7 Jul 2023 21:16:59 +0200 Subject: [PATCH 32/32] [SourceKit] Fix crash when completing a parameter of an unapplied instance member reference --- lib/IDE/ArgumentCompletion.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) 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(); }