Skip to content

Commit 82b5f2a

Browse files
committed
[CodeCompletion] Find parsed expression from typechecked decl context
Instead of re-typechecking parsed expression, find typechecked expression that corresponds to the parsed expression from the typechecked decl context, because the sub expressions of the parsed expression can be weirdly mutated/replaced by decl context typechecking. rdar://problem/48141174
1 parent 3f76e63 commit 82b5f2a

8 files changed

+118
-37
lines changed

lib/IDE/CodeCompletion.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,9 +1292,14 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
12921292
// FIXME: if it's ErrorType but we've already typechecked we shouldn't
12931293
// typecheck again. rdar://21466394
12941294
if (CheckKind == CompletionTypeCheckKind::Normal &&
1295-
ParsedExpr->getType() && !ParsedExpr->getType()->is<ErrorType>())
1296-
return std::make_pair(ParsedExpr->getType(),
1297-
ParsedExpr->getReferencedDecl());
1295+
ParsedExpr->getType() && !ParsedExpr->getType()->is<ErrorType>()) {
1296+
auto refDecl = ParsedExpr->getReferencedDecl();
1297+
if (!refDecl) {
1298+
if (auto apply = dyn_cast<ApplyExpr>(ParsedExpr))
1299+
refDecl = apply->getFn()->getReferencedDecl();
1300+
}
1301+
return std::make_pair(ParsedExpr->getType(), refDecl);
1302+
}
12981303

12991304
prepareForRetypechecking(ParsedExpr);
13001305

@@ -3283,18 +3288,18 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
32833288
if (leadingSequence.empty())
32843289
return LHS;
32853290

3286-
assert(leadingSequence.size() % 2 == 0);
3287-
SmallVector<Expr *, 3> sequence(leadingSequence.begin(),
3288-
leadingSequence.end());
3289-
sequence.push_back(LHS);
3291+
SourceRange sequenceRange(leadingSequence.front()->getStartLoc(),
3292+
LHS->getEndLoc());
3293+
auto *expr = findParsedExpr(CurrDeclContext, sequenceRange);
3294+
if (!expr)
3295+
return LHS;
3296+
3297+
if (expr->getType() && !expr->getType()->hasError())
3298+
return expr;
32903299

3291-
Expr *expr =
3292-
SequenceExpr::create(CurrDeclContext->getASTContext(), sequence);
32933300
prepareForRetypechecking(expr);
3294-
if (!typeCheckExpression(const_cast<DeclContext *>(CurrDeclContext),
3295-
expr)) {
3301+
if (!typeCheckExpression(const_cast<DeclContext *>(CurrDeclContext), expr))
32963302
return expr;
3297-
}
32983303
return LHS;
32993304
}
33003305

@@ -4860,6 +4865,11 @@ void CodeCompletionCallbacksImpl::doneParsing() {
48604865
Optional<Type> ExprType;
48614866
ConcreteDeclRef ReferencedDecl = nullptr;
48624867
if (ParsedExpr) {
4868+
if (auto *checkedExpr = findParsedExpr(CurDeclContext,
4869+
ParsedExpr->getSourceRange())) {
4870+
ParsedExpr = checkedExpr;
4871+
}
4872+
48634873
if (auto typechecked = typeCheckParsedExpr()) {
48644874
ExprType = typechecked->first;
48654875
ReferencedDecl = typechecked->second;

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,58 @@ void swift::ide::typeCheckContextUntil(DeclContext *DC, SourceLoc Loc) {
142142
typeCheckContextImpl(DC, Loc);
143143
}
144144

145+
//===----------------------------------------------------------------------===//
146+
// findParsedExpr(DeclContext, Expr)
147+
//===----------------------------------------------------------------------===//
148+
149+
namespace {
150+
class ExprFinder : public ASTWalker {
151+
SourceManager &SM;
152+
SourceRange TargetRange;
153+
Expr *FoundExpr = nullptr;
154+
155+
template <typename NodeType> bool isInterstingRange(NodeType *Node) {
156+
return SM.rangeContains(Node->getSourceRange(), TargetRange);
157+
}
158+
159+
public:
160+
ExprFinder(SourceManager &SM, SourceRange TargetRange)
161+
: SM(SM), TargetRange(TargetRange) {}
162+
163+
Expr *get() const { return FoundExpr; }
164+
165+
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
166+
if (TargetRange == E->getSourceRange() && !isa<ImplicitConversionExpr>(E) &&
167+
!isa<AutoClosureExpr>(E) && !isa<ConstructorRefCallExpr>(E)) {
168+
assert(!FoundExpr && "non-nullptr for found expr");
169+
FoundExpr = E;
170+
return {false, nullptr};
171+
}
172+
return {isInterstingRange(E), E};
173+
}
174+
175+
std::pair<bool, Pattern *> walkToPatternPre(Pattern *P) override {
176+
return {isInterstingRange(P), P};
177+
}
178+
179+
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
180+
return {isInterstingRange(S), S};
181+
}
182+
183+
bool walkToDeclPre(Decl *D) override { return isInterstingRange(D); }
184+
185+
bool walkToTypeLocPre(TypeLoc &TL) override { return false; }
186+
bool walkToTypeReprPre(TypeRepr *T) override { return false; }
187+
};
188+
} // anonymous namespace
189+
190+
Expr *swift::ide::findParsedExpr(const DeclContext *DC,
191+
SourceRange TargetRange) {
192+
ExprFinder finder(DC->getASTContext().SourceMgr, TargetRange);
193+
const_cast<DeclContext *>(DC)->walkContext(finder);
194+
return finder.get();
195+
}
196+
145197
//===----------------------------------------------------------------------===//
146198
// getReturnTypeFromContext(DeclContext)
147199
//===----------------------------------------------------------------------===//

lib/IDE/ExprContextAnalysis.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ void prepareForRetypechecking(Expr *E);
3434
/// given context until \c Loc if the context is a function body.
3535
void typeCheckContextUntil(DeclContext *DC, SourceLoc Loc);
3636

37+
/// From \p DC, find and returns the outer most expression which source range is
38+
/// exact the same as \p TargetRange. Returns \c nullptr if not found.
39+
Expr *findParsedExpr(const DeclContext *DC, SourceRange TargetRange);
40+
3741
/// Returns expected return type of the given decl context.
3842
/// \p DC should be an \c AbstractFunctionDecl or an \c AbstractClosureExpr.
3943
Type getReturnTypeFromContext(const DeclContext *DC);

lib/Parse/ParseExpr.cpp

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,14 @@ ParserResult<Expr> Parser::parseExprSequence(Diag<> Message,
183183
ParserResult<Expr> Primary =
184184
parseExprSequenceElement(Message, isExprBasic);
185185

186-
HasCodeCompletion |= Primary.hasCodeCompletion();
187-
if (Primary.isNull()) {
188-
if (Primary.hasCodeCompletion()) {
189-
if (CodeCompletion) {
190-
CodeCompletion->setLeadingSequenceExprs(SequencedExprs);
191-
}
192-
return Primary;
193-
} else {
194-
return nullptr;
195-
}
186+
if (Primary.hasCodeCompletion()) {
187+
HasCodeCompletion = true;
188+
if (CodeCompletion)
189+
CodeCompletion->setLeadingSequenceExprs(SequencedExprs);
196190
}
191+
if (Primary.isNull())
192+
return Primary;
193+
197194
SequencedExprs.push_back(Primary.get());
198195

199196
// We know we can make a syntax node for ternary expression.
@@ -1338,7 +1335,8 @@ Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
13381335
}
13391336
// Eat the code completion token because we handled it.
13401337
consumeToken(tok::code_complete);
1341-
return makeParserCodeCompletionResult<Expr>();
1338+
Result.setHasCodeCompletion();
1339+
return Result;
13421340
}
13431341

13441342
// If we end up with an unknown token on this line, return an ErrorExpr

test/IDE/complete_crashes.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,8 @@ func test_28188259(x: ((Int) -> Void) -> Void) {
299299
x({_ in }#^RDAR_28188259^#)
300300
}
301301
// RDAR_28188259: Begin completions
302-
// RDAR_28188259-DAG: Pattern/CurrModule: ({#_#})[#Void#]; name=(_)
303-
// RDAR_28188259-DAG: Keyword[self]/CurrNominal: .self[#(_) -> ()#]; name=self
302+
// RDAR_28188259-DAG: Pattern/CurrModule: ({#Int#})[#Void#]; name=(Int)
303+
// RDAR_28188259-DAG: Keyword[self]/CurrNominal: .self[#(Int) -> ()#]; name=self
304304
// RDAR_28188259: End completions
305305

306306
// rdar://problem/40956846

test/IDE/complete_dynamic_lookup.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ func testAnyObject13(_ dl: AnyObject) {
475475
dl.returnsObjcClass!#^DL_FUNC_NAME_BANG_1^#
476476
}
477477
// DL_FUNC_NAME_BANG_1: Begin completions
478-
// DL_FUNC_NAME_BANG_1-NEXT: Pattern/CurrModule: ({#Int#})[#TopLevelObjcClass#]
478+
// DL_FUNC_NAME_BANG_1-NEXT: Pattern/CurrModule: ({#(i): Int#})[#TopLevelObjcClass#]
479479
// DL_FUNC_NAME_BANG_1-NEXT: Keyword[self]/CurrNominal: .self[#(Int) -> TopLevelObjcClass#]; name=self
480480
// DL_FUNC_NAME_BANG_1-NEXT: End completions
481481

test/IDE/complete_stdlib_optional.swift

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
// RUN: %FileCheck %s -check-prefix=UN_OPT_DOT_FOOSTRUCT < %t.opt.txt
2121

2222
// RUN: %target-swift-ide-test -code-completion -source-filename %s -disable-objc-attr-requires-foundation-module -code-completion-token=UN_OPT_DOT_2 > %t.opt.txt
23-
// RUN: %FileCheck %s -check-prefix=UN_OPT_DOT_FOOSTRUCT_RETURN < %t.opt.txt
23+
// RUN: %FileCheck %s -check-prefix=UN_OPT_DOT_FOOSTRUCT < %t.opt.txt
2424

2525
// RUN: %target-swift-ide-test -code-completion -source-filename %s -disable-objc-attr-requires-foundation-module -code-completion-token=UN_OPT_NO_DOT_2 > %t.opt.txt
26-
// RUN: %FileCheck %s -check-prefix=UN_OPT_NO_DOT_FOOSTRUCT_RETURN < %t.opt.txt
26+
// RUN: %FileCheck %s -check-prefix=UN_OPT_NO_DOT_FOOSTRUCT < %t.opt.txt
2727

2828
// RUN: %target-swift-ide-test -code-completion -source-filename %s -disable-objc-attr-requires-foundation-module -code-completion-token=OPT_TUPLE_1 | %FileCheck %s -check-prefix=OPT_TUPLE_1
2929
// RUN: %target-swift-ide-test -code-completion -source-filename %s -disable-objc-attr-requires-foundation-module -code-completion-token=OPT_TUPLE_2 | %FileCheck %s -check-prefix=OPT_TUPLE_2
@@ -72,11 +72,6 @@ func returnsImplicitlyUnwrappedOptional() -> FooStruct! {
7272
// UN_OPT_DOT_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc()[#Void#]{{; name=.+$}}
7373
// UN_OPT_DOT_FOOSTRUCT: End completions
7474

75-
// UN_OPT_DOT_FOOSTRUCT_RETURN: Begin completions
76-
// UN_OPT_DOT_FOOSTRUCT_RETURN-DAG: Decl[InstanceVar]/CurrNominal/Erase[1]: ?.instanceVar[#Int#]{{; name=.+$}}
77-
// UN_OPT_DOT_FOOSTRUCT_RETURN-DAG: Decl[InstanceMethod]/CurrNominal/Erase[1]: ?.instanceFunc()[#Void#]{{; name=.+$}}
78-
// UN_OPT_DOT_FOOSTRUCT_RETURN: End completions
79-
8075

8176
//===---
8277
//===--- Tests.
@@ -153,7 +148,3 @@ func testOptionalTuple5(a: (x: Int, y: String)?) {
153148
// UN_OPT_NO_DOT_FOOSTRUCT-DAG: Decl[InstanceMethod]/CurrNominal: .instanceFunc()[#Void#]{{; name=.+$}}
154149
// UN_OPT_NO_DOT_FOOSTRUCT: End completions
155150

156-
// UN_OPT_NO_DOT_FOOSTRUCT_RETURN: Begin completions
157-
// UN_OPT_NO_DOT_FOOSTRUCT_RETURN-DAG: Decl[InstanceVar]/CurrNominal: ?.instanceVar[#Int#]{{; name=.+$}}
158-
// UN_OPT_NO_DOT_FOOSTRUCT_RETURN-DAG: Decl[InstanceMethod]/CurrNominal: ?.instanceFunc()[#Void#]{{; name=.+$}}
159-
// UN_OPT_NO_DOT_FOOSTRUCT_RETURN: End completions

test/IDE/complete_value_expr.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,3 +2084,29 @@ func testExistential() {
20842084
// PROTOCOLTYPE_DOT_3-DAG: Keyword/CurrNominal: Type[#ExistentialProto.Protocol.Type#]; name=Type
20852085
// PROTOCOLTYPE_DOT_3: End completions
20862086
}
2087+
2088+
// rdar://problem/48141174
2089+
class TestChain {
2090+
class Child {
2091+
var value: Struct1
2092+
}
2093+
class Struct1 {
2094+
var value: Struct2
2095+
}
2096+
class Struct2 {
2097+
var prop1: Int
2098+
var prop2: Int
2099+
}
2100+
2101+
var child: Child!
2102+
2103+
func foo() {
2104+
let _ = self.child.value.value.#^COMPLEX_CHAIN_1^#
2105+
let _ = child.value.value.#^COMPLEX_CHAIN_2^#
2106+
// COMPLEX_CHAIN_1: Begin completions, 3 items
2107+
// COMPLEX_CHAIN_1-DAG: Keyword[self]/CurrNominal: self[#Container.Struct2#]
2108+
// COMPLEX_CHAIN_1-DAG: Decl[InstanceVar]/CurrNominal: prop1[#Int#]
2109+
// COMPLEX_CHAIN_1-DAG: Decl[InstanceVar]/CurrNominal: prop2[#Int#]
2110+
// COMPLEX_CHAIN_1: End completions
2111+
}
2112+
}

0 commit comments

Comments
 (0)