1 //===---- QueryParserTest.cpp - clang-query test --------------------------===//
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 "llvm/LineEditor/LineEditor.h"
13 #include "gtest/gtest.h"
14
15 using namespace clang;
16 using namespace clang::query;
17
18 class QueryParserTest : public ::testing::Test {
19 protected:
QueryParserTest()20 QueryParserTest() : QS(llvm::ArrayRef<std::unique_ptr<ASTUnit>>()) {}
parse(StringRef Code)21 QueryRef parse(StringRef Code) { return QueryParser::parse(Code, QS); }
22
23 QuerySession QS;
24 };
25
TEST_F(QueryParserTest,NoOp)26 TEST_F(QueryParserTest, NoOp) {
27 QueryRef Q = parse("");
28 EXPECT_TRUE(isa<NoOpQuery>(Q));
29
30 Q = parse("\n");
31 EXPECT_TRUE(isa<NoOpQuery>(Q));
32 }
33
TEST_F(QueryParserTest,Invalid)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);
38 }
39
TEST_F(QueryParserTest,Help)40 TEST_F(QueryParserTest, Help) {
41 QueryRef Q = parse("help");
42 ASSERT_TRUE(isa<HelpQuery>(Q));
43
44 Q = parse("help me");
45 ASSERT_TRUE(isa<InvalidQuery>(Q));
46 EXPECT_EQ("unexpected extra input: ' me'", cast<InvalidQuery>(Q)->ErrStr);
47 }
48
TEST_F(QueryParserTest,Quit)49 TEST_F(QueryParserTest, Quit) {
50 QueryRef Q = parse("quit");
51 ASSERT_TRUE(isa<QuitQuery>(Q));
52
53 Q = parse("q");
54 ASSERT_TRUE(isa<QuitQuery>(Q));
55
56 Q = parse("quit me");
57 ASSERT_TRUE(isa<InvalidQuery>(Q));
58 EXPECT_EQ("unexpected extra input: ' me'", cast<InvalidQuery>(Q)->ErrStr);
59 }
60
TEST_F(QueryParserTest,Set)61 TEST_F(QueryParserTest, Set) {
62 QueryRef Q = parse("set");
63 ASSERT_TRUE(isa<InvalidQuery>(Q));
64 EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
65
66 Q = parse("set foo bar");
67 ASSERT_TRUE(isa<InvalidQuery>(Q));
68 EXPECT_EQ("unknown variable: 'foo'", cast<InvalidQuery>(Q)->ErrStr);
69
70 Q = parse("set output");
71 ASSERT_TRUE(isa<InvalidQuery>(Q));
72 EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got ''",
73 cast<InvalidQuery>(Q)->ErrStr);
74
75 Q = parse("set bind-root true foo");
76 ASSERT_TRUE(isa<InvalidQuery>(Q));
77 EXPECT_EQ("unexpected extra input: ' foo'", cast<InvalidQuery>(Q)->ErrStr);
78
79 Q = parse("set output foo");
80 ASSERT_TRUE(isa<InvalidQuery>(Q));
81 EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got 'foo'",
82 cast<InvalidQuery>(Q)->ErrStr);
83
84 Q = parse("set output dump");
85 ASSERT_TRUE(isa<SetExclusiveOutputQuery >(Q));
86 EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<SetExclusiveOutputQuery>(Q)->Var);
87
88 Q = parse("set output detailed-ast");
89 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));
90 EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<SetExclusiveOutputQuery>(Q)->Var);
91
92 Q = parse("enable output detailed-ast");
93 ASSERT_TRUE(isa<EnableOutputQuery>(Q));
94 EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<EnableOutputQuery>(Q)->Var);
95
96 Q = parse("enable");
97 ASSERT_TRUE(isa<InvalidQuery>(Q));
98 EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
99
100 Q = parse("disable output detailed-ast");
101 ASSERT_TRUE(isa<DisableOutputQuery>(Q));
102 EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<DisableOutputQuery>(Q)->Var);
103
104 Q = parse("set bind-root foo");
105 ASSERT_TRUE(isa<InvalidQuery>(Q));
106 EXPECT_EQ("expected 'true' or 'false', got 'foo'",
107 cast<InvalidQuery>(Q)->ErrStr);
108
109 Q = parse("set bind-root true");
110 ASSERT_TRUE(isa<SetQuery<bool> >(Q));
111 EXPECT_EQ(&QuerySession::BindRoot, cast<SetQuery<bool> >(Q)->Var);
112 EXPECT_EQ(true, cast<SetQuery<bool> >(Q)->Value);
113
114 Q = parse("set traversal AsIs");
115 ASSERT_TRUE(isa<SetQuery<ast_type_traits::TraversalKind>>(Q));
116 EXPECT_EQ(&QuerySession::TK,
117 cast<SetQuery<ast_type_traits::TraversalKind>>(Q)->Var);
118 EXPECT_EQ(ast_type_traits::TK_AsIs,
119 cast<SetQuery<ast_type_traits::TraversalKind>>(Q)->Value);
120
121 Q = parse("set traversal NotATraversal");
122 ASSERT_TRUE(isa<InvalidQuery>(Q));
123 EXPECT_EQ("expected traversal kind, got 'NotATraversal'",
124 cast<InvalidQuery>(Q)->ErrStr);
125 }
126
TEST_F(QueryParserTest,Match)127 TEST_F(QueryParserTest, Match) {
128 QueryRef Q = parse("match decl()");
129 ASSERT_TRUE(isa<MatchQuery>(Q));
130 EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Decl>());
131
132 Q = parse("m stmt()");
133 ASSERT_TRUE(isa<MatchQuery>(Q));
134 EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Stmt>());
135 }
136
TEST_F(QueryParserTest,LetUnlet)137 TEST_F(QueryParserTest, LetUnlet) {
138 QueryRef Q = parse("let foo decl()");
139 ASSERT_TRUE(isa<LetQuery>(Q));
140 EXPECT_EQ("foo", cast<LetQuery>(Q)->Name);
141 EXPECT_TRUE(cast<LetQuery>(Q)->Value.isMatcher());
142 EXPECT_TRUE(cast<LetQuery>(Q)->Value.getMatcher().hasTypedMatcher<Decl>());
143
144 Q = parse("l foo decl()");
145 ASSERT_TRUE(isa<LetQuery>(Q));
146 EXPECT_EQ("foo", cast<LetQuery>(Q)->Name);
147 EXPECT_TRUE(cast<LetQuery>(Q)->Value.isMatcher());
148 EXPECT_TRUE(cast<LetQuery>(Q)->Value.getMatcher().hasTypedMatcher<Decl>());
149
150 Q = parse("let bar \"str\"");
151 ASSERT_TRUE(isa<LetQuery>(Q));
152 EXPECT_EQ("bar", cast<LetQuery>(Q)->Name);
153 EXPECT_TRUE(cast<LetQuery>(Q)->Value.isString());
154 EXPECT_EQ("str", cast<LetQuery>(Q)->Value.getString());
155
156 Q = parse("let");
157 ASSERT_TRUE(isa<InvalidQuery>(Q));
158 EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
159
160 Q = parse("unlet x");
161 ASSERT_TRUE(isa<LetQuery>(Q));
162 EXPECT_EQ("x", cast<LetQuery>(Q)->Name);
163 EXPECT_FALSE(cast<LetQuery>(Q)->Value.hasValue());
164
165 Q = parse("unlet");
166 ASSERT_TRUE(isa<InvalidQuery>(Q));
167 EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
168
169 Q = parse("unlet x bad_data");
170 ASSERT_TRUE(isa<InvalidQuery>(Q));
171 EXPECT_EQ("unexpected extra input: ' bad_data'",
172 cast<InvalidQuery>(Q)->ErrStr);
173 }
174
TEST_F(QueryParserTest,Comment)175 TEST_F(QueryParserTest, Comment) {
176 QueryRef Q = parse("# let foo decl()");
177 ASSERT_TRUE(isa<NoOpQuery>(Q));
178
179 Q = parse("let foo decl() # creates a decl() matcher called foo");
180 ASSERT_TRUE(isa<LetQuery>(Q));
181
182 Q = parse("set bind-root false # reduce noise");
183 ASSERT_TRUE(isa<SetQuery<bool>>(Q));
184 }
185
TEST_F(QueryParserTest,Complete)186 TEST_F(QueryParserTest, Complete) {
187 std::vector<llvm::LineEditor::Completion> Comps =
188 QueryParser::complete("", 0, QS);
189 ASSERT_EQ(8u, Comps.size());
190 EXPECT_EQ("help ", Comps[0].TypedText);
191 EXPECT_EQ("help", Comps[0].DisplayText);
192 EXPECT_EQ("let ", Comps[1].TypedText);
193 EXPECT_EQ("let", Comps[1].DisplayText);
194 EXPECT_EQ("match ", Comps[2].TypedText);
195 EXPECT_EQ("match", Comps[2].DisplayText);
196 EXPECT_EQ("quit ", Comps[3].TypedText);
197 EXPECT_EQ("quit", Comps[3].DisplayText);
198 EXPECT_EQ("set ", Comps[4].TypedText);
199 EXPECT_EQ("set", Comps[4].DisplayText);
200 EXPECT_EQ("enable ", Comps[5].TypedText);
201 EXPECT_EQ("enable", Comps[5].DisplayText);
202 EXPECT_EQ("disable ", Comps[6].TypedText);
203 EXPECT_EQ("disable", Comps[6].DisplayText);
204 EXPECT_EQ("unlet ", Comps[7].TypedText);
205 EXPECT_EQ("unlet", Comps[7].DisplayText);
206
207 Comps = QueryParser::complete("set o", 5, QS);
208 ASSERT_EQ(1u, Comps.size());
209 EXPECT_EQ("utput ", Comps[0].TypedText);
210 EXPECT_EQ("output", Comps[0].DisplayText);
211
212 Comps = QueryParser::complete("set t", 5, QS);
213 ASSERT_EQ(1u, Comps.size());
214 EXPECT_EQ("raversal ", Comps[0].TypedText);
215 EXPECT_EQ("traversal", Comps[0].DisplayText);
216
217 Comps = QueryParser::complete("enable ", 7, QS);
218 ASSERT_EQ(1u, Comps.size());
219 EXPECT_EQ("output ", Comps[0].TypedText);
220 EXPECT_EQ("output", Comps[0].DisplayText);
221
222 Comps = QueryParser::complete("enable output ", 14, QS);
223 ASSERT_EQ(4u, Comps.size());
224
225 EXPECT_EQ("diag ", Comps[0].TypedText);
226 EXPECT_EQ("diag", Comps[0].DisplayText);
227 EXPECT_EQ("print ", Comps[1].TypedText);
228 EXPECT_EQ("print", Comps[1].DisplayText);
229 EXPECT_EQ("detailed-ast ", Comps[2].TypedText);
230 EXPECT_EQ("detailed-ast", Comps[2].DisplayText);
231 EXPECT_EQ("dump ", Comps[3].TypedText);
232 EXPECT_EQ("dump", Comps[3].DisplayText);
233
234 Comps = QueryParser::complete("set traversal ", 14, QS);
235 ASSERT_EQ(2u, Comps.size());
236
237 EXPECT_EQ("AsIs ", Comps[0].TypedText);
238 EXPECT_EQ("AsIs", Comps[0].DisplayText);
239 EXPECT_EQ("IgnoreUnlessSpelledInSource ", Comps[1].TypedText);
240 EXPECT_EQ("IgnoreUnlessSpelledInSource", Comps[1].DisplayText);
241
242 Comps = QueryParser::complete("match while", 11, QS);
243 ASSERT_EQ(1u, Comps.size());
244 EXPECT_EQ("Stmt(", Comps[0].TypedText);
245 EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",
246 Comps[0].DisplayText);
247
248 Comps = QueryParser::complete("m", 1, QS);
249 ASSERT_EQ(1u, Comps.size());
250 EXPECT_EQ("atch ", Comps[0].TypedText);
251 EXPECT_EQ("match", Comps[0].DisplayText);
252
253 Comps = QueryParser::complete("l", 1, QS);
254 ASSERT_EQ(1u, Comps.size());
255 EXPECT_EQ("et ", Comps[0].TypedText);
256 EXPECT_EQ("let", Comps[0].DisplayText);
257 }
258
TEST_F(QueryParserTest,Multiline)259 TEST_F(QueryParserTest, Multiline) {
260
261 // Single string with multiple commands
262 QueryRef Q = parse(R"matcher(
263 set bind-root false
264 set output dump
265 )matcher");
266
267 ASSERT_TRUE(isa<SetQuery<bool>>(Q));
268
269 Q = parse(Q->RemainingContent);
270 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));
271
272 // Missing newline
273 Q = parse(R"matcher(
274 set bind-root false set output dump
275 )matcher");
276
277 ASSERT_TRUE(isa<InvalidQuery>(Q));
278 EXPECT_EQ("unexpected extra input: ' set output dump\n '",
279 cast<InvalidQuery>(Q)->ErrStr);
280
281 // Commands which do their own parsing
282 Q = parse(R"matcher(
283 let fn functionDecl(hasName("foo"))
284 match callExpr(callee(functionDecl()))
285 )matcher");
286
287 ASSERT_TRUE(isa<LetQuery>(Q));
288
289 Q = parse(Q->RemainingContent);
290 ASSERT_TRUE(isa<MatchQuery>(Q));
291
292 // Multi-line matcher
293 Q = parse(R"matcher(
294 match callExpr(callee(
295 functionDecl().bind("fn")
296 ))
297
298 )matcher");
299
300 ASSERT_TRUE(isa<MatchQuery>(Q));
301
302 // Comment locations
303 Q = parse(R"matcher(
304 #nospacecomment
305 # Leading comment
306 match callExpr ( # Trailing comment
307 # Comment alone on line
308
309 callee(
310 functionDecl(
311 ).bind(
312 "fn"
313 )
314 )) # Comment trailing close
315 # Comment after match
316 )matcher");
317
318 ASSERT_TRUE(isa<MatchQuery>(Q));
319
320 // \r\n
321 Q = parse("set bind-root false\r\nset output dump");
322
323 ASSERT_TRUE(isa<SetQuery<bool>>(Q));
324
325 Q = parse(Q->RemainingContent);
326 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));
327
328 // Leading and trailing space in lines
329 Q = parse(" set bind-root false \r\n set output dump ");
330
331 ASSERT_TRUE(isa<SetQuery<bool>>(Q));
332
333 Q = parse(Q->RemainingContent);
334 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));
335
336 // Incomplete commands
337 Q = parse("set\nbind-root false");
338
339 ASSERT_TRUE(isa<InvalidQuery>(Q));
340 EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
341
342 Q = parse("set bind-root\nfalse");
343
344 ASSERT_TRUE(isa<InvalidQuery>(Q));
345 EXPECT_EQ("expected 'true' or 'false', got ''",
346 cast<InvalidQuery>(Q)->ErrStr);
347
348 Q = parse(R"matcher(
349 match callExpr
350 (
351 )
352 )matcher");
353
354 ASSERT_TRUE(isa<InvalidQuery>(Q));
355 EXPECT_EQ("1:9: Error parsing matcher. Found token <NewLine> "
356 "while looking for '('.",
357 cast<InvalidQuery>(Q)->ErrStr);
358
359 Q = parse("let someMatcher\nm parmVarDecl()");
360
361 ASSERT_TRUE(isa<InvalidQuery>(Q));
362 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
363 cast<InvalidQuery>(Q)->ErrStr);
364
365 Q = parse("\nm parmVarDecl()\nlet someMatcher\nm parmVarDecl()");
366
367 ASSERT_TRUE(isa<MatchQuery>(Q));
368 Q = parse(Q->RemainingContent);
369
370 ASSERT_TRUE(isa<InvalidQuery>(Q));
371 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
372 cast<InvalidQuery>(Q)->ErrStr);
373
374 Q = parse("\nlet someMatcher\n");
375
376 ASSERT_TRUE(isa<InvalidQuery>(Q));
377 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
378 cast<InvalidQuery>(Q)->ErrStr);
379
380 Q = parse("\nm parmVarDecl()\nlet someMatcher\n");
381
382 ASSERT_TRUE(isa<MatchQuery>(Q));
383 Q = parse(Q->RemainingContent);
384
385 ASSERT_TRUE(isa<InvalidQuery>(Q));
386 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
387 cast<InvalidQuery>(Q)->ErrStr);
388
389 Q = parse(R"matcher(
390
391 let Construct parmVarDecl()
392
393 m parmVarDecl(
394 Construct
395 )
396 )matcher");
397
398 ASSERT_TRUE(isa<LetQuery>(Q));
399 {
400 llvm::raw_null_ostream NullOutStream;
401 dyn_cast<LetQuery>(Q)->run(NullOutStream, QS);
402 }
403
404 Q = parse(Q->RemainingContent);
405
406 ASSERT_TRUE(isa<MatchQuery>(Q));
407 }
408