mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[IDE] Avoid name binding in sourcekitd's syntactic requests
It looks like we recently started binding extensions to their nominals in order to continue to compute access levels via ValueDecl::getFormalAccess() after an assertion was added to enforce that bindExtensions had been called before anything tried to call ExtensionDecl::getBoundNominal() - which getFormalAccess() depends on. Sourcekitd's syntactic requests are made on every keypress in the editor though, so we shouldn't do any name binding (which may require module loading) to keep them as fast as possible. This patch restores the old inferAccessLevel() functions we used prior to the switch to ValueDecl::getFormalAccess() (plus a few fixes) that does as much as it can syntactically, without any name binding, and simply doesn't report the access level in cases where it couldn't be computed without name-binding. This also fixes an assertion hit we were getting trying to bind extensions in inactive ifconfig clauses, which ASTScope doesn't support. Resolves rdar://problem/57202584
This commit is contained in:
@@ -1120,6 +1120,73 @@ static UIdent getAccessLevelUID(AccessLevel Access) {
|
||||
llvm_unreachable("Unhandled access level in switch.");
|
||||
}
|
||||
|
||||
static Optional<AccessLevel>
|
||||
inferDefaultAccessSyntactically(const ExtensionDecl *ED) {
|
||||
// Check if the extension has an explicit access control attribute.
|
||||
if (auto *AA = ED->getAttrs().getAttribute<AccessControlAttr>())
|
||||
return std::min(std::max(AA->getAccess(), AccessLevel::FilePrivate),
|
||||
AccessLevel::Public);
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Document structure is a purely syntactic request that shouldn't require name lookup
|
||||
/// or type-checking, so this is a best-effort computation, particularly where extensions
|
||||
/// are concerned.
|
||||
static Optional<AccessLevel> inferAccessSyntactically(const ValueDecl *D) {
|
||||
assert(D);
|
||||
|
||||
// Check if the decl has an explicit access control attribute.
|
||||
if (auto *AA = D->getAttrs().getAttribute<AccessControlAttr>())
|
||||
return AA->getAccess();
|
||||
|
||||
DeclContext *DC = D->getDeclContext();
|
||||
|
||||
if (D->getKind() == DeclKind::Destructor ||
|
||||
D->getKind() == DeclKind::EnumElement) {
|
||||
if (auto container = dyn_cast<NominalTypeDecl>(D->getDeclContext()))
|
||||
return std::max(container->getFormalAccess(), AccessLevel::Internal);
|
||||
return AccessLevel::Private;
|
||||
}
|
||||
|
||||
switch (DC->getContextKind()) {
|
||||
case DeclContextKind::TopLevelCodeDecl:
|
||||
return AccessLevel::FilePrivate;
|
||||
case DeclContextKind::SerializedLocal:
|
||||
case DeclContextKind::AbstractClosureExpr:
|
||||
case DeclContextKind::EnumElementDecl:
|
||||
case DeclContextKind::Initializer:
|
||||
case DeclContextKind::AbstractFunctionDecl:
|
||||
case DeclContextKind::SubscriptDecl:
|
||||
return AccessLevel::Private;
|
||||
case DeclContextKind::Module:
|
||||
case DeclContextKind::FileUnit:
|
||||
return AccessLevel::Internal;
|
||||
case DeclContextKind::GenericTypeDecl: {
|
||||
auto generic = cast<GenericTypeDecl>(DC);
|
||||
AccessLevel access = AccessLevel::Internal;
|
||||
if (isa<ProtocolDecl>(generic)) {
|
||||
if (auto protoAccess = inferAccessSyntactically(generic))
|
||||
access = std::max(AccessLevel::FilePrivate, protoAccess.getValue());
|
||||
}
|
||||
return access;
|
||||
}
|
||||
case DeclContextKind::ExtensionDecl:
|
||||
auto *ED = cast<ExtensionDecl>(DC);
|
||||
return inferDefaultAccessSyntactically(ED);
|
||||
}
|
||||
|
||||
llvm_unreachable("Unhandled DeclContextKind in switch.");
|
||||
}
|
||||
|
||||
static Optional<AccessLevel>
|
||||
inferSetterAccessSyntactically(const AbstractStorageDecl *D) {
|
||||
if (!D->isSettable(/*UseDC=*/nullptr))
|
||||
return None;
|
||||
if (auto *AA = D->getAttrs().getAttribute<SetterAccessAttr>())
|
||||
return AA->getAccess();
|
||||
return inferAccessSyntactically(D);
|
||||
}
|
||||
|
||||
class SwiftDocumentStructureWalker: public ide::SyntaxModelWalker {
|
||||
SourceManager &SrcManager;
|
||||
EditorConsumer &Consumer;
|
||||
@@ -1176,16 +1243,15 @@ public:
|
||||
Node.Kind != SyntaxStructureKind::LocalVariable &&
|
||||
Node.Kind != SyntaxStructureKind::GenericTypeParam) {
|
||||
if (auto *VD = dyn_cast_or_null<ValueDecl>(Node.Dcl)) {
|
||||
AccessLevel = getAccessLevelUID(VD->getFormalAccess());
|
||||
if (auto Access = inferAccessSyntactically(VD))
|
||||
AccessLevel = getAccessLevelUID(Access.getValue());
|
||||
} else if (auto *ED = dyn_cast_or_null<ExtensionDecl>(Node.Dcl)) {
|
||||
auto StrictAccess = ED->getDefaultAccessLevel();
|
||||
AccessLevel = getAccessLevelUID(StrictAccess);
|
||||
if (auto DefaultAccess = inferDefaultAccessSyntactically(ED))
|
||||
AccessLevel = getAccessLevelUID(DefaultAccess.getValue());
|
||||
}
|
||||
if (auto *ASD = dyn_cast_or_null<AbstractStorageDecl>(Node.Dcl)) {
|
||||
if (ASD->isSettable(/*UseDC=*/nullptr)) {
|
||||
swift::AccessLevel SetAccess = ASD->getSetterFormalAccess();
|
||||
SetterAccessLevel = getAccessLevelUID(SetAccess);
|
||||
}
|
||||
if (auto SetAccess = inferSetterAccessSyntactically(ASD))
|
||||
SetterAccessLevel = getAccessLevelUID(SetAccess.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user