409 lines · cpp
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-exception6//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 15using namespace clang;16using namespace clang::query;17 18class QueryParserTest : public ::testing::Test {19protected: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 26TEST_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 34TEST_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 40TEST_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 49TEST_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 61TEST_F(QueryParserTest, Set) {62 63 QueryRef Q = parse("set");64 ASSERT_TRUE(isa<InvalidQuery>(Q));65 EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);66 67 Q = parse("set foo bar");68 ASSERT_TRUE(isa<InvalidQuery>(Q));69 EXPECT_EQ("unknown variable: 'foo'", cast<InvalidQuery>(Q)->ErrStr);70 71 Q = parse("set output");72 ASSERT_TRUE(isa<InvalidQuery>(Q));73 EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got ''",74 cast<InvalidQuery>(Q)->ErrStr);75 76 Q = parse("set bind-root true foo");77 ASSERT_TRUE(isa<InvalidQuery>(Q));78 EXPECT_EQ("unexpected extra input: ' foo'", cast<InvalidQuery>(Q)->ErrStr);79 80 Q = parse("set output foo");81 ASSERT_TRUE(isa<InvalidQuery>(Q));82 EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got 'foo'",83 cast<InvalidQuery>(Q)->ErrStr);84 85 Q = parse("set output dump");86 ASSERT_TRUE(isa<SetExclusiveOutputQuery >(Q));87 EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<SetExclusiveOutputQuery>(Q)->Var);88 89 Q = parse("set output detailed-ast");90 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));91 EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<SetExclusiveOutputQuery>(Q)->Var);92 93 Q = parse("enable output detailed-ast");94 ASSERT_TRUE(isa<EnableOutputQuery>(Q));95 EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<EnableOutputQuery>(Q)->Var);96 97 Q = parse("enable");98 ASSERT_TRUE(isa<InvalidQuery>(Q));99 EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);100 101 Q = parse("disable output detailed-ast");102 ASSERT_TRUE(isa<DisableOutputQuery>(Q));103 EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<DisableOutputQuery>(Q)->Var);104 105 Q = parse("set bind-root foo");106 ASSERT_TRUE(isa<InvalidQuery>(Q));107 EXPECT_EQ("expected 'true' or 'false', got 'foo'",108 cast<InvalidQuery>(Q)->ErrStr);109 110 Q = parse("set bind-root true");111 ASSERT_TRUE(isa<SetQuery<bool> >(Q));112 EXPECT_EQ(&QuerySession::BindRoot, cast<SetQuery<bool> >(Q)->Var);113 EXPECT_EQ(true, cast<SetQuery<bool> >(Q)->Value);114 115 Q = parse("set traversal AsIs");116 ASSERT_TRUE(isa<SetQuery<TraversalKind>>(Q));117 EXPECT_EQ(&QuerySession::TK, cast<SetQuery<TraversalKind>>(Q)->Var);118 EXPECT_EQ(TK_AsIs, cast<SetQuery<TraversalKind>>(Q)->Value);119 120 Q = parse("set traversal NotATraversal");121 ASSERT_TRUE(isa<InvalidQuery>(Q));122 EXPECT_EQ("expected traversal kind, got 'NotATraversal'",123 cast<InvalidQuery>(Q)->ErrStr);124}125 126TEST_F(QueryParserTest, Match) {127 QueryRef Q = parse("match decl()");128 ASSERT_TRUE(isa<MatchQuery>(Q));129 EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Decl>());130 131 Q = parse("m stmt()");132 ASSERT_TRUE(isa<MatchQuery>(Q));133 EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Stmt>());134}135 136TEST_F(QueryParserTest, LetUnlet) {137 QueryRef Q = parse("let foo decl()");138 ASSERT_TRUE(isa<LetQuery>(Q));139 EXPECT_EQ("foo", cast<LetQuery>(Q)->Name);140 EXPECT_TRUE(cast<LetQuery>(Q)->Value.isMatcher());141 EXPECT_TRUE(cast<LetQuery>(Q)->Value.getMatcher().hasTypedMatcher<Decl>());142 143 Q = parse("l foo decl()");144 ASSERT_TRUE(isa<LetQuery>(Q));145 EXPECT_EQ("foo", cast<LetQuery>(Q)->Name);146 EXPECT_TRUE(cast<LetQuery>(Q)->Value.isMatcher());147 EXPECT_TRUE(cast<LetQuery>(Q)->Value.getMatcher().hasTypedMatcher<Decl>());148 149 Q = parse("let bar \"str\"");150 ASSERT_TRUE(isa<LetQuery>(Q));151 EXPECT_EQ("bar", cast<LetQuery>(Q)->Name);152 EXPECT_TRUE(cast<LetQuery>(Q)->Value.isString());153 EXPECT_EQ("str", cast<LetQuery>(Q)->Value.getString());154 155 Q = parse("let");156 ASSERT_TRUE(isa<InvalidQuery>(Q));157 EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);158 159 Q = parse("unlet x");160 ASSERT_TRUE(isa<LetQuery>(Q));161 EXPECT_EQ("x", cast<LetQuery>(Q)->Name);162 EXPECT_FALSE(cast<LetQuery>(Q)->Value.hasValue());163 164 Q = parse("unlet");165 ASSERT_TRUE(isa<InvalidQuery>(Q));166 EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);167 168 Q = parse("unlet x bad_data");169 ASSERT_TRUE(isa<InvalidQuery>(Q));170 EXPECT_EQ("unexpected extra input: ' bad_data'",171 cast<InvalidQuery>(Q)->ErrStr);172}173 174TEST_F(QueryParserTest, Comment) {175 QueryRef Q = parse("# let foo decl()");176 ASSERT_TRUE(isa<NoOpQuery>(Q));177 178 Q = parse("let foo decl() # creates a decl() matcher called foo");179 ASSERT_TRUE(isa<LetQuery>(Q));180 181 Q = parse("set bind-root false # reduce noise");182 ASSERT_TRUE(isa<SetQuery<bool>>(Q));183}184 185TEST_F(QueryParserTest, Complete) {186 std::vector<llvm::LineEditor::Completion> Comps =187 QueryParser::complete("", 0, QS);188 ASSERT_EQ(9u, Comps.size());189 EXPECT_EQ("help ", Comps[0].TypedText);190 EXPECT_EQ("help", Comps[0].DisplayText);191 EXPECT_EQ("let ", Comps[1].TypedText);192 EXPECT_EQ("let", Comps[1].DisplayText);193 EXPECT_EQ("match ", Comps[2].TypedText);194 EXPECT_EQ("match", Comps[2].DisplayText);195 EXPECT_EQ("quit ", Comps[3].TypedText);196 EXPECT_EQ("quit", Comps[3].DisplayText);197 EXPECT_EQ("set ", Comps[4].TypedText);198 EXPECT_EQ("set", Comps[4].DisplayText);199 EXPECT_EQ("enable ", Comps[5].TypedText);200 EXPECT_EQ("enable", Comps[5].DisplayText);201 EXPECT_EQ("disable ", Comps[6].TypedText);202 EXPECT_EQ("disable", Comps[6].DisplayText);203 EXPECT_EQ("unlet ", Comps[7].TypedText);204 EXPECT_EQ("unlet", Comps[7].DisplayText);205 EXPECT_EQ("file ", Comps[8].TypedText);206 EXPECT_EQ("file", Comps[8].DisplayText);207 208 Comps = QueryParser::complete("set o", 5, QS);209 ASSERT_EQ(1u, Comps.size());210 EXPECT_EQ("utput ", Comps[0].TypedText);211 EXPECT_EQ("output", Comps[0].DisplayText);212 213 Comps = QueryParser::complete("set t", 5, QS);214 ASSERT_EQ(1u, Comps.size());215 EXPECT_EQ("raversal ", Comps[0].TypedText);216 EXPECT_EQ("traversal", Comps[0].DisplayText);217 218 Comps = QueryParser::complete("enable ", 7, QS);219 ASSERT_EQ(1u, Comps.size());220 EXPECT_EQ("output ", Comps[0].TypedText);221 EXPECT_EQ("output", Comps[0].DisplayText);222 223 Comps = QueryParser::complete("enable output ", 14, QS);224 ASSERT_EQ(4u, Comps.size());225 226 EXPECT_EQ("diag ", Comps[0].TypedText);227 EXPECT_EQ("diag", Comps[0].DisplayText);228 EXPECT_EQ("print ", Comps[1].TypedText);229 EXPECT_EQ("print", Comps[1].DisplayText);230 EXPECT_EQ("detailed-ast ", Comps[2].TypedText);231 EXPECT_EQ("detailed-ast", Comps[2].DisplayText);232 EXPECT_EQ("dump ", Comps[3].TypedText);233 EXPECT_EQ("dump", Comps[3].DisplayText);234 235 Comps = QueryParser::complete("set traversal ", 14, QS);236 ASSERT_EQ(2u, Comps.size());237 238 EXPECT_EQ("AsIs ", Comps[0].TypedText);239 EXPECT_EQ("AsIs", Comps[0].DisplayText);240 EXPECT_EQ("IgnoreUnlessSpelledInSource ", Comps[1].TypedText);241 EXPECT_EQ("IgnoreUnlessSpelledInSource", Comps[1].DisplayText);242 243 Comps = QueryParser::complete("match while", 11, QS);244 ASSERT_EQ(1u, Comps.size());245 EXPECT_EQ("Stmt(", Comps[0].TypedText);246 EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",247 Comps[0].DisplayText);248 249 Comps = QueryParser::complete("m", 1, QS);250 ASSERT_EQ(1u, Comps.size());251 EXPECT_EQ("atch ", Comps[0].TypedText);252 EXPECT_EQ("match", Comps[0].DisplayText);253 254 Comps = QueryParser::complete("l", 1, QS);255 ASSERT_EQ(1u, Comps.size());256 EXPECT_EQ("et ", Comps[0].TypedText);257 EXPECT_EQ("let", Comps[0].DisplayText);258}259 260TEST_F(QueryParserTest, Multiline) {261 262 // Single string with multiple commands263 QueryRef Q = parse(R"matcher(264set bind-root false265set output dump266 )matcher");267 268 ASSERT_TRUE(isa<SetQuery<bool>>(Q));269 270 Q = parse(Q->RemainingContent);271 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));272 273 // Missing newline274 Q = parse(R"matcher(275set bind-root false set output dump276 )matcher");277 278 ASSERT_TRUE(isa<InvalidQuery>(Q));279 EXPECT_EQ("unexpected extra input: ' set output dump\n '",280 cast<InvalidQuery>(Q)->ErrStr);281 282 // Commands which do their own parsing283 Q = parse(R"matcher(284let fn functionDecl(hasName("foo"))285match callExpr(callee(functionDecl()))286 )matcher");287 288 ASSERT_TRUE(isa<LetQuery>(Q));289 290 Q = parse(Q->RemainingContent);291 ASSERT_TRUE(isa<MatchQuery>(Q));292 293 // Multi-line matcher294 Q = parse(R"matcher(295match callExpr(callee(296 functionDecl().bind("fn")297 ))298 299 )matcher");300 301 ASSERT_TRUE(isa<MatchQuery>(Q));302 303 // Comment locations304 Q = parse(R"matcher(305#nospacecomment306# Leading comment307match callExpr ( # Trailing comment308 # Comment alone on line309 310 callee(311 functionDecl(312 ).bind(313 "fn"314 )315 )) # Comment trailing close316# Comment after match317 )matcher");318 319 ASSERT_TRUE(isa<MatchQuery>(Q));320 321 // \r\n322 Q = parse("set bind-root false\r\nset output dump");323 324 ASSERT_TRUE(isa<SetQuery<bool>>(Q));325 326 Q = parse(Q->RemainingContent);327 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));328 329 // Leading and trailing space in lines330 Q = parse(" set bind-root false \r\n set output dump ");331 332 ASSERT_TRUE(isa<SetQuery<bool>>(Q));333 334 Q = parse(Q->RemainingContent);335 ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));336 337 // Incomplete commands338 Q = parse("set\nbind-root false");339 340 ASSERT_TRUE(isa<InvalidQuery>(Q));341 EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);342 343 Q = parse("set bind-root\nfalse");344 345 ASSERT_TRUE(isa<InvalidQuery>(Q));346 EXPECT_EQ("expected 'true' or 'false', got ''",347 cast<InvalidQuery>(Q)->ErrStr);348 349 Q = parse(R"matcher(350match callExpr351(352)353 )matcher");354 355 ASSERT_TRUE(isa<InvalidQuery>(Q));356 EXPECT_EQ("1:9: Error parsing matcher. Found token <NewLine> "357 "while looking for '('.",358 cast<InvalidQuery>(Q)->ErrStr);359 360 Q = parse("let someMatcher\nm parmVarDecl()");361 362 ASSERT_TRUE(isa<InvalidQuery>(Q));363 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",364 cast<InvalidQuery>(Q)->ErrStr);365 366 Q = parse("\nm parmVarDecl()\nlet someMatcher\nm parmVarDecl()");367 368 ASSERT_TRUE(isa<MatchQuery>(Q));369 Q = parse(Q->RemainingContent);370 371 ASSERT_TRUE(isa<InvalidQuery>(Q));372 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",373 cast<InvalidQuery>(Q)->ErrStr);374 375 Q = parse("\nlet someMatcher\n");376 377 ASSERT_TRUE(isa<InvalidQuery>(Q));378 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",379 cast<InvalidQuery>(Q)->ErrStr);380 381 Q = parse("\nm parmVarDecl()\nlet someMatcher\n");382 383 ASSERT_TRUE(isa<MatchQuery>(Q));384 Q = parse(Q->RemainingContent);385 386 ASSERT_TRUE(isa<InvalidQuery>(Q));387 EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",388 cast<InvalidQuery>(Q)->ErrStr);389 390 Q = parse(R"matcher(391 392let Construct parmVarDecl()393 394m parmVarDecl(395 Construct396)397)matcher");398 399 ASSERT_TRUE(isa<LetQuery>(Q));400 {401 llvm::raw_null_ostream NullOutStream;402 dyn_cast<LetQuery>(Q)->run(NullOutStream, QS);403 }404 405 Q = parse(Q->RemainingContent);406 407 ASSERT_TRUE(isa<MatchQuery>(Q));408}409