Files
swift-mirror/lib/AST/ASTScopeSourceRange.cpp
Slava Pestov 4f1ab9d8e5 ASTScope: Directly calculate end location of GuardStmtScope and PatternEntryDeclScope
Today, the reported source range of a GuardStmtScope is just that of
the statement itself, ending after the 'else' block. Similarly, a
PatternEntryDeclScope ends immediately after the initializer
expression.

Since these scopes introduce names until the end of the parent
BraceStmt (meaning they change the insertion point), we were
relying on the addition of child scopes to extend the source
range.

While we still need the hack for extending source ranges to deal
with string interpolation, at least in the other cases we can
get rid of it.

Note that this fixes a bug where a GuardStmtScope would end
before an inactive '#if' block if the inactive '#if' block was
the last element of a BraceStmt.
2020-10-02 23:49:59 -04:00

725 lines
26 KiB
C++

//===--- ASTScopeSourceRange.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 source range queries for the ASTScopeImpl ontology.
///
//===----------------------------------------------------------------------===//
#include "swift/AST/ASTScope.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Expr.h"
#include "swift/AST/GenericParamList.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/SourceFile.h"
#include "swift/AST/Stmt.h"
#include "swift/AST/TypeRepr.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Parse/Lexer.h"
#include "llvm/Support/Compiler.h"
#include <algorithm>
using namespace swift;
using namespace ast_scope;
static SourceLoc getLocEncompassingPotentialLookups(const SourceManager &,
SourceLoc endLoc);
static SourceLoc getLocAfterExtendedNominal(const ExtensionDecl *);
SourceRange ASTScopeImpl::widenSourceRangeForIgnoredASTNodes(
const SourceRange range) const {
if (range.isInvalid())
return sourceRangeOfIgnoredASTNodes;
auto r = range;
if (sourceRangeOfIgnoredASTNodes.isValid())
r.widen(sourceRangeOfIgnoredASTNodes);
return r;
}
SourceRange
ASTScopeImpl::widenSourceRangeForChildren(const SourceRange range,
const bool omitAssertions) const {
if (getChildren().empty()) {
ASTScopeAssert(omitAssertions || range.Start.isValid(), "Bad range.");
return range;
}
const auto childStart =
getChildren().front()->getSourceRangeOfScope(omitAssertions).Start;
const auto childEnd =
getChildren().back()->getSourceRangeOfScope(omitAssertions).End;
auto childRange = SourceRange(childStart, childEnd);
ASTScopeAssert(omitAssertions || childRange.isValid(), "Bad range.");
if (range.isInvalid())
return childRange;
auto r = range;
// HACK: For code completion. If the range of the child is from another
// source buffer, don't widen using that range.
if (const auto &replacedRange = getSourceManager().getReplacedRange()) {
if (getSourceManager().rangeContains(replacedRange.Original, range) &&
getSourceManager().rangeContains(replacedRange.New, childRange))
return r;
}
r.widen(childRange);
return r;
}
bool ASTScopeImpl::checkSourceRangeAfterExpansion(const ASTContext &ctx) const {
ASTScopeAssert(getSourceRangeOfThisASTNode().isValid() ||
!getChildren().empty(),
"need to be able to find source range");
ASTScopeAssert(verifyThatChildrenAreContainedWithin(getSourceRangeOfScope()),
"Search will fail");
ASTScopeAssert(
checkLazySourceRange(ctx),
"Lazy scopes must have compatible ranges before and after expansion");
return true;
}
#pragma mark validation & debugging
bool ASTScopeImpl::hasValidSourceRange() const {
const auto sourceRange = getSourceRangeOfScope();
return sourceRange.Start.isValid() && sourceRange.End.isValid() &&
!getSourceManager().isBeforeInBuffer(sourceRange.End,
sourceRange.Start);
}
bool ASTScopeImpl::hasValidSourceRangeOfIgnoredASTNodes() const {
return sourceRangeOfIgnoredASTNodes.isValid();
}
bool ASTScopeImpl::precedesInSource(const ASTScopeImpl *next) const {
if (!hasValidSourceRange() || !next->hasValidSourceRange())
return false;
return !getSourceManager().isBeforeInBuffer(
next->getSourceRangeOfScope().Start, getSourceRangeOfScope().End);
}
bool ASTScopeImpl::verifyThatChildrenAreContainedWithin(
const SourceRange range) const {
// assumes children are already in order
if (getChildren().empty())
return true;
const SourceRange rangeOfChildren =
SourceRange(getChildren().front()->getSourceRangeOfScope().Start,
getChildren().back()->getSourceRangeOfScope().End);
if (getSourceManager().rangeContains(range, rangeOfChildren))
return true;
// HACK: For code completion. Handle replaced range.
if (const auto &replacedRange = getSourceManager().getReplacedRange()) {
if (getSourceManager().rangeContains(replacedRange.Original, range) &&
getSourceManager().rangeContains(replacedRange.New, rangeOfChildren))
return true;
}
auto &out = verificationError() << "children not contained in its parent\n";
if (getChildren().size() == 1) {
out << "\n***Only Child node***\n";
getChildren().front()->print(out);
} else {
out << "\n***First Child node***\n";
getChildren().front()->print(out);
out << "\n***Last Child node***\n";
getChildren().back()->print(out);
}
out << "\n***Parent node***\n";
this->print(out);
abort();
}
bool ASTScopeImpl::verifyThatThisNodeComeAfterItsPriorSibling() const {
auto priorSibling = getPriorSibling();
if (!priorSibling)
return true;
if (priorSibling.get()->precedesInSource(this))
return true;
auto &out = verificationError() << "unexpected out-of-order nodes\n";
out << "\n***Penultimate child node***\n";
priorSibling.get()->print(out);
out << "\n***Last Child node***\n";
print(out);
out << "\n***Parent node***\n";
getParent().get()->print(out);
// llvm::errs() << "\n\nsource:\n"
// << getSourceManager()
// .getRangeForBuffer(
// getSourceFile()->getBufferID().getValue())
// .str();
ASTScope_unreachable("unexpected out-of-order nodes");
return false;
}
NullablePtr<ASTScopeImpl> ASTScopeImpl::getPriorSibling() const {
auto parent = getParent();
if (!parent)
return nullptr;
auto const &siblingsAndMe = parent.get()->getChildren();
// find myIndex, which is probably the last one
int myIndex = -1;
for (int i = siblingsAndMe.size() - 1; i >= 0; --i) {
if (siblingsAndMe[i] == this) {
myIndex = i;
break;
}
}
ASTScopeAssert(myIndex != -1, "I have been disowned!");
if (myIndex == 0)
return nullptr;
return siblingsAndMe[myIndex - 1];
}
bool ASTScopeImpl::doesRangeMatch(unsigned start, unsigned end, StringRef file,
StringRef className) {
if (!className.empty() && className != getClassName())
return false;
const auto &SM = getSourceManager();
const auto r = getSourceRangeOfScope(true);
if (start && start != SM.getLineAndColumnInBuffer(r.Start).first)
return false;
if (end && end != SM.getLineAndColumnInBuffer(r.End).first)
return false;
if (file.empty())
return true;
const auto buf = SM.findBufferContainingLoc(r.Start);
return SM.getIdentifierForBuffer(buf).endswith(file);
}
#pragma mark getSourceRangeOfThisASTNode
SourceRange SpecializeAttributeScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
return specializeAttr->getRange();
}
SourceRange DifferentiableAttributeScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
return differentiableAttr->getRange();
}
SourceRange FunctionBodyScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
return decl->getOriginalBodySourceRange();
}
SourceRange TopLevelCodeScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
return decl->getSourceRange();
}
SourceRange SubscriptDeclScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
return decl->getSourceRange();
}
SourceRange
EnumElementScope::getSourceRangeOfThisASTNode(const bool omitAssertions) const {
return decl->getSourceRange();
}
SourceRange AbstractStmtScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
return getStmt()->getSourceRange();
}
SourceRange DefaultArgumentInitializerScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
if (auto *dv = decl->getStructuralDefaultExpr())
return dv->getSourceRange();
return SourceRange();
}
SourceRange PatternEntryDeclScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
SourceRange range = getPatternEntry().getSourceRange();
if (endLoc.hasValue()) {
ASTScopeAssert(endLoc->isValid(),
"BraceStmt ends before pattern binding entry?");
range.End = *endLoc;
}
return range;
}
SourceRange PatternEntryInitializerScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
// TODO: Don't remove the initializer in the rest of the compiler:
// Search for "When the initializer is removed we don't actually clear the
// pointer" because we do!
return initAsWrittenWhenCreated->getSourceRange();
}
SourceRange GenericParamScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
auto nOrE = holder;
// A protocol's generic parameter list is not written in source, and
// is visible from the start of the body.
if (auto *protoDecl = dyn_cast<ProtocolDecl>(nOrE))
return SourceRange(protoDecl->getBraces().Start, protoDecl->getEndLoc());
const auto startLoc = paramList->getSourceRange().Start;
const auto validStartLoc =
startLoc.isValid() ? startLoc : holder->getStartLoc();
// Since ExtensionScope (whole portion) range doesn't start till after the
// extended nominal, the range here must be pushed back, too.
if (auto const *const ext = dyn_cast<ExtensionDecl>(holder)) {
return SourceRange(getLocAfterExtendedNominal(ext), ext->getEndLoc());
}
return SourceRange(validStartLoc, holder->getEndLoc());
}
SourceRange ASTSourceFileScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
if (auto bufferID = SF->getBufferID()) {
auto charRange = getSourceManager().getRangeForBuffer(*bufferID);
return SourceRange(charRange.getStart(), charRange.getEnd());
}
if (SF->getTopLevelDecls().empty())
return SourceRange();
// Use the source ranges of the declarations in the file.
return SourceRange(SF->getTopLevelDecls().front()->getStartLoc(),
SF->getTopLevelDecls().back()->getEndLoc());
}
SourceRange GenericTypeOrExtensionScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
return portion->getChildlessSourceRangeOf(this, omitAssertions);
}
SourceRange GenericTypeOrExtensionWholePortion::getChildlessSourceRangeOf(
const GenericTypeOrExtensionScope *scope, const bool omitAssertions) const {
auto *d = scope->getDecl();
auto r = d->getSourceRangeIncludingAttrs();
if (r.Start.isValid()) {
ASTScopeAssert(r.End.isValid(), "Start valid imples end valid.");
return scope->moveStartPastExtendedNominal(r);
}
return d->getSourceRange();
}
SourceRange
ExtensionScope::moveStartPastExtendedNominal(const SourceRange sr) const {
const auto afterExtendedNominal = getLocAfterExtendedNominal(decl);
// Illegal code can have an endLoc that is before the end of the
// ExtendedNominal
if (getSourceManager().isBeforeInBuffer(sr.End, afterExtendedNominal)) {
// Must not have the source range returned include the extended nominal
return SourceRange(sr.Start, sr.Start);
}
return SourceRange(afterExtendedNominal, sr.End);
}
SourceRange
GenericTypeScope::moveStartPastExtendedNominal(const SourceRange sr) const {
// There is no extended nominal
return sr;
}
SourceRange GenericTypeOrExtensionWherePortion::getChildlessSourceRangeOf(
const GenericTypeOrExtensionScope *scope, const bool omitAssertions) const {
return scope->getGenericContext()->getTrailingWhereClause()->getSourceRange();
}
SourceRange IterableTypeBodyPortion::getChildlessSourceRangeOf(
const GenericTypeOrExtensionScope *scope, const bool omitAssertions) const {
auto *d = scope->getDecl();
if (auto *nt = dyn_cast<NominalTypeDecl>(d))
return nt->getBraces();
if (auto *e = dyn_cast<ExtensionDecl>(d))
return e->getBraces();
if (omitAssertions)
return SourceRange();
ASTScope_unreachable("No body!");
}
SourceRange AbstractFunctionDeclScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
// For a get/put accessor all of the parameters are implicit, so start
// them at the start location of the accessor.
auto r = decl->getSourceRangeIncludingAttrs();
if (r.Start.isValid()) {
ASTScopeAssert(r.End.isValid(), "Start valid imples end valid.");
return r;
}
return decl->getOriginalBodySourceRange();
}
SourceRange ParameterListScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
auto rangeForGoodInput = params->getSourceRange();
auto r = SourceRange(rangeForGoodInput.Start,
fixupEndForBadInput(rangeForGoodInput));
ASTScopeAssert(getSourceManager().rangeContains(
getParent().get()->getSourceRangeOfThisASTNode(true), r),
"Parameters not within function?!");
return r;
}
SourceLoc ParameterListScope::fixupEndForBadInput(
const SourceRange rangeForGoodInput) const {
const auto s = rangeForGoodInput.Start;
const auto e = rangeForGoodInput.End;
return getSourceManager().isBeforeInBuffer(s, e) ? e : s;
}
SourceRange ForEachPatternScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
// The scope of the pattern extends from the 'where' expression (if present)
// until the end of the body.
if (stmt->getWhere())
return SourceRange(stmt->getWhere()->getStartLoc(),
stmt->getBody()->getEndLoc());
// Otherwise, scope of the pattern covers the body.
return stmt->getBody()->getSourceRange();
}
SourceRange
CaseLabelItemScope::getSourceRangeOfThisASTNode(const bool omitAssertions) const {
return item.getGuardExpr()->getSourceRange();
}
SourceRange
CaseStmtBodyScope::getSourceRangeOfThisASTNode(const bool omitAssertions) const {
return stmt->getBody()->getSourceRange();
}
SourceRange
BraceStmtScope::getSourceRangeOfThisASTNode(const bool omitAssertions) const {
// The brace statements that represent closures start their scope at the
// 'in' keyword, when present.
if (auto closure = parentClosureIfAny()) {
if (closure.get()->getInLoc().isValid())
return SourceRange(closure.get()->getInLoc(), stmt->getEndLoc());
}
return stmt->getSourceRange();
}
SourceRange ConditionalClauseScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
// From the start of this particular condition to the start of the
// then/body part.
const auto startLoc = getStmtConditionElement().getStartLoc();
return startLoc.isValid()
? SourceRange(startLoc, endLoc)
: SourceRange(endLoc);
}
SourceRange ConditionalClausePatternUseScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
// For a guard continuation, the scope extends from the end of the 'else'
// to the end of the continuation.
return SourceRange(startLoc);
}
SourceRange
CaptureListScope::getSourceRangeOfThisASTNode(const bool omitAssertions) const {
auto *closureExpr = expr->getClosureBody();
if (!omitAssertions)
ASTScopeAssert(closureExpr->getInLoc().isValid(),
"We don't create these if no in loc");
return SourceRange(closureExpr->getInLoc(), closureExpr->getEndLoc());
}
SourceRange ClosureParametersScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
if (closureExpr->getInLoc().isValid())
return SourceRange(closureExpr->getInLoc(), closureExpr->getEndLoc());
return closureExpr->getSourceRange();
}
SourceRange AttachedPropertyWrapperScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
return sourceRangeWhenCreated;
}
SourceRange GuardStmtScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
return SourceRange(getStmt()->getStartLoc(), endLoc);
}
SourceRange LookupParentDiversionScope::getSourceRangeOfThisASTNode(
const bool omitAssertions) const {
return SourceRange(startLoc, endLoc);
}
#pragma mark source range caching
SourceRange
ASTScopeImpl::getSourceRangeOfScope(const bool omitAssertions) const {
if (!isSourceRangeCached(omitAssertions))
computeAndCacheSourceRangeOfScope(omitAssertions);
return *cachedSourceRange;
}
bool ASTScopeImpl::isSourceRangeCached(const bool omitAssertions) const {
const bool isCached = cachedSourceRange.hasValue();
ASTScopeAssert(omitAssertions || isCached ||
ensureNoAncestorsSourceRangeIsCached(),
"Cached ancestor's range likely is obsolete.");
return isCached;
}
bool ASTScopeImpl::ensureNoAncestorsSourceRangeIsCached() const {
if (const auto *const p = getParent().getPtrOrNull()) {
auto r = !p->isSourceRangeCached(true) &&
p->ensureNoAncestorsSourceRangeIsCached();
if (!r)
ASTScope_unreachable("found a violation");
return true;
}
return true;
}
void ASTScopeImpl::computeAndCacheSourceRangeOfScope(
const bool omitAssertions) const {
// In order to satisfy the invariant that, if my range is uncached,
// my parent's range is uncached, (which is needed to optimize invalidation
// by obviating the need to uncache all the way to the root every time),
// when caching a range, must ensure all children's ranges are cached.
for (auto *c : getChildren())
c->computeAndCacheSourceRangeOfScope(omitAssertions);
cachedSourceRange = computeSourceRangeOfScope(omitAssertions);
}
bool ASTScopeImpl::checkLazySourceRange(const ASTContext &ctx) const {
const auto unexpandedRange = sourceRangeForDeferredExpansion();
const auto expandedRange = computeSourceRangeOfScopeWithChildASTNodes();
if (unexpandedRange.isInvalid() || expandedRange.isInvalid())
return true;
if (unexpandedRange == expandedRange)
return true;
llvm::errs() << "*** Lazy range problem. Parent unexpanded: ***\n";
unexpandedRange.print(llvm::errs(), getSourceManager(), false);
llvm::errs() << "\n";
if (!getChildren().empty()) {
llvm::errs() << "*** vs last child: ***\n";
auto b = getChildren().back()->computeSourceRangeOfScope();
b.print(llvm::errs(), getSourceManager(), false);
llvm::errs() << "\n";
}
else if (hasValidSourceRangeOfIgnoredASTNodes()) {
llvm::errs() << "*** vs ignored AST nodes: ***\n";
sourceRangeOfIgnoredASTNodes.print(llvm::errs(), getSourceManager(), false);
llvm::errs() << "\n";
}
print(llvm::errs(), 0, false);
llvm::errs() << "\n";
return false;
}
SourceRange
ASTScopeImpl::computeSourceRangeOfScope(const bool omitAssertions) const {
// If we don't need to consider children, it's cheaper
const auto deferredRange = sourceRangeForDeferredExpansion();
return deferredRange.isValid()
? deferredRange
: computeSourceRangeOfScopeWithChildASTNodes(omitAssertions);
}
SourceRange ASTScopeImpl::computeSourceRangeOfScopeWithChildASTNodes(
const bool omitAssertions) const {
const auto rangeOfJustThisASTNode =
getSourceRangeOfThisASTNode(omitAssertions);
const auto rangeIncludingIgnoredNodes =
widenSourceRangeForIgnoredASTNodes(rangeOfJustThisASTNode);
const auto uncachedSourceRange =
widenSourceRangeForChildren(rangeIncludingIgnoredNodes, omitAssertions);
return uncachedSourceRange;
}
void ASTScopeImpl::clearCachedSourceRangesOfMeAndAncestors() {
// An optimization: if my range isn't cached, my ancestors must not be
if (!isSourceRangeCached())
return;
cachedSourceRange = None;
if (auto p = getParent())
p.get()->clearCachedSourceRangesOfMeAndAncestors();
}
#pragma mark compensating for InterpolatedStringLiteralExprs and EditorPlaceHolders
static bool isInterpolatedStringLiteral(const Token& tok) {
SmallVector<Lexer::StringSegment, 1> Segments;
Lexer::getStringLiteralSegments(tok, Segments, nullptr);
return Segments.size() != 1 ||
Segments.front().Kind != Lexer::StringSegment::Literal;
}
/// If right brace is missing, the source range of the body will end
/// at the last token, which may be a one of the special cases below.
/// This work is only needed for *unexpanded* scopes because unioning the range
/// with the children will do the same thing for an expanded scope.
/// It is also needed for ignored \c ASTNodes, which may be, e.g. \c
/// InterpolatedStringLiterals
static SourceLoc getLocEncompassingPotentialLookups(const SourceManager &SM,
const SourceLoc endLoc) {
const auto tok = Lexer::getTokenAtLocation(SM, endLoc);
switch (tok.getKind()) {
default:
return endLoc;
case tok::string_literal:
if (!isInterpolatedStringLiteral(tok))
return endLoc; // Just the start of the last token
break;
case tok::identifier:
// subtract one to get a closed-range endpoint from a half-open
if (!Identifier::isEditorPlaceholder(tok.getText()))
return endLoc;
break;
}
return tok.getRange().getEnd().getAdvancedLoc(-1);
}
SourceRange ASTScopeImpl::sourceRangeForDeferredExpansion() const {
return SourceRange();
}
SourceRange IterableTypeScope::sourceRangeForDeferredExpansion() const {
return portion->sourceRangeForDeferredExpansion(this);
}
SourceRange FunctionBodyScope::sourceRangeForDeferredExpansion() const {
const auto bsr = decl->getOriginalBodySourceRange();
const SourceLoc endEvenIfNoCloseBraceAndEndsWithInterpolatedStringLiteral =
getLocEncompassingPotentialLookups(getSourceManager(), bsr.End);
return SourceRange(bsr.Start,
endEvenIfNoCloseBraceAndEndsWithInterpolatedStringLiteral);
}
SourceRange GenericTypeOrExtensionWholePortion::sourceRangeForDeferredExpansion(
const IterableTypeScope *s) const {
const auto rangeOfThisNodeWithoutChildren =
getChildlessSourceRangeOf(s, false);
const auto rangeExtendedForFinalToken = SourceRange(
rangeOfThisNodeWithoutChildren.Start,
getLocEncompassingPotentialLookups(s->getSourceManager(),
rangeOfThisNodeWithoutChildren.End));
const auto rangePastExtendedNominal =
s->moveStartPastExtendedNominal(rangeExtendedForFinalToken);
return rangePastExtendedNominal;
}
SourceRange GenericTypeOrExtensionWherePortion::sourceRangeForDeferredExpansion(
const IterableTypeScope *) const {
return SourceRange();
}
SourceRange IterableTypeBodyPortion::sourceRangeForDeferredExpansion(
const IterableTypeScope *s) const {
const auto bracesRange = getChildlessSourceRangeOf(s, false);
return SourceRange(bracesRange.Start,
getLocEncompassingPotentialLookups(s->getSourceManager(),
bracesRange.End));
}
SourceRange ASTScopeImpl::getEffectiveSourceRange(const ASTNode n) const {
if (const auto *d = n.dyn_cast<Decl *>())
return d->getSourceRange();
if (const auto *s = n.dyn_cast<Stmt *>())
return s->getSourceRange();
auto *e = n.dyn_cast<Expr *>();
return getLocEncompassingPotentialLookups(getSourceManager(), e->getEndLoc());
}
/// Some nodes (e.g. the error expression) cannot possibly contain anything to
/// be looked up and if included in a parent scope's source range would expand
/// it beyond an ancestor's source range. But if the ancestor is expanded
/// lazily, we check that its source range does not change when expanding it,
/// and this check would fail.
static bool sourceRangeWouldInterfereWithLaziness(const ASTNode n) {
return n.isExpr(ExprKind::Error);
}
static bool
shouldIgnoredASTNodeSourceRangeWidenEnclosingScope(const ASTNode n) {
if (n.isDecl(DeclKind::Var)) {
// The pattern scopes will include the source ranges for VarDecls.
// Using its range here would cause a pattern initializer scope's range
// to overlap the pattern use scope's range.
return false;
}
if (sourceRangeWouldInterfereWithLaziness(n))
return false;
return true;
}
void ASTScopeImpl::widenSourceRangeForIgnoredASTNode(const ASTNode n) {
if (!shouldIgnoredASTNodeSourceRangeWidenEnclosingScope(n))
return;
// FIXME: why only do effectiveness bit for *ignored* nodes?
SourceRange r = getEffectiveSourceRange(n);
if (r.isInvalid())
return;
if (sourceRangeOfIgnoredASTNodes.isInvalid())
sourceRangeOfIgnoredASTNodes = r;
else
sourceRangeOfIgnoredASTNodes.widen(r);
}
SourceLoc getLocAfterExtendedNominal(const ExtensionDecl *const ext) {
const auto *const etr = ext->getExtendedTypeRepr();
if (!etr)
return ext->getStartLoc();
const auto &SM = ext->getASTContext().SourceMgr;
return Lexer::getCharSourceRangeFromSourceRange(SM, etr->getSourceRange())
.getEnd();
}
SourceLoc ast_scope::extractNearestSourceLoc(
std::tuple<ASTScopeImpl *, ScopeCreator *> scopeAndCreator) {
const ASTScopeImpl *scope = std::get<0>(scopeAndCreator);
return scope->getSourceRangeOfThisASTNode().Start;
}
int ASTScopeImpl::compare(const SourceRange lhs, const SourceRange rhs,
const SourceManager &SM, const bool ensureDisjoint) {
ASTScopeAssert(!SM.isBeforeInBuffer(lhs.End, lhs.Start),
"Range is backwards.");
ASTScopeAssert(!SM.isBeforeInBuffer(rhs.End, rhs.Start),
"Range is backwards.");
auto cmpLoc = [&](const SourceLoc lhs, const SourceLoc rhs) {
return lhs == rhs ? 0 : SM.isBeforeInBuffer(lhs, rhs) ? -1 : 1;
};
// Establish that we use end locations throughout ASTScopes here
const int endOrder = cmpLoc(lhs.End, rhs.End);
#ifndef NDEBUG
if (ensureDisjoint) {
const int startOrder = cmpLoc(lhs.Start, rhs.Start);
if (startOrder * endOrder == -1) {
llvm::errs() << "*** Start order contradicts end order between: ***\n";
lhs.print(llvm::errs(), SM, false);
llvm::errs() << "\n*** and: ***\n";
rhs.print(llvm::errs(), SM, false);
}
ASTScopeAssert(startOrder * endOrder != -1,
"Start order contradicts end order");
}
#endif
return endOrder;
}