Skip to content

Commit d972a95

Browse files
authored
Merge pull request #32415 from rintaro/5.3-ide-completion-postfixfallback-rdar64176730
[5.3][CodeCompletion] Typecheck without CodeCompletionExpr
2 parents debdc1d + b3c1dee commit d972a95

File tree

4 files changed

+132
-8
lines changed

4 files changed

+132
-8
lines changed

lib/IDE/CodeCompletion.cpp

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6170,15 +6170,33 @@ void CodeCompletionCallbacksImpl::doneParsing() {
61706170
if (analyzedExpr->getEndLoc() != CodeCompleteTokenExpr->getLoc())
61716171
break;
61726172

6173-
// If the call expression doesn't have a type, infer it from the
6174-
// possible callee info.
61756173
Type resultTy = analyzedExpr->getType();
6176-
if (!resultTy) {
6177-
if (ContextInfo.getPossibleCallees().empty())
6178-
break;
6179-
auto calleeInfo = ContextInfo.getPossibleCallees()[0];
6180-
resultTy = calleeInfo.Type->getResult();
6181-
analyzedExpr->setType(resultTy);
6174+
// If the call expression doesn't have a type, fallback to:
6175+
if (!resultTy || resultTy->is<ErrorType>()) {
6176+
// 1) Try to type check removing CodeCompletionExpr from the call.
6177+
Expr *removedExpr = analyzedExpr;
6178+
removeCodeCompletionExpr(CurDeclContext->getASTContext(),
6179+
removedExpr);
6180+
ConcreteDeclRef referencedDecl;
6181+
auto optT = getTypeOfCompletionContextExpr(
6182+
CurDeclContext->getASTContext(), CurDeclContext,
6183+
CompletionTypeCheckKind::Normal, removedExpr, referencedDecl);
6184+
if (optT) {
6185+
resultTy = *optT;
6186+
analyzedExpr->setType(resultTy);
6187+
}
6188+
}
6189+
if (!resultTy || resultTy->is<ErrorType>()) {
6190+
// 2) Infer it from the possible callee info.
6191+
if (!ContextInfo.getPossibleCallees().empty()) {
6192+
auto calleeInfo = ContextInfo.getPossibleCallees()[0];
6193+
resultTy = calleeInfo.Type->getResult();
6194+
analyzedExpr->setType(resultTy);
6195+
}
6196+
}
6197+
if (!resultTy || resultTy->is<ErrorType>()) {
6198+
// 3) Give up providing postfix completions.
6199+
break;
61826200
}
61836201

61846202
auto &SM = CurDeclContext->getASTContext().SourceMgr;

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "ExprContextAnalysis.h"
1414
#include "swift/AST/ASTContext.h"
15+
#include "swift/AST/ASTVisitor.h"
1516
#include "swift/AST/ASTWalker.h"
1617
#include "swift/AST/Decl.h"
1718
#include "swift/AST/DeclContext.h"
@@ -194,6 +195,81 @@ Expr *swift::ide::findParsedExpr(const DeclContext *DC,
194195
return finder.get();
195196
}
196197

198+
//===----------------------------------------------------------------------===//
199+
// removeCodeCompletionExpr(ASTContext, Expr)
200+
//===----------------------------------------------------------------------===//
201+
202+
namespace {
203+
// TODO: Implement other expressions?
204+
class CCExprRemover: public ASTWalker, public ExprVisitor<CCExprRemover, Expr *> {
205+
ASTContext &Ctx;
206+
207+
public:
208+
bool Removed = false;
209+
210+
CCExprRemover(ASTContext &Ctx) : Ctx(Ctx) {}
211+
212+
Expr *visitCallExpr(CallExpr *E) {
213+
SourceLoc lParenLoc, rParenLoc;
214+
SmallVector<Identifier, 2> argLabels;
215+
SmallVector<SourceLoc, 2> argLabelLocs;
216+
SmallVector<Expr *, 2> args;
217+
SmallVector<TrailingClosure, 2> trailingClosures;
218+
bool removing = false;
219+
220+
if (auto paren = dyn_cast<ParenExpr>(E->getArg())) {
221+
if (isa<CodeCompletionExpr>(paren->getSubExpr())) {
222+
lParenLoc = paren->getLParenLoc();
223+
rParenLoc = paren->getRParenLoc();
224+
removing = true;
225+
}
226+
} else if (auto tuple = dyn_cast<TupleExpr>(E->getArg())) {
227+
lParenLoc = tuple->getLParenLoc();
228+
rParenLoc = tuple->getRParenLoc();
229+
for (unsigned i = 0, e = tuple->getNumElements(); i != e; ++i) {
230+
if (isa<CodeCompletionExpr>(tuple->getElement(i))) {
231+
removing = true;
232+
continue;
233+
}
234+
235+
if (i < E->getUnlabeledTrailingClosureIndex()) {
236+
// Normal arguments.
237+
argLabels.push_back(E->getArgumentLabels()[i]);
238+
argLabelLocs.push_back(E->getArgumentLabelLocs()[i]);
239+
args.push_back(tuple->getElement(i));
240+
} else {
241+
// Trailing closure arguments.
242+
trailingClosures.emplace_back(E->getArgumentLabels()[i],
243+
E->getArgumentLabelLocs()[i],
244+
tuple->getElement(i));
245+
}
246+
}
247+
}
248+
if (removing) {
249+
Removed = true;
250+
return CallExpr::create(Ctx, E->getFn(), lParenLoc, args, argLabels,
251+
argLabelLocs, rParenLoc, trailingClosures,
252+
E->isImplicit());
253+
}
254+
return E;
255+
}
256+
257+
Expr *visitExpr(Expr *E) {
258+
return E;
259+
}
260+
261+
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
262+
return {true, visit(E)};
263+
}
264+
};
265+
}
266+
267+
bool swift::ide::removeCodeCompletionExpr(ASTContext &Ctx, Expr *&expr) {
268+
CCExprRemover remover(Ctx);
269+
expr = expr->walk(remover);
270+
return remover.Removed;
271+
}
272+
197273
//===----------------------------------------------------------------------===//
198274
// getReturnTypeFromContext(DeclContext)
199275
//===----------------------------------------------------------------------===//

lib/IDE/ExprContextAnalysis.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ void typeCheckContextUntil(DeclContext *DC, SourceLoc Loc);
3434
/// exact the same as \p TargetRange. Returns \c nullptr if not found.
3535
Expr *findParsedExpr(const DeclContext *DC, SourceRange TargetRange);
3636

37+
/// Remove \c CodeCompletionExpr from \p expr . Returns \c true if it actually
38+
/// mutated the expression.
39+
///
40+
/// NOTE: Currently, this only removes CodeCompletionExpr at call argument
41+
/// position.
42+
bool removeCodeCompletionExpr(ASTContext &Ctx, Expr *&expr);
43+
3744
/// Returns expected return type of the given decl context.
3845
/// \p DC should be an \c AbstractFunctionDecl or an \c AbstractClosureExpr.
3946
Type getReturnTypeFromContext(const DeclContext *DC);

test/IDE/complete_multiple_trailingclosure.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_REQUIRED_NEWLINE_2 | %FileCheck %s -check-prefix=INIT_REQUIRED_NEWLINE_2
1414
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_REQUIRED_SAMELINE_3 | %FileCheck %s -check-prefix=INIT_REQUIRED_SAMELINE_3
1515
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_REQUIRED_NEWLINE_3 | %FileCheck %s -check-prefix=INIT_REQUIRED_NEWLINE_3
16+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_FALLBACK_1 | %FileCheck %s -check-prefix=INIT_FALLBACK
17+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INIT_FALLBACK_2 | %FileCheck %s -check-prefix=INIT_FALLBACK
1618

1719
func globalFunc1(fn1: () -> Int, fn2: () -> String) {}
1820
func testGlobalFunc() {
@@ -184,3 +186,24 @@ func testOptionalInit() {
184186
// INIT_REQUIRED_NEWLINE_3-NOT: name=fn3
185187
// INIT_REQUIRED_NEWLINE_3: End completions
186188
}
189+
190+
struct MyStruct4<T> {
191+
init(arg1: Int = 0, arg2: () -> T) {}
192+
init(name: String, arg2: () -> String, arg3: () -> T) {}
193+
194+
func testStructMethod() {}
195+
}
196+
func testFallbackPostfix() {
197+
let _ = MyStruct4 {
198+
1
199+
} #^INIT_FALLBACK_1^#
200+
// INIT_FALLBACK: Begin completions, 2 items
201+
// INIT_FALLBACK-DAG: Decl[InstanceMethod]/CurrNominal: .testStructMethod()[#Void#];
202+
// INIT_FALLBACK-DAG: Keyword[self]/CurrNominal: .self[#MyStruct4<Int>#];
203+
// INIT_FALLBACK: End completions
204+
let _ = MyStruct4(name: "test") {
205+
""
206+
} arg3: {
207+
1
208+
} #^INIT_FALLBACK_2^#
209+
}

0 commit comments

Comments
 (0)