File: | build/source/clang-tools-extra/clang-query/QueryParser.cpp |
Warning: | line 362, column 1 Potential leak of memory pointed to by 'Q.Obj' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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")::llvm::llvm_unreachable_internal("Invalid output kind", "clang-tools-extra/clang-query/QueryParser.cpp" , 137); | |||
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 Extra = Line; | |||
155 | StringRef ExtraTrimmed = 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 | PQK_Comment, | |||
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")::llvm::llvm_unreachable_internal("Invalid query kind", "clang-tools-extra/clang-query/QueryParser.cpp" , 320); | |||
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
| |||
335 | return new InvalidQuery("unknown variable: '" + VarStr + "'"); | |||
336 | ||||
337 | QueryRef Q; | |||
338 | ||||
339 | if (QKind
| |||
340 | Q = parseSetOutputKind<EnableOutputQuery>(); | |||
341 | else if (QKind
| |||
342 | Q = parseSetOutputKind<DisableOutputQuery>(); | |||
343 | else | |||
344 | llvm_unreachable("Invalid query kind")::llvm::llvm_unreachable_internal("Invalid query kind", "clang-tools-extra/clang-query/QueryParser.cpp" , 344); | |||
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")::llvm::llvm_unreachable_internal("Invalid query kind", "clang-tools-extra/clang-query/QueryParser.cpp" , 361); | |||
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 |