Skip to content

[4.2][CodeComplete] Strip out try and optional eval expr in operator completion #18245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions lib/IDE/CodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3468,10 +3468,13 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
prepareForRetypechecking(SE);

for (auto &element : sequence.drop_back(2)) {
// Unfold AssignExpr for re-typechecking sequence.
if (auto *AE = dyn_cast_or_null<AssignExpr>(element)) {
AE->setSrc(nullptr);
AE->setDest(nullptr);
// Unfold expressions for re-typechecking sequence.
if (auto *assignExpr = dyn_cast_or_null<AssignExpr>(element)) {
assignExpr->setSrc(nullptr);
assignExpr->setDest(nullptr);
} else if (auto *ifExpr = dyn_cast_or_null<IfExpr>(element)) {
ifExpr->setCondExpr(nullptr);
ifExpr->setElseExpr(nullptr);
}

// Reset any references to operators in types, so they are properly
Expand Down Expand Up @@ -3522,24 +3525,44 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
flattenBinaryExpr(assignExpr->getSrc(), sequence);
assignExpr->setDest(nullptr);
assignExpr->setSrc(nullptr);
} else if (auto ifExpr = dyn_cast<IfExpr>(expr)) {
flattenBinaryExpr(ifExpr->getCondExpr(), sequence);
sequence.push_back(ifExpr);
flattenBinaryExpr(ifExpr->getElseExpr(), sequence);
ifExpr->setCondExpr(nullptr);
ifExpr->setElseExpr(nullptr);
} else if (auto tryExpr = dyn_cast<AnyTryExpr>(expr)) {
// Strip out try expression. It doesn't affect completion.
flattenBinaryExpr(tryExpr->getSubExpr(), sequence);
} else if (auto optEval = dyn_cast<OptionalEvaluationExpr>(expr)){
// Strip out optional evaluation expression. It doesn't affect completion.
flattenBinaryExpr(optEval->getSubExpr(), sequence);
} else {
sequence.push_back(expr);
}
}

void typeCheckLeadingSequence(SmallVectorImpl<Expr *> &sequence) {

// Strip out try and optional evaluation expr because foldSequence() mutates
// hierarchy of these expressions. They don't affect completion anyway.
for (auto &element : sequence) {
if (auto *tryExpr = dyn_cast<AnyTryExpr>(element))
element = tryExpr->getSubExpr();
if (auto *optEval = dyn_cast<OptionalEvaluationExpr>(element))
element = optEval->getSubExpr();
}

Expr *expr =
SequenceExpr::create(CurrDeclContext->getASTContext(), sequence);
prepareForRetypechecking(expr);
// Take advantage of the fact the type-checker leaves the types on the AST.
if (!typeCheckExpression(const_cast<DeclContext *>(CurrDeclContext),
expr)) {
if (isa<BinaryExpr>(expr) || isa<AssignExpr>(expr)) {
// Rebuild the sequence from the type-checked version.
sequence.clear();
flattenBinaryExpr(expr, sequence);
return;
}
// Rebuild the sequence from the type-checked version.
sequence.clear();
flattenBinaryExpr(expr, sequence);
return;
}

// Fall back to just using the immediate LHS.
Expand Down
19 changes: 14 additions & 5 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2106,11 +2106,20 @@ bool TypeChecker::typeCheckCompletionSequence(Expr *&expr, DeclContext *DC) {
expr = foldSequence(SE, DC);

// Find the code-completion expression and operator again.
BinaryExpr *exprAsBinOp = nullptr;
while (auto *binExpr = dyn_cast<BinaryExpr>(expr)) {
auto *RHS = binExpr->getArg()->getElement(1);
Expr *exprAsBinOp = nullptr;
while (true) {
Expr *RHS;
if (auto *binExpr = dyn_cast<BinaryExpr>(expr))
RHS = binExpr->getArg()->getElement(1);
else if (auto *assignExpr = dyn_cast<AssignExpr>(expr))
RHS = assignExpr->getSrc();
else if (auto *ifExpr = dyn_cast<IfExpr>(expr))
RHS = ifExpr->getElseExpr();
else
break;

if (RHS == CCE) {
exprAsBinOp = binExpr;
exprAsBinOp = expr;
break;
}
expr = RHS;
Expand All @@ -2119,7 +2128,7 @@ bool TypeChecker::typeCheckCompletionSequence(Expr *&expr, DeclContext *DC) {
return true;

// Ensure the output expression is up to date.
assert(exprAsBinOp == expr && "found wrong expr?");
assert(exprAsBinOp == expr && isa<BinaryExpr>(expr) && "found wrong expr?");

// Add type variable for the code-completion expression.
auto tvRHS =
Expand Down
27 changes: 24 additions & 3 deletions test/IDE/complete_crashes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,10 @@ protocol Bar_38149042 {
func foo_38149042(bar: Bar_38149042) {
_ = bar.foo? #^RDAR_38149042^# .x
}

// RDAR_38149042: Begin completions
// RDAR_38149042-DAG: Decl[InstanceVar]/CurrNominal: .x[#Int#]; name=x
// RDAR_38149042-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: [' ']=== {#AnyObject?#}[#Bool#]; name==== AnyObject?
// RDAR_38149042-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: [' ']!== {#AnyObject?#}[#Bool#]; name=!== AnyObject?
// RDAR_38149042: End completions

// rdar://problem/38272904
Expand Down Expand Up @@ -274,18 +275,22 @@ func foo(x: RDAR41159258_MyResult1) {


// rdar://problem/41232519
// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=RDAR41232519 | %FileCheck %s -check-prefix=RDAR_41232519
// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=RDAR41232519_1 | %FileCheck %s -check-prefix=RDAR_41232519
// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=RDAR41232519_2 | %FileCheck %s -check-prefix=RDAR_41232519
public protocol IntProvider {
func nextInt() -> Int
}

public final class IntStore {
public var myInt: Int = 0
func readNextInt(from provider: IntProvider) {
myInt = provider.nextInt() #^RDAR41232519^#
myInt = provider.nextInt() #^RDAR41232519_1^#
_ = true ? 1 : provider.nextInt() #^RDAR41232519_2^#
}
}
// RDAR_41232519: Begin completions
// RDAR_41232519: Decl[InfixOperatorFunction]/OtherModule[Swift]: [' ']+ {#Int#}[#Int#]; name=+ Int
// RDAR_41232519: End completions

// rdar://problem/28188259
// RUN: %target-swift-ide-test -code-completion -code-completion-token=RDAR_28188259 -source-filename=%s | %FileCheck %s -check-prefix=RDAR_28188259
Expand All @@ -312,3 +317,19 @@ func test_40956846(
// RDAR_40956846-DAG: Decl[LocalVar]/Local: arg_40956846_3[#(() -> Int)!#]; name=arg_40956846_3
// RDAR_40956846-DAG: Decl[LocalVar]/Local: arg_40956846_4[#inout ((Int) -> Int)!#]; name=arg_40956846_4
// RDAR_40956846: End completions

// rdar://problem/42452085
// RUN: %target-swift-ide-test -code-completion -code-completion-token=RDAR_42452085_1 -source-filename=%s | %FileCheck %s -check-prefix=RDAR_42452085
// RUN: %target-swift-ide-test -code-completion -code-completion-token=RDAR_42452085_2 -source-filename=%s | %FileCheck %s -check-prefix=RDAR_42452085
// RUN: %target-swift-ide-test -code-completion -code-completion-token=RDAR_42452085_3 -source-filename=%s | %FileCheck %s -check-prefix=RDAR_42452085
class cls_42452085 {
var value: Any
func canThrow() throws -> Int { return 1 }
}
func test_42452085(any: Any, obj: cls_42452085?) throws {
var object: Any? = nil
object = (any as? String) #^RDAR_42452085_1^#
obj?.value = any #^RDAR_42452085_2^#
_ = try obj?.canThrow() #^RDAR_42452085_3^#
}
// RDAR_42452085: found code completion token