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(5u, 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("srcloc ", Comps[3].TypedText); 230 EXPECT_EQ("srcloc", Comps[3].DisplayText); 231 EXPECT_EQ("dump ", Comps[4].TypedText); 232 EXPECT_EQ("dump", Comps[4].DisplayText); 233 234 Comps = QueryParser::complete("set traversal ", 14, QS); 235 ASSERT_EQ(2u, Comps.size()); 236 237 EXPECT_EQ("AsIs ", Comps[0].TypedText); 238 EXPECT_EQ("AsIs", Comps[0].DisplayText); 239 EXPECT_EQ("IgnoreUnlessSpelledInSource ", Comps[1].TypedText); 240 EXPECT_EQ("IgnoreUnlessSpelledInSource", Comps[1].DisplayText); 241 242 Comps = QueryParser::complete("match while", 11, QS); 243 ASSERT_EQ(1u, Comps.size()); 244 EXPECT_EQ("Stmt(", Comps[0].TypedText); 245 EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)", 246 Comps[0].DisplayText); 247 248 Comps = QueryParser::complete("m", 1, QS); 249 ASSERT_EQ(1u, Comps.size()); 250 EXPECT_EQ("atch ", Comps[0].TypedText); 251 EXPECT_EQ("match", Comps[0].DisplayText); 252 253 Comps = QueryParser::complete("l", 1, QS); 254 ASSERT_EQ(1u, Comps.size()); 255 EXPECT_EQ("et ", Comps[0].TypedText); 256 EXPECT_EQ("let", Comps[0].DisplayText); 257 } 258 259 TEST_F(QueryParserTest, Multiline) { 260 261 // Single string with multiple commands 262 QueryRef Q = parse(R"matcher( 263 set bind-root false 264 set output dump 265 )matcher"); 266 267 ASSERT_TRUE(isa<SetQuery<bool>>(Q)); 268 269 Q = parse(Q->RemainingContent); 270 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q)); 271 272 // Missing newline 273 Q = parse(R"matcher( 274 set bind-root false set output dump 275 )matcher"); 276 277 ASSERT_TRUE(isa<InvalidQuery>(Q)); 278 EXPECT_EQ("unexpected extra input: ' set output dump\n '", 279 cast<InvalidQuery>(Q)->ErrStr); 280 281 // Commands which do their own parsing 282 Q = parse(R"matcher( 283 let fn functionDecl(hasName("foo")) 284 match callExpr(callee(functionDecl())) 285 )matcher"); 286 287 ASSERT_TRUE(isa<LetQuery>(Q)); 288 289 Q = parse(Q->RemainingContent); 290 ASSERT_TRUE(isa<MatchQuery>(Q)); 291 292 // Multi-line matcher 293 Q = parse(R"matcher( 294 match callExpr(callee( 295 functionDecl().bind("fn") 296 )) 297 298 )matcher"); 299 300 ASSERT_TRUE(isa<MatchQuery>(Q)); 301 302 // Comment locations 303 Q = parse(R"matcher( 304 #nospacecomment 305 # Leading comment 306 match callExpr ( # Trailing comment 307 # Comment alone on line 308 309 callee( 310 functionDecl( 311 ).bind( 312 "fn" 313 ) 314 )) # Comment trailing close 315 # Comment after match 316 )matcher"); 317 318 ASSERT_TRUE(isa<MatchQuery>(Q)); 319 320 // \r\n 321 Q = parse("set bind-root false\r\nset output dump"); 322 323 ASSERT_TRUE(isa<SetQuery<bool>>(Q)); 324 325 Q = parse(Q->RemainingContent); 326 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q)); 327 328 // Leading and trailing space in lines 329 Q = parse(" set bind-root false \r\n set output dump "); 330 331 ASSERT_TRUE(isa<SetQuery<bool>>(Q)); 332 333 Q = parse(Q->RemainingContent); 334 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q)); 335 336 // Incomplete commands 337 Q = parse("set\nbind-root false"); 338 339 ASSERT_TRUE(isa<InvalidQuery>(Q)); 340 EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr); 341 342 Q = parse("set bind-root\nfalse"); 343 344 ASSERT_TRUE(isa<InvalidQuery>(Q)); 345 EXPECT_EQ("expected 'true' or 'false', got ''", 346 cast<InvalidQuery>(Q)->ErrStr); 347 348 Q = parse(R"matcher( 349 match callExpr 350 ( 351 ) 352 )matcher"); 353 354 ASSERT_TRUE(isa<InvalidQuery>(Q)); 355 EXPECT_EQ("1:9: Error parsing matcher. Found token <NewLine> " 356 "while looking for '('.", 357 cast<InvalidQuery>(Q)->ErrStr); 358 359 Q = parse("let someMatcher\nm parmVarDecl()"); 360 361 ASSERT_TRUE(isa<InvalidQuery>(Q)); 362 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.", 363 cast<InvalidQuery>(Q)->ErrStr); 364 365 Q = parse("\nm parmVarDecl()\nlet someMatcher\nm parmVarDecl()"); 366 367 ASSERT_TRUE(isa<MatchQuery>(Q)); 368 Q = parse(Q->RemainingContent); 369 370 ASSERT_TRUE(isa<InvalidQuery>(Q)); 371 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.", 372 cast<InvalidQuery>(Q)->ErrStr); 373 374 Q = parse("\nlet someMatcher\n"); 375 376 ASSERT_TRUE(isa<InvalidQuery>(Q)); 377 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.", 378 cast<InvalidQuery>(Q)->ErrStr); 379 380 Q = parse("\nm parmVarDecl()\nlet someMatcher\n"); 381 382 ASSERT_TRUE(isa<MatchQuery>(Q)); 383 Q = parse(Q->RemainingContent); 384 385 ASSERT_TRUE(isa<InvalidQuery>(Q)); 386 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.", 387 cast<InvalidQuery>(Q)->ErrStr); 388 389 Q = parse(R"matcher( 390 391 let Construct parmVarDecl() 392 393 m parmVarDecl( 394 Construct 395 ) 396 )matcher"); 397 398 ASSERT_TRUE(isa<LetQuery>(Q)); 399 { 400 llvm::raw_null_ostream NullOutStream; 401 dyn_cast<LetQuery>(Q)->run(NullOutStream, QS); 402 } 403 404 Q = parse(Q->RemainingContent); 405 406 ASSERT_TRUE(isa<MatchQuery>(Q)); 407 } 408