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