[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:
Nathan Hawes
2019-11-15 11:44:54 -08:00
parent 77daaf3338
commit 11d20b8c92
11 changed files with 353 additions and 61 deletions

View File

@@ -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());
}
}