blob: 27ac8195ebc94f219ef2444fec45e65255650304 [file] [log] [blame]
//===--- UnqualifiedLookup.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 unqualified lookup, which searches for an identifier
/// from a given context.
///
//===----------------------------------------------------------------------===//
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/DebuggerClient.h"
#include "swift/AST/ImportCache.h"
#include "swift/AST/ModuleNameLookup.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/NameLookupRequests.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/Basic/Debug.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Basic/Statistic.h"
#include "swift/Parse/Lexer.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;
namespace {
class UnqualifiedLookupFactory {
friend class ASTScopeDeclConsumerForUnqualifiedLookup;
public:
using Flags = UnqualifiedLookupFlags;
using Options = UnqualifiedLookupOptions;
using ResultsVector = SmallVector<LookupResultEntry, 4>;
private:
/// Finds lookup results based on the types that self conforms to.
/// For instance, self always conforms to a struct, enum or class.
/// But in addition, self could conform to any number of protocols.
/// For example, when there's a protocol extension, e.g. extension P where
/// self: P2, self also conforms to P2 so P2 must be searched.
class ResultFinderForTypeContext {
UnqualifiedLookupFactory *const factory;
/// Nontypes are formally members of the base type, i.e. the dynamic type
/// of the activation record.
const DeclContext *const dynamicContext;
/// Types are formally members of the metatype, i.e. the static type of the
/// activation record.
const DeclContext *const staticContext;
using SelfBounds = SmallVector<NominalTypeDecl *, 2>;
SelfBounds selfBounds;
public:
/// \p staticContext is also the context from which to derive the self types
ResultFinderForTypeContext(UnqualifiedLookupFactory *factory,
const DeclContext *dynamicContext,
const DeclContext *staticContext);
SWIFT_DEBUG_DUMP;
private:
SelfBounds findSelfBounds(const DeclContext *dc);
// Classify this declaration.
// Types are formally members of the metatype.
const DeclContext *
whereValueIsMember(const ValueDecl *const member) const {
return isa<TypeDecl>(member) ? staticContext : dynamicContext;
}
public:
/// Do the lookups and add matches to results.
void findResults(const DeclNameRef &Name, NLOptions baseNLOptions,
const DeclContext *contextForLookup,
SmallVectorImpl<LookupResultEntry> &results) const;
};
// Inputs
const DeclNameRef Name;
DeclContext *const DC;
ModuleDecl &M;
const ASTContext &Ctx;
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;
// Outputs
SmallVectorImpl<LookupResultEntry> &Results;
size_t &IndexOfFirstOuterResult;
ResultsVector UnavailableInnerResults;
#ifndef NDEBUG
static unsigned lookupCounter;
static const unsigned targetLookup;
#endif
public:
// clang-format off
UnqualifiedLookupFactory(DeclNameRef Name,
DeclContext *const DC,
SourceLoc Loc,
Options options,
SmallVectorImpl<LookupResultEntry> &Results,
size_t &IndexOfFirstOuterResult);
// clang-format on
void performUnqualifiedLookup();
private:
void lookUpTopLevelNamesInModuleScopeContext(const DeclContext *);
void lookInASTScopes();
/// Can lookup stop searching for results, assuming hasn't looked for outer
/// results yet?
bool isFirstResultEnough() const;
/// Do we want precise scoping of VarDecls? If IncludeOuterResults is on,
/// this is true, which allows us to resolve forward references to
/// local VarDecls from inside local function and closure bodies.
bool hasPreciseScopingOfVarDecls() const;
/// Every time lookup finishes searching a scope, call me
/// to record the dividing line between results from first fruitful scope and
/// the result.
void recordCompletionOfAScope();
#pragma mark context-based lookup declarations
bool isInsideBodyOfFunction(const AbstractFunctionDecl *const AFD) const;
/// For diagnostic purposes, move aside the unavailables, and put
/// them back as a last-ditch effort.
/// Could be cleaner someday with a richer interface to UnqualifiedLookup.
void setAsideUnavailableResults(size_t firstPossiblyUnavailableResult);
void addImportedResults(const DeclContext *const dc);
void addNamesKnownToDebugClient(const DeclContext *dc);
void addUnavailableInnerResults();
void lookForAModuleWithTheGivenName(const DeclContext *dc);
#pragma mark common helper declarations
static NLOptions
computeBaseNLOptions(const UnqualifiedLookupOptions options,
const bool isOriginallyTypeLookup);
void findResultsAndSaveUnavailables(
const DeclContext *lookupContextForThisContext,
ResultFinderForTypeContext &&resultFinderForTypeContext,
NLOptions baseNLOptions);
public:
SWIFT_DEBUG_DUMP;
SWIFT_DEBUG_DUMPER(dumpResults());
SWIFT_DEBUG_DUMPER(dumpScopes());
void printScopes(raw_ostream &OS) const;
void print(raw_ostream &OS) const;
void printResults(raw_ostream &OS) const;
#ifndef NDEBUG
bool isTargetLookup() const;
void stopForDebuggingIfStartingTargetLookup(bool isASTScopeLookup) const;
void stopForDebuggingIfDuringTargetLookup(bool isASTScopeLookup) const;
void
stopForDebuggingIfAddingTargetLookupResult(const LookupResultEntry &) const;
void addedResult(const LookupResultEntry &) const;
#endif
};
} // namespace
namespace {
/// Used to gather lookup results
class ASTScopeDeclConsumerForUnqualifiedLookup
: public AbstractASTScopeDeclConsumer {
UnqualifiedLookupFactory &factory;
/// The 'self' parameter from the innermost scope containing the lookup
/// location to be used when an instance member of a type is accessed,
/// or nullptr if instance members should not be 'self' qualified.
DeclContext *candidateSelfDC;
public:
ASTScopeDeclConsumerForUnqualifiedLookup(UnqualifiedLookupFactory &factory)
: factory(factory), candidateSelfDC(nullptr) {}
virtual ~ASTScopeDeclConsumerForUnqualifiedLookup() = default;
void maybeUpdateSelfDC(VarDecl *var);
bool consume(ArrayRef<ValueDecl *> values,
NullablePtr<DeclContext> baseDC = nullptr) override;
bool consumePossiblyNotInScope(ArrayRef<VarDecl *> vars) override;
/// returns true if finished
bool lookInMembers(DeclContext *const scopeDC,
NominalTypeDecl *const nominal) override;
#ifndef NDEBUG
void startingNextLookupStep() override {
factory.stopForDebuggingIfDuringTargetLookup(true);
}
bool isTargetLookup() const override { return factory.isTargetLookup(); }
void finishingLookup(std::string msg) const override {
if (isTargetLookup())
llvm::errs() << "Finishing lookup: " << msg << "\n";
}
#endif
};
} // namespace
#pragma mark UnqualifiedLookupFactory functions
// clang-format off
UnqualifiedLookupFactory::UnqualifiedLookupFactory(
DeclNameRef Name,
DeclContext *const DC,
SourceLoc Loc,
Options options,
SmallVectorImpl<LookupResultEntry> &Results,
size_t &IndexOfFirstOuterResult)
:
Name(Name),
DC(DC),
M(*DC->getParentModule()),
Ctx(M.getASTContext()),
Loc(Loc),
SM(Ctx.SourceMgr),
DebugClient(M.getDebugClient()),
options(options),
isOriginallyTypeLookup(options.contains(Flags::TypeLookup)),
baseNLOptions(computeBaseNLOptions(options, isOriginallyTypeLookup)),
Results(Results),
IndexOfFirstOuterResult(IndexOfFirstOuterResult)
{}
// clang-format on
void UnqualifiedLookupFactory::performUnqualifiedLookup() {
#ifndef NDEBUG
++lookupCounter;
auto localCounter = lookupCounter;
(void)localCounter; // for debugging
#endif
FrontendStatsTracer StatsTracer(Ctx.Stats,
"performUnqualifedLookup",
DC->getParentSourceFile());
if (Loc.isValid()) {
// Operator lookup is always global, for the time being.
if (!Name.isOperator())
lookInASTScopes();
} else {
assert(DC->isModuleScopeContext() &&
"Unqualified lookup without a source location must start from "
"a module-scope context");
#ifndef NDEBUG
stopForDebuggingIfStartingTargetLookup(false);
#endif
}
recordCompletionOfAScope();
if (!isFirstResultEnough()) {
// If no result has been found yet, the dependency must be on a top-level
// name, since up to now, the search has been for non-top-level names.
auto *moduleScopeContext = DC->getModuleScopeContext();
lookUpTopLevelNamesInModuleScopeContext(moduleScopeContext);
}
}
void UnqualifiedLookupFactory::lookUpTopLevelNamesInModuleScopeContext(
const DeclContext *DC) {
// TODO: Does the debugger client care about compound names?
if (Name.isSimpleName() && !Name.isSpecial() && DebugClient &&
DebugClient->lookupOverrides(Name.getBaseName(),
const_cast<DeclContext *>(DC), Loc,
isOriginallyTypeLookup, Results)) {
return;
}
addImportedResults(DC);
addNamesKnownToDebugClient(DC);
if (Results.empty()) {
// If we still haven't found anything, but we do have some
// declarations that are "unavailable in the current Swift", drop
// those in.
addUnavailableInnerResults();
if (Results.empty())
lookForAModuleWithTheGivenName(DC);
}
recordCompletionOfAScope();
}
#pragma mark context-based lookup definitions
bool UnqualifiedLookupFactory::isInsideBodyOfFunction(
const AbstractFunctionDecl *const AFD) const {
auto range = Lexer::getCharSourceRangeFromSourceRange(
SM, AFD->getBodySourceRange());
return range.contains(Loc);
}
void UnqualifiedLookupFactory::ResultFinderForTypeContext::findResults(
const DeclNameRef &Name, NLOptions baseNLOptions,
const DeclContext *contextForLookup,
SmallVectorImpl<LookupResultEntry> &results) const {
// An optimization:
if (selfBounds.empty())
return;
SmallVector<ValueDecl *, 4> Lookup;
contextForLookup->lookupQualified(selfBounds, Name, baseNLOptions, Lookup);
for (auto Result : Lookup) {
results.emplace_back(const_cast<DeclContext *>(whereValueIsMember(Result)),
Result);
#ifndef NDEBUG
factory->addedResult(results.back());
#endif
}
}
// TODO (someday): Instead of adding unavailable entries to Results,
// then later shunting them aside, just put them in the right place
// to begin with.
void UnqualifiedLookupFactory::setAsideUnavailableResults(
const size_t firstPossiblyUnavailableResult) {
// An optimization:
assert(Results.size() >= firstPossiblyUnavailableResult);
if (Results.size() == firstPossiblyUnavailableResult)
return;
// 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() + firstPossiblyUnavailableResult;
if (std::all_of(begin, Results.end(), unavailableLookupResult)) {
// better to have more structure in results
UnavailableInnerResults.append(begin, Results.end());
Results.erase(begin, Results.end());
return;
}
// The debugger may have a different private discriminator
// in order to support lookup relative to the place where
// execution is suspended.
filterForDiscriminator(Results, DebugClient);
}
void UnqualifiedLookupFactory::addImportedResults(const DeclContext *const dc) {
using namespace namelookup;
SmallVector<ValueDecl *, 8> CurModuleResults;
auto resolutionKind = isOriginallyTypeLookup ? ResolutionKind::TypesOnly
: ResolutionKind::Overloadable;
auto nlOptions = NL_UnqualifiedDefault;
if (options.contains(Flags::IncludeUsableFromInline))
nlOptions |= NL_IncludeUsableFromInline;
lookupInModule(dc, Name.getFullName(), CurModuleResults,
NLKind::UnqualifiedLookup, resolutionKind, dc, nlOptions);
// Always perform name shadowing for type lookup.
if (options.contains(Flags::TypeLookup)) {
removeShadowedDecls(CurModuleResults, dc);
}
for (auto VD : CurModuleResults) {
Results.push_back(LookupResultEntry(VD));
#ifndef NDEBUG
addedResult(Results.back());
#endif
}
filterForDiscriminator(Results, DebugClient);
}
void UnqualifiedLookupFactory::addNamesKnownToDebugClient(
const DeclContext *dc) {
if (Name.isSimpleName() && DebugClient)
DebugClient->lookupAdditions(Name.getBaseName(),
const_cast<DeclContext *>(dc), Loc,
isOriginallyTypeLookup, Results);
}
void UnqualifiedLookupFactory::addUnavailableInnerResults() {
Results = std::move(UnavailableInnerResults);
}
void UnqualifiedLookupFactory::lookForAModuleWithTheGivenName(
const DeclContext *const dc) {
using namespace namelookup;
if (!Name.isSimpleName() || Name.isSpecial())
return;
// Look for a module with the given name.
if (Name.isSimpleName(M.getName())) {
Results.push_back(LookupResultEntry(&M));
#ifndef NDEBUG
addedResult(Results.back());
#endif
return;
}
ModuleDecl *desiredModule = Ctx.getLoadedModule(Name.getBaseIdentifier());
if (!desiredModule && Name.getFullName() == Ctx.TheBuiltinModule->getName())
desiredModule = Ctx.TheBuiltinModule;
if (desiredModule) {
// Make sure the desired module is actually visible from the current
// context.
if (Ctx.getImportCache().isImportedBy(desiredModule, dc)) {
Results.push_back(LookupResultEntry(desiredModule));
#ifndef NDEBUG
addedResult(Results.back());
#endif
}
}
}
#pragma mark common helper definitions
void UnqualifiedLookupFactory::findResultsAndSaveUnavailables(
const DeclContext *lookupContextForThisContext,
ResultFinderForTypeContext &&resultFinderForTypeContext,
NLOptions baseNLOptions) {
auto firstPossiblyUnavailableResult = Results.size();
resultFinderForTypeContext.findResults(Name, baseNLOptions,
lookupContextForThisContext, Results);
setAsideUnavailableResults(firstPossiblyUnavailableResult);
}
NLOptions UnqualifiedLookupFactory::computeBaseNLOptions(
const UnqualifiedLookupOptions 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;
}
bool UnqualifiedLookupFactory::isFirstResultEnough() const {
return !Results.empty() && !options.contains(Flags::IncludeOuterResults);
}
bool UnqualifiedLookupFactory::hasPreciseScopingOfVarDecls() const {
return !options.contains(Flags::IncludeOuterResults);
}
void UnqualifiedLookupFactory::recordCompletionOfAScope() {
// OK to call (NOOP) if there are more inner results and Results is empty
if (IndexOfFirstOuterResult == 0)
IndexOfFirstOuterResult = Results.size();
}
UnqualifiedLookupFactory::ResultFinderForTypeContext::
ResultFinderForTypeContext(UnqualifiedLookupFactory *factory,
const DeclContext *dynamicContext,
const DeclContext *staticContext)
: factory(factory), dynamicContext(dynamicContext),
staticContext(staticContext), selfBounds(findSelfBounds(staticContext)) {}
UnqualifiedLookupFactory::ResultFinderForTypeContext::SelfBounds
UnqualifiedLookupFactory::ResultFinderForTypeContext::findSelfBounds(
const DeclContext *dc) {
auto nominal = dc->getSelfNominalTypeDecl();
if (!nominal)
return {};
SelfBounds selfBounds;
selfBounds.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)
selfBounds.push_back(bound);
}
return selfBounds;
}
#pragma mark ASTScopeImpl support
void UnqualifiedLookupFactory::lookInASTScopes() {
ASTScopeDeclConsumerForUnqualifiedLookup consumer(*this);
#ifndef NDEBUG
stopForDebuggingIfStartingTargetLookup(true);
#endif
ASTScope::unqualifiedLookup(DC->getParentSourceFile(), Loc, consumer);
}
void ASTScopeDeclConsumerForUnqualifiedLookup::maybeUpdateSelfDC(
VarDecl *var) {
// We have a binding named 'self'.
//
// There are three possibilities:
//
// 1) This binding is the 'self' parameter of a method,
// 2) This binding is a bona-fide 'self' capture, meaning a capture
// list entry named 'self' with initial value expression 'self',
// 3) None of the above.
//
// How we handle these cases depends on whether we've already seen
// another 'self' binding.
if (candidateSelfDC == nullptr) {
// We haven't seen one yet, so record it.
if (var->isSelfParameter())
candidateSelfDC = var->getDeclContext();
else if (var->isSelfParamCapture())
candidateSelfDC = var->getParentCaptureList()->getClosureBody();
} else {
// If we see a binding named 'self' that is not a bona-fide
// 'self', we have to forget about the previous 'self' capture
// because it's not going to be the right one for accessing
// instance members of the innermost nominal type. Eg,
//
// class C {
// func bar() {}
// func foo() {
// _ { [self=12] { [self] bar() } }
// }
// }
//
// Instead, we're going to move on and look for the next-innermost
// 'self' binding.
if (!var->isSelfParameter() &&
!var->isSelfParamCapture())
candidateSelfDC = nullptr;
}
}
bool ASTScopeDeclConsumerForUnqualifiedLookup::consume(
ArrayRef<ValueDecl *> values, NullablePtr<DeclContext> baseDC) {
for (auto *value: values) {
if (factory.isOriginallyTypeLookup && !isa<TypeDecl>(value))
continue;
if (auto *var = dyn_cast<VarDecl>(value)) {
// Try to resolve the base for unqualified instance member
// references. This is used by lookInMembers().
if (var->getName() == factory.Ctx.Id_self) {
maybeUpdateSelfDC(var);
}
// Local VarDecls with a pattern binding are visited as part of their
// BraceStmt when hasPreciseScopingOfVarDecls() is off.
if (var->getParentPatternBinding() &&
!factory.hasPreciseScopingOfVarDecls())
continue;
}
auto fullName = factory.Name.getFullName();
if (!value->getName().matchesRef(fullName)) {
bool foundMatch = false;
if (auto *varDecl = dyn_cast<VarDecl>(value)) {
// Check if the name matches any auxiliary decls not in the AST
varDecl->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) {
if (auxiliaryVar->ValueDecl::getName().matchesRef(fullName)) {
value = auxiliaryVar;
foundMatch = true;
}
});
}
if (!foundMatch)
continue;
}
factory.Results.push_back(LookupResultEntry(value));
#ifndef NDEBUG
factory.stopForDebuggingIfAddingTargetLookupResult(factory.Results.back());
#endif
}
factory.recordCompletionOfAScope();
return factory.isFirstResultEnough();
}
bool ASTScopeDeclConsumerForUnqualifiedLookup::consumePossiblyNotInScope(
ArrayRef<VarDecl *> vars) {
if (factory.hasPreciseScopingOfVarDecls())
return false;
for (auto *var : vars) {
if (!factory.Name.getFullName().isSimpleName(var->getName()))
continue;
factory.Results.push_back(LookupResultEntry(var));
}
return false;
}
bool ASTScopeDeclGatherer::consume(ArrayRef<ValueDecl *> valuesArg,
NullablePtr<DeclContext>) {
for (auto *v: valuesArg)
values.push_back(v);
return false;
}
// TODO: in future, migrate this functionality into ASTScopes
bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers(
DeclContext *const scopeDC,
NominalTypeDecl *const nominal) {
if (candidateSelfDC) {
if (auto *afd = dyn_cast<AbstractFunctionDecl>(candidateSelfDC)) {
assert(factory.isInsideBodyOfFunction(afd) && "Should be inside");
}
}
// We're looking for members of a type.
//
// If we started the looking from inside a scope where a 'self' parameter
// is visible, instance members are returned with the 'self' parameter's
// DeclContext as the base, which is how the expression checker knows to
// convert the unqualified reference into a self member access.
auto resultFinder = UnqualifiedLookupFactory::ResultFinderForTypeContext(
&factory, candidateSelfDC ? candidateSelfDC : scopeDC, scopeDC);
factory.findResultsAndSaveUnavailables(scopeDC, std::move(resultFinder),
factory.baseNLOptions);
factory.recordCompletionOfAScope();
// We're done looking inside a nominal type declaration. It is possible
// that this nominal type is nested inside of another type, in which case
// we will visit the outer type next. Make sure to clear out the known
// 'self' parameeter context, since any members of the outer type are
// not accessed via the innermost 'self' parameter.
candidateSelfDC = nullptr;
return factory.isFirstResultEnough();
}
LookupResult
UnqualifiedLookupRequest::evaluate(Evaluator &evaluator,
UnqualifiedLookupDescriptor desc) const {
SmallVector<LookupResultEntry, 4> results;
size_t indexOfFirstOuterResult = 0;
UnqualifiedLookupFactory factory(desc.Name, desc.DC, desc.Loc, desc.Options,
results, indexOfFirstOuterResult);
factory.performUnqualifiedLookup();
return LookupResult(results, indexOfFirstOuterResult);
}
#pragma mark debugging
void UnqualifiedLookupFactory::ResultFinderForTypeContext::dump() const {
(void)factory;
llvm::errs() << "dynamicContext: ";
dynamicContext->dumpContext();
llvm::errs() << "staticContext: ";
staticContext->dumpContext();
llvm::errs() << "selfBounds: ";
for (const auto *D : selfBounds)
D->dump(llvm::errs(), 1);
llvm::errs() << "\n";
}
void UnqualifiedLookupFactory::dump() const { print(llvm::errs()); }
void UnqualifiedLookupFactory::dumpScopes() const { printScopes(llvm::errs()); }
void UnqualifiedLookupFactory::dumpResults() const {
printResults(llvm::errs());
}
void UnqualifiedLookupFactory::printScopes(raw_ostream &out) const {
out << "\n\nScopes:\n";
DC->getParentSourceFile()->getScope().print(out);
out << "\n";
}
void UnqualifiedLookupFactory::printResults(raw_ostream &out) const {
for (auto i : indices(Results)) {
out << i << ": ";
Results[i].print(out);
out << "\n";
}
}
void UnqualifiedLookupFactory::print(raw_ostream &OS) const {
OS << "Look up";
#ifndef NDEBUG
OS << " (" << lookupCounter << ")";
#endif
OS << " '" << Name << "' at: ";
Loc.print(OS, DC->getASTContext().SourceMgr);
OS << "\nStarting in: ";
DC->printContext(OS);
OS << "\n";
}
#pragma mark breakpointing
#ifndef NDEBUG
bool UnqualifiedLookupFactory::isTargetLookup() const {
return lookupCounter == targetLookup;
}
void UnqualifiedLookupFactory::stopForDebuggingIfStartingTargetLookup(
const bool isASTScopeLookup) const {
if (!isTargetLookup())
return;
if (isASTScopeLookup)
llvm::errs() << "starting target ASTScopeImpl lookup\n";
else
llvm::errs() << "starting target context-based lookup\n";
}
void UnqualifiedLookupFactory::stopForDebuggingIfDuringTargetLookup(
const bool isASTScopeLookup) const {
if (!isTargetLookup())
return;
if (isASTScopeLookup)
llvm::errs() << "during target ASTScopeImpl lookup\n";
else
llvm::errs() << "during target context-based lookup\n";
}
void UnqualifiedLookupFactory::stopForDebuggingIfAddingTargetLookupResult(
const LookupResultEntry &e) const {
if (!isTargetLookup())
return;
auto &out = llvm::errs();
out << "\nresult for Target lookup:\n";
e.print(out);
out << "\n";
}
void UnqualifiedLookupFactory::addedResult(const LookupResultEntry &e) const {
stopForDebuggingIfAddingTargetLookupResult(e);
}
unsigned UnqualifiedLookupFactory::lookupCounter = 0;
// set to ~0 when not debugging
const unsigned UnqualifiedLookupFactory::targetLookup = ~0;
#endif // NDEBUG
namespace {
class ASTScopeDeclConsumerForLocalLookup
: public AbstractASTScopeDeclConsumer {
DeclName name;
bool stopAfterInnermostBraceStmt;
SmallVectorImpl<ValueDecl *> &results;
public:
ASTScopeDeclConsumerForLocalLookup(
DeclName name, bool stopAfterInnermostBraceStmt,
SmallVectorImpl<ValueDecl *> &results)
: name(name), stopAfterInnermostBraceStmt(stopAfterInnermostBraceStmt),
results(results) {}
bool consume(ArrayRef<ValueDecl *> values,
NullablePtr<DeclContext> baseDC) override {
for (auto *value: values) {
if (auto *varDecl = dyn_cast<VarDecl>(value)) {
// Check if the name matches any auxiliary decls not in the AST
varDecl->visitAuxiliaryDecls([&](VarDecl *auxiliaryVar) {
if (name.isSimpleName(auxiliaryVar->getName())) {
results.push_back(auxiliaryVar);
}
});
}
if (value->getName().matchesRef(name))
results.push_back(value);
}
return (!stopAfterInnermostBraceStmt && !results.empty());
}
bool lookInMembers(DeclContext *const,
NominalTypeDecl *const) override {
return true;
}
bool finishLookupInBraceStmt(BraceStmt *stmt) override {
return stopAfterInnermostBraceStmt;
}
#ifndef NDEBUG
void startingNextLookupStep() override {}
void finishingLookup(std::string) const override {}
bool isTargetLookup() const override { return false; }
#endif
};
}
/// Lookup that only finds local declarations and does not trigger
/// interface type computation.
void ASTScope::lookupLocalDecls(SourceFile *sf, DeclName name, SourceLoc loc,
bool stopAfterInnermostBraceStmt,
SmallVectorImpl<ValueDecl *> &results) {
ASTScopeDeclConsumerForLocalLookup consumer(name, stopAfterInnermostBraceStmt,
results);
ASTScope::unqualifiedLookup(sf, loc, consumer);
}
ValueDecl *ASTScope::lookupSingleLocalDecl(SourceFile *sf, DeclName name,
SourceLoc loc) {
SmallVector<ValueDecl *, 1> result;
ASTScope::lookupLocalDecls(sf, name, loc,
/*finishLookupInBraceStmt=*/false,
result);
if (result.size() != 1)
return nullptr;
return result[0];
}