[Test] Add range-info test driver to swift-ide-test. NFC

This commit is contained in:
Xi Ge
2016-11-15 13:47:32 -08:00
parent 6a9298cd36
commit 5e8d8da380
5 changed files with 209 additions and 78 deletions

View File

@@ -209,13 +209,15 @@ struct ResolvedRangeInfo {
RangeKind Kind; RangeKind Kind;
Type Ty; Type Ty;
StringRef Content; StringRef Content;
ResolvedRangeInfo(RangeKind Kind, Type Ty, StringRef Content) : Kind(Kind), ResolvedRangeInfo(RangeKind Kind, Type Ty, StringRef Content): Kind(Kind),
Ty(Ty), Content(Content) {} Ty(Ty), Content(Content) {}
ResolvedRangeInfo(): ResolvedRangeInfo(RangeKind::Invalid, Type(), StringRef()) {}
void print(llvm::raw_ostream &OS);
}; };
class RangeResolver : public SourceEntityWalker { class RangeResolver : public SourceEntityWalker {
struct Implementation; struct Implementation;
Implementation &Impl; Implementation *Impl;
bool walkToExprPre(Expr *E) override; bool walkToExprPre(Expr *E) override;
bool walkToExprPost(Expr *E) override; bool walkToExprPost(Expr *E) override;
bool walkToStmtPre(Stmt *S) override; bool walkToStmtPre(Stmt *S) override;
@@ -224,6 +226,7 @@ class RangeResolver : public SourceEntityWalker {
bool walkToDeclPost(Decl *D) override; bool walkToDeclPost(Decl *D) override;
public: public:
RangeResolver(SourceFile &File, SourceLoc Start, SourceLoc End); RangeResolver(SourceFile &File, SourceLoc Start, SourceLoc End);
RangeResolver(SourceFile &File, unsigned Offset, unsigned Length);
ResolvedRangeInfo resolve(); ResolvedRangeInfo resolve();
~RangeResolver(); ~RangeResolver();
}; };

View File

@@ -13,6 +13,7 @@
#include "clang/Basic/Module.h" #include "clang/Basic/Module.h"
#include "clang/Index/USRGeneration.h" #include "clang/Index/USRGeneration.h"
#include "clang/Lex/Lexer.h" #include "clang/Lex/Lexer.h"
#include "clang/Basic/CharInfo.h"
#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/MemoryBuffer.h"
@@ -174,6 +175,25 @@ bool SemaLocResolver::visitModuleReference(ModuleEntity Mod,
return !tryResolve(Mod, Range.getStart()); return !tryResolve(Mod, Range.getStart());
} }
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::SingleStatement: OS << "SingleStatement"; break;
case RangeKind::Invalid: OS << "Invalid"; break;
}
OS << "</Kind>\n";
OS << "<Content>" << Content << "</Content>\n";
if (Ty) {
OS << "<Type>";
Ty->print(OS);
OS << "</Type>\n";
}
}
struct RangeResolver::Implementation { struct RangeResolver::Implementation {
SourceFile &File; SourceFile &File;
private: private:
@@ -214,9 +234,47 @@ private:
} }
} }
public: static SourceLoc getNonwhitespaceLocBefore(SourceManager &SM,
unsigned BufferID,
unsigned Offset) {
CharSourceRange entireRange = SM.getRangeForBuffer(BufferID);
StringRef Buffer = SM.extractText(entireRange);
const char *BufStart = Buffer.data();
if (Offset >= Buffer.size())
return SourceLoc();
for (unsigned Off = Offset; Off != 0; Off --) {
if (!clang::isWhitespace(*(BufStart + Off))) {
return SM.getLocForOffset(BufferID, Off);
}
}
return clang::isWhitespace(*BufStart) ? SourceLoc() :
SM.getLocForOffset(BufferID, 0);
}
static SourceLoc getNonwhitespaceLocAfter(SourceManager &SM,
unsigned BufferID,
unsigned Offset) {
CharSourceRange entireRange = SM.getRangeForBuffer(BufferID);
StringRef Buffer = SM.extractText(entireRange);
const char *BufStart = Buffer.data();
if (Offset >= Buffer.size())
return SourceLoc();
for (unsigned Off = Offset; Off < Buffer.size(); Off ++) {
if (!clang::isWhitespace(*(BufStart + Off))) {
return SM.getLocForOffset(BufferID, Off);
}
}
return SourceLoc();
}
Implementation(SourceFile &File, SourceLoc Start, SourceLoc End) : Implementation(SourceFile &File, SourceLoc Start, SourceLoc End) :
File(File), Start(Start), End(End), Content(getContent()) {} File(File), Start(Start), End(End), Content(getContent()) {}
public:
bool hasResult() { return Result.hasValue(); } bool hasResult() { return Result.hasValue(); }
void enter(ASTNode Node) { ContextStack.emplace_back(Node); } void enter(ASTNode Node) { ContextStack.emplace_back(Node); }
void leave(ASTNode Node) { void leave(ASTNode Node) {
@@ -224,6 +282,29 @@ public:
ContextStack.pop_back(); ContextStack.pop_back();
} }
static Implementation *createInstance(SourceFile &File, unsigned StartOff,
unsigned Length) {
SourceManager &SM = File.getASTContext().SourceMgr;
unsigned BufferId = File.getBufferID().getValue();
SourceLoc StartLoc = Implementation::getNonwhitespaceLocAfter(SM, BufferId,
StartOff);
SourceLoc EndLoc = Implementation::getNonwhitespaceLocBefore(SM, BufferId,
StartOff + Length - 1);
StartLoc = Lexer::getLocForStartOfToken(SM, StartLoc);
EndLoc = Lexer::getLocForStartOfToken(SM, EndLoc);
return StartLoc.isInvalid() || EndLoc.isInvalid() ? nullptr :
new Implementation(File, StartLoc, EndLoc);
}
static Implementation *createInstance(SourceFile &File, SourceLoc Start,
SourceLoc End) {
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 analyze(ASTNode Node) { void analyze(ASTNode Node) {
auto &DCInfo = getCurrentDC(); auto &DCInfo = getCurrentDC();
switch (getRangeMatchKind(Node.getSourceRange())) { switch (getRangeMatchKind(Node.getSourceRange())) {
@@ -283,53 +364,58 @@ private:
}; };
RangeResolver::RangeResolver(SourceFile &File, SourceLoc Start, SourceLoc End) : RangeResolver::RangeResolver(SourceFile &File, SourceLoc Start, SourceLoc End) :
Impl(*new Implementation(File, Start, End)) {} Impl(Implementation::createInstance(File, Start, End)) {}
RangeResolver::~RangeResolver() { delete &Impl; } RangeResolver::RangeResolver(SourceFile &File, unsigned Offset, unsigned Length) :
Impl(Implementation::createInstance(File, Offset, Length)) {}
RangeResolver::~RangeResolver() { if (Impl) delete Impl; }
bool RangeResolver::walkToExprPre(Expr *E) { bool RangeResolver::walkToExprPre(Expr *E) {
if (!Impl.shouldEnter(E)) if (!Impl->shouldEnter(E))
return false; return false;
Impl.analyze(E); Impl->analyze(E);
Impl.enter(E); Impl->enter(E);
return true; return true;
} }
bool RangeResolver::walkToStmtPre(Stmt *S) { bool RangeResolver::walkToStmtPre(Stmt *S) {
if (!Impl.shouldEnter(S)) if (!Impl->shouldEnter(S))
return false; return false;
Impl.analyze(S); Impl->analyze(S);
Impl.enter(S); Impl->enter(S);
return true; return true;
}; };
bool RangeResolver::walkToDeclPre(Decl *D, CharSourceRange Range) { bool RangeResolver::walkToDeclPre(Decl *D, CharSourceRange Range) {
if (!Impl.shouldEnter(D)) if (!Impl->shouldEnter(D))
return false; return false;
Impl.analyze(D); Impl->analyze(D);
Impl.enter(D); Impl->enter(D);
return true; return true;
} }
bool RangeResolver::walkToExprPost(Expr *E) { bool RangeResolver::walkToExprPost(Expr *E) {
Impl.leave(E); Impl->leave(E);
return !Impl.hasResult(); return !Impl->hasResult();
} }
bool RangeResolver::walkToStmtPost(Stmt *S) { bool RangeResolver::walkToStmtPost(Stmt *S) {
Impl.leave(S); Impl->leave(S);
return !Impl.hasResult(); return !Impl->hasResult();
}; };
bool RangeResolver::walkToDeclPost(Decl *D) { bool RangeResolver::walkToDeclPost(Decl *D) {
Impl.leave(D); Impl->leave(D);
return !Impl.hasResult(); return !Impl->hasResult();
} }
ResolvedRangeInfo RangeResolver::resolve() { ResolvedRangeInfo RangeResolver::resolve() {
Impl.enter(ASTNode()); if (!Impl)
walk(Impl.File); return ResolvedRangeInfo();
return Impl.getResult(); Impl->enter(ASTNode());
walk(Impl->File);
return Impl->getResult();
} }
void swift::ide::getLocationInfoForClangNode(ClangNode ClangNode, void swift::ide::getLocationInfoForClangNode(ClangNode ClangNode,

View File

@@ -0,0 +1,40 @@
func foo() -> Int{
var aaa = 1 + 2
aaa = aaa + 3
if aaa == 3 { aaa = 4 }
return aaa
}
func foo1() -> Int { return 0 }
class C { func foo() {} }
struct S { func foo() {} }
// RUN: %target-swift-ide-test -range -pos=8:1 -end-pos 8:32 -source-filename %s | %FileCheck %s -check-prefix=CHECK1
// RUN: %target-swift-ide-test -range -pos=9:1 -end-pos 9:26 -source-filename %s | %FileCheck %s -check-prefix=CHECK2
// RUN: %target-swift-ide-test -range -pos=10:1 -end-pos 10:27 -source-filename %s | %FileCheck %s -check-prefix=CHECK3
// RUN: %target-swift-ide-test -range -pos=3:1 -end-pos=4:26 -source-filename %s | %FileCheck %s -check-prefix=CHECK4
// RUN: %target-swift-ide-test -range -pos=3:1 -end-pos=5:13 -source-filename %s | %FileCheck %s -check-prefix=CHECK5
// RUN: %target-swift-ide-test -range -pos=4:1 -end-pos=5:13 -source-filename %s | %FileCheck %s -check-prefix=CHECK6
// CHECK1: <Kind>SingleDecl</Kind>
// CHECK1-NEXT: <Content>func foo1() -> Int { return 0 }</Content>
// CHECK2: <Kind>SingleDecl</Kind>
// CHECK2-NEXT: <Content>class C { func foo() {} }</Content>
// CHECK3: <Kind>SingleDecl</Kind>
// CHECK3-NEXT: <Content>struct S { func foo() {} }</Content>
// CHECK4: <Kind>MultiStatement</Kind>
// CHECK4-NEXT: <Content>aaa = aaa + 3
// CHECK4-NEXT: if aaa == 3 { aaa = 4 }</Content>
// CHECK5: <Kind>MultiStatement</Kind>
// CHECK5-NEXT: <Content>aaa = aaa + 3
// CHECK5-NEXT: if aaa == 3 { aaa = 4 }
// CHECK5-NEXT: return aaa</Content>
// CHECK6: <Kind>MultiStatement</Kind>
// CHECK6-NEXT: if aaa == 3 { aaa = 4 }
// CHECK6-NEXT: return aaa</Content>

View File

@@ -493,7 +493,7 @@ static StringRef getSourceToken(unsigned Offset,
return L.getTokenAt(Loc).getText(); return L.getTokenAt(Loc).getText();
} }
static llvm::Optional<unsigned> static llvm::Optional<unsigned>
mapOffsetToOlderSnapshot(unsigned Offset, mapOffsetToOlderSnapshot(unsigned Offset,
ImmutableTextSnapshotRef NewSnap, ImmutableTextSnapshotRef NewSnap,
ImmutableTextSnapshotRef OldSnap) { ImmutableTextSnapshotRef OldSnap) {
@@ -519,7 +519,7 @@ mapOffsetToOlderSnapshot(unsigned Offset,
return Offset; return Offset;
} }
static llvm::Optional<unsigned> static llvm::Optional<unsigned>
mapOffsetToNewerSnapshot(unsigned Offset, mapOffsetToNewerSnapshot(unsigned Offset,
ImmutableTextSnapshotRef OldSnap, ImmutableTextSnapshotRef OldSnap,
ImmutableTextSnapshotRef NewSnap) { ImmutableTextSnapshotRef NewSnap) {
@@ -1023,43 +1023,6 @@ static void resolveRange(SwiftLangSupport &Lang,
unsigned Length; unsigned Length;
std::function<void(const RangeInfo&)> Receiver; std::function<void(const RangeInfo&)> Receiver;
static SourceLoc getNonwhitespaceLocBefore(SourceManager &SM,
unsigned BufferID,
unsigned Offset) {
CharSourceRange entireRange = SM.getRangeForBuffer(BufferID);
StringRef Buffer = SM.extractText(entireRange);
const char *BufStart = Buffer.data();
if (Offset >= Buffer.size())
return SourceLoc();
for (unsigned Off = Offset; Off != 0; Off --) {
if (!clang::isWhitespace(*(BufStart + Off))) {
return SM.getLocForOffset(BufferID, Off);
}
}
return clang::isWhitespace(*BufStart) ? SourceLoc() :
SM.getLocForOffset(BufferID, 0);
}
static SourceLoc getNonwhitespaceLocAfter(SourceManager &SM,
unsigned BufferID,
unsigned Offset) {
CharSourceRange entireRange = SM.getRangeForBuffer(BufferID);
StringRef Buffer = SM.extractText(entireRange);
const char *BufStart = Buffer.data();
if (Offset >= Buffer.size())
return SourceLoc();
for (unsigned Off = Offset; Off < Buffer.size(); Off ++) {
if (!clang::isWhitespace(*(BufStart + Off))) {
return SM.getLocForOffset(BufferID, Off);
}
}
return SourceLoc();
}
public: public:
RangeInfoConsumer(StringRef InputFile, unsigned Offset, unsigned Length, RangeInfoConsumer(StringRef InputFile, unsigned Offset, unsigned Length,
SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok, SwiftLangSupport &Lang, SwiftInvocationRef ASTInvok,
@@ -1070,25 +1033,10 @@ static void resolveRange(SwiftLangSupport &Lang,
void handlePrimaryAST(ASTUnitRef AstUnit) override { void handlePrimaryAST(ASTUnitRef AstUnit) override {
auto &CompIns = AstUnit->getCompilerInstance(); auto &CompIns = AstUnit->getCompilerInstance();
unsigned BufferID = AstUnit->getPrimarySourceFile().getBufferID().getValue();
SourceManager &SM = CompIns.getSourceMgr();
SourceLoc StartLoc = getNonwhitespaceLocAfter(SM, BufferID, Offset);
SourceLoc EndLoc = getNonwhitespaceLocBefore(SM, BufferID,
Offset + Length - 1);
StartLoc = Lexer::getLocForStartOfToken(SM, StartLoc);
EndLoc = Lexer::getLocForStartOfToken(SM, EndLoc);
if (StartLoc.isInvalid() || EndLoc.isInvalid()) {
Receiver({});
return;
}
if (trace::enabled()) { if (trace::enabled()) {
// FIXME: Implement tracing // FIXME: Implement tracing
} }
RangeResolver Resolver(AstUnit->getPrimarySourceFile(), StartLoc, EndLoc); RangeResolver Resolver(AstUnit->getPrimarySourceFile(), Offset, Length);
ResolvedRangeInfo Info = Resolver.resolve(); ResolvedRangeInfo Info = Resolver.resolve();
CompilerInvocation CompInvok; CompilerInvocation CompInvok;

View File

@@ -95,6 +95,7 @@ enum class ActionType {
GenerateModuleAPIDescription, GenerateModuleAPIDescription,
DiffModuleAPI, DiffModuleAPI,
ReconstructType, ReconstructType,
Range,
}; };
class NullDebuggerClient : public DebuggerClient { class NullDebuggerClient : public DebuggerClient {
@@ -208,6 +209,9 @@ Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None),
clEnumValN(ActionType::PrintModuleGroups, clEnumValN(ActionType::PrintModuleGroups,
"print-module-groups", "print-module-groups",
"Print group names in a module"), "Print group names in a module"),
clEnumValN(ActionType::Range,
"range",
"Print information about a given range"),
clEnumValEnd)); clEnumValEnd));
static llvm::cl::opt<std::string> static llvm::cl::opt<std::string>
@@ -523,6 +527,9 @@ DeclToPrint("decl-to-print",
static llvm::cl::opt<std::string> static llvm::cl::opt<std::string>
LineColumnPair("pos", llvm::cl::desc("Line:Column pair")); LineColumnPair("pos", llvm::cl::desc("Line:Column pair"));
static llvm::cl::opt<std::string>
EndLineColumnPair("end-pos", llvm::cl::desc("Line:Column pair"));
static llvm::cl::opt<std::string> static llvm::cl::opt<std::string>
USR("usr", llvm::cl::desc("USR")); USR("usr", llvm::cl::desc("USR"));
@@ -2615,6 +2622,48 @@ static int doReconstructType(const CompilerInvocation &InitInvok,
return 0; return 0;
} }
static int doPrintRangeInfo(const CompilerInvocation &InitInvok,
StringRef SourceFileName,
StringRef StartPos,
StringRef EndPos) {
auto StartOp = parseLineCol(StartPos);
auto EndOp = parseLineCol(EndPos);
if (!StartOp.hasValue() || !EndOp.hasValue())
return 1;
auto StartLineCol = StartOp.getValue();
auto EndLineCol = EndOp.getValue();
CompilerInvocation Invocation(InitInvok);
Invocation.addInputFilename(SourceFileName);
Invocation.getLangOptions().DisableAvailabilityChecking = false;
CompilerInstance CI;
// Display diagnostics to stderr.
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
if (CI.setup(Invocation))
return 1;
CI.performSema();
SourceFile *SF = nullptr;
for (auto Unit : CI.getMainModule()->getFiles()) {
SF = dyn_cast<SourceFile>(Unit);
if (SF)
break;
}
assert(SF && "no source file?");
assert(SF->getBufferID().hasValue() && "no buffer id?");
SourceManager &SM = SF->getASTContext().SourceMgr;
unsigned bufferID = SF->getBufferID().getValue();
SourceLoc StartLoc = SM.getLocForLineCol(bufferID, StartLineCol.first,
StartLineCol.second);
SourceLoc EndLoc = SM.getLocForLineCol(bufferID, EndLineCol.first,
EndLineCol.second);
RangeResolver Resolver(*SF, StartLoc, EndLoc);
ResolvedRangeInfo Result = Resolver.resolve();
Result.print(llvm::outs());
return 0;
}
static int doPrintUSRs(const CompilerInvocation &InitInvok, static int doPrintUSRs(const CompilerInvocation &InitInvok,
StringRef SourceFilename) { StringRef SourceFilename) {
CompilerInvocation Invocation(InitInvok); CompilerInvocation Invocation(InitInvok);
@@ -3026,6 +3075,11 @@ int main(int argc, char *argv[]) {
case ActionType::ReconstructType: case ActionType::ReconstructType:
ExitCode = doReconstructType(InitInvok, options::SourceFilename); ExitCode = doReconstructType(InitInvok, options::SourceFilename);
break; break;
case ActionType::Range:
ExitCode = doPrintRangeInfo(InitInvok, options::SourceFilename,
options::LineColumnPair,
options::EndLineColumnPair);
break;
} }
if (options::PrintStats) if (options::PrintStats)