Skip to content

Commit d9a0f25

Browse files
committed
Add completion to the query parser, and hook it up to clang-query.
Differential Revision: http://llvm-reviews.chandlerc.com/D2263 llvm-svn: 200604
1 parent c31176d commit d9a0f25

File tree

4 files changed

+201
-62
lines changed

4 files changed

+201
-62
lines changed

clang-tools-extra/clang-query/QueryParser.cpp

+110-38
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include "llvm/ADT/StringRef.h"
1616
#include "llvm/ADT/StringSwitch.h"
1717

18+
#include <set>
19+
1820
using namespace llvm;
1921
using namespace clang::ast_matchers::dynamic;
2022

@@ -25,10 +27,10 @@ namespace query {
2527
// non-whitespace characters) from the start of region [Begin,End). If no word
2628
// is found before End, return StringRef(). Begin is adjusted to exclude the
2729
// lexed region.
28-
static StringRef LexWord(const char *&Begin, const char *End) {
30+
StringRef QueryParser::lexWord() {
2931
while (true) {
3032
if (Begin == End)
31-
return StringRef();
33+
return StringRef(Begin, 0);
3234

3335
if (!isWhitespace(*Begin))
3436
break;
@@ -46,8 +48,60 @@ static StringRef LexWord(const char *&Begin, const char *End) {
4648
}
4749
}
4850

49-
static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) {
50-
unsigned Value = StringSwitch<unsigned>(ValStr)
51+
// This is the StringSwitch-alike used by lexOrCompleteWord below. See that
52+
// function for details.
53+
template <typename T> struct QueryParser::LexOrCompleteWord {
54+
StringSwitch<T> Switch;
55+
56+
QueryParser *P;
57+
StringRef Word;
58+
// Set to the completion point offset in Word, or StringRef::npos if
59+
// completion point not in Word.
60+
size_t WordCompletionPos;
61+
62+
LexOrCompleteWord(QueryParser *P, StringRef Word, size_t WCP)
63+
: Switch(Word), P(P), Word(Word), WordCompletionPos(WCP) {}
64+
65+
template <unsigned N>
66+
LexOrCompleteWord &Case(const char (&S)[N], const T &Value,
67+
bool IsCompletion = true) {
68+
StringRef CaseStr(S, N - 1);
69+
70+
if (WordCompletionPos == StringRef::npos)
71+
Switch.Case(S, Value);
72+
else if (N != 1 && IsCompletion && WordCompletionPos <= CaseStr.size() &&
73+
CaseStr.substr(0, WordCompletionPos) ==
74+
Word.substr(0, WordCompletionPos))
75+
P->Completions.push_back(LineEditor::Completion(
76+
(CaseStr.substr(WordCompletionPos) + " ").str(), CaseStr));
77+
return *this;
78+
}
79+
80+
T Default(const T& Value) const {
81+
return Switch.Default(Value);
82+
}
83+
};
84+
85+
// Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
86+
// that can be used like a llvm::StringSwitch<T>, but adds cases as possible
87+
// completions if the lexed word contains the completion point.
88+
template <typename T>
89+
QueryParser::LexOrCompleteWord<T>
90+
QueryParser::lexOrCompleteWord(StringRef &Word) {
91+
Word = lexWord();
92+
size_t WordCompletionPos = StringRef::npos;
93+
if (CompletionPos && CompletionPos <= Word.data() + Word.size()) {
94+
if (CompletionPos < Word.data())
95+
WordCompletionPos = 0;
96+
else
97+
WordCompletionPos = CompletionPos - Word.data();
98+
}
99+
return LexOrCompleteWord<T>(this, Word, WordCompletionPos);
100+
}
101+
102+
QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
103+
StringRef ValStr;
104+
unsigned Value = lexOrCompleteWord<unsigned>(ValStr)
51105
.Case("false", 0)
52106
.Case("true", 1)
53107
.Default(~0u);
@@ -57,8 +111,9 @@ static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) {
57111
return new SetQuery<bool>(Var, Value);
58112
}
59113

60-
static QueryRef ParseSetOutputKind(StringRef ValStr) {
61-
unsigned OutKind = StringSwitch<unsigned>(ValStr)
114+
QueryRef QueryParser::parseSetOutputKind() {
115+
StringRef ValStr;
116+
unsigned OutKind = lexOrCompleteWord<unsigned>(ValStr)
62117
.Case("diag", OK_Diag)
63118
.Case("print", OK_Print)
64119
.Case("dump", OK_Dump)
@@ -70,9 +125,9 @@ static QueryRef ParseSetOutputKind(StringRef ValStr) {
70125
return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
71126
}
72127

73-
static QueryRef EndQuery(const char *Begin, const char *End, QueryRef Q) {
128+
QueryRef QueryParser::endQuery(QueryRef Q) {
74129
const char *Extra = Begin;
75-
if (!LexWord(Begin, End).empty())
130+
if (!lexWord().empty())
76131
return new InvalidQuery("unexpected extra input: '" +
77132
StringRef(Extra, End - Extra) + "'");
78133
return Q;
@@ -92,15 +147,12 @@ enum ParsedQueryVariable {
92147
PQV_BindRoot
93148
};
94149

95-
QueryRef ParseQuery(StringRef Line) {
96-
const char *Begin = Line.data();
97-
const char *End = Line.data() + Line.size();
98-
99-
StringRef CommandStr = LexWord(Begin, End);
100-
ParsedQueryKind QKind = StringSwitch<ParsedQueryKind>(CommandStr)
150+
QueryRef QueryParser::doParse() {
151+
StringRef CommandStr;
152+
ParsedQueryKind QKind = lexOrCompleteWord<ParsedQueryKind>(CommandStr)
101153
.Case("", PQK_NoOp)
102154
.Case("help", PQK_Help)
103-
.Case("m", PQK_Match)
155+
.Case("m", PQK_Match, /*IsCompletion=*/false)
104156
.Case("match", PQK_Match)
105157
.Case("set", PQK_Set)
106158
.Default(PQK_Invalid);
@@ -110,50 +162,57 @@ QueryRef ParseQuery(StringRef Line) {
110162
return new NoOpQuery;
111163

112164
case PQK_Help:
113-
return EndQuery(Begin, End, new HelpQuery);
165+
return endQuery(new HelpQuery);
114166

115167
case PQK_Match: {
116-
Diagnostics Diag;
117-
Optional<DynTypedMatcher> Matcher =
118-
Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag);
119-
if (!Matcher) {
120-
std::string ErrStr;
121-
llvm::raw_string_ostream OS(ErrStr);
122-
Diag.printToStreamFull(OS);
123-
return new InvalidQuery(OS.str());
168+
if (CompletionPos) {
169+
std::vector<MatcherCompletion> Comps = Parser::completeExpression(
170+
StringRef(Begin, End - Begin), CompletionPos - Begin);
171+
for (std::vector<MatcherCompletion>::iterator I = Comps.begin(),
172+
E = Comps.end();
173+
I != E; ++I) {
174+
Completions.push_back(
175+
LineEditor::Completion(I->TypedText, I->MatcherDecl));
176+
}
177+
return QueryRef();
178+
} else {
179+
Diagnostics Diag;
180+
Optional<DynTypedMatcher> Matcher =
181+
Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag);
182+
if (!Matcher) {
183+
std::string ErrStr;
184+
llvm::raw_string_ostream OS(ErrStr);
185+
Diag.printToStreamFull(OS);
186+
return new InvalidQuery(OS.str());
187+
}
188+
return new MatchQuery(*Matcher);
124189
}
125-
return new MatchQuery(*Matcher);
126190
}
127191

128192
case PQK_Set: {
129-
StringRef VarStr = LexWord(Begin, End);
193+
StringRef VarStr;
194+
ParsedQueryVariable Var = lexOrCompleteWord<ParsedQueryVariable>(VarStr)
195+
.Case("output", PQV_Output)
196+
.Case("bind-root", PQV_BindRoot)
197+
.Default(PQV_Invalid);
130198
if (VarStr.empty())
131199
return new InvalidQuery("expected variable name");
132-
133-
ParsedQueryVariable Var = StringSwitch<ParsedQueryVariable>(VarStr)
134-
.Case("output", PQV_Output)
135-
.Case("bind-root", PQV_BindRoot)
136-
.Default(PQV_Invalid);
137200
if (Var == PQV_Invalid)
138201
return new InvalidQuery("unknown variable: '" + VarStr + "'");
139202

140-
StringRef ValStr = LexWord(Begin, End);
141-
if (ValStr.empty())
142-
return new InvalidQuery("expected variable value");
143-
144203
QueryRef Q;
145204
switch (Var) {
146205
case PQV_Output:
147-
Q = ParseSetOutputKind(ValStr);
206+
Q = parseSetOutputKind();
148207
break;
149208
case PQV_BindRoot:
150-
Q = ParseSetBool(&QuerySession::BindRoot, ValStr);
209+
Q = parseSetBool(&QuerySession::BindRoot);
151210
break;
152211
case PQV_Invalid:
153212
llvm_unreachable("Invalid query kind");
154213
}
155214

156-
return EndQuery(Begin, End, Q);
215+
return endQuery(Q);
157216
}
158217

159218
case PQK_Invalid:
@@ -163,5 +222,18 @@ QueryRef ParseQuery(StringRef Line) {
163222
llvm_unreachable("Invalid query kind");
164223
}
165224

225+
QueryRef QueryParser::parse(StringRef Line) {
226+
return QueryParser(Line).doParse();
227+
}
228+
229+
std::vector<LineEditor::Completion> QueryParser::complete(StringRef Line,
230+
size_t Pos) {
231+
QueryParser P(Line);
232+
P.CompletionPos = Line.data() + Pos;
233+
234+
P.doParse();
235+
return P.Completions;
236+
}
237+
166238
} // namespace query
167239
} // namespace clang

clang-tools-extra/clang-query/QueryParser.h

+46-5
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,55 @@
1212

1313
#include "Query.h"
1414

15+
#include <stddef.h>
16+
#include "llvm/LineEditor/LineEditor.h"
17+
1518
namespace clang {
1619
namespace query {
1720

18-
/// \brief Parse \p Line.
19-
///
20-
/// \return A reference to the parsed query object, which may be an
21-
/// \c InvalidQuery if a parse error occurs.
22-
QueryRef ParseQuery(StringRef Line);
21+
class QuerySession;
22+
23+
class QueryParser {
24+
public:
25+
/// Parse \param Line as a query.
26+
///
27+
/// \return A QueryRef representing the query, which may be an InvalidQuery.
28+
static QueryRef parse(StringRef Line);
29+
30+
/// Compute a list of completions for \param Line assuming a cursor at
31+
/// \param Pos characters past the start of \param Line, ordered from most
32+
/// likely to least likely.
33+
///
34+
/// \return A vector of completions for \param Line.
35+
static std::vector<llvm::LineEditor::Completion> complete(StringRef Line,
36+
size_t Pos);
37+
38+
private:
39+
QueryParser(StringRef Line)
40+
: Begin(Line.data()), End(Line.data() + Line.size()), CompletionPos(0) {}
41+
42+
StringRef lexWord();
43+
44+
template <typename T> struct LexOrCompleteWord;
45+
template <typename T> LexOrCompleteWord<T> lexOrCompleteWord(StringRef &Str);
46+
47+
QueryRef parseSetBool(bool QuerySession::*Var);
48+
QueryRef parseSetOutputKind();
49+
50+
QueryRef endQuery(QueryRef Q);
51+
52+
/// \brief Parse [\p Begin,\p End).
53+
///
54+
/// \return A reference to the parsed query object, which may be an
55+
/// \c InvalidQuery if a parse error occurs.
56+
QueryRef doParse();
57+
58+
const char *Begin;
59+
const char *End;
60+
61+
const char *CompletionPos;
62+
std::vector<llvm::LineEditor::Completion> Completions;
63+
};
2364

2465
} // namespace query
2566
} // namespace clang

clang-tools-extra/clang-query/tool/ClangQuery.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ int main(int argc, const char **argv) {
9696
for (cl::list<std::string>::iterator I = Commands.begin(),
9797
E = Commands.end();
9898
I != E; ++I) {
99-
QueryRef Q = ParseQuery(I->c_str());
99+
QueryRef Q = QueryParser::parse(I->c_str());
100100
if (!Q->run(llvm::outs(), QS))
101101
return 1;
102102
}
@@ -113,15 +113,16 @@ int main(int argc, const char **argv) {
113113
std::string Line;
114114
std::getline(Input, Line);
115115

116-
QueryRef Q = ParseQuery(Line.c_str());
116+
QueryRef Q = QueryParser::parse(Line.c_str());
117117
if (!Q->run(llvm::outs(), QS))
118118
return 1;
119119
}
120120
}
121121
} else {
122122
LineEditor LE("clang-query");
123+
LE.setListCompleter(QueryParser::complete);
123124
while (llvm::Optional<std::string> Line = LE.readLine()) {
124-
QueryRef Q = ParseQuery(*Line);
125+
QueryRef Q = QueryParser::parse(*Line);
125126
Q->run(llvm::outs(), QS);
126127
}
127128
}

0 commit comments

Comments
 (0)