Skip to content

[CodeCompletion] Teach TypeCheckASTNodeAtLocRequest to check initializers #42163

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
9 changes: 5 additions & 4 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,11 @@ ProtocolDecl *DeclContext::getExtendedProtocolDecl() const {

VarDecl *DeclContext::getNonLocalVarDecl() const {
if (auto *init = dyn_cast<PatternBindingInitializer>(this)) {
if (auto *var =
init->getBinding()->getAnchoringVarDecl(init->getBindingIndex())) {
return var;
}
if (auto binding = init->getBinding()) {
if (auto *var = binding->getAnchoringVarDecl(init->getBindingIndex())) {
return var;
}
}
}
return nullptr;
}
Expand Down
58 changes: 1 addition & 57 deletions lib/IDE/ExprContextAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ using namespace ide;
//===----------------------------------------------------------------------===//

void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) {
while (isa<AbstractClosureExpr>(DC))
DC = DC->getParent();

// Make sure the extension has been bound.
{
// Even if the extension is invalid (e.g. nested in a function or another
Expand Down Expand Up @@ -77,60 +74,7 @@ void swift::ide::typeCheckContextAt(DeclContext *DC, SourceLoc Loc) {
}
}

// Type-check this context.
switch (DC->getContextKind()) {
case DeclContextKind::AbstractClosureExpr:
case DeclContextKind::Module:
case DeclContextKind::FileUnit:
case DeclContextKind::SerializedLocal:
case DeclContextKind::EnumElementDecl:
case DeclContextKind::GenericTypeDecl:
case DeclContextKind::SubscriptDecl:
case DeclContextKind::ExtensionDecl:
// Nothing to do for these.
break;

case DeclContextKind::Initializer:
if (auto *patternInit = dyn_cast<PatternBindingInitializer>(DC)) {
if (auto *PBD = patternInit->getBinding()) {
auto i = patternInit->getBindingIndex();
PBD->getPattern(i)->forEachVariable(
[](VarDecl *VD) { (void)VD->getInterfaceType(); });
if (PBD->getInit(i)) {
if (!PBD->isInitializerChecked(i))
typeCheckPatternBinding(PBD, i,
/*LeaveClosureBodyUnchecked=*/true);
}
}
} else if (auto *defaultArg = dyn_cast<DefaultArgumentInitializer>(DC)) {
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(defaultArg->getParent())) {
auto *Param = AFD->getParameters()->get(defaultArg->getIndex());
(void)Param->getTypeCheckedDefaultExpr();
}
if (auto *SD = dyn_cast<SubscriptDecl>(defaultArg->getParent())) {
auto *Param = SD->getIndices()->get(defaultArg->getIndex());
(void)Param->getTypeCheckedDefaultExpr();
}
}
break;

case DeclContextKind::TopLevelCodeDecl:
swift::typeCheckASTNodeAtLoc(DC, Loc);
break;

case DeclContextKind::AbstractFunctionDecl: {
auto *AFD = cast<AbstractFunctionDecl>(DC);
auto &SM = DC->getASTContext().SourceMgr;
auto bodyRange = AFD->getBodySourceRange();
if (SM.rangeContainsTokenLoc(bodyRange, Loc)) {
swift::typeCheckASTNodeAtLoc(DC, Loc);
} else {
assert(bodyRange.isInvalid() && "The body should not be parsed if the "
"completion happens in the signature");
}
break;
}
}
swift::typeCheckASTNodeAtLoc(DC, Loc);
}

//===----------------------------------------------------------------------===//
Expand Down
30 changes: 30 additions & 0 deletions lib/Sema/TypeCheckStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1800,6 +1800,36 @@ bool TypeCheckASTNodeAtLocRequest::evaluate(Evaluator &evaluator,
assert(DiagnosticSuppression::isEnabled(ctx.Diags) &&
"Diagnosing and Single ASTNode type checknig don't mix");

// Initializers aren't walked by ASTWalker and thus we don't find the context
// to type check using ASTNodeFinder. Also, initializers aren't representable
// by ASTNodes that can be type checked using typeCheckASTNode.
// Handle them specifically here.
if (auto *patternInit = dyn_cast<PatternBindingInitializer>(DC)) {
if (auto *PBD = patternInit->getBinding()) {
auto i = patternInit->getBindingIndex();
PBD->getPattern(i)->forEachVariable(
[](VarDecl *VD) { (void)VD->getInterfaceType(); });
if (PBD->getInit(i)) {
if (!PBD->isInitializerChecked(i)) {
typeCheckPatternBinding(PBD, i,
/*LeaveClosureBodyUnchecked=*/true);
return false;
}
}
}
} else if (auto *defaultArg = dyn_cast<DefaultArgumentInitializer>(DC)) {
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(defaultArg->getParent())) {
auto *Param = AFD->getParameters()->get(defaultArg->getIndex());
(void)Param->getTypeCheckedDefaultExpr();
return false;
}
if (auto *SD = dyn_cast<SubscriptDecl>(defaultArg->getParent())) {
auto *Param = SD->getIndices()->get(defaultArg->getIndex());
(void)Param->getTypeCheckedDefaultExpr();
return false;
}
}

// Find innermost ASTNode at Loc from DC. Results the reference to the found
// ASTNode and the decl context of it.
class ASTNodeFinder : public ASTWalker {
Expand Down
17 changes: 17 additions & 0 deletions test/IDE/complete_call_arg.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1371,3 +1371,20 @@ func testDynamicMemberSubscriptLookup() {
// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: index[#Int#]; name=index
// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath<Binding<MyStruct>, Value>#}[']'][#Value#]; name=keyPath:
// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP: End completions

func testVarInitializedByCallingClosure() {
struct MyBundle {
func vrl(forResource: String, withExtension: String?)
}

struct Foo {
private lazy var calculatorContext: Void = {
let Bundle_main = MyBundle()
Bundle_main.vrl(forResource: "turnips", #^VAR_INITIALIZED_BY_CALLING_CLOSURE^#withExtension: "js")
}()
}

// VAR_INITIALIZED_BY_CALLING_CLOSURE: Begin completions, 1 items
// VAR_INITIALIZED_BY_CALLING_CLOSURE-DAG: Pattern/Local/Flair[ArgLabels]: {#withExtension: String?#}[#String?#];
// VAR_INITIALIZED_BY_CALLING_CLOSURE: End completions
}
17 changes: 17 additions & 0 deletions test/IDE/complete_multiple_files.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
//
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MODULE_SCOPED %S/Inputs/multiple-files-1.swift %S/Inputs/multiple-files-2.swift | %FileCheck %s -check-prefix=MODULE_SCOPED

// RUN: %empty-directory(%t)
// RUN: echo "" > %t/empty.swift
// RUN: %swift-ide-test --code-completion --code-completion-token VAR_INITIALIZED_BY_CALLING_CLOSURE --source-filename %s --second-source-filename %t/empty.swift | %FileCheck %s -check-prefix=VAR_INITIALIZED_BY_CALLING_CLOSURE

func testObjectExpr() {
fooObject.#^T1^#
}
Expand Down Expand Up @@ -45,3 +49,16 @@ func moduleScoped() {
// MODULE_SCOPED: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]{{; name=.+$}}
// MODULE_SCOPED-NOT: ERROR
// MODULE_SCOPED: End completions

enum Foo {
case bar
}

var sr15495: Void = {
let foo: Foo = .#^VAR_INITIALIZED_BY_CALLING_CLOSURE^#
}()

// VAR_INITIALIZED_BY_CALLING_CLOSURE: Begin completions, 2 items
// VAR_INITIALIZED_BY_CALLING_CLOSURE-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: bar[#Foo#];
// VAR_INITIALIZED_BY_CALLING_CLOSURE-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Foo#})[#(into: inout Hasher) -> Void#];
// VAR_INITIALIZED_BY_CALLING_CLOSURE: End completions