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