mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
4049 lines
146 KiB
C++
4049 lines
146 KiB
C++
//===--- NameLookup.cpp - Swift Name Lookup Routines ----------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements interfaces for performing name lookup.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/ASTScope.h"
|
|
#include "swift/AST/ASTVisitor.h"
|
|
#include "swift/AST/ClangModuleLoader.h"
|
|
#include "swift/AST/DebuggerClient.h"
|
|
#include "swift/AST/ExistentialLayout.h"
|
|
#include "swift/AST/LazyResolver.h"
|
|
#include "swift/AST/Initializer.h"
|
|
#include "swift/AST/NameLookupRequests.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/ReferencedNameTracker.h"
|
|
#include "swift/Basic/SourceManager.h"
|
|
#include "swift/Basic/Statistic.h"
|
|
#include "swift/Basic/STLExtras.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/TinyPtrVector.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#define DEBUG_TYPE "namelookup"
|
|
|
|
using namespace swift;
|
|
using namespace swift::namelookup;
|
|
|
|
void VisibleDeclConsumer::anchor() {}
|
|
void VectorDeclConsumer::anchor() {}
|
|
void NamedDeclConsumer::anchor() {}
|
|
|
|
ValueDecl *LookupResultEntry::getBaseDecl() const {
|
|
if (BaseDC == nullptr)
|
|
return nullptr;
|
|
|
|
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(BaseDC))
|
|
return AFD->getImplicitSelfDecl();
|
|
|
|
if (auto *PBI = dyn_cast<PatternBindingInitializer>(BaseDC)) {
|
|
auto *selfDecl = PBI->getImplicitSelfDecl();
|
|
assert(selfDecl);
|
|
return selfDecl;
|
|
}
|
|
|
|
auto *nominalDecl = BaseDC->getSelfNominalTypeDecl();
|
|
assert(nominalDecl);
|
|
return nominalDecl;
|
|
}
|
|
|
|
void DebuggerClient::anchor() {}
|
|
|
|
void AccessFilteringDeclConsumer::foundDecl(ValueDecl *D,
|
|
DeclVisibilityKind reason) {
|
|
if (D->isInvalid())
|
|
return;
|
|
if (!D->isAccessibleFrom(DC))
|
|
return;
|
|
|
|
ChainedConsumer.foundDecl(D, reason);
|
|
}
|
|
|
|
|
|
template <typename Fn>
|
|
static void forAllVisibleModules(const DeclContext *DC, const Fn &fn) {
|
|
DeclContext *moduleScope = DC->getModuleScopeContext();
|
|
if (auto file = dyn_cast<FileUnit>(moduleScope))
|
|
file->forAllVisibleModules(fn);
|
|
else
|
|
cast<ModuleDecl>(moduleScope)->forAllVisibleModules(ModuleDecl::AccessPathTy(), fn);
|
|
}
|
|
|
|
bool swift::removeOverriddenDecls(SmallVectorImpl<ValueDecl*> &decls) {
|
|
if (decls.size() < 2)
|
|
return false;
|
|
|
|
llvm::SmallPtrSet<ValueDecl*, 8> overridden;
|
|
for (auto decl : decls) {
|
|
// Don't look at the overrides of operators in protocols. The global
|
|
// lookup of operators means that we can find overriding operators that
|
|
// aren't relevant to the types in hand, and will fail to type check.
|
|
if (isa<ProtocolDecl>(decl->getDeclContext())) {
|
|
if (auto func = dyn_cast<FuncDecl>(decl))
|
|
if (func->isOperator())
|
|
continue;
|
|
}
|
|
|
|
while (auto overrides = decl->getOverriddenDecl()) {
|
|
overridden.insert(overrides);
|
|
|
|
// Because initializers from Objective-C base classes have greater
|
|
// visibility than initializers written in Swift classes, we can
|
|
// have a "break" in the set of declarations we found, where
|
|
// C.init overrides B.init overrides A.init, but only C.init and
|
|
// A.init are in the chain. Make sure we still remove A.init from the
|
|
// set in this case.
|
|
if (decl->getFullName().getBaseName() == DeclBaseName::createConstructor()) {
|
|
/// FIXME: Avoid the possibility of an infinite loop by fixing the root
|
|
/// cause instead (incomplete circularity detection).
|
|
assert(decl != overrides && "Circular class inheritance?");
|
|
decl = overrides;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If no methods were overridden, we're done.
|
|
if (overridden.empty()) return false;
|
|
|
|
// Erase any overridden declarations
|
|
bool anyOverridden = false;
|
|
decls.erase(std::remove_if(decls.begin(), decls.end(),
|
|
[&](ValueDecl *decl) -> bool {
|
|
if (overridden.count(decl) > 0) {
|
|
anyOverridden = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}),
|
|
decls.end());
|
|
|
|
return anyOverridden;
|
|
}
|
|
|
|
enum class ConstructorComparison {
|
|
Worse,
|
|
Same,
|
|
Better,
|
|
};
|
|
|
|
/// Determines whether \p ctor1 is a "better" initializer than \p ctor2.
|
|
static ConstructorComparison compareConstructors(ConstructorDecl *ctor1,
|
|
ConstructorDecl *ctor2,
|
|
const swift::ASTContext &ctx) {
|
|
bool available1 = !ctor1->getAttrs().isUnavailable(ctx);
|
|
bool available2 = !ctor2->getAttrs().isUnavailable(ctx);
|
|
|
|
// An unavailable initializer is always worse than an available initializer.
|
|
if (available1 < available2)
|
|
return ConstructorComparison::Worse;
|
|
|
|
if (available1 > available2)
|
|
return ConstructorComparison::Better;
|
|
|
|
CtorInitializerKind kind1 = ctor1->getInitKind();
|
|
CtorInitializerKind kind2 = ctor2->getInitKind();
|
|
|
|
if (kind1 > kind2)
|
|
return ConstructorComparison::Worse;
|
|
|
|
if (kind1 < kind2)
|
|
return ConstructorComparison::Better;
|
|
|
|
return ConstructorComparison::Same;
|
|
}
|
|
|
|
/// Given a set of declarations whose names and signatures have matched,
|
|
/// figure out which of these declarations have been shadowed by others.
|
|
static void recordShadowedDeclsAfterSignatureMatch(
|
|
ArrayRef<ValueDecl *> decls,
|
|
const ModuleDecl *curModule,
|
|
llvm::SmallPtrSetImpl<ValueDecl *> &shadowed) {
|
|
assert(decls.size() > 1 && "Nothing collided");
|
|
|
|
// Compare each declaration to every other declaration. This is
|
|
// unavoidably O(n^2) in the number of declarations, but because they
|
|
// all have the same signature, we expect n to remain small.
|
|
ASTContext &ctx = curModule->getASTContext();
|
|
for (unsigned firstIdx : indices(decls)) {
|
|
auto firstDecl = decls[firstIdx];
|
|
auto firstModule = firstDecl->getModuleContext();
|
|
auto firstSig = firstDecl->getOverloadSignature();
|
|
for (unsigned secondIdx : range(firstIdx + 1, decls.size())) {
|
|
// Determine whether one module takes precedence over another.
|
|
auto secondDecl = decls[secondIdx];
|
|
auto secondModule = secondDecl->getModuleContext();
|
|
|
|
// Swift 4 compatibility hack: Don't shadow properties defined in
|
|
// extensions of generic types with properties defined elsewhere.
|
|
// This is due to the fact that in Swift 4, we only gave custom overload
|
|
// types to properties in extensions of generic types, otherwise we
|
|
// used the null type.
|
|
if (!ctx.isSwiftVersionAtLeast(5)) {
|
|
auto secondSig = secondDecl->getOverloadSignature();
|
|
if (firstSig.IsVariable && secondSig.IsVariable)
|
|
if (firstSig.InExtensionOfGenericType !=
|
|
secondSig.InExtensionOfGenericType)
|
|
continue;
|
|
}
|
|
|
|
// If one declaration is in a protocol or extension thereof and the
|
|
// other is not, prefer the one that is not.
|
|
if ((bool)firstDecl->getDeclContext()->getSelfProtocolDecl() !=
|
|
(bool)secondDecl->getDeclContext()->getSelfProtocolDecl()) {
|
|
if (firstDecl->getDeclContext()->getSelfProtocolDecl()) {
|
|
shadowed.insert(firstDecl);
|
|
break;
|
|
} else {
|
|
shadowed.insert(secondDecl);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// If one declaration is available and the other is not, prefer the
|
|
// available one.
|
|
if (firstDecl->getAttrs().isUnavailable(ctx) !=
|
|
secondDecl->getAttrs().isUnavailable(ctx)) {
|
|
if (firstDecl->getAttrs().isUnavailable(ctx)) {
|
|
shadowed.insert(firstDecl);
|
|
break;
|
|
} else {
|
|
shadowed.insert(secondDecl);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Don't apply module-shadowing rules to members of protocol types.
|
|
if (isa<ProtocolDecl>(firstDecl->getDeclContext()) ||
|
|
isa<ProtocolDecl>(secondDecl->getDeclContext()))
|
|
continue;
|
|
|
|
// Prefer declarations in the current module over those in another
|
|
// module.
|
|
// FIXME: This is a hack. We should query a (lazily-built, cached)
|
|
// module graph to determine shadowing.
|
|
if ((firstModule == curModule) != (secondModule == curModule)) {
|
|
// If the first module is the current module, the second declaration
|
|
// is shadowed by the first.
|
|
if (firstModule == curModule) {
|
|
shadowed.insert(secondDecl);
|
|
continue;
|
|
}
|
|
|
|
// Otherwise, the first declaration is shadowed by the second. There is
|
|
// no point in continuing to compare the first declaration to others.
|
|
shadowed.insert(firstDecl);
|
|
break;
|
|
}
|
|
|
|
// Prefer declarations in the any module over those in the standard
|
|
// library module.
|
|
if (auto swiftModule = ctx.getStdlibModule()) {
|
|
if ((firstModule == swiftModule) != (secondModule == swiftModule)) {
|
|
// If the second module is the standard library module, the second
|
|
// declaration is shadowed by the first.
|
|
if (secondModule == swiftModule) {
|
|
shadowed.insert(secondDecl);
|
|
continue;
|
|
}
|
|
|
|
// Otherwise, the first declaration is shadowed by the second. There is
|
|
// no point in continuing to compare the first declaration to others.
|
|
shadowed.insert(firstDecl);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// The Foundation overlay introduced Data.withUnsafeBytes, which is
|
|
// treated as being ambiguous with SwiftNIO's Data.withUnsafeBytes
|
|
// extension. Apply a special-case name shadowing rule to use the
|
|
// latter rather than the former, which be the consequence of a more
|
|
// significant change to name shadowing in the future.
|
|
if (auto owningStruct1
|
|
= firstDecl->getDeclContext()->getSelfStructDecl()) {
|
|
if (auto owningStruct2
|
|
= secondDecl->getDeclContext()->getSelfStructDecl()) {
|
|
if (owningStruct1 == owningStruct2 &&
|
|
owningStruct1->getName().is("Data") &&
|
|
isa<FuncDecl>(firstDecl) && isa<FuncDecl>(secondDecl) &&
|
|
firstDecl->getFullName() == secondDecl->getFullName() &&
|
|
firstDecl->getBaseName().userFacingName() == "withUnsafeBytes") {
|
|
// If the second module is the Foundation module and the first
|
|
// is the NIOFoundationCompat module, the second is shadowed by the
|
|
// first.
|
|
if (firstDecl->getModuleContext()->getName()
|
|
.is("NIOFoundationCompat") &&
|
|
secondDecl->getModuleContext()->getName().is("Foundation")) {
|
|
shadowed.insert(secondDecl);
|
|
continue;
|
|
}
|
|
|
|
// If it's the other way around, the first declaration is shadowed
|
|
// by the second.
|
|
if (secondDecl->getModuleContext()->getName()
|
|
.is("NIOFoundationCompat") &&
|
|
firstDecl->getModuleContext()->getName().is("Foundation")) {
|
|
shadowed.insert(firstDecl);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prefer declarations in an overlay to similar declarations in
|
|
// the Clang module it customizes.
|
|
if (firstDecl->hasClangNode() != secondDecl->hasClangNode()) {
|
|
auto clangLoader = ctx.getClangModuleLoader();
|
|
if (!clangLoader) continue;
|
|
|
|
if (clangLoader->isInOverlayModuleForImportedModule(
|
|
firstDecl->getDeclContext(),
|
|
secondDecl->getDeclContext())) {
|
|
shadowed.insert(secondDecl);
|
|
continue;
|
|
}
|
|
|
|
if (clangLoader->isInOverlayModuleForImportedModule(
|
|
secondDecl->getDeclContext(),
|
|
firstDecl->getDeclContext())) {
|
|
shadowed.insert(firstDecl);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Look through the given set of declarations (that all have the same name),
|
|
/// recording those that are shadowed by another declaration in the
|
|
/// \c shadowed set.
|
|
static void recordShadowDeclsAfterObjCInitMatch(
|
|
ArrayRef<ConstructorDecl *> ctors,
|
|
llvm::SmallPtrSetImpl<ValueDecl *> &shadowed) {
|
|
assert(ctors.size() > 1 && "No collisions");
|
|
|
|
ASTContext &ctx = ctors.front()->getASTContext();
|
|
|
|
// Find the "best" constructor with this signature.
|
|
ConstructorDecl *bestCtor = ctors[0];
|
|
for (auto ctor : ctors.slice(1)) {
|
|
auto comparison = compareConstructors(ctor, bestCtor, ctx);
|
|
if (comparison == ConstructorComparison::Better)
|
|
bestCtor = ctor;
|
|
}
|
|
|
|
// Shadow any initializers that are worse.
|
|
for (auto ctor : ctors) {
|
|
auto comparison = compareConstructors(ctor, bestCtor, ctx);
|
|
if (comparison == ConstructorComparison::Worse)
|
|
shadowed.insert(ctor);
|
|
}
|
|
}
|
|
|
|
/// Look through the given set of declarations (that all have the same name),
|
|
/// recording those that are shadowed by another declaration in the
|
|
/// \c shadowed set.
|
|
static void recordShadowedDecls(ArrayRef<ValueDecl *> decls,
|
|
const ModuleDecl *curModule,
|
|
llvm::SmallPtrSetImpl<ValueDecl *> &shadowed) {
|
|
if (decls.size() < 2)
|
|
return;
|
|
|
|
auto typeResolver = decls[0]->getASTContext().getLazyResolver();
|
|
|
|
// Categorize all of the declarations based on their overload signatures.
|
|
llvm::SmallDenseMap<CanType, llvm::TinyPtrVector<ValueDecl *>> collisions;
|
|
llvm::SmallVector<CanType, 2> collisionTypes;
|
|
llvm::SmallDenseMap<NominalTypeDecl *, llvm::TinyPtrVector<ConstructorDecl *>>
|
|
objCInitializerCollisions;
|
|
llvm::TinyPtrVector<NominalTypeDecl *> objCInitializerCollisionNominals;
|
|
|
|
for (auto decl : decls) {
|
|
// Specifically keep track of Objective-C initializers, which can come from
|
|
// either init methods or factory methods.
|
|
if (decl->hasClangNode()) {
|
|
if (auto ctor = dyn_cast<ConstructorDecl>(decl)) {
|
|
auto nominal = ctor->getDeclContext()->getSelfNominalTypeDecl();
|
|
auto &knownInits = objCInitializerCollisions[nominal];
|
|
if (knownInits.size() == 1) {
|
|
objCInitializerCollisionNominals.push_back(nominal);
|
|
}
|
|
knownInits.push_back(ctor);
|
|
}
|
|
}
|
|
|
|
CanType signature;
|
|
|
|
if (!isa<TypeDecl>(decl)) {
|
|
// We need an interface type here.
|
|
if (typeResolver)
|
|
typeResolver->resolveDeclSignature(decl);
|
|
|
|
// If the decl is currently being validated, this is likely a recursive
|
|
// reference and we'll want to skip ahead so as to avoid having its type
|
|
// attempt to desugar itself.
|
|
if (!decl->hasValidSignature())
|
|
continue;
|
|
|
|
// FIXME: the canonical type makes a poor signature, because we don't
|
|
// canonicalize away default arguments.
|
|
signature = decl->getInterfaceType()->getCanonicalType();
|
|
|
|
// FIXME: The type of a variable or subscript doesn't include
|
|
// enough context to distinguish entities from different
|
|
// constrained extensions, so use the overload signature's
|
|
// type. This is layering a partial fix upon a total hack.
|
|
if (auto asd = dyn_cast<AbstractStorageDecl>(decl))
|
|
signature = asd->getOverloadSignatureType();
|
|
} else if (decl->getDeclContext()->isTypeContext()) {
|
|
// Do not apply shadowing rules for member types.
|
|
continue;
|
|
}
|
|
|
|
// Record this declaration based on its signature.
|
|
auto &known = collisions[signature];
|
|
if (known.size() == 1) {
|
|
collisionTypes.push_back(signature);
|
|
}
|
|
known.push_back(decl);
|
|
}
|
|
|
|
// Check whether we have shadowing for signature collisions.
|
|
for (auto signature : collisionTypes) {
|
|
recordShadowedDeclsAfterSignatureMatch(collisions[signature], curModule,
|
|
shadowed);
|
|
}
|
|
|
|
// Check whether we have shadowing for Objective-C initializer collisions.
|
|
for (auto nominal : objCInitializerCollisionNominals) {
|
|
recordShadowDeclsAfterObjCInitMatch(objCInitializerCollisions[nominal],
|
|
shadowed);
|
|
}
|
|
}
|
|
|
|
bool swift::removeShadowedDecls(SmallVectorImpl<ValueDecl*> &decls,
|
|
const ModuleDecl *curModule) {
|
|
// Collect declarations with the same (full) name.
|
|
llvm::SmallDenseMap<DeclName, llvm::TinyPtrVector<ValueDecl *>>
|
|
collidingDeclGroups;
|
|
bool anyCollisions = false;
|
|
for (auto decl : decls) {
|
|
// Record this declaration based on its full name.
|
|
auto &knownDecls = collidingDeclGroups[decl->getFullName()];
|
|
if (!knownDecls.empty())
|
|
anyCollisions = true;
|
|
|
|
knownDecls.push_back(decl);
|
|
}
|
|
|
|
// If nothing collided, we're done.
|
|
if (!anyCollisions)
|
|
return false;
|
|
|
|
// Walk through the declarations again, marking any declarations that shadow.
|
|
llvm::SmallPtrSet<ValueDecl *, 4> shadowed;
|
|
for (auto decl : decls) {
|
|
auto known = collidingDeclGroups.find(decl->getFullName());
|
|
if (known == collidingDeclGroups.end()) {
|
|
// We already handled this group.
|
|
continue;
|
|
}
|
|
|
|
recordShadowedDecls(known->second, curModule, shadowed);
|
|
collidingDeclGroups.erase(known);
|
|
}
|
|
|
|
// If no declarations were shadowed, we're done.
|
|
if (shadowed.empty())
|
|
return false;
|
|
|
|
// Remove shadowed declarations from the list of declarations.
|
|
bool anyRemoved = false;
|
|
decls.erase(std::remove_if(decls.begin(), decls.end(),
|
|
[&](ValueDecl *vd) {
|
|
if (shadowed.count(vd) > 0) {
|
|
anyRemoved = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}),
|
|
decls.end());
|
|
|
|
return anyRemoved;
|
|
}
|
|
|
|
namespace {
|
|
enum class DiscriminatorMatch {
|
|
NoDiscriminator,
|
|
Matches,
|
|
Different
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
static DiscriminatorMatch matchDiscriminator(Identifier discriminator,
|
|
const ValueDecl *value) {
|
|
if (value->getFormalAccess() > AccessLevel::FilePrivate)
|
|
return DiscriminatorMatch::NoDiscriminator;
|
|
|
|
auto containingFile =
|
|
dyn_cast<FileUnit>(value->getDeclContext()->getModuleScopeContext());
|
|
if (!containingFile)
|
|
return DiscriminatorMatch::Different;
|
|
|
|
if (discriminator == containingFile->getDiscriminatorForPrivateValue(value))
|
|
return DiscriminatorMatch::Matches;
|
|
|
|
return DiscriminatorMatch::Different;
|
|
}
|
|
|
|
static DiscriminatorMatch
|
|
matchDiscriminator(Identifier discriminator,
|
|
LookupResultEntry lookupResult) {
|
|
return matchDiscriminator(discriminator, lookupResult.getValueDecl());
|
|
}
|
|
|
|
template <typename Result>
|
|
static void filterForDiscriminator(SmallVectorImpl<Result> &results,
|
|
DebuggerClient *debugClient) {
|
|
Identifier discriminator = debugClient->getPreferredPrivateDiscriminator();
|
|
if (discriminator.empty())
|
|
return;
|
|
|
|
auto lastMatchIter = std::find_if(results.rbegin(), results.rend(),
|
|
[discriminator](Result next) -> bool {
|
|
return
|
|
matchDiscriminator(discriminator, next) == DiscriminatorMatch::Matches;
|
|
});
|
|
if (lastMatchIter == results.rend())
|
|
return;
|
|
|
|
Result lastMatch = *lastMatchIter;
|
|
|
|
auto newEnd = std::remove_if(results.begin(), lastMatchIter.base()-1,
|
|
[discriminator](Result next) -> bool {
|
|
return
|
|
matchDiscriminator(discriminator, next) == DiscriminatorMatch::Different;
|
|
});
|
|
results.erase(newEnd, results.end());
|
|
results.push_back(lastMatch);
|
|
}
|
|
|
|
static void recordLookupOfTopLevelName(DeclContext *topLevelContext,
|
|
DeclName name,
|
|
bool isCascading) {
|
|
auto SF = dyn_cast<SourceFile>(topLevelContext);
|
|
if (!SF)
|
|
return;
|
|
auto *nameTracker = SF->getReferencedNameTracker();
|
|
if (!nameTracker)
|
|
return;
|
|
nameTracker->addTopLevelName(name.getBaseName(), isCascading);
|
|
}
|
|
|
|
/// Determine the local declaration visibility key for an \c ASTScope in which
|
|
/// name lookup successfully resolved.
|
|
static DeclVisibilityKind getLocalDeclVisibilityKind(const ASTScope *scope) {
|
|
switch (scope->getKind()) {
|
|
case ASTScopeKind::Preexpanded:
|
|
case ASTScopeKind::SourceFile:
|
|
case ASTScopeKind::TypeDecl:
|
|
case ASTScopeKind::AbstractFunctionDecl:
|
|
case ASTScopeKind::TypeOrExtensionBody:
|
|
case ASTScopeKind::AbstractFunctionBody:
|
|
case ASTScopeKind::DefaultArgument:
|
|
case ASTScopeKind::PatternBinding:
|
|
case ASTScopeKind::IfStmt:
|
|
case ASTScopeKind::GuardStmt:
|
|
case ASTScopeKind::RepeatWhileStmt:
|
|
case ASTScopeKind::ForEachStmt:
|
|
case ASTScopeKind::DoCatchStmt:
|
|
case ASTScopeKind::SwitchStmt:
|
|
case ASTScopeKind::Accessors:
|
|
case ASTScopeKind::TopLevelCode:
|
|
llvm_unreachable("no local declarations?");
|
|
|
|
case ASTScopeKind::ExtensionGenericParams:
|
|
case ASTScopeKind::GenericParams:
|
|
return DeclVisibilityKind::GenericParameter;
|
|
|
|
case ASTScopeKind::AbstractFunctionParams:
|
|
case ASTScopeKind::Closure:
|
|
case ASTScopeKind::PatternInitializer: // lazy var 'self'
|
|
return DeclVisibilityKind::FunctionParameter;
|
|
|
|
case ASTScopeKind::AfterPatternBinding:
|
|
case ASTScopeKind::ConditionalClause:
|
|
case ASTScopeKind::ForEachPattern:
|
|
case ASTScopeKind::BraceStmt:
|
|
case ASTScopeKind::CatchStmt:
|
|
case ASTScopeKind::CaseStmt:
|
|
return DeclVisibilityKind::LocalVariable;
|
|
}
|
|
|
|
llvm_unreachable("Unhandled ASTScopeKind in switch.");
|
|
}
|
|
|
|
/// Retrieve the set of type declarations that are directly referenced from
|
|
/// the given parsed type representation.
|
|
static DirectlyReferencedTypeDecls
|
|
directReferencesForTypeRepr(Evaluator &evaluator,
|
|
ASTContext &ctx, TypeRepr *typeRepr,
|
|
DeclContext *dc);
|
|
|
|
/// Retrieve the set of type declarations that are directly referenced from
|
|
/// the given type.
|
|
static DirectlyReferencedTypeDecls directReferencesForType(Type type);
|
|
|
|
/// Given a set of type declarations, find all of the nominal type declarations
|
|
/// that they reference, looking through typealiases as appropriate.
|
|
static TinyPtrVector<NominalTypeDecl *>
|
|
resolveTypeDeclsToNominal(Evaluator &evaluator,
|
|
ASTContext &ctx,
|
|
ArrayRef<TypeDecl *> typeDecls,
|
|
SmallVectorImpl<ModuleDecl *> &modulesFound,
|
|
bool &anyObject);
|
|
|
|
SelfBounds
|
|
SelfBoundsFromWhereClauseRequest::evaluate(
|
|
Evaluator &evaluator,
|
|
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl) const {
|
|
auto *typeDecl = decl.dyn_cast<TypeDecl *>();
|
|
auto *protoDecl = dyn_cast_or_null<ProtocolDecl>(typeDecl);
|
|
auto *extDecl = decl.dyn_cast<ExtensionDecl *>();
|
|
|
|
DeclContext *dc = protoDecl ? (DeclContext *)protoDecl : (DeclContext *)extDecl;
|
|
|
|
// A protocol or extension 'where' clause can reference associated types of
|
|
// the protocol itself, so we have to start unqualified lookup from 'dc'.
|
|
//
|
|
// However, the right hand side of a 'Self' conformance constraint must be
|
|
// resolved before unqualified lookup into 'dc' can work, so we make an
|
|
// exception here and begin lookup from the parent context instead.
|
|
auto *lookupDC = dc->getParent();
|
|
auto requirements = protoDecl ? protoDecl->getTrailingWhereClause()
|
|
: extDecl->getTrailingWhereClause();
|
|
|
|
ASTContext &ctx = dc->getASTContext();
|
|
|
|
SelfBounds result;
|
|
|
|
if (requirements == nullptr)
|
|
return result;
|
|
|
|
for (const auto &req : requirements->getRequirements()) {
|
|
// We only care about type constraints.
|
|
if (req.getKind() != RequirementReprKind::TypeConstraint)
|
|
continue;
|
|
|
|
// The left-hand side of the type constraint must be 'Self'.
|
|
bool isSelfLHS = false;
|
|
if (auto typeRepr = req.getSubjectRepr()) {
|
|
if (auto identTypeRepr = dyn_cast<SimpleIdentTypeRepr>(typeRepr))
|
|
isSelfLHS = (identTypeRepr->getIdentifier() == ctx.Id_Self);
|
|
} else if (Type type = req.getSubject()) {
|
|
isSelfLHS = type->isEqual(dc->getSelfInterfaceType());
|
|
}
|
|
if (!isSelfLHS)
|
|
continue;
|
|
|
|
// Resolve the right-hand side.
|
|
DirectlyReferencedTypeDecls rhsDecls;
|
|
if (auto typeRepr = req.getConstraintRepr()) {
|
|
rhsDecls = directReferencesForTypeRepr(evaluator, ctx, typeRepr, lookupDC);
|
|
} else if (Type type = req.getConstraint()) {
|
|
rhsDecls = directReferencesForType(type);
|
|
}
|
|
|
|
SmallVector<ModuleDecl *, 2> modulesFound;
|
|
auto rhsNominals = resolveTypeDeclsToNominal(evaluator, ctx, rhsDecls,
|
|
modulesFound,
|
|
result.anyObject);
|
|
result.decls.insert(result.decls.end(),
|
|
rhsNominals.begin(),
|
|
rhsNominals.end());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
SelfBounds swift::getSelfBoundsFromWhereClause(
|
|
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl) {
|
|
auto *typeDecl = decl.dyn_cast<TypeDecl *>();
|
|
auto *extDecl = decl.dyn_cast<ExtensionDecl *>();
|
|
auto &ctx = typeDecl ? typeDecl->getASTContext()
|
|
: extDecl->getASTContext();
|
|
return evaluateOrDefault(ctx.evaluator,
|
|
SelfBoundsFromWhereClauseRequest{decl}, {});
|
|
}
|
|
|
|
// TODO: change name when ExpUnqualifiedLookup is adopted to
|
|
// populatePlacesToSearchFromContext
|
|
static void
|
|
populateLookupDeclsFromContext(DeclContext *dc,
|
|
SmallVectorImpl<NominalTypeDecl *> &lookupDecls) {
|
|
auto nominal = dc->getSelfNominalTypeDecl();
|
|
if (!nominal)
|
|
return;
|
|
|
|
lookupDecls.push_back(nominal);
|
|
|
|
// For a protocol extension, check whether there are additional "Self"
|
|
// constraints that can affect name lookup.
|
|
if (dc->getExtendedProtocolDecl()) {
|
|
auto ext = cast<ExtensionDecl>(dc);
|
|
auto bounds = getSelfBoundsFromWhereClause(ext);
|
|
for (auto bound : bounds.decls)
|
|
lookupDecls.push_back(bound);
|
|
}
|
|
}
|
|
|
|
TinyPtrVector<TypeDecl *>
|
|
TypeDeclsFromWhereClauseRequest::evaluate(Evaluator &evaluator,
|
|
ExtensionDecl *ext) const {
|
|
ASTContext &ctx = ext->getASTContext();
|
|
|
|
TinyPtrVector<TypeDecl *> result;
|
|
for (const auto &req : ext->getGenericParams()->getTrailingRequirements()) {
|
|
auto resolve = [&](TypeLoc loc) {
|
|
DirectlyReferencedTypeDecls decls;
|
|
if (auto *typeRepr = loc.getTypeRepr())
|
|
decls = directReferencesForTypeRepr(evaluator, ctx, typeRepr, ext);
|
|
else if (Type type = loc.getType())
|
|
decls = directReferencesForType(type);
|
|
|
|
result.insert(result.end(), decls.begin(), decls.end());
|
|
};
|
|
|
|
switch (req.getKind()) {
|
|
case RequirementReprKind::TypeConstraint:
|
|
resolve(req.getSubjectLoc());
|
|
resolve(req.getConstraintLoc());
|
|
break;
|
|
|
|
case RequirementReprKind::SameType:
|
|
resolve(req.getFirstTypeLoc());
|
|
resolve(req.getSecondTypeLoc());
|
|
break;
|
|
|
|
case RequirementReprKind::LayoutConstraint:
|
|
resolve(req.getSubjectLoc());
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// Determine whether unqualified lookup should look at the members of the
|
|
/// given nominal type or extension, vs. only looking at type parameters.
|
|
template<typename D>
|
|
bool shouldLookupMembers(D *decl, SourceLoc loc) {
|
|
// Only look at members of this type (or its inherited types) when
|
|
// inside the body or a protocol's top-level 'where' clause. (Why the
|
|
// 'where' clause? Because that's where you put constraints on
|
|
// inherited associated types.)
|
|
|
|
// When we have no source-location information, we have to perform member
|
|
// lookup.
|
|
if (loc.isInvalid() || decl->getBraces().isInvalid())
|
|
return true;
|
|
|
|
// Within the braces, always look for members.
|
|
auto &ctx = decl->getASTContext();
|
|
if (ctx.SourceMgr.rangeContainsTokenLoc(decl->getBraces(), loc))
|
|
return true;
|
|
|
|
// Within 'where' clause, we can also look for members.
|
|
if (auto *whereClause = decl->getTrailingWhereClause()) {
|
|
SourceRange whereClauseRange = whereClause->getSourceRange();
|
|
if (whereClauseRange.isValid() &&
|
|
ctx.SourceMgr.rangeContainsTokenLoc(whereClauseRange, loc)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Don't look at the members.
|
|
return false;
|
|
}
|
|
} // end anonymous namespace
|
|
|
|
UnqualifiedLookup::UnqualifiedLookup(DeclName Name, DeclContext *DC,
|
|
LazyResolver *TypeResolver, SourceLoc Loc,
|
|
Options options)
|
|
: UnqualifiedLookup("dummy", Name, DC, TypeResolver, Loc, options) {
|
|
assert(ExpUnqualifiedLookup(Name, DC, TypeResolver, Loc, options)
|
|
.verifyEqual(*this) &&
|
|
"bad refactoring");
|
|
}
|
|
|
|
UnqualifiedLookup::UnqualifiedLookup(const char *dummy, DeclName Name,
|
|
DeclContext *DC,
|
|
LazyResolver *TypeResolver, SourceLoc Loc,
|
|
Options options)
|
|
: IndexOfFirstOuterResult(0) {
|
|
ModuleDecl &M = *DC->getParentModule();
|
|
ASTContext &Ctx = M.getASTContext();
|
|
if (!TypeResolver) TypeResolver = Ctx.getLazyResolver();
|
|
const SourceManager &SM = Ctx.SourceMgr;
|
|
DebuggerClient *DebugClient = M.getDebugClient();
|
|
|
|
auto isOriginallyTypeLookup = options.contains(Flags::TypeLookup);
|
|
NamedDeclConsumer Consumer(Name, Results, isOriginallyTypeLookup);
|
|
|
|
NLOptions baseNLOptions = NL_UnqualifiedDefault;
|
|
if (options.contains(Flags::AllowProtocolMembers))
|
|
baseNLOptions |= NL_ProtocolMembers;
|
|
if (isOriginallyTypeLookup)
|
|
baseNLOptions |= NL_OnlyTypes;
|
|
if (options.contains(Flags::IgnoreAccessControl))
|
|
baseNLOptions |= NL_IgnoreAccessControl;
|
|
|
|
Optional<bool> isCascadingUse;
|
|
if (options.contains(Flags::KnownPrivate))
|
|
isCascadingUse = false;
|
|
|
|
SmallVector<LookupResultEntry, 4> UnavailableInnerResults;
|
|
|
|
auto shouldReturnBasedOnResults = [&](bool noMoreOuterResults = false) {
|
|
if (Results.empty())
|
|
return false;
|
|
|
|
if (IndexOfFirstOuterResult == 0)
|
|
IndexOfFirstOuterResult = Results.size();
|
|
|
|
return !options.contains(Flags::IncludeOuterResults) || noMoreOuterResults;
|
|
};
|
|
|
|
if (Loc.isValid() &&
|
|
DC->getParentSourceFile() &&
|
|
DC->getParentSourceFile()->Kind != SourceFileKind::REPL &&
|
|
Ctx.LangOpts.EnableASTScopeLookup) {
|
|
// Find the source file in which we are performing the lookup.
|
|
SourceFile &sourceFile = *DC->getParentSourceFile();
|
|
|
|
// Find the scope from which we will initiate unqualified name lookup.
|
|
const ASTScope *lookupScope =
|
|
sourceFile.getScope().findInnermostEnclosingScope(Loc);
|
|
|
|
// Operator lookup is always at module scope.
|
|
if (Name.isOperator()) {
|
|
if (!isCascadingUse.hasValue()) {
|
|
DeclContext *innermostDC =
|
|
lookupScope->getInnermostEnclosingDeclContext();
|
|
isCascadingUse = innermostDC->isCascadingContextForLookup(
|
|
/*functionsAreNonCascading=*/true);
|
|
}
|
|
|
|
lookupScope = &sourceFile.getScope();
|
|
}
|
|
|
|
// Walk scopes outward from the innermost scope until we find something.
|
|
DeclContext *selfDC = nullptr;
|
|
for (auto currentScope = lookupScope; currentScope;
|
|
currentScope = currentScope->getParent()) {
|
|
// Perform local lookup within this scope.
|
|
auto localBindings = currentScope->getLocalBindings();
|
|
for (auto local : localBindings) {
|
|
Consumer.foundDecl(local,
|
|
getLocalDeclVisibilityKind(currentScope));
|
|
}
|
|
|
|
// If we found anything, we're done.
|
|
if (shouldReturnBasedOnResults())
|
|
return;
|
|
|
|
// When we are in the body of a method, get the 'self' declaration.
|
|
if (currentScope->getKind() == ASTScopeKind::AbstractFunctionBody &&
|
|
currentScope->getAbstractFunctionDecl()
|
|
->getDeclContext()
|
|
->isTypeContext()) {
|
|
selfDC = currentScope->getAbstractFunctionDecl();
|
|
continue;
|
|
}
|
|
|
|
// If there is a declaration context associated with this scope, we might
|
|
// want to look in it.
|
|
if (auto dc = currentScope->getDeclContext()) {
|
|
// If we haven't determined whether we have a cascading use, do so now.
|
|
if (!isCascadingUse.hasValue()) {
|
|
isCascadingUse = dc->isCascadingContextForLookup(
|
|
/*functionsAreNonCascading=*/false);
|
|
}
|
|
|
|
// Pattern binding initializers are only interesting insofar as they
|
|
// affect lookup in an enclosing nominal type or extension thereof.
|
|
if (auto *bindingInit = dyn_cast<PatternBindingInitializer>(dc)) {
|
|
// Lazy variable initializer contexts have a 'self' parameter for
|
|
// instance member lookup.
|
|
if (bindingInit->getImplicitSelfDecl())
|
|
selfDC = bindingInit;
|
|
|
|
continue;
|
|
}
|
|
|
|
// Default arguments only have 'static' access to the members of the
|
|
// enclosing type, if there is one.
|
|
if (isa<DefaultArgumentInitializer>(dc)) continue;
|
|
|
|
// Functions/initializers/deinitializers are only interesting insofar as
|
|
// they affect lookup in an enclosing nominal type or extension thereof.
|
|
if (isa<AbstractFunctionDecl>(dc)) continue;
|
|
|
|
// Subscripts have no lookup of their own.
|
|
if (isa<SubscriptDecl>(dc)) continue;
|
|
|
|
// Closures have no lookup of their own.
|
|
if (isa<AbstractClosureExpr>(dc)) continue;
|
|
|
|
// Top-level declarations have no lookup of their own.
|
|
if (isa<TopLevelCodeDecl>(dc)) continue;
|
|
|
|
// Typealiases have no lookup of their own.
|
|
if (isa<TypeAliasDecl>(dc)) continue;
|
|
|
|
// Lookup in the source file's scope marks the end.
|
|
if (isa<SourceFile>(dc)) {
|
|
// FIXME: A bit of a hack.
|
|
DC = dc;
|
|
break;
|
|
}
|
|
|
|
// We have a nominal type or an extension thereof. Perform lookup into
|
|
// the nominal type.
|
|
auto nominal = dc->getSelfNominalTypeDecl();
|
|
if (!nominal) continue;
|
|
|
|
// Dig out the type we're looking into.
|
|
SmallVector<NominalTypeDecl *, 2> lookupDecls;
|
|
populateLookupDeclsFromContext(dc, lookupDecls);
|
|
|
|
NLOptions options = baseNLOptions;
|
|
// Perform lookup into the type.
|
|
if (isCascadingUse.getValue())
|
|
options |= NL_KnownCascadingDependency;
|
|
else
|
|
options |= NL_KnownNonCascadingDependency;
|
|
|
|
SmallVector<ValueDecl *, 4> lookup;
|
|
dc->lookupQualified(lookupDecls, Name, options, lookup);
|
|
|
|
auto startIndex = Results.size();
|
|
for (auto result : lookup) {
|
|
auto *baseDC = dc;
|
|
if (!isa<TypeDecl>(result) && selfDC) baseDC = selfDC;
|
|
Results.push_back(LookupResultEntry(baseDC, result));
|
|
}
|
|
|
|
if (!Results.empty()) {
|
|
// Predicate that determines whether a lookup result should
|
|
// be unavailable except as a last-ditch effort.
|
|
auto unavailableLookupResult = [&](const LookupResultEntry &result) {
|
|
auto &effectiveVersion = Ctx.LangOpts.EffectiveLanguageVersion;
|
|
return result.getValueDecl()
|
|
->getAttrs()
|
|
.isUnavailableInSwiftVersion(effectiveVersion);
|
|
};
|
|
|
|
// If all of the results we just found are unavailable, keep looking.
|
|
auto begin = Results.begin() + startIndex;
|
|
if (std::all_of(begin, Results.end(), unavailableLookupResult)) {
|
|
UnavailableInnerResults.append(begin, Results.end());
|
|
Results.erase(begin, Results.end());
|
|
} else {
|
|
if (DebugClient)
|
|
filterForDiscriminator(Results, DebugClient);
|
|
|
|
if (shouldReturnBasedOnResults())
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Forget the 'self' declaration.
|
|
selfDC = nullptr;
|
|
}
|
|
}
|
|
} else {
|
|
// Never perform local lookup for operators.
|
|
if (Name.isOperator()) {
|
|
if (!isCascadingUse.hasValue()) {
|
|
isCascadingUse =
|
|
DC->isCascadingContextForLookup(/*functionsAreNonCascading=*/true);
|
|
}
|
|
DC = DC->getModuleScopeContext();
|
|
|
|
} else {
|
|
// If we are inside of a method, check to see if there are any ivars in
|
|
// scope, and if so, whether this is a reference to one of them.
|
|
// FIXME: We should persist this information between lookups.
|
|
while (!DC->isModuleScopeContext()) {
|
|
DeclContext *BaseDC = nullptr;
|
|
DeclContext *MetaBaseDC = nullptr;
|
|
GenericParamList *GenericParams = nullptr;
|
|
|
|
SmallVector<NominalTypeDecl *, 2> lookupDecls;
|
|
|
|
if (auto *PBI = dyn_cast<PatternBindingInitializer>(DC)) {
|
|
auto *PBD = PBI->getBinding();
|
|
assert(PBD);
|
|
|
|
// Lazy variable initializer contexts have a 'self' parameter for
|
|
// instance member lookup.
|
|
if (auto *selfParam = PBI->getImplicitSelfDecl()) {
|
|
Consumer.foundDecl(selfParam,
|
|
DeclVisibilityKind::FunctionParameter);
|
|
if (shouldReturnBasedOnResults())
|
|
return;
|
|
|
|
DC = DC->getParent();
|
|
|
|
populateLookupDeclsFromContext(DC, lookupDecls);
|
|
MetaBaseDC = DC;
|
|
BaseDC = PBI;
|
|
}
|
|
// Initializers for stored properties of types perform static
|
|
// lookup into the surrounding context.
|
|
else if (PBD->getDeclContext()->isTypeContext()) {
|
|
DC = DC->getParent();
|
|
|
|
populateLookupDeclsFromContext(DC, lookupDecls);
|
|
MetaBaseDC = DC;
|
|
BaseDC = MetaBaseDC;
|
|
|
|
isCascadingUse = DC->isCascadingContextForLookup(false);
|
|
}
|
|
// Otherwise, we have an initializer for a global or local property.
|
|
// There's not much to find here, we'll keep going up to a parent
|
|
// context.
|
|
|
|
if (!isCascadingUse.hasValue())
|
|
isCascadingUse = DC->isCascadingContextForLookup(false);
|
|
} else if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DC)) {
|
|
// Look for local variables; normally, the parser resolves these
|
|
// for us, but it can't do the right thing inside local types.
|
|
// FIXME: when we can parse and typecheck the function body partially
|
|
// for code completion, AFD->getBody() check can be removed.
|
|
if (Loc.isValid() && AFD->getBody()) {
|
|
if (!isCascadingUse.hasValue()) {
|
|
isCascadingUse =
|
|
!SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc);
|
|
}
|
|
|
|
namelookup::FindLocalVal localVal(SM, Loc, Consumer);
|
|
localVal.visit(AFD->getBody());
|
|
if (shouldReturnBasedOnResults())
|
|
return;
|
|
|
|
if (auto *P = AFD->getImplicitSelfDecl())
|
|
localVal.checkValueDecl(P, DeclVisibilityKind::FunctionParameter);
|
|
localVal.checkParameterList(AFD->getParameters());
|
|
if (shouldReturnBasedOnResults())
|
|
return;
|
|
}
|
|
if (!isCascadingUse.hasValue() || isCascadingUse.getValue())
|
|
isCascadingUse = AFD->isCascadingContextForLookup(false);
|
|
|
|
if (AFD->getDeclContext()->isTypeContext()) {
|
|
populateLookupDeclsFromContext(AFD->getDeclContext(),
|
|
lookupDecls);
|
|
BaseDC = AFD;
|
|
MetaBaseDC = AFD->getDeclContext();
|
|
DC = DC->getParent();
|
|
|
|
// If we're not in the body of the function (for example, we
|
|
// might be type checking a default argument expression and
|
|
// performing name lookup from there), the base declaration
|
|
// is the nominal type, not 'self'.
|
|
if (!AFD->isImplicit() &&
|
|
Loc.isValid() &&
|
|
AFD->getBodySourceRange().isValid() &&
|
|
!SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc)) {
|
|
BaseDC = MetaBaseDC;
|
|
}
|
|
}
|
|
|
|
// Look in the generic parameters after checking our local declaration.
|
|
GenericParams = AFD->getGenericParams();
|
|
} else if (auto *ACE = dyn_cast<AbstractClosureExpr>(DC)) {
|
|
// Look for local variables; normally, the parser resolves these
|
|
// for us, but it can't do the right thing inside local types.
|
|
if (Loc.isValid()) {
|
|
if (auto *CE = dyn_cast<ClosureExpr>(ACE)) {
|
|
namelookup::FindLocalVal localVal(SM, Loc, Consumer);
|
|
if (auto body = CE->getBody())
|
|
localVal.visit(body);
|
|
if (shouldReturnBasedOnResults())
|
|
return;
|
|
if (auto params = CE->getParameters())
|
|
localVal.checkParameterList(params);
|
|
if (shouldReturnBasedOnResults())
|
|
return;
|
|
}
|
|
}
|
|
if (!isCascadingUse.hasValue())
|
|
isCascadingUse = ACE->isCascadingContextForLookup(false);
|
|
} else if (auto *ED = dyn_cast<ExtensionDecl>(DC)) {
|
|
if (shouldLookupMembers(ED, Loc))
|
|
populateLookupDeclsFromContext(ED, lookupDecls);
|
|
|
|
BaseDC = ED;
|
|
MetaBaseDC = ED;
|
|
if (!isCascadingUse.hasValue())
|
|
isCascadingUse = ED->isCascadingContextForLookup(false);
|
|
} else if (auto *ND = dyn_cast<NominalTypeDecl>(DC)) {
|
|
if (shouldLookupMembers(ND, Loc))
|
|
populateLookupDeclsFromContext(ND, lookupDecls);
|
|
BaseDC = DC;
|
|
MetaBaseDC = DC;
|
|
if (!isCascadingUse.hasValue())
|
|
isCascadingUse = ND->isCascadingContextForLookup(false);
|
|
} else if (auto I = dyn_cast<DefaultArgumentInitializer>(DC)) {
|
|
// In a default argument, skip immediately out of both the
|
|
// initializer and the function.
|
|
isCascadingUse = false;
|
|
DC = I->getParent()->getParent();
|
|
continue;
|
|
} else {
|
|
assert(isa<TopLevelCodeDecl>(DC) || isa<Initializer>(DC) ||
|
|
isa<TypeAliasDecl>(DC) || isa<SubscriptDecl>(DC));
|
|
if (!isCascadingUse.hasValue())
|
|
isCascadingUse = DC->isCascadingContextForLookup(false);
|
|
}
|
|
|
|
// If we're inside a function context, we've already moved to
|
|
// the parent DC, so we have to check the function's generic
|
|
// parameters first.
|
|
if (GenericParams) {
|
|
namelookup::FindLocalVal localVal(SM, Loc, Consumer);
|
|
localVal.checkGenericParams(GenericParams);
|
|
|
|
if (shouldReturnBasedOnResults())
|
|
return;
|
|
}
|
|
|
|
// Check the generic parameters of our context.
|
|
GenericParamList *dcGenericParams = nullptr;
|
|
if (auto nominal = dyn_cast<NominalTypeDecl>(DC))
|
|
dcGenericParams = nominal->getGenericParams();
|
|
else if (auto ext = dyn_cast<ExtensionDecl>(DC))
|
|
dcGenericParams = ext->getGenericParams();
|
|
else if (auto subscript = dyn_cast<SubscriptDecl>(DC))
|
|
dcGenericParams = subscript->getGenericParams();
|
|
|
|
while (dcGenericParams) {
|
|
namelookup::FindLocalVal localVal(SM, Loc, Consumer);
|
|
localVal.checkGenericParams(dcGenericParams);
|
|
|
|
if (shouldReturnBasedOnResults())
|
|
return;
|
|
|
|
dcGenericParams = dcGenericParams->getOuterParameters();
|
|
}
|
|
|
|
if (BaseDC && !lookupDecls.empty()) {
|
|
NLOptions options = baseNLOptions;
|
|
if (isCascadingUse.getValue())
|
|
options |= NL_KnownCascadingDependency;
|
|
else
|
|
options |= NL_KnownNonCascadingDependency;
|
|
|
|
SmallVector<ValueDecl *, 4> Lookup;
|
|
DC->lookupQualified(lookupDecls, Name, options, Lookup);
|
|
bool FoundAny = false;
|
|
auto startIndex = Results.size();
|
|
for (auto Result : Lookup) {
|
|
// Classify this declaration.
|
|
FoundAny = true;
|
|
|
|
// Types are formally members of the metatype.
|
|
if (auto TD = dyn_cast<TypeDecl>(Result)) {
|
|
Results.push_back(LookupResultEntry(MetaBaseDC, Result));
|
|
continue;
|
|
}
|
|
|
|
Results.push_back(LookupResultEntry(BaseDC, Result));
|
|
}
|
|
|
|
if (FoundAny) {
|
|
// Predicate that determines whether a lookup result should
|
|
// be unavailable except as a last-ditch effort.
|
|
auto unavailableLookupResult =
|
|
[&](const LookupResultEntry &result) {
|
|
auto &effectiveVersion =
|
|
Ctx.LangOpts.EffectiveLanguageVersion;
|
|
return result.getValueDecl()
|
|
->getAttrs()
|
|
.isUnavailableInSwiftVersion(effectiveVersion);
|
|
};
|
|
|
|
// If all of the results we found are unavailable, keep looking.
|
|
auto begin = Results.begin() + startIndex;
|
|
if (std::all_of(begin, Results.end(), unavailableLookupResult)) {
|
|
UnavailableInnerResults.append(begin, Results.end());
|
|
Results.erase(begin, Results.end());
|
|
} else {
|
|
if (DebugClient)
|
|
filterForDiscriminator(Results, DebugClient);
|
|
|
|
if (shouldReturnBasedOnResults())
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
DC = DC->getParentForLookup();
|
|
}
|
|
|
|
if (!isCascadingUse.hasValue())
|
|
isCascadingUse = true;
|
|
}
|
|
|
|
if (auto SF = dyn_cast<SourceFile>(DC)) {
|
|
if (Loc.isValid()) {
|
|
// Look for local variables in top-level code; normally, the parser
|
|
// resolves these for us, but it can't do the right thing for
|
|
// local types.
|
|
namelookup::FindLocalVal localVal(SM, Loc, Consumer);
|
|
localVal.checkSourceFile(*SF);
|
|
if (shouldReturnBasedOnResults())
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Does the debugger client care about compound names?
|
|
if (Name.isSimpleName() && DebugClient &&
|
|
DebugClient->lookupOverrides(Name.getBaseName(), DC, Loc,
|
|
isOriginallyTypeLookup, Results))
|
|
return;
|
|
|
|
recordLookupOfTopLevelName(DC, Name, isCascadingUse.getValue());
|
|
recordedSF = dyn_cast<SourceFile>(DC);
|
|
recordedName = Name;
|
|
recordedIsCascadingUse = isCascadingUse.getValue();
|
|
|
|
// Add private imports to the extra search list.
|
|
SmallVector<ModuleDecl::ImportedModule, 8> extraImports;
|
|
if (auto FU = dyn_cast<FileUnit>(DC))
|
|
FU->getImportedModules(extraImports, ModuleDecl::ImportFilter::Private);
|
|
|
|
using namespace namelookup;
|
|
SmallVector<ValueDecl *, 8> CurModuleResults;
|
|
auto resolutionKind = isOriginallyTypeLookup ? ResolutionKind::TypesOnly
|
|
: ResolutionKind::Overloadable;
|
|
lookupInModule(&M, {}, Name, CurModuleResults, NLKind::UnqualifiedLookup,
|
|
resolutionKind, TypeResolver, DC, extraImports);
|
|
|
|
// Always perform name shadowing for type lookup.
|
|
if (options.contains(Flags::TypeLookup)) {
|
|
removeShadowedDecls(CurModuleResults, &M);
|
|
}
|
|
|
|
for (auto VD : CurModuleResults)
|
|
Results.push_back(LookupResultEntry(VD));
|
|
|
|
if (DebugClient)
|
|
filterForDiscriminator(Results, DebugClient);
|
|
|
|
// Now add any names the DebugClient knows about to the lookup.
|
|
if (Name.isSimpleName() && DebugClient)
|
|
DebugClient->lookupAdditions(Name.getBaseName(), DC, Loc,
|
|
isOriginallyTypeLookup, Results);
|
|
|
|
// If we've found something, we're done.
|
|
if (shouldReturnBasedOnResults(/*noMoreOuterResults=*/true))
|
|
return;
|
|
|
|
// If we still haven't found anything, but we do have some
|
|
// declarations that are "unavailable in the current Swift", drop
|
|
// those in.
|
|
Results = std::move(UnavailableInnerResults);
|
|
if (shouldReturnBasedOnResults(/*noMoreOuterResults=*/true))
|
|
return;
|
|
|
|
if (!Name.isSimpleName())
|
|
return;
|
|
|
|
// Look for a module with the given name.
|
|
if (Name.isSimpleName(M.getName())) {
|
|
Results.push_back(LookupResultEntry(&M));
|
|
if (shouldReturnBasedOnResults(/*noMoreOuterResults=*/true))
|
|
return;
|
|
}
|
|
|
|
ModuleDecl *desiredModule = Ctx.getLoadedModule(Name.getBaseIdentifier());
|
|
if (!desiredModule && Name == Ctx.TheBuiltinModule->getName())
|
|
desiredModule = Ctx.TheBuiltinModule;
|
|
if (desiredModule) {
|
|
forAllVisibleModules(DC, [&](const ModuleDecl::ImportedModule &import) -> bool {
|
|
if (import.second == desiredModule) {
|
|
Results.push_back(LookupResultEntry(import.second));
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
// Make sure we've recorded the inner-result-boundary.
|
|
(void)shouldReturnBasedOnResults(/*noMoreOuterResults=*/true);
|
|
}
|
|
|
|
TypeDecl* UnqualifiedLookup::getSingleTypeResult() {
|
|
if (Results.size() != 1)
|
|
return nullptr;
|
|
return dyn_cast<TypeDecl>(Results.back().getValueDecl());
|
|
}
|
|
|
|
ExpUnqualifiedLookup::PlacesToSearch::PlacesToSearch(
|
|
DeclContext *fromWhence, DeclContext *whereNonTypesAreMembers,
|
|
DeclContext *whereTypesAreMembers, DeclContext *placesHolder)
|
|
: fromWhence(fromWhence), whereNonTypesAreMembers(whereNonTypesAreMembers),
|
|
whereTypesAreMembers(whereTypesAreMembers) {
|
|
populateLookupDeclsFromContext(placesHolder, places);
|
|
}
|
|
|
|
void ExpUnqualifiedLookup::PlacesToSearch::dump() const {
|
|
llvm::errs() << "fromWhence: ";
|
|
fromWhence->dumpContext();
|
|
llvm::errs() << "whereNonTypesAreMembers: ";
|
|
whereNonTypesAreMembers->dumpContext();
|
|
llvm::errs() << "whereTypesAreMembers: ";
|
|
whereTypesAreMembers->dumpContext();
|
|
llvm::errs() << "places: ";
|
|
for (const auto *D : places)
|
|
D->dump();
|
|
llvm::errs() << "\n";
|
|
}
|
|
|
|
namespace {
|
|
class UnqualifiedLookupFactory {
|
|
public:
|
|
using Flags = UnqualifiedLookup::Flags;
|
|
using Options = UnqualifiedLookup::Options;
|
|
using PlacesToSearch = ExpUnqualifiedLookup::PlacesToSearch;
|
|
using PerScopeLookupState = ExpUnqualifiedLookup::PerScopeLookupState;
|
|
|
|
private:
|
|
// Inputs
|
|
const DeclName Name;
|
|
DeclContext *const DC;
|
|
ModuleDecl &M;
|
|
const ASTContext &Ctx;
|
|
LazyResolver *const TypeResolver;
|
|
const SourceLoc Loc;
|
|
const SourceManager &SM;
|
|
/// Used to find the file-local names.
|
|
DebuggerClient *const DebugClient;
|
|
const Options options;
|
|
const bool isOriginallyTypeLookup;
|
|
const NLOptions baseNLOptions;
|
|
// Transputs
|
|
// TODO: WHy some code pushes to Results, others goes via Consumer?
|
|
NamedDeclConsumer Consumer;
|
|
// Outputs
|
|
SmallVectorImpl<LookupResultEntry> &Results;
|
|
size_t &IndexOfFirstOuterResult;
|
|
|
|
// For debugging:
|
|
SourceFile const *&recordedSF;
|
|
DeclName &recordedName;
|
|
bool &recordedIsCascadingUse;
|
|
std::vector<PerScopeLookupState> &breadcrumbs;
|
|
|
|
SmallVector<LookupResultEntry, 4> UnavailableInnerResults;
|
|
|
|
public:
|
|
// clang-format off
|
|
UnqualifiedLookupFactory(DeclName Name,
|
|
DeclContext *const DC,
|
|
LazyResolver *TypeResolver,
|
|
SourceLoc Loc,
|
|
Options options,
|
|
ExpUnqualifiedLookup &lookupToBeCreated);
|
|
// clang-format on
|
|
|
|
void fillInLookup();
|
|
|
|
private:
|
|
/// Return nullptr if done with whole enchilada and isCascadingUse
|
|
std::pair<DeclContext *, bool>
|
|
astScopeBasedLookup(DeclContext *dc, Optional<bool> isCascadingUse);
|
|
|
|
static NLOptions
|
|
computeBaseNLOptions(const UnqualifiedLookup::Options options,
|
|
const bool isOriginallyTypeLookup);
|
|
|
|
/// Return true if done with lookup.
|
|
bool isFinishedWithLookupNowThatIsAboutToLookForOuterResults(
|
|
bool noMoreOuterResults = false);
|
|
|
|
bool shouldUseASTScopeLookup() const;
|
|
|
|
std::pair<const ASTScope *, bool>
|
|
operatorScopeForASTScopeLookup(DeclContext *dc,
|
|
Optional<bool> isCascadingUse);
|
|
|
|
std::pair<const ASTScope *, Optional<bool>>
|
|
nonoperatorScopeForASTScopeLookup(DeclContext *dc,
|
|
Optional<bool> isCascadingUse) const;
|
|
|
|
struct LookInScopeResult {
|
|
bool isDone; // Searching outers
|
|
DeclContext *selfDC;
|
|
DeclContext *dc;
|
|
Optional<bool>isCascadingUse;
|
|
};
|
|
|
|
/// Return status and new selfDC and new DC
|
|
Optional<LookInScopeResult>
|
|
lookInScope(const ASTScope *const currentScope,
|
|
DeclContext *selfDC,
|
|
DeclContext *dc,
|
|
const Optional<bool> isCascadingUse);
|
|
|
|
// clang-format off
|
|
/// Returns status and selfDC and DC
|
|
Optional<LookInScopeResult>
|
|
lookIntoDeclarationContext(DeclContext *dc,
|
|
DeclContext *selfDC,
|
|
DeclContext *wasDC,
|
|
Optional<bool> isCascadingUse);
|
|
// clang-format on
|
|
|
|
bool resolveIsCascadingUse(const DeclContext *const dc,
|
|
Optional<bool> isCascadingUse,
|
|
bool onlyCareAboutFunctionBody);
|
|
|
|
/// Return nullptr if lookup done.
|
|
std::pair<DeclContext *, bool>
|
|
operatorLookup(DeclContext *dc, Optional<bool> isCascadingUse);
|
|
|
|
/// Return nullptr if done looking up.
|
|
std::pair<DeclContext *, bool>
|
|
nonASTScopeBasedLookup(DeclContext *const dc,
|
|
const Optional<bool> isCascadingUseArg);
|
|
|
|
struct LookupInOneDeclContextResult {
|
|
bool isDone;
|
|
DeclContext* dc;
|
|
Optional<bool> isCascadingUse;
|
|
};
|
|
|
|
/// Return the next context to search.
|
|
Optional<LookupInOneDeclContextResult>
|
|
lookupInOneDeclContext(DeclContext *dc,
|
|
const Optional<bool> isCascadingUseArg);
|
|
|
|
/// Check the generic parameters of our context.
|
|
/// Return true if done with lookup
|
|
bool addGenericParametersHereAndInEnclosingScopes(DeclContext *dc);
|
|
|
|
/// Consume generic parameters
|
|
void addGenericParametersForFunction(AbstractFunctionDecl *AFD);
|
|
|
|
static GenericParamList *getGenericParams(const DeclContext *const dc);
|
|
|
|
Optional<PerScopeLookupState>
|
|
lookupInAppropriateContext(DeclContext *dc, Optional<bool> isCascadingUse);
|
|
|
|
// TODO: use objects & virtuals
|
|
|
|
Optional<PerScopeLookupState>
|
|
lookupInPatternBindingInitializer(PatternBindingInitializer *PBI,
|
|
Optional<bool> isCascadingUse);
|
|
|
|
Optional<PerScopeLookupState> lookupInFunctionDecl(AbstractFunctionDecl *AFD,
|
|
Optional<bool> isCascadingUse);
|
|
|
|
Optional<PerScopeLookupState> lookupInClosure(AbstractClosureExpr *ACE,
|
|
Optional<bool> isCascadingUse);
|
|
|
|
Optional<PerScopeLookupState> lookupInExtension(ExtensionDecl *ED,
|
|
Optional<bool> isCascadingUse);
|
|
|
|
Optional<PerScopeLookupState> lookupInNominalType(NominalTypeDecl *ND,
|
|
Optional<bool> isCascadingUse);
|
|
|
|
Optional<PerScopeLookupState>
|
|
lookupInDefaultArgumentInitializer(DefaultArgumentInitializer *I,
|
|
Optional<bool> isCascadingUse);
|
|
|
|
bool isOutsideBodyOfFunction(const AbstractFunctionDecl *const AFD) const;
|
|
|
|
Optional<PerScopeLookupState> lookupInMiscContext(DeclContext *dc,
|
|
Optional<bool> isCascadingUse);
|
|
|
|
/// Return true if lookup is done
|
|
bool addLocalVariableResults(DeclContext *dc);
|
|
|
|
/// Return true if found any
|
|
// TODO: make member fn of PlacesToSearch, take ref to Result as arg
|
|
void searchPlacesToSearch(PlacesToSearch placesToSearch, DeclName Name,
|
|
const bool isCascadingUse,
|
|
const NLOptions baseNLOptions,
|
|
DeclContext *contextForLookup);
|
|
|
|
/// Return true if finished with lookup
|
|
bool handleUnavailableInnerResults(const size_t startIndexOfInnerResults);
|
|
|
|
void recordDependencyOnTopLevelName(DeclContext *topLevelContext,
|
|
DeclName name, bool isCascadingUse);
|
|
|
|
void addPrivateImports(DeclContext *const dc);
|
|
|
|
/// Return true if done
|
|
bool addNamesKnownToDebugClient(DeclContext *dc);
|
|
|
|
/// Return true if done
|
|
bool addUnavailableInnerResults();
|
|
|
|
bool lookForAModuleWithTheGivenName(DeclContext *const dc);
|
|
};
|
|
} // namespace
|
|
|
|
#pragma mark ExpUnqualifiedLookup functions
|
|
|
|
// clang-format off
|
|
ExpUnqualifiedLookup::ExpUnqualifiedLookup(DeclName Name,
|
|
DeclContext *const DC,
|
|
LazyResolver *TypeResolver,
|
|
SourceLoc Loc,
|
|
Options options)
|
|
// clang-format on
|
|
: IndexOfFirstOuterResult(0) {
|
|
UnqualifiedLookupFactory(Name, DC, TypeResolver, Loc, options, *this)
|
|
.fillInLookup();
|
|
}
|
|
|
|
TypeDecl* ExpUnqualifiedLookup::getSingleTypeResult() {
|
|
if (Results.size() != 1)
|
|
return nullptr;
|
|
return dyn_cast<TypeDecl>(Results.back().getValueDecl());
|
|
}
|
|
|
|
bool ExpUnqualifiedLookup::verifyEqual(const UnqualifiedLookup &other) const {
|
|
assert(Results.size() == other.Results.size());
|
|
for (size_t i: indices(Results)) {
|
|
const auto &e = Results[i];
|
|
const auto &oe = other.Results[i];
|
|
assert(e.getValueDecl() == oe.getValueDecl());
|
|
assert(e.getDeclContext() == oe.getDeclContext());
|
|
//unsigned printContext(llvm::raw_ostream &OS, unsigned indent = 0,
|
|
// bool onlyAPartialLine = false) const;
|
|
assert(e.getBaseDecl() == oe.getBaseDecl());
|
|
}
|
|
assert ( IndexOfFirstOuterResult == other.IndexOfFirstOuterResult);
|
|
assert(recordedSF == other.recordedSF);
|
|
assert(recordedName == other.recordedName);
|
|
if (recordedIsCascadingUse)
|
|
assert(other.recordedIsCascadingUse);
|
|
else
|
|
assert(!other.recordedIsCascadingUse);
|
|
return true;
|
|
}
|
|
|
|
void ExpUnqualifiedLookup::dumpBreadcrumbs() const {
|
|
auto &e = llvm::errs();
|
|
for (size_t i : indices(breadcrumbs)) {
|
|
e << i << "\n";
|
|
const PerScopeLookupState &s = breadcrumbs[i];
|
|
s.dump();
|
|
}
|
|
}
|
|
|
|
void ExpUnqualifiedLookup::PerScopeLookupState::dump() const {
|
|
auto &e = llvm::errs();
|
|
e << (isDone ? "done: " : "not done: ");
|
|
e << " dc: ";
|
|
childOfNextDC->dumpContext();
|
|
if (placesToSearch.hasValue())
|
|
placesToSearch.getValue().dump();
|
|
}
|
|
|
|
#pragma mark UnqualifiedLookupFactory functions
|
|
|
|
// clang-format off
|
|
UnqualifiedLookupFactory::UnqualifiedLookupFactory(
|
|
DeclName Name,
|
|
DeclContext *const DC,
|
|
LazyResolver *TypeResolver,
|
|
SourceLoc Loc,
|
|
Options options,
|
|
ExpUnqualifiedLookup &lookupToBeCreated)
|
|
:
|
|
Name(Name),
|
|
DC(DC),
|
|
M(*DC->getParentModule()),
|
|
Ctx(M.getASTContext()),
|
|
TypeResolver(TypeResolver ? TypeResolver : Ctx.getLazyResolver()),
|
|
Loc(Loc),
|
|
SM(Ctx.SourceMgr),
|
|
DebugClient(M.getDebugClient()),
|
|
options(options),
|
|
isOriginallyTypeLookup(options.contains(Flags::TypeLookup)),
|
|
baseNLOptions(computeBaseNLOptions(options, isOriginallyTypeLookup)),
|
|
Consumer(Name, lookupToBeCreated.Results, isOriginallyTypeLookup),
|
|
Results(lookupToBeCreated.Results),
|
|
IndexOfFirstOuterResult(lookupToBeCreated.IndexOfFirstOuterResult),
|
|
recordedSF(lookupToBeCreated.recordedSF),
|
|
recordedName(lookupToBeCreated.recordedName),
|
|
recordedIsCascadingUse(lookupToBeCreated.recordedIsCascadingUse),
|
|
breadcrumbs(lookupToBeCreated.breadcrumbs) {}
|
|
// clang-format on
|
|
|
|
void UnqualifiedLookupFactory::fillInLookup() {
|
|
const Optional<bool> isCascadingUse =
|
|
options.contains(Flags::KnownPrivate) ? Optional<bool>(false) : None;
|
|
// Never perform local lookup for operators.
|
|
std::pair<DeclContext *, bool> dcAndIsCascadingUse =
|
|
shouldUseASTScopeLookup()
|
|
? astScopeBasedLookup(DC, isCascadingUse)
|
|
: Name.isOperator() ? operatorLookup(DC, isCascadingUse)
|
|
: nonASTScopeBasedLookup(DC, isCascadingUse);
|
|
|
|
if (!dcAndIsCascadingUse.first)
|
|
return;
|
|
|
|
// TODO: Does the debugger client care about compound names?
|
|
if (Name.isSimpleName() && DebugClient &&
|
|
DebugClient->lookupOverrides(Name.getBaseName(),
|
|
dcAndIsCascadingUse.first, Loc,
|
|
isOriginallyTypeLookup, Results))
|
|
return;
|
|
|
|
recordDependencyOnTopLevelName(dcAndIsCascadingUse.first, Name,
|
|
dcAndIsCascadingUse.second);
|
|
addPrivateImports(dcAndIsCascadingUse.first);
|
|
if (addNamesKnownToDebugClient(dcAndIsCascadingUse.first))
|
|
return;
|
|
// If we still haven't found anything, but we do have some
|
|
// declarations that are "unavailable in the current Swift", drop
|
|
// those in.
|
|
if (addUnavailableInnerResults())
|
|
return;
|
|
if (lookForAModuleWithTheGivenName(dcAndIsCascadingUse.first))
|
|
return;
|
|
// Make sure we've recorded the inner-result-boundary.
|
|
(void)isFinishedWithLookupNowThatIsAboutToLookForOuterResults(
|
|
/*noMoreOuterResults=*/true);
|
|
}
|
|
|
|
std::pair<DeclContext *, bool>
|
|
UnqualifiedLookupFactory::astScopeBasedLookup(DeclContext *const startDC,
|
|
Optional<bool> isCascadingUse) {
|
|
const std::pair<const ASTScope *, Optional<bool>>
|
|
lookupScopeAndIsCascadingUse =
|
|
Name.isOperator()
|
|
? operatorScopeForASTScopeLookup(startDC, isCascadingUse)
|
|
: nonoperatorScopeForASTScopeLookup(startDC, isCascadingUse);
|
|
// Walk scopes outward from the innermost scope until we find something.
|
|
DeclContext *dc = startDC;
|
|
DeclContext *selfDC = nullptr;
|
|
Optional<bool> currentIsCascadingUse = lookupScopeAndIsCascadingUse.second;
|
|
for (auto currentScope = lookupScopeAndIsCascadingUse.first; currentScope;
|
|
currentScope = currentScope->getParent()) {
|
|
auto r =
|
|
lookInScope(currentScope, selfDC, dc, currentIsCascadingUse);
|
|
if (!r.hasValue())
|
|
return std::make_pair(nullptr, false);
|
|
const bool isDone = r.getValue().isDone;
|
|
selfDC = r.getValue().selfDC;
|
|
dc = r.getValue().dc;
|
|
currentIsCascadingUse = r.getValue().isCascadingUse;
|
|
if (isDone)
|
|
return std::make_pair(dc, currentIsCascadingUse.getValue());
|
|
}
|
|
llvm_unreachable("impossible");
|
|
}
|
|
|
|
NLOptions UnqualifiedLookupFactory::computeBaseNLOptions(
|
|
const UnqualifiedLookup::Options options,
|
|
const bool isOriginallyTypeLookup) {
|
|
NLOptions baseNLOptions = NL_UnqualifiedDefault;
|
|
if (options.contains(Flags::AllowProtocolMembers))
|
|
baseNLOptions |= NL_ProtocolMembers;
|
|
if (isOriginallyTypeLookup)
|
|
baseNLOptions |= NL_OnlyTypes;
|
|
if (options.contains(Flags::IgnoreAccessControl))
|
|
baseNLOptions |= NL_IgnoreAccessControl;
|
|
return baseNLOptions;
|
|
}
|
|
// TODO: elim flag?
|
|
// TODO: fold this into return None?
|
|
bool UnqualifiedLookupFactory::
|
|
isFinishedWithLookupNowThatIsAboutToLookForOuterResults(
|
|
bool noMoreOuterResults) {
|
|
if (Results.empty())
|
|
return false;
|
|
|
|
if (IndexOfFirstOuterResult == 0)
|
|
IndexOfFirstOuterResult = Results.size();
|
|
|
|
return !options.contains(Flags::IncludeOuterResults) || noMoreOuterResults;
|
|
}
|
|
|
|
bool UnqualifiedLookupFactory::shouldUseASTScopeLookup() const {
|
|
return Loc.isValid() && DC->getParentSourceFile() &&
|
|
DC->getParentSourceFile()->Kind != SourceFileKind::REPL &&
|
|
Ctx.LangOpts.EnableASTScopeLookup;
|
|
}
|
|
|
|
std::pair<const ASTScope *, bool>
|
|
UnqualifiedLookupFactory::operatorScopeForASTScopeLookup(
|
|
DeclContext *dc, Optional<bool> isCascadingUse) {
|
|
// Find the source file in which we are performing the lookup.
|
|
SourceFile &sourceFile = *dc->getParentSourceFile();
|
|
|
|
// Find the scope from which we will initiate unqualified name lookup.
|
|
const ASTScope *lookupScope =
|
|
sourceFile.getScope().findInnermostEnclosingScope(Loc);
|
|
|
|
// Operator lookup is always at module scope.
|
|
return std::make_pair(
|
|
&sourceFile.getScope(),
|
|
resolveIsCascadingUse(lookupScope->getInnermostEnclosingDeclContext(),
|
|
isCascadingUse,
|
|
/*onlyCareAboutFunctionBody*/ true));
|
|
}
|
|
|
|
std::pair<const ASTScope *, Optional<bool>>
|
|
UnqualifiedLookupFactory::nonoperatorScopeForASTScopeLookup(
|
|
DeclContext *dc, Optional<bool> isCascadingUse) const {
|
|
// Find the source file in which we are performing the lookup.
|
|
SourceFile &sourceFile = *dc->getParentSourceFile();
|
|
|
|
// Find the scope from which we will initiate unqualified name lookup.
|
|
const ASTScope *lookupScope =
|
|
sourceFile.getScope().findInnermostEnclosingScope(Loc);
|
|
|
|
return std::make_pair(lookupScope, isCascadingUse);
|
|
}
|
|
|
|
|
|
Optional<UnqualifiedLookupFactory::LookInScopeResult>
|
|
UnqualifiedLookupFactory::lookInScope(const ASTScope *const currentScope,
|
|
DeclContext *selfDC,
|
|
DeclContext *dc,
|
|
const Optional<bool> isCascadingUse) {
|
|
|
|
// Perform local lookup within this scope.
|
|
auto localBindings = currentScope->getLocalBindings();
|
|
for (auto local : localBindings) {
|
|
Consumer.foundDecl(local, getLocalDeclVisibilityKind(currentScope));
|
|
}
|
|
|
|
// If we found anything, we're done.
|
|
if (isFinishedWithLookupNowThatIsAboutToLookForOuterResults())
|
|
return None;
|
|
|
|
// When we are in the body of a method, get the 'self' declaration.
|
|
if (currentScope->getKind() == ASTScopeKind::AbstractFunctionBody &&
|
|
currentScope->getAbstractFunctionDecl()
|
|
->getDeclContext()
|
|
->isTypeContext()) {
|
|
selfDC = currentScope->getAbstractFunctionDecl();
|
|
return LookInScopeResult{false, selfDC, dc, isCascadingUse};
|
|
}
|
|
|
|
// If there is a declaration context associated with this scope, we might
|
|
// want to look in it.
|
|
if (auto scopeDC = currentScope->getDeclContext())
|
|
return lookIntoDeclarationContext(scopeDC, selfDC, dc, isCascadingUse);
|
|
return LookInScopeResult{false, selfDC, dc, isCascadingUse};
|
|
}
|
|
|
|
// TODO: reorder decls and defs
|
|
|
|
// TODO: struct instead of tuples
|
|
// TODO: name convention for astscope routines
|
|
Optional<UnqualifiedLookupFactory::LookInScopeResult>
|
|
UnqualifiedLookupFactory::lookIntoDeclarationContext(
|
|
DeclContext *dc,
|
|
DeclContext *selfDC,
|
|
DeclContext *wasDC,
|
|
Optional<bool> isCascadingUse) {
|
|
|
|
// If we haven't determined whether we have a cascading use, do so now.
|
|
const bool isCascadingUseResult = resolveIsCascadingUse(
|
|
dc, isCascadingUse, /*onlyCareAboutFunctionBody=*/false);
|
|
|
|
// Pattern binding initializers are only interesting insofar as they
|
|
// affect lookup in an enclosing nominal type or extension thereof.
|
|
if (auto *bindingInit = dyn_cast<PatternBindingInitializer>(dc)) {
|
|
// Lazy variable initializer contexts have a 'self' parameter for
|
|
// instance member lookup.
|
|
if (bindingInit->getImplicitSelfDecl())
|
|
selfDC = bindingInit;
|
|
|
|
return LookInScopeResult{false, selfDC, wasDC,
|
|
isCascadingUseResult};
|
|
}
|
|
|
|
// Default arguments only have 'static' access to the members of the
|
|
// enclosing type, if there is one.
|
|
if (isa<DefaultArgumentInitializer>(dc))
|
|
return LookInScopeResult{false, selfDC, wasDC,
|
|
isCascadingUseResult};
|
|
|
|
// Functions/initializers/deinitializers are only interesting insofar as
|
|
// they affect lookup in an enclosing nominal type or extension thereof.
|
|
if (isa<AbstractFunctionDecl>(dc))
|
|
return LookInScopeResult{false, selfDC, wasDC,
|
|
isCascadingUseResult};
|
|
|
|
// Subscripts have no lookup of their own.
|
|
if (isa<SubscriptDecl>(dc))
|
|
return LookInScopeResult{false, selfDC, wasDC,
|
|
isCascadingUseResult};
|
|
|
|
// Closures have no lookup of their own.
|
|
if (isa<AbstractClosureExpr>(dc))
|
|
return LookInScopeResult{false, selfDC, wasDC,
|
|
isCascadingUseResult};
|
|
|
|
// Top-level declarations have no lookup of their own.
|
|
if (isa<TopLevelCodeDecl>(dc))
|
|
return LookInScopeResult{false, selfDC, wasDC,
|
|
isCascadingUseResult};
|
|
|
|
// Typealiases have no lookup of their own.
|
|
if (isa<TypeAliasDecl>(dc))
|
|
return LookInScopeResult{false, selfDC, wasDC,
|
|
isCascadingUseResult};
|
|
|
|
// Lookup in the source file's scope marks the end.
|
|
if (isa<SourceFile>(dc)) {
|
|
// FIXME: A bit of a hack.
|
|
return LookInScopeResult{true, selfDC, dc,
|
|
isCascadingUseResult};
|
|
}
|
|
|
|
// We have a nominal type or an extension thereof. Perform lookup into
|
|
// the nominal type.
|
|
auto nominal = dc->getSelfNominalTypeDecl();
|
|
if (!nominal)
|
|
return LookInScopeResult{false, selfDC, wasDC,
|
|
isCascadingUseResult};
|
|
|
|
// Dig out the type we're looking into.
|
|
using LookupDecls = SmallVector<NominalTypeDecl *, 2>;
|
|
LookupDecls lookupDecls;
|
|
populateLookupDeclsFromContext(dc, lookupDecls);
|
|
|
|
NLOptions options = baseNLOptions;
|
|
// Perform lookup into the type.
|
|
if (isCascadingUseResult)
|
|
options |= NL_KnownCascadingDependency;
|
|
else
|
|
options |= NL_KnownNonCascadingDependency;
|
|
|
|
SmallVector<ValueDecl *, 4> lookup;
|
|
dc->lookupQualified(lookupDecls, Name, options, lookup);
|
|
|
|
auto startIndex = Results.size();
|
|
for (auto result : lookup) {
|
|
auto *baseDC = dc;
|
|
if (!isa<TypeDecl>(result) && selfDC)
|
|
baseDC = selfDC;
|
|
Results.push_back(LookupResultEntry(baseDC, result));
|
|
}
|
|
|
|
if (!Results.empty()) {
|
|
// Predicate that determines whether a lookup result should
|
|
// be unavailable except as a last-ditch effort.
|
|
auto unavailableLookupResult = [&](const LookupResultEntry &result) {
|
|
auto &effectiveVersion = Ctx.LangOpts.EffectiveLanguageVersion;
|
|
return result.getValueDecl()->getAttrs().isUnavailableInSwiftVersion(
|
|
effectiveVersion);
|
|
};
|
|
|
|
// If all of the results we just found are unavailable, keep looking.
|
|
auto begin = Results.begin() + startIndex;
|
|
if (std::all_of(begin, Results.end(), unavailableLookupResult)) {
|
|
UnavailableInnerResults.append(begin, Results.end());
|
|
Results.erase(begin, Results.end());
|
|
} else {
|
|
if (DebugClient)
|
|
filterForDiscriminator(Results, DebugClient);
|
|
|
|
if (isFinishedWithLookupNowThatIsAboutToLookForOuterResults())
|
|
return None;
|
|
}
|
|
}
|
|
|
|
// Forget the 'self' declaration.
|
|
selfDC = nullptr;
|
|
|
|
return LookInScopeResult{false, selfDC, dc,
|
|
isCascadingUseResult};
|
|
}
|
|
|
|
bool UnqualifiedLookupFactory::resolveIsCascadingUse(
|
|
const DeclContext *const dc, Optional<bool> isCascadingUse,
|
|
bool onlyCareAboutFunctionBody) {
|
|
return isCascadingUse.hasValue()
|
|
? isCascadingUse.getValue()
|
|
: dc->isCascadingContextForLookup(
|
|
/*functionsAreNonCascading=*/onlyCareAboutFunctionBody);
|
|
}
|
|
|
|
std::pair<DeclContext *, bool>
|
|
UnqualifiedLookupFactory::operatorLookup(DeclContext *dc,
|
|
Optional<bool> isCascadingUse) {
|
|
auto *msc = dc->getModuleScopeContext();
|
|
return std::make_pair(
|
|
addLocalVariableResults(msc) ? nullptr : msc,
|
|
resolveIsCascadingUse(dc, isCascadingUse,
|
|
/*onlyCareAboutFunctionBody*/ true));
|
|
}
|
|
|
|
// TODO: fix this convention w/ struct: Return nullptr if done looking up.
|
|
std::pair<DeclContext *, bool> UnqualifiedLookupFactory::nonASTScopeBasedLookup(
|
|
DeclContext *const dc, const Optional<bool> isCascadingUseArg) {
|
|
// If we are inside of a method, check to see if there are any ivars in
|
|
// scope, and if so, whether this is a reference to one of them.
|
|
// FIXME: We should persist this information between lookups.
|
|
DeclContext *nextDC = dc;
|
|
auto isCascadingUse = isCascadingUseArg;
|
|
DeclContext *priorDC = nullptr;
|
|
while (!nextDC->isModuleScopeContext()) {
|
|
auto r =
|
|
lookupInOneDeclContext(nextDC, isCascadingUse);
|
|
if (!r.hasValue())
|
|
return std::make_pair(nullptr, false);
|
|
const bool isDone = r.getValue().isDone;
|
|
nextDC = r.getValue().dc;
|
|
isCascadingUse = r.getValue().isCascadingUse;
|
|
if (isDone)
|
|
break;
|
|
assert(nextDC != priorDC && "non-termination");
|
|
priorDC = nextDC;
|
|
}
|
|
return std::make_pair(addLocalVariableResults(nextDC) ? nullptr : nextDC,
|
|
isCascadingUse.hasValue() ? isCascadingUse.getValue()
|
|
: true);
|
|
}
|
|
|
|
// clang-format off
|
|
Optional<UnqualifiedLookupFactory::LookupInOneDeclContextResult>
|
|
UnqualifiedLookupFactory::lookupInOneDeclContext(
|
|
DeclContext *dc,
|
|
const Optional<bool> isCascadingUseArg) {
|
|
// clang-format on
|
|
const auto r =
|
|
lookupInAppropriateContext(dc, isCascadingUseArg);
|
|
if (!r.hasValue())
|
|
return None;
|
|
const bool isDone = r.getValue().isDone;
|
|
breadcrumbs.push_back(r.getValue());
|
|
auto childOfNextDC = r.getValue().childOfNextDC;
|
|
auto placesToSearch = std::move(r.getValue().placesToSearch);
|
|
auto isCascadingUse = r.getValue().isCascadingUse;
|
|
|
|
if (isDone)
|
|
return LookupInOneDeclContextResult{false, childOfNextDC, isCascadingUse};
|
|
|
|
if (addGenericParametersHereAndInEnclosingScopes(childOfNextDC))
|
|
return None;
|
|
if (placesToSearch.hasValue() &&
|
|
!placesToSearch.getValue().empty()) {
|
|
auto startIndexOfInnerResults = Results.size();
|
|
searchPlacesToSearch(std::move(placesToSearch.getValue()),
|
|
Name,
|
|
isCascadingUse.getValue(),
|
|
baseNLOptions,
|
|
childOfNextDC);
|
|
if (handleUnavailableInnerResults(startIndexOfInnerResults))
|
|
return None;
|
|
}
|
|
// TODO: What if !BaseDC && lookupDecls non-empty?
|
|
DeclContext *nextDC = childOfNextDC->getParentForLookup();
|
|
return LookupInOneDeclContextResult{false, nextDC, isCascadingUse};
|
|
}
|
|
// clang-format on
|
|
|
|
bool UnqualifiedLookupFactory::addGenericParametersHereAndInEnclosingScopes(
|
|
DeclContext *dc) {
|
|
for (GenericParamList *dcGenericParams = getGenericParams(dc);
|
|
dcGenericParams;
|
|
dcGenericParams = dcGenericParams->getOuterParameters()) {
|
|
namelookup::FindLocalVal localVal(SM, Loc, Consumer);
|
|
localVal.checkGenericParams(dcGenericParams);
|
|
|
|
if (isFinishedWithLookupNowThatIsAboutToLookForOuterResults())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void UnqualifiedLookupFactory::addGenericParametersForFunction(
|
|
AbstractFunctionDecl *AFD) {
|
|
// If we're inside a function context, we've already moved to
|
|
// the parent DC, so we have to check the function's generic
|
|
// parameters first.
|
|
GenericParamList *GenericParams = AFD->getGenericParams();
|
|
if (GenericParams) {
|
|
namelookup::FindLocalVal localVal(SM, Loc, Consumer);
|
|
localVal.checkGenericParams(GenericParams);
|
|
}
|
|
}
|
|
|
|
GenericParamList *
|
|
UnqualifiedLookupFactory::getGenericParams(const DeclContext *const dc) {
|
|
if (auto nominal = dyn_cast<NominalTypeDecl>(dc))
|
|
return nominal->getGenericParams();
|
|
if (auto ext = dyn_cast<ExtensionDecl>(dc))
|
|
return ext->getGenericParams();
|
|
if (auto subscript = dyn_cast<SubscriptDecl>(dc))
|
|
return subscript->getGenericParams();
|
|
return nullptr;
|
|
}
|
|
|
|
Optional<UnqualifiedLookupFactory::PerScopeLookupState>
|
|
UnqualifiedLookupFactory::lookupInAppropriateContext(
|
|
DeclContext *dc, Optional<bool> isCascadingUse) {
|
|
if (auto *PBI = dyn_cast<PatternBindingInitializer>(dc))
|
|
return lookupInPatternBindingInitializer(PBI, isCascadingUse);
|
|
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(dc))
|
|
return lookupInFunctionDecl(AFD, isCascadingUse);
|
|
if (auto *ACE = dyn_cast<AbstractClosureExpr>(dc))
|
|
return lookupInClosure(ACE, isCascadingUse);
|
|
if (auto *ED = dyn_cast<ExtensionDecl>(dc))
|
|
return lookupInExtension(ED, isCascadingUse);
|
|
if (auto *ND = dyn_cast<NominalTypeDecl>(dc))
|
|
return lookupInNominalType(ND, isCascadingUse);
|
|
if (auto I = dyn_cast<DefaultArgumentInitializer>(dc))
|
|
return lookupInDefaultArgumentInitializer(I, isCascadingUse);
|
|
|
|
return lookupInMiscContext(dc, isCascadingUse);
|
|
}
|
|
|
|
Optional<UnqualifiedLookupFactory::PerScopeLookupState>
|
|
UnqualifiedLookupFactory::lookupInPatternBindingInitializer(
|
|
PatternBindingInitializer *PBI, Optional<bool> isCascadingUse) {
|
|
auto *PBD = PBI->getBinding();
|
|
assert(PBD);
|
|
// Lazy variable initializer contexts have a 'self' parameter for
|
|
// instance member lookup.
|
|
if (auto *selfParam = PBI->getImplicitSelfDecl()) {
|
|
Consumer.foundDecl(selfParam, DeclVisibilityKind::FunctionParameter);
|
|
if (isFinishedWithLookupNowThatIsAboutToLookForOuterResults())
|
|
return None;
|
|
DeclContext *const parent = PBI->getParent();
|
|
// clang-format off
|
|
return PerScopeLookupState{
|
|
false,
|
|
parent,
|
|
PlacesToSearch(parent, PBI, parent, parent),
|
|
resolveIsCascadingUse(PBI,isCascadingUse,
|
|
/*onlyCareAboutFunctionBody=*/false)};
|
|
// clang-format no
|
|
}
|
|
// Initializers for stored properties of types perform static
|
|
// lookup into the surrounding context.
|
|
if (PBD->getDeclContext()->isTypeContext()) {
|
|
DeclContext *const surroundingContext = PBI->getParent();
|
|
// clang-format off
|
|
return PerScopeLookupState{
|
|
false,
|
|
surroundingContext,
|
|
PlacesToSearch(surroundingContext, surroundingContext,
|
|
surroundingContext, surroundingContext),
|
|
resolveIsCascadingUse(surroundingContext,
|
|
None,
|
|
/*onlyCareAboutFunctionBody=*/false)};
|
|
// clang-format on
|
|
}
|
|
// Otherwise, we have an initializer for a global or local property.
|
|
// There's not much to find here, we'll keep going up to a parent
|
|
// context.
|
|
// clang-format off
|
|
return PerScopeLookupState{
|
|
false,
|
|
PBI,
|
|
None,
|
|
resolveIsCascadingUse(PBI, isCascadingUse,
|
|
/*onlyCareAboutFunctionBody=*/false)};
|
|
// clang-format on
|
|
}
|
|
|
|
Optional<UnqualifiedLookupFactory::PerScopeLookupState>
|
|
UnqualifiedLookupFactory::lookupInFunctionDecl(AbstractFunctionDecl *AFD,
|
|
Optional<bool> isCascadingUse) {
|
|
// Look for local variables; normally, the parser resolves these
|
|
// for us, but it can't do the right thing inside local types.
|
|
// FIXME: when we can parse and typecheck the function body partially
|
|
// for code completion, AFD->getBody() check can be removed.
|
|
if (Loc.isValid() && AFD->getBody()) {
|
|
|
|
namelookup::FindLocalVal localVal(SM, Loc, Consumer);
|
|
localVal.visit(AFD->getBody());
|
|
if (isFinishedWithLookupNowThatIsAboutToLookForOuterResults())
|
|
// clang-format off
|
|
// TODO: when returning finished, return nulls?
|
|
return None;
|
|
|
|
if (auto *P = AFD->getImplicitSelfDecl())
|
|
localVal.checkValueDecl(P, DeclVisibilityKind::FunctionParameter);
|
|
localVal.checkParameterList(AFD->getParameters());
|
|
if (isFinishedWithLookupNowThatIsAboutToLookForOuterResults())
|
|
return None;
|
|
}
|
|
const bool returnValueForIsCascadingUse =
|
|
AFD->isCascadingContextForLookup(false) &&
|
|
(isCascadingUse.hasValue()
|
|
? isCascadingUse.getValue()
|
|
: !Loc.isValid() || !AFD->getBody() ||
|
|
!SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc));
|
|
|
|
if (AFD->getDeclContext()->isTypeContext()) {
|
|
DeclContext *fnDeclContext = AFD->getDeclContext();
|
|
DeclContext *fnParent = AFD->getParent();
|
|
addGenericParametersForFunction(AFD);
|
|
if (isFinishedWithLookupNowThatIsAboutToLookForOuterResults())
|
|
return None;
|
|
return PerScopeLookupState{
|
|
false,
|
|
fnParent,
|
|
PlacesToSearch(
|
|
fnParent,
|
|
// If we're not in the body of the function (for example, we
|
|
// might be type checking a default argument expression and
|
|
// performing name lookup from there), the base declaration
|
|
// is the nominal type, not 'self'.
|
|
isOutsideBodyOfFunction(AFD) ? fnDeclContext : AFD, fnDeclContext,
|
|
AFD->getDeclContext()),
|
|
returnValueForIsCascadingUse};
|
|
}
|
|
// Look in the generic parameters after checking our local declaration.
|
|
addGenericParametersForFunction(AFD);
|
|
if (isFinishedWithLookupNowThatIsAboutToLookForOuterResults())
|
|
return None;
|
|
// clang-format off
|
|
return PerScopeLookupState{
|
|
false,
|
|
AFD,
|
|
None,
|
|
returnValueForIsCascadingUse};
|
|
// clang-format on
|
|
}
|
|
|
|
Optional<UnqualifiedLookupFactory::PerScopeLookupState>
|
|
UnqualifiedLookupFactory::lookupInClosure(AbstractClosureExpr *ACE,
|
|
Optional<bool> isCascadingUse) {
|
|
// Look for local variables; normally, the parser resolves these
|
|
// for us, but it can't do the right thing inside local types.
|
|
if (Loc.isValid()) {
|
|
if (auto *CE = dyn_cast<ClosureExpr>(ACE)) {
|
|
namelookup::FindLocalVal localVal(SM, Loc, Consumer);
|
|
if (auto body = CE->getBody())
|
|
localVal.visit(body);
|
|
if (isFinishedWithLookupNowThatIsAboutToLookForOuterResults())
|
|
return None;
|
|
if (auto params = CE->getParameters())
|
|
localVal.checkParameterList(params);
|
|
if (isFinishedWithLookupNowThatIsAboutToLookForOuterResults())
|
|
return None;
|
|
}
|
|
}
|
|
// clang-format off
|
|
return PerScopeLookupState{
|
|
false,
|
|
ACE,
|
|
None,
|
|
resolveIsCascadingUse(ACE, isCascadingUse,
|
|
/*onlyCareAboutFunctionBody=*/false)};
|
|
// clang-format on
|
|
}
|
|
|
|
// TODO: calls of isCascadingContextForLookup do extra work?
|
|
// or factor out isCascadingUse to another dispatch??
|
|
Optional<UnqualifiedLookupFactory::PerScopeLookupState>
|
|
UnqualifiedLookupFactory::lookupInExtension(ExtensionDecl *ED,
|
|
Optional<bool> isCascadingUse) {
|
|
// clang-format off
|
|
return PerScopeLookupState{
|
|
false,
|
|
ED,
|
|
shouldLookupMembers(ED, Loc)
|
|
? Optional<PlacesToSearch>(PlacesToSearch(ED, ED, ED, ED))
|
|
: None,
|
|
resolveIsCascadingUse(ED, isCascadingUse,
|
|
/*onlyCareAboutFunctionBody=*/false)};
|
|
// clang-format on
|
|
}
|
|
|
|
// TODO: Unify lookupInNominalType w/ lookupInExtension?
|
|
Optional<UnqualifiedLookupFactory::PerScopeLookupState>
|
|
UnqualifiedLookupFactory::lookupInNominalType(NominalTypeDecl *ND,
|
|
Optional<bool> isCascadingUse) {
|
|
// clang-format off
|
|
return PerScopeLookupState{
|
|
false,
|
|
ND,
|
|
shouldLookupMembers(ND, Loc)
|
|
? Optional<PlacesToSearch>(PlacesToSearch(ND, ND, ND, ND))
|
|
: None,
|
|
resolveIsCascadingUse(ND, isCascadingUse,
|
|
/*onlyCareAboutFunctionBody=*/false)};
|
|
|
|
// clang-format on
|
|
}
|
|
|
|
Optional<UnqualifiedLookupFactory::PerScopeLookupState>
|
|
UnqualifiedLookupFactory::lookupInDefaultArgumentInitializer(
|
|
DefaultArgumentInitializer *I, Optional<bool> isCascadingUse) {
|
|
// In a default argument, skip immediately out of both the
|
|
// initializer and the function.
|
|
// clang-format off
|
|
return PerScopeLookupState{true,
|
|
I->getParent()->getParent(),
|
|
None,
|
|
false};
|
|
// clang-format on
|
|
}
|
|
|
|
bool UnqualifiedLookupFactory::isOutsideBodyOfFunction(
|
|
const AbstractFunctionDecl *const AFD) const {
|
|
return !AFD->isImplicit() && Loc.isValid() &&
|
|
AFD->getBodySourceRange().isValid() &&
|
|
!SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc);
|
|
}
|
|
|
|
Optional<UnqualifiedLookupFactory::PerScopeLookupState>
|
|
UnqualifiedLookupFactory::lookupInMiscContext(DeclContext *dc,
|
|
Optional<bool> isCascadingUse) {
|
|
// clang-format off
|
|
assert(isa<TopLevelCodeDecl>(dc) ||
|
|
isa<Initializer>(dc) ||
|
|
isa<TypeAliasDecl>(dc) ||
|
|
isa<SubscriptDecl>(dc));
|
|
return PerScopeLookupState{
|
|
false,
|
|
dc,
|
|
None,
|
|
resolveIsCascadingUse(DC, isCascadingUse,
|
|
/*onlyCareAboutFunctionBody=*/false)};
|
|
// clang-format on
|
|
}
|
|
|
|
// TODO enum instead of bool?
|
|
bool UnqualifiedLookupFactory::addLocalVariableResults(DeclContext *dc) {
|
|
if (auto SF = dyn_cast<SourceFile>(dc)) {
|
|
if (Loc.isValid()) {
|
|
// Look for local variables in top-level code; normally, the parser
|
|
// resolves these for us, but it can't do the right thing for
|
|
// local types.
|
|
namelookup::FindLocalVal localVal(SM, Loc, Consumer);
|
|
localVal.checkSourceFile(*SF);
|
|
if (isFinishedWithLookupNowThatIsAboutToLookForOuterResults())
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// TODO: make member fn of PlacesToSearch, take ref to Result as arg
|
|
// clang-format off
|
|
void UnqualifiedLookupFactory::searchPlacesToSearch(
|
|
PlacesToSearch placesToSearch,
|
|
DeclName Name,
|
|
const bool isCascadingUse,
|
|
const NLOptions baseNLOptions,
|
|
DeclContext *contextForLookup) {
|
|
// clang-format on
|
|
const NLOptions options =
|
|
baseNLOptions | (isCascadingUse ? NL_KnownCascadingDependency
|
|
: NL_KnownNonCascadingDependency);
|
|
|
|
SmallVector<ValueDecl *, 4> Lookup;
|
|
contextForLookup->lookupQualified(placesToSearch.places, Name, options,
|
|
Lookup);
|
|
for (auto Result : Lookup)
|
|
Results.push_back(
|
|
LookupResultEntry(placesToSearch.whereValueIsMember(Result), Result));
|
|
}
|
|
|
|
bool UnqualifiedLookupFactory::handleUnavailableInnerResults(
|
|
const size_t startIndexOfInnerResults) {
|
|
// An optimization:
|
|
assert(Results.size() >= startIndexOfInnerResults);
|
|
if (Results.size() == startIndexOfInnerResults)
|
|
return false;
|
|
// Predicate that determines whether a lookup result should
|
|
// be unavailable except as a last-ditch effort.
|
|
auto unavailableLookupResult = [&](const LookupResultEntry &result) {
|
|
auto &effectiveVersion = Ctx.LangOpts.EffectiveLanguageVersion;
|
|
return result.getValueDecl()->getAttrs().isUnavailableInSwiftVersion(
|
|
effectiveVersion);
|
|
};
|
|
|
|
// If all of the results we found are unavailable, keep looking.
|
|
auto begin = Results.begin() + startIndexOfInnerResults;
|
|
if (std::all_of(begin, Results.end(), unavailableLookupResult)) {
|
|
UnavailableInnerResults.append(begin, Results.end());
|
|
Results.erase(begin, Results.end());
|
|
return false;
|
|
}
|
|
if (DebugClient)
|
|
filterForDiscriminator(Results, DebugClient);
|
|
|
|
return isFinishedWithLookupNowThatIsAboutToLookForOuterResults();
|
|
}
|
|
|
|
void UnqualifiedLookupFactory::recordDependencyOnTopLevelName(
|
|
DeclContext *topLevelContext, DeclName name, bool isCascadingUse) {
|
|
recordLookupOfTopLevelName(topLevelContext, Name, isCascadingUse);
|
|
recordedSF = dyn_cast<SourceFile>(topLevelContext);
|
|
recordedName = Name;
|
|
recordedIsCascadingUse = isCascadingUse;
|
|
}
|
|
|
|
void UnqualifiedLookupFactory::addPrivateImports(DeclContext *const dc) {
|
|
// Add private imports to the extra search list.
|
|
SmallVector<ModuleDecl::ImportedModule, 8> extraImports;
|
|
if (auto FU = dyn_cast<FileUnit>(dc))
|
|
FU->getImportedModules(extraImports, ModuleDecl::ImportFilter::Private);
|
|
|
|
using namespace namelookup;
|
|
SmallVector<ValueDecl *, 8> CurModuleResults;
|
|
auto resolutionKind = isOriginallyTypeLookup ? ResolutionKind::TypesOnly
|
|
: ResolutionKind::Overloadable;
|
|
lookupInModule(&M, {}, Name, CurModuleResults, NLKind::UnqualifiedLookup,
|
|
resolutionKind, TypeResolver, dc, extraImports);
|
|
|
|
// Always perform name shadowing for type lookup.
|
|
if (options.contains(Flags::TypeLookup)) {
|
|
removeShadowedDecls(CurModuleResults, &M);
|
|
}
|
|
|
|
for (auto VD : CurModuleResults)
|
|
Results.push_back(LookupResultEntry(VD));
|
|
|
|
if (DebugClient)
|
|
filterForDiscriminator(Results, DebugClient);
|
|
}
|
|
|
|
bool UnqualifiedLookupFactory::addNamesKnownToDebugClient(DeclContext *dc) {
|
|
if (Name.isSimpleName() && DebugClient)
|
|
DebugClient->lookupAdditions(Name.getBaseName(), dc, Loc,
|
|
isOriginallyTypeLookup, Results);
|
|
|
|
// If we've found something, we're done.
|
|
return isFinishedWithLookupNowThatIsAboutToLookForOuterResults(
|
|
/*noMoreOuterResults=*/true);
|
|
}
|
|
|
|
bool UnqualifiedLookupFactory::addUnavailableInnerResults() {
|
|
Results = std::move(UnavailableInnerResults);
|
|
return isFinishedWithLookupNowThatIsAboutToLookForOuterResults(
|
|
/*noMoreOuterResults=*/true);
|
|
}
|
|
|
|
bool UnqualifiedLookupFactory::lookForAModuleWithTheGivenName(
|
|
DeclContext *const dc) {
|
|
using namespace namelookup;
|
|
if (!Name.isSimpleName())
|
|
return true;
|
|
|
|
// Look for a module with the given name.
|
|
if (Name.isSimpleName(M.getName())) {
|
|
Results.push_back(LookupResultEntry(&M));
|
|
return isFinishedWithLookupNowThatIsAboutToLookForOuterResults(
|
|
/*noMoreOuterResults=*/true);
|
|
}
|
|
ModuleDecl *desiredModule = Ctx.getLoadedModule(Name.getBaseIdentifier());
|
|
if (!desiredModule && Name == Ctx.TheBuiltinModule->getName())
|
|
desiredModule = Ctx.TheBuiltinModule;
|
|
if (desiredModule) {
|
|
forAllVisibleModules(
|
|
dc, [&](const ModuleDecl::ImportedModule &import) -> bool {
|
|
if (import.second == desiredModule) {
|
|
Results.push_back(LookupResultEntry(import.second));
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#pragma mark Member lookup table
|
|
|
|
void LazyMemberLoader::anchor() {}
|
|
|
|
void LazyConformanceLoader::anchor() {}
|
|
|
|
/// Lookup table used to store members of a nominal type (and its extensions)
|
|
/// for fast retrieval.
|
|
class swift::MemberLookupTable {
|
|
/// The last extension that was included within the member lookup table's
|
|
/// results.
|
|
ExtensionDecl *LastExtensionIncluded = nullptr;
|
|
|
|
/// The type of the internal lookup table.
|
|
typedef llvm::DenseMap<DeclName, llvm::TinyPtrVector<ValueDecl *>>
|
|
LookupTable;
|
|
|
|
/// Lookup table mapping names to the set of declarations with that name.
|
|
LookupTable Lookup;
|
|
|
|
public:
|
|
/// Create a new member lookup table.
|
|
explicit MemberLookupTable(ASTContext &ctx);
|
|
|
|
/// Update a lookup table with members from newly-added extensions.
|
|
void updateLookupTable(NominalTypeDecl *nominal);
|
|
|
|
/// Add the given member to the lookup table.
|
|
void addMember(Decl *members);
|
|
|
|
/// Add the given members to the lookup table.
|
|
void addMembers(DeclRange members);
|
|
|
|
/// Iterator into the lookup table.
|
|
typedef LookupTable::iterator iterator;
|
|
|
|
iterator begin() { return Lookup.begin(); }
|
|
iterator end() { return Lookup.end(); }
|
|
|
|
iterator find(DeclName name) {
|
|
return Lookup.find(name);
|
|
}
|
|
|
|
void dump(llvm::raw_ostream &os) const {
|
|
os << "LastExtensionIncluded:\n";
|
|
if (LastExtensionIncluded)
|
|
LastExtensionIncluded->printContext(os, 2);
|
|
else
|
|
os << " nullptr\n";
|
|
|
|
os << "Lookup:\n ";
|
|
for (auto &pair : Lookup) {
|
|
pair.getFirst().print(os) << ":\n ";
|
|
for (auto &decl : pair.getSecond()) {
|
|
os << "- ";
|
|
decl->dumpRef(os);
|
|
os << "\n ";
|
|
}
|
|
}
|
|
os << "\n";
|
|
}
|
|
|
|
LLVM_ATTRIBUTE_DEPRECATED(void dump() const LLVM_ATTRIBUTE_USED,
|
|
"only for use within the debugger") {
|
|
dump(llvm::errs());
|
|
}
|
|
|
|
// Mark all Decls in this table as not-resident in a table, drop
|
|
// references to them. Should only be called when this was not fully-populated
|
|
// from an IterableDeclContext.
|
|
void clear() {
|
|
// LastExtensionIncluded would only be non-null if this was populated from
|
|
// an IterableDeclContext (though it might still be null in that case).
|
|
assert(LastExtensionIncluded == nullptr);
|
|
for (auto const &i : Lookup) {
|
|
for (auto d : i.getSecond()) {
|
|
d->setAlreadyInLookupTable(false);
|
|
}
|
|
}
|
|
Lookup.clear();
|
|
}
|
|
|
|
// Only allow allocation of member lookup tables using the allocator in
|
|
// ASTContext or by doing a placement new.
|
|
void *operator new(size_t Bytes, ASTContext &C,
|
|
unsigned Alignment = alignof(MemberLookupTable)) {
|
|
return C.Allocate(Bytes, Alignment);
|
|
}
|
|
void *operator new(size_t Bytes, void *Mem) {
|
|
assert(Mem);
|
|
return Mem;
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
/// Stores the set of Objective-C methods with a given selector within the
|
|
/// Objective-C method lookup table.
|
|
struct StoredObjCMethods {
|
|
/// The generation count at which this list was last updated.
|
|
unsigned Generation = 0;
|
|
|
|
/// The set of methods with the given selector.
|
|
llvm::TinyPtrVector<AbstractFunctionDecl *> Methods;
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
/// Class member lookup table, which is a member lookup table with a second
|
|
/// table for lookup based on Objective-C selector.
|
|
class ClassDecl::ObjCMethodLookupTable
|
|
: public llvm::DenseMap<std::pair<ObjCSelector, char>,
|
|
StoredObjCMethods>
|
|
{
|
|
public:
|
|
// Only allow allocation of member lookup tables using the allocator in
|
|
// ASTContext or by doing a placement new.
|
|
void *operator new(size_t Bytes, ASTContext &C,
|
|
unsigned Alignment = alignof(MemberLookupTable)) {
|
|
return C.Allocate(Bytes, Alignment);
|
|
}
|
|
void *operator new(size_t Bytes, void *Mem) {
|
|
assert(Mem);
|
|
return Mem;
|
|
}
|
|
};
|
|
|
|
MemberLookupTable::MemberLookupTable(ASTContext &ctx) {
|
|
// Register a cleanup with the ASTContext to call the lookup table
|
|
// destructor.
|
|
ctx.addCleanup([this]() {
|
|
this->~MemberLookupTable();
|
|
});
|
|
}
|
|
|
|
void MemberLookupTable::addMember(Decl *member) {
|
|
// Only value declarations matter.
|
|
auto vd = dyn_cast<ValueDecl>(member);
|
|
if (!vd)
|
|
return;
|
|
|
|
// @_implements members get added under their declared name.
|
|
auto A = vd->getAttrs().getAttribute<ImplementsAttr>();
|
|
|
|
// Unnamed entities w/o @_implements synonyms cannot be found by name lookup.
|
|
if (!A && !vd->hasName())
|
|
return;
|
|
|
|
// If this declaration is already in the lookup table, don't add it
|
|
// again.
|
|
if (vd->isAlreadyInLookupTable()) {
|
|
return;
|
|
}
|
|
vd->setAlreadyInLookupTable();
|
|
|
|
// Add this declaration to the lookup set under its compound name and simple
|
|
// name.
|
|
vd->getFullName().addToLookupTable(Lookup, vd);
|
|
|
|
// And if given a synonym, under that name too.
|
|
if (A)
|
|
A->getMemberName().addToLookupTable(Lookup, vd);
|
|
}
|
|
|
|
void MemberLookupTable::addMembers(DeclRange members) {
|
|
for (auto member : members) {
|
|
addMember(member);
|
|
}
|
|
}
|
|
|
|
void MemberLookupTable::updateLookupTable(NominalTypeDecl *nominal) {
|
|
// If the last extension we included is the same as the last known extension,
|
|
// we're already up-to-date.
|
|
if (LastExtensionIncluded == nominal->LastExtension)
|
|
return;
|
|
|
|
// Add members from each of the extensions that we have not yet visited.
|
|
for (auto next = LastExtensionIncluded
|
|
? LastExtensionIncluded->NextExtension.getPointer()
|
|
: nominal->FirstExtension;
|
|
next;
|
|
(LastExtensionIncluded = next,next = next->NextExtension.getPointer())) {
|
|
addMembers(next->getMembers());
|
|
}
|
|
}
|
|
|
|
void NominalTypeDecl::addedMember(Decl *member) {
|
|
// Remember if we added a destructor.
|
|
if (auto *CD = dyn_cast<ClassDecl>(this))
|
|
if (isa<DestructorDecl>(member))
|
|
CD->setHasDestructor();
|
|
|
|
// If we have a lookup table, add the new member to it.
|
|
if (LookupTable.getPointer()) {
|
|
LookupTable.getPointer()->addMember(member);
|
|
}
|
|
}
|
|
|
|
void NominalTypeDecl::addedExtension(ExtensionDecl * ext) {
|
|
if (hasLazyMembers())
|
|
setLookupTablePopulated(false);
|
|
}
|
|
|
|
void ExtensionDecl::addedMember(Decl *member) {
|
|
if (NextExtension.getInt()) {
|
|
auto nominal = getExtendedNominal();
|
|
if (!nominal)
|
|
return;
|
|
|
|
if (nominal->LookupTable.getPointer() &&
|
|
nominal->isLookupTablePopulated()) {
|
|
// Make sure we have the complete list of extensions.
|
|
// FIXME: This is completely unnecessary. We want to determine whether
|
|
// our own extension has already been included in the lookup table.
|
|
(void)nominal->getExtensions();
|
|
|
|
nominal->LookupTable.getPointer()->addMember(member);
|
|
}
|
|
}
|
|
}
|
|
|
|
// For lack of anywhere more sensible to put it, here's a diagram of the pieces
|
|
// involved in finding members and extensions of a NominalTypeDecl.
|
|
//
|
|
// ┌────────────────────────────┬─┐
|
|
// │IterableDeclContext │ │ ┌─────────────────────────────┐
|
|
// │------------------- │ │ │┌───────────────┬┐ ▼
|
|
// │Decl *LastDecl ───────────┼─┼─────┘│Decl ││ ┌───────────────┬┐
|
|
// │Decl *FirstDecl ───────────┼─┼─────▶│---- ││ │Decl ││
|
|
// │ │ │ │Decl *NextDecl├┼─▶│---- ││
|
|
// │bool HasLazyMembers │ │ ├───────────────┘│ │Decl *NextDecl ││
|
|
// │IterableDeclContextKind Kind│ │ │ │ ├───────────────┘│
|
|
// │ │ │ │ValueDecl │ │ │
|
|
// ├────────────────────────────┘ │ │--------- │ │ValueDecl │
|
|
// │ │ │DeclName Name │ │--------- │
|
|
// │NominalTypeDecl │ └────────────────┘ │DeclName Name │
|
|
// │--------------- │ ▲ └────────────────┘
|
|
// │ExtensionDecl *FirstExtension─┼────────┐ │ ▲
|
|
// │ExtensionDecl *LastExtension ─┼───────┐│ │ └───┐
|
|
// │ │ ││ └──────────────────────┐│
|
|
// │MemberLookupTable *LookupTable├─┐ ││ ││
|
|
// │bool LookupTableComplete │ │ ││ ┌─────────────────┐ ││
|
|
// └──────────────────────────────┘ │ ││ │ExtensionDecl │ ││
|
|
// │ ││ │------------- │ ││
|
|
// ┌─────────────┘ │└────▶│ExtensionDecl │ ││
|
|
// │ │ │ *NextExtension ├──┐ ││
|
|
// ▼ │ └─────────────────┘ │ ││
|
|
// ┌─────────────────────────────────────┐│ ┌─────────────────┐ │ ││
|
|
// │MemberLookupTable ││ │ExtensionDecl │ │ ││
|
|
// │----------------- ││ │------------- │ │ ││
|
|
// │ExtensionDecl *LastExtensionIncluded ├┴─────▶│ExtensionDecl │◀─┘ ││
|
|
// │ │ │ *NextExtension │ ││
|
|
// │┌───────────────────────────────────┐│ └─────────────────┘ ││
|
|
// ││DenseMap<Declname, ...> LookupTable││ ││
|
|
// ││-----------------------------------││ ┌──────────────────────────┐ ││
|
|
// ││[NameA] TinyPtrVector<ValueDecl *> ││ │TinyPtrVector<ValueDecl *>│ ││
|
|
// ││[NameB] TinyPtrVector<ValueDecl *> ││ │--------------------------│ ││
|
|
// ││[NameC] TinyPtrVector<ValueDecl *>─┼┼─▶│[0] ValueDecl * ─────┼─┘│
|
|
// │└───────────────────────────────────┘│ │[1] ValueDecl * ─────┼──┘
|
|
// └─────────────────────────────────────┘ └──────────────────────────┘
|
|
//
|
|
// The HasLazyMembers, Kind, and LookupTableComplete fields are packed into
|
|
// PointerIntPairs so don't go grepping for them; but for purposes of
|
|
// illustration they are effectively their own fields.
|
|
//
|
|
// MemberLookupTable is populated en-masse when the IterableDeclContext's
|
|
// (IDC's) list of Decls is populated. But MemberLookupTable can also be
|
|
// populated incrementally by one-name-at-a-time lookups by lookupDirect, in
|
|
// which case those Decls are _not_ added to the IDC's list. They are cached in
|
|
// the loader they come from, lifecycle-wise, and are added to the
|
|
// MemberLookupTable to accelerate subsequent retrieval, but the IDC is not
|
|
// considered populated until someone calls getMembers().
|
|
//
|
|
// If the IDC list is later populated and/or an extension is added _after_
|
|
// MemberLookupTable is constructed (and possibly has entries in it),
|
|
// MemberLookupTable is purged and reconstructed from IDC's list.
|
|
//
|
|
// In all lookup routines, the 'ignoreNewExtensions' flag means that
|
|
// lookup should only use the set of extensions already observed.
|
|
|
|
static bool
|
|
populateLookupTableEntryFromLazyIDCLoader(ASTContext &ctx,
|
|
MemberLookupTable &LookupTable,
|
|
DeclName name,
|
|
IterableDeclContext *IDC) {
|
|
if (IDC->isLoadingLazyMembers()) {
|
|
return false;
|
|
}
|
|
IDC->setLoadingLazyMembers(true);
|
|
auto ci = ctx.getOrCreateLazyIterableContextData(IDC,
|
|
/*lazyLoader=*/nullptr);
|
|
if (auto res = ci->loader->loadNamedMembers(IDC, name.getBaseName(),
|
|
ci->memberData)) {
|
|
IDC->setLoadingLazyMembers(false);
|
|
if (auto s = ctx.Stats) {
|
|
++s->getFrontendCounters().NamedLazyMemberLoadSuccessCount;
|
|
}
|
|
for (auto d : *res) {
|
|
LookupTable.addMember(d);
|
|
}
|
|
return false;
|
|
} else {
|
|
IDC->setLoadingLazyMembers(false);
|
|
if (auto s = ctx.Stats) {
|
|
++s->getFrontendCounters().NamedLazyMemberLoadFailureCount;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static void populateLookupTableEntryFromCurrentMembersWithoutLoading(
|
|
ASTContext &ctx, MemberLookupTable &LookupTable, DeclName name,
|
|
IterableDeclContext *IDC) {
|
|
for (auto m : IDC->getCurrentMembersWithoutLoading()) {
|
|
if (auto v = dyn_cast<ValueDecl>(m)) {
|
|
if (v->getFullName().matchesRef(name.getBaseName())) {
|
|
LookupTable.addMember(m);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool
|
|
populateLookupTableEntryFromExtensions(ASTContext &ctx,
|
|
MemberLookupTable &table,
|
|
NominalTypeDecl *nominal,
|
|
DeclName name,
|
|
bool ignoreNewExtensions) {
|
|
if (!ignoreNewExtensions) {
|
|
for (auto e : nominal->getExtensions()) {
|
|
if (e->wasDeserialized() || e->hasClangNode()) {
|
|
if (populateLookupTableEntryFromLazyIDCLoader(ctx, table,
|
|
name, e)) {
|
|
return true;
|
|
}
|
|
} else {
|
|
populateLookupTableEntryFromCurrentMembersWithoutLoading(ctx, table,
|
|
name, e);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool NominalTypeDecl::isLookupTablePopulated() const {
|
|
return LookupTable.getInt();
|
|
}
|
|
|
|
void NominalTypeDecl::setLookupTablePopulated(bool value) {
|
|
LookupTable.setInt(value);
|
|
}
|
|
|
|
void NominalTypeDecl::prepareLookupTable(bool ignoreNewExtensions) {
|
|
// If we haven't allocated the lookup table yet, do so now.
|
|
if (!LookupTable.getPointer()) {
|
|
auto &ctx = getASTContext();
|
|
LookupTable.setPointer(new (ctx) MemberLookupTable(ctx));
|
|
}
|
|
|
|
if (hasLazyMembers()) {
|
|
// Lazy members: if the table needs population, populate the table _only
|
|
// from those members already in the IDC member list_ such as implicits or
|
|
// globals-as-members, then update table entries from the extensions that
|
|
// have the same names as any such initial-population members.
|
|
if (!isLookupTablePopulated()) {
|
|
setLookupTablePopulated(true);
|
|
LookupTable.getPointer()->addMembers(getCurrentMembersWithoutLoading());
|
|
|
|
llvm::SetVector<DeclName> baseNamesPresent;
|
|
for (auto entry : *LookupTable.getPointer()) {
|
|
baseNamesPresent.insert(entry.getFirst().getBaseName());
|
|
}
|
|
|
|
for (auto baseName : baseNamesPresent) {
|
|
populateLookupTableEntryFromExtensions(getASTContext(),
|
|
*LookupTable.getPointer(),
|
|
this, baseName,
|
|
ignoreNewExtensions);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// No lazy members: if the table needs population, populate the table
|
|
// en-masse; and in either case update the extensions.
|
|
if (!isLookupTablePopulated()) {
|
|
setLookupTablePopulated(true);
|
|
LookupTable.getPointer()->addMembers(getMembers());
|
|
}
|
|
if (!ignoreNewExtensions) {
|
|
LookupTable.getPointer()->updateLookupTable(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NominalTypeDecl::makeMemberVisible(ValueDecl *member) {
|
|
if (!LookupTable.getPointer()) {
|
|
auto &ctx = getASTContext();
|
|
LookupTable.setPointer(new (ctx) MemberLookupTable(ctx));
|
|
}
|
|
|
|
LookupTable.getPointer()->addMember(member);
|
|
}
|
|
|
|
|
|
static TinyPtrVector<ValueDecl *>
|
|
maybeFilterOutAttrImplements(TinyPtrVector<ValueDecl *> decls,
|
|
DeclName name,
|
|
bool includeAttrImplements) {
|
|
if (includeAttrImplements)
|
|
return decls;
|
|
TinyPtrVector<ValueDecl*> result;
|
|
for (auto V : decls) {
|
|
// Filter-out any decl that doesn't have the name we're looking for
|
|
// (asserting as a consistency-check that such entries all have
|
|
// @_implements attrs for the name!)
|
|
if (V->getFullName().matchesRef(name)) {
|
|
result.push_back(V);
|
|
} else {
|
|
auto A = V->getAttrs().getAttribute<ImplementsAttr>();
|
|
assert(A && A->getMemberName().matchesRef(name));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
TinyPtrVector<ValueDecl *> NominalTypeDecl::lookupDirect(
|
|
DeclName name,
|
|
OptionSet<LookupDirectFlags> flags) {
|
|
ASTContext &ctx = getASTContext();
|
|
if (auto s = ctx.Stats) {
|
|
++s->getFrontendCounters().NominalTypeLookupDirectCount;
|
|
}
|
|
|
|
// We only use NamedLazyMemberLoading when a user opts-in and we have
|
|
// not yet loaded all the members into the IDC list in the first place.
|
|
bool useNamedLazyMemberLoading = (ctx.LangOpts.NamedLazyMemberLoading &&
|
|
hasLazyMembers());
|
|
|
|
bool ignoreNewExtensions =
|
|
flags.contains(LookupDirectFlags::IgnoreNewExtensions);
|
|
|
|
bool includeAttrImplements =
|
|
flags.contains(LookupDirectFlags::IncludeAttrImplements);
|
|
|
|
// FIXME: At present, lazy member is not able to find inherited constructors
|
|
// in imported classes, because SwiftDeclConverter::importInheritedConstructors()
|
|
// is only called via ClangImporter::Implementation::loadAllMembers().
|
|
if (hasClangNode() &&
|
|
name.getBaseName() == DeclBaseName::createConstructor())
|
|
useNamedLazyMemberLoading = false;
|
|
|
|
LLVM_DEBUG(llvm::dbgs() << getNameStr() << ".lookupDirect("
|
|
<< name << ", " << ignoreNewExtensions << ")"
|
|
<< ", isLookupTablePopulated()=" << isLookupTablePopulated()
|
|
<< ", hasLazyMembers()=" << hasLazyMembers()
|
|
<< ", useNamedLazyMemberLoading=" << useNamedLazyMemberLoading
|
|
<< "\n");
|
|
|
|
// We check the LookupTable at most twice, possibly treating a miss in the
|
|
// first try as a cache-miss that we then do a cache-fill on, and retry.
|
|
for (int i = 0; i < 2; ++i) {
|
|
|
|
// First, if we're _not_ doing NamedLazyMemberLoading, we make sure we've
|
|
// populated the IDC and brought it up to date with any extensions. This
|
|
// will flip the hasLazyMembers() flag to false as well.
|
|
if (!useNamedLazyMemberLoading) {
|
|
// It's possible that the lookup table exists but has information in it
|
|
// that is either currently out of date or soon to be out of date.
|
|
// This can happen two ways:
|
|
//
|
|
// - We've not yet indexed the members we have (isLookupTablePopulated()
|
|
// is zero).
|
|
//
|
|
// - We've still got more lazy members left to load; this can happen
|
|
// even if we _did_ index some members.
|
|
//
|
|
// In either of these cases, we want to reset the table to empty and
|
|
// mark it as needing reconstruction.
|
|
if (LookupTable.getPointer() &&
|
|
(hasLazyMembers() || !isLookupTablePopulated())) {
|
|
LookupTable.getPointer()->clear();
|
|
setLookupTablePopulated(false);
|
|
}
|
|
|
|
(void)getMembers();
|
|
|
|
// Make sure we have the complete list of members (in this nominal and in
|
|
// all extensions).
|
|
if (!ignoreNewExtensions) {
|
|
for (auto E : getExtensions())
|
|
(void)E->getMembers();
|
|
}
|
|
}
|
|
|
|
// Next, in all cases, prepare the lookup table for use, possibly
|
|
// repopulating it from the IDC if the IDC member list has just grown.
|
|
prepareLookupTable(ignoreNewExtensions);
|
|
|
|
// Look for a declaration with this name.
|
|
auto known = LookupTable.getPointer()->find(name);
|
|
|
|
// We found something; return it.
|
|
if (known != LookupTable.getPointer()->end())
|
|
return maybeFilterOutAttrImplements(known->second, name,
|
|
includeAttrImplements);
|
|
|
|
// If we have no more second chances, stop now.
|
|
if (!useNamedLazyMemberLoading || i > 0)
|
|
break;
|
|
|
|
// If we get here, we had a cache-miss and _are_ using
|
|
// NamedLazyMemberLoading. Try to populate a _single_ entry in the
|
|
// MemberLookupTable from both this nominal and all of its extensions, and
|
|
// retry. Any failure to load here flips the useNamedLazyMemberLoading to
|
|
// false, and we fall back to loading all members during the retry.
|
|
auto &Table = *LookupTable.getPointer();
|
|
if (populateLookupTableEntryFromLazyIDCLoader(ctx, Table,
|
|
name, this) ||
|
|
populateLookupTableEntryFromExtensions(ctx, Table, this, name,
|
|
ignoreNewExtensions)) {
|
|
useNamedLazyMemberLoading = false;
|
|
}
|
|
}
|
|
|
|
// None of our attempts found anything.
|
|
return { };
|
|
}
|
|
|
|
void ClassDecl::createObjCMethodLookup() {
|
|
assert(!ObjCMethodLookup && "Already have an Objective-C member table");
|
|
auto &ctx = getASTContext();
|
|
ObjCMethodLookup = new (ctx) ObjCMethodLookupTable();
|
|
|
|
// Register a cleanup with the ASTContext to call the lookup table
|
|
// destructor.
|
|
ctx.addCleanup([this]() {
|
|
this->ObjCMethodLookup->~ObjCMethodLookupTable();
|
|
});
|
|
}
|
|
|
|
MutableArrayRef<AbstractFunctionDecl *>
|
|
ClassDecl::lookupDirect(ObjCSelector selector, bool isInstance) {
|
|
if (!ObjCMethodLookup) {
|
|
createObjCMethodLookup();
|
|
}
|
|
|
|
// If any modules have been loaded since we did the search last (or if we
|
|
// hadn't searched before), look in those modules, too.
|
|
auto &stored = (*ObjCMethodLookup)[{selector, isInstance}];
|
|
ASTContext &ctx = getASTContext();
|
|
if (ctx.getCurrentGeneration() > stored.Generation) {
|
|
ctx.loadObjCMethods(this, selector, isInstance, stored.Generation,
|
|
stored.Methods);
|
|
stored.Generation = ctx.getCurrentGeneration();
|
|
}
|
|
|
|
return { stored.Methods.begin(), stored.Methods.end() };
|
|
}
|
|
|
|
void ClassDecl::recordObjCMethod(AbstractFunctionDecl *method,
|
|
ObjCSelector selector) {
|
|
if (!ObjCMethodLookup) {
|
|
createObjCMethodLookup();
|
|
}
|
|
|
|
// Record the method.
|
|
bool isInstanceMethod = method->isObjCInstanceMethod();
|
|
auto &vec = (*ObjCMethodLookup)[{selector, isInstanceMethod}].Methods;
|
|
|
|
// In a non-empty vector, we could have duplicates or conflicts.
|
|
if (!vec.empty()) {
|
|
// Check whether we have a duplicate. This only checks more than one
|
|
// element in ill-formed code, so the linear search is acceptable.
|
|
if (std::find(vec.begin(), vec.end(), method) != vec.end())
|
|
return;
|
|
|
|
if (vec.size() == 1) {
|
|
// We have a conflict.
|
|
getASTContext().recordObjCMethodConflict(this, selector,
|
|
isInstanceMethod);
|
|
}
|
|
} else {
|
|
// Record the first method that has this selector.
|
|
getASTContext().recordObjCMethod(method);
|
|
}
|
|
|
|
vec.push_back(method);
|
|
}
|
|
|
|
/// Configure name lookup for the given declaration context and options.
|
|
///
|
|
/// This utility is used by qualified name lookup.
|
|
static void configureLookup(const DeclContext *dc,
|
|
NLOptions &options,
|
|
ReferencedNameTracker *&tracker,
|
|
bool &isLookupCascading) {
|
|
auto &ctx = dc->getASTContext();
|
|
if (ctx.isAccessControlDisabled())
|
|
options |= NL_IgnoreAccessControl;
|
|
|
|
// Find the dependency tracker we'll need for this lookup.
|
|
tracker = nullptr;
|
|
if (auto containingSourceFile =
|
|
dyn_cast<SourceFile>(dc->getModuleScopeContext())) {
|
|
tracker = containingSourceFile->getReferencedNameTracker();
|
|
}
|
|
|
|
auto checkLookupCascading = [dc, options]() -> Optional<bool> {
|
|
switch (static_cast<unsigned>(options & NL_KnownDependencyMask)) {
|
|
case 0:
|
|
return dc->isCascadingContextForLookup(
|
|
/*functionsAreNonCascading=*/false);
|
|
case NL_KnownNonCascadingDependency:
|
|
return false;
|
|
case NL_KnownCascadingDependency:
|
|
return true;
|
|
case NL_KnownNoDependency:
|
|
return None;
|
|
default:
|
|
// FIXME: Use llvm::CountPopulation_64 when that's declared constexpr.
|
|
#if defined(__clang__) || defined(__GNUC__)
|
|
static_assert(__builtin_popcountll(NL_KnownDependencyMask) == 2,
|
|
"mask should only include four values");
|
|
#endif
|
|
llvm_unreachable("mask only includes four values");
|
|
}
|
|
};
|
|
|
|
// Determine whether a lookup here will cascade.
|
|
isLookupCascading = false;
|
|
if (tracker) {
|
|
if (auto maybeLookupCascade = checkLookupCascading())
|
|
isLookupCascading = maybeLookupCascade.getValue();
|
|
else
|
|
tracker = nullptr;
|
|
}
|
|
}
|
|
|
|
/// Determine whether the given declaration is an acceptable lookup
|
|
/// result when searching from the given DeclContext.
|
|
static bool isAcceptableLookupResult(const DeclContext *dc,
|
|
NLOptions options,
|
|
ValueDecl *decl,
|
|
bool onlyCompleteObjectInits) {
|
|
// Filter out designated initializers, if requested.
|
|
if (onlyCompleteObjectInits) {
|
|
if (auto ctor = dyn_cast<ConstructorDecl>(decl)) {
|
|
if (isa<ClassDecl>(ctor->getDeclContext()) && !ctor->isInheritable())
|
|
return false;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Ignore stub implementations.
|
|
if (auto ctor = dyn_cast<ConstructorDecl>(decl)) {
|
|
if (ctor->hasStubImplementation())
|
|
return false;
|
|
}
|
|
|
|
// Check access.
|
|
if (!(options & NL_IgnoreAccessControl)) {
|
|
return decl->isAccessibleFrom(dc);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Only name lookup has gathered a set of results, perform any necessary
|
|
/// steps to prune the result set before returning it to the caller.
|
|
static bool finishLookup(const DeclContext *dc, NLOptions options,
|
|
SmallVectorImpl<ValueDecl *> &decls) {
|
|
// If we're supposed to remove overridden declarations, do so now.
|
|
if (options & NL_RemoveOverridden)
|
|
removeOverriddenDecls(decls);
|
|
|
|
// If we're supposed to remove shadowed/hidden declarations, do so now.
|
|
ModuleDecl *M = dc->getParentModule();
|
|
if (options & NL_RemoveNonVisible)
|
|
removeShadowedDecls(decls, M);
|
|
|
|
if (auto *debugClient = M->getDebugClient())
|
|
filterForDiscriminator(decls, debugClient);
|
|
|
|
// We're done. Report success/failure.
|
|
return !decls.empty();
|
|
}
|
|
|
|
/// Inspect the given type to determine which nominal type declarations it
|
|
/// directly references, to facilitate name lookup into those types.
|
|
static void extractDirectlyReferencedNominalTypes(
|
|
Type type, SmallVectorImpl<NominalTypeDecl *> &decls) {
|
|
if (auto nominal = type->getAnyNominal()) {
|
|
decls.push_back(nominal);
|
|
return;
|
|
}
|
|
|
|
if (auto unbound = type->getAs<UnboundGenericType>()) {
|
|
if (auto nominal = dyn_cast<NominalTypeDecl>(unbound->getDecl()))
|
|
decls.push_back(nominal);
|
|
return;
|
|
}
|
|
|
|
if (auto archetypeTy = type->getAs<ArchetypeType>()) {
|
|
// Look in the protocols to which the archetype conforms (always).
|
|
for (auto proto : archetypeTy->getConformsTo())
|
|
decls.push_back(proto);
|
|
|
|
// Look into the superclasses of this archetype.
|
|
if (auto superclass = archetypeTy->getSuperclass()) {
|
|
if (auto superclassDecl = superclass->getClassOrBoundGenericClass())
|
|
decls.push_back(superclassDecl);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (auto compositionTy = type->getAs<ProtocolCompositionType>()) {
|
|
auto layout = compositionTy->getExistentialLayout();
|
|
|
|
for (auto proto : layout.getProtocols()) {
|
|
auto *protoDecl = proto->getDecl();
|
|
decls.push_back(protoDecl);
|
|
}
|
|
|
|
if (auto superclass = layout.explicitSuperclass) {
|
|
auto *superclassDecl = superclass->getClassOrBoundGenericClass();
|
|
if (superclassDecl)
|
|
decls.push_back(superclassDecl);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
llvm_unreachable("Not a type containing nominal types?");
|
|
}
|
|
|
|
bool DeclContext::lookupQualified(Type type,
|
|
DeclName member,
|
|
NLOptions options,
|
|
LazyResolver *typeResolver,
|
|
SmallVectorImpl<ValueDecl *> &decls) const {
|
|
using namespace namelookup;
|
|
assert(decls.empty() && "additive lookup not supported");
|
|
|
|
// Handle AnyObject lookup.
|
|
if (type->isAnyObject())
|
|
return lookupAnyObject(member, options, decls);
|
|
|
|
// Handle lookup in a module.
|
|
if (auto moduleTy = type->getAs<ModuleType>())
|
|
return lookupQualified(moduleTy->getModule(), member, options, decls);
|
|
|
|
// Figure out which nominal types we will look into.
|
|
SmallVector<NominalTypeDecl *, 4> nominalTypesToLookInto;
|
|
extractDirectlyReferencedNominalTypes(type, nominalTypesToLookInto);
|
|
|
|
return lookupQualified(nominalTypesToLookInto, member, options, decls);
|
|
}
|
|
|
|
bool DeclContext::lookupQualified(ArrayRef<NominalTypeDecl *> typeDecls,
|
|
DeclName member,
|
|
NLOptions options,
|
|
SmallVectorImpl<ValueDecl *> &decls) const {
|
|
using namespace namelookup;
|
|
assert(decls.empty() && "additive lookup not supported");
|
|
|
|
// Configure lookup and dig out the tracker.
|
|
ReferencedNameTracker *tracker = nullptr;
|
|
bool isLookupCascading;
|
|
configureLookup(this, options, tracker, isLookupCascading);
|
|
|
|
// Tracking for the nominal types we'll visit.
|
|
SmallVector<NominalTypeDecl *, 4> stack;
|
|
llvm::SmallPtrSet<NominalTypeDecl *, 4> visited;
|
|
bool sawClassDecl = false;
|
|
|
|
// Add the given nominal type to the stack.
|
|
auto addNominalType = [&](NominalTypeDecl *nominal) {
|
|
if (!visited.insert(nominal).second)
|
|
return false;
|
|
|
|
if (isa<ClassDecl>(nominal))
|
|
sawClassDecl = true;
|
|
|
|
stack.push_back(nominal);
|
|
return true;
|
|
};
|
|
|
|
// Add all of the nominal types to the stack.
|
|
for (auto nominal : typeDecls) {
|
|
addNominalType(nominal);
|
|
}
|
|
|
|
// Whether we only want to return complete object initializers.
|
|
bool onlyCompleteObjectInits = false;
|
|
|
|
// Visit all of the nominal types we know about, discovering any others
|
|
// we need along the way.
|
|
auto &ctx = getASTContext();
|
|
auto typeResolver = ctx.getLazyResolver();
|
|
bool wantProtocolMembers = (options & NL_ProtocolMembers);
|
|
while (!stack.empty()) {
|
|
auto current = stack.back();
|
|
stack.pop_back();
|
|
|
|
if (tracker)
|
|
tracker->addUsedMember({current, member.getBaseName()},isLookupCascading);
|
|
|
|
// Make sure we've resolved implicit members, if we need them.
|
|
if (typeResolver) {
|
|
if (member.getBaseName() == DeclBaseName::createConstructor())
|
|
typeResolver->resolveImplicitConstructors(current);
|
|
|
|
typeResolver->resolveImplicitMember(current, member);
|
|
}
|
|
|
|
// Look for results within the current nominal type and its extensions.
|
|
bool currentIsProtocol = isa<ProtocolDecl>(current);
|
|
auto flags = OptionSet<NominalTypeDecl::LookupDirectFlags>();
|
|
if (options & NL_IncludeAttributeImplements)
|
|
flags |= NominalTypeDecl::LookupDirectFlags::IncludeAttrImplements;
|
|
for (auto decl : current->lookupDirect(member, flags)) {
|
|
// If we're performing a type lookup, don't even attempt to validate
|
|
// the decl if its not a type.
|
|
if ((options & NL_OnlyTypes) && !isa<TypeDecl>(decl))
|
|
continue;
|
|
|
|
if (isAcceptableLookupResult(this, options, decl,
|
|
onlyCompleteObjectInits))
|
|
decls.push_back(decl);
|
|
}
|
|
|
|
// Visit superclass.
|
|
if (auto classDecl = dyn_cast<ClassDecl>(current)) {
|
|
// If we're looking for initializers, only look at the superclass if the
|
|
// current class permits inheritance. Even then, only find complete
|
|
// object initializers.
|
|
bool visitSuperclass = true;
|
|
if (member.getBaseName() == DeclBaseName::createConstructor()) {
|
|
if (classDecl->inheritsSuperclassInitializers(typeResolver))
|
|
onlyCompleteObjectInits = true;
|
|
else
|
|
visitSuperclass = false;
|
|
}
|
|
|
|
if (visitSuperclass) {
|
|
if (auto superclassDecl = classDecl->getSuperclassDecl())
|
|
if (visited.insert(superclassDecl).second)
|
|
stack.push_back(superclassDecl);
|
|
}
|
|
}
|
|
|
|
// If we're not looking at a protocol and we're not supposed to
|
|
// visit the protocols that this type conforms to, skip the next
|
|
// step.
|
|
if (!wantProtocolMembers && !currentIsProtocol)
|
|
continue;
|
|
|
|
SmallVector<ProtocolDecl *, 4> protocols;
|
|
|
|
if (auto *protoDecl = dyn_cast<ProtocolDecl>(current)) {
|
|
// If we haven't seen a class declaration yet, look into the protocol.
|
|
if (!sawClassDecl) {
|
|
if (auto superclassDecl = protoDecl->getSuperclassDecl()) {
|
|
visited.insert(superclassDecl);
|
|
stack.push_back(superclassDecl);
|
|
}
|
|
}
|
|
|
|
// Collect inherited protocols.
|
|
for (auto inheritedProto : protoDecl->getInheritedProtocols()) {
|
|
addNominalType(inheritedProto);
|
|
}
|
|
} else {
|
|
// Collect the protocols to which the nominal type conforms.
|
|
for (auto proto : current->getAllProtocols()) {
|
|
if (visited.insert(proto).second) {
|
|
stack.push_back(proto);
|
|
}
|
|
}
|
|
|
|
// For a class, we don't need to visit the protocol members of the
|
|
// superclass: that's already handled.
|
|
if (isa<ClassDecl>(current))
|
|
wantProtocolMembers = false;
|
|
}
|
|
}
|
|
|
|
return finishLookup(this, options, decls);
|
|
}
|
|
|
|
bool DeclContext::lookupQualified(ModuleDecl *module, DeclName member,
|
|
NLOptions options,
|
|
SmallVectorImpl<ValueDecl *> &decls) const {
|
|
using namespace namelookup;
|
|
|
|
// Configure lookup and dig out the tracker.
|
|
ReferencedNameTracker *tracker = nullptr;
|
|
bool isLookupCascading;
|
|
configureLookup(this, options, tracker, isLookupCascading);
|
|
|
|
ASTContext &ctx = getASTContext();
|
|
auto topLevelScope = getModuleScopeContext();
|
|
if (module == topLevelScope->getParentModule()) {
|
|
if (tracker) {
|
|
recordLookupOfTopLevelName(topLevelScope, member, isLookupCascading);
|
|
}
|
|
lookupInModule(module, /*accessPath=*/{}, member, decls,
|
|
NLKind::QualifiedLookup, ResolutionKind::Overloadable,
|
|
ctx.getLazyResolver(), topLevelScope);
|
|
} else {
|
|
// Note: This is a lookup into another module. Unless we're compiling
|
|
// multiple modules at once, or if the other module re-exports this one,
|
|
// it shouldn't be possible to have a dependency from that module on
|
|
// anything in this one.
|
|
|
|
// Perform the lookup in all imports of this module.
|
|
forAllVisibleModules(this,
|
|
[&](const ModuleDecl::ImportedModule &import) -> bool {
|
|
if (import.second != module)
|
|
return true;
|
|
lookupInModule(import.second, import.first, member, decls,
|
|
NLKind::QualifiedLookup, ResolutionKind::Overloadable,
|
|
ctx.getLazyResolver(), topLevelScope);
|
|
// If we're able to do an unscoped lookup, we see everything. No need
|
|
// to keep going.
|
|
return !import.first.empty();
|
|
});
|
|
}
|
|
|
|
llvm::SmallPtrSet<ValueDecl *, 4> knownDecls;
|
|
decls.erase(std::remove_if(decls.begin(), decls.end(),
|
|
[&](ValueDecl *vd) -> bool {
|
|
// If we're performing a type lookup, skip non-types.
|
|
if ((options & NL_OnlyTypes) && !isa<TypeDecl>(vd))
|
|
return true;
|
|
|
|
return !knownDecls.insert(vd).second;
|
|
}), decls.end());
|
|
|
|
return finishLookup(this, options, decls);
|
|
}
|
|
|
|
bool DeclContext::lookupAnyObject(DeclName member, NLOptions options,
|
|
SmallVectorImpl<ValueDecl *> &decls) const {
|
|
using namespace namelookup;
|
|
assert(decls.empty() && "additive lookup not supported");
|
|
|
|
// Configure lookup and dig out the tracker.
|
|
ReferencedNameTracker *tracker = nullptr;
|
|
bool isLookupCascading;
|
|
configureLookup(this, options, tracker, isLookupCascading);
|
|
|
|
// Record this lookup.
|
|
if (tracker)
|
|
tracker->addDynamicLookupName(member.getBaseName(), isLookupCascading);
|
|
|
|
// Type-only lookup won't find anything on AnyObject.
|
|
if (options & NL_OnlyTypes)
|
|
return false;
|
|
|
|
// Collect all of the visible declarations.
|
|
SmallVector<ValueDecl *, 4> allDecls;
|
|
forAllVisibleModules(this, [&](ModuleDecl::ImportedModule import) {
|
|
import.second->lookupClassMember(import.first, member, allDecls);
|
|
});
|
|
|
|
// For each declaration whose context is not something we've
|
|
// already visited above, add it to the list of declarations.
|
|
llvm::SmallPtrSet<ValueDecl *, 4> knownDecls;
|
|
for (auto decl : allDecls) {
|
|
// If the declaration is not @objc, it cannot be called dynamically.
|
|
if (!decl->isObjC())
|
|
continue;
|
|
|
|
// If the declaration has an override, name lookup will also have
|
|
// found the overridden method. Skip this declaration, because we
|
|
// prefer the overridden method.
|
|
if (decl->getOverriddenDecl())
|
|
continue;
|
|
|
|
auto dc = decl->getDeclContext();
|
|
auto nominal = dc->getSelfNominalTypeDecl();
|
|
assert(nominal && "Couldn't find nominal type?");
|
|
(void)nominal;
|
|
|
|
// If we didn't see this declaration before, and it's an acceptable
|
|
// result, add it to the list.
|
|
// declaration to the list.
|
|
if (knownDecls.insert(decl).second &&
|
|
isAcceptableLookupResult(this, options, decl,
|
|
/*onlyCompleteObjectInits=*/false))
|
|
decls.push_back(decl);
|
|
}
|
|
|
|
return finishLookup(this, options, decls);
|
|
}
|
|
|
|
void DeclContext::lookupAllObjCMethods(
|
|
ObjCSelector selector,
|
|
SmallVectorImpl<AbstractFunctionDecl *> &results) const {
|
|
// Collect all of the methods with this selector.
|
|
forAllVisibleModules(this, [&](ModuleDecl::ImportedModule import) {
|
|
import.second->lookupObjCMethods(selector, results);
|
|
});
|
|
|
|
// Filter out duplicates.
|
|
llvm::SmallPtrSet<AbstractFunctionDecl *, 8> visited;
|
|
results.erase(
|
|
std::remove_if(results.begin(), results.end(),
|
|
[&](AbstractFunctionDecl *func) -> bool {
|
|
return !visited.insert(func).second;
|
|
}),
|
|
results.end());
|
|
}
|
|
|
|
/// Given a set of type declarations, find all of the nominal type declarations
|
|
/// that they reference, looking through typealiases as appropriate.
|
|
static TinyPtrVector<NominalTypeDecl *>
|
|
resolveTypeDeclsToNominal(Evaluator &evaluator,
|
|
ASTContext &ctx,
|
|
ArrayRef<TypeDecl *> typeDecls,
|
|
SmallVectorImpl<ModuleDecl *> &modulesFound,
|
|
bool &anyObject,
|
|
llvm::SmallPtrSetImpl<TypeAliasDecl *> &typealiases) {
|
|
TinyPtrVector<NominalTypeDecl *> nominalDecls;
|
|
|
|
for (auto typeDecl : typeDecls) {
|
|
// Nominal type declarations get copied directly.
|
|
if (auto nominalDecl = dyn_cast<NominalTypeDecl>(typeDecl)) {
|
|
nominalDecls.push_back(nominalDecl);
|
|
continue;
|
|
}
|
|
|
|
// Recursively resolve typealiases.
|
|
if (auto typealias = dyn_cast<TypeAliasDecl>(typeDecl)) {
|
|
// FIXME: Ad hoc recursion breaking, so we don't look through the
|
|
// same typealias multiple times.
|
|
if (!typealiases.insert(typealias).second)
|
|
continue;
|
|
|
|
auto underlyingTypeReferences = evaluateOrDefault(evaluator,
|
|
UnderlyingTypeDeclsReferencedRequest{typealias}, {});
|
|
|
|
auto underlyingNominalReferences
|
|
= resolveTypeDeclsToNominal(evaluator, ctx, underlyingTypeReferences,
|
|
modulesFound, anyObject, typealiases);
|
|
nominalDecls.insert(nominalDecls.end(),
|
|
underlyingNominalReferences.begin(),
|
|
underlyingNominalReferences.end());
|
|
|
|
// Recognize Swift.AnyObject directly.
|
|
if (typealias->getName().is("AnyObject")) {
|
|
// TypeRepr version: Builtin.AnyObject
|
|
if (auto typeRepr = typealias->getUnderlyingTypeLoc().getTypeRepr()) {
|
|
if (auto compound = dyn_cast<CompoundIdentTypeRepr>(typeRepr)) {
|
|
auto components = compound->getComponents();
|
|
if (components.size() == 2 &&
|
|
components[0]->getIdentifier().is("Builtin") &&
|
|
components[1]->getIdentifier().is("AnyObject")) {
|
|
anyObject = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Type version: an empty class-bound existential.
|
|
if (auto type = typealias->getUnderlyingTypeLoc().getType()) {
|
|
if (type->isAnyObject())
|
|
anyObject = true;
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// Keep track of modules we see.
|
|
if (auto module = dyn_cast<ModuleDecl>(typeDecl)) {
|
|
modulesFound.push_back(module);
|
|
continue;
|
|
}
|
|
|
|
// Make sure we didn't miss some interesting kind of type declaration.
|
|
assert(isa<AbstractTypeParamDecl>(typeDecl));
|
|
}
|
|
|
|
return nominalDecls;
|
|
}
|
|
|
|
static TinyPtrVector<NominalTypeDecl *>
|
|
resolveTypeDeclsToNominal(Evaluator &evaluator,
|
|
ASTContext &ctx,
|
|
ArrayRef<TypeDecl *> typeDecls,
|
|
SmallVectorImpl<ModuleDecl *> &modulesFound,
|
|
bool &anyObject) {
|
|
llvm::SmallPtrSet<TypeAliasDecl *, 4> typealiases;
|
|
return resolveTypeDeclsToNominal(evaluator, ctx, typeDecls, modulesFound,
|
|
anyObject, typealiases);
|
|
}
|
|
|
|
/// Perform unqualified name lookup for types at the given location.
|
|
static DirectlyReferencedTypeDecls
|
|
directReferencesForUnqualifiedTypeLookup(ASTContext &ctx, DeclName name,
|
|
SourceLoc loc, DeclContext *dc) {
|
|
DirectlyReferencedTypeDecls results;
|
|
UnqualifiedLookup::Options options = UnqualifiedLookup::Flags::TypeLookup;
|
|
UnqualifiedLookup lookup(name, dc, ctx.getLazyResolver(), loc, options);
|
|
for (const auto &result : lookup.Results) {
|
|
if (auto typeDecl = dyn_cast<TypeDecl>(result.getValueDecl()))
|
|
results.push_back(typeDecl);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/// Perform qualified name lookup for types.
|
|
static DirectlyReferencedTypeDecls
|
|
directReferencesForQualifiedTypeLookup(Evaluator &evaluator,
|
|
ASTContext &ctx,
|
|
ArrayRef<TypeDecl *> baseTypes,
|
|
DeclName name,
|
|
DeclContext *dc) {
|
|
DirectlyReferencedTypeDecls result;
|
|
auto addResults = [&result](ArrayRef<ValueDecl *> found){
|
|
for (auto decl : found){
|
|
assert(isa<TypeDecl>(decl) &&
|
|
"Lookup should only have found type declarations");
|
|
result.push_back(cast<TypeDecl>(decl));
|
|
}
|
|
};
|
|
|
|
{
|
|
// Look into the base types.
|
|
SmallVector<ValueDecl *, 4> members;
|
|
auto options = NL_RemoveNonVisible | NL_OnlyTypes;
|
|
|
|
// Look through the type declarations we were given, resolving them down
|
|
// to nominal type declarations, module declarations, and
|
|
SmallVector<ModuleDecl *, 2> moduleDecls;
|
|
bool anyObject = false;
|
|
auto nominalTypeDecls =
|
|
resolveTypeDeclsToNominal(ctx.evaluator, ctx, baseTypes, moduleDecls,
|
|
anyObject);
|
|
|
|
dc->lookupQualified(nominalTypeDecls, name, options, members);
|
|
|
|
// Search all of the modules.
|
|
for (auto module : moduleDecls) {
|
|
auto innerOptions = options;
|
|
innerOptions &= ~NL_RemoveOverridden;
|
|
innerOptions &= ~NL_RemoveNonVisible;
|
|
dc->lookupQualified(module, name, innerOptions, members);
|
|
}
|
|
|
|
addResults(members);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Determine the types directly referenced by the given identifier type.
|
|
static DirectlyReferencedTypeDecls
|
|
directReferencesForIdentTypeRepr(Evaluator &evaluator,
|
|
ASTContext &ctx, IdentTypeRepr *ident,
|
|
DeclContext *dc) {
|
|
DirectlyReferencedTypeDecls current;
|
|
|
|
bool firstComponent = true;
|
|
for (const auto &component : ident->getComponentRange()) {
|
|
// If we already set a declaration, use it.
|
|
if (auto typeDecl = component->getBoundDecl()) {
|
|
current = {1, typeDecl};
|
|
continue;
|
|
}
|
|
|
|
// For the first component, perform unqualified name lookup.
|
|
if (current.empty()) {
|
|
current =
|
|
directReferencesForUnqualifiedTypeLookup(ctx,
|
|
component->getIdentifier(),
|
|
component->getIdLoc(),
|
|
dc);
|
|
|
|
// If we didn't find anything, fail now.
|
|
if (current.empty())
|
|
return current;
|
|
|
|
firstComponent = false;
|
|
continue;
|
|
}
|
|
|
|
// For subsequent components, perform qualified name lookup.
|
|
current =
|
|
directReferencesForQualifiedTypeLookup(evaluator, ctx, current,
|
|
component->getIdentifier(), dc);
|
|
if (current.empty())
|
|
return current;
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
static DirectlyReferencedTypeDecls
|
|
directReferencesForTypeRepr(Evaluator &evaluator,
|
|
ASTContext &ctx, TypeRepr *typeRepr,
|
|
DeclContext *dc) {
|
|
switch (typeRepr->getKind()) {
|
|
case TypeReprKind::Array:
|
|
return {1, ctx.getArrayDecl()};
|
|
|
|
case TypeReprKind::Attributed: {
|
|
auto attributed = cast<AttributedTypeRepr>(typeRepr);
|
|
return directReferencesForTypeRepr(evaluator, ctx,
|
|
attributed->getTypeRepr(), dc);
|
|
}
|
|
|
|
case TypeReprKind::Composition: {
|
|
DirectlyReferencedTypeDecls result;
|
|
auto composition = cast<CompositionTypeRepr>(typeRepr);
|
|
for (auto component : composition->getTypes()) {
|
|
auto componentResult =
|
|
directReferencesForTypeRepr(evaluator, ctx, component, dc);
|
|
result.insert(result.end(),
|
|
componentResult.begin(),
|
|
componentResult.end());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
case TypeReprKind::CompoundIdent:
|
|
case TypeReprKind::GenericIdent:
|
|
case TypeReprKind::SimpleIdent:
|
|
return directReferencesForIdentTypeRepr(evaluator, ctx,
|
|
cast<IdentTypeRepr>(typeRepr), dc);
|
|
|
|
case TypeReprKind::Dictionary:
|
|
return { 1, ctx.getDictionaryDecl()};
|
|
|
|
case TypeReprKind::Tuple: {
|
|
auto tupleRepr = cast<TupleTypeRepr>(typeRepr);
|
|
if (tupleRepr->isParenType()) {
|
|
return directReferencesForTypeRepr(evaluator, ctx,
|
|
tupleRepr->getElementType(0), dc);
|
|
}
|
|
return { };
|
|
}
|
|
|
|
case TypeReprKind::Error:
|
|
case TypeReprKind::Function:
|
|
case TypeReprKind::InOut:
|
|
case TypeReprKind::Metatype:
|
|
case TypeReprKind::Owned:
|
|
case TypeReprKind::Protocol:
|
|
case TypeReprKind::Shared:
|
|
case TypeReprKind::SILBox:
|
|
return { };
|
|
|
|
case TypeReprKind::Fixed:
|
|
llvm_unreachable("Cannot get fixed TypeReprs in name lookup");
|
|
|
|
case TypeReprKind::Optional:
|
|
case TypeReprKind::ImplicitlyUnwrappedOptional:
|
|
return { 1, ctx.getOptionalDecl() };
|
|
}
|
|
llvm_unreachable("unhandled kind");
|
|
}
|
|
|
|
static DirectlyReferencedTypeDecls directReferencesForType(Type type) {
|
|
// If it's a typealias, return that.
|
|
if (auto aliasType = dyn_cast<TypeAliasType>(type.getPointer()))
|
|
return { 1, aliasType->getDecl() };
|
|
|
|
// If there is a generic declaration, return it.
|
|
if (auto genericDecl = type->getAnyGeneric())
|
|
return { 1, genericDecl };
|
|
|
|
if (type->isExistentialType()) {
|
|
DirectlyReferencedTypeDecls result;
|
|
const auto &layout = type->getExistentialLayout();
|
|
|
|
// Superclass.
|
|
if (auto superclassType = layout.explicitSuperclass) {
|
|
if (auto superclassDecl = superclassType->getAnyGeneric()) {
|
|
result.push_back(superclassDecl);
|
|
}
|
|
}
|
|
|
|
// Protocols.
|
|
for (auto protocolTy : layout.getProtocols())
|
|
result.push_back(protocolTy->getDecl());
|
|
return result;
|
|
}
|
|
|
|
return { };
|
|
}
|
|
|
|
DirectlyReferencedTypeDecls InheritedDeclsReferencedRequest::evaluate(
|
|
Evaluator &evaluator,
|
|
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
|
|
unsigned index) const {
|
|
|
|
// Prefer syntactic information when we have it.
|
|
TypeLoc &typeLoc = getTypeLoc(decl, index);
|
|
if (auto typeRepr = typeLoc.getTypeRepr()) {
|
|
// Figure out the context in which name lookup will occur.
|
|
DeclContext *dc;
|
|
if (auto typeDecl = decl.dyn_cast<TypeDecl *>())
|
|
dc = typeDecl->getInnermostDeclContext();
|
|
else
|
|
dc = decl.get<ExtensionDecl *>();
|
|
|
|
return directReferencesForTypeRepr(evaluator, dc->getASTContext(), typeRepr,
|
|
dc);
|
|
}
|
|
|
|
// Fall back to semantic types.
|
|
// FIXME: In the long run, we shouldn't need this. Non-syntactic results
|
|
// should be cached.
|
|
if (auto type = typeLoc.getType()) {
|
|
return directReferencesForType(type);
|
|
}
|
|
|
|
return { };
|
|
}
|
|
|
|
DirectlyReferencedTypeDecls UnderlyingTypeDeclsReferencedRequest::evaluate(
|
|
Evaluator &evaluator,
|
|
TypeAliasDecl *typealias) const {
|
|
// Prefer syntactic information when we have it.
|
|
if (auto typeRepr = typealias->getUnderlyingTypeLoc().getTypeRepr()) {
|
|
return directReferencesForTypeRepr(evaluator, typealias->getASTContext(),
|
|
typeRepr, typealias);
|
|
}
|
|
|
|
// Fall back to semantic types.
|
|
// FIXME: In the long run, we shouldn't need this. Non-syntactic results
|
|
// should be cached.
|
|
if (auto type = typealias->getUnderlyingTypeLoc().getType()) {
|
|
return directReferencesForType(type);
|
|
}
|
|
|
|
return { };
|
|
}
|
|
|
|
/// Evaluate a superclass declaration request.
|
|
llvm::Expected<ClassDecl *>
|
|
SuperclassDeclRequest::evaluate(Evaluator &evaluator,
|
|
NominalTypeDecl *subject) const {
|
|
auto &Ctx = subject->getASTContext();
|
|
|
|
for (unsigned i : indices(subject->getInherited())) {
|
|
// Find the inherited declarations referenced at this position.
|
|
auto inheritedTypes = evaluateOrDefault(evaluator,
|
|
InheritedDeclsReferencedRequest{subject, i}, {});
|
|
|
|
// Resolve those type declarations to nominal type declarations.
|
|
SmallVector<ModuleDecl *, 2> modulesFound;
|
|
bool anyObject = false;
|
|
auto inheritedNominalTypes
|
|
= resolveTypeDeclsToNominal(evaluator, Ctx,
|
|
inheritedTypes, modulesFound, anyObject);
|
|
|
|
// Look for a class declaration.
|
|
for (auto inheritedNominal : inheritedNominalTypes) {
|
|
if (auto classDecl = dyn_cast<ClassDecl>(inheritedNominal))
|
|
return classDecl;
|
|
}
|
|
}
|
|
|
|
// Protocols also support '... where Self : Superclass'.
|
|
auto *proto = dyn_cast<ProtocolDecl>(subject);
|
|
if (proto == nullptr)
|
|
return nullptr;
|
|
|
|
auto selfBounds = getSelfBoundsFromWhereClause(proto);
|
|
for (auto inheritedNominal : selfBounds.decls)
|
|
if (auto classDecl = dyn_cast<ClassDecl>(inheritedNominal))
|
|
return classDecl;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
llvm::Expected<NominalTypeDecl *>
|
|
ExtendedNominalRequest::evaluate(Evaluator &evaluator,
|
|
ExtensionDecl *ext) const {
|
|
DirectlyReferencedTypeDecls referenced;
|
|
ASTContext &ctx = ext->getASTContext();
|
|
|
|
// Prefer syntactic information when we have it.
|
|
TypeLoc &typeLoc = ext->getExtendedTypeLoc();
|
|
if (auto typeRepr = typeLoc.getTypeRepr()) {
|
|
referenced = directReferencesForTypeRepr(evaluator, ctx, typeRepr, ext);
|
|
} else if (auto type = typeLoc.getType()) {
|
|
// Fall back to semantic types.
|
|
// FIXME: In the long run, we shouldn't need this. Non-syntactic results
|
|
// should be cached.
|
|
referenced = directReferencesForType(type);
|
|
}
|
|
|
|
// Resolve those type declarations to nominal type declarations.
|
|
SmallVector<ModuleDecl *, 2> modulesFound;
|
|
bool anyObject = false;
|
|
auto nominalTypes
|
|
= resolveTypeDeclsToNominal(evaluator, ctx, referenced, modulesFound,
|
|
anyObject);
|
|
return nominalTypes.empty() ? nullptr : nominalTypes.front();
|
|
}
|
|
|
|
void swift::getDirectlyInheritedNominalTypeDecls(
|
|
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
|
|
unsigned i,
|
|
llvm::SmallVectorImpl<std::pair<SourceLoc, NominalTypeDecl *>> &result,
|
|
bool &anyObject) {
|
|
auto typeDecl = decl.dyn_cast<TypeDecl *>();
|
|
auto extDecl = decl.dyn_cast<ExtensionDecl *>();
|
|
|
|
ASTContext &ctx = typeDecl ? typeDecl->getASTContext()
|
|
: extDecl->getASTContext();
|
|
|
|
// Find inherited declarations.
|
|
auto referenced = evaluateOrDefault(ctx.evaluator,
|
|
InheritedDeclsReferencedRequest{decl, i}, {});
|
|
|
|
// Resolve those type declarations to nominal type declarations.
|
|
SmallVector<ModuleDecl *, 2> modulesFound;
|
|
auto nominalTypes
|
|
= resolveTypeDeclsToNominal(ctx.evaluator, ctx, referenced, modulesFound,
|
|
anyObject);
|
|
|
|
// Dig out the source location
|
|
// FIXME: This is a hack. We need cooperation from
|
|
// InheritedDeclsReferencedRequest to make this work.
|
|
SourceLoc loc;
|
|
if (TypeRepr *typeRepr = typeDecl ? typeDecl->getInherited()[i].getTypeRepr()
|
|
: extDecl->getInherited()[i].getTypeRepr()){
|
|
loc = typeRepr->getLoc();
|
|
}
|
|
|
|
// Form the result.
|
|
for (auto nominal : nominalTypes) {
|
|
result.push_back({loc, nominal});
|
|
}
|
|
}
|
|
|
|
SmallVector<std::pair<SourceLoc, NominalTypeDecl *>, 4>
|
|
swift::getDirectlyInheritedNominalTypeDecls(
|
|
llvm::PointerUnion<TypeDecl *, ExtensionDecl *> decl,
|
|
bool &anyObject) {
|
|
auto typeDecl = decl.dyn_cast<TypeDecl *>();
|
|
auto extDecl = decl.dyn_cast<ExtensionDecl *>();
|
|
|
|
// Gather results from all of the inherited types.
|
|
unsigned numInherited = typeDecl ? typeDecl->getInherited().size()
|
|
: extDecl->getInherited().size();
|
|
SmallVector<std::pair<SourceLoc, NominalTypeDecl *>, 4> result;
|
|
for (unsigned i : range(numInherited)) {
|
|
getDirectlyInheritedNominalTypeDecls(decl, i, result, anyObject);
|
|
}
|
|
|
|
auto *protoDecl = dyn_cast_or_null<ProtocolDecl>(typeDecl);
|
|
if (protoDecl == nullptr)
|
|
return result;
|
|
|
|
// FIXME: Refactor SelfBoundsFromWhereClauseRequest to dig out
|
|
// the source location.
|
|
SourceLoc loc = SourceLoc();
|
|
auto selfBounds = getSelfBoundsFromWhereClause(decl);
|
|
anyObject |= selfBounds.anyObject;
|
|
|
|
for (auto inheritedNominal : selfBounds.decls)
|
|
result.emplace_back(loc, inheritedNominal);
|
|
|
|
return result;
|
|
}
|
|
|
|
void FindLocalVal::checkPattern(const Pattern *Pat, DeclVisibilityKind Reason) {
|
|
switch (Pat->getKind()) {
|
|
case PatternKind::Tuple:
|
|
for (auto &field : cast<TuplePattern>(Pat)->getElements())
|
|
checkPattern(field.getPattern(), Reason);
|
|
return;
|
|
case PatternKind::Paren:
|
|
case PatternKind::Typed:
|
|
case PatternKind::Var:
|
|
return checkPattern(Pat->getSemanticsProvidingPattern(), Reason);
|
|
case PatternKind::Named:
|
|
return checkValueDecl(cast<NamedPattern>(Pat)->getDecl(), Reason);
|
|
case PatternKind::EnumElement: {
|
|
auto *OP = cast<EnumElementPattern>(Pat);
|
|
if (OP->hasSubPattern())
|
|
checkPattern(OP->getSubPattern(), Reason);
|
|
return;
|
|
}
|
|
case PatternKind::OptionalSome:
|
|
checkPattern(cast<OptionalSomePattern>(Pat)->getSubPattern(), Reason);
|
|
return;
|
|
|
|
case PatternKind::Is: {
|
|
auto *isPat = cast<IsPattern>(Pat);
|
|
if (isPat->hasSubPattern())
|
|
checkPattern(isPat->getSubPattern(), Reason);
|
|
return;
|
|
}
|
|
|
|
// Handle non-vars.
|
|
case PatternKind::Bool:
|
|
case PatternKind::Expr:
|
|
case PatternKind::Any:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void FindLocalVal::checkParameterList(const ParameterList *params) {
|
|
for (auto param : *params) {
|
|
checkValueDecl(param, DeclVisibilityKind::FunctionParameter);
|
|
}
|
|
}
|
|
|
|
void FindLocalVal::checkGenericParams(GenericParamList *Params) {
|
|
if (!Params)
|
|
return;
|
|
|
|
for (auto P : *Params)
|
|
checkValueDecl(P, DeclVisibilityKind::GenericParameter);
|
|
}
|
|
|
|
void FindLocalVal::checkSourceFile(const SourceFile &SF) {
|
|
for (Decl *D : SF.Decls)
|
|
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D))
|
|
visitBraceStmt(TLCD->getBody(), /*isTopLevel=*/true);
|
|
}
|
|
|
|
void FindLocalVal::checkStmtCondition(const StmtCondition &Cond) {
|
|
SourceLoc start = SourceLoc();
|
|
for (auto entry : Cond) {
|
|
if (start.isInvalid())
|
|
start = entry.getStartLoc();
|
|
if (auto *P = entry.getPatternOrNull()) {
|
|
SourceRange previousConditionsToHere = SourceRange(start, entry.getEndLoc());
|
|
if (!isReferencePointInRange(previousConditionsToHere))
|
|
checkPattern(P, DeclVisibilityKind::LocalVariable);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FindLocalVal::visitIfStmt(IfStmt *S) {
|
|
if (!isReferencePointInRange(S->getSourceRange()))
|
|
return;
|
|
|
|
if (!S->getElseStmt() ||
|
|
!isReferencePointInRange(S->getElseStmt()->getSourceRange())) {
|
|
checkStmtCondition(S->getCond());
|
|
}
|
|
|
|
visit(S->getThenStmt());
|
|
if (S->getElseStmt())
|
|
visit(S->getElseStmt());
|
|
}
|
|
|
|
void FindLocalVal::visitGuardStmt(GuardStmt *S) {
|
|
if (SM.isBeforeInBuffer(Loc, S->getStartLoc()))
|
|
return;
|
|
|
|
// Names in the guard aren't visible until after the body.
|
|
if (!isReferencePointInRange(S->getBody()->getSourceRange()))
|
|
checkStmtCondition(S->getCond());
|
|
|
|
visit(S->getBody());
|
|
}
|
|
|
|
void FindLocalVal::visitWhileStmt(WhileStmt *S) {
|
|
if (!isReferencePointInRange(S->getSourceRange()))
|
|
return;
|
|
|
|
checkStmtCondition(S->getCond());
|
|
visit(S->getBody());
|
|
}
|
|
void FindLocalVal::visitRepeatWhileStmt(RepeatWhileStmt *S) {
|
|
visit(S->getBody());
|
|
}
|
|
void FindLocalVal::visitDoStmt(DoStmt *S) {
|
|
visit(S->getBody());
|
|
}
|
|
|
|
void FindLocalVal::visitForEachStmt(ForEachStmt *S) {
|
|
if (!isReferencePointInRange(S->getSourceRange()))
|
|
return;
|
|
visit(S->getBody());
|
|
if (!isReferencePointInRange(S->getSequence()->getSourceRange()))
|
|
checkPattern(S->getPattern(), DeclVisibilityKind::LocalVariable);
|
|
}
|
|
|
|
void FindLocalVal::visitBraceStmt(BraceStmt *S, bool isTopLevelCode) {
|
|
if (isTopLevelCode) {
|
|
if (SM.isBeforeInBuffer(Loc, S->getStartLoc()))
|
|
return;
|
|
} else {
|
|
if (!isReferencePointInRange(S->getSourceRange()))
|
|
return;
|
|
}
|
|
|
|
for (auto elem : S->getElements()) {
|
|
if (auto *S = elem.dyn_cast<Stmt*>())
|
|
visit(S);
|
|
}
|
|
for (auto elem : S->getElements()) {
|
|
if (auto *D = elem.dyn_cast<Decl*>()) {
|
|
if (auto *VD = dyn_cast<ValueDecl>(D))
|
|
checkValueDecl(VD, DeclVisibilityKind::LocalVariable);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FindLocalVal::visitSwitchStmt(SwitchStmt *S) {
|
|
if (!isReferencePointInRange(S->getSourceRange()))
|
|
return;
|
|
for (CaseStmt *C : S->getCases()) {
|
|
visit(C);
|
|
}
|
|
}
|
|
|
|
void FindLocalVal::visitCaseStmt(CaseStmt *S) {
|
|
if (!isReferencePointInRange(S->getSourceRange()))
|
|
return;
|
|
// Pattern names aren't visible in the patterns themselves,
|
|
// just in the body or in where guards.
|
|
bool inPatterns = isReferencePointInRange(S->getLabelItemsRange());
|
|
auto items = S->getCaseLabelItems();
|
|
if (inPatterns) {
|
|
for (const auto &CLI : items) {
|
|
auto guard = CLI.getGuardExpr();
|
|
if (guard && isReferencePointInRange(guard->getSourceRange())) {
|
|
checkPattern(CLI.getPattern(), DeclVisibilityKind::LocalVariable);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!inPatterns && !items.empty())
|
|
checkPattern(items[0].getPattern(), DeclVisibilityKind::LocalVariable);
|
|
visit(S->getBody());
|
|
}
|
|
|
|
void FindLocalVal::visitDoCatchStmt(DoCatchStmt *S) {
|
|
if (!isReferencePointInRange(S->getSourceRange()))
|
|
return;
|
|
visit(S->getBody());
|
|
visitCatchClauses(S->getCatches());
|
|
}
|
|
void FindLocalVal::visitCatchClauses(ArrayRef<CatchStmt*> clauses) {
|
|
// TODO: some sort of binary search?
|
|
for (auto clause : clauses) {
|
|
visitCatchStmt(clause);
|
|
}
|
|
}
|
|
void FindLocalVal::visitCatchStmt(CatchStmt *S) {
|
|
if (!isReferencePointInRange(S->getSourceRange()))
|
|
return;
|
|
// Names in the pattern aren't visible until after the pattern.
|
|
if (!isReferencePointInRange(S->getErrorPattern()->getSourceRange()))
|
|
checkPattern(S->getErrorPattern(), DeclVisibilityKind::LocalVariable);
|
|
visit(S->getBody());
|
|
}
|