Skip to content

Commit f2017b8

Browse files
committed
[IDE] Don't fallback type check if the completion expression is inside a closure
1 parent 1bacfe9 commit f2017b8

File tree

5 files changed

+108
-12
lines changed

5 files changed

+108
-12
lines changed

lib/IDE/PostfixCompletion.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ void PostfixCompletionCallback::fallbackTypeCheck(DeclContext *DC) {
9595
}
9696
}
9797

98+
if (isa<AbstractClosureExpr>(fallbackDC)) {
99+
// If the expression is embedded in a closure, the constraint system tries
100+
// to retrieve that closure's type, which will fail since we won't have
101+
// generated any type variables for it. Thus, fallback type checking isn't
102+
// available in this case.
103+
return;
104+
}
105+
98106
SyntacticElementTarget completionTarget(fallbackExpr, fallbackDC, CTP_Unused,
99107
Type(),
100108
/*isDiscared=*/true);

lib/IDE/TypeCheckCompletionCallback.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,13 @@ void TypeCheckCompletionCallback::fallbackTypeCheck(DeclContext *DC) {
2828
return;
2929

3030
auto fallback = finder.getFallbackCompletionExpr();
31-
if (!fallback)
31+
if (!fallback || isa<AbstractClosureExpr>(fallback->DC)) {
32+
// If the expression is embedded in a closure, the constraint system tries
33+
// to retrieve that closure's type, which will fail since we won't have
34+
// generated any type variables for it. Thus, fallback type checking isn't
35+
// available in this case.
3236
return;
37+
}
3338

3439
SyntacticElementTarget completionTarget(fallback->E, fallback->DC, CTP_Unused,
3540
Type(),

lib/Sema/TypeCheckCodeCompletion.cpp

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,13 @@ static Type
301301
getTypeOfExpressionWithoutApplying(Expr *&expr, DeclContext *dc,
302302
ConcreteDeclRef &referencedDecl,
303303
FreeTypeVariableBinding allowFreeTypeVariables) {
304+
if (isa<AbstractClosureExpr>(dc)) {
305+
// If the expression is embedded in a closure, the constraint system tries
306+
// to retrieve that closure's type, which will fail since we won't have
307+
// generated any type variables for it. Thus, fallback type checking isn't
308+
// available in this case.
309+
return Type();
310+
}
304311
auto &Context = dc->getASTContext();
305312

306313
expr = expr->walk(SanitizeExpr(Context));
@@ -647,18 +654,27 @@ bool TypeChecker::typeCheckForCodeCompletion(
647654

648655
// Determine the best subexpression to use based on the collected context
649656
// of the code completion expression.
650-
if (auto fallback = contextAnalyzer.getFallbackCompletionExpr()) {
651-
if (auto *expr = target.getAsExpr()) {
652-
assert(fallback->E != expr);
653-
(void)expr;
654-
}
655-
SyntacticElementTarget completionTarget(fallback->E, fallback->DC,
656-
CTP_Unused,
657-
/*contextualType=*/Type(),
658-
/*isDiscarded=*/true);
659-
typeCheckForCodeCompletion(completionTarget, fallback->SeparatePrecheck,
660-
callback);
657+
auto fallback = contextAnalyzer.getFallbackCompletionExpr();
658+
if (!fallback) {
659+
return true;
660+
}
661+
if (isa<AbstractClosureExpr>(fallback->DC)) {
662+
// If the expression is embedded in a closure, the constraint system tries
663+
// to retrieve that closure's type, which will fail since we won't have
664+
// generated any type variables for it. Thus, fallback type checking isn't
665+
// available in this case.
666+
return true;
667+
}
668+
if (auto *expr = target.getAsExpr()) {
669+
assert(fallback->E != expr);
670+
(void)expr;
661671
}
672+
SyntacticElementTarget completionTarget(fallback->E, fallback->DC,
673+
CTP_Unused,
674+
/*contextualType=*/Type(),
675+
/*isDiscarded=*/true);
676+
typeCheckForCodeCompletion(completionTarget, fallback->SeparatePrecheck,
677+
callback);
662678
return true;
663679
}
664680

test/IDE/complete_in_closures.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,3 +498,56 @@ func testBinaryOperatorWithEnum() {
498498
// BINARY_OPERATOR_WITH_ENUM: End completions
499499

500500
}
501+
502+
func testPreviousSyntacticElementHasError() {
503+
struct MyStruct {}
504+
505+
class MyClass {
506+
var myMember: Int = 1
507+
var myString: String = "1"
508+
}
509+
510+
@resultBuilder struct ViewBuilder {
511+
static func buildBlock<Content>(_ content: Content) -> Content { content }
512+
}
513+
514+
func buildView<Content>(@ViewBuilder content: () -> Content) -> Int { 0 }
515+
516+
func takeClosure(_ action: () -> Void) {}
517+
518+
func test(x: MyClass) {
519+
// Not that the previous syntactic element (let a) has an error because we
520+
// skip MyStruct inside the result builder for performance reasons.
521+
takeClosure {
522+
let a = buildView {
523+
MyStruct()
524+
}
525+
x.#^PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR^#myMember = 1234
526+
}
527+
}
528+
// PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR: Begin completions
529+
// PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR-DAG: Keyword[self]/CurrNominal: self[#MyClass#]; name=self
530+
// PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: myMember[#Int#]; name=myMember
531+
// PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR-DAG: Decl[InstanceVar]/CurrNominal: myString[#String#]; name=myString
532+
// PREVIOUS_SYNTACTIC_ELEMENT_HAS_ERROR: End completions
533+
}
534+
535+
func testCompleteAfterClosureWithIfExprThatContainErrors() {
536+
_ = {
537+
if true {
538+
invalid(1)
539+
} else if true {
540+
invalid(2)
541+
} else {
542+
invalid(3)
543+
}
544+
}#^AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS^#
545+
546+
// FIXME: We shouldn't be suggesting 'self' and '()' twice here
547+
// AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS: Begin completions, 4 items
548+
// AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS-DAG: Keyword[self]/CurrNominal: .self[#() -> _#]; name=self
549+
// AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS-DAG: Keyword[self]/CurrNominal: .self[#() -> ()#]; name=self
550+
// AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS-DAG: Pattern/CurrModule/Flair[ArgLabels]: ()[#_#]; name=()
551+
// AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS-DAG: Pattern/CurrModule/Flair[ArgLabels]: ()[#Void#]; name=()
552+
// AFTER_CLOSURE_WITH_IF_EXPR_THAT_CONTAIN_ERRORS: End completions
553+
}

test/IDE/complete_multiple_trailingclosure_signatures.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,17 @@ func test() {
2424
// GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn7: (inout Int) -> Void {<#inout Int#> in|}#}[#(inout Int) -> Void#];
2525
// GLOBALFUNC_SAMELINE-DAG: Pattern/Local/Flair[ArgLabels]: {#fn8: (Int...) -> Void {<#Int...#> in|}#}[#(Int...) -> Void#];
2626
}
27+
28+
func testStringAndMulipleTrailingClosures() {
29+
func stringAndClosure(_ key: String, _ body: () -> Void) {}
30+
31+
func takeClosure(_ x: () -> Void) {}
32+
33+
takeClosure {
34+
stringAndClosure("\(1)") { }#^STRING_AND_MULTIPLE_TRAILING_CLOSURES^#
35+
}
36+
// STRING_AND_MULTIPLE_TRAILING_CLOSURES: Begin completions
37+
// STRING_AND_MULTIPLE_TRAILING_CLOSURES-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]/IsSystem: == {#()#}[#Bool#];
38+
// STRING_AND_MULTIPLE_TRAILING_CLOSURES-DAG: Keyword[self]/CurrNominal: .self[#Void#]; name=self
39+
// STRING_AND_MULTIPLE_TRAILING_CLOSURES: End completions
40+
}

0 commit comments

Comments
 (0)