Skip to content

Commit 860d68e

Browse files
authored
Merge pull request #79217 from hamishknight/hole-in-one
[CS] Avoid penalizing holes for placeholder vars for completion
2 parents c4af3b3 + 5af9282 commit 860d68e

File tree

2 files changed

+90
-27
lines changed

2 files changed

+90
-27
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2879,6 +2879,55 @@ TypeVariableBinding::fixForHole(ConstraintSystem &cs) const {
28792879
return std::nullopt;
28802880
}
28812881

2882+
static bool shouldIgnoreHoleForCodeCompletion(ConstraintSystem &cs,
2883+
TypeVariableType *typeVar,
2884+
ConstraintLocator *srcLocator) {
2885+
if (!cs.isForCodeCompletion())
2886+
return false;
2887+
2888+
// Don't penalize solutions with unresolved generics.
2889+
if (typeVar->getImpl().getGenericParameter())
2890+
return true;
2891+
2892+
// Don't penalize solutions if we couldn't determine the type of the code
2893+
// completion token. We still want to examine the surrounding types in
2894+
// that case.
2895+
if (typeVar->getImpl().isCodeCompletionToken())
2896+
return true;
2897+
2898+
// When doing completion in a result builder, we avoid solving unrelated
2899+
// expressions by replacing them with unbound placeholder variables.
2900+
// As such, we need to avoid penalizing holes for references to
2901+
// placeholder variables.
2902+
if (srcLocator->isLastElement<LocatorPathElt::PlaceholderType>()) {
2903+
if (auto *DRE = getAsExpr<DeclRefExpr>(srcLocator->getAnchor())) {
2904+
if (auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl())) {
2905+
if (auto *PBD = VD->getParentPatternBinding()) {
2906+
if (isPlaceholderVar(PBD))
2907+
return true;
2908+
}
2909+
}
2910+
}
2911+
}
2912+
2913+
// Don't penalize solutions with holes due to missing arguments after the
2914+
// code completion position.
2915+
auto argLoc = srcLocator->findLast<LocatorPathElt::SynthesizedArgument>();
2916+
if (argLoc && argLoc->isAfterCodeCompletionLoc())
2917+
return true;
2918+
2919+
// Don't penalize solutions that have holes for ignored arguments.
2920+
if (cs.hasArgumentsIgnoredForCodeCompletion()) {
2921+
// Avoid simplifying the locator if the constraint system didn't ignore
2922+
// any arguments.
2923+
auto argExpr = simplifyLocatorToAnchor(typeVar->getImpl().getLocator());
2924+
if (cs.isArgumentIgnoredForCodeCompletion(argExpr.dyn_cast<Expr *>())) {
2925+
return true;
2926+
}
2927+
}
2928+
return false;
2929+
}
2930+
28822931
bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
28832932
auto type = Binding.BindingType;
28842933
auto *srcLocator = Binding.getLocator();
@@ -2934,33 +2983,9 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
29342983
}
29352984

29362985
auto reportHole = [&]() {
2937-
if (cs.isForCodeCompletion()) {
2938-
// Don't penalize solutions with unresolved generics.
2939-
if (TypeVar->getImpl().getGenericParameter())
2940-
return false;
2941-
2942-
// Don't penalize solutions if we couldn't determine the type of the code
2943-
// completion token. We still want to examine the surrounding types in
2944-
// that case.
2945-
if (TypeVar->getImpl().isCodeCompletionToken())
2946-
return false;
2947-
2948-
// Don't penalize solutions with holes due to missing arguments after the
2949-
// code completion position.
2950-
auto argLoc = srcLocator->findLast<LocatorPathElt::SynthesizedArgument>();
2951-
if (argLoc && argLoc->isAfterCodeCompletionLoc())
2952-
return false;
2953-
2954-
// Don't penalize solutions that have holes for ignored arguments.
2955-
if (cs.hasArgumentsIgnoredForCodeCompletion()) {
2956-
// Avoid simplifying the locator if the constraint system didn't ignore
2957-
// any arguments.
2958-
auto argExpr = simplifyLocatorToAnchor(TypeVar->getImpl().getLocator());
2959-
if (cs.isArgumentIgnoredForCodeCompletion(argExpr.dyn_cast<Expr *>())) {
2960-
return false;
2961-
}
2962-
}
2963-
}
2986+
if (shouldIgnoreHoleForCodeCompletion(cs, TypeVar, srcLocator))
2987+
return false;
2988+
29642989
// Reflect in the score that this type variable couldn't be
29652990
// resolved and had to be bound to a placeholder "hole" type.
29662991
cs.increaseScore(SK_Hole, srcLocator);

test/IDE/issue-79213.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %batch-code-completion
2+
3+
protocol View {}
4+
5+
struct EmptyView: View {}
6+
struct Either<T: View, U: View>: View {}
7+
struct Tuple<T>: View {}
8+
extension Optional: View where Wrapped: View {}
9+
10+
@resultBuilder
11+
struct ViewBuilder {
12+
static func buildBlock() -> EmptyView { .init() }
13+
static func buildBlock<T: View>(_ x: T) -> T { x }
14+
@_disfavoredOverload static func buildBlock<each T: View>(
15+
_ x: repeat each T
16+
) -> Tuple<(repeat each T)> { .init() }
17+
static func buildEither<T, U>(first component: T) -> Either<T, U> { .init() }
18+
static func buildEither<T, U>(second component: U) -> Either<T, U> { .init() }
19+
static func buildIf<T: View>(_ x: T?) -> T? { x }
20+
}
21+
22+
struct R {
23+
var bar: Bool
24+
}
25+
26+
func baz<R>(@ViewBuilder _ fn: () -> R) {}
27+
28+
// https://github.com/swiftlang/swift/issues/79213 - Make sure we get a result here.
29+
func foo(_ x: R, _ b: Bool) {
30+
baz {
31+
if b {
32+
EmptyView()
33+
} else if b {
34+
if x.#^COMPLETE^# {}
35+
// COMPLETE: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: bar[#Bool#]; name=bar
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)