1 //===---- QueryParser.cpp - clang-query command parser --------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "QueryParser.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"
20 using namespace clang::ast_matchers::dynamic
;
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
29 StringRef
QueryParser::lexWord() {
30 // Don't trim newlines.
31 Line
= Line
.ltrim(" \t\v\f\r");
34 // Even though the Line is empty, it contains a pointer and
35 // a (zero) length. The pointer is used in the LexOrCompleteWord
40 if (Line
.front() == '#')
41 Word
= Line
.substr(0, 1);
43 Word
= Line
.take_until(isWhitespace
);
45 Line
= Line
.drop_front(Word
.size());
49 // This is the StringSwitch-alike used by lexOrCompleteWord below. See that
50 // function for details.
51 template <typename T
> struct QueryParser::LexOrCompleteWord
{
53 StringSwitch
<T
> Switch
;
56 // Set to the completion point offset in Word, or StringRef::npos if
57 // completion point not in Word.
58 size_t WordCompletionPos
;
60 // Lexes a word and stores it in Word. Returns a LexOrCompleteWord<T> object
61 // that can be used like a llvm::StringSwitch<T>, but adds cases as possible
62 // completions if the lexed word contains the completion point.
63 LexOrCompleteWord(QueryParser
*P
, StringRef
&OutWord
)
64 : Word(P
->lexWord()), Switch(Word
), P(P
),
65 WordCompletionPos(StringRef::npos
) {
67 if (P
->CompletionPos
&& P
->CompletionPos
<= Word
.data() + Word
.size()) {
68 if (P
->CompletionPos
< Word
.data())
69 WordCompletionPos
= 0;
71 WordCompletionPos
= P
->CompletionPos
- Word
.data();
75 LexOrCompleteWord
&Case(llvm::StringLiteral CaseStr
, const T
&Value
,
76 bool IsCompletion
= true) {
78 if (WordCompletionPos
== StringRef::npos
)
79 Switch
.Case(CaseStr
, Value
);
80 else if (CaseStr
.size() != 0 && IsCompletion
&& WordCompletionPos
<= CaseStr
.size() &&
81 CaseStr
.substr(0, WordCompletionPos
) ==
82 Word
.substr(0, WordCompletionPos
))
83 P
->Completions
.push_back(LineEditor::Completion(
84 (CaseStr
.substr(WordCompletionPos
) + " ").str(),
85 std::string(CaseStr
)));
89 T
Default(T Value
) { return Switch
.Default(Value
); }
92 QueryRef
QueryParser::parseSetBool(bool QuerySession::*Var
) {
94 unsigned Value
= LexOrCompleteWord
<unsigned>(this, ValStr
)
99 return new InvalidQuery("expected 'true' or 'false', got '" + ValStr
+ "'");
101 return new SetQuery
<bool>(Var
, Value
);
104 template <typename QueryType
> QueryRef
QueryParser::parseSetOutputKind() {
106 unsigned OutKind
= LexOrCompleteWord
<unsigned>(this, ValStr
)
107 .Case("diag", OK_Diag
)
108 .Case("print", OK_Print
)
109 .Case("detailed-ast", OK_DetailedAST
)
110 .Case("dump", OK_DetailedAST
)
112 if (OutKind
== ~0u) {
113 return new InvalidQuery("expected 'diag', 'print', 'detailed-ast' or "
120 return new QueryType(&QuerySession::DetailedASTOutput
);
122 return new QueryType(&QuerySession::DiagOutput
);
124 return new QueryType(&QuerySession::PrintOutput
);
127 llvm_unreachable("Invalid output kind");
130 QueryRef
QueryParser::parseSetTraversalKind(TraversalKind
QuerySession::*Var
) {
133 LexOrCompleteWord
<unsigned>(this, ValStr
)
134 .Case("AsIs", TK_AsIs
)
135 .Case("IgnoreUnlessSpelledInSource", TK_IgnoreUnlessSpelledInSource
)
138 return new InvalidQuery("expected traversal kind, got '" + ValStr
+ "'");
140 return new SetQuery
<TraversalKind
>(Var
, static_cast<TraversalKind
>(Value
));
143 QueryRef
QueryParser::endQuery(QueryRef Q
) {
144 StringRef Extra
= Line
;
145 StringRef ExtraTrimmed
= Extra
.ltrim(" \t\v\f\r");
147 if (ExtraTrimmed
.starts_with('\n') || ExtraTrimmed
.starts_with("\r\n"))
148 Q
->RemainingContent
= Extra
;
150 StringRef TrailingWord
= lexWord();
151 if (TrailingWord
.starts_with('#')) {
152 Line
= Line
.drop_until([](char c
) { return c
== '\n'; });
153 Line
= Line
.drop_while([](char c
) { return c
== '\n'; });
156 if (!TrailingWord
.empty()) {
157 return new InvalidQuery("unexpected extra input: '" + Extra
+ "'");
165 enum ParsedQueryKind
{
180 enum ParsedQueryVariable
{
189 QueryRef
makeInvalidQueryFromDiagnostics(const Diagnostics
&Diag
) {
191 llvm::raw_string_ostream
OS(ErrStr
);
192 Diag
.printToStreamFull(OS
);
193 return new InvalidQuery(OS
.str());
198 QueryRef
QueryParser::completeMatcherExpression() {
199 std::vector
<MatcherCompletion
> Comps
= Parser::completeExpression(
200 Line
, CompletionPos
- Line
.begin(), nullptr, &QS
.NamedValues
);
201 for (auto I
= Comps
.begin(), E
= Comps
.end(); I
!= E
; ++I
) {
202 Completions
.push_back(LineEditor::Completion(I
->TypedText
, I
->MatcherDecl
));
207 QueryRef
QueryParser::doParse() {
208 StringRef CommandStr
;
209 ParsedQueryKind QKind
= LexOrCompleteWord
<ParsedQueryKind
>(this, CommandStr
)
211 .Case("#", PQK_Comment
, /*IsCompletion=*/false)
212 .Case("help", PQK_Help
)
213 .Case("l", PQK_Let
, /*IsCompletion=*/false)
214 .Case("let", PQK_Let
)
215 .Case("m", PQK_Match
, /*IsCompletion=*/false)
216 .Case("match", PQK_Match
)
217 .Case("q", PQK_Quit
, /*IsCompletion=*/false)
218 .Case("quit", PQK_Quit
)
219 .Case("set", PQK_Set
)
220 .Case("enable", PQK_Enable
)
221 .Case("disable", PQK_Disable
)
222 .Case("unlet", PQK_Unlet
)
223 .Case("f", PQK_File
, /*IsCompletion=*/false)
224 .Case("file", PQK_File
)
225 .Default(PQK_Invalid
);
230 Line
= Line
.drop_until([](char c
) { return c
== '\n'; });
231 Line
= Line
.drop_while([](char c
) { return c
== '\n'; });
233 return new NoOpQuery
;
237 return endQuery(new HelpQuery
);
240 return endQuery(new QuitQuery
);
243 StringRef Name
= lexWord();
246 return new InvalidQuery("expected variable name");
249 return completeMatcherExpression();
252 ast_matchers::dynamic::VariantValue Value
;
253 if (!Parser::parseExpression(Line
, nullptr, &QS
.NamedValues
, &Value
,
255 return makeInvalidQueryFromDiagnostics(Diag
);
258 auto *Q
= new LetQuery(Name
, Value
);
259 Q
->RemainingContent
= Line
;
265 return completeMatcherExpression();
268 auto MatcherSource
= Line
.ltrim();
269 auto OrigMatcherSource
= MatcherSource
;
270 std::optional
<DynTypedMatcher
> Matcher
= Parser::parseMatcherExpression(
271 MatcherSource
, nullptr, &QS
.NamedValues
, &Diag
);
273 return makeInvalidQueryFromDiagnostics(Diag
);
275 auto ActualSource
= OrigMatcherSource
.slice(0, OrigMatcherSource
.size() -
276 MatcherSource
.size());
277 auto *Q
= new MatchQuery(ActualSource
, *Matcher
);
278 Q
->RemainingContent
= MatcherSource
;
284 ParsedQueryVariable Var
=
285 LexOrCompleteWord
<ParsedQueryVariable
>(this, VarStr
)
286 .Case("output", PQV_Output
)
287 .Case("bind-root", PQV_BindRoot
)
288 .Case("print-matcher", PQV_PrintMatcher
)
289 .Case("enable-profile", PQV_EnableProfile
)
290 .Case("traversal", PQV_Traversal
)
291 .Default(PQV_Invalid
);
293 return new InvalidQuery("expected variable name");
294 if (Var
== PQV_Invalid
)
295 return new InvalidQuery("unknown variable: '" + VarStr
+ "'");
300 Q
= parseSetOutputKind
<SetExclusiveOutputQuery
>();
303 Q
= parseSetBool(&QuerySession::BindRoot
);
305 case PQV_PrintMatcher
:
306 Q
= parseSetBool(&QuerySession::PrintMatcher
);
308 case PQV_EnableProfile
:
309 Q
= parseSetBool(&QuerySession::EnableProfile
);
312 Q
= parseSetTraversalKind(&QuerySession::TK
);
315 llvm_unreachable("Invalid query kind");
323 ParsedQueryVariable Var
=
324 LexOrCompleteWord
<ParsedQueryVariable
>(this, VarStr
)
325 .Case("output", PQV_Output
)
326 .Default(PQV_Invalid
);
328 return new InvalidQuery("expected variable name");
329 if (Var
== PQV_Invalid
)
330 return new InvalidQuery("unknown variable: '" + VarStr
+ "'");
334 if (QKind
== PQK_Enable
)
335 Q
= parseSetOutputKind
<EnableOutputQuery
>();
336 else if (QKind
== PQK_Disable
)
337 Q
= parseSetOutputKind
<DisableOutputQuery
>();
339 llvm_unreachable("Invalid query kind");
344 StringRef Name
= lexWord();
347 return new InvalidQuery("expected variable name");
349 return endQuery(new LetQuery(Name
, VariantValue()));
353 return new FileQuery(Line
);
356 return new InvalidQuery("unknown command: " + CommandStr
);
359 llvm_unreachable("Invalid query kind");
362 QueryRef
QueryParser::parse(StringRef Line
, const QuerySession
&QS
) {
363 return QueryParser(Line
, QS
).doParse();
366 std::vector
<LineEditor::Completion
>
367 QueryParser::complete(StringRef Line
, size_t Pos
, const QuerySession
&QS
) {
368 QueryParser
P(Line
, QS
);
369 P
.CompletionPos
= Line
.data() + Pos
;
372 return P
.Completions
;