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 "clang/Tooling/NodeIntrospection.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/ADT/StringSwitch.h"
21 using namespace clang::ast_matchers::dynamic
;
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
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
);
37 // Even though the Line is empty, it contains a pointer and
38 // a (zero) length. The pointer is used in the LexOrCompleteWord
43 if (Line
.front() == '#')
44 Word
= Line
.substr(0, 1);
46 Word
= Line
.take_until(isWhitespace
);
48 Line
= Line
.drop_front(Word
.size());
52 // This is the StringSwitch-alike used by lexOrCompleteWord below. See that
53 // function for details.
54 template <typename T
> struct QueryParser::LexOrCompleteWord
{
56 StringSwitch
<T
> Switch
;
59 // Set to the completion point offset in Word, or StringRef::npos if
60 // completion point not in Word.
61 size_t WordCompletionPos
;
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
) {
70 if (P
->CompletionPos
&& P
->CompletionPos
<= Word
.data() + Word
.size()) {
71 if (P
->CompletionPos
< Word
.data())
72 WordCompletionPos
= 0;
74 WordCompletionPos
= P
->CompletionPos
- Word
.data();
78 LexOrCompleteWord
&Case(llvm::StringLiteral CaseStr
, const T
&Value
,
79 bool IsCompletion
= true) {
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
)));
92 T
Default(T Value
) { return Switch
.Default(Value
); }
95 QueryRef
QueryParser::parseSetBool(bool QuerySession::*Var
) {
97 unsigned Value
= LexOrCompleteWord
<unsigned>(this, ValStr
)
102 return new InvalidQuery("expected 'true' or 'false', got '" + ValStr
+ "'");
104 return new SetQuery
<bool>(Var
, Value
);
107 template <typename QueryType
> QueryRef
QueryParser::parseSetOutputKind() {
109 bool HasIntrospection
= tooling::NodeIntrospection::hasIntrospectionSupport();
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
)
118 if (OutKind
== ~0u) {
119 return new InvalidQuery("expected 'diag', 'print', 'detailed-ast'" +
120 StringRef(HasIntrospection
? ", 'srcloc'" : "") +
121 " or 'dump', got '" + ValStr
+ "'");
126 return new QueryType(&QuerySession::DetailedASTOutput
);
128 return new QueryType(&QuerySession::DiagOutput
);
130 return new QueryType(&QuerySession::PrintOutput
);
132 if (HasIntrospection
)
133 return new QueryType(&QuerySession::SrcLocOutput
);
134 return new InvalidQuery("'srcloc' output support is not available.");
137 llvm_unreachable("Invalid output kind");
140 QueryRef
QueryParser::parseSetTraversalKind(TraversalKind
QuerySession::*Var
) {
143 LexOrCompleteWord
<unsigned>(this, ValStr
)
144 .Case("AsIs", TK_AsIs
)
145 .Case("IgnoreUnlessSpelledInSource", TK_IgnoreUnlessSpelledInSource
)
148 return new InvalidQuery("expected traversal kind, got '" + ValStr
+ "'");
150 return new SetQuery
<TraversalKind
>(Var
, static_cast<TraversalKind
>(Value
));
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
); });
158 if ((!ExtraTrimmed
.empty() && ExtraTrimmed
[0] == '\n') ||
159 (ExtraTrimmed
.size() >= 2 && ExtraTrimmed
[0] == '\r' &&
160 ExtraTrimmed
[1] == '\n'))
161 Q
->RemainingContent
= Extra
;
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'; });
169 if (!TrailingWord
.empty()) {
170 return new InvalidQuery("unexpected extra input: '" + Extra
+ "'");
178 enum ParsedQueryKind
{
192 enum ParsedQueryVariable
{
200 QueryRef
makeInvalidQueryFromDiagnostics(const Diagnostics
&Diag
) {
202 llvm::raw_string_ostream
OS(ErrStr
);
203 Diag
.printToStreamFull(OS
);
204 return new InvalidQuery(OS
.str());
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
));
218 QueryRef
QueryParser::doParse() {
219 StringRef CommandStr
;
220 ParsedQueryKind QKind
= LexOrCompleteWord
<ParsedQueryKind
>(this, CommandStr
)
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
);
239 Line
= Line
.drop_until([](char c
) { return c
== '\n'; });
240 Line
= Line
.drop_while([](char c
) { return c
== '\n'; });
242 return new NoOpQuery
;
246 return endQuery(new HelpQuery
);
249 return endQuery(new QuitQuery
);
252 StringRef Name
= lexWord();
255 return new InvalidQuery("expected variable name");
258 return completeMatcherExpression();
261 ast_matchers::dynamic::VariantValue Value
;
262 if (!Parser::parseExpression(Line
, nullptr, &QS
.NamedValues
, &Value
,
264 return makeInvalidQueryFromDiagnostics(Diag
);
267 auto *Q
= new LetQuery(Name
, Value
);
268 Q
->RemainingContent
= Line
;
274 return completeMatcherExpression();
277 auto MatcherSource
= Line
.ltrim();
278 auto OrigMatcherSource
= MatcherSource
;
279 std::optional
<DynTypedMatcher
> Matcher
= Parser::parseMatcherExpression(
280 MatcherSource
, nullptr, &QS
.NamedValues
, &Diag
);
282 return makeInvalidQueryFromDiagnostics(Diag
);
284 auto ActualSource
= OrigMatcherSource
.slice(0, OrigMatcherSource
.size() -
285 MatcherSource
.size());
286 auto *Q
= new MatchQuery(ActualSource
, *Matcher
);
287 Q
->RemainingContent
= MatcherSource
;
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
);
301 return new InvalidQuery("expected variable name");
302 if (Var
== PQV_Invalid
)
303 return new InvalidQuery("unknown variable: '" + VarStr
+ "'");
308 Q
= parseSetOutputKind
<SetExclusiveOutputQuery
>();
311 Q
= parseSetBool(&QuerySession::BindRoot
);
313 case PQV_PrintMatcher
:
314 Q
= parseSetBool(&QuerySession::PrintMatcher
);
317 Q
= parseSetTraversalKind(&QuerySession::TK
);
320 llvm_unreachable("Invalid query kind");
328 ParsedQueryVariable Var
=
329 LexOrCompleteWord
<ParsedQueryVariable
>(this, VarStr
)
330 .Case("output", PQV_Output
)
331 .Default(PQV_Invalid
);
333 return new InvalidQuery("expected variable name");
334 if (Var
== PQV_Invalid
)
335 return new InvalidQuery("unknown variable: '" + VarStr
+ "'");
339 if (QKind
== PQK_Enable
)
340 Q
= parseSetOutputKind
<EnableOutputQuery
>();
341 else if (QKind
== PQK_Disable
)
342 Q
= parseSetOutputKind
<DisableOutputQuery
>();
344 llvm_unreachable("Invalid query kind");
349 StringRef Name
= lexWord();
352 return new InvalidQuery("expected variable name");
354 return endQuery(new LetQuery(Name
, VariantValue()));
358 return new InvalidQuery("unknown command: " + CommandStr
);
361 llvm_unreachable("Invalid query kind");
364 QueryRef
QueryParser::parse(StringRef Line
, const QuerySession
&QS
) {
365 return QueryParser(Line
, QS
).doParse();
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
;
374 return P
.Completions
;