mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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=.+$}}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -32,7 +32,7 @@ class Base {
|
||||
}
|
||||
|
||||
class Derived: Base {
|
||||
func foo() {}
|
||||
override func foo() {}
|
||||
}
|
||||
|
||||
func testOverrideUSR() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -82,7 +82,7 @@ static bool swiftConformingMethodListImpl(
|
||||
return true;
|
||||
}
|
||||
registerIDERequestFunctions(CI.getASTContext().evaluator);
|
||||
CI.performSema();
|
||||
CI.performParseAndResolveImportsOnly();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang,
|
||||
return true;
|
||||
}
|
||||
registerIDETypeCheckRequestFunctions(CI.getASTContext().evaluator);
|
||||
CI.performSema();
|
||||
CI.performParseAndResolveImportsOnly();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user