Skip to content

[4.2][CodeComplete] Fix several crashes in getOperatorCompletions #17573

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 3 commits into from
Jun 28, 2018
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
64 changes: 43 additions & 21 deletions lib/IDE/CodeCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3368,8 +3368,17 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
}

void tryPostfixOperator(Expr *expr, PostfixOperatorDecl *op) {
if (!expr->getType())
auto Ty = expr->getType();
if (!Ty)
return;

SWIFT_DEFER {
// Restore type.
// FIXME: This is workaround for getTypeOfExpressionWithoutApplying()
// modifies type of 'expr'.
expr->setType(Ty);
};

// 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(op->getName(), DeclRefKind::PostfixOperator,
Expand Down Expand Up @@ -3444,6 +3453,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
LHS->getType()->is<AnyFunctionType>()))
return;

// Preserve LHS type for restoring it.
Type LHSTy = LHS->getType();

// 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(op->getName(), DeclRefKind::BinaryOperator,
Expand All @@ -3456,13 +3468,20 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
// Reset sequence.
SE->setElement(SE->getNumElements() - 1, nullptr);
SE->setElement(SE->getNumElements() - 2, nullptr);
LHS->setType(LHSTy);
prepareForRetypechecking(SE);

// Reset any references to operators in types, so they are properly
// handled as operators by sequence folding.
//
// FIXME: Would be better to have some kind of 'OperatorRefExpr'?
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);
}

// Reset any references to operators in types, so they are properly
// handled as operators by sequence folding.
//
// FIXME: Would be better to have some kind of 'OperatorRefExpr'?
if (auto operatorRef = element->getMemberOperatorRef()) {
operatorRef->setType(nullptr);
element = operatorRef;
Expand Down Expand Up @@ -3496,20 +3515,20 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
}
}

void flattenBinaryExpr(BinaryExpr *expr, SmallVectorImpl<Expr *> &sequence) {
auto LHS = expr->getArg()->getElement(0);
if (auto binexpr = dyn_cast<BinaryExpr>(LHS))
flattenBinaryExpr(binexpr, sequence);
else
sequence.push_back(LHS);

sequence.push_back(expr->getFn());

auto RHS = expr->getArg()->getElement(1);
if (auto binexpr = dyn_cast<BinaryExpr>(RHS))
flattenBinaryExpr(binexpr, sequence);
else
sequence.push_back(RHS);
void flattenBinaryExpr(Expr *expr, SmallVectorImpl<Expr *> &sequence) {
if (auto binExpr = dyn_cast<BinaryExpr>(expr)) {
flattenBinaryExpr(binExpr->getArg()->getElement(0), sequence);
sequence.push_back(binExpr->getFn());
flattenBinaryExpr(binExpr->getArg()->getElement(1), sequence);
} else if (auto assignExpr = dyn_cast<AssignExpr>(expr)) {
flattenBinaryExpr(assignExpr->getDest(), sequence);
sequence.push_back(assignExpr);
flattenBinaryExpr(assignExpr->getSrc(), sequence);
assignExpr->setDest(nullptr);
assignExpr->setSrc(nullptr);
} else {
sequence.push_back(expr);
}
}

void typeCheckLeadingSequence(SmallVectorImpl<Expr *> &sequence) {
Expand All @@ -3519,10 +3538,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
// Take advantage of the fact the type-checker leaves the types on the AST.
if (!typeCheckExpression(const_cast<DeclContext *>(CurrDeclContext),
expr)) {
if (auto binexpr = dyn_cast<BinaryExpr>(expr)) {
if (isa<BinaryExpr>(expr) || isa<AssignExpr>(expr)) {
// Rebuild the sequence from the type-checked version.
sequence.clear();
flattenBinaryExpr(binexpr, sequence);
flattenBinaryExpr(expr, sequence);
return;
}
}
Expand All @@ -3548,6 +3567,9 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
if (sequence.size() > 1)
typeCheckLeadingSequence(sequence);

// Retrieve typechecked LHS.
LHS = sequence.back();

// Create a single sequence expression, which we will modify for each
// operator, filling in the operator and dummy right-hand side.
sequence.push_back(nullptr); // operator
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/CSRanking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,10 @@ static bool paramIsIUO(Decl *decl, int paramNum) {
auto *param = paramList->get(paramNum);
return param->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
}
if (auto *ee = dyn_cast<EnumElementDecl>(decl)) {
auto *param = ee->getParameterList()->get(paramNum);
return param->getAttrs().hasAttribute<ImplicitlyUnwrappedOptionalAttr>();
}

auto *subscript = cast<SubscriptDecl>(decl);
auto *index = subscript->getIndices()->get(paramNum);
Expand Down
55 changes: 52 additions & 3 deletions test/IDE/complete_crashes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,9 @@ protocol Bar_38149042 {
func foo_38149042(bar: Bar_38149042) {
_ = bar.foo? #^RDAR_38149042^# .x
}
// RDAR_38149042: Begin completions, 3 items

// 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 All @@ -246,3 +245,53 @@ func foo_38272904(a: A_38272904) {
bar_38272904(a: .foo() #^RDAR_38272904^#)
}
// RDAR_38272904: Begin completions

// rdar://problem/41159258
// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=RDAR41159258_1
// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=RDAR41159258_2 | %FileCheck %s -check-prefix=RDAR_41159258
public func ==(lhs: RDAR41159258_MyResult1, rhs: RDAR41159258_MyResult1) -> Bool {
fatalError()
}
public func ==(lhs: RDAR41159258_MyResult2, rhs: RDAR41159258_MyResult2) -> Bool {
fatalError()
}
public enum RDAR41159258_MyResult1 {
case failure(Error)
}
public enum RDAR41159258_MyResult2 {
case failure(Error)
}

public struct RDAR41159258_MyError: Error {}

func foo(x: RDAR41159258_MyResult1) {
let x: RDAR41159258_MyResult1
x = .failure(RDAR41159258_MyError()) #^RDAR41159258_1^#
let y: Bool
y = .failure(RDAR41159258_MyError()) #^RDAR41159258_2^#
}
// RDAR_41159258: Begin completions


// rdar://problem/41232519
// RUN: %target-swift-ide-test -code-completion -source-filename=%s -code-completion-token=RDAR41232519 | %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^#
}
}
// RDAR_41232519: Begin 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
func test_28188259(x: ((Int) -> Void) -> Void) {
x({_ in }#^RDAR_28188259^#)
}
// RDAR_28188259: Begin completions
// RDAR_28188259-DAG: Pattern/CurrModule: ({#_#})[#Void#]; name=(_)
// RDAR_28188259: End completions