[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
This commit is contained in:
Rintaro Ishizaki
2019-10-30 17:13:16 -07:00
parent 9203080c0e
commit 2564a6e494
13 changed files with 109 additions and 39 deletions

View File

@@ -46,6 +46,8 @@ namespace swift {
class ValueDecl;
struct PrintOptions;
void bindExtensions(SourceFile &SF);
/// Typecheck binding initializer at \p bindingIndex.
void typeCheckPatternBinding(PatternBindingDecl *PBD, unsigned bindingIndex);

View File

@@ -845,8 +845,13 @@ void CompilerInstance::parseAndCheckTypesUpTo(
// If the limiting AST stage is name binding, we're done.
if (limitStage <= SourceFile::NameBound) {
if (Invocation.isCodeCompletion()) {
performCodeCompletionSecondPass(*PersistentState.get(),
*Invocation.getCodeCompletionFactory());
}
return;
}
assert(!Invocation.isCodeCompletion());
const auto &options = Invocation.getFrontendOptions();
forEachFileToTypeCheck([&](SourceFile &SF) {
@@ -870,10 +875,6 @@ void CompilerInstance::parseAndCheckTypesUpTo(
}
});
if (Invocation.isCodeCompletion()) {
performCodeCompletionSecondPass(*PersistentState.get(),
*Invocation.getCodeCompletionFactory());
}
finishTypeChecking(TypeCheckOptions);
}

View File

@@ -4138,10 +4138,47 @@ public:
void addAccessControl(const ValueDecl *VD,
CodeCompletionResultBuilder &Builder) {
assert(CurrDeclContext->getSelfNominalTypeDecl());
auto AccessOfContext =
CurrDeclContext->getSelfNominalTypeDecl()->getFormalAccess();
auto Access = std::min(VD->getFormalAccess(), AccessOfContext);
auto CurrentNominal = CurrDeclContext->getSelfNominalTypeDecl();
assert(CurrentNominal);
auto AccessOfContext = CurrentNominal->getFormalAccess();
if (AccessOfContext < AccessLevel::Public)
return;
auto Access = VD->getFormalAccess();
// Use the greater access between the protocol requirement and the witness.
// In case of:
//
// public protocol P { func foo() }
// public class B { func foo() {} }
// public class C: B, P {
// <complete>
// }
//
// 'VD' is 'B.foo()' which is implicitly 'internal'. But as the overriding
// declaration, the user needs to write both 'public' and 'override':
//
// public class C: B {
// public override func foo() {}
// }
if (Access < AccessLevel::Public &&
!isa<ProtocolDecl>(VD->getDeclContext())) {
for (auto Conformance : CurrentNominal->getAllConformances()) {
auto Proto = Conformance->getProtocol();
for (auto Member : Proto->getMembers()) {
auto Requirement = dyn_cast<ValueDecl>(Member);
if (!Requirement || !Requirement->isProtocolRequirement() ||
isa<AssociatedTypeDecl>(Requirement))
continue;
auto Witness = Conformance->getWitnessDecl(Requirement);
if (Witness == VD)
Access = std::max(Access, Requirement->getFormalAccess());
}
}
}
Access = std::min(Access, AccessOfContext);
// Only emit 'public', not needed otherwise.
if (Access >= AccessLevel::Public)
Builder.addAccessControlKeyword(Access);
@@ -4364,9 +4401,11 @@ public:
if (D->shouldHideFromEditor())
return;
if (D->isFinal() ||
if (D->isFinal())
return;
// A 'class' member with an initial value cannot be overriden either.
(D->isStatic() && D->getAttrs().hasAttribute<HasInitialValueAttr>()))
if (D->isStatic() && isa<VarDecl>(D) && cast<VarDecl>(D)->hasInitialValue())
return;
bool hasIntroducer = hasFuncIntroducer ||
@@ -4955,13 +4994,16 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
case CompletionKind::TypeIdentifierWithoutDot:
break;
case CompletionKind::TypeDeclResultBeginning:
if (!isa<ProtocolDecl>(CurDeclContext))
if (CurDeclContext->isTypeContext() ||
(ParsedDecl && isa<FuncDecl>(ParsedDecl)))
case CompletionKind::TypeDeclResultBeginning: {
auto DC = CurDeclContext;
if (ParsedDecl && ParsedDecl == CurDeclContext->getAsDecl())
DC = ParsedDecl->getDeclContext();
if (!isa<ProtocolDecl>(DC))
if (DC->isTypeContext() || (ParsedDecl && isa<FuncDecl>(ParsedDecl)))
addOpaqueTypeKeyword(Sink);
LLVM_FALLTHROUGH;
}
case CompletionKind::TypeSimpleBeginning:
addAnyTypeKeyword(Sink);
break;
@@ -5130,8 +5172,6 @@ void CodeCompletionCallbacksImpl::doneParsing() {
CD->getContextKind() == DeclContextKind::TopLevelCodeDecl)
MaybeFuncBody = false;
}
// Add keywords even if type checking fails completely.
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
if (auto *DC = dyn_cast_or_null<DeclContext>(ParsedDecl)) {
if (DC->isChildContextOf(CurDeclContext))
@@ -5142,6 +5182,9 @@ void CodeCompletionCallbacksImpl::doneParsing() {
CurDeclContext,
CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc());
// Add keywords even if type checking fails completely.
addKeywords(CompletionContext.getResultSink(), MaybeFuncBody);
Optional<Type> ExprType;
ConcreteDeclRef ReferencedDecl = nullptr;
if (ParsedExpr) {

View File

@@ -22,6 +22,7 @@
#include "swift/AST/Module.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/Stmt.h"
#include "swift/AST/Type.h"
#include "swift/AST/Types.h"
@@ -61,10 +62,9 @@ void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) {
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)) {
PBD->getPattern(i)->forEachVariable([](VarDecl *VD) {
(void) VD->getInterfaceType();
});
if (!PBD->isInitializerChecked(i))
typeCheckPatternBinding(PBD, i);
}
@@ -91,16 +91,36 @@ void typeCheckContextImpl(DeclContext *DC, SourceLoc Loc) {
} // anonymous namespace
void swift::ide::typeCheckContextUntil(DeclContext *DC, SourceLoc Loc) {
// The only time we have to explicitly check a TopLevelCodeDecl
// is when we're directly inside of one. In this case,
// performTypeChecking() did not type check it for us.
// Lookup the swift module. This ensures that we record all known
// protocols in the AST.
(void) DC->getASTContext().getStdlibModule();
bindExtensions(*DC->getParentSourceFile());
while (isa<AbstractClosureExpr>(DC))
DC = DC->getParent();
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(DC))
typeCheckTopLevelCodeDecl(TLCD);
else
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(DC)) {
// Typecheck all 'TopLevelCodeDecl's up to the target one.
// In theory, this is not needed, but it fails to resolve the type of
// 'guard'ed variable. e.g.
//
// guard value = something() else { fatalError() }
// <complete>
// Here, 'value' is '<error type>' unless we explicitly typecheck the
// 'guard' statement.
SourceFile *SF = DC->getParentSourceFile();
for (auto *D : SF->Decls) {
if (auto Code = dyn_cast<TopLevelCodeDecl>(D)) {
typeCheckTopLevelCodeDecl(Code);
if (Code == TLCD)
break;
}
}
} else {
typeCheckContextImpl(DC, Loc);
}
}
//===----------------------------------------------------------------------===//
// findParsedExpr(DeclContext, Expr)

View File

@@ -398,7 +398,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC,
// Resolve extensions. This has to occur first during type checking,
// because the extensions need to be wired into the AST for name lookup
// to work.
bindExtensions(SF);
::bindExtensions(SF);
// Type check the top-level elements of the source file.
for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) {
@@ -739,3 +739,7 @@ TypeChecker::getDeclTypeCheckingSemantics(ValueDecl *decl) {
}
return DeclTypeCheckingSemantics::Normal;
}
void swift::bindExtensions(SourceFile &SF) {
::bindExtensions(SF);
}

View File

@@ -491,7 +491,7 @@ class SemanticContextDerived1 : SemanticContextBase1 {
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}}
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_2-NEXT: End completions
}
func instanceFunc1() {
override func instanceFunc1() {
#^SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3^#
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3: Begin completions
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_3-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc1()[#Void#]{{; name=.+$}}
@@ -504,7 +504,7 @@ class SemanticContextDerived1 : SemanticContextBase1 {
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}}
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_4-NEXT: End completions
}
func instanceFunc1(_ a: Int) {
override func instanceFunc1(_ a: Int) {
super.#^SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5^#
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5: Begin completions
// SEMANTIC_CONTEXT_OVERRIDDEN_DECL_5-NEXT: Decl[InstanceMethod]/CurrNominal: instanceFunc1()[#Void#]{{; name=.+$}}

View File

@@ -75,8 +75,8 @@
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_TUPLE_1 | %FileCheck %s -check-prefix=IN_TUPLE_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=IN_TUPLE_2 | %FileCheck %s -check-prefix=IN_TUPLE_2
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_1 | %FileCheck %s -check-prefix=OWN_INIT_1
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_2 | %FileCheck %s -check-prefix=OWN_INIT_2
// 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
// 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
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_3 | %FileCheck %s -check-prefix=OWN_INIT_3
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_4 | %FileCheck %s -check-prefix=OWN_INIT_4
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=OWN_INIT_5 | %FileCheck %s -check-prefix=OWN_INIT_5

View File

@@ -32,7 +32,7 @@ class Base {
}
class Derived: Base {
func foo() {}
override func foo() {}
}
func testOverrideUSR() {

View File

@@ -63,7 +63,7 @@
// Note: we're missing the "compiler is in code completion mode" diagnostic,
// which is probably just as well.
// RUN: %sourcekitd-test -req=track-compiles == -req=complete -offset=0 %s -- %s | %FileCheck %s -check-prefix=NODIAGS
// 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
// 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
// FIXME: invalid arguments cause us to early-exit and not send the notifications
// RUN_DISABLED: %sourcekitd-test -req=track-compiles == -req=sema %s -- %s -invalid-arg | %FileCheck %s -check-prefix=INVALID_ARG

View File

@@ -212,7 +212,7 @@ static bool swiftCodeCompleteImpl(
SwiftConsumer.setContext(&CI.getASTContext(), &Invocation,
&CompletionContext);
registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
CI.performParseAndResolveImportsOnly();
SwiftConsumer.clearContext();
return true;

View File

@@ -82,7 +82,7 @@ static bool swiftConformingMethodListImpl(
return true;
}
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
CI.performParseAndResolveImportsOnly();
return true;
}

View File

@@ -81,7 +81,7 @@ static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang,
return true;
}
registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
CI.performParseAndResolveImportsOnly();
return true;
}

View File

@@ -766,7 +766,7 @@ static int doTypeContextInfo(const CompilerInvocation &InitInvok,
if (CI.setup(Invocation))
return 1;
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
CI.performParseAndResolveImportsOnly();
return 0;
}
@@ -831,7 +831,7 @@ doConformingMethodList(const CompilerInvocation &InitInvok,
if (CI.setup(Invocation))
return 1;
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
CI.performParseAndResolveImportsOnly();
return 0;
}
@@ -908,7 +908,7 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok,
if (CI.setup(Invocation))
return 1;
registerIDERequestFunctions(CI.getASTContext().evaluator);
CI.performSema();
CI.performParseAndResolveImportsOnly();
return 0;
}