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(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("set o", 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("set t", 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("enable ", 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("enable output ", 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("set traversal ", 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("match while", 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("m", 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("l", 1, QS); 271 ASSERT_EQ(1u, Comps.size()); 272 EXPECT_EQ("et ", Comps[0].TypedText); 273 EXPECT_EQ("let", Comps[0].DisplayText); 274 } 275 276 TEST_F(QueryParserTest, Multiline) { 277 278 // Single string with multiple commands 279 QueryRef Q = parse(R"matcher( 280 set bind-root false 281 set output dump 282 )matcher"); 283 284 ASSERT_TRUE(isa<SetQuery<bool>>(Q)); 285 286 Q = parse(Q->RemainingContent); 287 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q)); 288 289 // Missing newline 290 Q = parse(R"matcher( 291 set 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(R"matcher( 300 let fn functionDecl(hasName("foo")) 301 match callExpr(callee(functionDecl())) 302 )matcher"); 303 304 ASSERT_TRUE(isa<LetQuery>(Q)); 305 306 Q = parse(Q->RemainingContent); 307 ASSERT_TRUE(isa<MatchQuery>(Q)); 308 309 // Multi-line matcher 310 Q = parse(R"matcher( 311 match callExpr(callee( 312 functionDecl().bind("fn") 313 )) 314 315 )matcher"); 316 317 ASSERT_TRUE(isa<MatchQuery>(Q)); 318 319 // Comment locations 320 Q = parse(R"matcher( 321 #nospacecomment 322 # Leading comment 323 match 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("set bind-root false\r\nset output dump"); 339 340 ASSERT_TRUE(isa<SetQuery<bool>>(Q)); 341 342 Q = parse(Q->RemainingContent); 343 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q)); 344 345 // Leading and trailing space in lines 346 Q = parse(" set bind-root false \r\n set output dump "); 347 348 ASSERT_TRUE(isa<SetQuery<bool>>(Q)); 349 350 Q = parse(Q->RemainingContent); 351 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q)); 352 353 // Incomplete commands 354 Q = parse("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("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(R"matcher( 366 match 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("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("\nm parmVarDecl()\nlet someMatcher\nm parmVarDecl()"); 383 384 ASSERT_TRUE(isa<MatchQuery>(Q)); 385 Q = parse(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("\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("\nm parmVarDecl()\nlet someMatcher\n"); 398 399 ASSERT_TRUE(isa<MatchQuery>(Q)); 400 Q = parse(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(R"matcher( 407 408 let Construct parmVarDecl() 409 410 m parmVarDecl( 411 Construct 412 ) 413 )matcher"); 414 415 ASSERT_TRUE(isa<LetQuery>(Q)); 416 { 417 llvm::raw_null_ostream NullOutStream; 418 dyn_cast<LetQuery>(Q)->run(NullOutStream, QS); 419 } 420 421 Q = parse(Q->RemainingContent); 422 423 ASSERT_TRUE(isa<MatchQuery>(Q)); 424 } 425