1 //===---- QueryParserTest.cpp - clang-query test --------------------------===//
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 "llvm/LineEditor/LineEditor.h"
13 #include "gtest/gtest.h"
15 using namespace clang
;
16 using namespace clang::query
;
18 class QueryParserTest
: public ::testing::Test
{
20 QueryParserTest() : QS(llvm::ArrayRef
<std::unique_ptr
<ASTUnit
>>()) {}
21 QueryRef
parse(StringRef Code
) { return QueryParser::parse(Code
, QS
); }
26 TEST_F(QueryParserTest
, NoOp
) {
27 QueryRef Q
= parse("");
28 EXPECT_TRUE(isa
<NoOpQuery
>(Q
));
31 EXPECT_TRUE(isa
<NoOpQuery
>(Q
));
34 TEST_F(QueryParserTest
, Invalid
) {
35 QueryRef Q
= parse("foo");
36 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
37 EXPECT_EQ("unknown command: foo", cast
<InvalidQuery
>(Q
)->ErrStr
);
40 TEST_F(QueryParserTest
, Help
) {
41 QueryRef Q
= parse("help");
42 ASSERT_TRUE(isa
<HelpQuery
>(Q
));
45 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
46 EXPECT_EQ("unexpected extra input: ' me'", cast
<InvalidQuery
>(Q
)->ErrStr
);
49 TEST_F(QueryParserTest
, Quit
) {
50 QueryRef Q
= parse("quit");
51 ASSERT_TRUE(isa
<QuitQuery
>(Q
));
54 ASSERT_TRUE(isa
<QuitQuery
>(Q
));
57 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
58 EXPECT_EQ("unexpected extra input: ' me'", cast
<InvalidQuery
>(Q
)->ErrStr
);
61 TEST_F(QueryParserTest
, Set
) {
63 QueryRef Q
= parse("set");
64 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
65 EXPECT_EQ("expected variable name", cast
<InvalidQuery
>(Q
)->ErrStr
);
67 Q
= parse("set foo bar");
68 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
69 EXPECT_EQ("unknown variable: 'foo'", cast
<InvalidQuery
>(Q
)->ErrStr
);
71 Q
= parse("set output");
72 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
73 EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got ''",
74 cast
<InvalidQuery
>(Q
)->ErrStr
);
76 Q
= parse("set bind-root true foo");
77 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
78 EXPECT_EQ("unexpected extra input: ' foo'", cast
<InvalidQuery
>(Q
)->ErrStr
);
80 Q
= parse("set output foo");
81 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
82 EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got 'foo'",
83 cast
<InvalidQuery
>(Q
)->ErrStr
);
85 Q
= parse("set output dump");
86 ASSERT_TRUE(isa
<SetExclusiveOutputQuery
>(Q
));
87 EXPECT_EQ(&QuerySession::DetailedASTOutput
, cast
<SetExclusiveOutputQuery
>(Q
)->Var
);
89 Q
= parse("set output detailed-ast");
90 ASSERT_TRUE(isa
<SetExclusiveOutputQuery
>(Q
));
91 EXPECT_EQ(&QuerySession::DetailedASTOutput
, cast
<SetExclusiveOutputQuery
>(Q
)->Var
);
93 Q
= parse("enable output detailed-ast");
94 ASSERT_TRUE(isa
<EnableOutputQuery
>(Q
));
95 EXPECT_EQ(&QuerySession::DetailedASTOutput
, cast
<EnableOutputQuery
>(Q
)->Var
);
98 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
99 EXPECT_EQ("expected variable name", cast
<InvalidQuery
>(Q
)->ErrStr
);
101 Q
= parse("disable output detailed-ast");
102 ASSERT_TRUE(isa
<DisableOutputQuery
>(Q
));
103 EXPECT_EQ(&QuerySession::DetailedASTOutput
, cast
<DisableOutputQuery
>(Q
)->Var
);
105 Q
= parse("set bind-root foo");
106 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
107 EXPECT_EQ("expected 'true' or 'false', got 'foo'",
108 cast
<InvalidQuery
>(Q
)->ErrStr
);
110 Q
= parse("set bind-root true");
111 ASSERT_TRUE(isa
<SetQuery
<bool> >(Q
));
112 EXPECT_EQ(&QuerySession::BindRoot
, cast
<SetQuery
<bool> >(Q
)->Var
);
113 EXPECT_EQ(true, cast
<SetQuery
<bool> >(Q
)->Value
);
115 Q
= parse("set traversal AsIs");
116 ASSERT_TRUE(isa
<SetQuery
<TraversalKind
>>(Q
));
117 EXPECT_EQ(&QuerySession::TK
, cast
<SetQuery
<TraversalKind
>>(Q
)->Var
);
118 EXPECT_EQ(TK_AsIs
, cast
<SetQuery
<TraversalKind
>>(Q
)->Value
);
120 Q
= parse("set traversal NotATraversal");
121 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
122 EXPECT_EQ("expected traversal kind, got 'NotATraversal'",
123 cast
<InvalidQuery
>(Q
)->ErrStr
);
126 TEST_F(QueryParserTest
, Match
) {
127 QueryRef Q
= parse("match decl()");
128 ASSERT_TRUE(isa
<MatchQuery
>(Q
));
129 EXPECT_TRUE(cast
<MatchQuery
>(Q
)->Matcher
.canConvertTo
<Decl
>());
131 Q
= parse("m stmt()");
132 ASSERT_TRUE(isa
<MatchQuery
>(Q
));
133 EXPECT_TRUE(cast
<MatchQuery
>(Q
)->Matcher
.canConvertTo
<Stmt
>());
136 TEST_F(QueryParserTest
, LetUnlet
) {
137 QueryRef Q
= parse("let foo decl()");
138 ASSERT_TRUE(isa
<LetQuery
>(Q
));
139 EXPECT_EQ("foo", cast
<LetQuery
>(Q
)->Name
);
140 EXPECT_TRUE(cast
<LetQuery
>(Q
)->Value
.isMatcher());
141 EXPECT_TRUE(cast
<LetQuery
>(Q
)->Value
.getMatcher().hasTypedMatcher
<Decl
>());
143 Q
= parse("l foo decl()");
144 ASSERT_TRUE(isa
<LetQuery
>(Q
));
145 EXPECT_EQ("foo", cast
<LetQuery
>(Q
)->Name
);
146 EXPECT_TRUE(cast
<LetQuery
>(Q
)->Value
.isMatcher());
147 EXPECT_TRUE(cast
<LetQuery
>(Q
)->Value
.getMatcher().hasTypedMatcher
<Decl
>());
149 Q
= parse("let bar \"str\"");
150 ASSERT_TRUE(isa
<LetQuery
>(Q
));
151 EXPECT_EQ("bar", cast
<LetQuery
>(Q
)->Name
);
152 EXPECT_TRUE(cast
<LetQuery
>(Q
)->Value
.isString());
153 EXPECT_EQ("str", cast
<LetQuery
>(Q
)->Value
.getString());
156 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
157 EXPECT_EQ("expected variable name", cast
<InvalidQuery
>(Q
)->ErrStr
);
159 Q
= parse("unlet x");
160 ASSERT_TRUE(isa
<LetQuery
>(Q
));
161 EXPECT_EQ("x", cast
<LetQuery
>(Q
)->Name
);
162 EXPECT_FALSE(cast
<LetQuery
>(Q
)->Value
.hasValue());
165 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
166 EXPECT_EQ("expected variable name", cast
<InvalidQuery
>(Q
)->ErrStr
);
168 Q
= parse("unlet x bad_data");
169 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
170 EXPECT_EQ("unexpected extra input: ' bad_data'",
171 cast
<InvalidQuery
>(Q
)->ErrStr
);
174 TEST_F(QueryParserTest
, Comment
) {
175 QueryRef Q
= parse("# let foo decl()");
176 ASSERT_TRUE(isa
<NoOpQuery
>(Q
));
178 Q
= parse("let foo decl() # creates a decl() matcher called foo");
179 ASSERT_TRUE(isa
<LetQuery
>(Q
));
181 Q
= parse("set bind-root false # reduce noise");
182 ASSERT_TRUE(isa
<SetQuery
<bool>>(Q
));
185 TEST_F(QueryParserTest
, Complete
) {
186 std::vector
<llvm::LineEditor::Completion
> Comps
=
187 QueryParser::complete("", 0, QS
);
188 ASSERT_EQ(9u, Comps
.size());
189 EXPECT_EQ("help ", Comps
[0].TypedText
);
190 EXPECT_EQ("help", Comps
[0].DisplayText
);
191 EXPECT_EQ("let ", Comps
[1].TypedText
);
192 EXPECT_EQ("let", Comps
[1].DisplayText
);
193 EXPECT_EQ("match ", Comps
[2].TypedText
);
194 EXPECT_EQ("match", Comps
[2].DisplayText
);
195 EXPECT_EQ("quit ", Comps
[3].TypedText
);
196 EXPECT_EQ("quit", Comps
[3].DisplayText
);
197 EXPECT_EQ("set ", Comps
[4].TypedText
);
198 EXPECT_EQ("set", Comps
[4].DisplayText
);
199 EXPECT_EQ("enable ", Comps
[5].TypedText
);
200 EXPECT_EQ("enable", Comps
[5].DisplayText
);
201 EXPECT_EQ("disable ", Comps
[6].TypedText
);
202 EXPECT_EQ("disable", Comps
[6].DisplayText
);
203 EXPECT_EQ("unlet ", Comps
[7].TypedText
);
204 EXPECT_EQ("unlet", Comps
[7].DisplayText
);
205 EXPECT_EQ("file ", Comps
[8].TypedText
);
206 EXPECT_EQ("file", Comps
[8].DisplayText
);
208 Comps
= QueryParser::complete("set o", 5, QS
);
209 ASSERT_EQ(1u, Comps
.size());
210 EXPECT_EQ("utput ", Comps
[0].TypedText
);
211 EXPECT_EQ("output", Comps
[0].DisplayText
);
213 Comps
= QueryParser::complete("set t", 5, QS
);
214 ASSERT_EQ(1u, Comps
.size());
215 EXPECT_EQ("raversal ", Comps
[0].TypedText
);
216 EXPECT_EQ("traversal", Comps
[0].DisplayText
);
218 Comps
= QueryParser::complete("enable ", 7, QS
);
219 ASSERT_EQ(1u, Comps
.size());
220 EXPECT_EQ("output ", Comps
[0].TypedText
);
221 EXPECT_EQ("output", Comps
[0].DisplayText
);
223 Comps
= QueryParser::complete("enable output ", 14, QS
);
224 ASSERT_EQ(4u, Comps
.size());
226 EXPECT_EQ("diag ", Comps
[0].TypedText
);
227 EXPECT_EQ("diag", Comps
[0].DisplayText
);
228 EXPECT_EQ("print ", Comps
[1].TypedText
);
229 EXPECT_EQ("print", Comps
[1].DisplayText
);
230 EXPECT_EQ("detailed-ast ", Comps
[2].TypedText
);
231 EXPECT_EQ("detailed-ast", Comps
[2].DisplayText
);
232 EXPECT_EQ("dump ", Comps
[3].TypedText
);
233 EXPECT_EQ("dump", Comps
[3].DisplayText
);
235 Comps
= QueryParser::complete("set traversal ", 14, QS
);
236 ASSERT_EQ(2u, Comps
.size());
238 EXPECT_EQ("AsIs ", Comps
[0].TypedText
);
239 EXPECT_EQ("AsIs", Comps
[0].DisplayText
);
240 EXPECT_EQ("IgnoreUnlessSpelledInSource ", Comps
[1].TypedText
);
241 EXPECT_EQ("IgnoreUnlessSpelledInSource", Comps
[1].DisplayText
);
243 Comps
= QueryParser::complete("match while", 11, QS
);
244 ASSERT_EQ(1u, Comps
.size());
245 EXPECT_EQ("Stmt(", Comps
[0].TypedText
);
246 EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",
247 Comps
[0].DisplayText
);
249 Comps
= QueryParser::complete("m", 1, QS
);
250 ASSERT_EQ(1u, Comps
.size());
251 EXPECT_EQ("atch ", Comps
[0].TypedText
);
252 EXPECT_EQ("match", Comps
[0].DisplayText
);
254 Comps
= QueryParser::complete("l", 1, QS
);
255 ASSERT_EQ(1u, Comps
.size());
256 EXPECT_EQ("et ", Comps
[0].TypedText
);
257 EXPECT_EQ("let", Comps
[0].DisplayText
);
260 TEST_F(QueryParserTest
, Multiline
) {
262 // Single string with multiple commands
263 QueryRef Q
= parse(R
"matcher(
268 ASSERT_TRUE(isa
<SetQuery
<bool>>(Q
));
270 Q
= parse(Q
->RemainingContent
);
271 ASSERT_TRUE(isa
<SetExclusiveOutputQuery
>(Q
));
275 set bind-root false set output dump
278 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
279 EXPECT_EQ("unexpected extra input: ' set output dump\n '",
280 cast
<InvalidQuery
>(Q
)->ErrStr
);
282 // Commands which do their own parsing
284 let fn functionDecl(hasName("foo
"))
285 match callExpr(callee(functionDecl()))
288 ASSERT_TRUE(isa
<LetQuery
>(Q
));
290 Q
= parse(Q
->RemainingContent
);
291 ASSERT_TRUE(isa
<MatchQuery
>(Q
));
293 // Multi-line matcher
295 match callExpr(callee(
296 functionDecl().bind("fn
")
301 ASSERT_TRUE(isa
<MatchQuery
>(Q
));
307 match callExpr ( # Trailing comment
308 # Comment alone on line
315 )) # Comment trailing close
316 # Comment after match
319 ASSERT_TRUE(isa
<MatchQuery
>(Q
));
322 Q
= parse("set bind-root false\r\nset output dump");
324 ASSERT_TRUE(isa
<SetQuery
<bool>>(Q
));
326 Q
= parse(Q
->RemainingContent
);
327 ASSERT_TRUE(isa
<SetExclusiveOutputQuery
>(Q
));
329 // Leading and trailing space in lines
330 Q
= parse(" set bind-root false \r\n set output dump ");
332 ASSERT_TRUE(isa
<SetQuery
<bool>>(Q
));
334 Q
= parse(Q
->RemainingContent
);
335 ASSERT_TRUE(isa
<SetExclusiveOutputQuery
>(Q
));
337 // Incomplete commands
338 Q
= parse("set\nbind-root false");
340 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
341 EXPECT_EQ("expected variable name", cast
<InvalidQuery
>(Q
)->ErrStr
);
343 Q
= parse("set bind-root\nfalse");
345 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
346 EXPECT_EQ("expected 'true' or 'false', got ''",
347 cast
<InvalidQuery
>(Q
)->ErrStr
);
355 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
356 EXPECT_EQ("1:9: Error parsing matcher. Found token <NewLine> "
357 "while looking for '('.",
358 cast
<InvalidQuery
>(Q
)->ErrStr
);
360 Q
= parse("let someMatcher\nm parmVarDecl()");
362 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
363 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
364 cast
<InvalidQuery
>(Q
)->ErrStr
);
366 Q
= parse("\nm parmVarDecl()\nlet someMatcher\nm parmVarDecl()");
368 ASSERT_TRUE(isa
<MatchQuery
>(Q
));
369 Q
= parse(Q
->RemainingContent
);
371 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
372 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
373 cast
<InvalidQuery
>(Q
)->ErrStr
);
375 Q
= parse("\nlet someMatcher\n");
377 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
378 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
379 cast
<InvalidQuery
>(Q
)->ErrStr
);
381 Q
= parse("\nm parmVarDecl()\nlet someMatcher\n");
383 ASSERT_TRUE(isa
<MatchQuery
>(Q
));
384 Q
= parse(Q
->RemainingContent
);
386 ASSERT_TRUE(isa
<InvalidQuery
>(Q
));
387 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
388 cast
<InvalidQuery
>(Q
)->ErrStr
);
392 let Construct parmVarDecl()
399 ASSERT_TRUE(isa
<LetQuery
>(Q
));
401 llvm::raw_null_ostream NullOutStream
;
402 dyn_cast
<LetQuery
>(Q
)->run(NullOutStream
, QS
);
405 Q
= parse(Q
->RemainingContent
);
407 ASSERT_TRUE(isa
<MatchQuery
>(Q
));