forked from OSchip/llvm-project
Add completion to the query parser, and hook it up to clang-query.
Differential Revision: http://llvm-reviews.chandlerc.com/D2263 llvm-svn: 200604
This commit is contained in:
parent
c31176da02
commit
d9a0f254bc
|
@ -15,6 +15,8 @@
|
|||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace clang::ast_matchers::dynamic;
|
||||
|
||||
|
@ -25,10 +27,10 @@ namespace query {
|
|||
// non-whitespace characters) from the start of region [Begin,End). If no word
|
||||
// is found before End, return StringRef(). Begin is adjusted to exclude the
|
||||
// lexed region.
|
||||
static StringRef LexWord(const char *&Begin, const char *End) {
|
||||
StringRef QueryParser::lexWord() {
|
||||
while (true) {
|
||||
if (Begin == End)
|
||||
return StringRef();
|
||||
return StringRef(Begin, 0);
|
||||
|
||||
if (!isWhitespace(*Begin))
|
||||
break;
|
||||
|
@ -46,8 +48,60 @@ static StringRef LexWord(const char *&Begin, const char *End) {
|
|||
}
|
||||
}
|
||||
|
||||
static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) {
|
||||
unsigned Value = StringSwitch<unsigned>(ValStr)
|
||||
// This is the StringSwitch-alike used by lexOrCompleteWord below. See that
|
||||
// function for details.
|
||||
template <typename T> struct QueryParser::LexOrCompleteWord {
|
||||
StringSwitch<T> Switch;
|
||||
|
||||
QueryParser *P;
|
||||
StringRef Word;
|
||||
// Set to the completion point offset in Word, or StringRef::npos if
|
||||
// completion point not in Word.
|
||||
size_t WordCompletionPos;
|
||||
|
||||
LexOrCompleteWord(QueryParser *P, StringRef Word, size_t WCP)
|
||||
: Switch(Word), P(P), Word(Word), WordCompletionPos(WCP) {}
|
||||
|
||||
template <unsigned N>
|
||||
LexOrCompleteWord &Case(const char (&S)[N], const T &Value,
|
||||
bool IsCompletion = true) {
|
||||
StringRef CaseStr(S, N - 1);
|
||||
|
||||
if (WordCompletionPos == StringRef::npos)
|
||||
Switch.Case(S, Value);
|
||||
else if (N != 1 && IsCompletion && WordCompletionPos <= CaseStr.size() &&
|
||||
CaseStr.substr(0, WordCompletionPos) ==
|
||||
Word.substr(0, WordCompletionPos))
|
||||
P->Completions.push_back(LineEditor::Completion(
|
||||
(CaseStr.substr(WordCompletionPos) + " ").str(), CaseStr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
T Default(const T& Value) const {
|
||||
return Switch.Default(Value);
|
||||
}
|
||||
};
|
||||
|
||||
// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
|
||||
// that can be used like a llvm::StringSwitch<T>, but adds cases as possible
|
||||
// completions if the lexed word contains the completion point.
|
||||
template <typename T>
|
||||
QueryParser::LexOrCompleteWord<T>
|
||||
QueryParser::lexOrCompleteWord(StringRef &Word) {
|
||||
Word = lexWord();
|
||||
size_t WordCompletionPos = StringRef::npos;
|
||||
if (CompletionPos && CompletionPos <= Word.data() + Word.size()) {
|
||||
if (CompletionPos < Word.data())
|
||||
WordCompletionPos = 0;
|
||||
else
|
||||
WordCompletionPos = CompletionPos - Word.data();
|
||||
}
|
||||
return LexOrCompleteWord<T>(this, Word, WordCompletionPos);
|
||||
}
|
||||
|
||||
QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
|
||||
StringRef ValStr;
|
||||
unsigned Value = lexOrCompleteWord<unsigned>(ValStr)
|
||||
.Case("false", 0)
|
||||
.Case("true", 1)
|
||||
.Default(~0u);
|
||||
|
@ -57,8 +111,9 @@ static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) {
|
|||
return new SetQuery<bool>(Var, Value);
|
||||
}
|
||||
|
||||
static QueryRef ParseSetOutputKind(StringRef ValStr) {
|
||||
unsigned OutKind = StringSwitch<unsigned>(ValStr)
|
||||
QueryRef QueryParser::parseSetOutputKind() {
|
||||
StringRef ValStr;
|
||||
unsigned OutKind = lexOrCompleteWord<unsigned>(ValStr)
|
||||
.Case("diag", OK_Diag)
|
||||
.Case("print", OK_Print)
|
||||
.Case("dump", OK_Dump)
|
||||
|
@ -70,9 +125,9 @@ static QueryRef ParseSetOutputKind(StringRef ValStr) {
|
|||
return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
|
||||
}
|
||||
|
||||
static QueryRef EndQuery(const char *Begin, const char *End, QueryRef Q) {
|
||||
QueryRef QueryParser::endQuery(QueryRef Q) {
|
||||
const char *Extra = Begin;
|
||||
if (!LexWord(Begin, End).empty())
|
||||
if (!lexWord().empty())
|
||||
return new InvalidQuery("unexpected extra input: '" +
|
||||
StringRef(Extra, End - Extra) + "'");
|
||||
return Q;
|
||||
|
@ -92,15 +147,12 @@ enum ParsedQueryVariable {
|
|||
PQV_BindRoot
|
||||
};
|
||||
|
||||
QueryRef ParseQuery(StringRef Line) {
|
||||
const char *Begin = Line.data();
|
||||
const char *End = Line.data() + Line.size();
|
||||
|
||||
StringRef CommandStr = LexWord(Begin, End);
|
||||
ParsedQueryKind QKind = StringSwitch<ParsedQueryKind>(CommandStr)
|
||||
QueryRef QueryParser::doParse() {
|
||||
StringRef CommandStr;
|
||||
ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
|
||||
.Case("", PQK_NoOp)
|
||||
.Case("help", PQK_Help)
|
||||
.Case("m", PQK_Match)
|
||||
.Case("m", PQK_Match, /*IsCompletion=*/false)
|
||||
.Case("match", PQK_Match)
|
||||
.Case("set", PQK_Set)
|
||||
.Default(PQK_Invalid);
|
||||
|
@ -110,50 +162,57 @@ QueryRef ParseQuery(StringRef Line) {
|
|||
return new NoOpQuery;
|
||||
|
||||
case PQK_Help:
|
||||
return EndQuery(Begin, End, new HelpQuery);
|
||||
return endQuery(new HelpQuery);
|
||||
|
||||
case PQK_Match: {
|
||||
Diagnostics Diag;
|
||||
Optional<DynTypedMatcher> Matcher =
|
||||
Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag);
|
||||
if (!Matcher) {
|
||||
std::string ErrStr;
|
||||
llvm::raw_string_ostream OS(ErrStr);
|
||||
Diag.printToStreamFull(OS);
|
||||
return new InvalidQuery(OS.str());
|
||||
if (CompletionPos) {
|
||||
std::vector<MatcherCompletion> Comps = Parser::completeExpression(
|
||||
StringRef(Begin, End - Begin), CompletionPos - Begin);
|
||||
for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
|
||||
E = Comps.end();
|
||||
I != E; ++I) {
|
||||
Completions.push_back(
|
||||
LineEditor::Completion(I->TypedText, I->MatcherDecl));
|
||||
}
|
||||
return QueryRef();
|
||||
} else {
|
||||
Diagnostics Diag;
|
||||
Optional<DynTypedMatcher> Matcher =
|
||||
Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag);
|
||||
if (!Matcher) {
|
||||
std::string ErrStr;
|
||||
llvm::raw_string_ostream OS(ErrStr);
|
||||
Diag.printToStreamFull(OS);
|
||||
return new InvalidQuery(OS.str());
|
||||
}
|
||||
return new MatchQuery(*Matcher);
|
||||
}
|
||||
return new MatchQuery(*Matcher);
|
||||
}
|
||||
|
||||
case PQK_Set: {
|
||||
StringRef VarStr = LexWord(Begin, End);
|
||||
StringRef VarStr;
|
||||
ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr)
|
||||
.Case("output", PQV_Output)
|
||||
.Case("bind-root", PQV_BindRoot)
|
||||
.Default(PQV_Invalid);
|
||||
if (VarStr.empty())
|
||||
return new InvalidQuery("expected variable name");
|
||||
|
||||
ParsedQueryVariable Var = StringSwitch<ParsedQueryVariable>(VarStr)
|
||||
.Case("output", PQV_Output)
|
||||
.Case("bind-root", PQV_BindRoot)
|
||||
.Default(PQV_Invalid);
|
||||
if (Var == PQV_Invalid)
|
||||
return new InvalidQuery("unknown variable: '" + VarStr + "'");
|
||||
|
||||
StringRef ValStr = LexWord(Begin, End);
|
||||
if (ValStr.empty())
|
||||
return new InvalidQuery("expected variable value");
|
||||
|
||||
QueryRef Q;
|
||||
switch (Var) {
|
||||
case PQV_Output:
|
||||
Q = ParseSetOutputKind(ValStr);
|
||||
Q = parseSetOutputKind();
|
||||
break;
|
||||
case PQV_BindRoot:
|
||||
Q = ParseSetBool(&QuerySession::BindRoot, ValStr);
|
||||
Q = parseSetBool(&QuerySession::BindRoot);
|
||||
break;
|
||||
case PQV_Invalid:
|
||||
llvm_unreachable("Invalid query kind");
|
||||
}
|
||||
|
||||
return EndQuery(Begin, End, Q);
|
||||
return endQuery(Q);
|
||||
}
|
||||
|
||||
case PQK_Invalid:
|
||||
|
@ -163,5 +222,18 @@ QueryRef ParseQuery(StringRef Line) {
|
|||
llvm_unreachable("Invalid query kind");
|
||||
}
|
||||
|
||||
QueryRef QueryParser::parse(StringRef Line) {
|
||||
return QueryParser(Line).doParse();
|
||||
}
|
||||
|
||||
std::vector<LineEditor::Completion> QueryParser::complete(StringRef Line,
|
||||
size_t Pos) {
|
||||
QueryParser P(Line);
|
||||
P.CompletionPos = Line.data() + Pos;
|
||||
|
||||
P.doParse();
|
||||
return P.Completions;
|
||||
}
|
||||
|
||||
} // namespace query
|
||||
} // namespace clang
|
||||
|
|
|
@ -12,14 +12,55 @@
|
|||
|
||||
#include "Query.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include "llvm/LineEditor/LineEditor.h"
|
||||
|
||||
namespace clang {
|
||||
namespace query {
|
||||
|
||||
/// \brief Parse \p Line.
|
||||
///
|
||||
/// \return A reference to the parsed query object, which may be an
|
||||
/// \c InvalidQuery if a parse error occurs.
|
||||
QueryRef ParseQuery(StringRef Line);
|
||||
class QuerySession;
|
||||
|
||||
class QueryParser {
|
||||
public:
|
||||
/// Parse \param Line as a query.
|
||||
///
|
||||
/// \return A QueryRef representing the query, which may be an InvalidQuery.
|
||||
static QueryRef parse(StringRef Line);
|
||||
|
||||
/// Compute a list of completions for \param Line assuming a cursor at
|
||||
/// \param Pos characters past the start of \param Line, ordered from most
|
||||
/// likely to least likely.
|
||||
///
|
||||
/// \return A vector of completions for \param Line.
|
||||
static std::vector<llvm::LineEditor::Completion> complete(StringRef Line,
|
||||
size_t Pos);
|
||||
|
||||
private:
|
||||
QueryParser(StringRef Line)
|
||||
: Begin(Line.data()), End(Line.data() + Line.size()), CompletionPos(0) {}
|
||||
|
||||
StringRef lexWord();
|
||||
|
||||
template <typename T> struct LexOrCompleteWord;
|
||||
template <typename T> LexOrCompleteWord<T> lexOrCompleteWord(StringRef &Str);
|
||||
|
||||
QueryRef parseSetBool(bool QuerySession::*Var);
|
||||
QueryRef parseSetOutputKind();
|
||||
|
||||
QueryRef endQuery(QueryRef Q);
|
||||
|
||||
/// \brief Parse [\p Begin,\p End).
|
||||
///
|
||||
/// \return A reference to the parsed query object, which may be an
|
||||
/// \c InvalidQuery if a parse error occurs.
|
||||
QueryRef doParse();
|
||||
|
||||
const char *Begin;
|
||||
const char *End;
|
||||
|
||||
const char *CompletionPos;
|
||||
std::vector<llvm::LineEditor::Completion> Completions;
|
||||
};
|
||||
|
||||
} // namespace query
|
||||
} // namespace clang
|
||||
|
|
|
@ -96,7 +96,7 @@ int main(int argc, const char **argv) {
|
|||
for (cl::list<std::string>::iterator I = Commands.begin(),
|
||||
E = Commands.end();
|
||||
I != E; ++I) {
|
||||
QueryRef Q = ParseQuery(I->c_str());
|
||||
QueryRef Q = QueryParser::parse(I->c_str());
|
||||
if (!Q->run(llvm::outs(), QS))
|
||||
return 1;
|
||||
}
|
||||
|
@ -113,15 +113,16 @@ int main(int argc, const char **argv) {
|
|||
std::string Line;
|
||||
std::getline(Input, Line);
|
||||
|
||||
QueryRef Q = ParseQuery(Line.c_str());
|
||||
QueryRef Q = QueryParser::parse(Line.c_str());
|
||||
if (!Q->run(llvm::outs(), QS))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LineEditor LE("clang-query");
|
||||
LE.setListCompleter(QueryParser::complete);
|
||||
while (llvm::Optional<std::string> Line = LE.readLine()) {
|
||||
QueryRef Q = ParseQuery(*Line);
|
||||
QueryRef Q = QueryParser::parse(*Line);
|
||||
Q->run(llvm::outs(), QS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,77 +11,102 @@
|
|||
#include "Query.h"
|
||||
#include "QuerySession.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "llvm/LineEditor/LineEditor.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::query;
|
||||
|
||||
TEST(QueryParser, NoOp) {
|
||||
QueryRef Q = ParseQuery("");
|
||||
QueryRef Q = QueryParser::parse("");
|
||||
EXPECT_TRUE(isa<NoOpQuery>(Q));
|
||||
|
||||
Q = ParseQuery("\n");
|
||||
Q = QueryParser::parse("\n");
|
||||
EXPECT_TRUE(isa<NoOpQuery>(Q));
|
||||
}
|
||||
|
||||
TEST(QueryParser, Invalid) {
|
||||
QueryRef Q = ParseQuery("foo");
|
||||
QueryRef Q = QueryParser::parse("foo");
|
||||
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||
EXPECT_EQ("unknown command: foo", cast<InvalidQuery>(Q)->ErrStr);
|
||||
}
|
||||
|
||||
TEST(QueryParser, Help) {
|
||||
QueryRef Q = ParseQuery("help");
|
||||
QueryRef Q = QueryParser::parse("help");
|
||||
ASSERT_TRUE(isa<HelpQuery>(Q));
|
||||
|
||||
Q = ParseQuery("help me");
|
||||
Q = QueryParser::parse("help me");
|
||||
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||
EXPECT_EQ("unexpected extra input: ' me'", cast<InvalidQuery>(Q)->ErrStr);
|
||||
}
|
||||
|
||||
TEST(QueryParser, Set) {
|
||||
QueryRef Q = ParseQuery("set");
|
||||
QueryRef Q = QueryParser::parse("set");
|
||||
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||
EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
|
||||
|
||||
Q = ParseQuery("set foo bar");
|
||||
Q = QueryParser::parse("set foo bar");
|
||||
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||
EXPECT_EQ("unknown variable: 'foo'", cast<InvalidQuery>(Q)->ErrStr);
|
||||
|
||||
Q = ParseQuery("set output");
|
||||
Q = QueryParser::parse("set output");
|
||||
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||
EXPECT_EQ("expected variable value", cast<InvalidQuery>(Q)->ErrStr);
|
||||
EXPECT_EQ("expected 'diag', 'print' or 'dump', got ''",
|
||||
cast<InvalidQuery>(Q)->ErrStr);
|
||||
|
||||
Q = ParseQuery("set bind-root true foo");
|
||||
Q = QueryParser::parse("set bind-root true foo");
|
||||
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||
EXPECT_EQ("unexpected extra input: ' foo'", cast<InvalidQuery>(Q)->ErrStr);
|
||||
|
||||
Q = ParseQuery("set output foo");
|
||||
Q = QueryParser::parse("set output foo");
|
||||
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||
EXPECT_EQ("expected 'diag', 'print' or 'dump', got 'foo'",
|
||||
cast<InvalidQuery>(Q)->ErrStr);
|
||||
|
||||
Q = ParseQuery("set output dump");
|
||||
Q = QueryParser::parse("set output dump");
|
||||
ASSERT_TRUE(isa<SetQuery<OutputKind> >(Q));
|
||||
EXPECT_EQ(&QuerySession::OutKind, cast<SetQuery<OutputKind> >(Q)->Var);
|
||||
EXPECT_EQ(OK_Dump, cast<SetQuery<OutputKind> >(Q)->Value);
|
||||
|
||||
Q = ParseQuery("set bind-root foo");
|
||||
Q = QueryParser::parse("set bind-root foo");
|
||||
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||
EXPECT_EQ("expected 'true' or 'false', got 'foo'",
|
||||
cast<InvalidQuery>(Q)->ErrStr);
|
||||
|
||||
Q = ParseQuery("set bind-root true");
|
||||
Q = QueryParser::parse("set bind-root true");
|
||||
ASSERT_TRUE(isa<SetQuery<bool> >(Q));
|
||||
EXPECT_EQ(&QuerySession::BindRoot, cast<SetQuery<bool> >(Q)->Var);
|
||||
EXPECT_EQ(true, cast<SetQuery<bool> >(Q)->Value);
|
||||
}
|
||||
|
||||
TEST(QueryParser, Match) {
|
||||
QueryRef Q = ParseQuery("match decl()");
|
||||
QueryRef Q = QueryParser::parse("match decl()");
|
||||
ASSERT_TRUE(isa<MatchQuery>(Q));
|
||||
EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Decl>());
|
||||
|
||||
Q = ParseQuery("m stmt()");
|
||||
Q = QueryParser::parse("m stmt()");
|
||||
ASSERT_TRUE(isa<MatchQuery>(Q));
|
||||
EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Stmt>());
|
||||
}
|
||||
|
||||
TEST(QueryParser, Complete) {
|
||||
std::vector<llvm::LineEditor::Completion> Comps =
|
||||
QueryParser::complete("", 0);
|
||||
ASSERT_EQ(3u, Comps.size());
|
||||
EXPECT_EQ("help ", Comps[0].TypedText);
|
||||
EXPECT_EQ("help", Comps[0].DisplayText);
|
||||
EXPECT_EQ("match ", Comps[1].TypedText);
|
||||
EXPECT_EQ("match", Comps[1].DisplayText);
|
||||
EXPECT_EQ("set ", Comps[2].TypedText);
|
||||
EXPECT_EQ("set", Comps[2].DisplayText);
|
||||
|
||||
Comps = QueryParser::complete("set o", 5);
|
||||
ASSERT_EQ(1u, Comps.size());
|
||||
EXPECT_EQ("utput ", Comps[0].TypedText);
|
||||
EXPECT_EQ("output", Comps[0].DisplayText);
|
||||
|
||||
Comps = QueryParser::complete("match while", 11);
|
||||
ASSERT_EQ(1u, Comps.size());
|
||||
EXPECT_EQ("Stmt(", Comps[0].TypedText);
|
||||
EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",
|
||||
Comps[0].DisplayText);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue