Skip to content

Commit 4205e46

Browse files
authored
Merge pull request #32184 from rintaro/ide-completion-rdar63965160
[CodeCompletion] Wrap base expression with CodeCompletionExpr
2 parents 1979d3d + 3ec250f commit 4205e46

File tree

8 files changed

+99
-18
lines changed

8 files changed

+99
-18
lines changed

include/swift/AST/Expr.h

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -597,13 +597,23 @@ class ErrorExpr : public Expr {
597597
/// CodeCompletionExpr - Represents the code completion token in the AST, this
598598
/// can help us preserve the context of the code completion position.
599599
class CodeCompletionExpr : public Expr {
600-
SourceRange Range;
600+
Expr *Base;
601+
SourceLoc Loc;
601602

602603
public:
603-
CodeCompletionExpr(SourceRange Range, Type Ty = Type())
604-
: Expr(ExprKind::CodeCompletion, /*Implicit=*/true, Ty), Range(Range) {}
604+
CodeCompletionExpr(Expr *Base, SourceLoc Loc)
605+
: Expr(ExprKind::CodeCompletion, /*Implicit=*/true, Type()),
606+
Base(Base), Loc(Loc) {}
605607

606-
SourceRange getSourceRange() const { return Range; }
608+
CodeCompletionExpr(SourceLoc Loc)
609+
: CodeCompletionExpr(/*Base=*/nullptr, Loc) {}
610+
611+
Expr *getBase() const { return Base; }
612+
void setBase(Expr *E) { Base = E; }
613+
614+
SourceLoc getLoc() const { return Loc; }
615+
SourceLoc getStartLoc() const { return Base ? Base->getStartLoc() : Loc; }
616+
SourceLoc getEndLoc() const { return Loc; }
607617

608618
static bool classof(const Expr *E) {
609619
return E->getKind() == ExprKind::CodeCompletion;

lib/AST/ASTWalker.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,15 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
476476
} while (false)
477477

478478
Expr *visitErrorExpr(ErrorExpr *E) { return E; }
479-
Expr *visitCodeCompletionExpr(CodeCompletionExpr *E) { return E; }
479+
Expr *visitCodeCompletionExpr(CodeCompletionExpr *E) {
480+
if (Expr *baseExpr = E->getBase()) {
481+
Expr *newBaseExpr = doIt(baseExpr);
482+
if (!newBaseExpr)
483+
return nullptr;
484+
E->setBase(newBaseExpr);
485+
}
486+
return E;
487+
}
480488
Expr *visitLiteralExpr(LiteralExpr *E) { return E; }
481489
Expr *visitDiscardAssignmentExpr(DiscardAssignmentExpr *E) { return E; }
482490
Expr *visitTypeExpr(TypeExpr *E) {

lib/Parse/ParseExpr.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,10 +1133,9 @@ Parser::parseExprPostfixSuffix(ParserResult<Expr> Result, bool isExprBasic,
11331133
if (CodeCompletion) {
11341134
CodeCompletion->completeDotExpr(Result.get(), /*DotLoc=*/TokLoc);
11351135
}
1136-
// Eat the code completion token because we handled it.
1137-
consumeToken(tok::code_complete);
1138-
Result.setHasCodeCompletion();
1139-
return Result;
1136+
auto CCExpr = new (Context) CodeCompletionExpr(Result.get(),
1137+
consumeToken(tok::code_complete));
1138+
return makeParserCodeCompletionResult(CCExpr);
11401139
}
11411140

11421141
DeclNameLoc NameLoc;

lib/Parse/ParseStmt.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ ParserResult<Stmt> Parser::parseStmtReturn(SourceLoc tryLoc) {
750750
SourceLoc ReturnLoc = consumeToken(tok::kw_return);
751751

752752
if (Tok.is(tok::code_complete)) {
753-
auto CCE = new (Context) CodeCompletionExpr(SourceRange(Tok.getLoc()));
753+
auto CCE = new (Context) CodeCompletionExpr(Tok.getLoc());
754754
auto Result = makeParserResult(new (Context) ReturnStmt(ReturnLoc, CCE));
755755
if (CodeCompletion) {
756756
CodeCompletion->completeReturnStmt(CCE);
@@ -818,7 +818,7 @@ ParserResult<Stmt> Parser::parseStmtYield(SourceLoc tryLoc) {
818818
SourceLoc yieldLoc = consumeToken(tok::kw_yield);
819819

820820
if (Tok.is(tok::code_complete)) {
821-
auto cce = new (Context) CodeCompletionExpr(SourceRange(Tok.getLoc()));
821+
auto cce = new (Context) CodeCompletionExpr(Tok.getLoc());
822822
auto result = makeParserResult(
823823
YieldStmt::create(Context, yieldLoc, SourceLoc(), cce, SourceLoc()));
824824
if (CodeCompletion) {

lib/Sema/CSGen.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,9 +1176,24 @@ namespace {
11761176

11771177
virtual Type visitCodeCompletionExpr(CodeCompletionExpr *E) {
11781178
CS.Options |= ConstraintSystemFlags::SuppressDiagnostics;
1179-
return CS.createTypeVariable(CS.getConstraintLocator(E),
1180-
TVO_CanBindToLValue |
1181-
TVO_CanBindToNoEscape);
1179+
auto locator = CS.getConstraintLocator(E);
1180+
auto ty = CS.createTypeVariable(locator,
1181+
TVO_CanBindToLValue |
1182+
TVO_CanBindToNoEscape);
1183+
1184+
// Defaults to the type of the base expression if we have a base
1185+
// expression.
1186+
// FIXME: This is just to keep the old behavior where `foo(base.<HERE>)`
1187+
// the argument is type checked to the type of the 'base'. Ideally, code
1188+
// completion expression should be defauled to 'UnresolvedType'
1189+
// regardless of the existence of the base expression. But the constraint
1190+
// system is simply not ready for that.
1191+
if (auto base = E->getBase()) {
1192+
CS.addConstraint(ConstraintKind::Defaultable, ty, CS.getType(base),
1193+
locator);
1194+
}
1195+
1196+
return ty;
11821197
}
11831198

11841199
Type visitNilLiteralExpr(NilLiteralExpr *expr) {

test/IDE/complete_enum_elements.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
// RUN: %FileCheck %s -check-prefix=FOO_ENUM_DOT_ELEMENTS < %t.enum.txt
3030

3131
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_SW_WITH_QUAL_1 > %t.enum.txt
32-
// RUN: %FileCheck %s -check-prefix=FOO_ENUM_DOT < %t.enum.txt
32+
// RUN: %FileCheck %s -check-prefix=FOO_ENUM_DOT_CONTEXT < %t.enum.txt
3333

3434
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ENUM_SW_EXPR_ERROR_1 > %t.enum.txt
3535
// RUN: %FileCheck %s -check-prefix=FOO_ENUM_DOT < %t.enum.txt
@@ -116,6 +116,17 @@ enum FooEnum: CaseIterable {
116116
// FOO_ENUM_DOT-NEXT: Decl[StaticVar]/CurrNominal: allCases[#[FooEnum]#]{{; name=.+$}}
117117
// FOO_ENUM_DOT-NEXT: End completions
118118

119+
// FOO_ENUM_DOT_CONTEXT: Begin completions
120+
// FOO_ENUM_DOT_CONTEXT-NEXT: Keyword[self]/CurrNominal: self[#FooEnum.Type#]; name=self
121+
// FOO_ENUM_DOT_CONTEXT-NEXT: Keyword/CurrNominal: Type[#FooEnum.Type#]; name=Type
122+
// FOO_ENUM_DOT_CONTEXT-NEXT: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: Foo1[#FooEnum#]{{; name=.+$}}
123+
// FOO_ENUM_DOT_CONTEXT-NEXT: Decl[EnumElement]/CurrNominal/TypeRelation[Identical]: Foo2[#FooEnum#]{{; name=.+$}}
124+
// FOO_ENUM_DOT_CONTEXT-NEXT: Decl[StaticVar]/CurrNominal/TypeRelation[Identical]: alias1[#FooEnum#]{{; name=.+$}}
125+
// FOO_ENUM_DOT_CONTEXT-NEXT: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): FooEnum#})[#(into: inout Hasher) -> Void#]{{; name=.+$}}
126+
// FOO_ENUM_DOT_CONTEXT-NEXT: Decl[TypeAlias]/CurrNominal: AllCases[#[FooEnum]#]{{; name=.+$}}
127+
// FOO_ENUM_DOT_CONTEXT-NEXT: Decl[StaticVar]/CurrNominal: allCases[#[FooEnum]#]{{; name=.+$}}
128+
// FOO_ENUM_DOT_CONTEXT-NEXT: End completions
129+
119130
// FOO_ENUM_DOT_INVALID: Begin completions
120131
// FOO_ENUM_DOT_INVALID-NEXT: Keyword[self]/CurrNominal: self[#FooEnum.Type#]; name=self
121132
// FOO_ENUM_DOT_INVALID-NEXT: Keyword/CurrNominal: Type[#FooEnum.Type#]; name=Type

test/IDE/complete_rdar63965160.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=STRINGLITERAL | %FileCheck %s
2+
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=NORMAL | %FileCheck %s
3+
4+
protocol View {}
5+
6+
@_functionBuilder
7+
struct Builder {
8+
static func buildBlock<C0: View>(_ c0: C0) -> C0 {}
9+
static func buildBlock<C0: View, C1: View>(_ c0: C0, _ c1: C1) -> C1 {}
10+
static func buildBlock<C0: View, C1: View, C2: View>(_ c0: C0, _ c1: C1, _ c2: C2) -> C1 {}
11+
}
12+
13+
struct ForEach<Data, Content>: View where Data: RandomAccessCollection {
14+
init(_ dat: Data, @Builder content: (Data.Element) -> Content) {}
15+
}
16+
17+
struct Text: View {
18+
init(_ text: String) {}
19+
}
20+
21+
struct Value {
22+
var name: String
23+
}
24+
25+
func test(values: [Value]) {
26+
_ = ForEach(values) { value in
27+
Text("foobar")
28+
Text("value \(value.#^STRINGLITERAL^#)")
29+
}
30+
_ = ForEach(values) { value in
31+
Text("foobar")
32+
Text(value.#^NORMAL^#)
33+
}
34+
}
35+
// CHECK: Begin completions, 2 items
36+
// CHECK-DAG: Keyword[self]/CurrNominal: self[#Value#];
37+
// CHECK-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[{{Convertible|Identical}}]: name[#String#];
38+
// CHECK: End completions

test/IDE/complete_skipbody.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ let globalValue = globalValueOpt!
4949

5050
let FORBIDDEN_globalVar = 1
5151

52-
switch glovalValue.x {
52+
switch globalValue.x {
5353
case let x where x < 2:
5454
if x == globalValue.#^TOPLEVEL^# {}
5555
default:
@@ -58,6 +58,6 @@ default:
5858

5959
// CHECK: Begin completions, 3 items
6060
// CHECK-DAG: Keyword[self]/CurrNominal: self[#MyStruct#]; name=self
61-
// CHECK-DAG: Decl[InstanceVar]/CurrNominal: x[#Int#]; name=x
62-
// CHECK-DAG: Decl[InstanceVar]/CurrNominal: y[#Int#]; name=y
61+
// CHECK-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: x[#Int#]; name=x
62+
// CHECK-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: y[#Int#]; name=y
6363
// CHECK: End completions

0 commit comments

Comments
 (0)