//===--- 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/AST/SourceFile.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; 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; 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(member) ? staticContext : dynamicContext; } public: /// Do the lookups and add matches to results. void findResults(const DeclNameRef &Name, NLOptions baseNLOptions, const DeclContext *contextForLookup, SmallVectorImpl &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 &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 &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 { private: 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 \c nullptr if instance members should not be 'self' qualified. /// // FIXME: This field is currently reset to \c nullptr by `lookInMembers` as // part of the lookup traversal of scopes. If, instead, consumers were // created at each point in the traversal, this field would no longer need // to be marked \c mutable. mutable const DeclContext *candidateSelfDC; void maybeUpdateSelfDC(VarDecl *var); public: ASTScopeDeclConsumerForUnqualifiedLookup(UnqualifiedLookupFactory &factory) : factory(factory), candidateSelfDC(nullptr) {} virtual ~ASTScopeDeclConsumerForUnqualifiedLookup() = default; bool consume(ArrayRef values, NullablePtr baseDC = nullptr) override; bool consumePossiblyNotInScope(ArrayRef vars) override; bool lookInMembers(const DeclContext *) const 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 &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() && DC->getParentSourceFile()) { // Operator lookup is always global, for the time being. if (!Name.isOperator()) lookInASTScopes(); } else { assert((DC->isModuleScopeContext() || !DC->getParentSourceFile()) && "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(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 &results) const { // An optimization: if (selfBounds.empty()) return; SmallVector Lookup; contextForLookup->lookupQualified(selfBounds, Name, baseNLOptions, Lookup); for (auto Result : Lookup) { results.emplace_back(const_cast(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 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(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 = nullptr; auto givenName = Name.getBaseIdentifier(); // Check if the given name appearing in the source file is a module // real name or alias; for example, if `-module-alias Foo=Bar` was // passed, the alias 'Foo' should appear in source files, not 'Bar'. // If the real name 'Bar' was used, looking up getRealModuleName with // the real name 'Bar' and realNameFromAlias option should return // an empty Identifier. if (!Ctx.getRealModuleName(givenName, ASTContext::ModuleAliasLookupOption::realNameFromAlias).empty()) { // Only load the module if the lookup value is not empty, i.e. given // name is a module alias, not a real module name. desiredModule = Ctx.getLoadedModule(givenName); } 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(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 values, NullablePtr baseDC) { for (auto *value: values) { if (factory.isOriginallyTypeLookup && !isa(value)) continue; if (auto *var = dyn_cast(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(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 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 valuesArg, NullablePtr) { for (auto *v: valuesArg) values.push_back(v); return false; } // TODO: in future, migrate this functionality into ASTScopes bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers( const DeclContext *scopeDC) const { if (candidateSelfDC) { if (auto *afd = dyn_cast(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' parameter 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 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 &results; public: ASTScopeDeclConsumerForLocalLookup( DeclName name, bool stopAfterInnermostBraceStmt, SmallVectorImpl &results) : name(name), stopAfterInnermostBraceStmt(stopAfterInnermostBraceStmt), results(results) {} bool consume(ArrayRef values, NullablePtr baseDC) override { for (auto *value: values) { bool foundMatch = false; if (auto *varDecl = dyn_cast(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); foundMatch = true; } }); } if (!foundMatch && value->getName().matchesRef(name)) results.push_back(value); } return (!stopAfterInnermostBraceStmt && !results.empty()); } bool lookInMembers(const DeclContext *) 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 &results) { ASTScopeDeclConsumerForLocalLookup consumer(name, stopAfterInnermostBraceStmt, results); ASTScope::unqualifiedLookup(sf, loc, consumer); } ValueDecl *ASTScope::lookupSingleLocalDecl(SourceFile *sf, DeclName name, SourceLoc loc) { SmallVector result; ASTScope::lookupLocalDecls(sf, name, loc, /*finishLookupInBraceStmt=*/false, result); if (result.size() != 1) return nullptr; return result[0]; }