| //===--- 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/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 getStartOfFirstParam(ClosureExpr *closure); |
| 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; |
| 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; |
| 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.getLineNumber(r.Start)) |
| return false; |
| if (end && end != SM.getLineNumber(r.End)) |
| 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(); |
| } |
| |
| // SWIFT_ENABLE_TENSORFLOW |
| SourceRange DifferentiableAttributeScope::getSourceRangeOfThisASTNode( |
| const bool omitAssertions) const { |
| return differentiableAttr->getRange(); |
| } |
| // SWIFT_ENABLE_TENSORFLOW END |
| |
| SourceRange AbstractFunctionBodyScope::getSourceRangeOfThisASTNode( |
| const bool omitAssertions) const { |
| return decl->getBodySourceRange(); |
| } |
| |
| 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 WholeClosureScope::getSourceRangeOfThisASTNode( |
| const bool omitAssertions) const { |
| return closureExpr->getSourceRange(); |
| } |
| |
| SourceRange AbstractStmtScope::getSourceRangeOfThisASTNode( |
| const bool omitAssertions) const { |
| return getStmt()->getSourceRange(); |
| } |
| |
| SourceRange DefaultArgumentInitializerScope::getSourceRangeOfThisASTNode( |
| const bool omitAssertions) const { |
| if (auto *dv = decl->getDefaultValue()) |
| return dv->getSourceRange(); |
| return SourceRange(); |
| } |
| |
| SourceRange PatternEntryDeclScope::getSourceRangeOfThisASTNode( |
| const bool omitAssertions) const { |
| // TODO: Once rdar://53627317 is accomplished, the following may be able to be |
| // simplified. |
| if (!getChildren().empty()) { // why needed??? |
| bool hasOne = false; |
| getPattern()->forEachVariable([&](VarDecl *) { hasOne = true; }); |
| if (!hasOne) |
| return SourceRange(); // just the init |
| if (!getPatternEntry().getInit()) |
| return SourceRange(); // just the var decls |
| } |
| return getPatternEntry().getSourceRange(); |
| } |
| |
| SourceRange PatternEntryInitializerScope::getSourceRangeOfThisASTNode( |
| const bool omitAssertions) const { |
| // See rdar://53921703 |
| // Note: grep for "When the initializer is removed we don't actually clear the |
| // pointer" because we do! |
| return initAsWrittenWhenCreated->getSourceRange(); |
| } |
| |
| SourceRange |
| VarDeclScope::getSourceRangeOfThisASTNode(const bool omitAssertions) const { |
| const auto br = decl->getBracesRange(); |
| return br.isValid() ? br : decl->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->Decls.empty()) |
| return SourceRange(); |
| |
| // Use the source ranges of the declarations in the file. |
| return SourceRange(SF->Decls.front()->getStartLoc(), |
| SF->Decls.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 start = getLocAfterExtendedNominal(decl); |
| // Illegal code can have an endLoc that is before the end of the |
| // ExtendedNominal, so push the end back, too, in that case. |
| const auto end = |
| getSourceManager().isBeforeInBuffer(sr.End, start) ? start : sr.End; |
| return SourceRange(start, 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->getBodySourceRange(); |
| } |
| |
| SourceRange ParameterListScope::getSourceRangeOfThisASTNode( |
| const bool omitAssertions) const { |
| const auto rangeForGoodInput = |
| getSourceRangeOfEnclosedParamsOfASTNode(omitAssertions); |
| 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 |
| CatchStmtScope::getSourceRangeOfThisASTNode(const bool omitAssertions) const { |
| // The scope of the pattern extends from the 'where' (if present) |
| // to the end of the body. |
| if (stmt->getGuardExpr()) |
| return SourceRange(stmt->getWhereLoc(), stmt->getBody()->getEndLoc()); |
| |
| // Otherwise, the scope of the pattern encompasses the body. |
| return stmt->getBody()->getSourceRange(); |
| } |
| SourceRange |
| CaseStmtScope::getSourceRangeOfThisASTNode(const bool omitAssertions) const { |
| // The scope of the case statement begins at the first guard expression, |
| // if there is one, and extends to the end of the body. |
| // FIXME: Figure out what to do about multiple pattern bindings. We might |
| // want a more restrictive rule in those cases. |
| for (const auto &caseItem : stmt->getCaseLabelItems()) { |
| if (auto guardExpr = caseItem.getGuardExpr()) |
| return SourceRange(guardExpr->getStartLoc(), |
| stmt->getBody()->getEndLoc()); |
| } |
| |
| // Otherwise, it covers the body. |
| return stmt->getBody() |
| ->getSourceRange(); // The scope of the case statement begins |
| } |
| |
| 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 *const closure = expr->getClosureBody(); |
| return SourceRange(expr->getStartLoc(), getStartOfFirstParam(closure)); |
| } |
| |
| SourceRange ClosureParametersScope::getSourceRangeOfThisASTNode( |
| const bool omitAssertions) const { |
| if (!omitAssertions) |
| ASTScopeAssert(closureExpr->getInLoc().isValid(), |
| "We don't create these if no in loc"); |
| return SourceRange(getStartOfFirstParam(closureExpr), |
| closureExpr->getInLoc()); |
| } |
| |
| SourceRange |
| ClosureBodyScope::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 LookupParentDiversionScope::getSourceRangeOfThisASTNode( |
| const bool omitAssertions) const { |
| return SourceRange(startLoc); |
| } |
| |
| #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 { |
| if (!ctx.LangOpts.LazyASTScopes) |
| return true; |
| 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 AbstractFunctionBodyScope::sourceRangeForDeferredExpansion() const { |
| const auto bsr = decl->getBodySourceRange(); |
| 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); |
| } |
| |
| static SourceLoc getStartOfFirstParam(ClosureExpr *closure) { |
| if (auto *parms = closure->getParameters()) { |
| if (parms->size()) |
| return parms->get(0)->getStartLoc(); |
| } |
| if (closure->getInLoc().isValid()) |
| return closure->getInLoc(); |
| if (closure->getBody()) |
| return closure->getBody()->getLBraceLoc(); |
| return closure->getStartLoc(); |
| } |
| |
| #pragma mark getSourceRangeOfEnclosedParamsOfASTNode |
| |
| SourceRange ASTScopeImpl::getSourceRangeOfEnclosedParamsOfASTNode( |
| const bool omitAssertions) const { |
| return getParent().get()->getSourceRangeOfEnclosedParamsOfASTNode( |
| omitAssertions); |
| } |
| |
| SourceRange EnumElementScope::getSourceRangeOfEnclosedParamsOfASTNode( |
| bool omitAssertions) const { |
| auto *pl = decl->getParameterList(); |
| return pl ? pl->getSourceRange() : SourceRange(); |
| } |
| |
| SourceRange SubscriptDeclScope::getSourceRangeOfEnclosedParamsOfASTNode( |
| const bool omitAssertions) const { |
| auto r = SourceRange(decl->getIndices()->getLParenLoc(), decl->getEndLoc()); |
| // Because of "subscript(x: MyStruct#^PARAM_1^#) -> Int { return 0 }" |
| // Cannot just use decl->getEndLoc() |
| r.widen(decl->getIndices()->getRParenLoc()); |
| return r; |
| } |
| |
| SourceRange AbstractFunctionDeclScope::getSourceRangeOfEnclosedParamsOfASTNode( |
| const bool omitAssertions) const { |
| const auto s = getParmsSourceLocOfAFD(decl); |
| const auto e = getSourceRangeOfThisASTNode(omitAssertions).End; |
| return s.isInvalid() || e.isInvalid() ? SourceRange() : SourceRange(s, e); |
| } |
| |
| SourceLoc |
| AbstractFunctionDeclScope::getParmsSourceLocOfAFD(AbstractFunctionDecl *decl) { |
| if (auto *c = dyn_cast<ConstructorDecl>(decl)) |
| return c->getParameters()->getLParenLoc(); |
| |
| if (auto *dd = dyn_cast<DestructorDecl>(decl)) |
| return dd->getNameLoc(); |
| |
| auto *fd = cast<FuncDecl>(decl); |
| // clang-format off |
| return isa<AccessorDecl>(fd) ? fd->getLoc() |
| : fd->isDeferBody() ? fd->getNameLoc() |
| : fd->getParameters()->getLParenLoc(); |
| // clang-format on |
| } |
| |
| 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(); |
| } |