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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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