Skip to content

Commit 2564a6e

Browse files
committed
[CodeCompletion] Avoid typechecking all toplevel decls in the current file
- Use `performParseAndResolveImportsOnly()` to invoke the frontend - Do `bindExtensions()` in `ide::typeCheckContextUntil()` - Typecheck preceding `TopLevelCodeDecl`s only if the compleiton is in a `TopLevelCodeDecl` - Other related tweaks rdar://problem/56636747
1 parent 9203080 commit 2564a6e

File tree

13 files changed

+109
-39
lines changed

13 files changed

+109
-39
lines changed

include/swift/Sema/IDETypeChecking.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ namespace swift {
4646
class ValueDecl;
4747
struct PrintOptions;
4848

49+
void bindExtensions(SourceFile &SF);
50+
4951
/// Typecheck binding initializer at \p bindingIndex.
5052
void typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned bindingIndex);
5153

lib/Frontend/Frontend.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -845,8 +845,13 @@ void CompilerInstance::parseAndCheckTypesUpTo(
845845

846846
// If the limiting AST stage is name binding, we're done.
847847
if (limitStage <= SourceFile::NameBound) {
848+
if (Invocation.isCodeCompletion()) {
849+
performCodeCompletionSecondPass(*PersistentState.get(),
850+
*Invocation.getCodeCompletionFactory());
851+
}
848852
return;
849853
}
854+
assert(!Invocation.isCodeCompletion());
850855

851856
const auto &options = Invocation.getFrontendOptions();
852857
forEachFileToTypeCheck([&](SourceFile &SF) {
@@ -870,10 +875,6 @@ void CompilerInstance::parseAndCheckTypesUpTo(
870875
}
871876
});
872877

873-
if (Invocation.isCodeCompletion()) {
874-
performCodeCompletionSecondPass(*PersistentState.get(),
875-
*Invocation.getCodeCompletionFactory());
876-
}
877878
finishTypeChecking(TypeCheckOptions);
878879
}
879880

lib/IDE/CodeCompletion.cpp

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4138,10 +4138,47 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
41384138

41394139
void addAccessControl(const ValueDecl *VD,
41404140
CodeCompletionResultBuilder &Builder) {
4141-
assert(CurrDeclContext->getSelfNominalTypeDecl());
4142-
auto AccessOfContext =
4143-
CurrDeclContext->getSelfNominalTypeDecl()->getFormalAccess();
4144-
auto Access = std::min(VD->getFormalAccess(), AccessOfContext);
4141+
auto CurrentNominal = CurrDeclContext->getSelfNominalTypeDecl();
4142+
assert(CurrentNominal);
4143+
4144+
auto AccessOfContext = CurrentNominal->getFormalAccess();
4145+
if (AccessOfContext < AccessLevel::Public)
4146+
return;
4147+
4148+
auto Access = VD->getFormalAccess();
4149+
// Use the greater access between the protocol requirement and the witness.
4150+
// In case of:
4151+
//
4152+
// public protocol P { func foo() }
4153+
// public class B { func foo() {} }
4154+
// public class C: B, P {
4155+
// <complete>
4156+
// }
4157+
//
4158+
// 'VD' is 'B.foo()' which is implicitly 'internal'. But as the overriding
4159+
// declaration, the user needs to write both 'public' and 'override':
4160+
//
4161+
// public class C: B {
4162+
// public override func foo() {}
4163+
// }
4164+
if (Access < AccessLevel::Public &&
4165+
!isa<ProtocolDecl>(VD->getDeclContext())) {
4166+
for (auto Conformance : CurrentNominal->getAllConformances()) {
4167+
auto Proto = Conformance->getProtocol();
4168+
for (auto Member : Proto->getMembers()) {
4169+
auto Requirement = dyn_cast<ValueDecl>(Member);
4170+
if (!Requirement || !Requirement->isProtocolRequirement() ||
4171+
isa<AssociatedTypeDecl>(Requirement))
4172+
continue;
4173+
4174+
auto Witness = Conformance->getWitnessDecl(Requirement);
4175+
if (Witness == VD)
4176+
Access = std::max(Access, Requirement->getFormalAccess());
4177+
}
4178+
}
4179+
}
4180+
4181+
Access = std::min(Access, AccessOfContext);
41454182
// Only emit 'public', not needed otherwise.
41464183
if (Access >= AccessLevel::Public)
41474184
Builder.addAccessControlKeyword(Access);
@@ -4364,9 +4401,11 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
43644401
if (D->shouldHideFromEditor())
43654402
return;
43664403

4367-
if (D->isFinal() ||
4368-
// A 'class' member with an initial value cannot be overriden either.
4369-
(D->isStatic() && D->getAttrs().hasAttribute<HasInitialValueAttr>()))
4404+
if (D->isFinal())
4405+
return;
4406+
4407+
// A 'class' member with an initial value cannot be overriden either.
4408+
if (D->isStatic() && isa<VarDecl>(D) && cast<VarDecl>(D)->hasInitialValue())
43704409
return;
43714410

43724411
bool hasIntroducer = hasFuncIntroducer ||
@@ -4955,13 +4994,16 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
49554994
case CompletionKind::TypeIdentifierWithoutDot:
49564995
break;
49574996

4958-
case CompletionKind::TypeDeclResultBeginning:
4959-
if (!isa<ProtocolDecl>(CurDeclContext))
4960-
if (CurDeclContext->isTypeContext() ||
4961-
(ParsedDecl && isa<FuncDecl>(ParsedDecl)))
4997+
case CompletionKind::TypeDeclResultBeginning: {
4998+
auto DC = CurDeclContext;
4999+
if (ParsedDecl && ParsedDecl == CurDeclContext->getAsDecl())
5000+
DC = ParsedDecl->getDeclContext();
5001+
if (!isa<ProtocolDecl>(DC))
5002+
if (DC->isTypeContext() || (ParsedDecl && isa<FuncDecl>(ParsedDecl)))
49625003
addOpaqueTypeKeyword(Sink);
49635004

49645005
LLVM_FALLTHROUGH;
5006+
}
49655007
case CompletionKind::TypeSimpleBeginning:
49665008
addAnyTypeKeyword(Sink);
49675009
break;
@@ -5130,8 +5172,6 @@ void CodeCompletionCallbacksImpl::doneParsing() {
51305172
CD->getContextKind() == DeclContextKind::TopLevelCodeDecl)
51315173
MaybeFuncBody = false;
51325174
}
5133-
// Add keywords even if type checking fails completely.
5134-
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
51355175

51365176
if (auto *DC = dyn_cast_or_null<DeclContext>(ParsedDecl)) {
51375177
if (DC->isChildContextOf(CurDeclContext))
@@ -5142,6 +5182,9 @@ void CodeCompletionCallbacksImpl::doneParsing() {
51425182
CurDeclContext,
51435183
CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc());
51445184

5185+
// Add keywords even if type checking fails completely.
5186+
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
5187+
51455188
Optional<Type> ExprType;
51465189
ConcreteDeclRef ReferencedDecl = nullptr;
51475190
if (ParsedExpr) {

lib/IDE/ExprContextAnalysis.cpp

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/Module.h"
2323
#include "swift/AST/ParameterList.h"
2424
#include "swift/AST/Pattern.h"
25+
#include "swift/AST/SourceFile.h"
2526
#include "swift/AST/Stmt.h"
2627
#include "swift/AST/Type.h"
2728
#include "swift/AST/Types.h"
@@ -61,10 +62,9 @@ void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) {
6162
if (auto *patternInit = dyn_cast<PatternBindingInitializer>(DC)) {
6263
if (auto *PBD = patternInit->getBinding()) {
6364
auto i = patternInit->getBindingIndex();
65+
PBD->getPattern(i)->forEachVariable(
66+
[](VarDecl *VD) { (void)VD->getInterfaceType(); });
6467
if (PBD->getInit(i)) {
65-
PBD->getPattern(i)->forEachVariable([](VarDecl *VD) {
66-
(void) VD->getInterfaceType();
67-
});
6868
if (!PBD->isInitializerChecked(i))
6969
typeCheckPatternBinding(PBD, i);
7070
}
@@ -91,15 +91,35 @@ void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) {
9191
} // anonymous namespace
9292

9393
void swift::ide::typeCheckContextUntil(DeclContext *DC, SourceLoc Loc) {
94-
// The only time we have to explicitly check a TopLevelCodeDecl
95-
// is when we're directly inside of one. In this case,
96-
// performTypeChecking() did not type check it for us.
94+
// Lookup the swift module. This ensures that we record all known
95+
// protocols in the AST.
96+
(void) DC->getASTContext().getStdlibModule();
97+
98+
bindExtensions(*DC->getParentSourceFile());
99+
97100
while (isa<AbstractClosureExpr>(DC))
98101
DC = DC->getParent();
99-
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(DC))
100-
typeCheckTopLevelCodeDecl(TLCD);
101-
else
102+
103+
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(DC)) {
104+
// Typecheck all 'TopLevelCodeDecl's up to the target one.
105+
// In theory, this is not needed, but it fails to resolve the type of
106+
// 'guard'ed variable. e.g.
107+
//
108+
// guard value = something() else { fatalError() }
109+
// <complete>
110+
// Here, 'value' is '<error type>' unless we explicitly typecheck the
111+
// 'guard' statement.
112+
SourceFile *SF = DC->getParentSourceFile();
113+
for (auto *D : SF->Decls) {
114+
if (auto Code = dyn_cast<TopLevelCodeDecl>(D)) {
115+
typeCheckTopLevelCodeDecl(Code);
116+
if (Code == TLCD)
117+
break;
118+
}
119+
}
120+
} else {
102121
typeCheckContextImpl(DC, Loc);
122+
}
103123
}
104124

105125
//===----------------------------------------------------------------------===//

lib/Sema/TypeChecker.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC,
398398
// Resolve extensions. This has to occur first during type checking,
399399
// because the extensions need to be wired into the AST for name lookup
400400
// to work.
401-
bindExtensions(SF);
401+
::bindExtensions(SF);
402402

403403
// Type check the top-level elements of the source file.
404404
for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) {
@@ -739,3 +739,7 @@ TypeChecker::getDeclTypeCheckingSemantics(ValueDecl *decl) {
739739
}
740740
return DeclTypeCheckingSemantics::Normal;
741741
}
742+
743+
void swift::bindExtensions(SourceFile &SF) {
744+
::bindExtensions(SF);
745+
}

test/IDE/complete_after_super.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ class SemanticContextDerived1 : SemanticContextBase1 {
491491
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}}
492492
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: End completions
493493
}
494-
func instanceFunc1() {
494+
override func instanceFunc1() {
495495
#^SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3^#
496496
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3: Begin completions
497497
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1()[#Void#]{{; name=.+$}}
@@ -504,7 +504,7 @@ class SemanticContextDerived1 : SemanticContextBase1 {
504504
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}}
505505
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: End completions
506506
}
507-
func instanceFunc1(_ a: Int) {
507+
override func instanceFunc1(_ a: Int) {
508508
super.#^SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5^#
509509
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5: Begin completions
510510
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1()[#Void#]{{; name=.+$}}

test/IDE/complete_expr_postfix_begin.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@
7575
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_TUPLE_1 | %FileCheck %s -check-prefix=IN_TUPLE_1
7676
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_TUPLE_2 | %FileCheck %s -check-prefix=IN_TUPLE_2
7777

78-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_1 | %FileCheck %s -check-prefix=OWN_INIT_1
79-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_2 | %FileCheck %s -check-prefix=OWN_INIT_2
78+
// RUN-FIXME(rdar56755598): %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_1 | %FileCheck %s -check-prefix=OWN_INIT_1
79+
// RUN-FIXME(rdar56755598): %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_2 | %FileCheck %s -check-prefix=OWN_INIT_2
8080
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_3 | %FileCheck %s -check-prefix=OWN_INIT_3
8181
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_4 | %FileCheck %s -check-prefix=OWN_INIT_4
8282
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_5 | %FileCheck %s -check-prefix=OWN_INIT_5

test/SourceKit/CodeComplete/complete_member.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class Base {
3232
}
3333

3434
class Derived: Base {
35-
func foo() {}
35+
override func foo() {}
3636
}
3737

3838
func testOverrideUSR() {

test/SourceKit/CompileNotifications/diagnostics.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
// Note: we're missing the "compiler is in code completion mode" diagnostic,
6464
// which is probably just as well.
6565
// RUN: %sourcekitd-test -req=track-compiles == -req=complete -offset=0 %s -- %s | %FileCheck %s -check-prefix=NODIAGS
66-
// RUN: %sourcekitd-test -req=track-compiles == -req=complete -pos=2:1 %S/Inputs/sema-error.swift -- %S/Inputs/sema-error.swift | %FileCheck %s -check-prefix=SEMA
66+
// RUN: %sourcekitd-test -req=track-compiles == -req=complete -pos=2:1 %S/Inputs/sema-error.swift -- %S/Inputs/sema-error.swift | %FileCheck %s -check-prefix=NODIAGS
6767

6868
// FIXME: invalid arguments cause us to early-exit and not send the notifications
6969
// RUN_DISABLED: %sourcekitd-test -req=track-compiles == -req=sema %s -- %s -invalid-arg | %FileCheck %s -check-prefix=INVALID_ARG

tools/SourceKit/lib/SwiftLang/SwiftCompletion.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ static bool swiftCodeCompleteImpl(
212212
SwiftConsumer.setContext(&CI.getASTContext(), &Invocation,
213213
&CompletionContext);
214214
registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator);
215-
CI.performSema();
215+
CI.performParseAndResolveImportsOnly();
216216
SwiftConsumer.clearContext();
217217

218218
return true;

tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ static bool swiftConformingMethodListImpl(
8282
return true;
8383
}
8484
registerIDERequestFunctions(CI.getASTContext().evaluator);
85-
CI.performSema();
85+
CI.performParseAndResolveImportsOnly();
8686

8787
return true;
8888
}

tools/SourceKit/lib/SwiftLang/SwiftTypeContextInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang,
8181
return true;
8282
}
8383
registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator);
84-
CI.performSema();
84+
CI.performParseAndResolveImportsOnly();
8585

8686
return true;
8787
}

tools/swift-ide-test/swift-ide-test.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ static int doTypeContextInfo(const CompilerInvocation &InitInvok,
766766
if (CI.setup(Invocation))
767767
return 1;
768768
registerIDERequestFunctions(CI.getASTContext().evaluator);
769-
CI.performSema();
769+
CI.performParseAndResolveImportsOnly();
770770
return 0;
771771
}
772772

@@ -831,7 +831,7 @@ doConformingMethodList(const CompilerInvocation &InitInvok,
831831
if (CI.setup(Invocation))
832832
return 1;
833833
registerIDERequestFunctions(CI.getASTContext().evaluator);
834-
CI.performSema();
834+
CI.performParseAndResolveImportsOnly();
835835
return 0;
836836
}
837837

@@ -908,7 +908,7 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok,
908908
if (CI.setup(Invocation))
909909
return 1;
910910
registerIDERequestFunctions(CI.getASTContext().evaluator);
911-
CI.performSema();
911+
CI.performParseAndResolveImportsOnly();
912912
return 0;
913913
}
914914

0 commit comments

Comments
 (0)