blob: e2215eabddfdb372ecc64f7771dff6d9a5458763 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "swift/AST/ASTPrinter.h"
#include "swift/AST/Decl.h"
#include "swift/AST/NameLookup.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/IDE/CommentConversion.h"
#include "swift/IDE/Utils.h"
#include "swift/Markup/XMLUtils.h"
#include "swift/Subsystems.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/Module.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Lex/Lexer.h"
#include "clang/Basic/CharInfo.h"
#include "llvm/Support/MemoryBuffer.h"
#include <numeric>
using namespace swift;
using namespace swift::ide;
Optional<std::pair<unsigned, unsigned>>
swift::ide::parseLineCol(StringRef LineCol) {
unsigned Line, Col;
size_t ColonIdx = LineCol.find(':');
if (ColonIdx == StringRef::npos) {
llvm::errs() << "wrong pos format, it should be '<line>:<column>'\n";
return None;
}
if (LineCol.substr(0, ColonIdx).getAsInteger(10, Line)) {
llvm::errs() << "wrong pos format, it should be '<line>:<column>'\n";
return None;
}
if (LineCol.substr(ColonIdx+1).getAsInteger(10, Col)) {
llvm::errs() << "wrong pos format, it should be '<line>:<column>'\n";
return None;
}
if (Line == 0 || Col == 0) {
llvm::errs() << "wrong pos format, line/col should start from 1\n";
return None;
}
return std::make_pair(Line, Col);
}
void XMLEscapingPrinter::printText(StringRef Text) {
swift::markup::appendWithXMLEscaping(OS, Text);
}
void XMLEscapingPrinter::printXML(StringRef Text) {
OS << Text;
}
SourceManager &CursorInfoResolver::getSourceMgr() const
{
return SrcFile.getASTContext().SourceMgr;
}
bool CursorInfoResolver::tryResolve(ValueDecl *D, TypeDecl *CtorTyRef,
ExtensionDecl *ExtTyRef, SourceLoc Loc,
bool IsRef, Type Ty) {
if (!D->hasName())
return false;
if (Loc == LocToResolve) {
CursorInfo = { D, CtorTyRef, ExtTyRef, Loc, IsRef, Ty, ContainerType };
return true;
}
return false;
}
bool CursorInfoResolver::tryResolve(ModuleEntity Mod, SourceLoc Loc) {
if (Loc == LocToResolve) {
CursorInfo = { Mod, Loc };
return true;
}
return false;
}
bool CursorInfoResolver::tryResolve(Stmt *St) {
if (auto *LST = dyn_cast<LabeledStmt>(St)) {
if (LST->getStartLoc() == LocToResolve) {
CursorInfo = { St };
return true;
}
}
if (auto *CS = dyn_cast<CaseStmt>(St)) {
if (CS->getStartLoc() == LocToResolve) {
CursorInfo = { St };
return true;
}
}
return false;
}
bool CursorInfoResolver::visitSubscriptReference(ValueDecl *D, CharSourceRange Range,
bool IsOpenBracket) {
// We should treat both open and close brackets equally
return visitDeclReference(D, Range, nullptr, nullptr, Type(),
ReferenceMetaData(SemaReferenceKind::SubscriptRef, None));
}
ResolvedCursorInfo CursorInfoResolver::resolve(SourceLoc Loc) {
assert(Loc.isValid());
LocToResolve = Loc;
CursorInfo = ResolvedCursorInfo();
walk(SrcFile);
return CursorInfo;
}
bool CursorInfoResolver::walkToDeclPre(Decl *D, CharSourceRange Range) {
if (!rangeContainsLoc(D->getSourceRange()))
return false;
if (isa<ExtensionDecl>(D))
return true;
if (auto *VD = dyn_cast<ValueDecl>(D))
return !tryResolve(VD, /*CtorTyRef=*/nullptr, /*ExtTyRef=*/nullptr,
Range.getStart(), /*IsRef=*/false);
return true;
}
bool CursorInfoResolver::walkToDeclPost(Decl *D) {
if (isDone())
return false;
if (getSourceMgr().isBeforeInBuffer(LocToResolve, D->getStartLoc()))
return false;
return true;
}
bool CursorInfoResolver::walkToStmtPre(Stmt *S) {
// FIXME: Even implicit Stmts should have proper ranges that include any
// non-implicit Stmts (fix Stmts created for lazy vars).
if (!S->isImplicit() && !rangeContainsLoc(S->getSourceRange()))
return false;
return !tryResolve(S);
}
bool CursorInfoResolver::walkToStmtPost(Stmt *S) {
if (isDone())
return false;
// FIXME: Even implicit Stmts should have proper ranges that include any
// non-implicit Stmts (fix Stmts created for lazy vars).
if (!S->isImplicit() && getSourceMgr().isBeforeInBuffer(LocToResolve,
S->getStartLoc()))
return false;
return true;
}
bool CursorInfoResolver::visitDeclReference(ValueDecl *D,
CharSourceRange Range,
TypeDecl *CtorTyRef,
ExtensionDecl *ExtTyRef, Type T,
ReferenceMetaData Data) {
if (isDone())
return false;
return !tryResolve(D, CtorTyRef, ExtTyRef, Range.getStart(), /*IsRef=*/true, T);
}
bool CursorInfoResolver::walkToExprPre(Expr *E) {
if (!isDone()) {
if (auto SAE = dyn_cast<SelfApplyExpr>(E)) {
if (SAE->getFn()->getStartLoc() == LocToResolve) {
ContainerType = SAE->getBase()->getType();
}
} else if (auto ME = dyn_cast<MemberRefExpr>(E)) {
SourceLoc DotLoc = ME->getDotLoc();
if (DotLoc.isValid() && DotLoc.getAdvancedLoc(1) == LocToResolve) {
ContainerType = ME->getBase()->getType();
}
}
auto IsProperCursorLocation = E->getStartLoc() == LocToResolve;
// Handle cursor placement between try and ! in ForceTryExpr.
if (auto *FTE = dyn_cast<ForceTryExpr>(E)) {
IsProperCursorLocation = LocToResolve == FTE->getExclaimLoc() ||
IsProperCursorLocation;
}
// Keep track of trailing expressions.
if (!E->isImplicit() && IsProperCursorLocation)
TrailingExprStack.push_back(E);
}
return true;
}
bool CursorInfoResolver::walkToExprPost(Expr *E) {
if (isDone())
return false;
if (!TrailingExprStack.empty() && TrailingExprStack.back() == E) {
// We return the outtermost expression in the token info.
CursorInfo = { TrailingExprStack.front() };
return false;
}
return true;
}
bool CursorInfoResolver::visitCallArgName(Identifier Name,
CharSourceRange Range,
ValueDecl *D) {
if (isDone())
return false;
bool Found = tryResolve(D, nullptr, nullptr, Range.getStart(), /*IsRef=*/true);
if (Found)
CursorInfo.IsKeywordArgument = true;
return !Found;
}
bool CursorInfoResolver::
visitDeclarationArgumentName(Identifier Name, SourceLoc StartLoc, ValueDecl *D) {
if (isDone())
return false;
return !tryResolve(D, nullptr, nullptr, StartLoc, /*IsRef=*/false);
}
bool CursorInfoResolver::visitModuleReference(ModuleEntity Mod,
CharSourceRange Range) {
if (isDone())
return false;
if (Mod.isBuiltinModule())
return true; // Ignore.
return !tryResolve(Mod, Range.getStart());
}
SourceManager &NameMatcher::getSourceMgr() const {
return SrcFile.getASTContext().SourceMgr;
}
std::vector<ResolvedLoc> NameMatcher::resolve(ArrayRef<UnresolvedLoc> Locs, ArrayRef<Token> Tokens) {
// Note the original indices and sort them in reverse source order
std::vector<size_t> MapToOriginalIndex(Locs.size());
std::iota(MapToOriginalIndex.begin(), MapToOriginalIndex.end(), 0);
std::sort(MapToOriginalIndex.begin(), MapToOriginalIndex.end(),
[this, Locs](size_t first, size_t second) {
return first != second && !getSourceMgr()
.isBeforeInBuffer(Locs[first].Loc, Locs[second].Loc);
});
// Add the locs themselves
LocsToResolve.clear();
std::transform(MapToOriginalIndex.begin(), MapToOriginalIndex.end(),
std::back_inserter(LocsToResolve),
[&](size_t index){ return Locs[index]; });
InactiveConfigRegionNestings = 0;
SelectorNestings = 0;
TokensToCheck = Tokens;
ResolvedLocs.clear();
SrcFile.walk(*this);
checkComments();
// handle any unresolved locs past the end of the last AST node or comment
std::vector<ResolvedLoc> Remaining(Locs.size() - ResolvedLocs.size(), {
ASTWalker::ParentTy(), CharSourceRange(), {}, LabelRangeType::None,
/*isActice*/true, /*isInSelector*/false});
ResolvedLocs.insert(ResolvedLocs.end(), Remaining.begin(), Remaining.end());
// return in the original order
std::vector<ResolvedLoc> Ordered(ResolvedLocs.size());
for(size_t Index = 0; Index < ResolvedLocs.size(); ++Index) {
size_t Flipped = ResolvedLocs.size() - 1 - Index;
Ordered[MapToOriginalIndex[Flipped]] = ResolvedLocs[Index];
}
return Ordered;
}
static std::vector<CharSourceRange> getLabelRanges(const ParameterList* List, const SourceManager &SM) {
std::vector<CharSourceRange> LabelRanges;
for (ParamDecl *Param: *List) {
if (Param->isImplicit())
continue;
SourceLoc NameLoc = Param->getArgumentNameLoc();
SourceLoc ParamLoc = Param->getNameLoc();
size_t NameLength;
if (NameLoc.isValid()) {
LabelRanges.push_back(Lexer::getCharSourceRangeFromSourceRange(SM,
SourceRange(NameLoc, ParamLoc)));
} else {
NameLoc = ParamLoc;
NameLength = Param->getNameStr().size();
LabelRanges.push_back(CharSourceRange(NameLoc, NameLength));
}
}
return LabelRanges;
}
bool NameMatcher::walkToDeclPre(Decl *D) {
// Handle occurrences in any preceding doc comments
RawComment R = D->getRawComment();
if (!R.isEmpty()) {
for(SingleRawComment C: R.Comments) {
while(!shouldSkip(C.Range))
tryResolve(ASTWalker::ParentTy(), nextLoc());
}
}
if (shouldSkip(D->getSourceRange()))
return false;
if (auto *ICD = dyn_cast<IfConfigDecl>(D)) {
for (auto Clause : ICD->getClauses()) {
if (!Clause.isActive)
++InactiveConfigRegionNestings;
for (auto Member : Clause.Elements) {
Member.walk(*this);
}
if (!Clause.isActive) {
assert(InactiveConfigRegionNestings > 0);
--InactiveConfigRegionNestings;
}
}
return false;
} else if (AbstractFunctionDecl *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
std::vector<CharSourceRange> LabelRanges;
if (AFD->getNameLoc() == nextLoc()) {
for(auto ParamList: AFD->getParameterLists()) {
LabelRanges = getLabelRanges(ParamList, getSourceMgr());
if (LabelRanges.size() == ParamList->size())
break;
}
}
tryResolve(ASTWalker::ParentTy(D), D->getLoc(), LabelRangeType::Param,
LabelRanges);
} else if (SubscriptDecl *SD = dyn_cast<SubscriptDecl>(D)) {
tryResolve(ASTWalker::ParentTy(D), D->getLoc(), LabelRangeType::Param,
getLabelRanges(SD->getIndices(), getSourceMgr()));
} else if (EnumElementDecl *EED = dyn_cast<EnumElementDecl>(D)) {
if (TupleTypeRepr *TTR = dyn_cast_or_null<TupleTypeRepr>(EED->getArgumentTypeLoc().getTypeRepr())) {
size_t ElemIndex = 0;
std::vector<CharSourceRange> LabelRanges;
for(const TupleTypeReprElement &Elem: TTR->getElements()) {
SourceLoc LabelStart(Elem.Type->getStartLoc());
SourceLoc LabelEnd(LabelStart);
auto NameIdentifier = TTR->getElementName(ElemIndex);
if (!NameIdentifier.empty()) {
LabelStart = TTR->getElementNameLoc(ElemIndex);
}
LabelRanges.push_back(CharSourceRange(getSourceMgr(), LabelStart, LabelEnd));
++ElemIndex;
}
tryResolve(ASTWalker::ParentTy(D), D->getLoc(), LabelRangeType::CallArg, LabelRanges);
} else {
tryResolve(ASTWalker::ParentTy(D), D->getLoc());
}
} else if (ImportDecl *ID = dyn_cast<ImportDecl>(D)) {
for(const ImportDecl::AccessPathElement &Element: ID->getFullAccessPath()) {
tryResolve(ASTWalker::ParentTy(D), Element.second);
if (isDone())
break;
}
} else if (isa<ValueDecl>(D) || isa<ExtensionDecl>(D) ||
isa<PrecedenceGroupDecl>(D)) {
tryResolve(ASTWalker::ParentTy(D), D->getLoc());
}
return !isDone();
}
bool NameMatcher::walkToDeclPost(Decl *D) {
return !isDone();
}
std::pair<bool, Stmt *> NameMatcher::walkToStmtPre(Stmt *S) {
// FIXME: Even implicit Stmts should have proper ranges that include any
// non-implicit Stmts (fix Stmts created for lazy vars).
if (!S->isImplicit() && shouldSkip(S->getSourceRange()))
return std::make_pair(false, isDone()? nullptr : S);
return std::make_pair(true, S);
}
Stmt *NameMatcher::walkToStmtPost(Stmt *S) {
if (isDone())
return nullptr;
return S;
}
std::pair<bool, Expr*> NameMatcher::walkToExprPre(Expr *E) {
if (shouldSkip(E))
return std::make_pair(false, isDone()? nullptr : E);
if (isa<ObjCSelectorExpr>(E)) {
++SelectorNestings;
}
// only match name locations of expressions apparent in the original source
if (!E->isImplicit()) {
// Try to resolve against the below kinds *before* their children are
// visited to ensure visitation happens in source order.
switch (E->getKind()) {
case ExprKind::UnresolvedMember: {
auto UME = cast<UnresolvedMemberExpr>(E);
tryResolve(ASTWalker::ParentTy(E), UME->getNameLoc(), UME->getArgument(), !UME->getArgument());
} break;
case ExprKind::DeclRef: {
auto DRE = cast<DeclRefExpr>(E);
tryResolve(ASTWalker::ParentTy(E), DRE->getNameLoc(), nullptr, true);
break;
}
case ExprKind::UnresolvedDeclRef: {
auto UDRE = cast<UnresolvedDeclRefExpr>(E);
tryResolve(ASTWalker::ParentTy(E), UDRE->getNameLoc(), nullptr, true);
break;
}
case ExprKind::StringLiteral:
// Handle multple locations in a single string literal
do {
tryResolve(ASTWalker::ParentTy(E), nextLoc());
} while (!shouldSkip(E));
break;
case ExprKind::Tuple: {
TupleExpr *T = cast<TupleExpr>(E);
// Handle arg label locations (the index reports property occurrences
// on them for memberwise inits)
for (unsigned i = 0, e = T->getNumElements(); i != e; ++i) {
auto Name = T->getElementName(i);
if (!Name.empty()) {
tryResolve(ASTWalker::ParentTy(E), T->getElementNameLoc(i));
if (isDone())
break;
}
if (auto *Elem = T->getElement(i)) {
if (!Elem->walk(*this))
return {false, nullptr};
}
}
// We already visited the children.
if (!walkToExprPost(E))
return {false, nullptr};
return {false, E};
}
case ExprKind::Binary: {
BinaryExpr *BinE = cast<BinaryExpr>(E);
// Visit in source order.
if (!BinE->getArg()->getElement(0)->walk(*this))
return {false, nullptr};
if (!BinE->getFn()->walk(*this))
return {false, nullptr};
if (!BinE->getArg()->getElement(1)->walk(*this))
return {false, nullptr};
// We already visited the children.
if (!walkToExprPost(E))
return {false, nullptr};
return {false, E};
}
default: // ignored
break;
}
}
return std::make_pair(!isDone(), isDone()? nullptr : E);
}
Expr *NameMatcher::walkToExprPost(Expr *E) {
if (isDone())
return nullptr;
if (!E->isImplicit()) {
// Try to resolve against the below kinds *after* their children have been
// visited to ensure visitation happens in source order.
switch (E->getKind()) {
case ExprKind::MemberRef:
tryResolve(ASTWalker::ParentTy(E), E->getLoc());
break;
case ExprKind::UnresolvedDot: {
auto UDE = cast<UnresolvedDotExpr>(E);
tryResolve(ASTWalker::ParentTy(E), UDE->getNameLoc(), nullptr, true);
break;
}
default:
break;
}
}
if (isa<ObjCSelectorExpr>(E)) {
assert(SelectorNestings > 0);
--SelectorNestings;
}
return E;
}
bool NameMatcher::walkToTypeLocPre(TypeLoc &TL) {
if (isDone() || shouldSkip(TL.getSourceRange()))
return false;
return true;
}
bool NameMatcher::walkToTypeLocPost(TypeLoc &TL) {
return !isDone();
}
bool NameMatcher::walkToTypeReprPre(TypeRepr *T) {
if (isDone() || shouldSkip(T->getSourceRange()))
return false;
if (isa<ComponentIdentTypeRepr>(T))
tryResolve(ASTWalker::ParentTy(T), T->getLoc());
return !isDone();
}
bool NameMatcher::walkToTypeReprPost(TypeRepr *T) {
return !isDone();
}
std::pair<bool, Pattern*> NameMatcher::walkToPatternPre(Pattern *P) {
if (isDone() || shouldSkip(P->getSourceRange()))
return std::make_pair(false, P);
tryResolve(ASTWalker::ParentTy(P), P->getLoc());
return std::make_pair(!isDone(), P);
}
bool NameMatcher::checkComments() {
if (isDone())
return false;
TokensToCheck = TokensToCheck.drop_while([this](const Token &tok) -> bool {
return getSourceMgr().isBeforeInBuffer(tok.getRange().getEnd(), nextLoc());
});
if (TokensToCheck.empty())
return false;
const Token &next = TokensToCheck.front();
if (next.is(swift::tok::comment) && next.getRange().contains(nextLoc()) &&
!next.getText().startswith("///"))
return tryResolve(ASTWalker::ParentTy(), nextLoc());
return false;
}
void NameMatcher::skipLocsBefore(SourceLoc Start) {
while (!isDone() && getSourceMgr().isBeforeInBuffer(nextLoc(), Start)) {
if (!checkComments()) {
LocsToResolve.pop_back();
ResolvedLocs.push_back({ASTWalker::ParentTy(), CharSourceRange(), {},
LabelRangeType::None, isActive(), isInSelector()});
}
}
}
bool NameMatcher::shouldSkip(Expr *E) {
if (!isa<StringLiteralExpr>(E) || !Parent.getAsExpr() ||
!isa<InterpolatedStringLiteralExpr>(Parent.getAsExpr()))
return shouldSkip(E->getSourceRange());
// The lexer treats interpolated strings as a single token when computing the
// CharSourceRange, so when we try to get the CharSourceRange of its first
// child StringLiteralExpr (at the same SourceLoc) it goes beyond any
// interpolated values. Use the StartLoc of the next sibling to bound it.
StringLiteralExpr *SL = cast<StringLiteralExpr>(E);
InterpolatedStringLiteralExpr *ISL =
cast<InterpolatedStringLiteralExpr>(Parent.getAsExpr());
SourceLoc Start = SL->getStartLoc();
ArrayRef<Expr*> Segments = ISL->getSegments();
Segments = Segments.drop_until([&](Expr *Item){ return Item == SL; })
.drop_front();
CharSourceRange Range;
if (Segments.empty()) {
Range = Lexer::getCharSourceRangeFromSourceRange(getSourceMgr(),
SourceRange(Start));
} else {
SourceLoc NextSiblingLoc = Segments.front()->getStartLoc();
unsigned Length = getSourceMgr().getByteDistance(Start, NextSiblingLoc);
Range = CharSourceRange(Start, Length);
}
return shouldSkip(Range);
}
bool NameMatcher::shouldSkip(SourceRange Range) {
return shouldSkip(Lexer::getCharSourceRangeFromSourceRange(getSourceMgr(),
Range));
}
bool NameMatcher::shouldSkip(CharSourceRange Range) {
if (isDone())
return true;
if (Range.isInvalid())
return false;
skipLocsBefore(Range.getStart());
return isDone() || !Range.contains(nextLoc());
}
SourceLoc NameMatcher::nextLoc() const {
assert(!LocsToResolve.empty());
return LocsToResolve.back().Loc;
}
std::vector<CharSourceRange> getSelectorLabelRanges(SourceManager &SM,
DeclNameLoc NameLoc) {
SourceLoc Loc;
std::vector<CharSourceRange> Ranges;
size_t index = 0;
while((Loc = NameLoc.getArgumentLabelLoc(index++)).isValid()) {
CharSourceRange Range = Lexer::getCharSourceRangeFromSourceRange(SM,
SourceRange(Loc));
Ranges.push_back(Range);
}
return Ranges;
}
bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, DeclNameLoc NameLoc,
Expr *Arg, bool checkParentForLabels) {
if (NameLoc.isInvalid())
return false;
if (NameLoc.isCompound()) {
auto Labels = getSelectorLabelRanges(getSourceMgr(), NameLoc);
bool Resolved = tryResolve(Node, NameLoc.getBaseNameLoc(),
LabelRangeType::Selector, Labels);
if (!isDone()) {
for (auto Label: Labels) {
if (tryResolve(Node, Label.getStart())) {
Resolved = true;
if (isDone())
break;
}
}
}
return Resolved;
}
if (LocsToResolve.back().ResolveArgLocs) {
if (Arg)
return tryResolve(Node, NameLoc.getBaseNameLoc(), LabelRangeType::CallArg,
getCallArgLabelRanges(getSourceMgr(), Arg,
LabelRangeEndAt::BeforeElemStart));
if (checkParentForLabels) {
if (auto P = dyn_cast_or_null<ApplyExpr>(Parent.getAsExpr())) {
if (P->getFn() == Node.getAsExpr())
return tryResolve(Node, NameLoc.getBaseNameLoc(),
LabelRangeType::CallArg,
getCallArgLabelRanges(getSourceMgr(), P->getArg(),
LabelRangeEndAt::BeforeElemStart));
}
}
}
return tryResolve(Node, NameLoc.getBaseNameLoc());
}
bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc) {
assert(!isDone());
return tryResolve(Node, NameLoc, LabelRangeType::None, None);
}
bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc,
LabelRangeType RangeType,
ArrayRef<CharSourceRange> LabelRanges) {
skipLocsBefore(NameLoc);
if (isDone())
return false;
CharSourceRange Range = Lexer::getCharSourceRangeFromSourceRange(getSourceMgr(),
NameLoc);
UnresolvedLoc &Next = LocsToResolve.back();
if (Range.isValid() && NameLoc == Next.Loc) {
LocsToResolve.pop_back();
ResolvedLocs.push_back({Node, Range, LabelRanges, RangeType,
isActive(), isInSelector()});
return true;
}
return false;
};
void ResolvedRangeInfo::print(llvm::raw_ostream &OS) {
OS << "<Kind>";
switch (Kind) {
case RangeKind::SingleExpression: OS << "SingleExpression"; break;
case RangeKind::SingleDecl: OS << "SingleDecl"; break;
case RangeKind::MultiStatement: OS << "MultiStatement"; break;
case RangeKind::PartOfExpression: OS << "PartOfExpression"; break;
case RangeKind::SingleStatement: OS << "SingleStatement"; break;
case RangeKind::Invalid: OS << "Invalid"; break;
}
OS << "</Kind>\n";
OS << "<Content>" << ContentRange.str() << "</Content>\n";
if (auto Ty = getType()) {
OS << "<Type>";
Ty->print(OS);
OS << "</Type>";
switch(exit()) {
case ExitState::Positive: OS << "<Exit>true</Exit>"; break;
case ExitState::Unsure: OS << "<Exit>unsure</Exit>"; break;
case ExitState::Negative: OS << "<Exit>false</Exit>"; break;
}
OS << "\n";
}
if (RangeContext) {
OS << "<Context>";
printContext(OS, RangeContext);
OS << "</Context>\n";
}
if (CommonExprParent) {
OS << "<Parent>";
OS << Expr::getKindName(CommonExprParent->getKind());
OS << "</Parent>\n";
}
if (!HasSingleEntry) {
OS << "<Entry>Multi</Entry>\n";
}
if (ThrowingUnhandledError) {
OS << "<Error>Throwing</Error>\n";
}
if (Orphan != OrphanKind::None) {
OS << "<Orphan>";
switch (Orphan) {
case OrphanKind::Continue:
OS << "Continue";
break;
case OrphanKind::Break:
OS << "Break";
break;
case OrphanKind::None:
llvm_unreachable("cannot enter here.");
}
OS << "</Orphan>";
}
for (auto &VD : DeclaredDecls) {
OS << "<Declared>" << VD.VD->getBaseName() << "</Declared>";
OS << "<OutscopeReference>";
if (VD.ReferredAfterRange)
OS << "true";
else
OS << "false";
OS << "</OutscopeReference>\n";
}
for (auto &RD : ReferencedDecls) {
OS << "<Referenced>" << RD.VD->getBaseName() << "</Referenced>";
OS << "<Type>";
RD.Ty->print(OS);
OS << "</Type>\n";
}
OS << "<ASTNodes>" << ContainedNodes.size() << "</ASTNodes>\n";
OS << "<end>\n";
}
CharSourceRange ResolvedRangeInfo::
calculateContentRange(ArrayRef<Token> Tokens) {
if (Tokens.empty())
return CharSourceRange();
auto StartTok = Tokens.front();
auto EndTok = Tokens.back();
auto StartLoc = StartTok.hasComment() ?
StartTok.getCommentStart() : StartTok.getLoc();
auto EndLoc = EndTok.getRange().getEnd();
auto Length = static_cast<const char *>(EndLoc.getOpaquePointerValue()) -
static_cast<const char *>(StartLoc.getOpaquePointerValue());
return CharSourceRange(StartLoc, Length);
}
bool DeclaredDecl::operator==(const DeclaredDecl& Other) {
return VD == Other.VD;
}
static bool hasUnhandledError(ArrayRef<ASTNode> Nodes) {
class ThrowingEntityAnalyzer : public SourceEntityWalker {
bool Throwing;
public:
ThrowingEntityAnalyzer(): Throwing(false) {}
bool walkToStmtPre(Stmt *S) override {
if (auto DCS = dyn_cast<DoCatchStmt>(S)) {
if (DCS->isSyntacticallyExhaustive())
return false;
Throwing = true;
} else if (isa<ThrowStmt>(S)) {
Throwing = true;
}
return !Throwing;
}
bool walkToExprPre(Expr *E) override {
if (isa<TryExpr>(E)) {
Throwing = true;
}
return !Throwing;
}
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
return false;
}
bool walkToDeclPost(Decl *D) override { return !Throwing; }
bool walkToStmtPost(Stmt *S) override { return !Throwing; }
bool walkToExprPost(Expr *E) override { return !Throwing; }
bool isThrowing() { return Throwing; }
};
return Nodes.end() != std::find_if(Nodes.begin(), Nodes.end(), [](ASTNode N) {
ThrowingEntityAnalyzer Analyzer;
N.walk(Analyzer);
return Analyzer.isThrowing();
});
}
ReturnInfo::
ReturnInfo(ASTContext &Ctx, ArrayRef<ReturnInfo> Branches):
ReturnType(Ctx.TheErrorType.getPointer()), Exit(ExitState::Unsure) {
std::set<TypeBase*> AllTypes;
std::set<ExitState> AllExitStates;
for (auto I : Branches) {
AllTypes.insert(I.ReturnType);
AllExitStates.insert(I.Exit);
}
if (AllTypes.size() == 1) {
ReturnType = *AllTypes.begin();
}
if (AllExitStates.size() == 1) {
Exit = *AllExitStates.begin();
}
}
struct RangeResolver::Implementation {
SourceFile &File;
ASTContext &Ctx;
SourceManager &SM;
private:
enum class RangeMatchKind : int8_t {
NoneMatch,
StartMatch,
EndMatch,
RangeMatch,
};
struct ContextInfo {
ASTNode Parent;
// Whether the context is entirely contained in the given range under
// scrutiny.
bool ContainedInRange;
std::vector<ASTNode> StartMatches;
std::vector<ASTNode> EndMatches;
ContextInfo(ASTNode Parent, bool ContainedInRange) : Parent(Parent),
ContainedInRange(ContainedInRange) {}
bool isMultiStatement() {
if (StartMatches.empty() || EndMatches.empty())
return false;
// Multi-statement should have a common parent of brace statement, this
// can be implicit brace statement, e.g. in case statement.
if (Parent.isStmt(StmtKind::Brace))
return true;
// Explicitly allow the selection of multiple case statements.
auto IsCase = [](ASTNode N) { return N.isStmt(StmtKind::Case); };
return llvm::any_of(StartMatches, IsCase) &&
llvm::any_of(EndMatches, IsCase);
}
};
ArrayRef<Token> TokensInRange;
SourceLoc Start;
SourceLoc End;
Optional<ResolvedRangeInfo> Result;
std::vector<ContextInfo> ContextStack;
ContextInfo &getCurrentDC() {
assert(!ContextStack.empty());
return ContextStack.back();
}
std::vector<DeclaredDecl> DeclaredDecls;
std::vector<ReferencedDecl> ReferencedDecls;
// Keep track of the AST nodes contained in the range under question.
std::vector<ASTNode> ContainedASTNodes;
/// Collect the type that an ASTNode should be evaluated to.
ReturnInfo resolveNodeType(ASTNode N, RangeKind Kind) {
auto *VoidTy = Ctx.getVoidDecl()->getDeclaredInterfaceType().getPointer();
if (N.isNull())
return {VoidTy, ExitState::Negative};
switch(Kind) {
case RangeKind::Invalid:
case RangeKind::SingleDecl:
case RangeKind::PartOfExpression:
llvm_unreachable("cannot get type.");
// For a single expression, its type is apparent.
case RangeKind::SingleExpression:
return {N.get<Expr*>()->getType().getPointer(), ExitState::Negative};
// For statements, we either resolve to the returning type or Void.
case RangeKind::SingleStatement:
case RangeKind::MultiStatement: {
if (N.is<Stmt*>()) {
if (auto RS = dyn_cast<ReturnStmt>(N.get<Stmt*>())) {
return {
resolveNodeType(RS->hasResult() ? RS->getResult() : nullptr,
RangeKind::SingleExpression).ReturnType, ExitState::Positive };
}
// Unbox the brace statement to find its type.
if (auto BS = dyn_cast<BraceStmt>(N.get<Stmt*>())) {
if (!BS->getElements().empty()) {
return resolveNodeType(BS->getElements().back(),
RangeKind::SingleStatement);
}
}
// Unbox the if statement to find its type.
if (auto *IS = dyn_cast<IfStmt>(N.get<Stmt*>())) {
llvm::SmallVector<ReturnInfo, 2> Branches;
Branches.push_back(resolveNodeType(IS->getThenStmt(),
RangeKind::SingleStatement));
Branches.push_back(resolveNodeType(IS->getElseStmt(),
RangeKind::SingleStatement));
return {Ctx, Branches};
}
// Unbox switch statement to find return information.
if (auto *SWS = dyn_cast<SwitchStmt>(N.get<Stmt*>())) {
llvm::SmallVector<ReturnInfo, 4> Branches;
for (auto *CS : SWS->getCases()) {
Branches.push_back(resolveNodeType(CS->getBody(),
RangeKind::SingleStatement));
}
return {Ctx, Branches};
}
}
// For other statements, the type should be void.
return {VoidTy, ExitState::Negative};
}
}
}
ResolvedRangeInfo getSingleNodeKind(ASTNode Node) {
assert(!Node.isNull());
assert(ContainedASTNodes.size() == 1);
// Single node implies single entry point, or is it?
bool SingleEntry = true;
bool UnhandledError = hasUnhandledError({Node});
OrphanKind Kind = getOrphanKind(ContainedASTNodes);
if (Node.is<Expr*>())
return ResolvedRangeInfo(RangeKind::SingleExpression,
resolveNodeType(Node, RangeKind::SingleExpression),
TokensInRange,
getImmediateContext(),
/*Common Parent Expr*/nullptr,
SingleEntry,
UnhandledError, Kind,
llvm::makeArrayRef(ContainedASTNodes),
llvm::makeArrayRef(DeclaredDecls),
llvm::makeArrayRef(ReferencedDecls));
else if (Node.is<Stmt*>())
return ResolvedRangeInfo(RangeKind::SingleStatement,
resolveNodeType(Node, RangeKind::SingleStatement),
TokensInRange,
getImmediateContext(),
/*Common Parent Expr*/nullptr,
SingleEntry,
UnhandledError, Kind,
llvm::makeArrayRef(ContainedASTNodes),
llvm::makeArrayRef(DeclaredDecls),
llvm::makeArrayRef(ReferencedDecls));
else {
assert(Node.is<Decl*>());
return ResolvedRangeInfo(RangeKind::SingleDecl,
ReturnInfo(),
TokensInRange,
getImmediateContext(),
/*Common Parent Expr*/nullptr,
SingleEntry,
UnhandledError, Kind,
llvm::makeArrayRef(ContainedASTNodes),
llvm::makeArrayRef(DeclaredDecls),
llvm::makeArrayRef(ReferencedDecls));
}
}
bool isContainedInSelection(CharSourceRange Range) {
if (SM.isBeforeInBuffer(Range.getStart(), Start))
return false;
if (SM.isBeforeInBuffer(End, Range.getEnd()))
return false;
return true;
}
DeclContext *getImmediateContext() {
for (auto It = ContextStack.rbegin(); It != ContextStack.rend(); It ++) {
if (auto *DC = It->Parent.getAsDeclContext())
return DC;
}
return static_cast<DeclContext*>(&File);
}
Implementation(SourceFile &File, ArrayRef<Token> TokensInRange) :
File(File), Ctx(File.getASTContext()), SM(Ctx.SourceMgr),
TokensInRange(TokensInRange),
Start(TokensInRange.front().getLoc()),
End(TokensInRange.back().getLoc()) {
assert(Start.isValid() && End.isValid());
}
public:
bool hasResult() { return Result.hasValue(); }
void enter(ASTNode Node) {
bool ContainedInRange;
if (!Node.getOpaqueValue()) {
// If the node is the root, it's not contained for sure.
ContainedInRange = false;
} else if (ContextStack.back().ContainedInRange) {
// If the node's parent is contained in the range, so is the node.
ContainedInRange = true;
} else {
// If the node's parent is not contained in the range, check if this node is.
ContainedInRange = isContainedInSelection(CharSourceRange(SM,
Node.getStartLoc(),
Node.getEndLoc()));
}
ContextStack.emplace_back(Node, ContainedInRange);
}
void leave(ASTNode Node) {
if (!hasResult() && !Node.isImplicit() && nodeContainSelection(Node)) {
if (auto Parent = Node.is<Expr*>() ? Node.get<Expr*>() : nullptr) {
Result = {
RangeKind::PartOfExpression,
ReturnInfo(),
TokensInRange,
getImmediateContext(),
Parent,
hasSingleEntryPoint(ContainedASTNodes),
hasUnhandledError(ContainedASTNodes),
getOrphanKind(ContainedASTNodes),
llvm::makeArrayRef(ContainedASTNodes),
llvm::makeArrayRef(DeclaredDecls),
llvm::makeArrayRef(ReferencedDecls)
};
}
}
assert(ContextStack.back().Parent.getOpaqueValue() == Node.getOpaqueValue());
ContextStack.pop_back();
}
static std::unique_ptr<Implementation>
createInstance(SourceFile &File, unsigned StartOff, unsigned Length) {
SourceManager &SM = File.getASTContext().SourceMgr;
unsigned BufferId = File.getBufferID().getValue();
auto AllTokens = File.getAllTokens();
SourceLoc StartRaw = SM.getLocForOffset(BufferId, StartOff);
SourceLoc EndRaw = SM.getLocForOffset(BufferId, StartOff + Length);
// This points to the first token after or on the start loc.
auto StartIt = token_lower_bound(AllTokens, StartRaw);
// Skip all the comments.
while(StartIt != AllTokens.end()) {
if (StartIt->getKind() != tok::comment)
break;
StartIt ++;
}
// Erroneous case.
if (StartIt == AllTokens.end())
return nullptr;
// This points to the first token after or on the end loc;
auto EndIt = token_lower_bound(AllTokens, EndRaw);
// Adjust end token to skip comments.
while (EndIt != AllTokens.begin()) {
EndIt --;
if (EndIt->getKind() != tok::comment)
break;
}
// Erroneous case.
if (EndIt < StartIt)
return nullptr;
unsigned StartIdx = StartIt - AllTokens.begin();
return std::unique_ptr<Implementation>(new Implementation(File,
AllTokens.slice(StartIdx, EndIt - StartIt + 1)));
}
static std::unique_ptr<Implementation>
createInstance(SourceFile &File, SourceLoc Start, SourceLoc End) {
if (Start.isInvalid() || End.isInvalid())
return nullptr;
SourceManager &SM = File.getASTContext().SourceMgr;
unsigned BufferId = File.getBufferID().getValue();
unsigned StartOff = SM.getLocOffsetInBuffer(Start, BufferId);
unsigned EndOff = SM.getLocOffsetInBuffer(End, BufferId);
return createInstance(File, StartOff, EndOff - StartOff);
}
void analyzeDecl(Decl *D) {
// Collect declared decls in the range.
if (auto *VD = dyn_cast_or_null<ValueDecl>(D)) {
if (isContainedInSelection(CharSourceRange(SM, VD->getStartLoc(),
VD->getEndLoc())))
if (std::find(DeclaredDecls.begin(), DeclaredDecls.end(),
DeclaredDecl(VD)) == DeclaredDecls.end())
DeclaredDecls.push_back(VD);
}
}
class CompleteWalker : public SourceEntityWalker {
Implementation *Impl;
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
if (D->isImplicit())
return false;
Impl->analyzeDecl(D);
return true;
}
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T,
ReferenceMetaData Data) override {
Impl->analyzeDeclRef(D, Range.getStart(), T, Data);
return true;
}
public:
CompleteWalker(Implementation *Impl) : Impl(Impl) {}
};
/// This walker walk the current decl context and analyze whether declared
/// decls in the range is referenced after it.
class FurtherReferenceWalker : public SourceEntityWalker {
Implementation *Impl;
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T,
ReferenceMetaData Data) override {
// If the reference is after the given range, continue logic.
if (!Impl->SM.isBeforeInBuffer(Impl->End, Range.getStart()))
return true;
// If the referenced decl is declared in the range, than the declared decl
// is referenced out of scope/range.
auto It = std::find(Impl->DeclaredDecls.begin(),
Impl->DeclaredDecls.end(), D);
if (It != Impl->DeclaredDecls.end()) {
It->ReferredAfterRange = true;
}
return true;
}
public:
FurtherReferenceWalker(Implementation *Impl) : Impl(Impl) {}
};
void postAnalysis(ASTNode EndNode) {
// Visit the content of this node thoroughly, because the walker may
// abort early.
EndNode.walk(CompleteWalker(this));
// Analyze whether declared decls in the range is referenced outside of it.
FurtherReferenceWalker(this).walk(getImmediateContext());
}
bool hasSingleEntryPoint(ArrayRef<ASTNode> Nodes) {
unsigned CaseCount = 0;
// Count the number of case/default statements.
for (auto N : Nodes) {
if (Stmt *S = N.is<Stmt*>() ? N.get<Stmt*>() : nullptr) {
if (S->getKind() == StmtKind::Case)
CaseCount++;
}
}
// If there are more than one case/default statements, there are more than
// one entry point.
return CaseCount == 0;
}
OrphanKind getOrphanKind(ArrayRef<ASTNode> Nodes) {
if (Nodes.empty())
return OrphanKind::None;
// Prepare the entire range.
SourceRange WholeRange(Nodes.front().getStartLoc(),
Nodes.back().getEndLoc());
struct ControlFlowStmtSelector : public SourceEntityWalker {
std::vector<std::pair<SourceRange, OrphanKind>> Ranges;
bool walkToStmtPre(Stmt *S) override {
// For each continue/break statement, record its target's range and the
// orphan kind.
if (auto *CS = dyn_cast<ContinueStmt>(S)) {
if (auto *Target = CS->getTarget()) {
Ranges.emplace_back(Target->getSourceRange(), OrphanKind::Continue);
}
} else if (auto *BS = dyn_cast<BreakStmt>(S)) {
if (auto *Target = BS->getTarget()) {
Ranges.emplace_back(Target->getSourceRange(), OrphanKind::Break);
}
}
return true;
}
};
for (auto N : Nodes) {
ControlFlowStmtSelector TheWalker;
N.walk(TheWalker);
for (auto Pair : TheWalker.Ranges) {
// If the entire range does not include the target's range, we find
// an orphan.
if (!SM.rangeContains(WholeRange, Pair.first))
return Pair.second;
}
}
// We find no orphan.
return OrphanKind::None;
}
void analyze(ASTNode Node) {
if (!shouldAnalyze(Node))
return;
Decl *D = Node.is<Decl*>() ? Node.get<Decl*>() : nullptr;
analyzeDecl(D);
auto &DCInfo = getCurrentDC();
switch (getRangeMatchKind(Node.getSourceRange())) {
case RangeMatchKind::NoneMatch: {
// PatternBindingDecl is not visited; we need to explicitly analyze here.
if (auto *VA = dyn_cast_or_null<VarDecl>(D))
if (auto PBD = VA->getParentPatternBinding())
analyze(PBD);
break;
}
case RangeMatchKind::RangeMatch: {
postAnalysis(Node);
// The node is contained in the given range.
ContainedASTNodes.push_back(Node);
Result = getSingleNodeKind(Node);
return;
}
case RangeMatchKind::StartMatch:
DCInfo.StartMatches.emplace_back(Node);
break;
case RangeMatchKind::EndMatch:
DCInfo.EndMatches.emplace_back(Node);
break;
}
// If no parent is considered as a contained node; this node should be
// a top-level contained node.
if (std::none_of(ContainedASTNodes.begin(), ContainedASTNodes.end(),
[&](ASTNode N) { return SM.rangeContains(N.getSourceRange(),
Node.getSourceRange()); })) {
ContainedASTNodes.push_back(Node);
}
if (DCInfo.isMultiStatement()) {
postAnalysis(DCInfo.EndMatches.back());
Result = {RangeKind::MultiStatement,
/* Last node has the type */
resolveNodeType(DCInfo.EndMatches.back(),
RangeKind::MultiStatement),
TokensInRange,
getImmediateContext(), nullptr,
hasSingleEntryPoint(ContainedASTNodes),
hasUnhandledError(ContainedASTNodes),
getOrphanKind(ContainedASTNodes),
llvm::makeArrayRef(ContainedASTNodes),
llvm::makeArrayRef(DeclaredDecls),
llvm::makeArrayRef(ReferencedDecls)};
}
}
bool shouldEnter(ASTNode Node) {
if (hasResult())
return false;
if (SM.isBeforeInBuffer(End, Node.getSourceRange().Start))
return false;
if (SM.isBeforeInBuffer(Node.getSourceRange().End, Start))
return false;
return true;
}
bool nodeContainSelection(ASTNode Node) {
// If the selection starts before the node, return false.
if (SM.isBeforeInBuffer(Start, Node.getStartLoc()))
return false;
// If the node ends before the selection, return false.
if (SM.isBeforeInBuffer(Lexer::getLocForEndOfToken(SM, Node.getEndLoc()), End))
return false;
// Contained.
return true;
}
bool shouldAnalyze(ASTNode Node) {
// Avoid analyzing implicit nodes.
if (Node.isImplicit())
return false;
// Avoid analyzing nodes that are not enclosed.
if (SM.isBeforeInBuffer(End, Node.getEndLoc()))
return false;
if (SM.isBeforeInBuffer(Node.getStartLoc(), Start))
return false;
return true;
}
ResolvedRangeInfo getResult() {
if (Result.hasValue())
return Result.getValue();
return ResolvedRangeInfo(TokensInRange);
}
void analyzeDeclRef(ValueDecl *VD, SourceLoc Start, Type Ty,
ReferenceMetaData Data) {
// Add defensive check in case the given type is null.
// FIXME: we should receive error type instead of null type.
if (Ty.isNull())
return;
// Only collect decl ref.
if (Data.Kind != SemaReferenceKind::DeclRef)
return;
if (!isContainedInSelection(CharSourceRange(Start, 0)))
return;
// If the VD is declared outside of current file, exclude such decl.
if (VD->getDeclContext()->getParentSourceFile() != &File)
return;
// Down-grade LValue type to RValue type if it's read-only.
if (auto Access = Data.AccKind) {
switch (Access.getValue()) {
case AccessKind::Read:
Ty = Ty->getRValueType();
break;
case AccessKind::Write:
case AccessKind::ReadWrite:
break;
}
}
auto It = llvm::find_if(ReferencedDecls,
[&](ReferencedDecl D) { return D.VD == VD; });
if (It == ReferencedDecls.end()) {
ReferencedDecls.emplace_back(VD, Ty);
} else {
// LValue type should take precedence.
if (!It->Ty->hasLValueType() && Ty->hasLValueType()) {
It->Ty = Ty;
}
}
}
private:
RangeMatchKind getRangeMatchKind(SourceRange Input) {
bool StartMatch = Input.Start == Start;
bool EndMatch = Input.End == End;
if (StartMatch && EndMatch)
return RangeMatchKind::RangeMatch;
else if (StartMatch)
return RangeMatchKind::StartMatch;
else if (EndMatch)
return RangeMatchKind::EndMatch;
else
return RangeMatchKind::NoneMatch;
}
};
RangeResolver::RangeResolver(SourceFile &File, SourceLoc Start, SourceLoc End) :
Impl(Implementation::createInstance(File, Start, End)) {}
RangeResolver::RangeResolver(SourceFile &File, unsigned Offset, unsigned Length) :
Impl(Implementation::createInstance(File, Offset, Length)) {}
RangeResolver::~RangeResolver() = default;
bool RangeResolver::walkToExprPre(Expr *E) {
if (!Impl->shouldEnter(E))
return false;
Impl->analyze(E);
Impl->enter(E);
return true;
}
bool RangeResolver::walkToStmtPre(Stmt *S) {
if (!Impl->shouldEnter(S))
return false;
Impl->analyze(S);
Impl->enter(S);
return true;
};
bool RangeResolver::walkToDeclPre(Decl *D, CharSourceRange Range) {
if (D->isImplicit())
return false;
if (!Impl->shouldEnter(D))
return false;
Impl->analyze(D);
Impl->enter(D);
return true;
}
bool RangeResolver::walkToExprPost(Expr *E) {
Impl->leave(E);
return !Impl->hasResult();
}
bool RangeResolver::walkToStmtPost(Stmt *S) {
Impl->leave(S);
return !Impl->hasResult();
};
bool RangeResolver::walkToDeclPost(Decl *D) {
Impl->leave(D);
return !Impl->hasResult();
}
bool RangeResolver::
visitDeclReference(ValueDecl *D, CharSourceRange Range, TypeDecl *CtorTyRef,
ExtensionDecl *ExtTyRef, Type T, ReferenceMetaData Data) {
Impl->analyzeDeclRef(D, Range.getStart(), T, Data);
return true;
}
ResolvedRangeInfo RangeResolver::resolve() {
if (!Impl)
return ResolvedRangeInfo({});
Impl->enter(ASTNode());
walk(Impl->File);
return Impl->getResult();
}
void swift::ide::getLocationInfoForClangNode(ClangNode ClangNode,
ClangImporter *Importer,
llvm::Optional<std::pair<unsigned, unsigned>> &DeclarationLoc,
StringRef &Filename) {
clang::ASTContext &ClangCtx = Importer->getClangASTContext();
clang::SourceManager &ClangSM = ClangCtx.getSourceManager();
clang::SourceRange SR = ClangNode.getLocation();
if (auto MD = dyn_cast_or_null<clang::ObjCMethodDecl>(ClangNode.getAsDecl())) {
SR = clang::SourceRange(MD->getSelectorStartLoc(),
MD->getDeclaratorEndLoc());
}
clang::CharSourceRange CharRange =
clang::Lexer::makeFileCharRange(clang::CharSourceRange::getTokenRange(SR),
ClangSM, ClangCtx.getLangOpts());
if (CharRange.isInvalid())
return;
std::pair<clang::FileID, unsigned>
Decomp = ClangSM.getDecomposedLoc(CharRange.getBegin());
if (!Decomp.first.isInvalid()) {
if (auto FE = ClangSM.getFileEntryForID(Decomp.first)) {
Filename = FE->getName();
std::pair<clang::FileID, unsigned>
EndDecomp = ClangSM.getDecomposedLoc(CharRange.getEnd());
DeclarationLoc = { Decomp.second, EndDecomp.second-Decomp.second };
}
}
}
static unsigned getCharLength(SourceManager &SM, SourceRange TokenRange) {
SourceLoc CharEndLoc = Lexer::getLocForEndOfToken(SM, TokenRange.End);
return SM.getByteDistance(TokenRange.Start, CharEndLoc);
}
void swift::ide::getLocationInfo(const ValueDecl *VD,
llvm::Optional<std::pair<unsigned, unsigned>> &DeclarationLoc,
StringRef &Filename) {
ASTContext &Ctx = VD->getASTContext();
SourceManager &SM = Ctx.SourceMgr;
auto ClangNode = VD->getClangNode();
if (VD->getLoc().isValid()) {
unsigned NameLen;
if (auto FD = dyn_cast<AbstractFunctionDecl>(VD)) {
SourceRange R = FD->getSignatureSourceRange();
if (R.isInvalid())
return;
NameLen = getCharLength(SM, R);
} else {
if (VD->hasName()) {
NameLen = VD->getBaseName().userFacingName().size();
} else {
NameLen = getCharLength(SM, VD->getLoc());
}
}
unsigned DeclBufID = SM.findBufferContainingLoc(VD->getLoc());
DeclarationLoc = { SM.getLocOffsetInBuffer(VD->getLoc(), DeclBufID),
NameLen };
Filename = SM.getIdentifierForBuffer(DeclBufID);
} else if (ClangNode) {
ClangImporter *Importer =
static_cast<ClangImporter*>(Ctx.getClangModuleLoader());
return getLocationInfoForClangNode(ClangNode, Importer,
DeclarationLoc, Filename);
}
}
CharSourceRange CallArgInfo::getEntireCharRange(const SourceManager &SM) const {
return CharSourceRange(SM, LabelRange.getStart(),
Lexer::getLocForEndOfToken(SM, ArgExp->getEndLoc()));
}
static Expr* getSingleNonImplicitChild(Expr *Parent) {
// If this expr is non-implicit, we are done.
if (!Parent->isImplicit())
return Parent;
// Collect all immediate children.
llvm::SmallVector<Expr*, 4> Children;
Parent->forEachImmediateChildExpr([&](Expr *E) {
Children.push_back(E);
return E;
});
// If more than one children are found, we are not sure the non-implicit node.
if (Children.size() != 1)
return Parent;
// Dig deeper if necessary.
return getSingleNonImplicitChild(Children[0]);
}
std::vector<CallArgInfo> swift::ide::
getCallArgInfo(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind) {
std::vector<CallArgInfo> InfoVec;
if (auto *TE = dyn_cast<TupleExpr>(Arg)) {
size_t ElemIndex = 0;
for (Expr *Elem : TE->getElements()) {
SourceLoc LabelStart(Elem->getStartLoc());
SourceLoc LabelEnd(LabelStart);
auto NameIdentifier = TE->getElementName(ElemIndex);
if (!NameIdentifier.empty()) {
LabelStart = TE->getElementNameLoc(ElemIndex);
if (EndKind == LabelRangeEndAt::LabelNameOnly)
LabelEnd = LabelStart.getAdvancedLoc(NameIdentifier.getLength());
}
InfoVec.push_back({getSingleNonImplicitChild(Elem),
CharSourceRange(SM, LabelStart, LabelEnd)});
++ElemIndex;
}
} else if (auto *PE = dyn_cast<ParenExpr>(Arg)) {
if (auto Sub = PE->getSubExpr())
InfoVec.push_back({getSingleNonImplicitChild(Sub),
CharSourceRange(Sub->getStartLoc(), 0)});
}
return InfoVec;
}
std::vector<CharSourceRange> swift::ide::
getCallArgLabelRanges(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind) {
std::vector<CharSourceRange> Ranges;
auto InfoVec = getCallArgInfo(SM, Arg, EndKind);
std::transform(InfoVec.begin(), InfoVec.end(), std::back_inserter(Ranges),
[](CallArgInfo &Info) { return Info.LabelRange; });
return Ranges;
}