//===--- 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/ClangModuleLoader.h" #include "swift/AST/DebuggerClient.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/ImportCache.h" #include "swift/AST/Initializer.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/ModuleNameLookup.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ReferencedNameTracker.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 "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 { /// Determine whether unqualified lookup should look at the members of the /// given nominal type or extension, vs. only looking at type parameters. template 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; SourceManager &SM = decl->getASTContext().SourceMgr; // If a code completion happens inside a function body, some lookups may // happen from the 'loc' that is in a different buffer from the 'decl'. // In such cases, look for members of the 'decl' because we know 'loc' is // inside a function body in the 'decl'. if (SM.hasCodeCompletionBuffer()) { auto completionBufferID = SM.getCodeCompletionBufferID(); if (SM.getRangeForBuffer(completionBufferID).contains(loc)) { auto declBufferID = decl->getDeclContext()->getParentSourceFile()->getBufferID(); if (completionBufferID != declBufferID) return true; } } // Within the braces, always look for members. auto braces = decl->getBraces(); if (braces.Start != braces.End && SM.rangeContainsTokenLoc(braces, loc)) return true; // Within 'where' clause, we can also look for members. if (auto *whereClause = decl->getTrailingWhereClause()) { SourceRange whereClauseRange = whereClause->getSourceRange(); if (whereClauseRange.isValid() && SM.rangeContainsTokenLoc(whereClauseRange, loc)) { return true; } } // Don't look at the members. return false; } } // end anonymous namespace namespace { class UnqualifiedLookupFactory { friend class ASTScopeDeclConsumerForUnqualifiedLookup; public: using Flags = UnqualifiedLookupFlags; using Options = UnqualifiedLookupOptions; using ResultsVector = SmallVector; private: struct ContextAndResolvedIsCascadingUse { DeclContext *const DC; const bool isCascadingUse; }; /// 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. DeclContext *const dynamicContext; /// Types are formally members of the metatype, i.e. the static type of the /// activation record. 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, DeclContext *dynamicContext, DeclContext *staticContext); SWIFT_DEBUG_DUMP; private: SelfBounds findSelfBounds(DeclContext *dc); // Classify this declaration. // Types are formally members of the metatype. 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, bool isCascadingUse, NLOptions baseNLOptions, DeclContext *contextForLookup, SmallVectorImpl &results) const; }; enum class AddGenericParameters { Yes, No }; #ifndef NDEBUG /// A consumer for debugging that lets the UnqualifiedLookupFactory know when /// finding something. class InstrumentedNamedDeclConsumer : public NamedDeclConsumer { virtual void anchor() override; UnqualifiedLookupFactory *factory; public: InstrumentedNamedDeclConsumer(UnqualifiedLookupFactory *factory, DeclNameRef name, SmallVectorImpl &results, bool isTypeLookup) : NamedDeclConsumer(name, results, isTypeLookup), factory(factory) {} virtual void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo = {}) override { unsigned before = results.size(); NamedDeclConsumer::foundDecl(VD, Reason, dynamicLookupInfo); unsigned after = results.size(); if (after > before) factory->addedResult(results.back()); } }; #endif // 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; // Transputs #ifndef NDEBUG InstrumentedNamedDeclConsumer Consumer; #else NamedDeclConsumer Consumer; #endif // Outputs SmallVectorImpl &Results; size_t &IndexOfFirstOuterResult; ResultsVector UnavailableInnerResults; #ifndef NDEBUG static unsigned lookupCounter; static const unsigned targetLookup; #endif public: // for exp debugging SourceFile const *recordedSF = nullptr; bool recordedIsCascadingUse = false; unsigned resultsSizeBeforeLocalsPass = ~0; 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: struct ContextAndUnresolvedIsCascadingUse { DeclContext *whereToLook; Optional isCascadingUse; ContextAndResolvedIsCascadingUse resolve(const bool resolution) const { return ContextAndResolvedIsCascadingUse{ whereToLook, isCascadingUse.getValueOr(resolution)}; } }; bool useASTScopesForLookup() const; /// For testing, assume this lookup is enabled: bool wouldUseASTScopesForLookupIfItWereEnabled() const; void lookUpTopLevelNamesInModuleScopeContext(DeclContext *); void lookInASTScopes(); /// Can lookup stop searching for results, assuming hasn't looked for outer /// results yet? bool isFirstResultEnough() 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(); template void ifNotDoneYet(Fn fn) { recordCompletionOfAScope(); if (!isFirstResultEnough()) fn(); } template void ifNotDoneYet(Fn1 fn1, Fn2 fn2) { ifNotDoneYet(fn1); ifNotDoneYet(fn2); } #pragma mark context-based lookup declarations void lookupOperatorInDeclContexts(ContextAndUnresolvedIsCascadingUse); /// When performing a lookup, we may come across a capture of 'self'. We /// will need to remember the DeclContext of the innermost captured self so /// that it can be used as the base DeclContext if we find a lookup result /// in the enclosing type. \c capturedSelfContext tracks this. void lookupNamesIntroducedBy(const ContextAndUnresolvedIsCascadingUse, DeclContext *capturedSelfContext); void finishLookingInContext( AddGenericParameters addGenericParameters, DeclContext *lookupContextForThisContext, Optional &&resultFinderForTypeContext, Optional isCascadingUse, DeclContext *capturedSelfContext); void lookupInModuleScopeContext(DeclContext *, Optional isCascadingUse); void lookupNamesIntroducedByPatternBindingInitializer( PatternBindingInitializer *PBI, Optional isCascadingUse, DeclContext *capturedSelfContext); void lookupNamesIntroducedByLazyVariableInitializer(PatternBindingInitializer *PBI, ParamDecl *selfParam, Optional isCascadingUse, DeclContext *capturedSelfContext); void lookupNamesIntroducedByInitializerOfStoredPropertyOfAType( PatternBindingInitializer *PBI, Optional isCascadingUse); /// An initializer of a global name, or a function-likelocal name. void lookupNamesIntroducedByInitializerOfGlobalOrLocal( PatternBindingInitializer *PBI, Optional isCascadingUse, DeclContext *capturedSelfContext); void lookupNamesIntroducedByFunctionDecl(AbstractFunctionDecl *AFD, Optional isCascadingUse, DeclContext *capturedSelfContext); void lookupNamesIntroducedByMemberFunction(AbstractFunctionDecl *AFD, bool isCascadingUse, DeclContext *capturedSelfContext); void lookupNamesIntroducedByPureFunction(AbstractFunctionDecl *AFD, bool isCascadingUse, DeclContext *capturedSelfContext); void lookupNamesIntroducedByClosure(AbstractClosureExpr *ACE, Optional isCascadingUse, DeclContext *capturedSelfContext); template void lookupNamesIntroducedByNominalTypeOrExtension( NominalTypeDeclOrExtensionDecl *D, Optional isCascadingUse); void lookupNamesIntroducedByDefaultArgumentInitializer( DefaultArgumentInitializer *I, Optional isCascadingUse, DeclContext *capturedSelfContext); void lookupNamesIntroducedByMiscContext(DeclContext *dc, Optional isCascadingUse, DeclContext *capturedSelfContext); void lookForLocalVariablesIn(AbstractFunctionDecl *AFD, Optional isCascadingUse); void lookForLocalVariablesIn(ClosureExpr *); void lookForLocalVariablesIn(SourceFile *); bool isOutsideBodyOfFunction(const AbstractFunctionDecl *const AFD) const; void addGenericParametersForContext(DeclContext *dc); void addGenericParametersForContext(GenericParamList *); /// Consume generic parameters void addGenericParametersForFunction(AbstractFunctionDecl *AFD); static GenericParamList *getGenericParams(const DeclContext *const dc); /// 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 recordDependencyOnTopLevelName(DeclContext *topLevelContext, DeclNameRef name, bool isCascadingUse); void addImportedResults(DeclContext *const dc); void addNamesKnownToDebugClient(DeclContext *dc); void addUnavailableInnerResults(); void lookForAModuleWithTheGivenName(DeclContext *const dc); #pragma mark common helper declarations static NLOptions computeBaseNLOptions(const UnqualifiedLookupOptions options, const bool isOriginallyTypeLookup); Optional getInitialIsCascadingUse() const { return options.contains(Flags::KnownPrivate) ? Optional(false) : None; } static bool resolveIsCascadingUse(const DeclContext *const dc, Optional isCascadingUse, bool onlyCareAboutFunctionBody) { return isCascadingUse.getValueOr(dc->isCascadingContextForLookup( /*functionsAreNonCascading=*/onlyCareAboutFunctionBody)); } static bool resolveIsCascadingUse(ContextAndUnresolvedIsCascadingUse x, bool onlyCareAboutFunctionBody) { return resolveIsCascadingUse(x.whereToLook, x.isCascadingUse, onlyCareAboutFunctionBody); } void findResultsAndSaveUnavailables( DeclContext *lookupContextForThisContext, ResultFinderForTypeContext &&resultFinderForTypeContext, bool isCascadingUse, 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; bool verifyEqualTo(const UnqualifiedLookupFactory &&, StringRef thisLabel, StringRef otherLabel) const; /// Legacy lookup is wrong here; we should NOT find this symbol. bool shouldDiffer() const; StringRef getSourceFileName() 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; public: ASTScopeDeclConsumerForUnqualifiedLookup(UnqualifiedLookupFactory &factory) : factory(factory) {} virtual ~ASTScopeDeclConsumerForUnqualifiedLookup() = default; bool consume(ArrayRef values, DeclVisibilityKind vis, NullablePtr baseDC = nullptr) override; /// returns true if finished and new value for isCascadingUse bool lookInMembers(NullablePtr selfDC, DeclContext *const scopeDC, NominalTypeDecl *const nominal, function_ref)>) 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)), #ifdef NDEBUG Consumer(Name, Results, isOriginallyTypeLookup), #else Consumer(this, Name, Results, isOriginallyTypeLookup), #endif 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()); const Optional initialIsCascadingUse = getInitialIsCascadingUse(); ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUse{ DC, initialIsCascadingUse}; const bool crosscheckUnqualifiedLookup = Ctx.LangOpts.CrosscheckUnqualifiedLookup; if (useASTScopesForLookup()) { static bool haveWarned = false; if (!haveWarned && Ctx.LangOpts.WarnIfASTScopeLookup) { haveWarned = true; llvm::errs() << "WARNING: TRYING Scope exclusively\n"; } lookInASTScopes(); } else { #ifndef NDEBUG stopForDebuggingIfStartingTargetLookup(false); #endif if (Name.isOperator()) lookupOperatorInDeclContexts(contextAndIsCascadingUse); else lookupNamesIntroducedBy(contextAndIsCascadingUse, NULL); } if (crosscheckUnqualifiedLookup && wouldUseASTScopesForLookupIfItWereEnabled()) { ResultsVector results; size_t indexOfFirstOuterResult = 0; UnqualifiedLookupFactory altLookup(Name, DC, Loc, options, results, indexOfFirstOuterResult); if (!useASTScopesForLookup()) altLookup.lookInASTScopes(); else if (Name.isOperator()) altLookup.lookupOperatorInDeclContexts(contextAndIsCascadingUse); else altLookup.lookupNamesIntroducedBy(contextAndIsCascadingUse, NULL); const auto *ASTScopeLabel = "ASTScope lookup"; const auto *contextLabel = "context-bsed lookup"; const auto *mainLabel = useASTScopesForLookup() ? ASTScopeLabel : contextLabel; const auto *alternateLabel = useASTScopesForLookup() ? contextLabel : ASTScopeLabel; assert(verifyEqualTo(std::move(altLookup), mainLabel, alternateLabel)); } } void UnqualifiedLookupFactory::lookUpTopLevelNamesInModuleScopeContext( DeclContext *DC) { // TODO: Does the debugger client care about compound names? if (Name.isSimpleName() && !Name.isSpecial() && DebugClient && DebugClient->lookupOverrides(Name.getBaseName(), 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(); } bool UnqualifiedLookupFactory::useASTScopesForLookup() const { return Ctx.LangOpts.EnableASTScopeLookup && wouldUseASTScopesForLookupIfItWereEnabled(); } bool UnqualifiedLookupFactory::wouldUseASTScopesForLookupIfItWereEnabled() const { if (!Loc.isValid()) return false; const auto *const SF = DC->getParentSourceFile(); return SF && SF->isSuitableForASTScopes(); } #pragma mark context-based lookup definitions void UnqualifiedLookupFactory::lookupOperatorInDeclContexts( const ContextAndUnresolvedIsCascadingUse contextAndUseArg) { ContextAndResolvedIsCascadingUse contextAndResolvedIsCascadingUse{ // Operators are global contextAndUseArg.whereToLook->getModuleScopeContext(), resolveIsCascadingUse(contextAndUseArg, /*onlyCareAboutFunctionBody*/ true)}; lookupInModuleScopeContext(contextAndResolvedIsCascadingUse.DC, contextAndResolvedIsCascadingUse.isCascadingUse); } // TODO: Unify with LookupVisibleDecls.cpp::lookupVisibleDeclsImpl void UnqualifiedLookupFactory::lookupNamesIntroducedBy( const ContextAndUnresolvedIsCascadingUse contextAndIsCascadingUseArg, DeclContext *capturedSelfContext) { #ifndef NDEBUG stopForDebuggingIfDuringTargetLookup(false); #endif DeclContext *const dc = contextAndIsCascadingUseArg.whereToLook; const auto isCascadingUseSoFar = contextAndIsCascadingUseArg.isCascadingUse; if (dc->isModuleScopeContext()) { assert(capturedSelfContext == NULL && "By the time we reach module scope," " there should be no 'self'."); lookupInModuleScopeContext(dc, isCascadingUseSoFar); } else if (auto *PBI = dyn_cast(dc)) lookupNamesIntroducedByPatternBindingInitializer(PBI, isCascadingUseSoFar, capturedSelfContext); else if (auto *AFD = dyn_cast(dc)) lookupNamesIntroducedByFunctionDecl(AFD, isCascadingUseSoFar, capturedSelfContext); else if (auto *ACE = dyn_cast(dc)) lookupNamesIntroducedByClosure(ACE, isCascadingUseSoFar, capturedSelfContext); else if (auto *ED = dyn_cast(dc)) { assert(capturedSelfContext == NULL && "When we recurse into type context," " 'self' should be forgotten."); lookupNamesIntroducedByNominalTypeOrExtension(ED, isCascadingUseSoFar); } else if (auto *ND = dyn_cast(dc)) { assert(capturedSelfContext == NULL && "When we recurse into type context," " 'self' should be forgotten."); lookupNamesIntroducedByNominalTypeOrExtension(ND, isCascadingUseSoFar); } else if (auto I = dyn_cast(dc)) lookupNamesIntroducedByDefaultArgumentInitializer(I, isCascadingUseSoFar, capturedSelfContext); else lookupNamesIntroducedByMiscContext(dc, isCascadingUseSoFar, capturedSelfContext); } void UnqualifiedLookupFactory::lookupInModuleScopeContext( DeclContext *dc, Optional isCascadingUse) { if (auto SF = dyn_cast(dc)) { resultsSizeBeforeLocalsPass = Results.size(); lookForLocalVariablesIn(SF); } ifNotDoneYet([&] { // 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. recordDependencyOnTopLevelName(dc, Name, isCascadingUse.getValueOr(true)); lookUpTopLevelNamesInModuleScopeContext(dc); }); } void UnqualifiedLookupFactory::lookupNamesIntroducedByPatternBindingInitializer( PatternBindingInitializer *PBI, Optional isCascadingUse, DeclContext *capturedSelfContext) { // Lazy variable initializer contexts have a 'self' parameter for // instance member lookup. if (auto *selfParam = PBI->getImplicitSelfDecl()) lookupNamesIntroducedByLazyVariableInitializer(PBI, selfParam, isCascadingUse, capturedSelfContext); else if (PBI->getParent()->isTypeContext()) { assert(capturedSelfContext == NULL && "If we were in a type's property" " initializer, there should be no 'self' to have been captured."); lookupNamesIntroducedByInitializerOfStoredPropertyOfAType( PBI, isCascadingUse); } else lookupNamesIntroducedByInitializerOfGlobalOrLocal(PBI, isCascadingUse, capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByLazyVariableInitializer( PatternBindingInitializer *PBI, ParamDecl *selfParam, Optional isCascadingUse, DeclContext *capturedSelfContext) { Consumer.foundDecl(selfParam, DeclVisibilityKind::FunctionParameter); ifNotDoneYet([&] { DeclContext *const patternContainer = PBI->getParent(); // clang-format off finishLookingInContext( AddGenericParameters::Yes, patternContainer, ResultFinderForTypeContext(this, PBI, patternContainer), resolveIsCascadingUse(PBI, isCascadingUse, /*onlyCareAboutFunctionBody=*/false), capturedSelfContext); // clang-format on }); } void UnqualifiedLookupFactory:: lookupNamesIntroducedByInitializerOfStoredPropertyOfAType( PatternBindingInitializer *PBI, Optional isCascadingUse) { // Initializers for stored properties of types perform static // lookup into the surrounding context. DeclContext *const storedPropertyContainer = PBI->getParent(); // clang-format off finishLookingInContext( AddGenericParameters::Yes, storedPropertyContainer, ResultFinderForTypeContext( this, storedPropertyContainer, storedPropertyContainer), resolveIsCascadingUse(storedPropertyContainer, None, /*onlyCareAboutFunctionBody=*/false), /*capturedSelfContext=*/NULL); // clang-format on } void UnqualifiedLookupFactory:: lookupNamesIntroducedByInitializerOfGlobalOrLocal( PatternBindingInitializer *PBI, Optional isCascadingUse, DeclContext *capturedSelfContext) { // There's not much to find here, we'll keep going up to a parent // context. // clang-format off finishLookingInContext( AddGenericParameters::Yes, PBI, None, // not looking in the partic type resolveIsCascadingUse(PBI, isCascadingUse, /*onlyCareAboutFunctionBody=*/false), capturedSelfContext); // clang-format on } void UnqualifiedLookupFactory::lookupNamesIntroducedByFunctionDecl( AbstractFunctionDecl *AFD, Optional isCascadingUseArg, DeclContext *capturedSelfContext) { // DOUG: how does this differ from isOutsideBodyOfFunction below? const bool isCascadingUse = AFD->isCascadingContextForLookup(false) && (isCascadingUseArg.getValueOr( Loc.isInvalid() || AFD->getBodySourceRange().isInvalid() || !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc))); if (AFD->getDeclContext()->isTypeContext()) lookupNamesIntroducedByMemberFunction(AFD, isCascadingUse, capturedSelfContext); else lookupNamesIntroducedByPureFunction(AFD, isCascadingUse, capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByMemberFunction( AbstractFunctionDecl *AFD, bool isCascadingUse, DeclContext *capturedSelfContext) { lookForLocalVariablesIn(AFD, isCascadingUse); ifNotDoneYet( [&] { // If we're inside a function context, we're about to move to // the parent DC, so we have to check the function's generic // parameters first. // Cannot start here in finishLookingInContext because AFD's // getOuterParameters may be null even when AFD's parent has generics. addGenericParametersForFunction(AFD); }, [&] { DeclContext *const fnDeclContext = AFD->getDeclContext(); // 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 we've captured self // somewhere down the tree, we should use that as the context // for lookup. DeclContext *const BaseDC = isOutsideBodyOfFunction(AFD) ? fnDeclContext : capturedSelfContext ? capturedSelfContext : AFD; // 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. // clang-format off finishLookingInContext( AddGenericParameters::Yes, AFD->getParent(), ResultFinderForTypeContext(this, BaseDC, fnDeclContext), isCascadingUse, NULL); // clang-format on }); } void UnqualifiedLookupFactory::lookupNamesIntroducedByPureFunction( AbstractFunctionDecl *AFD, bool isCascadingUse, DeclContext *capturedSelfContext) { lookForLocalVariablesIn(AFD, isCascadingUse); ifNotDoneYet([&] { // clang-format off finishLookingInContext( AddGenericParameters::Yes, AFD, None, isCascadingUse, capturedSelfContext); }); } void UnqualifiedLookupFactory::lookupNamesIntroducedByClosure( AbstractClosureExpr *ACE, Optional isCascadingUse, DeclContext *capturedSelfContext) { if (auto *CE = dyn_cast(ACE)) { lookForLocalVariablesIn(CE); // If we don't already have a captured self context, and this closure // captures the self param (not weakly, so that implicit self is available), // remember that. if (capturedSelfContext == nullptr) if (CE->capturesSelfEnablingImplictSelf()) capturedSelfContext = CE; } ifNotDoneYet([&] { // clang-format off finishLookingInContext( AddGenericParameters::Yes, ACE, None, resolveIsCascadingUse(ACE, isCascadingUse, /*onlyCareAboutFunctionBody=*/false), capturedSelfContext); // clang-format on }); } template void UnqualifiedLookupFactory::lookupNamesIntroducedByNominalTypeOrExtension( NominalTypeDeclOrExtensionDecl *D, Optional isCascadingUse) { // clang-format off finishLookingInContext( AddGenericParameters::Yes, D, shouldLookupMembers(D, Loc) ? Optional( ResultFinderForTypeContext(this, D, D)) : None, resolveIsCascadingUse(D, isCascadingUse, /*onlyCareAboutFunctionBody=*/false), /*capturedSelfContext=*/NULL); // clang-format on } void UnqualifiedLookupFactory:: lookupNamesIntroducedByDefaultArgumentInitializer( DefaultArgumentInitializer *I, Optional isCascadingUse, DeclContext *capturedSelfContext) { // In a default argument, skip immediately out of both the // initializer and the function. finishLookingInContext(AddGenericParameters::No, I->getParent(), None, false, capturedSelfContext); } void UnqualifiedLookupFactory::lookupNamesIntroducedByMiscContext( DeclContext *dc, Optional isCascadingUse, DeclContext *capturedSelfContext) { // clang-format off assert(isa(dc) || isa(dc) || isa(dc) || isa(dc) || isa(dc)); finishLookingInContext( AddGenericParameters::Yes, dc, None, resolveIsCascadingUse(DC, isCascadingUse, /*onlyCareAboutFunctionBody=*/false), capturedSelfContext); // clang-format on } void UnqualifiedLookupFactory::finishLookingInContext( const AddGenericParameters addGenericParameters, DeclContext *const lookupContextForThisContext, Optional &&resultFinderForTypeContext, const Optional isCascadingUse, DeclContext *capturedSelfContext) { #ifndef NDEBUG stopForDebuggingIfDuringTargetLookup(false); #endif // When a generic has the same name as a member, Swift prioritizes the generic // because the member could still be named by qualifying it. But there is no // corresponding way to qualify a generic parameter. // So, look for generics first. if (addGenericParameters == AddGenericParameters::Yes) addGenericParametersForContext(lookupContextForThisContext); ifNotDoneYet( [&] { if (resultFinderForTypeContext) findResultsAndSaveUnavailables(lookupContextForThisContext, std::move(*resultFinderForTypeContext), *isCascadingUse, baseNLOptions); }, // Recurse into the next context. [&] { lookupNamesIntroducedBy(ContextAndUnresolvedIsCascadingUse{ lookupContextForThisContext->getParentForLookup(), isCascadingUse}, capturedSelfContext); }); } void UnqualifiedLookupFactory::lookForLocalVariablesIn( AbstractFunctionDecl *AFD, Optional 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.isInvalid() || AFD->getBodySourceRange().isInvalid() || !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc) || !AFD->getBody()) { return; } namelookup::FindLocalVal localVal(SM, Loc, Consumer); localVal.visit(AFD->getBody()); ifNotDoneYet([&] { if (auto *P = AFD->getImplicitSelfDecl()) localVal.checkValueDecl(P, DeclVisibilityKind::FunctionParameter); localVal.checkParameterList(AFD->getParameters()); }); } void UnqualifiedLookupFactory::lookForLocalVariablesIn(ClosureExpr *CE) { // Look for local variables; normally, the parser resolves these // for us, but it can't do the right thing inside local types. if (Loc.isInvalid()) return; namelookup::FindLocalVal localVal(SM, Loc, Consumer); if (auto body = CE->getBody()) localVal.visit(body); ifNotDoneYet([&] { if (auto params = CE->getParameters()) localVal.checkParameterList(params); }); } void UnqualifiedLookupFactory::lookForLocalVariablesIn(SourceFile *SF) { if (Loc.isInvalid()) return; // 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); } bool UnqualifiedLookupFactory::isOutsideBodyOfFunction( const AbstractFunctionDecl *const AFD) const { return !AFD->isImplicit() && Loc.isValid() && AFD->getBodySourceRange().isValid() && !SM.rangeContainsTokenLoc(AFD->getBodySourceRange(), Loc); } GenericParamList * UnqualifiedLookupFactory::getGenericParams(const DeclContext *const dc) { if (auto nominal = dyn_cast(dc)) return nominal->getGenericParams(); if (auto ext = dyn_cast(dc)) return ext->getGenericParams(); if (auto subscript = dyn_cast(dc)) return subscript->getGenericParams(); if (auto func = dyn_cast(dc)) return func->getGenericParams(); return nullptr; } void UnqualifiedLookupFactory::addGenericParametersForContext( DeclContext *dc) { // Generics can be nested, so visit the generic list, innermost first. // Cannot use DeclContext::forEachGenericContext because this code breaks out // if it finds a match and isFirstResultEnough() addGenericParametersForContext(getGenericParams(dc)); } void UnqualifiedLookupFactory::addGenericParametersForContext( GenericParamList *dcGenericParams) { if (!dcGenericParams) return; namelookup::FindLocalVal localVal(SM, Loc, Consumer); localVal.checkGenericParams(dcGenericParams); ifNotDoneYet([&] { addGenericParametersForContext( dcGenericParams->getOuterParameters()); }); } void UnqualifiedLookupFactory::addGenericParametersForFunction( AbstractFunctionDecl *AFD) { GenericParamList *GenericParams = AFD->getGenericParams(); if (GenericParams) { namelookup::FindLocalVal localVal(SM, Loc, Consumer); localVal.checkGenericParams(GenericParams); } } void UnqualifiedLookupFactory::ResultFinderForTypeContext::findResults( const DeclNameRef &Name, bool isCascadingUse, NLOptions baseNLOptions, DeclContext *contextForLookup, SmallVectorImpl &results) const { // An optimization: if (selfBounds.empty()) return; const NLOptions options = baseNLOptions | (isCascadingUse ? NL_KnownCascadingDependency : NL_KnownNonCascadingDependency); SmallVector Lookup; contextForLookup->lookupQualified(selfBounds, Name, options, Lookup); for (auto Result : Lookup) { results.push_back(LookupResultEntry(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::recordDependencyOnTopLevelName( DeclContext *topLevelContext, DeclNameRef name, bool isCascadingUse) { recordLookupOfTopLevelName(topLevelContext, Name.getFullName(), isCascadingUse); recordedSF = dyn_cast(topLevelContext); recordedIsCascadingUse = isCascadingUse; } void UnqualifiedLookupFactory::addImportedResults(DeclContext *const dc) { using namespace namelookup; SmallVector CurModuleResults; auto resolutionKind = isOriginallyTypeLookup ? ResolutionKind::TypesOnly : ResolutionKind::Overloadable; lookupInModule(dc, Name.getFullName(), CurModuleResults, NLKind::UnqualifiedLookup, resolutionKind, dc); // 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(DeclContext *dc) { if (Name.isSimpleName() && DebugClient) DebugClient->lookupAdditions(Name.getBaseName(), dc, Loc, isOriginallyTypeLookup, Results); } void UnqualifiedLookupFactory::addUnavailableInnerResults() { Results = std::move(UnavailableInnerResults); } void UnqualifiedLookupFactory::lookForAModuleWithTheGivenName( 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( DeclContext *lookupContextForThisContext, ResultFinderForTypeContext &&resultFinderForTypeContext, bool isCascadingUse, NLOptions baseNLOptions) { auto firstPossiblyUnavailableResult = Results.size(); resultFinderForTypeContext.findResults(Name, isCascadingUse, 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); } 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, DeclContext *dynamicContext, DeclContext *staticContext) : factory(factory), dynamicContext(dynamicContext), staticContext(staticContext), selfBounds(findSelfBounds(staticContext)) {} UnqualifiedLookupFactory::ResultFinderForTypeContext::SelfBounds UnqualifiedLookupFactory::ResultFinderForTypeContext::findSelfBounds( 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 const auto history = ASTScope::unqualifiedLookup(DC->getParentSourceFile(), Name, Loc, DC, consumer); ifNotDoneYet([&] { // Copied from lookupInModuleScopeContext // 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 *const moduleScopeContext = DC->getParentSourceFile(); const Optional isCascadingUseAtStartOfLookup = !Name.isOperator() ? getInitialIsCascadingUse() : resolveIsCascadingUse(DC, getInitialIsCascadingUse(), /*onlyCareAboutFunctionBody*/ true); const Optional isCascadingUseAfterLookup = ASTScope::computeIsCascadingUse(history, isCascadingUseAtStartOfLookup); recordDependencyOnTopLevelName(moduleScopeContext, Name, isCascadingUseAfterLookup.getValueOr(true)); lookUpTopLevelNamesInModuleScopeContext(moduleScopeContext); }); } bool ASTScopeDeclConsumerForUnqualifiedLookup::consume( ArrayRef values, DeclVisibilityKind vis, NullablePtr baseDC) { for (auto *value: values) { if (factory.isOriginallyTypeLookup && !isa(value)) continue; if (!value->getFullName().matchesRef(factory.Name.getFullName())) continue; // In order to preserve the behavior of the existing context-based lookup, // which finds all results for non-local variables at the top level instead // of stopping at the first one, ignore results at the top level that are // not local variables. The caller \c lookInASTScopes will // then do the appropriate work when the scope lookup fails. In // FindLocalVal::visitBraceStmt, it sees PatternBindingDecls, not VarDecls, // so a VarDecl at top level would not be found by the context-based lookup. if (isa(value->getDeclContext()) && (vis != DeclVisibilityKind::LocalVariable || isa(value))) return false; factory.Results.push_back(LookupResultEntry(value)); #ifndef NDEBUG factory.stopForDebuggingIfAddingTargetLookupResult(factory.Results.back()); #endif } factory.recordCompletionOfAScope(); return factory.isFirstResultEnough(); } bool ASTScopeDeclGatherer::consume(ArrayRef valuesArg, DeclVisibilityKind, NullablePtr) { for (auto *v: valuesArg) values.push_back(v); return false; } // TODO: in future, migrate this functionality into ASTScopes bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers( NullablePtr selfDC, DeclContext *const scopeDC, NominalTypeDecl *const nominal, function_ref)> calculateIsCascadingUse) { if (selfDC) { if (auto *d = selfDC.get()->getAsDecl()) { if (auto *afd = dyn_cast(d)) assert(!factory.isOutsideBodyOfFunction(afd) && "Should be inside"); } } auto resultFinder = UnqualifiedLookupFactory::ResultFinderForTypeContext( &factory, selfDC ? selfDC.get() : scopeDC, scopeDC); const bool isCascadingUse = calculateIsCascadingUse(factory.getInitialIsCascadingUse()); factory.findResultsAndSaveUnavailables(scopeDC, std::move(resultFinder), isCascadingUse, factory.baseNLOptions); factory.recordCompletionOfAScope(); 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 #ifndef NDEBUG void UnqualifiedLookupFactory::InstrumentedNamedDeclConsumer::anchor() {} #endif 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)) { if (i == resultsSizeBeforeLocalsPass) out << "============== next pass ============\n"; out << i << ": "; Results[i].print(out); out << "\n"; } if (resultsSizeBeforeLocalsPass == Results.size()) out << "============== next pass ============\n"; if (resultsSizeBeforeLocalsPass == ~0u) out << "never tried locals\n\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 debugging: output utilities for grepping static void writeLine(std::string s) { llvm::errs() << "\n+-+-+-+- " << s << "\n"; } StringRef UnqualifiedLookupFactory::getSourceFileName() const { return DC->getParentSourceFile()->getFilename(); } static void writeFirstLine(const UnqualifiedLookupFactory &ul, llvm::Twine s) { std::string line = std::string("In file: ") + ul.getSourceFileName().str() + ", " + s.str(); writeLine(line); } static void writeInconsistent(const UnqualifiedLookupFactory &me, StringRef thisLabel, const UnqualifiedLookupFactory &other, StringRef otherLabel, llvm::Twine s) { writeFirstLine(me, s); other.print(llvm::errs()); llvm::errs() << "\n" << thisLabel << " Results:\n"; me.printResults(llvm::errs()); llvm::errs() << "\n" << otherLabel << " Results:\n"; other.printResults(llvm::errs()); me.printScopes(llvm::errs()); } #pragma mark comparing results bool UnqualifiedLookupFactory::verifyEqualTo( const UnqualifiedLookupFactory &&other, const StringRef thisLabel, StringRef otherLabel) const { if (shouldDiffer()) { return true; } auto writeErr = [&](llvm::Twine s) { writeInconsistent(*this, thisLabel, other, otherLabel, s); }; if (Results.size() != other.Results.size()) { writeErr(thisLabel + " found " + std::to_string(Results.size()) + " but " + otherLabel + " found " + std::to_string(other.Results.size())); assert(false && "mismatch in number of results"); } for (size_t i : indices(Results)) { const auto &e = Results[i]; const auto &oe = other.Results[i]; if (e.getValueDecl() != oe.getValueDecl()) { // print_ast_tc_function_bodies.swift generic from subscript vs get fn std::string a; llvm::raw_string_ostream as(a); std::string b; llvm::raw_string_ostream bs(b); e.getValueDecl()->print(as); oe.getValueDecl()->print(bs); if (a == b) llvm::errs() << "ValueDecls differ but print same\n"; else { writeErr(std::string( "ValueDecls differ at ") + std::to_string(i)); assert(false && "other lookup found different Decl"); } } if (e.getDeclContext() != oe.getDeclContext()) { writeErr((std::string("Contexts differ at ")) + std::to_string(i)); assert(false && "ASTScopeImpl found different context"); } // unsigned printContext(llvm::raw_ostream &OS, unsigned indent = 0, // bool onlyAPartialLine = false) const; } if (IndexOfFirstOuterResult != other.IndexOfFirstOuterResult) { writeErr( std::string("IndexOfFirstOuterResult differs, should be: ") + std::to_string(IndexOfFirstOuterResult) + std::string( ", is: ") + std::to_string(other.IndexOfFirstOuterResult)); assert(false && "other lookup IndexOfFirstOuterResult differs"); } if (recordedSF != other.recordedSF) { writeErr(std::string("recordedSF differs: shouldBe: ") + (recordedSF ? recordedSF->getFilename().str() : std::string("")) + std::string(" is: ") + (other.recordedSF ? other.recordedSF->getFilename().str() : std::string(""))); assert(false && "other lookup recordedSF differs"); } if (recordedSF && recordedIsCascadingUse != other.recordedIsCascadingUse) { writeErr(std::string("recordedIsCascadingUse differs: shouldBe: ") + std::to_string(recordedIsCascadingUse) + std::string(" is: ") + std::to_string(other.recordedIsCascadingUse)); assert(false && "other lookup recordedIsCascadingUse differs"); } return true; } bool UnqualifiedLookupFactory::shouldDiffer() const { auto *SF = dyn_cast(DC->getModuleScopeContext()); if (!SF) return false; static std::vector testsThatShouldDiffer { "swift/test/Constraints/diagnostics.swift", "swift/test/Constraints/enum_cases.swift", "swift/test/Constraints/rdar39401774.swift", "swift/test/Constraints/rdar39401774-astscope.swift", "swift/test/Interpreter/repl.swift", "swift/test/Sema/diag_defer_captures.swift", "swift/test/Sema/diag_use_before_declaration.swift", "swift/test/SourceKit/CodeFormat/indent-closure.swift", "swift/test/TypeCoercion/overload_noncall.swift", "swift/test/expr/capture/nested_class.swift", "swift/test/expr/capture/order.swift", "swift/test/NameLookup/name_lookup2.swift" }; StringRef fileName = SF->getFilename(); return llvm::any_of(testsThatShouldDiffer, [&](const char *testFile) { return fileName.endswith(testFile); }); } #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