mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
1934 lines
71 KiB
C++
1934 lines
71 KiB
C++
//===--- ASTScopeCreation.cpp - Swift Object-Oriented AST Scope -----------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 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 the creation methods of the ASTScopeImpl ontology.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
#include "swift/AST/ASTScope.h"
|
|
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/ASTWalker.h"
|
|
#include "swift/AST/Attr.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/Expr.h"
|
|
#include "swift/AST/Initializer.h"
|
|
#include "swift/AST/LazyResolver.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/Pattern.h"
|
|
#include "swift/AST/Stmt.h"
|
|
#include "swift/AST/TypeRepr.h"
|
|
#include "swift/Basic/STLExtras.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include <algorithm>
|
|
#include <unordered_set>
|
|
|
|
using namespace swift;
|
|
using namespace ast_scope;
|
|
|
|
#pragma mark source range utilities
|
|
static bool rangeableIsIgnored(const Decl *d) { return d->isImplicit(); }
|
|
static bool rangeableIsIgnored(const Expr *d) {
|
|
return false; // implicit expr may contain closures
|
|
}
|
|
static bool rangeableIsIgnored(const Stmt *d) {
|
|
return false; // ??
|
|
}
|
|
static bool rangeableIsIgnored(const SpecializeAttr *a) {
|
|
return a->isImplicit();
|
|
}
|
|
static bool rangeableIsIgnored(const ASTNode n) {
|
|
return n.is<Decl *>() && n.get<Decl *>()->isImplicit();
|
|
}
|
|
|
|
template <typename Rangeable>
|
|
static SourceRange getRangeableSourceRange(const Rangeable *const p) {
|
|
return p->getSourceRange();
|
|
}
|
|
static SourceRange getRangeableSourceRange(const SpecializeAttr *a) {
|
|
return a->getRange();
|
|
}
|
|
static SourceRange getRangeableSourceRange(const ASTNode n) {
|
|
return n.getSourceRange();
|
|
}
|
|
|
|
template <typename Rangeable>
|
|
static bool isLocalizable(const Rangeable astElement) {
|
|
return !rangeableIsIgnored(astElement) &&
|
|
getRangeableSourceRange(astElement).isValid();
|
|
}
|
|
|
|
template <typename Rangeable>
|
|
static void dumpRangeable(const Rangeable r, llvm::raw_ostream &f) {
|
|
r.dump(f);
|
|
}
|
|
template <typename Rangeable>
|
|
static void dumpRangeable(const Rangeable *r, llvm::raw_ostream &f) {
|
|
r->dump(f);
|
|
}
|
|
template <typename Rangeable>
|
|
static void dumpRangeable(Rangeable *r, llvm::raw_ostream &f) {
|
|
r->dump(f);
|
|
}
|
|
|
|
static void dumpRangeable(const SpecializeAttr r, llvm::raw_ostream &f) {
|
|
llvm::errs() << "SpecializeAttr\n";
|
|
}
|
|
static void dumpRangeable(const SpecializeAttr *r, llvm::raw_ostream &f) {
|
|
llvm::errs() << "SpecializeAttr\n";
|
|
}
|
|
static void dumpRangeable(SpecializeAttr *r, llvm::raw_ostream &f) {
|
|
llvm::errs() << "SpecializeAttr\n";
|
|
}
|
|
|
|
static std::vector<ASTNode> asNodeVector(DeclRange dr) {
|
|
std::vector<ASTNode> nodes;
|
|
llvm::transform(dr, std::back_inserter(nodes),
|
|
[&](Decl *d) { return ASTNode(d); });
|
|
return nodes;
|
|
}
|
|
|
|
namespace swift {
|
|
namespace ast_scope {
|
|
|
|
namespace {
|
|
/// Use me with any ASTNode, Expr*, Decl*, or Stmt*
|
|
/// I will yield a void* that is the same, even when given an Expr* and a
|
|
/// ClosureExor* because I take the Expr*, figure its real class, then up
|
|
/// cast.
|
|
class PtrCalc : public ASTVisitor<PtrCalc, void *, void *, void *, void *,
|
|
void *, void *> {
|
|
public:
|
|
template <typename T> const void *visit(const T *x) {
|
|
return const_cast<T *>(x);
|
|
}
|
|
|
|
// Call these only from the superclass
|
|
void *visitDecl(Decl *e) { return e; }
|
|
void *visitStmt(Stmt *e) { return e; }
|
|
void *visitExpr(Expr *e) { return e; }
|
|
void *visitPattern(Pattern *e) { return e; }
|
|
void *visitDeclAttribute(DeclAttribute *e) { return e; }
|
|
|
|
// Provide default implementations for statements as ASTVisitor does for Exprs
|
|
#define STMT(CLASS, PARENT) \
|
|
void *visit##CLASS##Stmt(CLASS##Stmt *S) { return visitStmt(S); }
|
|
#include "swift/AST/StmtNodes.def"
|
|
|
|
// Provide default implementations for patterns as ASTVisitor does for Exprs
|
|
#define PATTERN(CLASS, PARENT) \
|
|
void *visit##CLASS##Pattern(CLASS##Pattern *S) { return visitPattern(S); }
|
|
#include "swift/AST/PatternNodes.def"
|
|
};
|
|
|
|
/// A set that does the right pointer calculation for comparing Decls to
|
|
/// DeclContexts, and Exprs
|
|
class NodeSet {
|
|
::llvm::DenseSet<const void *> pointers;
|
|
|
|
public:
|
|
bool contains(const ASTScopeImpl *const s) {
|
|
if (auto *r = s->getReferrent().getPtrOrNull())
|
|
return pointers.count(r);
|
|
return false; // never exclude a non-checkable scope
|
|
}
|
|
bool insert(const ASTScopeImpl *const s) {
|
|
if (auto *r = s->getReferrent().getPtrOrNull())
|
|
return pointers.insert(r).second;
|
|
return true;
|
|
}
|
|
void erase(const ASTScopeImpl *const s) {
|
|
if (auto *r = s->getReferrent().getPtrOrNull())
|
|
pointers.erase(r);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
#pragma mark ScopeCreator
|
|
|
|
class ScopeCreator final {
|
|
friend class ASTSourceFileScope;
|
|
/// For allocating scopes.
|
|
ASTContext &ctx;
|
|
|
|
public:
|
|
ASTSourceFileScope *const sourceFileScope;
|
|
ASTContext &getASTContext() const { return ctx; }
|
|
|
|
private:
|
|
/// The AST can have duplicate nodes, and we don't want to create scopes for
|
|
/// those.
|
|
/// TODO: better to use a shared pointer? Unique pointer?
|
|
Optional<NodeSet> _scopedNodes;
|
|
|
|
public:
|
|
NodeSet &scopedNodes;
|
|
|
|
public:
|
|
ScopeCreator(SourceFile *SF)
|
|
: ctx(SF->getASTContext()),
|
|
sourceFileScope(new (ctx) ASTSourceFileScope(SF, this)),
|
|
_scopedNodes(NodeSet()), scopedNodes(_scopedNodes.getValue()) {}
|
|
|
|
public:
|
|
ScopeCreator(const ScopeCreator &) = delete; // ensure no copies
|
|
ScopeCreator(const ScopeCreator &&) = delete; // ensure no moves
|
|
|
|
public:
|
|
/// Given an array of ASTNodes or Decl pointers, add them
|
|
/// Return the resultant insertionPoint
|
|
ASTScopeImpl *addNodesToTree(ASTScopeImpl *const insertionPoint,
|
|
ArrayRef<ASTNode> nodesOrDeclsToAdd) {
|
|
|
|
auto *ip = insertionPoint;
|
|
for (auto nd :
|
|
expandClausesSortAndCullElementsOrMembers(nodesOrDeclsToAdd)) {
|
|
if (shouldThisNodeBeScopedWhenEncountered(nd))
|
|
ip = createScopeFor(nd, ip).getPtrOr(ip);
|
|
else
|
|
ip->widenSourceRangeForIgnoredASTNode(nd);
|
|
}
|
|
return ip;
|
|
}
|
|
|
|
public:
|
|
/// Return new insertion point if the scope was not a duplicate
|
|
NullablePtr<ASTScopeImpl> createScopeFor(ASTNode, ASTScopeImpl *parent);
|
|
|
|
bool shouldCreateScope(ASTNode n) const {
|
|
if (!n)
|
|
return false;
|
|
if (n.is<Expr *>())
|
|
return true;
|
|
// Cannot ignore implicit statements because implict return can contain
|
|
// scopes in the expression, such as closures.
|
|
// But must ignore other implicit statements, e.g. brace statments
|
|
// if they can have no children and no stmt source range.
|
|
// Deal with it in visitBraceStmt
|
|
if (n.is<Stmt *>())
|
|
return true;
|
|
|
|
auto *const d = n.get<Decl *>();
|
|
// Implicit nodes may not have source information for name lookup.
|
|
if (!isLocalizable(d))
|
|
return false;
|
|
/// In \c Parser::parseDeclVarGetSet fake PBDs are created. Ignore them.
|
|
/// Example:
|
|
/// \code
|
|
/// class SR10903 { static var _: Int { 0 } }
|
|
/// \endcode
|
|
|
|
// Commented out for
|
|
// validation-test/compiler_crashers_fixed/27962-swift-rebindselfinconstructorexpr-getcalledconstructor.swift
|
|
// In that test the invalid PBD -> var decl which contains the desired
|
|
// closure scope
|
|
// if (const auto *PBD = dyn_cast<PatternBindingDecl>(d))
|
|
// if (!isLocalizable(PBD))
|
|
// return false;
|
|
/// In
|
|
/// \code
|
|
/// @propertyWrapper
|
|
/// public struct Wrapper<T> {
|
|
/// public var value: T
|
|
///
|
|
/// public init(body: () -> T) {
|
|
/// self.value = body()
|
|
/// }
|
|
/// }
|
|
///
|
|
/// let globalInt = 17
|
|
///
|
|
/// @Wrapper(body: { globalInt })
|
|
/// public var y: Int
|
|
/// \endcode
|
|
/// I'm seeing a dumped AST include:
|
|
/// (pattern_binding_decl range=[test.swift:13:8 - line:12:29]
|
|
const auto &SM = d->getASTContext().SourceMgr;
|
|
|
|
// Once we allow invalid PatternBindingDecls (see shouldCreateScope),
|
|
// then IDE/complete_property_delegate_attribute.swift fails because
|
|
// we try to expand a member whose source range is backwards.
|
|
|
|
assert((d->getStartLoc().isInvalid() ||
|
|
!SM.isBeforeInBuffer(d->getEndLoc(), d->getStartLoc())) &&
|
|
"end-before-start will break tree search via location");
|
|
return true;
|
|
}
|
|
|
|
/// Create a new scope of class ChildScope initialized with a ChildElement,
|
|
/// expandScope it,
|
|
/// add it as a child of the receiver, and return the child and the scope to
|
|
/// receive more decls.
|
|
template <typename Scope, typename... Args>
|
|
ASTScopeImpl *createSubtree(ASTScopeImpl *parent, Args... args) {
|
|
Scope dryRun(args...);
|
|
assert(!dryRun.getReferrent() &&
|
|
"Not checking for dup but class supports it");
|
|
return createSubtreeImpl<Scope>(parent, args...);
|
|
}
|
|
|
|
template <typename Scope, typename... Args>
|
|
NullablePtr<ASTScopeImpl> createSubtreeIfUnique(ASTScopeImpl *parent,
|
|
Args... args) {
|
|
Scope dryRun(args...);
|
|
assert(dryRun.getReferrent() &&
|
|
"Checking for dup but class does not support it");
|
|
if (scopedNodes.insert(&dryRun))
|
|
return createSubtreeImpl<Scope>(parent, args...);
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename Scope, typename... Args>
|
|
ASTScopeImpl *createSubtreeMustBeUnique(ASTScopeImpl *parent, Args... args) {
|
|
if (auto s = createSubtreeIfUnique<Scope>(parent, args...))
|
|
return s.get();
|
|
llvm_unreachable("Scope should have been unique");
|
|
}
|
|
|
|
private:
|
|
template <typename Scope, typename... Args>
|
|
ASTScopeImpl *createSubtreeImpl(ASTScopeImpl *parent, Args... args) {
|
|
auto *child = new (ctx) Scope(args...);
|
|
parent->addChild(child, ctx);
|
|
ASTScopeImpl *insertionPoint = child->expandAndBeCurrent(*this);
|
|
assert(child->verifyThatThisNodeComeAfterItsPriorSibling() &&
|
|
"Ensure search will work");
|
|
return insertionPoint;
|
|
}
|
|
|
|
public:
|
|
template <typename Scope, typename PortionClass, typename... Args>
|
|
ASTScopeImpl *createSubtree2D(ASTScopeImpl *parent, Args... args) {
|
|
const Portion *portion = new (ctx) PortionClass();
|
|
return createSubtree<Scope>(parent, portion, args...);
|
|
}
|
|
|
|
template <typename Scope, typename PortionClass, typename... Args>
|
|
NullablePtr<ASTScopeImpl> createSubtree2DIfUnique(ASTScopeImpl *parent,
|
|
Args... args) {
|
|
const Portion *portion = new (ctx) PortionClass();
|
|
return createSubtreeIfUnique<Scope>(parent, portion, args...);
|
|
}
|
|
|
|
void addChildrenForCapturesAndClosuresIn(Expr *expr, ASTScopeImpl *parent) {
|
|
// Use the ASTWalker to find buried captures and closures
|
|
forEachClosureIn(expr, [&](NullablePtr<CaptureListExpr> captureList,
|
|
ClosureExpr *closureExpr) {
|
|
createSubtreeIfUnique<WholeClosureScope>(parent, closureExpr,
|
|
captureList);
|
|
});
|
|
}
|
|
|
|
private:
|
|
/// Find all of the (non-nested) closures (and associated capture lists)
|
|
/// referenced within this expression.
|
|
void forEachClosureIn(
|
|
Expr *expr,
|
|
function_ref<void(NullablePtr<CaptureListExpr>, ClosureExpr *)>
|
|
foundClosure);
|
|
|
|
// A safe way to discover this, without creating a circular request.
|
|
// Cannot call getAttachedPropertyWrappers.
|
|
static bool hasAttachedPropertyWrapper(VarDecl *vd) {
|
|
return AttachedPropertyWrapperScope::getSourceRangeFor(vd).isValid();
|
|
}
|
|
|
|
public:
|
|
/// If the pattern has an attached property wrapper, create a scope for it
|
|
/// so it can be looked up.
|
|
|
|
void createAttachedPropertyWrapperScope(PatternBindingDecl *patternBinding,
|
|
ASTScopeImpl *parent) {
|
|
patternBinding->getPattern(0)->forEachVariable([&](VarDecl *vd) {
|
|
if (hasAttachedPropertyWrapper(vd))
|
|
createSubtree<AttachedPropertyWrapperScope>(parent, vd);
|
|
});
|
|
}
|
|
|
|
public:
|
|
/// Create the matryoshka nested generic param scopes (if any)
|
|
/// that are subscopes of the receiver. Return
|
|
/// the furthest descendant.
|
|
/// Last GenericParamsScope includes the where clause
|
|
ASTScopeImpl *createGenericParamScopes(Decl *parameterizedDecl,
|
|
GenericParamList *generics,
|
|
ASTScopeImpl *parent) {
|
|
if (!generics)
|
|
return parent;
|
|
auto *s = parent;
|
|
for (unsigned i : indices(generics->getParams()))
|
|
s = createSubtreeIfUnique<GenericParamScope>(s, parameterizedDecl,
|
|
generics, i)
|
|
.getPtrOr(s);
|
|
return s;
|
|
}
|
|
|
|
void
|
|
addChildrenForAllLocalizableAccessorsInSourceOrder(AbstractStorageDecl *asd,
|
|
ASTScopeImpl *parent);
|
|
|
|
void
|
|
forEachSpecializeAttrInSourceOrder(Decl *declBeingSpecialized,
|
|
function_ref<void(SpecializeAttr *)> fn) {
|
|
std::vector<SpecializeAttr *> sortedSpecializeAttrs;
|
|
for (auto *attr : declBeingSpecialized->getAttrs()) {
|
|
if (auto *specializeAttr = dyn_cast<SpecializeAttr>(attr))
|
|
sortedSpecializeAttrs.push_back(specializeAttr);
|
|
}
|
|
// Part of rdar://53921774 rm extra copy
|
|
for (auto *specializeAttr : sortBySourceRange(sortedSpecializeAttrs))
|
|
fn(specializeAttr);
|
|
}
|
|
|
|
std::vector<ASTNode>
|
|
expandClausesSortAndCullElementsOrMembers(ArrayRef<ASTNode> input) const {
|
|
auto cleanedupNodes = sortBySourceRange(cull(expandClauses(input)));
|
|
// TODO: uncomment when working on rdar://53627317
|
|
// findCollidingPatterns(cleanedupNodes);
|
|
return cleanedupNodes;
|
|
}
|
|
|
|
public:
|
|
/// When ASTScopes are enabled for code completion, rdar://53321156
|
|
/// IfConfigs will pose a challenge because we may need to field lookups into
|
|
/// the inactive clauses, but the AST contains redundancy: the active clause's
|
|
/// elements are present in the members or elements of an IterableTypeDecl or
|
|
/// BraceStmt alongside of the IfConfigDecl. In addition there are two more
|
|
/// complications:
|
|
///
|
|
/// 1. The active clause's elements may be nested inside an init self
|
|
/// rebinding decl (as in StringObject.self).
|
|
///
|
|
/// 2. The active clause may be before or after the inactive ones
|
|
///
|
|
/// So, when encountering an IfConfigDecl, we will expand the inactive
|
|
/// elements. Also, always sort members or elements so that the child scopes
|
|
/// are in source order (Just one of several reasons we need to sort.)
|
|
///
|
|
static const bool includeInactiveClausesOfIfConfigDecls = false;
|
|
|
|
private:
|
|
static std::vector<ASTNode> expandClauses(ArrayRef<ASTNode> input) {
|
|
std::vector<ASTNode> expansion;
|
|
expandClausesInto(expansion, input, /*isInAnActiveNode=*/true);
|
|
return expansion;
|
|
}
|
|
|
|
static void expandClausesInto(std::vector<ASTNode> &expansion,
|
|
ArrayRef<ASTNode> input,
|
|
const bool isInAnActiveNode) {
|
|
for (auto n : input) {
|
|
if (!n.isDecl(DeclKind::IfConfig)) {
|
|
expansion.push_back(n);
|
|
continue;
|
|
}
|
|
auto *const icd = cast<IfConfigDecl>(n.get<Decl *>());
|
|
for (auto &clause : icd->getClauses()) {
|
|
if (auto *const cond = clause.Cond)
|
|
expansion.push_back(cond);
|
|
if (clause.isActive) {
|
|
// rdar://53922172
|
|
assert(isInAnActiveNode && "Clause should not be marked "
|
|
"active unless it's context is "
|
|
"active");
|
|
// get inactive nodes that nest in active clauses
|
|
for (auto n : clause.Elements)
|
|
if (auto *const d = n.dyn_cast<Decl *>())
|
|
if (auto *const icd = dyn_cast<IfConfigDecl>(d))
|
|
expandClausesInto(expansion, {d}, true);
|
|
} else if (includeInactiveClausesOfIfConfigDecls) {
|
|
expandClausesInto(expansion, clause.Elements,
|
|
/*isInAnActiveNode=*/false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Remove VarDecls because we'll find them when we expand the
|
|
/// PatternBindingDecls. Remove AccessorDecls because they overlap & we'll
|
|
/// find them when we expand the subscripts and var decls. Remove EnunCases
|
|
/// because the overlap EnumElements and AST includes the elements in the
|
|
/// members.
|
|
std::vector<ASTNode> cull(ArrayRef<ASTNode> input) const {
|
|
// TODO don't need to cull accessors anymore? - use new walker below, too?
|
|
// Cull ImportDecls because when running from lldb, we can see an ImportDecl
|
|
// within a large FuncDecl (manuafactured by lldb) and that would trip the
|
|
// "contradicts" assertion in the sort.
|
|
// TODO: move following into radar, maybe
|
|
/// For instance: \code
|
|
/// SOURCE_DIR/lldb/test/dotest.py -q --arch= -s
|
|
/// SOURCE_DIR/build/Ninja-DebugAssert/lldb-macosx-x86_64/lldb-test-traces
|
|
/// --build-dir
|
|
/// SOURCE_DIR/build/Ninja-DebugAssert/lldb-macosx-x86_64/lldb-test-build.noindex
|
|
/// -S nm -u CXXFLAGS -u CFLAGS --executable
|
|
/// SOURCE_DIR/build/Ninja-DebugAssert/lldb-macosx-x86_64/./bin/lldb
|
|
/// --dsymutil
|
|
/// SOURCE_DIR/build/Ninja-DebugAssert/llvm-macosx-x86_64/./bin/dsymutil
|
|
/// --filecheck
|
|
/// SOURCE_DIR/build/Ninja-DebugAssert/llvm-macosx-x86_64/./bin/FileCheck -C
|
|
/// SOURCE_DIR/build/Ninja-DebugAssert/llvm-macosx-x86_64/bin/clang
|
|
/// --swift-compiler
|
|
/// SOURCE_DIR/build/Ninja-DebugAssert/swift-macosx-x86_64/bin/swiftc
|
|
/// --swift-library
|
|
/// SOURCE_DIR/build/Ninja-DebugAssert/swift-macosx-x86_64/lib/swift --env
|
|
/// DYLD_LIBRARY_PATH="SOURCE_DIR/build/Ninja-DebugAssert/swift-macosx-x86_64/lib/swift"
|
|
/// LD_LIBRARY_PATH="SOURCE_DIR/build/Ninja-DebugAssert/swift-macosx-x86_64/lib/swift"
|
|
/// SIMCTL_CHILD_DYLD_LIBRARY_PATH="SOURCE_DIR/build/Ninja-DebugAssert/swift-macosx-x86_64/lib/swift"
|
|
/// --codesign-identity lldb_codesign --framework
|
|
/// SOURCE_DIR/build/Ninja-DebugAssert/lldb-macosx-x86_64/bin/LLDB.framework
|
|
/// --server
|
|
/// SOURCE_DIR/build/Ninja-DebugAssert/lldb-macosx-x86_64/./bin/debugserver
|
|
/// --out-of-tree-debugserver --build-dir
|
|
/// SOURCE_DIR/build/Ninja-DebugAssert/lldb-macosx-x86_64/lldb-test-build.noindex
|
|
/// --test-subdir lang/swift --skip-category=watchpoint --skip-category=dwo
|
|
/// -t -E
|
|
/// SOURCE_DIR/lldb/packages/Python/lldbsuite/test/lang/swift/parseable_interfaces/shared
|
|
/// -p TestSwiftInterfaceNoDebugInfo.py
|
|
/// \endcode
|
|
std::vector<ASTNode> culled;
|
|
llvm::copy_if(input, std::back_inserter(culled), [&](ASTNode n) {
|
|
return isLocalizable(n) && !n.isDecl(DeclKind::Var) &&
|
|
!n.isDecl(DeclKind::Accessor) && !n.isDecl(DeclKind::EnumCase) &&
|
|
!n.isDecl(DeclKind::Import);
|
|
});
|
|
return culled;
|
|
}
|
|
|
|
/// TODO: The parser yields two decls at the same source loc with the same
|
|
/// kind. Call me when tackling rdar://53627317, then move this to
|
|
/// ASTVerifier.
|
|
///
|
|
/// In all cases the first pattern seems to carry the initializer, and the
|
|
/// second, the accessor
|
|
void findCollidingPatterns(ArrayRef<ASTNode> input) const {
|
|
auto dumpPBD = [&](PatternBindingDecl *pbd, const char *which) {
|
|
llvm::errs() << "*** " << which
|
|
<< " pbd isImplicit: " << pbd->isImplicit()
|
|
<< ", #entries: " << pbd->getNumPatternEntries() << " :";
|
|
pbd->getSourceRange().print(llvm::errs(), pbd->getASTContext().SourceMgr,
|
|
false);
|
|
llvm::errs() << "\n";
|
|
llvm::errs() << "init: " << pbd->getInit(0) << "\n";
|
|
if (pbd->getInit(0)) {
|
|
llvm::errs() << "SR (init): ";
|
|
pbd->getInit(0)->getSourceRange().print(
|
|
llvm::errs(), pbd->getASTContext().SourceMgr, false);
|
|
llvm::errs() << "\n";
|
|
pbd->getInit(0)->dump(llvm::errs(), 0);
|
|
}
|
|
llvm::errs() << "vars:\n";
|
|
pbd->getPattern(0)->forEachVariable([&](VarDecl *vd) {
|
|
llvm::errs() << " " << vd->getName()
|
|
<< " implicit: " << vd->isImplicit()
|
|
<< " #accs: " << vd->getAllAccessors().size()
|
|
<< "\nSR (var):";
|
|
vd->getSourceRange().print(llvm::errs(), pbd->getASTContext().SourceMgr,
|
|
false);
|
|
llvm::errs() << "\nSR (braces)";
|
|
vd->getBracesRange().print(llvm::errs(), pbd->getASTContext().SourceMgr,
|
|
false);
|
|
llvm::errs() << "\n";
|
|
for (auto *a : vd->getAllAccessors()) {
|
|
llvm::errs() << "SR (acc): ";
|
|
a->getSourceRange().print(llvm::errs(),
|
|
pbd->getASTContext().SourceMgr, false);
|
|
llvm::errs() << "\n";
|
|
a->dump(llvm::errs(), 0);
|
|
}
|
|
});
|
|
};
|
|
|
|
Decl *lastD = nullptr;
|
|
for (auto n : input) {
|
|
auto *d = n.dyn_cast<Decl *>();
|
|
if (!d || !lastD || lastD->getStartLoc() != d->getStartLoc() ||
|
|
lastD->getKind() != d->getKind()) {
|
|
lastD = d;
|
|
}
|
|
if (auto *pbd = dyn_cast<PatternBindingDecl>(lastD))
|
|
dumpPBD(pbd, "prev");
|
|
if (auto *pbd = dyn_cast<PatternBindingDecl>(d)) {
|
|
dumpPBD(pbd, "curr");
|
|
assert(false && "found colliding pattern binding decls");
|
|
}
|
|
llvm::errs() << "Two same kind decls at same loc: \n";
|
|
lastD->dump(llvm::errs());
|
|
llvm::errs() << "and\n";
|
|
d->dump(llvm::errs());
|
|
assert(false && "Two same kind decls; unexpected kinds");
|
|
}
|
|
}
|
|
|
|
/// See rdar://53921962
|
|
template <typename Rangeable>
|
|
std::vector<Rangeable>
|
|
sortBySourceRange(std::vector<Rangeable> toBeSorted) const {
|
|
auto compareNodes = [&](Rangeable n1, Rangeable n2) {
|
|
return isNotAfter(n1, n2);
|
|
};
|
|
std::stable_sort(toBeSorted.begin(), toBeSorted.end(), compareNodes);
|
|
return toBeSorted;
|
|
}
|
|
|
|
template <typename Rangeable>
|
|
bool isNotAfter(Rangeable n1, Rangeable n2) const {
|
|
auto cmpLoc = [&](const SourceLoc l1, const SourceLoc l2) {
|
|
return l1 == l2 ? 0 : ctx.SourceMgr.isBeforeInBuffer(l1, l2) ? -1 : 1;
|
|
};
|
|
const auto r1 = getRangeableSourceRange(n1);
|
|
const auto r2 = getRangeableSourceRange(n2);
|
|
const int startOrder = cmpLoc(r1.Start, r2.Start);
|
|
const int endOrder = cmpLoc(r1.End, r2.End);
|
|
|
|
#ifndef NDEBUG
|
|
if (startOrder * endOrder == -1) {
|
|
llvm::errs() << "*** Start order contradicts end order between: ***\n";
|
|
dumpRangeable(n1, llvm::errs());
|
|
llvm::errs() << "\n*** and:\***\n";
|
|
dumpRangeable(n2, llvm::errs());
|
|
}
|
|
#endif
|
|
assert(startOrder * endOrder != -1 && "Start order contradicts end order");
|
|
return startOrder + endOrder < 1;
|
|
}
|
|
|
|
static bool isVarDeclInPatternBindingDecl(ASTNode n1, ASTNode n2) {
|
|
if (auto *d1 = n1.dyn_cast<Decl *>())
|
|
if (auto *vd = dyn_cast<VarDecl>(d1))
|
|
if (auto *d2 = n2.dyn_cast<Decl *>())
|
|
if (auto *pbd = dyn_cast<PatternBindingDecl>(d2))
|
|
return vd->getParentPatternBinding() == pbd;
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
bool shouldThisNodeBeScopedWhenEncountered(ASTNode n) {
|
|
// Do not scope VarDecls or Accessors when encountered because
|
|
// they get created directly by the pattern code.
|
|
// Doing otherwise distorts the source range
|
|
// of their parents.
|
|
return !PatternEntryDeclScope::isHandledSpecially(n);
|
|
}
|
|
|
|
template <typename ASTNodelike>
|
|
void pushAllNecessaryNodes(ArrayRef<ASTNodelike> nodesToPrepend) {
|
|
for (int i = nodesToPrepend.size() - 1; i >= 0; --i)
|
|
pushIfNecessary(nodesToPrepend[i]);
|
|
}
|
|
|
|
public:
|
|
/// Return true if scope tree contains all the decl contexts in the AST
|
|
/// May modify the scope tree in order to update obsolete scopes.
|
|
/// Likely slow.
|
|
bool containsAllDeclContextsFromAST() {
|
|
auto allDeclContexts = findLocalizableDeclContextsInAST();
|
|
llvm::DenseMap<const DeclContext *, const ASTScopeImpl *> bogusDCs;
|
|
bool rebuilt = false;
|
|
sourceFileScope->preOrderDo([&](ASTScopeImpl *scope) {
|
|
rebuilt |= scope->reexpandIfObsolete(*this);
|
|
});
|
|
sourceFileScope->postOrderDo([&](ASTScopeImpl *scope) {
|
|
if (auto *dc = scope->getDeclContext().getPtrOrNull()) {
|
|
auto iter = allDeclContexts.find(dc);
|
|
if (iter != allDeclContexts.end())
|
|
++iter->second;
|
|
else
|
|
bogusDCs.insert({dc, scope});
|
|
}
|
|
});
|
|
|
|
auto printDecl = [&](const Decl *d) {
|
|
llvm::errs() << "\ngetAsDecl() -> " << d << " ";
|
|
d->getSourceRange().dump(ctx.SourceMgr);
|
|
llvm::errs() << " : ";
|
|
d->dump(llvm::errs());
|
|
if (rebuilt)
|
|
llvm::errs() << " (rebuilt)";
|
|
llvm::errs() << "\n";
|
|
};
|
|
bool foundOmission = false;
|
|
for (const auto &p : allDeclContexts) {
|
|
if (p.second == 0) {
|
|
if (auto *d = p.first->getAsDecl()) {
|
|
if (isLocalizable(d)) {
|
|
llvm::errs() << "\nASTScope tree omitted DeclContext: " << p.first
|
|
<< " "
|
|
<< ":\n";
|
|
p.first->printContext(llvm::errs());
|
|
printDecl(d);
|
|
foundOmission = true;
|
|
}
|
|
} else {
|
|
// If no decl, no source range, so no scope
|
|
}
|
|
}
|
|
}
|
|
for (const auto dcAndScope : bogusDCs) {
|
|
llvm::errs() << "ASTScope tree confabulated: " << dcAndScope.getFirst()
|
|
<< ":\n";
|
|
dcAndScope.getFirst()->printContext(llvm::errs());
|
|
if (auto *d = dcAndScope.getFirst()->getAsDecl())
|
|
printDecl(d);
|
|
dcAndScope.getSecond()->print(llvm::errs(), 0, false);
|
|
}
|
|
return !foundOmission && bogusDCs.empty();
|
|
}
|
|
|
|
private:
|
|
/// Return a map of every DeclContext in the AST, and zero in the 2nd element.
|
|
llvm::DenseMap<const DeclContext *, unsigned>
|
|
findLocalizableDeclContextsInAST() const;
|
|
|
|
public:
|
|
void dump() const { print(llvm::errs()); }
|
|
|
|
void print(raw_ostream &out) const {
|
|
out << "(swift::ASTSourceFileScope*) " << sourceFileScope << "\n";
|
|
}
|
|
|
|
// Make vanilla new illegal for ASTScopes.
|
|
void *operator new(size_t bytes) = delete;
|
|
// Need this because have virtual destructors
|
|
void operator delete(void *data) {}
|
|
|
|
// Only allow allocation of scopes using the allocator of a particular source
|
|
// file.
|
|
void *operator new(size_t bytes, const ASTContext &ctx,
|
|
unsigned alignment = alignof(ScopeCreator));
|
|
void *operator new(size_t Bytes, void *Mem) {
|
|
assert(Mem);
|
|
return Mem;
|
|
}
|
|
};
|
|
} // ast_scope
|
|
} // namespace swift
|
|
|
|
#pragma mark Scope tree creation and extension
|
|
|
|
ASTScope::ASTScope(SourceFile *SF) : impl(createScopeTree(SF)) {}
|
|
|
|
ASTSourceFileScope *ASTScope::createScopeTree(SourceFile *SF) {
|
|
ScopeCreator *scopeCreator = new (SF->getASTContext()) ScopeCreator(SF);
|
|
scopeCreator->sourceFileScope->addNewDeclsToTree();
|
|
return scopeCreator->sourceFileScope;
|
|
}
|
|
|
|
void ASTSourceFileScope::addNewDeclsToTree() {
|
|
assert(SF && scopeCreator);
|
|
ArrayRef<Decl *> decls = SF->Decls;
|
|
ArrayRef<Decl *> newDecls = decls.slice(numberOfDeclsAlreadySeen);
|
|
std::vector<ASTNode> newNodes(newDecls.begin(), newDecls.end());
|
|
insertionPoint = scopeCreator->addNodesToTree(insertionPoint, newNodes);
|
|
numberOfDeclsAlreadySeen = SF->Decls.size();
|
|
|
|
// Too slow to perform all the time:
|
|
// assert(scopeCreator->containsAllDeclContextsFromAST() &&
|
|
// "ASTScope tree missed some DeclContexts or made some up");
|
|
}
|
|
|
|
ASTSourceFileScope::ASTSourceFileScope(SourceFile *SF,
|
|
ScopeCreator *scopeCreator)
|
|
: SF(SF), scopeCreator(scopeCreator), insertionPoint(this) {}
|
|
|
|
#pragma mark ASTVisitorForScopeCreation
|
|
|
|
namespace swift {
|
|
namespace ast_scope {
|
|
|
|
class ASTVisitorForScopeCreation
|
|
: public ASTVisitor<ASTVisitorForScopeCreation, NullablePtr<ASTScopeImpl>,
|
|
NullablePtr<ASTScopeImpl>, NullablePtr<ASTScopeImpl>,
|
|
void, void, void, ASTScopeImpl *, ScopeCreator &> {
|
|
public:
|
|
|
|
#pragma mark ASTNodes that do not create scopes
|
|
|
|
// Even ignored Decls and Stmts must extend the source range of a scope:
|
|
// E.g. a braceStmt with some definitions that ends in a statement that
|
|
// accesses such a definition must resolve as being IN the scope.
|
|
|
|
#define VISIT_AND_IGNORE(What) \
|
|
NullablePtr<ASTScopeImpl> visit##What(What *w, ASTScopeImpl *p, \
|
|
ScopeCreator &) { \
|
|
p->widenSourceRangeForIgnoredASTNode(w); \
|
|
return p; \
|
|
}
|
|
|
|
VISIT_AND_IGNORE(ImportDecl)
|
|
VISIT_AND_IGNORE(EnumCaseDecl)
|
|
VISIT_AND_IGNORE(PrecedenceGroupDecl)
|
|
VISIT_AND_IGNORE(InfixOperatorDecl)
|
|
VISIT_AND_IGNORE(PrefixOperatorDecl)
|
|
VISIT_AND_IGNORE(PostfixOperatorDecl)
|
|
VISIT_AND_IGNORE(GenericTypeParamDecl)
|
|
VISIT_AND_IGNORE(AssociatedTypeDecl)
|
|
VISIT_AND_IGNORE(ModuleDecl)
|
|
VISIT_AND_IGNORE(ParamDecl)
|
|
VISIT_AND_IGNORE(PoundDiagnosticDecl)
|
|
VISIT_AND_IGNORE(MissingMemberDecl)
|
|
|
|
// This declaration is handled from the PatternBindingDecl
|
|
VISIT_AND_IGNORE(VarDecl)
|
|
|
|
// This declaration is handled from addChildrenForAllExplicitAccessors
|
|
VISIT_AND_IGNORE(AccessorDecl)
|
|
|
|
// These contain nothing to scope.
|
|
VISIT_AND_IGNORE(BreakStmt)
|
|
VISIT_AND_IGNORE(ContinueStmt)
|
|
VISIT_AND_IGNORE(FallthroughStmt)
|
|
VISIT_AND_IGNORE(FailStmt)
|
|
|
|
#undef VISIT_AND_IGNORE
|
|
|
|
#pragma mark simple creation ignoring deferred nodes
|
|
|
|
#define VISIT_AND_CREATE(What, ScopeClass) \
|
|
NullablePtr<ASTScopeImpl> visit##What(What *w, ASTScopeImpl *p, \
|
|
ScopeCreator &scopeCreator) { \
|
|
return scopeCreator.createSubtreeIfUnique<ScopeClass>(p, w); \
|
|
}
|
|
|
|
VISIT_AND_CREATE(SubscriptDecl, SubscriptDeclScope)
|
|
VISIT_AND_CREATE(IfStmt, IfStmtScope)
|
|
VISIT_AND_CREATE(WhileStmt, WhileStmtScope)
|
|
VISIT_AND_CREATE(RepeatWhileStmt, RepeatWhileScope)
|
|
VISIT_AND_CREATE(DoCatchStmt, DoCatchStmtScope)
|
|
VISIT_AND_CREATE(SwitchStmt, SwitchStmtScope)
|
|
VISIT_AND_CREATE(ForEachStmt, ForEachStmtScope)
|
|
VISIT_AND_CREATE(CatchStmt, CatchStmtScope)
|
|
VISIT_AND_CREATE(CaseStmt, CaseStmtScope)
|
|
VISIT_AND_CREATE(AbstractFunctionDecl, AbstractFunctionDeclScope)
|
|
|
|
#undef VISIT_AND_CREATE
|
|
|
|
#pragma mark 2D simple creation (ignoring deferred nodes)
|
|
|
|
#define VISIT_AND_CREATE_WHOLE_PORTION(What, WhatScope) \
|
|
NullablePtr<ASTScopeImpl> visit##What(What *w, ASTScopeImpl *p, \
|
|
ScopeCreator &scopeCreator) { \
|
|
return scopeCreator.createSubtree2DIfUnique< \
|
|
WhatScope, GenericTypeOrExtensionWholePortion>(p, w); \
|
|
}
|
|
|
|
VISIT_AND_CREATE_WHOLE_PORTION(ExtensionDecl, ExtensionScope)
|
|
VISIT_AND_CREATE_WHOLE_PORTION(StructDecl, NominalTypeScope)
|
|
VISIT_AND_CREATE_WHOLE_PORTION(ClassDecl, NominalTypeScope)
|
|
VISIT_AND_CREATE_WHOLE_PORTION(EnumDecl, NominalTypeScope)
|
|
VISIT_AND_CREATE_WHOLE_PORTION(TypeAliasDecl, TypeAliasScope)
|
|
VISIT_AND_CREATE_WHOLE_PORTION(OpaqueTypeDecl, OpaqueTypeScope)
|
|
#undef VISIT_AND_CREATE_WHOLE_PORTION
|
|
|
|
NullablePtr<ASTScopeImpl> visitProtocolDecl(ProtocolDecl *e, ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
e->createGenericParamsIfMissing();
|
|
return scopeCreator.createSubtree2DIfUnique<
|
|
NominalTypeScope, GenericTypeOrExtensionWholePortion>(p, e);
|
|
}
|
|
|
|
#pragma mark simple creation with deferred nodes
|
|
|
|
// Each of the following creates a new scope, so that nodes which were parsed
|
|
// after them need to be placed in scopes BELOW them in the tree. So pass down
|
|
// the deferred nodes.
|
|
NullablePtr<ASTScopeImpl> visitGuardStmt(GuardStmt *e, ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
return scopeCreator.createSubtreeIfUnique<GuardStmtScope>(p, e);
|
|
}
|
|
NullablePtr<ASTScopeImpl> visitDoStmt(DoStmt *ds, ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
return scopeCreator.createScopeFor(ds->getBody(), p);
|
|
}
|
|
NullablePtr<ASTScopeImpl> visitTopLevelCodeDecl(TopLevelCodeDecl *d,
|
|
ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
return scopeCreator.createSubtreeIfUnique<TopLevelCodeScope>(p, d);
|
|
}
|
|
|
|
#pragma mark special-case creation
|
|
|
|
ASTScopeImpl *visitSourceFile(SourceFile *, ASTScopeImpl *, ScopeCreator &) {
|
|
llvm_unreachable("SourceFiles are orphans.");
|
|
}
|
|
|
|
NullablePtr<ASTScopeImpl> visitYieldStmt(YieldStmt *ys, ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
for (Expr *e : ys->getYields())
|
|
visitExpr(e, p, scopeCreator);
|
|
return p;
|
|
}
|
|
|
|
NullablePtr<ASTScopeImpl> visitDeferStmt(DeferStmt *ds, ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
visitFuncDecl(ds->getTempDecl(), p, scopeCreator);
|
|
return p;
|
|
}
|
|
|
|
NullablePtr<ASTScopeImpl> visitBraceStmt(BraceStmt *bs, ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
if (BraceStmtScope::shouldCreateScope(bs))
|
|
scopeCreator.createSubtreeIfUnique<BraceStmtScope>(p, bs);
|
|
return p;
|
|
}
|
|
|
|
NullablePtr<ASTScopeImpl>
|
|
visitPatternBindingDecl(PatternBindingDecl *patternBinding,
|
|
ASTScopeImpl *parentScope,
|
|
ScopeCreator &scopeCreator) {
|
|
scopeCreator.createAttachedPropertyWrapperScope(patternBinding,
|
|
parentScope);
|
|
|
|
const bool isInTypeDecl = parentScope->isATypeDeclScope();
|
|
|
|
const DeclVisibilityKind vis =
|
|
isInTypeDecl ? DeclVisibilityKind::MemberOfCurrentNominal
|
|
: DeclVisibilityKind::LocalVariable;
|
|
auto *insertionPoint = parentScope;
|
|
for (unsigned i = 0; i < patternBinding->getPatternList().size(); ++i) {
|
|
// TODO: Won't need to do so much work to avoid creating one without
|
|
// a SourceRange once rdar://53627317 is done and getChildlessSourceRange
|
|
// for PatternEntryDeclScope is simplified to use the PatternEntry's
|
|
// source range.
|
|
auto &patternEntry = patternBinding->getPatternList()[i];
|
|
if (!patternEntry.getOriginalInit()) {
|
|
bool found = false;
|
|
patternEntry.getPattern()->forEachVariable([&](VarDecl *vd) {
|
|
if (!vd->isImplicit())
|
|
found = true;
|
|
else
|
|
found |= llvm::any_of(vd->getAllAccessors(), [&](AccessorDecl *a) {
|
|
return isLocalizable(a);
|
|
});
|
|
});
|
|
if (!found)
|
|
continue;
|
|
}
|
|
insertionPoint = scopeCreator
|
|
.createSubtreeIfUnique<PatternEntryDeclScope>(
|
|
insertionPoint, patternBinding, i, vis)
|
|
.getPtrOr(insertionPoint);
|
|
}
|
|
// If in a type decl, the type search will find these,
|
|
// but if in a brace stmt, must continue under the last binding.
|
|
return isInTypeDecl ? parentScope : insertionPoint;
|
|
}
|
|
|
|
NullablePtr<ASTScopeImpl> visitEnumElementDecl(EnumElementDecl *eed,
|
|
ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
scopeCreator.createSubtree<EnumElementScope>(p, eed);
|
|
return p;
|
|
}
|
|
|
|
NullablePtr<ASTScopeImpl> visitIfConfigDecl(IfConfigDecl *icd,
|
|
ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
llvm_unreachable("Should be handled inside of "
|
|
"expandClausesSortAndCullElementsOrMembers");
|
|
}
|
|
|
|
NullablePtr<ASTScopeImpl> visitReturnStmt(ReturnStmt *rs, ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
if (rs->hasResult())
|
|
visitExpr(rs->getResult(), p, scopeCreator);
|
|
return p;
|
|
}
|
|
|
|
NullablePtr<ASTScopeImpl> visitThrowStmt(ThrowStmt *ts, ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
visitExpr(ts->getSubExpr(), p, scopeCreator);
|
|
return p;
|
|
}
|
|
|
|
NullablePtr<ASTScopeImpl> visitPoundAssertStmt(PoundAssertStmt *pas,
|
|
ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
visitExpr(pas->getCondition(), p, scopeCreator);
|
|
return p;
|
|
}
|
|
|
|
NullablePtr<ASTScopeImpl> visitExpr(Expr *expr, ASTScopeImpl *p,
|
|
ScopeCreator &scopeCreator) {
|
|
if (expr) {
|
|
p->widenSourceRangeForIgnoredASTNode(expr);
|
|
scopeCreator.addChildrenForCapturesAndClosuresIn(expr, p);
|
|
}
|
|
return p;
|
|
}
|
|
};
|
|
} // namespace ast_scope
|
|
} // namespace swift
|
|
|
|
// These definitions are way down here so it can call into
|
|
// ASTVisitorForScopeCreation
|
|
NullablePtr<ASTScopeImpl> ScopeCreator::createScopeFor(ASTNode n,
|
|
ASTScopeImpl *parent) {
|
|
if (!shouldCreateScope(n))
|
|
return parent;
|
|
if (auto *p = n.dyn_cast<Decl *>())
|
|
return ASTVisitorForScopeCreation().visit(p, parent, *this);
|
|
if (auto *p = n.dyn_cast<Expr *>())
|
|
return ASTVisitorForScopeCreation().visit(p, parent, *this);
|
|
auto *p = n.get<Stmt *>();
|
|
return ASTVisitorForScopeCreation().visit(p, parent, *this);
|
|
}
|
|
|
|
void ScopeCreator::addChildrenForAllLocalizableAccessorsInSourceOrder(
|
|
AbstractStorageDecl *asd, ASTScopeImpl *parent) {
|
|
// auto &SM = ctx.SourceMgr;
|
|
// auto file =
|
|
// SM.getIdentifierForBuffer(SM.findBufferContainingLoc(parent->getSourceRange().Start));
|
|
// auto line = SM.getLineNumber(parent->getSourceRange().Start);
|
|
// bool dumpEm = file.endswith("ArrayBody.swift") && line == 51;
|
|
//
|
|
|
|
// Accessors are always nested within their abstract storage
|
|
// declaration. The nesting may not be immediate, because subscripts may
|
|
// have intervening scopes for generics.
|
|
AbstractStorageDecl *const enclosingAbstractStorageDecl =
|
|
parent->getEnclosingAbstractStorageDecl().get();
|
|
|
|
std::vector<AccessorDecl *> accessorsToScope;
|
|
// Assume we don't have to deal with inactive clauses of IfConfigs here
|
|
llvm::copy_if(asd->getAllAccessors(), std::back_inserter(accessorsToScope),
|
|
[&](AccessorDecl *ad) {
|
|
return isLocalizable(ad) &&
|
|
enclosingAbstractStorageDecl == ad->getStorage();
|
|
});
|
|
|
|
// Sort in order to include synthesized ones, which are out of order.
|
|
// Part of rdar://53921774 rm extra copy
|
|
for (auto *accessor : sortBySourceRange(accessorsToScope)) {
|
|
ASTVisitorForScopeCreation().visitAbstractFunctionDecl(accessor, parent,
|
|
*this);
|
|
}
|
|
}
|
|
|
|
#pragma mark creation helpers
|
|
|
|
void ASTScopeImpl::addChild(ASTScopeImpl *child, ASTContext &ctx) {
|
|
// If this is the first time we've added children, notify the ASTContext
|
|
// that there's a SmallVector that needs to be cleaned up.
|
|
// FIXME: If we had access to SmallVector::isSmall(), we could do better.
|
|
if (storedChildren.empty() && !haveAddedCleanup) {
|
|
ctx.addDestructorCleanup(storedChildren);
|
|
haveAddedCleanup = true;
|
|
}
|
|
storedChildren.push_back(child);
|
|
assert(!child->getParent() && "child should not already have parent");
|
|
child->parent = this;
|
|
clearCachedSourceRangesOfMeAndAncestors();
|
|
}
|
|
|
|
void ASTScopeImpl::removeChildren() {
|
|
clearCachedSourceRangesOfMeAndAncestors();
|
|
storedChildren.clear();
|
|
}
|
|
|
|
void ASTScopeImpl::disownDescendants(ScopeCreator &scopeCreator) {
|
|
for (auto *c : getChildren()) {
|
|
c->disownDescendants(scopeCreator);
|
|
c->emancipate();
|
|
scopeCreator.scopedNodes.erase(c);
|
|
}
|
|
removeChildren();
|
|
}
|
|
|
|
bool PatternEntryDeclScope::isHandledSpecially(const ASTNode n) {
|
|
if (auto *d = n.dyn_cast<Decl *>()) {
|
|
#ifndef NDEBUG
|
|
if (auto *acc = dyn_cast<AccessorDecl>(d)) {
|
|
if (auto *vd = dyn_cast<VarDecl>(acc->getStorage())) {
|
|
const bool foundIt =
|
|
llvm::any_of(vd->getAllAccessors(),
|
|
[&](AccessorDecl *vda) { return vda == acc; });
|
|
assert(foundIt && "Cannot build a scope if cannot find an accessor");
|
|
}
|
|
}
|
|
#endif
|
|
return isa<VarDecl>(d) || isa<AccessorDecl>(d);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#pragma mark implementations of expansion
|
|
|
|
ASTScopeImpl *ASTScopeImpl::expandAndBeCurrent(ScopeCreator &scopeCreator) {
|
|
auto *insertionPoint = expandSpecifically(scopeCreator);
|
|
beCurrent();
|
|
setChildrenCountWhenLastExpanded();
|
|
assert((getChildlessSourceRange().isValid() || !getChildren().empty()) &&
|
|
"need to be able to find source range");
|
|
assert(verifyThatChildrenAreContainedWithin(getSourceRange()) &&
|
|
"Search will fail");
|
|
return insertionPoint;
|
|
}
|
|
|
|
// Do this whole bit so it's easy to see which type of scope is which
|
|
|
|
#define CREATES_NEW_INSERTION_POINT(Scope) \
|
|
ASTScopeImpl *Scope::expandSpecifically(ScopeCreator &scopeCreator) { \
|
|
return expandAScopeThatCreatesANewInsertionPoint(scopeCreator); \
|
|
}
|
|
|
|
#define NO_NEW_INSERTION_POINT(Scope) \
|
|
ASTScopeImpl *Scope::expandSpecifically(ScopeCreator &scopeCreator) { \
|
|
expandAScopeThatDoesNotCreateANewInsertionPoint(scopeCreator); \
|
|
return getParent().get(); \
|
|
}
|
|
|
|
// Return this in particular for GenericParamScope so body is scoped under it
|
|
#define NO_EXPANSION(Scope) \
|
|
ASTScopeImpl *Scope::expandSpecifically(ScopeCreator &) { return this; }
|
|
|
|
CREATES_NEW_INSERTION_POINT(ParameterListScope)
|
|
CREATES_NEW_INSERTION_POINT(ConditionalClauseScope)
|
|
CREATES_NEW_INSERTION_POINT(GuardStmtScope)
|
|
CREATES_NEW_INSERTION_POINT(PatternEntryDeclScope)
|
|
CREATES_NEW_INSERTION_POINT(PatternEntryInitializerScope)
|
|
CREATES_NEW_INSERTION_POINT(GenericTypeOrExtensionScope)
|
|
CREATES_NEW_INSERTION_POINT(BraceStmtScope)
|
|
CREATES_NEW_INSERTION_POINT(TopLevelCodeScope)
|
|
|
|
NO_NEW_INSERTION_POINT(AbstractFunctionBodyScope)
|
|
NO_NEW_INSERTION_POINT(AbstractFunctionDeclScope)
|
|
NO_NEW_INSERTION_POINT(AttachedPropertyWrapperScope)
|
|
NO_NEW_INSERTION_POINT(EnumElementScope)
|
|
|
|
NO_NEW_INSERTION_POINT(CaptureListScope)
|
|
NO_NEW_INSERTION_POINT(CaseStmtScope)
|
|
NO_NEW_INSERTION_POINT(CatchStmtScope)
|
|
NO_NEW_INSERTION_POINT(ClosureBodyScope)
|
|
NO_NEW_INSERTION_POINT(DefaultArgumentInitializerScope)
|
|
NO_NEW_INSERTION_POINT(DoCatchStmtScope)
|
|
NO_NEW_INSERTION_POINT(ForEachPatternScope)
|
|
NO_NEW_INSERTION_POINT(ForEachStmtScope)
|
|
NO_NEW_INSERTION_POINT(IfStmtScope)
|
|
NO_NEW_INSERTION_POINT(RepeatWhileScope)
|
|
NO_NEW_INSERTION_POINT(SubscriptDeclScope)
|
|
NO_NEW_INSERTION_POINT(SwitchStmtScope)
|
|
NO_NEW_INSERTION_POINT(VarDeclScope)
|
|
NO_NEW_INSERTION_POINT(WhileStmtScope)
|
|
NO_NEW_INSERTION_POINT(WholeClosureScope)
|
|
|
|
NO_EXPANSION(GenericParamScope)
|
|
NO_EXPANSION(ASTSourceFileScope)
|
|
NO_EXPANSION(ClosureParametersScope)
|
|
NO_EXPANSION(SpecializeAttributeScope)
|
|
NO_EXPANSION(ConditionalClausePatternUseScope)
|
|
NO_EXPANSION(LookupParentDiversionScope)
|
|
|
|
#undef CREATES_NEW_INSERTION_POINT
|
|
#undef NO_NEW_INSERTION_POINT
|
|
|
|
ASTScopeImpl *ParameterListScope::expandAScopeThatCreatesANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
// Each initializer for a function parameter is its own, sibling, scope.
|
|
// Unlike generic parameters or pattern initializers, it cannot refer to a
|
|
// previous parameter.
|
|
for (ParamDecl *pd : params->getArray()) {
|
|
if (pd->getDefaultValue())
|
|
scopeCreator.createSubtree<DefaultArgumentInitializerScope>(this, pd);
|
|
}
|
|
return this; // body of func goes under me
|
|
}
|
|
|
|
ASTScopeImpl *PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
// Initializers come before VarDecls, e.g. PCMacro/didSet.swift 19
|
|
auto patternEntry = getPatternEntry();
|
|
// Create a child for the initializer, if present.
|
|
// Cannot trust the source range given in the ASTScopeImpl for the end of the
|
|
// initializer (because of InterpolatedLiteralStrings and EditorPlaceHolders),
|
|
// so compute it ourselves.
|
|
// Even if this predicate fails, there may be an initContext but
|
|
// we cannot make a scope for it, since no source range.
|
|
if (patternEntry.getOriginalInit() &&
|
|
isLocalizable(patternEntry.getOriginalInit())) {
|
|
assert(
|
|
!getSourceManager().isBeforeInBuffer(
|
|
patternEntry.getOriginalInit()->getStartLoc(), decl->getStartLoc()) &&
|
|
"Original inits are always after the '='");
|
|
scopeCreator.createSubtree<PatternEntryInitializerScope>(
|
|
this, decl, patternEntryIndex, vis);
|
|
}
|
|
// Add accessors for the variables in this pattern.
|
|
forEachVarDeclWithLocalizableAccessors(scopeCreator, [&](VarDecl *var) {
|
|
scopeCreator.createSubtreeIfUnique<VarDeclScope>(this, var);
|
|
});
|
|
|
|
return getParent().get();
|
|
}
|
|
|
|
ASTScopeImpl *
|
|
PatternEntryInitializerScope::expandAScopeThatCreatesANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
// Create a child for the initializer expression.
|
|
ASTVisitorForScopeCreation().visitExpr(getPatternEntry().getOriginalInit(), this,
|
|
scopeCreator);
|
|
return this;
|
|
}
|
|
|
|
ASTScopeImpl *ConditionalClauseScope::expandAScopeThatCreatesANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
const StmtConditionElement &sec = getStmtConditionElement();
|
|
switch (sec.getKind()) {
|
|
case StmtConditionElement::CK_Availability:
|
|
return this;
|
|
case StmtConditionElement::CK_Boolean:
|
|
ASTVisitorForScopeCreation().visitExpr(sec.getBoolean(), this,
|
|
scopeCreator);
|
|
return this;
|
|
case StmtConditionElement::CK_PatternBinding:
|
|
ASTVisitorForScopeCreation().visitExpr(sec.getInitializer(), this,
|
|
scopeCreator);
|
|
return scopeCreator.createSubtree<ConditionalClausePatternUseScope>(
|
|
this, sec.getPattern(), endLoc);
|
|
}
|
|
}
|
|
|
|
ASTScopeImpl *GuardStmtScope::expandAScopeThatCreatesANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
|
|
ASTScopeImpl *conditionLookupParent =
|
|
createNestedConditionalClauseScopes(scopeCreator, stmt->getBody());
|
|
// Add a child for the 'guard' body, which always exits.
|
|
// Parent is whole guard stmt scope, NOT the cond scopes
|
|
scopeCreator.createScopeFor(stmt->getBody(), this);
|
|
|
|
return scopeCreator.createSubtree<LookupParentDiversionScope>(
|
|
this, conditionLookupParent, stmt->getEndLoc());
|
|
}
|
|
|
|
ASTScopeImpl *
|
|
GenericTypeOrExtensionScope::expandAScopeThatCreatesANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
return portion->expandScope(this, scopeCreator);
|
|
}
|
|
|
|
ASTScopeImpl *BraceStmtScope::expandAScopeThatCreatesANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
// TODO: remove the sort after performing rdar://53254395
|
|
return scopeCreator.addNodesToTree(this, stmt->getElements());
|
|
}
|
|
|
|
ASTScopeImpl *TopLevelCodeScope::expandAScopeThatCreatesANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
if (!BraceStmtScope::shouldCreateScope(decl->getBody()))
|
|
return this;
|
|
auto *insertionPoint =
|
|
scopeCreator.createSubtreeIfUnique<BraceStmtScope>(this, decl->getBody())
|
|
.getPtrOr(this);
|
|
return insertionPoint;
|
|
}
|
|
|
|
#pragma mark expandAScopeThatDoesNotCreateANewInsertionPoint
|
|
|
|
void ASTSourceFileScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
llvm_unreachable("expanded by addNewDeclsToTree()");
|
|
}
|
|
|
|
// Create child scopes for every declaration in a body.
|
|
|
|
void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
// Create scopes for specialize attributes
|
|
scopeCreator.forEachSpecializeAttrInSourceOrder(
|
|
decl, [&](SpecializeAttr *specializeAttr) {
|
|
scopeCreator.createSubtreeIfUnique<SpecializeAttributeScope>(
|
|
this, specializeAttr, decl);
|
|
});
|
|
// Create scopes for generic and ordinary parameters.
|
|
// For a subscript declaration, the generic and ordinary parameters are in an
|
|
// ancestor scope, so don't make them here.
|
|
ASTScopeImpl *leaf = this;
|
|
if (!isa<AccessorDecl>(decl)) {
|
|
leaf = scopeCreator.createGenericParamScopes(decl, decl->getGenericParams(),
|
|
leaf);
|
|
if (isLocalizable(decl) && getParamsSourceLoc(decl).isValid()) {
|
|
// swift::createDesignatedInitOverride just clones the parameters, so they
|
|
// end up with a bogus SourceRange, maybe *before* the start of the
|
|
// function.
|
|
if (!decl->isImplicit()) {
|
|
leaf = scopeCreator.createSubtree<ParameterListScope>(
|
|
leaf, decl->getParameters(), nullptr);
|
|
}
|
|
}
|
|
}
|
|
// Create scope for the body.
|
|
// We create body scopes when there is no body for source kit to complete
|
|
// erroneous code in bodies. But don't let compiler synthesize one.
|
|
if (decl->getBody(false) && decl->getBodySourceRange().isValid()) {
|
|
if (AbstractFunctionBodyScope::isAMethod(decl))
|
|
scopeCreator.createSubtree<MethodBodyScope>(leaf, decl);
|
|
else
|
|
scopeCreator.createSubtree<PureFunctionBodyScope>(leaf, decl);
|
|
}
|
|
}
|
|
|
|
void EnumElementScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
if (auto *pl = decl->getParameterList())
|
|
scopeCreator.createSubtree<ParameterListScope>(this, pl, nullptr);
|
|
if (auto *expr = decl->getRawValueExpr()) // might contain a closure
|
|
ASTVisitorForScopeCreation().visitExpr(expr, this, scopeCreator);
|
|
}
|
|
|
|
void AbstractFunctionBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
expandBody(scopeCreator);
|
|
}
|
|
|
|
void IfStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
ASTScopeImpl *insertionPoint =
|
|
createNestedConditionalClauseScopes(scopeCreator, stmt->getThenStmt());
|
|
|
|
// The 'then' branch
|
|
scopeCreator.createScopeFor(stmt->getThenStmt(), insertionPoint);
|
|
|
|
// Add the 'else' branch, if needed.
|
|
scopeCreator.createScopeFor(stmt->getElseStmt(), this);
|
|
}
|
|
|
|
void WhileStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
ASTScopeImpl *insertionPoint =
|
|
createNestedConditionalClauseScopes(scopeCreator, stmt->getBody());
|
|
scopeCreator.createScopeFor(stmt->getBody(), insertionPoint);
|
|
}
|
|
|
|
void RepeatWhileScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
scopeCreator.createScopeFor(stmt->getBody(), this);
|
|
ASTVisitorForScopeCreation().visitExpr(stmt->getCond(), this, scopeCreator);
|
|
}
|
|
|
|
void DoCatchStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
scopeCreator.createScopeFor(stmt->getBody(), this);
|
|
|
|
for (auto catchClause : stmt->getCatches()) {
|
|
ASTVisitorForScopeCreation().visitCatchStmt(catchClause, this,
|
|
scopeCreator);
|
|
}
|
|
}
|
|
|
|
void SwitchStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
ASTVisitorForScopeCreation().visitExpr(stmt->getSubjectExpr(), this,
|
|
scopeCreator);
|
|
|
|
for (auto caseStmt : stmt->getCases()) {
|
|
if (isLocalizable(caseStmt))
|
|
scopeCreator.createSubtreeIfUnique<CaseStmtScope>(this, caseStmt);
|
|
}
|
|
}
|
|
|
|
void ForEachStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
ASTVisitorForScopeCreation().visitExpr(stmt->getSequence(), this,
|
|
scopeCreator);
|
|
|
|
// Add a child describing the scope of the pattern.
|
|
// In error cases such as:
|
|
// let v: C { for b : Int -> S((array: P { }
|
|
// the body is implicit and it would overlap the source range of the expr
|
|
// above.
|
|
//
|
|
// TODO: refer to rdar://53921962
|
|
if (!stmt->getBody()->isImplicit()) {
|
|
if (isLocalizable(stmt->getBody()))
|
|
scopeCreator.createSubtree<ForEachPatternScope>(this, stmt);
|
|
}
|
|
}
|
|
|
|
void ForEachPatternScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
ASTVisitorForScopeCreation().visitExpr(stmt->getWhere(), this, scopeCreator);
|
|
ASTVisitorForScopeCreation().visitBraceStmt(stmt->getBody(), this,
|
|
scopeCreator);
|
|
}
|
|
|
|
void CatchStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
ASTVisitorForScopeCreation().visitExpr(stmt->getGuardExpr(), this,
|
|
scopeCreator);
|
|
scopeCreator.createScopeFor(stmt->getBody(), this);
|
|
}
|
|
|
|
void CaseStmtScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
for (auto &caseItem : stmt->getMutableCaseLabelItems())
|
|
ASTVisitorForScopeCreation().visitExpr(caseItem.getGuardExpr(), this,
|
|
scopeCreator);
|
|
|
|
// Add a child for the case body.
|
|
scopeCreator.createScopeFor(stmt->getBody(), this);
|
|
}
|
|
|
|
void VarDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
scopeCreator.addChildrenForAllLocalizableAccessorsInSourceOrder(decl, this);
|
|
}
|
|
|
|
void SubscriptDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
auto *sub = decl;
|
|
auto *leaf =
|
|
scopeCreator.createGenericParamScopes(sub, sub->getGenericParams(), this);
|
|
auto *params = scopeCreator.createSubtree<ParameterListScope>(
|
|
leaf, sub->getIndices(), sub->getAccessor(AccessorKind::Get));
|
|
scopeCreator.addChildrenForAllLocalizableAccessorsInSourceOrder(sub, params);
|
|
}
|
|
|
|
void WholeClosureScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
if (auto *cl = captureList.getPtrOrNull())
|
|
scopeCreator.createSubtreeMustBeUnique<CaptureListScope>(this, cl);
|
|
ASTScopeImpl *bodyParent = this;
|
|
if (closureExpr->getInLoc().isValid())
|
|
bodyParent = scopeCreator.createSubtree<ClosureParametersScope>(
|
|
this, closureExpr, captureList);
|
|
scopeCreator.createSubtree<ClosureBodyScope>(bodyParent, closureExpr,
|
|
captureList);
|
|
}
|
|
|
|
void CaptureListScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
// Patterns here are implicit, so need to dig out the intializers
|
|
for (const CaptureListEntry &captureListEntry : expr->getCaptureList()) {
|
|
for (unsigned patternEntryIndex = 0;
|
|
patternEntryIndex < captureListEntry.Init->getNumPatternEntries();
|
|
++patternEntryIndex) {
|
|
Expr *init = captureListEntry.Init->getInit(patternEntryIndex);
|
|
scopeCreator.addChildrenForCapturesAndClosuresIn(init, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClosureBodyScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
if (BraceStmtScope::shouldCreateScope(closureExpr->getBody())) {
|
|
scopeCreator.createSubtreeMustBeUnique<BraceStmtScope>(
|
|
this, closureExpr->getBody());
|
|
}
|
|
}
|
|
|
|
void DefaultArgumentInitializerScope::
|
|
expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
auto *initExpr = decl->getDefaultValue();
|
|
assert(initExpr);
|
|
ASTVisitorForScopeCreation().visitExpr(initExpr, this, scopeCreator);
|
|
}
|
|
|
|
void AttachedPropertyWrapperScope::
|
|
expandAScopeThatDoesNotCreateANewInsertionPoint(
|
|
ScopeCreator &scopeCreator) {
|
|
for (auto *attr : decl->getAttrs().getAttributes<CustomAttr>()) {
|
|
if (auto *expr = attr->getArg())
|
|
ASTVisitorForScopeCreation().visitExpr(expr, this, scopeCreator);
|
|
}
|
|
}
|
|
|
|
#pragma mark expandScope
|
|
|
|
ASTScopeImpl *GenericTypeOrExtensionWholePortion::expandScope(
|
|
GenericTypeOrExtensionScope *scope, ScopeCreator &scopeCreator) const {
|
|
// Prevent circular request bugs caused by illegal input and
|
|
// doing lookups that getExtendedNominal in the midst of getExtendedNominal.
|
|
// rdar://53972776
|
|
if (scope->shouldHaveABody() && !scope->doesDeclHaveABody())
|
|
return scope->getParent().get();
|
|
|
|
auto *deepestScope = scopeCreator.createGenericParamScopes(
|
|
scope->getDecl(), scope->getGenericContext()->getGenericParams(), scope);
|
|
if (scope->getGenericContext()->getTrailingWhereClause())
|
|
scope->createTrailingWhereClauseScope(deepestScope, scopeCreator);
|
|
scope->createBodyScope(deepestScope, scopeCreator);
|
|
return scope->getParent().get();
|
|
}
|
|
|
|
ASTScopeImpl *
|
|
IterableTypeBodyPortion::expandScope(GenericTypeOrExtensionScope *scope,
|
|
ScopeCreator &scopeCreator) const {
|
|
scope->expandBody(scopeCreator);
|
|
return scope->getParent().get();
|
|
}
|
|
|
|
ASTScopeImpl *GenericTypeOrExtensionWherePortion::expandScope(
|
|
GenericTypeOrExtensionScope *scope, ScopeCreator &) const {
|
|
return scope->getParent().get();
|
|
}
|
|
|
|
#pragma mark createBodyScope
|
|
|
|
void ExtensionScope::createBodyScope(ASTScopeImpl *leaf,
|
|
ScopeCreator &scopeCreator) {
|
|
scopeCreator.createSubtree2D<ExtensionScope, IterableTypeBodyPortion>(leaf,
|
|
decl);
|
|
}
|
|
void NominalTypeScope::createBodyScope(ASTScopeImpl *leaf,
|
|
ScopeCreator &scopeCreator) {
|
|
scopeCreator.createSubtree2D<NominalTypeScope, IterableTypeBodyPortion>(leaf,
|
|
decl);
|
|
}
|
|
|
|
#pragma mark createTrailingWhereClauseScope
|
|
|
|
ASTScopeImpl *GenericTypeOrExtensionScope::createTrailingWhereClauseScope(
|
|
ASTScopeImpl *parent, ScopeCreator &scopeCreator) {
|
|
return parent;
|
|
}
|
|
|
|
ASTScopeImpl *
|
|
ExtensionScope::createTrailingWhereClauseScope(ASTScopeImpl *parent,
|
|
ScopeCreator &scopeCreator) {
|
|
return scopeCreator
|
|
.createSubtree2D<ExtensionScope, GenericTypeOrExtensionWherePortion>(
|
|
parent, decl);
|
|
}
|
|
ASTScopeImpl *
|
|
NominalTypeScope::createTrailingWhereClauseScope(ASTScopeImpl *parent,
|
|
ScopeCreator &scopeCreator) {
|
|
return scopeCreator
|
|
.createSubtree2D<NominalTypeScope, GenericTypeOrExtensionWherePortion>(
|
|
parent, decl);
|
|
}
|
|
ASTScopeImpl *
|
|
TypeAliasScope::createTrailingWhereClauseScope(ASTScopeImpl *parent,
|
|
ScopeCreator &scopeCreator) {
|
|
return scopeCreator
|
|
.createSubtree2D<TypeAliasScope, GenericTypeOrExtensionWherePortion>(
|
|
parent, decl);
|
|
}
|
|
|
|
#pragma mark misc
|
|
|
|
ASTScopeImpl *LabeledConditionalStmtScope::createNestedConditionalClauseScopes(
|
|
ScopeCreator &scopeCreator, const Stmt *const afterConds) {
|
|
auto *stmt = getLabeledConditionalStmt();
|
|
ASTScopeImpl *insertionPoint = this;
|
|
for (unsigned i = 0; i < stmt->getCond().size(); ++i) {
|
|
insertionPoint = scopeCreator.createSubtree<ConditionalClauseScope>(
|
|
insertionPoint, stmt, i, afterConds->getStartLoc());
|
|
}
|
|
return insertionPoint;
|
|
}
|
|
|
|
AbstractPatternEntryScope::AbstractPatternEntryScope(
|
|
PatternBindingDecl *declBeingScoped, unsigned entryIndex,
|
|
DeclVisibilityKind vis)
|
|
: decl(declBeingScoped), patternEntryIndex(entryIndex), vis(vis) {
|
|
assert(entryIndex < declBeingScoped->getPatternList().size() &&
|
|
"out of bounds");
|
|
}
|
|
|
|
void AbstractPatternEntryScope::forEachVarDeclWithLocalizableAccessors(
|
|
ScopeCreator &scopeCreator, function_ref<void(VarDecl *)> foundOne) const {
|
|
getPatternEntry().getPattern()->forEachVariable([&](VarDecl *var) {
|
|
if (llvm::any_of(var->getAllAccessors(),
|
|
[&](AccessorDecl *a) { return isLocalizable(a); }))
|
|
foundOne(var);
|
|
});
|
|
}
|
|
|
|
bool AbstractPatternEntryScope::isLastEntry() const {
|
|
return patternEntryIndex + 1 == decl->getPatternList().size();
|
|
}
|
|
|
|
// Following must be after uses to ensure templates get instantiated
|
|
#pragma mark getEnclosingAbstractStorageDecl
|
|
|
|
NullablePtr<AbstractStorageDecl>
|
|
ASTScopeImpl::getEnclosingAbstractStorageDecl() const {
|
|
return nullptr;
|
|
}
|
|
|
|
NullablePtr<AbstractStorageDecl>
|
|
SpecializeAttributeScope::getEnclosingAbstractStorageDecl() const {
|
|
return getParent().get()->getEnclosingAbstractStorageDecl();
|
|
}
|
|
NullablePtr<AbstractStorageDecl>
|
|
AbstractFunctionDeclScope::getEnclosingAbstractStorageDecl() const {
|
|
return getParent().get()->getEnclosingAbstractStorageDecl();
|
|
}
|
|
NullablePtr<AbstractStorageDecl>
|
|
ParameterListScope::getEnclosingAbstractStorageDecl() const {
|
|
return getParent().get()->getEnclosingAbstractStorageDecl();
|
|
}
|
|
NullablePtr<AbstractStorageDecl>
|
|
GenericParamScope::getEnclosingAbstractStorageDecl() const {
|
|
return getParent().get()->getEnclosingAbstractStorageDecl();
|
|
}
|
|
|
|
bool ASTScopeImpl::isATypeDeclScope() const {
|
|
Decl *const pd = getDeclIfAny().getPtrOrNull();
|
|
return pd && (isa<NominalTypeDecl>(pd) || isa<ExtensionDecl>(pd));
|
|
}
|
|
|
|
void ScopeCreator::forEachClosureIn(
|
|
Expr *expr, function_ref<void(NullablePtr<CaptureListExpr>, ClosureExpr *)>
|
|
foundClosure) {
|
|
assert(expr);
|
|
|
|
/// AST walker that finds top-level closures in an expression.
|
|
class ClosureFinder : public ASTWalker {
|
|
function_ref<void(NullablePtr<CaptureListExpr>, ClosureExpr *)>
|
|
foundClosure;
|
|
|
|
public:
|
|
ClosureFinder(
|
|
function_ref<void(NullablePtr<CaptureListExpr>, ClosureExpr *)>
|
|
foundClosure)
|
|
: foundClosure(foundClosure) {}
|
|
|
|
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
|
|
if (auto *closure = dyn_cast<ClosureExpr>(E)) {
|
|
foundClosure(nullptr, closure);
|
|
return {false, E};
|
|
}
|
|
if (auto *capture = dyn_cast<CaptureListExpr>(E)) {
|
|
foundClosure(capture, capture->getClosureBody());
|
|
return {false, E};
|
|
}
|
|
return {true, E};
|
|
}
|
|
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
|
|
if (auto *bs = dyn_cast<BraceStmt>(S)) { // closures hidden in here
|
|
return {true, S};
|
|
}
|
|
return {false, S};
|
|
}
|
|
std::pair<bool, Pattern *> walkToPatternPre(Pattern *P) override {
|
|
return {false, P};
|
|
}
|
|
bool walkToDeclPre(Decl *D) override { return false; }
|
|
bool walkToTypeLocPre(TypeLoc &TL) override { return false; }
|
|
bool walkToTypeReprPre(TypeRepr *T) override { return false; }
|
|
bool walkToParameterListPre(ParameterList *PL) override { return false; }
|
|
};
|
|
|
|
expr->walk(ClosureFinder(foundClosure));
|
|
}
|
|
|
|
#pragma mark new operators
|
|
void *ASTScopeImpl::operator new(size_t bytes, const ASTContext &ctx,
|
|
unsigned alignment) {
|
|
return ctx.Allocate(bytes, alignment);
|
|
}
|
|
|
|
void *Portion::operator new(size_t bytes, const ASTContext &ctx,
|
|
unsigned alignment) {
|
|
return ctx.Allocate(bytes, alignment);
|
|
}
|
|
void *ASTScope::operator new(size_t bytes, const ASTContext &ctx,
|
|
unsigned alignment) {
|
|
return ctx.Allocate(bytes, alignment);
|
|
}
|
|
void *ScopeCreator::operator new(size_t bytes, const ASTContext &ctx,
|
|
unsigned alignment) {
|
|
return ctx.Allocate(bytes, alignment);
|
|
}
|
|
|
|
#pragma mark - expandBody
|
|
|
|
void AbstractFunctionBodyScope::expandBody(ScopeCreator &scopeCreator) {
|
|
BraceStmt *braceStmt = decl->getBody();
|
|
if (braceStmt)
|
|
ASTVisitorForScopeCreation().visitBraceStmt(braceStmt, this, scopeCreator);
|
|
}
|
|
|
|
void GenericTypeOrExtensionScope::expandBody(ScopeCreator &) {}
|
|
|
|
void IterableTypeScope::expandBody(ScopeCreator &scopeCreator) {
|
|
auto nodes = asNodeVector(getIterableDeclContext().get()->getMembers());
|
|
// Revisit the "false" below, rdar://53321156
|
|
auto nodesIncludingIfConfigClauses =
|
|
scopeCreator.expandClausesSortAndCullElementsOrMembers(nodes);
|
|
// TODO: can we call addNodesToTree here instead of expand...?
|
|
for (auto n : nodesIncludingIfConfigClauses)
|
|
scopeCreator.createScopeFor(n, this);
|
|
}
|
|
|
|
#pragma mark - reexpandIfObsolete
|
|
|
|
bool ASTScopeImpl::reexpandIfObsolete(ScopeCreator &scopeCreator) {
|
|
if (isCurrent())
|
|
return false;
|
|
reexpand(scopeCreator);
|
|
return true;
|
|
}
|
|
|
|
void ASTScopeImpl::reexpand(ScopeCreator &scopeCreator) {
|
|
auto scopesToReuse = rescueScopesToReuse();
|
|
disownDescendants(scopeCreator);
|
|
expandAndBeCurrent(scopeCreator);
|
|
addReusedScopes(scopesToReuse);
|
|
}
|
|
|
|
#pragma mark getScopeCreator
|
|
ScopeCreator &ASTScopeImpl::getScopeCreator() {
|
|
return getParent().get()->getScopeCreator();
|
|
}
|
|
|
|
ScopeCreator &ASTSourceFileScope::getScopeCreator() { return *scopeCreator; }
|
|
|
|
#pragma mark getReferrent
|
|
|
|
// These are the scopes whose ASTNodes (etc) might be duplicated in the AST
|
|
// getReferrent is the cookie used to dedup them
|
|
|
|
#define GET_REFERRENT(Scope, x) \
|
|
NullablePtr<const void> Scope::getReferrent() const { \
|
|
return PtrCalc().visit(x); \
|
|
}
|
|
|
|
GET_REFERRENT(AbstractFunctionDeclScope, getDecl())
|
|
// If the PatternBindingDecl is a dup, detect it for the first
|
|
// PatternEntryDeclScope; the others are subscopes.
|
|
GET_REFERRENT(PatternEntryDeclScope, getPattern())
|
|
GET_REFERRENT(TopLevelCodeScope, getDecl())
|
|
GET_REFERRENT(SubscriptDeclScope, getDecl())
|
|
GET_REFERRENT(VarDeclScope, getDecl())
|
|
GET_REFERRENT(GenericParamScope, paramList->getParams()[index])
|
|
GET_REFERRENT(AbstractStmtScope, getStmt())
|
|
GET_REFERRENT(CaptureListScope, getExpr())
|
|
GET_REFERRENT(WholeClosureScope, getExpr())
|
|
GET_REFERRENT(SpecializeAttributeScope, specializeAttr)
|
|
GET_REFERRENT(GenericTypeOrExtensionScope, portion->getReferrentOfScope(this));
|
|
|
|
const Decl *
|
|
Portion::getReferrentOfScope(const GenericTypeOrExtensionScope *s) const {
|
|
return nullptr;
|
|
};
|
|
|
|
const Decl *GenericTypeOrExtensionWholePortion::getReferrentOfScope(
|
|
const GenericTypeOrExtensionScope *s) const {
|
|
return s->getDecl();
|
|
};
|
|
|
|
#undef GET_REFERRENT
|
|
|
|
#pragma mark currency
|
|
void ASTScopeImpl::beCurrent() {}
|
|
bool ASTScopeImpl::isCurrent() const { return true; }
|
|
|
|
void IterableTypeScope::beCurrent() { portion->beCurrent(this); }
|
|
bool IterableTypeScope::isCurrent() const { return portion->isCurrent(this); }
|
|
|
|
void Portion::beCurrent(IterableTypeScope *) const {}
|
|
bool Portion::isCurrent(const IterableTypeScope *) const { return true; }
|
|
|
|
void IterableTypeBodyPortion::beCurrent(IterableTypeScope *s) const {
|
|
s->makeBodyCurrent();
|
|
}
|
|
bool IterableTypeBodyPortion::isCurrent(const IterableTypeScope *s) const {
|
|
return s->isBodyCurrent();
|
|
}
|
|
|
|
void IterableTypeScope::makeBodyCurrent() {
|
|
memberCount = getIterableDeclContext().get()->getMemberCount();
|
|
}
|
|
bool IterableTypeScope::isBodyCurrent() const {
|
|
return memberCount == getIterableDeclContext().get()->getMemberCount();
|
|
}
|
|
|
|
void AbstractFunctionBodyScope::beCurrent() {
|
|
bodyWhenLastExpanded = decl->getBody();
|
|
}
|
|
bool AbstractFunctionBodyScope::isCurrent() const {
|
|
return bodyWhenLastExpanded == decl->getBody();
|
|
;
|
|
}
|
|
|
|
void TopLevelCodeScope::beCurrent() { bodyWhenLastExpanded = decl->getBody(); }
|
|
bool TopLevelCodeScope::isCurrent() const {
|
|
return bodyWhenLastExpanded == decl->getBody();
|
|
}
|
|
|
|
void PatternEntryDeclScope::beCurrent() {
|
|
initWhenLastExpanded = getPatternEntry().getOriginalInit();
|
|
unsigned varCount = 0;
|
|
getPatternEntry().getPattern()->forEachVariable(
|
|
[&](VarDecl *) { ++varCount; });
|
|
varCountWhenLastExpanded = 0;
|
|
}
|
|
bool PatternEntryDeclScope::isCurrent() const {
|
|
if (initWhenLastExpanded != getPatternEntry().getOriginalInit())
|
|
return false;
|
|
unsigned varCount = 0;
|
|
getPatternEntry().getPattern()->forEachVariable(
|
|
[&](VarDecl *) { ++varCount; });
|
|
return varCount == varCountWhenLastExpanded;
|
|
}
|
|
|
|
void WholeClosureScope::beCurrent() {
|
|
bodyWhenLastExpanded = closureExpr->getBody();
|
|
}
|
|
bool WholeClosureScope::isCurrent() const {
|
|
return bodyWhenLastExpanded == closureExpr->getBody();
|
|
}
|
|
|
|
std::vector<ASTScopeImpl *> ASTScopeImpl::rescueScopesToReuse() {
|
|
return rescueYoungestChildren(getChildren().size() -
|
|
childrenCountWhenLastExpanded);
|
|
}
|
|
void ASTScopeImpl::addReusedScopes(ArrayRef<ASTScopeImpl *> scopesToAdd) {
|
|
auto &ctx = getASTContext();
|
|
for (auto *s : scopesToAdd) {
|
|
addChild(s, ctx);
|
|
assert(s->verifyThatThisNodeComeAfterItsPriorSibling() &&
|
|
"Ensure search will work");
|
|
}
|
|
}
|
|
// TODO: factor abs fn body scope and top level code scope
|
|
std::vector<ASTScopeImpl *> AbstractFunctionBodyScope::rescueScopesToReuse() {
|
|
// retrieve the scopes from the body
|
|
if (getChildren().empty())
|
|
return {};
|
|
return getChildren().front()->rescueScopesToReuse();
|
|
}
|
|
void AbstractFunctionBodyScope::addReusedScopes(
|
|
ArrayRef<ASTScopeImpl *> scopesToAdd) {
|
|
// add reused scopes to the body
|
|
if (scopesToAdd.empty())
|
|
return;
|
|
getChildren().front()->addReusedScopes(scopesToAdd);
|
|
}
|
|
|
|
std::vector<ASTScopeImpl *> TopLevelCodeScope::rescueScopesToReuse() {
|
|
// retrieve the scopes from the body
|
|
if (getChildren().empty())
|
|
return {};
|
|
return getChildren().front()->rescueScopesToReuse();
|
|
}
|
|
void TopLevelCodeScope::addReusedScopes(ArrayRef<ASTScopeImpl *> scopesToAdd) {
|
|
// add reused scopes to the body
|
|
if (scopesToAdd.empty())
|
|
return;
|
|
getChildren().front()->addReusedScopes(scopesToAdd);
|
|
}
|
|
|
|
std::vector<ASTScopeImpl *>
|
|
ASTScopeImpl::rescueYoungestChildren(const unsigned int count) {
|
|
std::vector<ASTScopeImpl *> youngestChildren;
|
|
for (unsigned i = getChildren().size() - count; i < getChildren().size(); ++i)
|
|
youngestChildren.push_back(getChildren()[i]);
|
|
// So they don't get disowned and children cleared.
|
|
for (unsigned i = 0; i < count; ++i) {
|
|
storedChildren.back()->emancipate();
|
|
storedChildren.pop_back();
|
|
}
|
|
return youngestChildren;
|
|
}
|
|
|
|
void ASTScopeImpl::setChildrenCountWhenLastExpanded() {
|
|
childrenCountWhenLastExpanded = getChildren().size();
|
|
}
|
|
|
|
bool BraceStmtScope::shouldCreateScope(const BraceStmt *const bs) {
|
|
return isLocalizable(bs);
|
|
}
|
|
|
|
bool AbstractFunctionDeclScope::shouldCreateAccessorScope(
|
|
const AccessorDecl *const ad) {
|
|
return isLocalizable(ad);
|
|
}
|
|
|
|
#pragma mark verification
|
|
|
|
namespace {
|
|
class LocalizableDeclContextCollector : public ASTWalker {
|
|
|
|
public:
|
|
llvm::DenseMap<const DeclContext *, unsigned> declContexts;
|
|
|
|
void record(const DeclContext *dc) {
|
|
if (dc)
|
|
declContexts.insert({dc, 0});
|
|
}
|
|
|
|
bool walkToDeclPre(Decl *D) override {
|
|
// catchForDebugging(D, "DictionaryBridging.swift", 694);
|
|
if (const auto *dc = dyn_cast<DeclContext>(D))
|
|
record(dc);
|
|
if (auto *icd =
|
|
dyn_cast<IfConfigDecl>(D)) { // TODO: fix ASTWalker with option
|
|
walkToClauses(icd);
|
|
return false;
|
|
}
|
|
if (auto *pd = dyn_cast<ParamDecl>(D))
|
|
record(pd->getDefaultArgumentInitContext());
|
|
else if (auto *pbd = dyn_cast<PatternBindingDecl>(D))
|
|
recordInitializers(pbd);
|
|
else if (auto *vd = dyn_cast<VarDecl>(D))
|
|
for (auto *ad : vd->getAllAccessors())
|
|
ad->walk(*this);
|
|
return ASTWalker::walkToDeclPre(D);
|
|
}
|
|
|
|
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
|
|
if (const auto *ce = dyn_cast<ClosureExpr>(E))
|
|
record(ce);
|
|
return ASTWalker::walkToExprPre(E);
|
|
}
|
|
|
|
private:
|
|
void walkToClauses(IfConfigDecl *icd) {
|
|
for (auto &clause : icd->getClauses()) {
|
|
// Generate scopes for any closures in the condition
|
|
if (ScopeCreator::includeInactiveClausesOfIfConfigDecls &&
|
|
clause.isActive) {
|
|
if (clause.Cond)
|
|
clause.Cond->walk(*this);
|
|
for (auto n : clause.Elements)
|
|
n.walk(*this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void recordInitializers(PatternBindingDecl *pbd) {
|
|
for (auto entry : pbd->getPatternList())
|
|
record(entry.getInitContext());
|
|
}
|
|
|
|
void catchForDebugging(Decl *D, const char *file, const unsigned line) {
|
|
auto &SM = D->getASTContext().SourceMgr;
|
|
auto loc = D->getStartLoc();
|
|
if (!loc.isValid())
|
|
return;
|
|
auto bufID = SM.findBufferContainingLoc(loc);
|
|
auto f = SM.getIdentifierForBuffer(bufID);
|
|
auto lin = SM.getLineNumber(loc);
|
|
if (f.endswith(file) && lin == line)
|
|
if (auto *v = dyn_cast<PatternBindingDecl>(D))
|
|
llvm::errs() << "HERE catchForDebugging: " << lin << "\n";
|
|
}
|
|
};
|
|
} // end namespace
|
|
|
|
llvm::DenseMap<const DeclContext *, unsigned>
|
|
ScopeCreator::findLocalizableDeclContextsInAST() const {
|
|
LocalizableDeclContextCollector collector;
|
|
sourceFileScope->SF->walk(collector);
|
|
// Walker omits the top
|
|
collector.record(sourceFileScope->SF);
|
|
return collector.declContexts;
|
|
}
|
|
|
|
bool ASTSourceFileScope::crossCheckWithAST() {
|
|
return scopeCreator->containsAllDeclContextsFromAST();
|
|
}
|