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

source code of clang-tools-extra/unittests/clang-query/QueryParserTest.cpp