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