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 "clang/Tooling/NodeIntrospection.h" |
15 | #include "llvm/ADT/StringRef.h" |
16 | #include "llvm/ADT/StringSwitch.h" |
17 | #include <optional> |
18 | #include <set> |
19 | |
20 | using namespace llvm; |
21 | using namespace clang::ast_matchers::dynamic; |
22 | |
23 | namespace clang { |
24 | namespace query { |
25 | |
26 | // Lex any amount of whitespace followed by a "word" (any sequence of |
27 | // non-whitespace characters) from the start of region [Begin,End). If no word |
28 | // is found before End, return StringRef(). Begin is adjusted to exclude the |
29 | // lexed region. |
30 | StringRef QueryParser::lexWord() { |
31 | Line = Line.drop_while([](char c) { |
32 | // Don't trim newlines. |
33 | return StringRef(" \t\v\f\r" ).contains(c); |
34 | }); |
35 | |
36 | if (Line.empty()) |
37 | // Even though the Line is empty, it contains a pointer and |
38 | // a (zero) length. The pointer is used in the LexOrCompleteWord |
39 | // code completion. |
40 | return Line; |
41 | |
42 | StringRef Word; |
43 | if (Line.front() == '#') |
44 | Word = Line.substr(0, 1); |
45 | else |
46 | Word = Line.take_until(isWhitespace); |
47 | |
48 | Line = Line.drop_front(Word.size()); |
49 | return Word; |
50 | } |
51 | |
52 | // This is the StringSwitch-alike used by lexOrCompleteWord below. See that |
53 | // function for details. |
54 | template <typename T> struct QueryParser::LexOrCompleteWord { |
55 | StringRef Word; |
56 | StringSwitch<T> Switch; |
57 | |
58 | QueryParser *P; |
59 | // Set to the completion point offset in Word, or StringRef::npos if |
60 | // completion point not in Word. |
61 | size_t WordCompletionPos; |
62 | |
63 | // Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object |
64 | // that can be used like a llvm::StringSwitch<T>, but adds cases as possible |
65 | // completions if the lexed word contains the completion point. |
66 | LexOrCompleteWord(QueryParser *P, StringRef &OutWord) |
67 | : Word(P->lexWord()), Switch(Word), P(P), |
68 | WordCompletionPos(StringRef::npos) { |
69 | OutWord = Word; |
70 | if (P->CompletionPos && P->CompletionPos <= Word.data() + Word.size()) { |
71 | if (P->CompletionPos < Word.data()) |
72 | WordCompletionPos = 0; |
73 | else |
74 | WordCompletionPos = P->CompletionPos - Word.data(); |
75 | } |
76 | } |
77 | |
78 | LexOrCompleteWord &Case(llvm::StringLiteral CaseStr, const T &Value, |
79 | bool IsCompletion = true) { |
80 | |
81 | if (WordCompletionPos == StringRef::npos) |
82 | Switch.Case(CaseStr, Value); |
83 | else if (CaseStr.size() != 0 && IsCompletion && WordCompletionPos <= CaseStr.size() && |
84 | CaseStr.substr(0, WordCompletionPos) == |
85 | Word.substr(0, WordCompletionPos)) |
86 | P->Completions.push_back(LineEditor::Completion( |
87 | (CaseStr.substr(WordCompletionPos) + " " ).str(), |
88 | std::string(CaseStr))); |
89 | return *this; |
90 | } |
91 | |
92 | T Default(T Value) { return Switch.Default(Value); } |
93 | }; |
94 | |
95 | QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) { |
96 | StringRef ValStr; |
97 | unsigned Value = LexOrCompleteWord<unsigned>(this, ValStr) |
98 | .Case("false" , 0) |
99 | .Case("true" , 1) |
100 | .Default(~0u); |
101 | if (Value == ~0u) { |
102 | return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'" ); |
103 | } |
104 | return new SetQuery<bool>(Var, Value); |
105 | } |
106 | |
107 | template <typename QueryType> QueryRef QueryParser::parseSetOutputKind() { |
108 | StringRef ValStr; |
109 | bool HasIntrospection = tooling::NodeIntrospection::hasIntrospectionSupport(); |
110 | unsigned OutKind = |
111 | LexOrCompleteWord<unsigned>(this, ValStr) |
112 | .Case("diag" , OK_Diag) |
113 | .Case("print" , OK_Print) |
114 | .Case("detailed-ast" , OK_DetailedAST) |
115 | .Case("srcloc" , OK_SrcLoc, /*IsCompletion=*/HasIntrospection) |
116 | .Case("dump" , OK_DetailedAST) |
117 | .Default(~0u); |
118 | if (OutKind == ~0u) { |
119 | return new InvalidQuery("expected 'diag', 'print', 'detailed-ast'" + |
120 | StringRef(HasIntrospection ? ", 'srcloc'" : "" ) + |
121 | " or 'dump', got '" + ValStr + "'" ); |
122 | } |
123 | |
124 | switch (OutKind) { |
125 | case OK_DetailedAST: |
126 | return new QueryType(&QuerySession::DetailedASTOutput); |
127 | case OK_Diag: |
128 | return new QueryType(&QuerySession::DiagOutput); |
129 | case OK_Print: |
130 | return new QueryType(&QuerySession::PrintOutput); |
131 | case OK_SrcLoc: |
132 | if (HasIntrospection) |
133 | return new QueryType(&QuerySession::SrcLocOutput); |
134 | return new InvalidQuery("'srcloc' output support is not available." ); |
135 | } |
136 | |
137 | llvm_unreachable("Invalid output kind" ); |
138 | } |
139 | |
140 | QueryRef QueryParser::parseSetTraversalKind(TraversalKind QuerySession::*Var) { |
141 | StringRef ValStr; |
142 | unsigned Value = |
143 | LexOrCompleteWord<unsigned>(this, ValStr) |
144 | .Case("AsIs" , TK_AsIs) |
145 | .Case("IgnoreUnlessSpelledInSource" , TK_IgnoreUnlessSpelledInSource) |
146 | .Default(~0u); |
147 | if (Value == ~0u) { |
148 | return new InvalidQuery("expected traversal kind, got '" + ValStr + "'" ); |
149 | } |
150 | return new SetQuery<TraversalKind>(Var, static_cast<TraversalKind>(Value)); |
151 | } |
152 | |
153 | QueryRef QueryParser::endQuery(QueryRef Q) { |
154 | StringRef = Line; |
155 | StringRef = Extra.drop_while( |
156 | [](char c) { return StringRef(" \t\v\f\r" ).contains(c); }); |
157 | |
158 | if ((!ExtraTrimmed.empty() && ExtraTrimmed[0] == '\n') || |
159 | (ExtraTrimmed.size() >= 2 && ExtraTrimmed[0] == '\r' && |
160 | ExtraTrimmed[1] == '\n')) |
161 | Q->RemainingContent = Extra; |
162 | else { |
163 | StringRef TrailingWord = lexWord(); |
164 | if (!TrailingWord.empty() && TrailingWord.front() == '#') { |
165 | Line = Line.drop_until([](char c) { return c == '\n'; }); |
166 | Line = Line.drop_while([](char c) { return c == '\n'; }); |
167 | return endQuery(Q); |
168 | } |
169 | if (!TrailingWord.empty()) { |
170 | return new InvalidQuery("unexpected extra input: '" + Extra + "'" ); |
171 | } |
172 | } |
173 | return Q; |
174 | } |
175 | |
176 | namespace { |
177 | |
178 | enum ParsedQueryKind { |
179 | PQK_Invalid, |
180 | , |
181 | PQK_NoOp, |
182 | PQK_Help, |
183 | PQK_Let, |
184 | PQK_Match, |
185 | PQK_Set, |
186 | PQK_Unlet, |
187 | PQK_Quit, |
188 | PQK_Enable, |
189 | PQK_Disable |
190 | }; |
191 | |
192 | enum ParsedQueryVariable { |
193 | PQV_Invalid, |
194 | PQV_Output, |
195 | PQV_BindRoot, |
196 | PQV_PrintMatcher, |
197 | PQV_Traversal |
198 | }; |
199 | |
200 | QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) { |
201 | std::string ErrStr; |
202 | llvm::raw_string_ostream OS(ErrStr); |
203 | Diag.printToStreamFull(OS); |
204 | return new InvalidQuery(OS.str()); |
205 | } |
206 | |
207 | } // namespace |
208 | |
209 | QueryRef QueryParser::completeMatcherExpression() { |
210 | std::vector<MatcherCompletion> Comps = Parser::completeExpression( |
211 | Line, CompletionPos - Line.begin(), nullptr, &QS.NamedValues); |
212 | for (auto I = Comps.begin(), E = Comps.end(); I != E; ++I) { |
213 | Completions.push_back(LineEditor::Completion(I->TypedText, I->MatcherDecl)); |
214 | } |
215 | return QueryRef(); |
216 | } |
217 | |
218 | QueryRef QueryParser::doParse() { |
219 | StringRef CommandStr; |
220 | ParsedQueryKind QKind = LexOrCompleteWord<ParsedQueryKind>(this, CommandStr) |
221 | .Case("" , PQK_NoOp) |
222 | .Case("#" , PQK_Comment, /*IsCompletion=*/false) |
223 | .Case("help" , PQK_Help) |
224 | .Case("l" , PQK_Let, /*IsCompletion=*/false) |
225 | .Case("let" , PQK_Let) |
226 | .Case("m" , PQK_Match, /*IsCompletion=*/false) |
227 | .Case("match" , PQK_Match) |
228 | .Case("q" , PQK_Quit, /*IsCompletion=*/false) |
229 | .Case("quit" , PQK_Quit) |
230 | .Case("set" , PQK_Set) |
231 | .Case("enable" , PQK_Enable) |
232 | .Case("disable" , PQK_Disable) |
233 | .Case("unlet" , PQK_Unlet) |
234 | .Default(PQK_Invalid); |
235 | |
236 | switch (QKind) { |
237 | case PQK_Comment: |
238 | case PQK_NoOp: |
239 | Line = Line.drop_until([](char c) { return c == '\n'; }); |
240 | Line = Line.drop_while([](char c) { return c == '\n'; }); |
241 | if (Line.empty()) |
242 | return new NoOpQuery; |
243 | return doParse(); |
244 | |
245 | case PQK_Help: |
246 | return endQuery(new HelpQuery); |
247 | |
248 | case PQK_Quit: |
249 | return endQuery(new QuitQuery); |
250 | |
251 | case PQK_Let: { |
252 | StringRef Name = lexWord(); |
253 | |
254 | if (Name.empty()) |
255 | return new InvalidQuery("expected variable name" ); |
256 | |
257 | if (CompletionPos) |
258 | return completeMatcherExpression(); |
259 | |
260 | Diagnostics Diag; |
261 | ast_matchers::dynamic::VariantValue Value; |
262 | if (!Parser::parseExpression(Line, nullptr, &QS.NamedValues, &Value, |
263 | &Diag)) { |
264 | return makeInvalidQueryFromDiagnostics(Diag); |
265 | } |
266 | |
267 | auto *Q = new LetQuery(Name, Value); |
268 | Q->RemainingContent = Line; |
269 | return Q; |
270 | } |
271 | |
272 | case PQK_Match: { |
273 | if (CompletionPos) |
274 | return completeMatcherExpression(); |
275 | |
276 | Diagnostics Diag; |
277 | auto MatcherSource = Line.ltrim(); |
278 | auto OrigMatcherSource = MatcherSource; |
279 | std::optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression( |
280 | MatcherSource, nullptr, &QS.NamedValues, &Diag); |
281 | if (!Matcher) { |
282 | return makeInvalidQueryFromDiagnostics(Diag); |
283 | } |
284 | auto ActualSource = OrigMatcherSource.slice(0, OrigMatcherSource.size() - |
285 | MatcherSource.size()); |
286 | auto *Q = new MatchQuery(ActualSource, *Matcher); |
287 | Q->RemainingContent = MatcherSource; |
288 | return Q; |
289 | } |
290 | |
291 | case PQK_Set: { |
292 | StringRef VarStr; |
293 | ParsedQueryVariable Var = |
294 | LexOrCompleteWord<ParsedQueryVariable>(this, VarStr) |
295 | .Case("output" , PQV_Output) |
296 | .Case("bind-root" , PQV_BindRoot) |
297 | .Case("print-matcher" , PQV_PrintMatcher) |
298 | .Case("traversal" , PQV_Traversal) |
299 | .Default(PQV_Invalid); |
300 | if (VarStr.empty()) |
301 | return new InvalidQuery("expected variable name" ); |
302 | if (Var == PQV_Invalid) |
303 | return new InvalidQuery("unknown variable: '" + VarStr + "'" ); |
304 | |
305 | QueryRef Q; |
306 | switch (Var) { |
307 | case PQV_Output: |
308 | Q = parseSetOutputKind<SetExclusiveOutputQuery>(); |
309 | break; |
310 | case PQV_BindRoot: |
311 | Q = parseSetBool(&QuerySession::BindRoot); |
312 | break; |
313 | case PQV_PrintMatcher: |
314 | Q = parseSetBool(&QuerySession::PrintMatcher); |
315 | break; |
316 | case PQV_Traversal: |
317 | Q = parseSetTraversalKind(&QuerySession::TK); |
318 | break; |
319 | case PQV_Invalid: |
320 | llvm_unreachable("Invalid query kind" ); |
321 | } |
322 | |
323 | return endQuery(Q); |
324 | } |
325 | case PQK_Enable: |
326 | case PQK_Disable: { |
327 | StringRef VarStr; |
328 | ParsedQueryVariable Var = |
329 | LexOrCompleteWord<ParsedQueryVariable>(this, VarStr) |
330 | .Case("output" , PQV_Output) |
331 | .Default(PQV_Invalid); |
332 | if (VarStr.empty()) |
333 | return new InvalidQuery("expected variable name" ); |
334 | if (Var == PQV_Invalid) |
335 | return new InvalidQuery("unknown variable: '" + VarStr + "'" ); |
336 | |
337 | QueryRef Q; |
338 | |
339 | if (QKind == PQK_Enable) |
340 | Q = parseSetOutputKind<EnableOutputQuery>(); |
341 | else if (QKind == PQK_Disable) |
342 | Q = parseSetOutputKind<DisableOutputQuery>(); |
343 | else |
344 | llvm_unreachable("Invalid query kind" ); |
345 | return endQuery(Q); |
346 | } |
347 | |
348 | case PQK_Unlet: { |
349 | StringRef Name = lexWord(); |
350 | |
351 | if (Name.empty()) |
352 | return new InvalidQuery("expected variable name" ); |
353 | |
354 | return endQuery(new LetQuery(Name, VariantValue())); |
355 | } |
356 | |
357 | case PQK_Invalid: |
358 | return new InvalidQuery("unknown command: " + CommandStr); |
359 | } |
360 | |
361 | llvm_unreachable("Invalid query kind" ); |
362 | } |
363 | |
364 | QueryRef QueryParser::parse(StringRef Line, const QuerySession &QS) { |
365 | return QueryParser(Line, QS).doParse(); |
366 | } |
367 | |
368 | std::vector<LineEditor::Completion> |
369 | QueryParser::complete(StringRef Line, size_t Pos, const QuerySession &QS) { |
370 | QueryParser P(Line, QS); |
371 | P.CompletionPos = Line.data() + Pos; |
372 | |
373 | P.doParse(); |
374 | return P.Completions; |
375 | } |
376 | |
377 | } // namespace query |
378 | } // namespace clang |
379 | |