Skip to content

Commit ce2c771

Browse files
authored
Merge pull request #41846 from ahoppen/pr/saw-solution-in-result-builder
[CodeCompletion] Call sawSolution when type checking a result builder
2 parents 43189db + 135c266 commit ce2c771

File tree

4 files changed

+56
-33
lines changed

4 files changed

+56
-33
lines changed

include/swift/Sema/CompletionContextFinder.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef SWIFT_SEMA_COMPLETIONCONTEXTFINDER_H
1414
#define SWIFT_SEMA_COMPLETIONCONTEXTFINDER_H
1515

16+
#include "swift/AST/ASTNode.h"
1617
#include "swift/AST/ASTWalker.h"
1718
#include "swift/AST/Expr.h"
1819
#include "swift/Sema/CodeCompletionTypeChecking.h"
@@ -49,10 +50,10 @@ class CompletionContextFinder : public ASTWalker {
4950

5051
public:
5152
/// Finder for completion contexts within the provided initial expression.
52-
CompletionContextFinder(Expr *initialExpr, DeclContext *DC)
53-
: InitialExpr(initialExpr), InitialDC(DC) {
53+
CompletionContextFinder(ASTNode initialNode, DeclContext *DC)
54+
: InitialExpr(initialNode.dyn_cast<Expr *>()), InitialDC(DC) {
5455
assert(DC);
55-
initialExpr->walk(*this);
56+
initialNode.walk(*this);
5657
};
5758

5859
/// Finder for completion contexts within the outermost non-closure context of

lib/Sema/BuilderTransform.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1736,7 +1736,17 @@ Optional<BraceStmt *> TypeChecker::applyResultBuilderBodyTransform(
17361736

17371737
// Solve the constraint system.
17381738
SmallVector<Solution, 4> solutions;
1739-
if (cs.solve(solutions) || solutions.size() != 1) {
1739+
bool solvingFailed = cs.solve(solutions);
1740+
1741+
if (cs.getASTContext().CompletionCallback) {
1742+
CompletionContextFinder analyzer(func, func->getDeclContext());
1743+
filterSolutionsForCodeCompletion(solutions, analyzer);
1744+
for (const auto &solution : solutions) {
1745+
cs.getASTContext().CompletionCallback->sawSolution(solution);
1746+
}
1747+
}
1748+
1749+
if (solvingFailed || solutions.size() != 1) {
17401750
// Try to fix the system or provide a decent diagnostic.
17411751
auto salvagedResult = cs.salvage();
17421752
switch (salvagedResult.getKind()) {

lib/Sema/TypeCheckCodeCompletion.cpp

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -513,11 +513,32 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS,
513513
}
514514
}
515515

516-
/// Remove any solutions from the provided vector that both require fixes and
517-
/// have a score worse than the best.
518-
static void filterSolutions(SolutionApplicationTarget &target,
519-
SmallVectorImpl<Solution> &solutions,
520-
CodeCompletionExpr *completionExpr) {
516+
static bool hasTypeForCompletion(Solution &solution,
517+
CompletionContextFinder &contextAnalyzer) {
518+
if (contextAnalyzer.hasCompletionExpr()) {
519+
return solution.hasType(contextAnalyzer.getCompletionExpr());
520+
} else {
521+
assert(contextAnalyzer.hasCompletionKeyPathComponent());
522+
return solution.hasType(
523+
contextAnalyzer.getKeyPathContainingCompletionComponent(),
524+
contextAnalyzer.getKeyPathCompletionComponentIndex());
525+
}
526+
}
527+
528+
void TypeChecker::filterSolutionsForCodeCompletion(
529+
SmallVectorImpl<Solution> &solutions,
530+
CompletionContextFinder &contextAnalyzer) {
531+
// Ignore solutions that didn't end up involving the completion (e.g. due to
532+
// a fix to skip over/ignore it).
533+
llvm::erase_if(solutions, [&](Solution &S) {
534+
if (hasTypeForCompletion(S, contextAnalyzer))
535+
return false;
536+
// FIXME: Technically this should never happen, but it currently does in
537+
// result builder contexts. Re-evaluate if we can assert here when we have
538+
// multi-statement closure checking for result builders.
539+
return true;
540+
});
541+
521542
if (solutions.size() <= 1)
522543
return;
523544

@@ -608,15 +629,6 @@ bool TypeChecker::typeCheckForCodeCompletion(
608629
if (!cs.solveForCodeCompletion(target, solutions))
609630
return CompletionResult::Fallback;
610631

611-
// FIXME: instead of filtering, expose the score and viability to clients.
612-
// Remove any solutions that both require fixes and have a score that is
613-
// worse than the best.
614-
CodeCompletionExpr *completionExpr = nullptr;
615-
if (contextAnalyzer.hasCompletionExpr()) {
616-
completionExpr = contextAnalyzer.getCompletionExpr();
617-
}
618-
filterSolutions(target, solutions, completionExpr);
619-
620632
// Similarly, if the type-check didn't produce any solutions, fall back
621633
// to type-checking a sub-expression in isolation.
622634
if (solutions.empty())
@@ -626,19 +638,7 @@ bool TypeChecker::typeCheckForCodeCompletion(
626638
// closure body it could either be type-checked together with the context
627639
// or not, it's impossible to say without checking.
628640
if (contextAnalyzer.locatedInMultiStmtClosure()) {
629-
auto &solution = solutions.front();
630-
631-
bool HasTypeForCompletionNode = false;
632-
if (completionExpr) {
633-
HasTypeForCompletionNode = solution.hasType(completionExpr);
634-
} else {
635-
assert(contextAnalyzer.hasCompletionKeyPathComponent());
636-
HasTypeForCompletionNode = solution.hasType(
637-
contextAnalyzer.getKeyPathContainingCompletionComponent(),
638-
contextAnalyzer.getKeyPathCompletionComponentIndex());
639-
}
640-
641-
if (!HasTypeForCompletionNode) {
641+
if (!hasTypeForCompletion(solutions.front(), contextAnalyzer)) {
642642
// At this point we know the code completion node wasn't checked with
643643
// the closure's surrounding context, so can defer to regular
644644
// type-checking for the current call to typeCheckExpression. If that
@@ -651,6 +651,11 @@ bool TypeChecker::typeCheckForCodeCompletion(
651651
}
652652
}
653653

654+
// FIXME: instead of filtering, expose the score and viability to clients.
655+
// Remove solutions that skipped over/ignored the code completion point
656+
// or that require fixes and have a score that is worse than the best.
657+
filterSolutionsForCodeCompletion(solutions, contextAnalyzer);
658+
654659
llvm::for_each(solutions, callback);
655660
return CompletionResult::Ok;
656661
};

lib/Sema/TypeChecker.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@
2929
#include "swift/AST/NameLookup.h"
3030
#include "swift/AST/PropertyWrappers.h"
3131
#include "swift/AST/TypeRefinementContext.h"
32-
#include "swift/Parse/Lexer.h"
3332
#include "swift/Basic/OptionSet.h"
34-
#include "swift/Sema/ConstraintSystem.h"
3533
#include "swift/Config.h"
34+
#include "swift/Parse/Lexer.h"
35+
#include "swift/Sema/CompletionContextFinder.h"
36+
#include "swift/Sema/ConstraintSystem.h"
3637
#include "llvm/ADT/SetVector.h"
3738
#include "llvm/ADT/TinyPtrVector.h"
3839
#include <functional>
@@ -571,6 +572,12 @@ FunctionType *getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS,
571572
DeclRefKind refKind,
572573
ConcreteDeclRef &refdDecl);
573574

575+
/// Remove any solutions from the provided vector that require more fixes than
576+
/// the best score or don't contain a type for the code completion token.
577+
void filterSolutionsForCodeCompletion(
578+
SmallVectorImpl<constraints::Solution> &solutions,
579+
CompletionContextFinder &contextAnalyzer);
580+
574581
/// Type check the given expression and provide results back to code completion
575582
/// via specified callback.
576583
///

0 commit comments

Comments
 (0)