mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The directory currently seems to have a mix of tests for import resolution and name lookup. Therefore split it into two directories; ImportResolution and NameLookup.
1506 lines
56 KiB
C++
1506 lines
56 KiB
C++
//===--- 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 <typename D> bool shouldLookupMembers(D *decl, SourceLoc loc) {
|
|
// Only look at members of this type (or its inherited types) when
|
|
// inside the body or a protocol's top-level 'where' clause. (Why the
|
|
// 'where' clause? Because that's where you put constraints on
|
|
// inherited associated types.)
|
|
|
|
// When we have no source-location information, we have to perform member
|
|
// lookup.
|
|
if (loc.isInvalid() || decl->getBraces().isInvalid())
|
|
return true;
|
|
|
|
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<LookupResultEntry, 4>;
|
|
|
|
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<NominalTypeDecl *, 2>;
|
|
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<TypeDecl>(member) ? staticContext : dynamicContext;
|
|
}
|
|
|
|
public:
|
|
/// Do the lookups and add matches to results.
|
|
void findResults(const DeclNameRef &Name, bool isCascadingUse,
|
|
NLOptions baseNLOptions, DeclContext *contextForLookup,
|
|
SmallVectorImpl<LookupResultEntry> &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<LookupResultEntry> &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<LookupResultEntry> &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<LookupResultEntry> &Results,
|
|
size_t &IndexOfFirstOuterResult);
|
|
// clang-format on
|
|
|
|
void performUnqualifiedLookup();
|
|
|
|
private:
|
|
struct ContextAndUnresolvedIsCascadingUse {
|
|
DeclContext *whereToLook;
|
|
Optional<bool> 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 <typename Fn> void ifNotDoneYet(Fn fn) {
|
|
recordCompletionOfAScope();
|
|
if (!isFirstResultEnough())
|
|
fn();
|
|
}
|
|
|
|
template <typename Fn1, typename Fn2> 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> &&resultFinderForTypeContext,
|
|
Optional<bool> isCascadingUse,
|
|
DeclContext *capturedSelfContext);
|
|
|
|
void lookupInModuleScopeContext(DeclContext *, Optional<bool> isCascadingUse);
|
|
|
|
void lookupNamesIntroducedByPatternBindingInitializer(
|
|
PatternBindingInitializer *PBI,
|
|
Optional<bool> isCascadingUse,
|
|
DeclContext *capturedSelfContext);
|
|
|
|
void
|
|
lookupNamesIntroducedByLazyVariableInitializer(PatternBindingInitializer *PBI,
|
|
ParamDecl *selfParam,
|
|
Optional<bool> isCascadingUse,
|
|
DeclContext *capturedSelfContext);
|
|
|
|
void lookupNamesIntroducedByInitializerOfStoredPropertyOfAType(
|
|
PatternBindingInitializer *PBI, Optional<bool> isCascadingUse);
|
|
|
|
/// An initializer of a global name, or a function-likelocal name.
|
|
void lookupNamesIntroducedByInitializerOfGlobalOrLocal(
|
|
PatternBindingInitializer *PBI,
|
|
Optional<bool> isCascadingUse,
|
|
DeclContext *capturedSelfContext);
|
|
|
|
void lookupNamesIntroducedByFunctionDecl(AbstractFunctionDecl *AFD,
|
|
Optional<bool> isCascadingUse,
|
|
DeclContext *capturedSelfContext);
|
|
|
|
void lookupNamesIntroducedByMemberFunction(AbstractFunctionDecl *AFD,
|
|
bool isCascadingUse,
|
|
DeclContext *capturedSelfContext);
|
|
|
|
void lookupNamesIntroducedByPureFunction(AbstractFunctionDecl *AFD,
|
|
bool isCascadingUse,
|
|
DeclContext *capturedSelfContext);
|
|
|
|
void lookupNamesIntroducedByClosure(AbstractClosureExpr *ACE,
|
|
Optional<bool> isCascadingUse,
|
|
DeclContext *capturedSelfContext);
|
|
|
|
template <typename NominalTypeDeclOrExtensionDecl>
|
|
void lookupNamesIntroducedByNominalTypeOrExtension(
|
|
NominalTypeDeclOrExtensionDecl *D, Optional<bool> isCascadingUse);
|
|
|
|
void lookupNamesIntroducedByDefaultArgumentInitializer(
|
|
DefaultArgumentInitializer *I,
|
|
Optional<bool> isCascadingUse,
|
|
DeclContext *capturedSelfContext);
|
|
|
|
void lookupNamesIntroducedByMiscContext(DeclContext *dc,
|
|
Optional<bool> isCascadingUse,
|
|
DeclContext *capturedSelfContext);
|
|
|
|
void lookForLocalVariablesIn(AbstractFunctionDecl *AFD,
|
|
Optional<bool> 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<bool> getInitialIsCascadingUse() const {
|
|
return options.contains(Flags::KnownPrivate) ? Optional<bool>(false)
|
|
: None;
|
|
}
|
|
|
|
static bool resolveIsCascadingUse(const DeclContext *const dc,
|
|
Optional<bool> 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<ValueDecl *> values, DeclVisibilityKind vis,
|
|
NullablePtr<DeclContext> baseDC = nullptr) override;
|
|
|
|
/// returns true if finished and new value for isCascadingUse
|
|
bool lookInMembers(NullablePtr<DeclContext> selfDC,
|
|
DeclContext *const scopeDC, NominalTypeDecl *const nominal,
|
|
function_ref<bool(Optional<bool>)>) 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)),
|
|
#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<bool> 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<PatternBindingInitializer>(dc))
|
|
lookupNamesIntroducedByPatternBindingInitializer(PBI, isCascadingUseSoFar,
|
|
capturedSelfContext);
|
|
else if (auto *AFD = dyn_cast<AbstractFunctionDecl>(dc))
|
|
lookupNamesIntroducedByFunctionDecl(AFD, isCascadingUseSoFar,
|
|
capturedSelfContext);
|
|
else if (auto *ACE = dyn_cast<AbstractClosureExpr>(dc))
|
|
lookupNamesIntroducedByClosure(ACE, isCascadingUseSoFar,
|
|
capturedSelfContext);
|
|
else if (auto *ED = dyn_cast<ExtensionDecl>(dc)) {
|
|
assert(capturedSelfContext == NULL && "When we recurse into type context,"
|
|
" 'self' should be forgotten.");
|
|
lookupNamesIntroducedByNominalTypeOrExtension(ED, isCascadingUseSoFar);
|
|
}
|
|
else if (auto *ND = dyn_cast<NominalTypeDecl>(dc)) {
|
|
assert(capturedSelfContext == NULL && "When we recurse into type context,"
|
|
" 'self' should be forgotten.");
|
|
lookupNamesIntroducedByNominalTypeOrExtension(ND, isCascadingUseSoFar);
|
|
}
|
|
else if (auto I = dyn_cast<DefaultArgumentInitializer>(dc))
|
|
lookupNamesIntroducedByDefaultArgumentInitializer(I, isCascadingUseSoFar,
|
|
capturedSelfContext);
|
|
else
|
|
lookupNamesIntroducedByMiscContext(dc, isCascadingUseSoFar,
|
|
capturedSelfContext);
|
|
}
|
|
|
|
void UnqualifiedLookupFactory::lookupInModuleScopeContext(
|
|
DeclContext *dc, Optional<bool> isCascadingUse) {
|
|
if (auto SF = dyn_cast<SourceFile>(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<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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<bool> isCascadingUse,
|
|
DeclContext *capturedSelfContext) {
|
|
if (auto *CE = dyn_cast<ClosureExpr>(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 <typename NominalTypeDeclOrExtensionDecl>
|
|
void UnqualifiedLookupFactory::lookupNamesIntroducedByNominalTypeOrExtension(
|
|
NominalTypeDeclOrExtensionDecl *D, Optional<bool> isCascadingUse) {
|
|
// clang-format off
|
|
finishLookingInContext(
|
|
AddGenericParameters::Yes,
|
|
D,
|
|
shouldLookupMembers(D, Loc)
|
|
? Optional<ResultFinderForTypeContext>(
|
|
ResultFinderForTypeContext(this, D, D))
|
|
: None,
|
|
resolveIsCascadingUse(D, isCascadingUse,
|
|
/*onlyCareAboutFunctionBody=*/false),
|
|
/*capturedSelfContext=*/NULL);
|
|
|
|
// clang-format on
|
|
}
|
|
|
|
void UnqualifiedLookupFactory::
|
|
lookupNamesIntroducedByDefaultArgumentInitializer(
|
|
DefaultArgumentInitializer *I, Optional<bool> 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<bool> isCascadingUse,
|
|
DeclContext *capturedSelfContext) {
|
|
// clang-format off
|
|
assert(isa<TopLevelCodeDecl>(dc) ||
|
|
isa<Initializer>(dc) ||
|
|
isa<TypeAliasDecl>(dc) ||
|
|
isa<SubscriptDecl>(dc) ||
|
|
isa<EnumElementDecl>(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> &&resultFinderForTypeContext,
|
|
const Optional<bool> 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<bool> isCascadingUse) {
|
|
// Look for local variables; normally, the parser resolves these
|
|
// for us, but it can't do the right thing inside local types.
|
|
// FIXME: when we can parse and typecheck the function body partially
|
|
// for code completion, AFD->getBody() check can be removed.
|
|
|
|
if (Loc.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<NominalTypeDecl>(dc))
|
|
return nominal->getGenericParams();
|
|
if (auto ext = dyn_cast<ExtensionDecl>(dc))
|
|
return ext->getGenericParams();
|
|
if (auto subscript = dyn_cast<SubscriptDecl>(dc))
|
|
return subscript->getGenericParams();
|
|
if (auto func = dyn_cast<AbstractFunctionDecl>(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<LookupResultEntry> &results) const {
|
|
// An optimization:
|
|
if (selfBounds.empty())
|
|
return;
|
|
const NLOptions options =
|
|
baseNLOptions | (isCascadingUse ? NL_KnownCascadingDependency
|
|
: NL_KnownNonCascadingDependency);
|
|
|
|
SmallVector<ValueDecl *, 4> 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<SourceFile>(topLevelContext);
|
|
recordedIsCascadingUse = isCascadingUse;
|
|
}
|
|
|
|
void UnqualifiedLookupFactory::addImportedResults(DeclContext *const dc) {
|
|
using namespace namelookup;
|
|
SmallVector<ValueDecl *, 8> 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<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
|
|
|
|
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<bool> isCascadingUseAtStartOfLookup =
|
|
!Name.isOperator()
|
|
? getInitialIsCascadingUse()
|
|
: resolveIsCascadingUse(DC, getInitialIsCascadingUse(),
|
|
/*onlyCareAboutFunctionBody*/ true);
|
|
|
|
const Optional<bool> isCascadingUseAfterLookup =
|
|
ASTScope::computeIsCascadingUse(history, isCascadingUseAtStartOfLookup);
|
|
|
|
recordDependencyOnTopLevelName(moduleScopeContext, Name,
|
|
isCascadingUseAfterLookup.getValueOr(true));
|
|
lookUpTopLevelNamesInModuleScopeContext(moduleScopeContext);
|
|
});
|
|
}
|
|
|
|
bool ASTScopeDeclConsumerForUnqualifiedLookup::consume(
|
|
ArrayRef<ValueDecl *> values, DeclVisibilityKind vis,
|
|
NullablePtr<DeclContext> baseDC) {
|
|
for (auto *value: values) {
|
|
if (factory.isOriginallyTypeLookup && !isa<TypeDecl>(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<SourceFile>(value->getDeclContext()) &&
|
|
(vis != DeclVisibilityKind::LocalVariable || isa<VarDecl>(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<ValueDecl *> valuesArg,
|
|
DeclVisibilityKind,
|
|
NullablePtr<DeclContext>) {
|
|
for (auto *v: valuesArg)
|
|
values.push_back(v);
|
|
return false;
|
|
}
|
|
|
|
// TODO: in future, migrate this functionality into ASTScopes
|
|
bool ASTScopeDeclConsumerForUnqualifiedLookup::lookInMembers(
|
|
NullablePtr<DeclContext> selfDC, DeclContext *const scopeDC,
|
|
NominalTypeDecl *const nominal,
|
|
function_ref<bool(Optional<bool>)> calculateIsCascadingUse) {
|
|
if (selfDC) {
|
|
if (auto *d = selfDC.get()->getAsDecl()) {
|
|
if (auto *afd = dyn_cast<AbstractFunctionDecl>(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<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
|
|
#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("<no name>")) +
|
|
std::string(" is: ") +
|
|
(other.recordedSF ? other.recordedSF->getFilename().str()
|
|
: std::string("<no name>")));
|
|
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<SourceFile>(DC->getModuleScopeContext());
|
|
if (!SF)
|
|
return false;
|
|
|
|
static std::vector<const char*> 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
|