Files
swift-mirror/lib/AST/UnqualifiedLookup.cpp
Rintaro Ishizaki ff2ccd485c [CodeCompletion] Workaround fast-completion issue in UnqualifiedLookup
In fast-completion, a function body can be replaced with another function
body parsed from a new buffer. In such cases, during typechecking the
expressions in the *new* function body, a source location range check in
UnqualifiedLookup didn't work well because they are not from the same
buffer.

This patch workaround it by skipping the source range checks and returns
'success' in such cases.

rdar://problem/58881999
2020-02-12 10:41:43 -08:00

1505 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();
}
llvm::Expected<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/NameBinding/name-binding.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