mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
IDE+Evaluator: refactor range info resolver to an IDE evaluator request. NFC
This commit is contained in:
@@ -598,7 +598,7 @@ bool NameMatcher::tryResolve(ASTWalker::ParentTy Node, SourceLoc NameLoc,
|
||||
return WasResolved;
|
||||
};
|
||||
|
||||
void ResolvedRangeInfo::print(llvm::raw_ostream &OS) {
|
||||
void ResolvedRangeInfo::print(llvm::raw_ostream &OS) const {
|
||||
OS << "<Kind>";
|
||||
switch (Kind) {
|
||||
case RangeKind::SingleExpression: OS << "SingleExpression"; break;
|
||||
@@ -698,43 +698,6 @@ 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;
|
||||
Analyzer.walk(N);
|
||||
return Analyzer.isThrowing();
|
||||
});
|
||||
}
|
||||
|
||||
ReturnInfo::
|
||||
ReturnInfo(ASTContext &Ctx, ArrayRef<ReturnInfo> Branches):
|
||||
ReturnType(Ctx.TheErrorType.getPointer()), Exit(ExitState::Unsure) {
|
||||
@@ -752,653 +715,6 @@ ReturnInfo(ASTContext &Ctx, ArrayRef<ReturnInfo> Branches):
|
||||
}
|
||||
}
|
||||
|
||||
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, 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.
|
||||
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(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,
|
||||
|
||||
Reference in New Issue
Block a user