1 //===---- QueryParser.cpp - clang-query command parser --------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "QueryParser.h"
10 #include "Query.h"
11 #include "QuerySession.h"
12 #include "clang/ASTMatchers/Dynamic/Parser.h"
13 #include "clang/Basic/CharInfo.h"
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/ADT/StringSwitch.h"
16 #include <set>
17 
18 using namespace llvm;
19 using namespace clang::ast_matchers::dynamic;
20 
21 namespace clang {
22 namespace query {
23 
24 // Lex any amount of whitespace followed by a "word" (any sequence of
25 // non-whitespace characters) from the start of region [Begin,End).  If no word
26 // is found before End, return StringRef().  Begin is adjusted to exclude the
27 // lexed region.
28 StringRef QueryParser::lexWord() {
29   Line = Line.drop_while([](char c) {
30     // Don't trim newlines.
31     return StringRef(" \t\v\f\r").contains(c);
32   });
33 
34   if (Line.empty())
35     // Even though the Line is empty, it contains a pointer and
36     // a (zero) length. The pointer is used in the LexOrCompleteWord
37     // code completion.
38     return Line;
39 
40   StringRef Word;
41   if (Line.front() == '#')
42     Word = Line.substr(0, 1);
43   else
44     Word = Line.take_until(isWhitespace);
45 
46   Line = Line.drop_front(Word.size());
47   return Word;
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(),
86           std::string(CaseStr)));
87     return *this;
88   }
89 
90   T Default(T Value) { return Switch.Default(Value); }
91 };
92 
93 QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
94   StringRef ValStr;
95   unsigned Value = LexOrCompleteWord<unsigned>(this, ValStr)
96                        .Case("false", 0)
97                        .Case("true", 1)
98                        .Default(~0u);
99   if (Value == ~0u) {
100     return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
101   }
102   return new SetQuery<bool>(Var, Value);
103 }
104 
105 template <typename QueryType> QueryRef QueryParser::parseSetOutputKind() {
106   StringRef ValStr;
107   unsigned OutKind = LexOrCompleteWord<unsigned>(this, ValStr)
108                          .Case("diag", OK_Diag)
109                          .Case("print", OK_Print)
110                          .Case("detailed-ast", OK_DetailedAST)
111                          .Case("dump", OK_DetailedAST)
112                          .Default(~0u);
113   if (OutKind == ~0u) {
114     return new InvalidQuery(
115         "expected 'diag', 'print', 'detailed-ast' or 'dump', got '" + ValStr +
116         "'");
117   }
118 
119   switch (OutKind) {
120   case OK_DetailedAST:
121     return new QueryType(&QuerySession::DetailedASTOutput);
122   case OK_Diag:
123     return new QueryType(&QuerySession::DiagOutput);
124   case OK_Print:
125     return new QueryType(&QuerySession::PrintOutput);
126   }
127 
128   llvm_unreachable("Invalid output kind");
129 }
130 
131 QueryRef QueryParser::endQuery(QueryRef Q) {
132   StringRef Extra = Line;
133   StringRef ExtraTrimmed = Extra.drop_while(
134       [](char c) { return StringRef(" \t\v\f\r").contains(c); });
135 
136   if ((!ExtraTrimmed.empty() && ExtraTrimmed[0] == '\n') ||
137       (ExtraTrimmed.size() >= 2 && ExtraTrimmed[0] == '\r' &&
138        ExtraTrimmed[1] == '\n'))
139     Q->RemainingContent = Extra;
140   else {
141     StringRef TrailingWord = lexWord();
142     if (!TrailingWord.empty() && TrailingWord.front() == '#') {
143       Line = Line.drop_until([](char c) { return c == '\n'; });
144       Line = Line.drop_while([](char c) { return c == '\n'; });
145       return endQuery(Q);
146     }
147     if (!TrailingWord.empty()) {
148       return new InvalidQuery("unexpected extra input: '" + Extra + "'");
149     }
150   }
151   return Q;
152 }
153 
154 namespace {
155 
156 enum ParsedQueryKind {
157   PQK_Invalid,
158   PQK_Comment,
159   PQK_NoOp,
160   PQK_Help,
161   PQK_Let,
162   PQK_Match,
163   PQK_Set,
164   PQK_Unlet,
165   PQK_Quit,
166   PQK_Enable,
167   PQK_Disable
168 };
169 
170 enum ParsedQueryVariable {
171   PQV_Invalid,
172   PQV_Output,
173   PQV_BindRoot,
174   PQV_PrintMatcher
175 };
176 
177 QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
178   std::string ErrStr;
179   llvm::raw_string_ostream OS(ErrStr);
180   Diag.printToStreamFull(OS);
181   return new InvalidQuery(OS.str());
182 }
183 
184 } // namespace
185 
186 QueryRef QueryParser::completeMatcherExpression() {
187   std::vector<MatcherCompletion> Comps = Parser::completeExpression(
188       Line, CompletionPos - Line.begin(), nullptr, &QS.NamedValues);
189   for (auto I = Comps.begin(), E = Comps.end(); I != E; ++I) {
190     Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl));
191   }
192   return QueryRef();
193 }
194 
195 QueryRef QueryParser::doParse() {
196   StringRef CommandStr;
197   ParsedQueryKind QKind = LexOrCompleteWord<ParsedQueryKind>(this, CommandStr)
198                               .Case("", PQK_NoOp)
199                               .Case("#", PQK_Comment, /*IsCompletion=*/false)
200                               .Case("help", PQK_Help)
201                               .Case("l", PQK_Let, /*IsCompletion=*/false)
202                               .Case("let", PQK_Let)
203                               .Case("m", PQK_Match, /*IsCompletion=*/false)
204                               .Case("match", PQK_Match)
205                               .Case("q", PQK_Quit,  /*IsCompletion=*/false)
206                               .Case("quit", PQK_Quit)
207                               .Case("set", PQK_Set)
208                               .Case("enable", PQK_Enable)
209                               .Case("disable", PQK_Disable)
210                               .Case("unlet", PQK_Unlet)
211                               .Default(PQK_Invalid);
212 
213   switch (QKind) {
214   case PQK_Comment:
215   case PQK_NoOp:
216     Line = Line.drop_until([](char c) { return c == '\n'; });
217     Line = Line.drop_while([](char c) { return c == '\n'; });
218     if (Line.empty())
219       return new NoOpQuery;
220     return doParse();
221 
222   case PQK_Help:
223     return endQuery(new HelpQuery);
224 
225   case PQK_Quit:
226     return endQuery(new QuitQuery);
227 
228   case PQK_Let: {
229     StringRef Name = lexWord();
230 
231     if (Name.empty())
232       return new InvalidQuery("expected variable name");
233 
234     if (CompletionPos)
235       return completeMatcherExpression();
236 
237     Diagnostics Diag;
238     ast_matchers::dynamic::VariantValue Value;
239     if (!Parser::parseExpression(Line, nullptr, &QS.NamedValues, &Value,
240                                  &Diag)) {
241       return makeInvalidQueryFromDiagnostics(Diag);
242     }
243 
244     auto *Q = new LetQuery(Name, Value);
245     Q->RemainingContent = Line;
246     return Q;
247   }
248 
249   case PQK_Match: {
250     if (CompletionPos)
251       return completeMatcherExpression();
252 
253     Diagnostics Diag;
254     auto MatcherSource = Line.ltrim();
255     auto OrigMatcherSource = MatcherSource;
256     Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
257         MatcherSource, nullptr, &QS.NamedValues, &Diag);
258     if (!Matcher) {
259       return makeInvalidQueryFromDiagnostics(Diag);
260     }
261     auto ActualSource = OrigMatcherSource.slice(0, OrigMatcherSource.size() -
262                                                        MatcherSource.size());
263     auto *Q = new MatchQuery(ActualSource, *Matcher);
264     Q->RemainingContent = MatcherSource;
265     return Q;
266   }
267 
268   case PQK_Set: {
269     StringRef VarStr;
270     ParsedQueryVariable Var =
271         LexOrCompleteWord<ParsedQueryVariable>(this, VarStr)
272             .Case("output", PQV_Output)
273             .Case("bind-root", PQV_BindRoot)
274             .Case("print-matcher", PQV_PrintMatcher)
275             .Default(PQV_Invalid);
276     if (VarStr.empty())
277       return new InvalidQuery("expected variable name");
278     if (Var == PQV_Invalid)
279       return new InvalidQuery("unknown variable: '" + VarStr + "'");
280 
281     QueryRef Q;
282     switch (Var) {
283     case PQV_Output:
284       Q = parseSetOutputKind<SetExclusiveOutputQuery>();
285       break;
286     case PQV_BindRoot:
287       Q = parseSetBool(&QuerySession::BindRoot);
288       break;
289     case PQV_PrintMatcher:
290       Q = parseSetBool(&QuerySession::PrintMatcher);
291       break;
292     case PQV_Invalid:
293       llvm_unreachable("Invalid query kind");
294     }
295 
296     return endQuery(Q);
297   }
298   case PQK_Enable:
299   case PQK_Disable: {
300     StringRef VarStr;
301     ParsedQueryVariable Var =
302         LexOrCompleteWord<ParsedQueryVariable>(this, VarStr)
303             .Case("output", PQV_Output)
304             .Default(PQV_Invalid);
305     if (VarStr.empty())
306       return new InvalidQuery("expected variable name");
307     if (Var == PQV_Invalid)
308       return new InvalidQuery("unknown variable: '" + VarStr + "'");
309 
310     QueryRef Q;
311 
312     if (QKind == PQK_Enable)
313       Q = parseSetOutputKind<EnableOutputQuery>();
314     else if (QKind == PQK_Disable)
315       Q = parseSetOutputKind<DisableOutputQuery>();
316     else
317       llvm_unreachable("Invalid query kind");
318     return endQuery(Q);
319   }
320 
321   case PQK_Unlet: {
322     StringRef Name = lexWord();
323 
324     if (Name.empty())
325       return new InvalidQuery("expected variable name");
326 
327     return endQuery(new LetQuery(Name, VariantValue()));
328   }
329 
330   case PQK_Invalid:
331     return new InvalidQuery("unknown command: " + CommandStr);
332   }
333 
334   llvm_unreachable("Invalid query kind");
335 }
336 
337 QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) {
338   return QueryParser(Line, QS).doParse();
339 }
340 
341 std::vector<LineEditor::Completion>
342 QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) {
343   QueryParser P(Line, QS);
344   P.CompletionPos = Line.data() + Pos;
345 
346   P.doParse();
347   return P.Completions;
348 }
349 
350 } // namespace query
351 } // namespace clang
352