summaryrefslogtreecommitdiffstats
path: root/clang-query
diff options
context:
space:
mode:
authorPeter Collingbourne <peter@pcc.me.uk>2014-02-01 01:42:46 +0000
committerPeter Collingbourne <peter@pcc.me.uk>2014-02-01 01:42:46 +0000
commite4cc48cfdf7c1848a91003f8049e1e1116bd3031 (patch)
treeaf870192ba5fe8ece6dc9ed8cda54e92d8120fb3 /clang-query
parentf88a2f75ab1bedc9df5ed3db41e41eca493efaba (diff)
Add completion to the query parser, and hook it up to clang-query.
Differential Revision: http://llvm-reviews.chandlerc.com/D2263 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@200604 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'clang-query')
-rw-r--r--clang-query/QueryParser.cpp148
-rw-r--r--clang-query/QueryParser.h51
-rw-r--r--clang-query/tool/ClangQuery.cpp7
3 files changed, 160 insertions, 46 deletions
diff --git a/clang-query/QueryParser.cpp b/clang-query/QueryParser.cpp
index 9ef6c233..38903e3e 100644
--- a/clang-query/QueryParser.cpp
+++ b/clang-query/QueryParser.cpp
@@ -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
diff --git a/clang-query/QueryParser.h b/clang-query/QueryParser.h
index 00c95fcf..cfba0266 100644
--- a/clang-query/QueryParser.h
+++ b/clang-query/QueryParser.h
@@ -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
diff --git a/clang-query/tool/ClangQuery.cpp b/clang-query/tool/ClangQuery.cpp
index 0150d416..f9b62e08 100644
--- a/clang-query/tool/ClangQuery.cpp
+++ b/clang-query/tool/ClangQuery.cpp
@@ -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);
}
}