blob: 69df1b361ac71782d5759b6e2892b2a01571ca3f [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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/AST/ASTDemangler.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/Sema/IDETypeChecking.h"
#include "swift/Markup/XMLUtils.h"
#include "swift/Subsystems.h"
#include "swift/IDE/IDERequests.h"
using namespace swift;
using namespace swift::ide;
namespace swift {
// Implement the IDE type zone.
#define SWIFT_TYPEID_ZONE IDE
#define SWIFT_TYPEID_HEADER "swift/IDE/IDERequestIDZone.def"
#include "swift/Basic/ImplementTypeIDZone.h"
#undef SWIFT_TYPEID_ZONE
#undef SWIFT_TYPEID_HEADER
}
// Define request evaluation functions for each of the IDE requests.
static AbstractRequestFunction *ideRequestFunctions[] = {
#define SWIFT_REQUEST(Zone, Name, Sig, Caching, LocOptions) \
reinterpret_cast<AbstractRequestFunction *>(&Name::evaluateRequest),
#include "swift/IDE/IDERequestIDZone.def"
#undef SWIFT_REQUEST
};
void swift::registerIDERequestFunctions(Evaluator &evaluator) {
evaluator.registerRequestFunctions(Zone::IDE,
ideRequestFunctions);
registerIDETypeCheckRequestFunctions(evaluator);
}
//----------------------------------------------------------------------------//
// Cusor info resolver
//----------------------------------------------------------------------------//
class CursorInfoResolver : public SourceEntityWalker {
SourceFile &SrcFile;
SourceLoc LocToResolve;
ResolvedCursorInfo CursorInfo;
Type ContainerType;
llvm::SmallVector<Expr*, 4> TrailingExprStack;
public:
explicit CursorInfoResolver(SourceFile &SrcFile) :
SrcFile(SrcFile), CursorInfo(&SrcFile) {}
ResolvedCursorInfo resolve(SourceLoc Loc);
SourceManager &getSourceMgr() const;
private:
bool walkToExprPre(Expr *E) override;
bool walkToExprPost(Expr *E) override;
bool walkToDeclPre(Decl *D, CharSourceRange Range) override;
bool walkToDeclPost(Decl *D) override;
bool walkToStmtPre(Stmt *S) override;
bool walkToStmtPost(Stmt *S) override;
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T,
ReferenceMetaData Data) override;
bool visitCallArgName(Identifier Name, CharSourceRange Range,
ValueDecl *D) override;
bool visitDeclarationArgumentName(Identifier Name, SourceLoc StartLoc,
ValueDecl *D) override;
bool visitModuleReference(ModuleEntity Mod, CharSourceRange Range) override;
bool rangeContainsLoc(SourceRange Range) const;
bool rangeContainsLoc(CharSourceRange Range) const;
bool isDone() const { return CursorInfo.isValid(); }
bool tryResolve(ValueDecl *D, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef,
SourceLoc Loc, bool IsRef, Type Ty = Type());
bool tryResolve(ModuleEntity Mod, SourceLoc Loc);
bool tryResolve(Stmt *St);
bool visitSubscriptReference(ValueDecl *D, CharSourceRange Range,
ReferenceMetaData Data,
bool IsOpenBracket) override;
};
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)
return false;
if (auto *VD = dyn_cast<VarDecl>(D)) {
// Handle references to the implicitly generated vars in case statements
// matching multiple patterns
if (VD->isImplicit()) {
if (auto *Parent = VD->getParentVarDecl()) {
D = Parent;
}
}
}
CursorInfo.setValueRef(D, CtorTyRef, ExtTyRef, IsRef, Ty, ContainerType);
return true;
}
bool CursorInfoResolver::tryResolve(ModuleEntity Mod, SourceLoc Loc) {
if (Loc == LocToResolve) {
CursorInfo.setModuleRef(Mod);
return true;
}
return false;
}
bool CursorInfoResolver::tryResolve(Stmt *St) {
if (auto *LST = dyn_cast<LabeledStmt>(St)) {
if (LST->getStartLoc() == LocToResolve) {
CursorInfo.setTrailingStmt(St);
return true;
}
}
if (auto *CS = dyn_cast<CaseStmt>(St)) {
if (CS->getStartLoc() == LocToResolve) {
CursorInfo.setTrailingStmt(St);
return true;
}
}
return false;
}
bool CursorInfoResolver::visitSubscriptReference(ValueDecl *D,
CharSourceRange Range,
ReferenceMetaData Data,
bool IsOpenBracket) {
// We should treat both open and close brackets equally
return visitDeclReference(D, Range, nullptr, nullptr, Type(), Data);
}
ResolvedCursorInfo CursorInfoResolver::resolve(SourceLoc Loc) {
assert(Loc.isValid());
LocToResolve = Loc;
CursorInfo.Loc = Loc;
walk(SrcFile);
return CursorInfo;
}
bool CursorInfoResolver::walkToDeclPre(Decl *D, CharSourceRange Range) {
if (!rangeContainsLoc(D->getSourceRangeIncludingAttrs()))
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) {
// Getting the character range for the statement, to account for interpolation
// strings. The token range for the interpolation string is the whole string,
// with begin/end locations pointing at the beginning of the string, so if
// there is a token location inside the string, it will seem as if it is out
// of the source range, unless we convert to character range.
// FIXME: Even implicit Stmts should have proper ranges that include any
// non-implicit Stmts (fix Stmts created for lazy vars).
if (!S->isImplicit() &&
!rangeContainsLoc(Lexer::getCharSourceRangeFromSourceRange(
getSourceMgr(), 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;
if (Data.isImplicit)
return true;
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 MemberLoc = ME->getNameLoc().getBaseNameLoc();
if (MemberLoc.isValid() && MemberLoc == LocToResolve) {
ContainerType = ME->getBase()->getType();
}
}
auto IsProperCursorLocation = E->getStartLoc() == LocToResolve;
// Handle cursor placement after `try` in ForceTry and OptionalTry Expr.
auto CheckLocation = [&IsProperCursorLocation, this](SourceLoc Loc) {
IsProperCursorLocation = Loc == LocToResolve || IsProperCursorLocation;
};
if (auto *FTE = dyn_cast<ForceTryExpr>(E)) {
CheckLocation(FTE->getExclaimLoc());
}
if (auto *OTE = dyn_cast<OptionalTryExpr>(E)) {
CheckLocation(OTE->getQuestionLoc());
}
// 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.setTrailingExpr(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());
}
bool CursorInfoResolver::rangeContainsLoc(SourceRange Range) const {
return getSourceMgr().rangeContainsTokenLoc(Range, LocToResolve);
}
bool CursorInfoResolver::rangeContainsLoc(CharSourceRange Range) const {
return Range.contains(LocToResolve);
}
llvm::Expected<ide::ResolvedCursorInfo>
CursorInfoRequest::evaluate(Evaluator &eval, CursorInfoOwner CI) const {
if (!CI.isValid())
return ResolvedCursorInfo();
CursorInfoResolver Resolver(*CI.File);
return Resolver.resolve(CI.Loc);
}
SourceLoc CursorInfoRequest::getNearestLoc() const {
return std::get<0>(getStorage()).Loc;
}
void swift::simple_display(llvm::raw_ostream &out, const CursorInfoOwner &owner) {
if (!owner.isValid())
return;
auto &SM = owner.File->getASTContext().SourceMgr;
out << SM.getIdentifierForBuffer(*owner.File->getBufferID());
auto LC = SM.getLineAndColumn(owner.Loc);
out << ":" << LC.first << ":" << LC.second;
}
void swift::ide::simple_display(llvm::raw_ostream &out,
const ide::ResolvedCursorInfo &info) {
if (info.isInvalid())
return;
out << "Resolved cursor info at ";
auto &SM = info.SF->getASTContext().SourceMgr;
out << SM.getIdentifierForBuffer(*info.SF->getBufferID());
auto LC = SM.getLineAndColumn(info.Loc);
out << ":" << LC.first << ":" << LC.second;
}
//----------------------------------------------------------------------------//
// Range info resolver
//----------------------------------------------------------------------------//
class RangeResolver : public SourceEntityWalker {
struct Implementation;
std::unique_ptr<Implementation> Impl;
bool walkToExprPre(Expr *E) override;
bool walkToExprPost(Expr *E) override;
bool walkToStmtPre(Stmt *S) override;
bool walkToStmtPost(Stmt *S) override;
bool walkToDeclPre(Decl *D, CharSourceRange Range) override;
bool walkToDeclPost(Decl *D) override;
bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T,
ReferenceMetaData Data) override;
ResolvedRangeInfo moveArrayToASTContext(ResolvedRangeInfo Info);
public:
RangeResolver(SourceFile &File, SourceLoc Start, SourceLoc End);
~RangeResolver();
ResolvedRangeInfo resolve();
};
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;
Analyzer.walk(N);
return Analyzer.isThrowing();
});
}
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);
}
bool isMultiTypeMemberDecl() {
if (StartMatches.empty() || EndMatches.empty())
return false;
// Multi-decls should have the same nominal type as a common parent
if (auto ParentDecl = Parent.dyn_cast<Decl *>())
return isa<NominalTypeDecl>(ParentDecl);
return false;
}
};
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::MultiTypeMemberDecl:
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};
}
}
llvm_unreachable("unhandled kind");
}
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, SourceLoc Start, SourceLoc End) {
if (Start.isInvalid() || End.isInvalid())
return nullptr;
auto AllTokens = File.getAllTokens();
// This points to the first token after or on the start loc.
auto StartIt = token_lower_bound(AllTokens, Start);
// 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, End);
// 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)));
}
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.
CompleteWalker(this).walk(EndNode);
// 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;
TheWalker.walk(N);
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();
auto NodeRange = Node.getSourceRange();
// Widen the node's source range to include all attributes to get a range
// match if a function with its attributes has been selected.
if (auto D = Node.dyn_cast<Decl *>())
NodeRange = D->getSourceRangeIncludingAttrs();
switch (getRangeMatchKind(NodeRange)) {
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)};
}
if (DCInfo.isMultiTypeMemberDecl()) {
postAnalysis(DCInfo.EndMatches.back());
Result = {RangeKind::MultiTypeMemberDecl,
ReturnInfo(),
TokensInRange,
getImmediateContext(),
/*Common Parent Expr*/ nullptr,
/*SinleEntry*/ true,
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 (Data.isImplicit || !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() = 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;
}
template <class T>
static ArrayRef<T> copyToContext(ASTContext &Ctx, ArrayRef<T> Arr) {
unsigned n = Arr.size();
auto buffer = Ctx.Allocate<T>(n);
for (unsigned i = 0; i != n; ++i) {
buffer[i] = Arr[i];
}
return buffer;
}
ResolvedRangeInfo
RangeResolver::moveArrayToASTContext(ResolvedRangeInfo Info) {
auto &Ctx = Impl->Ctx;
#define COPY(NAME) Info.NAME = copyToContext(Ctx, Info.NAME);
COPY(ContainedNodes)
COPY(DeclaredDecls)
COPY(ReferencedDecls)
#undef COPY
return Info;
}
ResolvedRangeInfo RangeResolver::resolve() {
if (!Impl)
return ResolvedRangeInfo();
Impl->enter(ASTNode());
walk(Impl->File);
return moveArrayToASTContext(Impl->getResult());
}
void swift::simple_display(llvm::raw_ostream &out,
const RangeInfoOwner &owner) {
if (!owner.isValid())
return;
auto &SM = owner.File->getASTContext().SourceMgr;
out << SM.getIdentifierForBuffer(*owner.File->getBufferID());
auto SLC = SM.getLineAndColumn(owner.StartLoc);
auto ELC = SM.getLineAndColumn(owner.EndLoc);
out << ": (" << SLC.first << ":" << SLC.second << ", "
<< ELC.first << ":" << ELC.second << ")";
}
RangeInfoOwner::RangeInfoOwner(SourceFile *File, unsigned Offset,
unsigned Length): File(File) {
SourceManager &SM = File->getASTContext().SourceMgr;
unsigned BufferId = File->getBufferID().getValue();
StartLoc = SM.getLocForOffset(BufferId, Offset);
EndLoc = SM.getLocForOffset(BufferId, Offset + Length);
}
llvm::Expected<ide::ResolvedRangeInfo>
RangeInfoRequest::evaluate(Evaluator &eval, RangeInfoOwner CI) const {
if (!CI.isValid())
return ResolvedRangeInfo();
return RangeResolver(*CI.File, CI.StartLoc, CI.EndLoc).resolve();
}
SourceLoc RangeInfoRequest::getNearestLoc() const {
return std::get<0>(getStorage()).StartLoc;
}
void
swift::ide::simple_display(llvm::raw_ostream &out, const ResolvedRangeInfo &info) {
info.print(out);
}
//----------------------------------------------------------------------------//
// ProvideDefaultImplForRequest
//----------------------------------------------------------------------------//
static Type getContextFreeInterfaceType(ValueDecl *VD) {
if (auto AFD = dyn_cast<AbstractFunctionDecl>(VD)) {
return AFD->getMethodInterfaceType();
}
return VD->getInterfaceType();
}
llvm::Expected<ArrayRef<ValueDecl*>>
ProvideDefaultImplForRequest::evaluate(Evaluator &eval, ValueDecl* VD) const {
// Skip decls that don't have valid names.
if (!VD->getFullName())
return ArrayRef<ValueDecl*>();
// Check if VD is from a protocol extension.
auto P = VD->getDeclContext()->getExtendedProtocolDecl();
if (!P)
return ArrayRef<ValueDecl*>();
SmallVector<ValueDecl*, 8> Results;
// Look up all decls in the protocol's inheritance chain for the ones with
// the same name with VD.
ResolvedMemberResult LookupResult =
resolveValueMember(*P->getInnermostDeclContext(),
P->getDeclaredInterfaceType(), VD->getFullName());
auto VDType = getContextFreeInterfaceType(VD);
for (auto Mem : LookupResult.getMemberDecls(InterestedMemberKind::All)) {
if (isa<ProtocolDecl>(Mem->getDeclContext())) {
if (Mem->isProtocolRequirement() &&
getContextFreeInterfaceType(Mem)->isEqual(VDType)) {
// We find a protocol requirement VD can provide default
// implementation for.
Results.push_back(Mem);
}
}
}
return copyToContext(VD->getASTContext(), llvm::makeArrayRef(Results));
}
//----------------------------------------------------------------------------//
// CollectOverriddenDeclsRequest
//----------------------------------------------------------------------------//
llvm::Expected<ArrayRef<ValueDecl*>>
CollectOverriddenDeclsRequest::evaluate(Evaluator &evaluator,
OverridenDeclsOwner Owner) const {
std::vector<ValueDecl*> results;
auto *VD = Owner.VD;
if (auto Overridden = VD->getOverriddenDecl()) {
results.push_back(Overridden);
while (Owner.Transitive && (Overridden = Overridden->getOverriddenDecl()))
results.push_back(Overridden);
}
for (auto Req : evaluateOrDefault(evaluator, ProvideDefaultImplForRequest(VD),
ArrayRef<ValueDecl*>())) {
results.push_back(Req);
}
if (Owner.IncludeProtocolRequirements) {
for (auto Satisfied : VD->getSatisfiedProtocolRequirements()) {
results.push_back(Satisfied);
}
}
return copyToContext(VD->getASTContext(), llvm::makeArrayRef(results));
}
//----------------------------------------------------------------------------//
// ResolveProtocolNameRequest
//----------------------------------------------------------------------------//
llvm::Expected<ProtocolDecl*>
ResolveProtocolNameRequest::evaluate(Evaluator &evaluator,
ProtocolNameOwner Input) const {
auto &ctx = Input.DC->getASTContext();
auto name = Input.Name;
// First try to solve by usr
ProtocolDecl *pd = dyn_cast_or_null<ProtocolDecl>(Demangle::
getTypeDeclForUSR(ctx, name));
if (!pd) {
// Second try to solve by mangled symbol name
pd = dyn_cast_or_null<ProtocolDecl>(Demangle::getTypeDeclForMangling(ctx, name));
}
if (!pd) {
// Thirdly try to solve by mangled type name
if (auto ty = Demangle::getTypeForMangling(ctx, name)) {
pd = dyn_cast_or_null<ProtocolDecl>(ty->getAnyGeneric());
}
}
return pd;
}