1 //===---- QueryParser.cpp - clang-query command parser --------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "QueryParser.h"
11 #include "Query.h"
12 #include "QuerySession.h"
13 #include "clang/ASTMatchers/Dynamic/Parser.h"
14 #include "clang/Basic/CharInfo.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/ADT/StringSwitch.h"
17 #include <set>
18 
19 using namespace llvm;
20 using namespace clang::ast_matchers::dynamic;
21 
22 namespace clang {
23 namespace query {
24 
25 // Lex any amount of whitespace followed by a "word" (any sequence of
26 // non-whitespace characters) from the start of region [Begin,End).  If no word
27 // is found before End, return StringRef().  Begin is adjusted to exclude the
28 // lexed region.
29 StringRef QueryParser::lexWord() {
30   while (true) {
31     if (Begin == End)
32       return StringRef(Begin, 0);
33 
34     if (!isWhitespace(*Begin))
35       break;
36 
37     ++Begin;
38   }
39 
40   const char *WordBegin = Begin;
41 
42   while (true) {
43     ++Begin;
44 
45     if (Begin == End || isWhitespace(*Begin))
46       return StringRef(WordBegin, Begin - WordBegin);
47   }
48 }
49 
50 // This is the StringSwitch-alike used by lexOrCompleteWord below. See that
51 // function for details.
52 template <typename T> struct QueryParser::LexOrCompleteWord {
53   StringRef Word;
54   StringSwitch<T> Switch;
55 
56   QueryParser *P;
57   // Set to the completion point offset in Word, or StringRef::npos if
58   // completion point not in Word.
59   size_t WordCompletionPos;
60 
61   // Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
62   // that can be used like a llvm::StringSwitch<T>, but adds cases as possible
63   // completions if the lexed word contains the completion point.
64   LexOrCompleteWord(QueryParser *P, StringRef &OutWord)
65       : Word(P->lexWord()), Switch(Word), P(P),
66         WordCompletionPos(StringRef::npos) {
67     OutWord = Word;
68     if (P->CompletionPos && P->CompletionPos <= Word.data() + Word.size()) {
69       if (P->CompletionPos < Word.data())
70         WordCompletionPos = 0;
71       else
72         WordCompletionPos = P->CompletionPos - Word.data();
73     }
74   }
75 
76   LexOrCompleteWord &Case(llvm::StringLiteral CaseStr, const T &Value,
77                           bool IsCompletion = true) {
78 
79     if (WordCompletionPos == StringRef::npos)
80       Switch.Case(CaseStr, Value);
81     else if (CaseStr.size() != 0 && IsCompletion && WordCompletionPos <= CaseStr.size() &&
82              CaseStr.substr(0, WordCompletionPos) ==
83                  Word.substr(0, WordCompletionPos))
84       P->Completions.push_back(LineEditor::Completion(
85           (CaseStr.substr(WordCompletionPos) + " ").str(), CaseStr));
86     return *this;
87   }
88 
89   T Default(T Value) { return Switch.Default(Value); }
90 };
91 
92 QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
93   StringRef ValStr;
94   unsigned Value = LexOrCompleteWord<unsigned>(this, ValStr)
95                        .Case("false", 0)
96                        .Case("true", 1)
97                        .Default(~0u);
98   if (Value == ~0u) {
99     return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
100   }
101   return new SetQuery<bool>(Var, Value);
102 }
103 
104 QueryRef QueryParser::parseSetOutputKind() {
105   StringRef ValStr;
106   unsigned OutKind = LexOrCompleteWord<unsigned>(this, ValStr)
107                          .Case("diag", OK_Diag)
108                          .Case("print", OK_Print)
109                          .Case("dump", OK_Dump)
110                          .Default(~0u);
111   if (OutKind == ~0u) {
112     return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" +
113                             ValStr + "'");
114   }
115   return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
116 }
117 
118 QueryRef QueryParser::endQuery(QueryRef Q) {
119   const char *Extra = Begin;
120   if (!lexWord().empty())
121     return new InvalidQuery("unexpected extra input: '" +
122                             StringRef(Extra, End - Extra) + "'");
123   return Q;
124 }
125 
126 namespace {
127 
128 enum ParsedQueryKind {
129   PQK_Invalid,
130   PQK_NoOp,
131   PQK_Help,
132   PQK_Let,
133   PQK_Match,
134   PQK_Set,
135   PQK_Unlet,
136   PQK_Quit
137 };
138 
139 enum ParsedQueryVariable { PQV_Invalid, PQV_Output, PQV_BindRoot };
140 
141 QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
142   std::string ErrStr;
143   llvm::raw_string_ostream OS(ErrStr);
144   Diag.printToStreamFull(OS);
145   return new InvalidQuery(OS.str());
146 }
147 
148 } // namespace
149 
150 QueryRef QueryParser::completeMatcherExpression() {
151   std::vector<MatcherCompletion> Comps = Parser::completeExpression(
152       StringRef(Begin, End - Begin), CompletionPos - Begin, nullptr,
153       &QS.NamedValues);
154   for (auto I = Comps.begin(), E = Comps.end(); I != E; ++I) {
155     Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
156   }
157   return QueryRef();
158 }
159 
160 QueryRef QueryParser::doParse() {
161   StringRef CommandStr;
162   ParsedQueryKind QKind = LexOrCompleteWord<ParsedQueryKind>(this, CommandStr)
163                               .Case("", PQK_NoOp)
164                               .Case("help", PQK_Help)
165                               .Case("m", PQK_Match, /*IsCompletion=*/false)
166                               .Case("let", PQK_Let)
167                               .Case("match", PQK_Match)
168                               .Case("set", PQK_Set)
169                               .Case("unlet", PQK_Unlet)
170                               .Case("quit", PQK_Quit)
171                               .Default(PQK_Invalid);
172 
173   switch (QKind) {
174   case PQK_NoOp:
175     return new NoOpQuery;
176 
177   case PQK_Help:
178     return endQuery(new HelpQuery);
179 
180   case PQK_Quit:
181     return endQuery(new QuitQuery);
182 
183   case PQK_Let: {
184     StringRef Name = lexWord();
185 
186     if (Name.empty())
187       return new InvalidQuery("expected variable name");
188 
189     if (CompletionPos)
190       return completeMatcherExpression();
191 
192     Diagnostics Diag;
193     ast_matchers::dynamic::VariantValue Value;
194     if (!Parser::parseExpression(StringRef(Begin, End - Begin), nullptr,
195                                  &QS.NamedValues, &Value, &Diag)) {
196       return makeInvalidQueryFromDiagnostics(Diag);
197     }
198 
199     return new LetQuery(Name, Value);
200   }
201 
202   case PQK_Match: {
203     if (CompletionPos)
204       return completeMatcherExpression();
205 
206     Diagnostics Diag;
207     Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
208         StringRef(Begin, End - Begin), nullptr, &QS.NamedValues, &Diag);
209     if (!Matcher) {
210       return makeInvalidQueryFromDiagnostics(Diag);
211     }
212     return new MatchQuery(*Matcher);
213   }
214 
215   case PQK_Set: {
216     StringRef VarStr;
217     ParsedQueryVariable Var = LexOrCompleteWord<ParsedQueryVariable>(this,
218                                                                      VarStr)
219                                   .Case("output", PQV_Output)
220                                   .Case("bind-root", PQV_BindRoot)
221                                   .Default(PQV_Invalid);
222     if (VarStr.empty())
223       return new InvalidQuery("expected variable name");
224     if (Var == PQV_Invalid)
225       return new InvalidQuery("unknown variable: '" + VarStr + "'");
226 
227     QueryRef Q;
228     switch (Var) {
229     case PQV_Output:
230       Q = parseSetOutputKind();
231       break;
232     case PQV_BindRoot:
233       Q = parseSetBool(&QuerySession::BindRoot);
234       break;
235     case PQV_Invalid:
236       llvm_unreachable("Invalid query kind");
237     }
238 
239     return endQuery(Q);
240   }
241 
242   case PQK_Unlet: {
243     StringRef Name = lexWord();
244 
245     if (Name.empty())
246       return new InvalidQuery("expected variable name");
247 
248     return endQuery(new LetQuery(Name, VariantValue()));
249   }
250 
251   case PQK_Invalid:
252     return new InvalidQuery("unknown command: " + CommandStr);
253   }
254 
255   llvm_unreachable("Invalid query kind");
256 }
257 
258 QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) {
259   return QueryParser(Line, QS).doParse();
260 }
261 
262 std::vector<LineEditor::Completion>
263 QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) {
264   QueryParser P(Line, QS);
265   P.CompletionPos = Line.data() + Pos;
266 
267   P.doParse();
268   return P.Completions;
269 }
270 
271 } // namespace query
272 } // namespace clang
273