| //===--- TypeCheckCodeCompletion.cpp - Type Checking for Code Completion --===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements various entry points for use by lib/IDE/. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "swift/Subsystems.h" |
| #include "TypeChecker.h" |
| #include "TypeCheckObjC.h" |
| #include "TypeCheckType.h" |
| #include "CodeSynthesis.h" |
| #include "MiscDiagnostics.h" |
| #include "swift/AST/ASTWalker.h" |
| #include "swift/AST/ASTVisitor.h" |
| #include "swift/AST/Attr.h" |
| #include "swift/AST/DiagnosticSuppression.h" |
| #include "swift/AST/ExistentialLayout.h" |
| #include "swift/AST/Identifier.h" |
| #include "swift/AST/ImportCache.h" |
| #include "swift/AST/Initializer.h" |
| #include "swift/AST/ModuleLoader.h" |
| #include "swift/AST/NameLookup.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/PrettyStackTrace.h" |
| #include "swift/AST/ProtocolConformance.h" |
| #include "swift/AST/SourceFile.h" |
| #include "swift/AST/Type.h" |
| #include "swift/AST/TypeCheckRequests.h" |
| #include "swift/Basic/Statistic.h" |
| #include "swift/Basic/STLExtras.h" |
| #include "swift/Parse/Lexer.h" |
| #include "swift/Sema/IDETypeChecking.h" |
| #include "swift/Sema/CodeCompletionTypeChecking.h" |
| #include "swift/Sema/ConstraintSystem.h" |
| #include "swift/Strings.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/PointerUnion.h" |
| #include "llvm/ADT/SmallSet.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/ADT/TinyPtrVector.h" |
| #include "llvm/ADT/Twine.h" |
| #include <algorithm> |
| |
| using namespace swift; |
| using namespace constraints; |
| |
| /// Find the declaration directly referenced by this expression. |
| static std::pair<ValueDecl *, FunctionRefKind> |
| findReferencedDecl(Expr *expr, DeclNameLoc &loc) { |
| do { |
| expr = expr->getSemanticsProvidingExpr(); |
| |
| if (auto ice = dyn_cast<ImplicitConversionExpr>(expr)) { |
| expr = ice->getSubExpr(); |
| continue; |
| } |
| |
| if (auto dre = dyn_cast<DeclRefExpr>(expr)) { |
| loc = dre->getNameLoc(); |
| return { dre->getDecl(), dre->getFunctionRefKind() }; |
| } |
| |
| return { nullptr, FunctionRefKind::Unapplied }; |
| } while (true); |
| } |
| |
| // Check if \p E is a call expression to curried thunk of "KeyPath as function". |
| // i.e. '{ `$kp$` in { $0[keyPath: $kp$] } }(keypath)' |
| static bool isKeyPathCurriedThunkCallExpr(Expr *E) { |
| auto CE = dyn_cast<CallExpr>(E); |
| if (!CE) |
| return false; |
| auto thunk = dyn_cast<AutoClosureExpr>(CE->getFn()); |
| if (!thunk) |
| return false; |
| if (thunk->getParameters()->size() != 1 || |
| thunk->getParameters()->get(0)->getParameterName().str() != "$kp$") |
| return false; |
| |
| auto PE = dyn_cast<ParenExpr>(CE->getArg()); |
| if (!PE) |
| return false; |
| return isa<KeyPathExpr>(PE->getSubExpr()); |
| } |
| |
| // Extract the keypath expression from the curried thunk expression. |
| static Expr *extractKeyPathFromCurryThunkCall(Expr *E) { |
| assert(isKeyPathCurriedThunkCallExpr(E)); |
| auto call = cast<CallExpr>(E); |
| auto arg = cast<ParenExpr>(call->getArg()); |
| return arg->getSubExpr(); |
| } |
| |
| namespace { |
| |
| /// AST walker that "sanitizes" an expression for re-typechecking during |
| /// code completion. |
| /// |
| /// FIXME: Remove this. |
| class SanitizeExpr : public ASTWalker { |
| ASTContext &C; |
| bool ShouldReusePrecheckedType; |
| llvm::SmallDenseMap<OpaqueValueExpr *, Expr *, 4> OpenExistentials; |
| |
| public: |
| SanitizeExpr(ASTContext &C, |
| bool shouldReusePrecheckedType) |
| : C(C), ShouldReusePrecheckedType(shouldReusePrecheckedType) { } |
| |
| std::pair<bool, Expr *> walkToExprPre(Expr *expr) override { |
| while (true) { |
| |
| // If we should reuse pre-checked types, don't sanitize the expression |
| // if it's already type-checked. |
| if (ShouldReusePrecheckedType && expr->getType()) |
| return { false, expr }; |
| |
| // OpenExistentialExpr contains OpaqueValueExpr in its sub expression. |
| if (auto OOE = dyn_cast<OpenExistentialExpr>(expr)) { |
| auto archetypeVal = OOE->getOpaqueValue(); |
| auto base = OOE->getExistentialValue(); |
| |
| bool inserted = OpenExistentials.insert({archetypeVal, base}).second; |
| assert(inserted && "OpaqueValue appears multiple times?"); |
| (void)inserted; |
| SWIFT_DEFER { OpenExistentials.erase(archetypeVal); }; |
| |
| // Walk to and return the base expression to erase any existentials |
| // within it. |
| return { false, OOE->getSubExpr()->walk(*this) }; |
| } |
| |
| // Hacky, this behaves just like an OpenedExistential in that it changes |
| // the expr tree. |
| if (auto ISLE = dyn_cast<InterpolatedStringLiteralExpr>(expr)) { |
| if (auto subExpr = ISLE->getAppendingExpr()->getSubExpr()) { |
| if (auto opaqueValue = dyn_cast<OpaqueValueExpr>(subExpr)) { |
| ISLE->getAppendingExpr()->setSubExpr(nullptr); |
| } |
| } |
| } |
| |
| // Substitute OpaqueValue with its representing existental. |
| if (auto OVE = dyn_cast<OpaqueValueExpr>(expr)) { |
| auto value = OpenExistentials.find(OVE); |
| |
| if (value != OpenExistentials.end()) { |
| expr = value->second; |
| continue; |
| } else { |
| assert(OVE->isPlaceholder() && |
| "Didn't see this OVE in a containing OpenExistentialExpr?"); |
| } |
| } |
| |
| // Skip any implicit conversions applied to this expression. |
| if (auto ICE = dyn_cast<ImplicitConversionExpr>(expr)) { |
| expr = ICE->getSubExpr(); |
| continue; |
| } |
| |
| // MakeTemporarilyEscapableExpr is typechecked expression. |
| if (auto MTEE = dyn_cast<MakeTemporarilyEscapableExpr>(expr)) { |
| expr = MTEE->getOriginalExpr(); |
| continue; |
| } |
| |
| // Extract keypath from '{ `$kp$` in { $0[keyPath: $kp$] } }(keypath)' |
| if (isKeyPathCurriedThunkCallExpr(expr)) { |
| expr = extractKeyPathFromCurryThunkCall(expr); |
| continue; |
| } |
| |
| // Restore '@autoclosure'd value. |
| if (auto ACE = dyn_cast<AutoClosureExpr>(expr)) { |
| // This is only valid if the closure doesn't have parameters. |
| if (ACE->getParameters()->size() == 0) { |
| expr = ACE->getSingleExpressionBody(); |
| continue; |
| } |
| llvm_unreachable("other AutoClosureExpr must be handled specially"); |
| } |
| |
| // Remove any semantic expression injected by typechecking. |
| if (auto EPE = dyn_cast<EditorPlaceholderExpr>(expr)) { |
| EPE->setSemanticExpr(nullptr); |
| } |
| |
| // Strip default arguments and varargs from type-checked call |
| // argument lists. |
| if (isa<ParenExpr>(expr) || isa<TupleExpr>(expr)) { |
| if (shouldSanitizeArgumentList(expr)) |
| expr = sanitizeArgumentList(expr); |
| } |
| |
| // If this expression represents keypath based dynamic member |
| // lookup, let's convert it back to the original form of |
| // member or subscript reference. |
| if (auto *SE = dyn_cast<SubscriptExpr>(expr)) { |
| if (auto *TE = dyn_cast<TupleExpr>(SE->getIndex())) { |
| auto isImplicitKeyPathExpr = [](Expr *argExpr) -> bool { |
| if (auto *KP = dyn_cast<KeyPathExpr>(argExpr)) |
| return KP->isImplicit(); |
| return false; |
| }; |
| |
| if (TE->isImplicit() && TE->getNumElements() == 1 && |
| TE->getElementName(0) == C.Id_dynamicMember && |
| isImplicitKeyPathExpr(TE->getElement(0))) { |
| auto *keyPathExpr = cast<KeyPathExpr>(TE->getElement(0)); |
| auto *componentExpr = keyPathExpr->getParsedPath(); |
| |
| if (auto *UDE = dyn_cast<UnresolvedDotExpr>(componentExpr)) { |
| UDE->setBase(SE->getBase()); |
| return {true, UDE}; |
| } |
| |
| if (auto *subscript = dyn_cast<SubscriptExpr>(componentExpr)) { |
| subscript->setBase(SE->getBase()); |
| return {true, subscript}; |
| } |
| |
| llvm_unreachable("unknown keypath component type"); |
| } |
| } |
| } |
| |
| // If this is a closure, only walk into its children if they |
| // are type-checked in the context of the enclosing expression. |
| if (auto closure = dyn_cast<ClosureExpr>(expr)) { |
| if (!shouldTypeCheckInEnclosingExpression(closure)) |
| return { false, expr }; |
| } |
| |
| // Now, we're ready to walk into sub expressions. |
| return {true, expr}; |
| } |
| } |
| |
| bool isSyntheticArgumentExpr(const Expr *expr) { |
| if (isa<DefaultArgumentExpr>(expr)) |
| return true; |
| |
| if (auto *varargExpr = dyn_cast<VarargExpansionExpr>(expr)) |
| if (isa<ArrayExpr>(varargExpr->getSubExpr())) |
| return true; |
| |
| return false; |
| } |
| |
| bool shouldSanitizeArgumentList(const Expr *expr) { |
| if (auto *parenExpr = dyn_cast<ParenExpr>(expr)) { |
| return isSyntheticArgumentExpr(parenExpr->getSubExpr()); |
| } else if (auto *tupleExpr = dyn_cast<TupleExpr>(expr)) { |
| for (auto *arg : tupleExpr->getElements()) { |
| if (isSyntheticArgumentExpr(arg)) |
| return true; |
| } |
| |
| return false; |
| } else { |
| return isSyntheticArgumentExpr(expr); |
| } |
| } |
| |
| Expr *sanitizeArgumentList(Expr *original) { |
| auto argList = getOriginalArgumentList(original); |
| |
| if (argList.args.size() == 1 && |
| argList.labels[0].empty() && |
| !isa<VarargExpansionExpr>(argList.args[0])) { |
| auto *result = |
| new (C) ParenExpr(argList.lParenLoc, |
| argList.args[0], |
| argList.rParenLoc, |
| argList.hasTrailingClosure); |
| result->setImplicit(); |
| return result; |
| } |
| |
| return TupleExpr::create(C, |
| argList.lParenLoc, |
| argList.args, |
| argList.labels, |
| argList.labelLocs, |
| argList.rParenLoc, |
| argList.hasTrailingClosure, |
| /*implicit=*/true); |
| } |
| |
| Expr *walkToExprPost(Expr *expr) override { |
| assert(!isa<ImplicitConversionExpr>(expr) && |
| "ImplicitConversionExpr should be eliminated in walkToExprPre"); |
| |
| auto buildMemberRef = [&](Type memberType, Expr *base, SourceLoc dotLoc, |
| ConcreteDeclRef member, DeclNameLoc memberLoc, |
| bool implicit) -> Expr * { |
| auto *memberRef = new (C) |
| MemberRefExpr(base, dotLoc, member, memberLoc, implicit); |
| |
| if (memberType) { |
| memberRef->setType(memberType); |
| return memberRef; |
| } |
| |
| return memberRef; |
| }; |
| |
| // A DotSyntaxCallExpr is a member reference that has already been |
| // type-checked down to a call; turn it back into an overloaded |
| // member reference expression. |
| if (auto dotCall = dyn_cast<DotSyntaxCallExpr>(expr)) { |
| DeclNameLoc memberLoc; |
| auto memberAndFunctionRef = findReferencedDecl(dotCall->getFn(), |
| memberLoc); |
| if (memberAndFunctionRef.first) { |
| assert(!isa<ImplicitConversionExpr>(dotCall->getBase())); |
| return buildMemberRef(dotCall->getType(), |
| dotCall->getBase(), |
| dotCall->getDotLoc(), |
| memberAndFunctionRef.first, |
| memberLoc, expr->isImplicit()); |
| } |
| } |
| |
| if (auto *dynamicMember = dyn_cast<DynamicMemberRefExpr>(expr)) { |
| if (auto memberRef = dynamicMember->getMember()) { |
| assert(!isa<ImplicitConversionExpr>(dynamicMember->getBase())); |
| return buildMemberRef(dynamicMember->getType(), |
| dynamicMember->getBase(), |
| dynamicMember->getDotLoc(), |
| memberRef, |
| dynamicMember->getNameLoc(), |
| expr->isImplicit()); |
| } |
| } |
| |
| // A DotSyntaxBaseIgnoredExpr is a static member reference that has |
| // already been type-checked down to a call where the argument doesn't |
| // actually matter; turn it back into an overloaded member reference |
| // expression. |
| if (auto dotIgnored = dyn_cast<DotSyntaxBaseIgnoredExpr>(expr)) { |
| DeclNameLoc memberLoc; |
| auto memberAndFunctionRef = findReferencedDecl(dotIgnored->getRHS(), |
| memberLoc); |
| if (memberAndFunctionRef.first) { |
| assert(!isa<ImplicitConversionExpr>(dotIgnored->getLHS())); |
| return buildMemberRef(dotIgnored->getType(), |
| dotIgnored->getLHS(), |
| dotIgnored->getDotLoc(), |
| memberAndFunctionRef.first, |
| memberLoc, expr->isImplicit()); |
| } |
| } |
| return expr; |
| } |
| |
| /// Ignore declarations. |
| bool walkToDeclPre(Decl *decl) override { return false; } |
| }; |
| |
| } // end namespace |
| |
| static Type |
| getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc, |
| ConcreteDeclRef &referencedDecl, |
| FreeTypeVariableBinding allowFreeTypeVariables) { |
| auto &Context = dc->getASTContext(); |
| |
| expr = expr->walk(SanitizeExpr(Context, |
| /*shouldReusePrecheckedType=*/false)); |
| |
| FrontendStatsTracer StatsTracer(Context.Stats, |
| "typecheck-expr-no-apply", expr); |
| PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); |
| referencedDecl = nullptr; |
| |
| // Construct a constraint system from this expression. |
| ConstraintSystem cs(dc, ConstraintSystemFlags::SuppressDiagnostics); |
| |
| // Attempt to solve the constraint system. |
| const Type originalType = expr->getType(); |
| const bool needClearType = originalType && originalType->hasError(); |
| const auto recoverOriginalType = [&] () { |
| if (needClearType) |
| expr->setType(originalType); |
| }; |
| |
| // If the previous checking gives the expr error type, clear the result and |
| // re-check. |
| if (needClearType) |
| expr->setType(Type()); |
| SolutionApplicationTarget target( |
| expr, dc, CTP_Unused, Type(), /*isDiscarded=*/false); |
| auto viable = cs.solve(target, allowFreeTypeVariables); |
| if (!viable) { |
| recoverOriginalType(); |
| return Type(); |
| } |
| |
| // Get the expression's simplified type. |
| expr = target.getAsExpr(); |
| auto &solution = (*viable)[0]; |
| auto &solutionCS = solution.getConstraintSystem(); |
| Type exprType = solution.simplifyType(solutionCS.getType(expr)); |
| |
| assert(exprType && !exprType->hasTypeVariable() && |
| "free type variable with FreeTypeVariableBinding::GenericParameters?"); |
| assert(exprType && !exprType->hasHole() && |
| "type hole with FreeTypeVariableBinding::GenericParameters?"); |
| |
| if (exprType->hasError()) { |
| recoverOriginalType(); |
| return Type(); |
| } |
| |
| // Dig the declaration out of the solution. |
| auto semanticExpr = expr->getSemanticsProvidingExpr(); |
| auto topLocator = cs.getConstraintLocator(semanticExpr); |
| referencedDecl = solution.resolveLocatorToDecl(topLocator); |
| |
| if (!referencedDecl.getDecl()) { |
| // Do another check in case we have a curried call from binding a function |
| // reference to a variable, for example: |
| // |
| // class C { |
| // func instanceFunc(p1: Int, p2: Int) {} |
| // } |
| // func t(c: C) { |
| // C.instanceFunc(c)#^COMPLETE^# |
| // } |
| // |
| // We need to get the referenced function so we can complete the argument |
| // labels. (Note that the requirement to have labels in the curried call |
| // seems inconsistent with the removal of labels from function types. |
| // If this changes the following code could be removed). |
| if (auto *CE = dyn_cast<CallExpr>(semanticExpr)) { |
| if (auto *UDE = dyn_cast<UnresolvedDotExpr>(CE->getFn())) { |
| if (isa<TypeExpr>(UDE->getBase())) { |
| auto udeLocator = cs.getConstraintLocator(UDE); |
| auto udeRefDecl = solution.resolveLocatorToDecl(udeLocator); |
| if (auto *FD = dyn_cast_or_null<FuncDecl>(udeRefDecl.getDecl())) { |
| if (FD->isInstanceMember()) |
| referencedDecl = udeRefDecl; |
| } |
| } |
| } |
| } |
| } |
| |
| // Recover the original type if needed. |
| recoverOriginalType(); |
| return exprType; |
| } |
| |
| static FunctionType * |
| getTypeOfCompletionOperatorImpl(DeclContext *DC, Expr *expr, |
| ConcreteDeclRef &referencedDecl) { |
| auto &Context = DC->getASTContext(); |
| |
| FrontendStatsTracer StatsTracer(Context.Stats, |
| "typecheck-completion-operator", expr); |
| PrettyStackTraceExpr stackTrace(Context, "type-checking", expr); |
| |
| expr = expr->walk(SanitizeExpr(Context, |
| /*shouldReusePrecheckedType=*/true)); |
| |
| ConstraintSystemOptions options; |
| options |= ConstraintSystemFlags::SuppressDiagnostics; |
| options |= ConstraintSystemFlags::ReusePrecheckedType; |
| |
| // Construct a constraint system from this expression. |
| ConstraintSystem CS(DC, options); |
| expr = CS.generateConstraints(expr, DC); |
| if (!expr) |
| return nullptr; |
| |
| if (CS.isDebugMode()) { |
| auto &log = llvm::errs(); |
| log << "---Initial constraints for the given expression---\n"; |
| expr->dump(log); |
| log << "\n"; |
| CS.print(log); |
| } |
| |
| // Attempt to solve the constraint system. |
| SmallVector<Solution, 4> viable; |
| if (CS.solve(viable, FreeTypeVariableBinding::Disallow)) |
| return nullptr; |
| |
| auto &solution = viable[0]; |
| if (CS.isDebugMode()) { |
| auto &log = llvm::errs(); |
| log << "---Solution---\n"; |
| solution.dump(log); |
| } |
| |
| // Fill the results. |
| Expr *opExpr = cast<ApplyExpr>(expr)->getFn(); |
| referencedDecl = |
| solution.resolveLocatorToDecl(CS.getConstraintLocator(opExpr)); |
| |
| // Return '(ArgType[, ArgType]) -> ResultType' as a function type. |
| // We don't use the type of the operator expression because we want the types |
| // of the *arguments* instead of the types of the parameters. |
| Expr *argsExpr = cast<ApplyExpr>(expr)->getArg(); |
| SmallVector<FunctionType::Param, 2> argTypes; |
| if (auto *PE = dyn_cast<ParenExpr>(argsExpr)) { |
| argTypes.emplace_back(solution.simplifyType(CS.getType(PE->getSubExpr()))); |
| } else if (auto *TE = dyn_cast<TupleExpr>(argsExpr)) { |
| for (auto arg : TE->getElements()) |
| argTypes.emplace_back(solution.simplifyType(CS.getType(arg))); |
| } |
| |
| return FunctionType::get(argTypes, solution.simplifyType(CS.getType(expr))); |
| } |
| |
| /// Return the type of operator function for specified LHS, or a null |
| /// \c Type on error. |
| FunctionType * |
| TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, |
| Identifier opName, DeclRefKind refKind, |
| ConcreteDeclRef &referencedDecl) { |
| |
| // For the infix operator, find the actual LHS from pre-folded LHS. |
| if (refKind == DeclRefKind::BinaryOperator) |
| LHS = TypeChecker::findLHS(DC, LHS, opName); |
| |
| if (!LHS) |
| return nullptr; |
| |
| auto LHSTy = LHS->getType(); |
| |
| // FIXME: 'UnresolvedType' still might be typechecked by an operator. |
| if (!LHSTy || LHSTy->is<UnresolvedType>()) |
| return nullptr; |
| |
| // Meta types and function types cannot be a operand of operator expressions. |
| if (LHSTy->is<MetatypeType>() || LHSTy->is<AnyFunctionType>()) |
| return nullptr; |
| |
| auto Loc = LHS->getEndLoc(); |
| |
| // Build temporary expression to typecheck. |
| // We allocate these expressions on the stack because we know they can't |
| // escape and there isn't a better way to allocate scratch Expr nodes. |
| UnresolvedDeclRefExpr UDRE(DeclNameRef(opName), refKind, DeclNameLoc(Loc)); |
| auto *opExpr = TypeChecker::resolveDeclRefExpr( |
| &UDRE, DC, /*replaceInvalidRefsWithErrors=*/true); |
| |
| switch (refKind) { |
| case DeclRefKind::PostfixOperator: { |
| // (postfix_unary_expr |
| // (declref_expr name=<opName>) |
| // (paren_expr |
| // (<LHS>))) |
| ParenExpr Args(SourceLoc(), LHS, SourceLoc(), |
| /*hasTrailingClosure=*/false); |
| PostfixUnaryExpr postfixExpr(opExpr, &Args); |
| return getTypeOfCompletionOperatorImpl(DC, &postfixExpr, |
| referencedDecl); |
| } |
| |
| case DeclRefKind::BinaryOperator: { |
| // (binary_expr |
| // (declref_expr name=<opName>) |
| // (tuple_expr |
| // (<LHS>) |
| // (code_completion_expr))) |
| CodeCompletionExpr dummyRHS(Loc); |
| auto Args = TupleExpr::create( |
| DC->getASTContext(), SourceLoc(), {LHS, &dummyRHS}, {}, {}, SourceLoc(), |
| /*hasTrailingClosure=*/false, /*isImplicit=*/true); |
| BinaryExpr binaryExpr(opExpr, Args, /*isImplicit=*/true); |
| |
| return getTypeOfCompletionOperatorImpl(DC, &binaryExpr, |
| referencedDecl); |
| } |
| |
| default: |
| llvm_unreachable("Invalid DeclRefKind for operator completion"); |
| } |
| } |
| |
| namespace { |
| |
| class CompletionContextFinder : public ASTWalker { |
| enum class ContextKind { |
| FallbackExpression, |
| StringInterpolation, |
| SingleStmtClosure, |
| MultiStmtClosure, |
| ErrorExpression |
| }; |
| |
| struct Context { |
| ContextKind Kind; |
| Expr * E; |
| }; |
| |
| /// Stack of all "interesting" contexts up to code completion expression. |
| llvm::SmallVector<Context, 4> Contexts; |
| CodeCompletionExpr *CompletionExpr = nullptr; |
| Expr *InitialExpr = nullptr; |
| DeclContext *InitialDC; |
| |
| public: |
| /// Finder for completion contexts within the provided initial expression. |
| CompletionContextFinder(Expr *initialExpr, DeclContext *DC) |
| : InitialExpr(initialExpr), InitialDC(DC) { |
| assert(DC); |
| initialExpr->walk(*this); |
| }; |
| |
| /// Finder for completion contexts within the outermost non-closure context of |
| /// the code completion expression's direct context. |
| CompletionContextFinder(DeclContext *completionDC): InitialDC(completionDC) { |
| while (auto *ACE = dyn_cast<AbstractClosureExpr>(InitialDC)) |
| InitialDC = ACE->getParent(); |
| InitialDC->walkContext(*this); |
| } |
| |
| std::pair<bool, Expr *> walkToExprPre(Expr *E) override { |
| if (auto *closure = dyn_cast<ClosureExpr>(E)) { |
| Contexts.push_back({closure->hasSingleExpressionBody() |
| ? ContextKind::SingleStmtClosure |
| : ContextKind::MultiStmtClosure, |
| closure}); |
| } |
| |
| if (isa<InterpolatedStringLiteralExpr>(E)) { |
| Contexts.push_back({ContextKind::StringInterpolation, E}); |
| } |
| |
| if (isa<ApplyExpr>(E) || isa<SequenceExpr>(E)) { |
| Contexts.push_back({ContextKind::FallbackExpression, E}); |
| } |
| |
| if (auto *Error = dyn_cast<ErrorExpr>(E)) { |
| Contexts.push_back({ContextKind::ErrorExpression, E}); |
| if (auto *OrigExpr = Error->getOriginalExpr()) { |
| OrigExpr->walk(*this); |
| if (hasCompletionExpr()) |
| return std::make_pair(false, nullptr); |
| } |
| } |
| |
| if (auto *CCE = dyn_cast<CodeCompletionExpr>(E)) { |
| CompletionExpr = CCE; |
| return std::make_pair(false, nullptr); |
| } |
| |
| return std::make_pair(true, E); |
| } |
| |
| Expr *walkToExprPost(Expr *E) override { |
| if (isa<ClosureExpr>(E) || isa<InterpolatedStringLiteralExpr>(E) || |
| isa<ApplyExpr>(E) || isa<SequenceExpr>(E) || isa<ErrorExpr>(E)) { |
| assert(Contexts.back().E == E); |
| Contexts.pop_back(); |
| } |
| return E; |
| } |
| |
| /// Check whether code completion expression is located inside of a |
| /// multi-statement closure. |
| bool locatedInMultiStmtClosure() const { |
| return hasContext(ContextKind::MultiStmtClosure); |
| } |
| |
| bool locatedInStringIterpolation() const { |
| return hasContext(ContextKind::StringInterpolation); |
| } |
| |
| bool hasCompletionExpr() const { |
| return CompletionExpr; |
| } |
| |
| CodeCompletionExpr *getCompletionExpr() const { |
| assert(CompletionExpr); |
| return CompletionExpr; |
| } |
| |
| struct Fallback { |
| Expr *E; ///< The fallback expression. |
| DeclContext *DC; ///< The fallback expression's decl context. |
| bool SeparatePrecheck; ///< True if the fallback may require prechecking. |
| }; |
| |
| /// As a fallback sometimes its useful to not only type-check |
| /// code completion expression directly but instead add some |
| /// of the enclosing context e.g. when completion is an argument |
| /// to a call. |
| Optional<Fallback> getFallbackCompletionExpr() const { |
| assert(CompletionExpr); |
| |
| Optional<Fallback> fallback; |
| bool separatePrecheck = false; |
| DeclContext *fallbackDC = InitialDC; |
| |
| // Find the outermost fallback expression within the innermost error |
| // expression or multi-statement closure, keeping track of its decl context. |
| for (auto context: Contexts) { |
| switch (context.Kind) { |
| case ContextKind::StringInterpolation: |
| LLVM_FALLTHROUGH; |
| case ContextKind::FallbackExpression: |
| if (!fallback && context.E != InitialExpr) |
| fallback = Fallback{context.E, fallbackDC, separatePrecheck}; |
| continue; |
| |
| case ContextKind::SingleStmtClosure: |
| if (!fallback && context.E != InitialExpr) |
| fallback = Fallback{context.E, fallbackDC, separatePrecheck}; |
| fallbackDC = cast<AbstractClosureExpr>(context.E); |
| continue; |
| |
| case ContextKind::MultiStmtClosure: |
| fallbackDC = cast<AbstractClosureExpr>(context.E); |
| LLVM_FALLTHROUGH; |
| case ContextKind::ErrorExpression:; |
| fallback = None; |
| separatePrecheck = true; |
| continue; |
| } |
| } |
| |
| if (fallback) |
| return fallback; |
| |
| if (CompletionExpr->getBase() && CompletionExpr != InitialExpr) |
| return Fallback{CompletionExpr, fallbackDC, separatePrecheck}; |
| return None; |
| } |
| |
| private: |
| bool hasContext(ContextKind kind) const { |
| return llvm::find_if(Contexts, [&kind](const Context &currContext) { |
| return currContext.Kind == kind; |
| }) != Contexts.end(); |
| } |
| }; |
| |
| } // end namespace |
| |
| // Determine if the target expression is the implicit BinaryExpr generated for |
| // pattern-matching in a switch/if/guard case (<completion> ~= matchValue). |
| static bool isForPatternMatch(SolutionApplicationTarget &target) { |
| if (target.getExprContextualTypePurpose() != CTP_Condition) |
| return false; |
| Expr *condition = target.getAsExpr(); |
| if (!condition->isImplicit()) |
| return false; |
| if (auto *BE = dyn_cast<BinaryExpr>(condition)) { |
| Identifier id; |
| if (auto *ODRE = dyn_cast<OverloadedDeclRefExpr>(BE->getFn())) { |
| id = ODRE->getDecls().front()->getBaseIdentifier(); |
| } else if (auto *DRE = dyn_cast<DeclRefExpr>(BE->getFn())) { |
| id = DRE->getDecl()->getBaseIdentifier(); |
| } |
| if (id != target.getDeclContext()->getASTContext().Id_MatchOperator) |
| return false; |
| return isa<CodeCompletionExpr>(BE->getArg()->getElement(0)); |
| } |
| return false; |
| } |
| |
| /// Remove any solutions from the provided vector that both require fixes and have a |
| /// score worse than the best. |
| static void filterSolutions(SolutionApplicationTarget &target, |
| SmallVectorImpl<Solution> &solutions, |
| CodeCompletionExpr *completionExpr) { |
| // FIXME: this is only needed because in pattern matching position, the |
| // code completion expression always becomes an expression pattern, which |
| // requires the ~= operator to be defined on the type being matched against. |
| // Pattern matching against an enum doesn't require that however, so valid |
| // solutions always end up having fixes. This is a problem because there will |
| // always be a valid solution as well. Optional defines ~= between Optional |
| // and _OptionalNilComparisonType (which defines a nilLiteral initializer), |
| // and the matched-against value can implicitly be made Optional if it isn't |
| // already, so _OptionalNilComparisonType is always a valid solution for the |
| // completion. That only generates the 'nil' completion, which is rarely what |
| // the user intends to write in this position and shouldn't be preferred over |
| // the other formed solutions (which require fixes). We should generate enum |
| // pattern completions separately, but for now ignore the |
| // _OptionalNilComparisonType solution. |
| if (isForPatternMatch(target)) { |
| solutions.erase(llvm::remove_if(solutions, [&](const Solution &S) { |
| ASTContext &ctx = S.getConstraintSystem().getASTContext(); |
| if (!S.hasType(completionExpr)) |
| return false; |
| if (auto ty = S.getResolvedType(completionExpr)) |
| if (auto *NTD = ty->getAnyNominal()) |
| return NTD->getBaseIdentifier() == ctx.Id_OptionalNilComparisonType; |
| return false; |
| }), solutions.end()); |
| } |
| |
| if (solutions.size() <= 1) |
| return; |
| |
| Score minScore = std::min_element(solutions.begin(), solutions.end(), |
| [](const Solution &a, const Solution &b) { |
| return a.getFixedScore() < b.getFixedScore(); |
| })->getFixedScore(); |
| |
| llvm::erase_if(solutions, [&](const Solution &S) { |
| return S.getFixedScore().Data[SK_Fix] != 0 && |
| S.getFixedScore() > minScore; |
| }); |
| } |
| |
| /// When solving for code completion we still consider solutions with holes as |
| /// valid. Regular type-checking does not. This is intended to return true only |
| /// if regular type-checking would consider this solution viable. |
| static bool isViableForReTypeCheck(const Solution &S) { |
| if (llvm::any_of(S.Fixes, [&](const ConstraintFix *CF) { |
| return !CF->isWarning(); |
| })) |
| return false; |
| using Binding = std::pair<swift::TypeVariableType *, swift::Type>; |
| return llvm::none_of(S.typeBindings, [&](const Binding& binding) { |
| return binding.second->hasHole(); |
| }); |
| } |
| |
| bool TypeChecker::typeCheckForCodeCompletion( |
| SolutionApplicationTarget &target, bool needsPrecheck, |
| llvm::function_ref<void(const Solution &)> callback) { |
| auto *DC = target.getDeclContext(); |
| auto &Context = DC->getASTContext(); |
| |
| auto *expr = target.getAsExpr(); |
| if (!expr) |
| return false; |
| |
| // First of all, let's check whether given target expression |
| // does indeed have the code completion location in it. |
| { |
| auto range = expr->getSourceRange(); |
| if (range.isInvalid() || |
| !Context.SourceMgr.rangeContainsCodeCompletionLoc(range)) |
| return false; |
| } |
| |
| FrontendStatsTracer StatsTracer(Context.Stats, |
| "typecheck-for-code-completion", expr); |
| PrettyStackTraceExpr stackTrace(Context, "code-completion", expr); |
| |
| expr = expr->walk(SanitizeExpr(Context, |
| /*shouldReusePrecheckedType=*/false)); |
| target.setExpr(expr); |
| |
| CompletionContextFinder contextAnalyzer(expr, DC); |
| |
| // If there was no completion expr (e.g. if the code completion location was |
| // among tokens that were skipped over during parser error recovery) bail. |
| if (!contextAnalyzer.hasCompletionExpr()) |
| return false; |
| |
| // Interpolation components are type-checked separately. |
| if (contextAnalyzer.locatedInStringIterpolation()) |
| 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. |
| auto failedPreCheck = ConstraintSystem::preCheckExpression( |
| expr, DC, |
| /*replaceInvalidRefsWithErrors=*/true); |
| |
| target.setExpr(expr); |
| |
| if (failedPreCheck) |
| return false; |
| } |
| |
| enum class CompletionResult { Ok, NotApplicable, Fallback }; |
| |
| auto solveForCodeCompletion = |
| [&](SolutionApplicationTarget &target) -> CompletionResult { |
| ConstraintSystemOptions options; |
| options |= ConstraintSystemFlags::AllowFixes; |
| options |= ConstraintSystemFlags::SuppressDiagnostics; |
| options |= ConstraintSystemFlags::ForCodeCompletion; |
| |
| ConstraintSystem cs(DC, options); |
| |
| llvm::SmallVector<Solution, 4> solutions; |
| |
| // If solve failed to generate constraints or with some other |
| // issue, we need to fallback to type-checking a sub-expression. |
| if (!cs.solveForCodeCompletion(target, solutions)) |
| return CompletionResult::Fallback; |
| |
| // FIXME: instead of filtering, expose the score and viability to clients. |
| // Remove any solutions that both require fixes and have a score that is |
| // worse than the best. |
| filterSolutions(target, solutions, contextAnalyzer.getCompletionExpr()); |
| |
| // Similarly, if the type-check didn't produce any solutions, fall back |
| // to type-checking a sub-expression in isolation. |
| if (solutions.empty()) |
| return CompletionResult::Fallback; |
| |
| // If code completion expression resides inside of multi-statement |
| // closure body it could either be type-checked together with the context |
| // or not, it's impossible to say without checking. |
| if (contextAnalyzer.locatedInMultiStmtClosure()) { |
| auto &solution = solutions.front(); |
| |
| if (solution.hasType(contextAnalyzer.getCompletionExpr())) { |
| llvm::for_each(solutions, callback); |
| return CompletionResult::Ok; |
| } |
| |
| // At this point we know the code completion expression wasn't checked |
| // with the closure's surrounding context. If a single valid solution |
| // was formed we can wait until the body of the closure is type-checked |
| // and gather completions then. |
| if (solutions.size() == 1 && isViableForReTypeCheck(solution)) |
| return CompletionResult::NotApplicable; |
| |
| // Otherwise, it's unlikely the body will ever be type-checked, so fall |
| // back to manually checking a sub-expression within the closure body. |
| return CompletionResult::Fallback; |
| } |
| |
| llvm::for_each(solutions, callback); |
| return CompletionResult::Ok; |
| }; |
| |
| switch (solveForCodeCompletion(target)) { |
| case CompletionResult::Ok: |
| return true; |
| |
| case CompletionResult::NotApplicable: |
| return false; |
| |
| case CompletionResult::Fallback: |
| break; |
| } |
| |
| // Determine the best subexpression to use based on the collected context |
| // of the code completion expression. |
| if (auto fallback = contextAnalyzer.getFallbackCompletionExpr()) { |
| assert(fallback->E != expr); |
| SolutionApplicationTarget completionTarget(fallback->E, |
| fallback->DC, CTP_Unused, |
| /*contextualType=*/Type(), |
| /*isDiscarded=*/true); |
| typeCheckForCodeCompletion(completionTarget, fallback->SeparatePrecheck, |
| callback); |
| } |
| return true; |
| } |
| |
| static Optional<Type> getTypeOfCompletionContextExpr( |
| DeclContext *DC, |
| CompletionTypeCheckKind kind, |
| Expr *&parsedExpr, |
| ConcreteDeclRef &referencedDecl) { |
| if (constraints::ConstraintSystem::preCheckExpression( |
| parsedExpr, DC, /*replaceInvalidRefsWithErrors=*/true)) |
| return None; |
| |
| switch (kind) { |
| case CompletionTypeCheckKind::Normal: |
| // Handle below. |
| break; |
| |
| case CompletionTypeCheckKind::KeyPath: |
| referencedDecl = nullptr; |
| if (auto keyPath = dyn_cast<KeyPathExpr>(parsedExpr)) { |
| auto components = keyPath->getComponents(); |
| if (!components.empty()) { |
| auto &last = components.back(); |
| if (last.isResolved()) { |
| if (last.getKind() == KeyPathExpr::Component::Kind::Property) |
| referencedDecl = last.getDeclRef(); |
| Type lookupTy = last.getComponentType(); |
| ASTContext &Ctx = DC->getASTContext(); |
| if (auto bridgedClass = Ctx.getBridgedToObjC(DC, lookupTy)) |
| return bridgedClass; |
| return lookupTy; |
| } |
| } |
| } |
| |
| return None; |
| } |
| |
| Type originalType = parsedExpr->getType(); |
| if (auto T = getTypeOfExpressionWithoutApplying(parsedExpr, DC, |
| referencedDecl, FreeTypeVariableBinding::UnresolvedType)) |
| return T; |
| |
| // Try to recover if we've made any progress. |
| if (parsedExpr && |
| !isa<ErrorExpr>(parsedExpr) && |
| parsedExpr->getType() && |
| !parsedExpr->getType()->hasError() && |
| (originalType.isNull() || |
| !parsedExpr->getType()->isEqual(originalType))) { |
| return parsedExpr->getType(); |
| } |
| |
| return None; |
| } |
| |
| /// Return the type of an expression parsed during code completion, or |
| /// a null \c Type on error. |
| Optional<Type> swift::getTypeOfCompletionContextExpr( |
| ASTContext &Ctx, |
| DeclContext *DC, |
| CompletionTypeCheckKind kind, |
| Expr *&parsedExpr, |
| ConcreteDeclRef &referencedDecl) { |
| DiagnosticSuppression suppression(Ctx.Diags); |
| |
| // Try to solve for the actual type of the expression. |
| return ::getTypeOfCompletionContextExpr(DC, kind, parsedExpr, |
| referencedDecl); |
| } |
| |
| /// Return the type of operator function for specified LHS, or a null |
| /// \c Type on error. |
| FunctionType * |
| swift::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, |
| Identifier opName, DeclRefKind refKind, |
| ConcreteDeclRef &referencedDecl) { |
| auto &ctx = DC->getASTContext(); |
| DiagnosticSuppression suppression(ctx.Diags); |
| return TypeChecker::getTypeOfCompletionOperator(DC, LHS, opName, refKind, |
| referencedDecl); |
| } |
| |
| bool swift::typeCheckExpression(DeclContext *DC, Expr *&parsedExpr) { |
| auto &ctx = DC->getASTContext(); |
| |
| parsedExpr = parsedExpr->walk(SanitizeExpr(ctx, /*shouldReusePrecheckedType=*/false)); |
| |
| DiagnosticSuppression suppression(ctx.Diags); |
| auto resultTy = TypeChecker::typeCheckExpression(parsedExpr, DC); |
| return !resultTy; |
| } |
| |
| LookupResult |
| swift::lookupSemanticMember(DeclContext *DC, Type ty, DeclName name) { |
| return TypeChecker::lookupMember(DC, ty, DeclNameRef(name), None); |
| } |
| |
| void DotExprTypeCheckCompletionCallback::fallbackTypeCheck() { |
| assert(!gotCallback()); |
| |
| // Default to checking the completion expression in isolation. |
| Expr *fallbackExpr = CompletionExpr; |
| DeclContext *fallbackDC = DC; |
| |
| CompletionContextFinder finder(DC); |
| if (finder.hasCompletionExpr()) { |
| if (auto fallback = finder.getFallbackCompletionExpr()) { |
| fallbackExpr = fallback->E; |
| fallbackDC = fallback->DC; |
| } |
| } |
| |
| SolutionApplicationTarget completionTarget(fallbackExpr, fallbackDC, |
| CTP_Unused, Type(), |
| /*isDiscared=*/true); |
| |
| TypeChecker::typeCheckForCodeCompletion( |
| completionTarget, /*needsPrecheck*/true, |
| [&](const Solution &S) { sawSolution(S); }); |
| } |
| |
| void UnresolvedMemberTypeCheckCompletionCallback:: |
| fallbackTypeCheck(DeclContext *DC) { |
| assert(!gotCallback()); |
| |
| CompletionContextFinder finder(DC); |
| if (!finder.hasCompletionExpr()) |
| return; |
| |
| auto fallback = finder.getFallbackCompletionExpr(); |
| if (!fallback) |
| return; |
| |
| |
| SolutionApplicationTarget completionTarget(fallback->E, fallback->DC, |
| CTP_Unused, Type(), |
| /*isDiscared=*/true); |
| TypeChecker::typeCheckForCodeCompletion( |
| completionTarget, /*needsPrecheck*/true, |
| [&](const Solution &S) { sawSolution(S); }); |
| } |
| |
| static Type getTypeForCompletion(const constraints::Solution &S, Expr *E) { |
| auto &CS = S.getConstraintSystem(); |
| |
| // To aid code completion, we need to attempt to convert type holes |
| // back into underlying generic parameters if possible, since type |
| // of the code completion expression is used as "expected" (or contextual) |
| // type so it's helpful to know what requirements it has to filter |
| // the list of possible member candidates e.g. |
| // |
| // \code |
| // func test<T: P>(_: [T]) {} |
| // |
| // test(42.#^MEMBERS^#) |
| // \code |
| // |
| // It's impossible to resolve `T` in this case but code completion |
| // expression should still have a type of `[T]` instead of `[<<hole>>]` |
| // because it helps to produce correct contextual member list based on |
| // a conformance requirement associated with generic parameter `T`. |
| if (isa<CodeCompletionExpr>(E)) { |
| auto completionTy = S.getType(E).transform([&](Type type) -> Type { |
| if (auto *typeVar = type->getAs<TypeVariableType>()) |
| return S.getFixedType(typeVar); |
| return type; |
| }); |
| |
| return S.simplifyType(completionTy.transform([&](Type type) { |
| if (auto *hole = type->getAs<HoleType>()) { |
| if (auto *typeVar = |
| hole->getOriginator().dyn_cast<TypeVariableType *>()) { |
| if (auto *GP = typeVar->getImpl().getGenericParameter()) { |
| // Code completion depends on generic parameter type being |
| // represented in terms of `ArchetypeType` since it's easy |
| // to extract protocol requirements from it. |
| if (auto *GPD = GP->getDecl()) |
| return GPD->getInnermostDeclContext()->mapTypeIntoContext(GP); |
| } |
| } |
| |
| return Type(CS.getASTContext().TheUnresolvedType); |
| } |
| |
| return type; |
| })); |
| } |
| |
| return S.getResolvedType(E); |
| }; |
| |
| /// Whether the given completion expression is the only expression in its |
| /// containing closure or function body and its value is implicitly returned. |
| /// |
| /// If these conditions are met, code completion needs to avoid penalizing |
| /// completion results that don't match the expected return type when computing |
| /// type relations, as since no return statement was explicitly written by the |
| /// user, it's possible they intend the single expression not as the return |
| /// value but merely the first entry in a multi-statement body they just haven't |
| /// finished writing yet. |
| static bool isImplicitSingleExpressionReturn(ConstraintSystem &CS, |
| Expr *CompletionExpr) { |
| Expr *ParentExpr = CS.getParentExpr(CompletionExpr); |
| if (!ParentExpr) |
| return CS.getContextualTypePurpose(CompletionExpr) == CTP_ReturnSingleExpr; |
| |
| if (auto *ParentCE = dyn_cast<ClosureExpr>(ParentExpr)) { |
| if (ParentCE->hasSingleExpressionBody() && |
| ParentCE->getSingleExpressionBody() == CompletionExpr) { |
| ASTNode Last = ParentCE->getBody()->getLastElement(); |
| return !Last.isStmt(StmtKind::Return) || Last.isImplicit(); |
| } |
| } |
| return false; |
| } |
| |
| void DotExprTypeCheckCompletionCallback:: |
| sawSolution(const constraints::Solution &S) { |
| GotCallback = true; |
| auto &CS = S.getConstraintSystem(); |
| auto *ParsedExpr = CompletionExpr->getBase(); |
| auto *SemanticExpr = ParsedExpr->getSemanticsProvidingExpr(); |
| |
| 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 |
| // it wouldn't produce any useful results anyway. |
| if (!BaseTy || BaseTy->getRValueType()->is<UnresolvedType>()) |
| return; |
| |
| auto *Locator = CS.getConstraintLocator(SemanticExpr); |
| Type ExpectedTy = getTypeForCompletion(S, CompletionExpr); |
| Expr *ParentExpr = CS.getParentExpr(CompletionExpr); |
| if (!ParentExpr) |
| ExpectedTy = CS.getContextualType(CompletionExpr); |
| |
| auto *CalleeLocator = S.getCalleeLocator(Locator); |
| ValueDecl *ReferencedDecl = nullptr; |
| if (auto SelectedOverload = S.getOverloadChoiceIfAvailable(CalleeLocator)) |
| ReferencedDecl = SelectedOverload->choice.getDeclOrNull(); |
| |
| 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 = ExpectedTy |
| ? !ExpectedTy->isVoid() |
| : !ParentExpr && CS.getContextualTypePurpose( |
| CompletionExpr) != CTP_Unused; |
| |
| Results.push_back( |
| {BaseTy, ReferencedDecl, {}, DisallowVoid, ISDMT, ImplicitReturn}); |
| if (ExpectedTy) |
| Results.back().ExpectedTypes.push_back(ExpectedTy); |
| } else if (ExpectedTy) { |
| auto &ExpectedTys = Results[Ret.first->getSecond()].ExpectedTypes; |
| auto IsEqual = [&](Type Ty) { return ExpectedTy->isEqual(Ty); }; |
| if (!llvm::any_of(ExpectedTys, IsEqual)) |
| ExpectedTys.push_back(ExpectedTy); |
| } |
| } |
| |
| void UnresolvedMemberTypeCheckCompletionCallback:: |
| sawSolution(const constraints::Solution &S) { |
| GotCallback = true; |
| |
| auto &CS = S.getConstraintSystem(); |
| Type ExpectedTy = getTypeForCompletion(S, CompletionExpr); |
| // If the type couldn't be determined (e.g. because there isn't any context |
| // to derive it from), let's not attempt to do a lookup since it wouldn't |
| // produce any useful results anyway. |
| if (!ExpectedTy || ExpectedTy->is<UnresolvedType>()) |
| return; |
| |
| // If ExpectedTy is a duplicate of any other result, ignore this solution. |
| if (llvm::any_of(Results, [&](const Result &R) { |
| return R.ExpectedTy->isEqual(ExpectedTy); |
| })) { |
| return; |
| } |
| |
| bool SingleExprBody = isImplicitSingleExpressionReturn(CS, CompletionExpr); |
| Results.push_back({ExpectedTy, SingleExprBody}); |
| } |