Aaron Ballman 371eccd5df
[clang-query] Remove support for srcloc output (#92442)
This functionality was added about three years ago, but has been in a
significantly broken state since it was added. It has begun to cause a
maintenance burden for work in Clang (largely due to the complexity of
having two levels of code generation involved), and the original author
is unable to help maintain it. Because it only worked under limited
circumstances and because of the maintenance burden, it is being
removed. If someone wishes to resurrect the functionality, they should
hopefully be able to do so from this one commit.

Fixes #82591
2024-05-17 08:06:46 -04:00

409 lines
12 KiB
C++

//===---- QueryParserTest.cpp - clang-query test --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "QueryParser.h"
#include "Query.h"
#include "QuerySession.h"
#include "llvm/LineEditor/LineEditor.h"
#include "gtest/gtest.h"
using namespace clang;
using namespace clang::query;
class QueryParserTest : public ::testing::Test {
protected:
QueryParserTest() : QS(llvm::ArrayRef<std::unique_ptr<ASTUnit>>()) {}
QueryRef parse(StringRef Code) { return QueryParser::parse(Code, QS); }
QuerySession QS;
};
TEST_F(QueryParserTest, NoOp) {
QueryRef Q = parse("");
EXPECT_TRUE(isa<NoOpQuery>(Q));
Q = parse("\n");
EXPECT_TRUE(isa<NoOpQuery>(Q));
}
TEST_F(QueryParserTest, Invalid) {
QueryRef Q = parse("foo");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("unknown command: foo", cast<InvalidQuery>(Q)->ErrStr);
}
TEST_F(QueryParserTest, Help) {
QueryRef Q = parse("help");
ASSERT_TRUE(isa<HelpQuery>(Q));
Q = parse("help me");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("unexpected extra input: ' me'", cast<InvalidQuery>(Q)->ErrStr);
}
TEST_F(QueryParserTest, Quit) {
QueryRef Q = parse("quit");
ASSERT_TRUE(isa<QuitQuery>(Q));
Q = parse("q");
ASSERT_TRUE(isa<QuitQuery>(Q));
Q = parse("quit me");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("unexpected extra input: ' me'", cast<InvalidQuery>(Q)->ErrStr);
}
TEST_F(QueryParserTest, Set) {
QueryRef Q = parse("set");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
Q = parse("set foo bar");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("unknown variable: 'foo'", cast<InvalidQuery>(Q)->ErrStr);
Q = parse("set output");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got ''",
cast<InvalidQuery>(Q)->ErrStr);
Q = parse("set bind-root true foo");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("unexpected extra input: ' foo'", cast<InvalidQuery>(Q)->ErrStr);
Q = parse("set output foo");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("expected 'diag', 'print', 'detailed-ast' or 'dump', got 'foo'",
cast<InvalidQuery>(Q)->ErrStr);
Q = parse("set output dump");
ASSERT_TRUE(isa<SetExclusiveOutputQuery >(Q));
EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<SetExclusiveOutputQuery>(Q)->Var);
Q = parse("set output detailed-ast");
ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));
EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<SetExclusiveOutputQuery>(Q)->Var);
Q = parse("enable output detailed-ast");
ASSERT_TRUE(isa<EnableOutputQuery>(Q));
EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<EnableOutputQuery>(Q)->Var);
Q = parse("enable");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
Q = parse("disable output detailed-ast");
ASSERT_TRUE(isa<DisableOutputQuery>(Q));
EXPECT_EQ(&QuerySession::DetailedASTOutput, cast<DisableOutputQuery>(Q)->Var);
Q = parse("set bind-root foo");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("expected 'true' or 'false', got 'foo'",
cast<InvalidQuery>(Q)->ErrStr);
Q = parse("set bind-root true");
ASSERT_TRUE(isa<SetQuery<bool> >(Q));
EXPECT_EQ(&QuerySession::BindRoot, cast<SetQuery<bool> >(Q)->Var);
EXPECT_EQ(true, cast<SetQuery<bool> >(Q)->Value);
Q = parse("set traversal AsIs");
ASSERT_TRUE(isa<SetQuery<TraversalKind>>(Q));
EXPECT_EQ(&QuerySession::TK, cast<SetQuery<TraversalKind>>(Q)->Var);
EXPECT_EQ(TK_AsIs, cast<SetQuery<TraversalKind>>(Q)->Value);
Q = parse("set traversal NotATraversal");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("expected traversal kind, got 'NotATraversal'",
cast<InvalidQuery>(Q)->ErrStr);
}
TEST_F(QueryParserTest, Match) {
QueryRef Q = parse("match decl()");
ASSERT_TRUE(isa<MatchQuery>(Q));
EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Decl>());
Q = parse("m stmt()");
ASSERT_TRUE(isa<MatchQuery>(Q));
EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Stmt>());
}
TEST_F(QueryParserTest, LetUnlet) {
QueryRef Q = parse("let foo decl()");
ASSERT_TRUE(isa<LetQuery>(Q));
EXPECT_EQ("foo", cast<LetQuery>(Q)->Name);
EXPECT_TRUE(cast<LetQuery>(Q)->Value.isMatcher());
EXPECT_TRUE(cast<LetQuery>(Q)->Value.getMatcher().hasTypedMatcher<Decl>());
Q = parse("l foo decl()");
ASSERT_TRUE(isa<LetQuery>(Q));
EXPECT_EQ("foo", cast<LetQuery>(Q)->Name);
EXPECT_TRUE(cast<LetQuery>(Q)->Value.isMatcher());
EXPECT_TRUE(cast<LetQuery>(Q)->Value.getMatcher().hasTypedMatcher<Decl>());
Q = parse("let bar \"str\"");
ASSERT_TRUE(isa<LetQuery>(Q));
EXPECT_EQ("bar", cast<LetQuery>(Q)->Name);
EXPECT_TRUE(cast<LetQuery>(Q)->Value.isString());
EXPECT_EQ("str", cast<LetQuery>(Q)->Value.getString());
Q = parse("let");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
Q = parse("unlet x");
ASSERT_TRUE(isa<LetQuery>(Q));
EXPECT_EQ("x", cast<LetQuery>(Q)->Name);
EXPECT_FALSE(cast<LetQuery>(Q)->Value.hasValue());
Q = parse("unlet");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
Q = parse("unlet x bad_data");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("unexpected extra input: ' bad_data'",
cast<InvalidQuery>(Q)->ErrStr);
}
TEST_F(QueryParserTest, Comment) {
QueryRef Q = parse("# let foo decl()");
ASSERT_TRUE(isa<NoOpQuery>(Q));
Q = parse("let foo decl() # creates a decl() matcher called foo");
ASSERT_TRUE(isa<LetQuery>(Q));
Q = parse("set bind-root false # reduce noise");
ASSERT_TRUE(isa<SetQuery<bool>>(Q));
}
TEST_F(QueryParserTest, Complete) {
std::vector<llvm::LineEditor::Completion> Comps =
QueryParser::complete("", 0, QS);
ASSERT_EQ(9u, Comps.size());
EXPECT_EQ("help ", Comps[0].TypedText);
EXPECT_EQ("help", Comps[0].DisplayText);
EXPECT_EQ("let ", Comps[1].TypedText);
EXPECT_EQ("let", Comps[1].DisplayText);
EXPECT_EQ("match ", Comps[2].TypedText);
EXPECT_EQ("match", Comps[2].DisplayText);
EXPECT_EQ("quit ", Comps[3].TypedText);
EXPECT_EQ("quit", Comps[3].DisplayText);
EXPECT_EQ("set ", Comps[4].TypedText);
EXPECT_EQ("set", Comps[4].DisplayText);
EXPECT_EQ("enable ", Comps[5].TypedText);
EXPECT_EQ("enable", Comps[5].DisplayText);
EXPECT_EQ("disable ", Comps[6].TypedText);
EXPECT_EQ("disable", Comps[6].DisplayText);
EXPECT_EQ("unlet ", Comps[7].TypedText);
EXPECT_EQ("unlet", Comps[7].DisplayText);
EXPECT_EQ("file ", Comps[8].TypedText);
EXPECT_EQ("file", Comps[8].DisplayText);
Comps = QueryParser::complete("set o", 5, QS);
ASSERT_EQ(1u, Comps.size());
EXPECT_EQ("utput ", Comps[0].TypedText);
EXPECT_EQ("output", Comps[0].DisplayText);
Comps = QueryParser::complete("set t", 5, QS);
ASSERT_EQ(1u, Comps.size());
EXPECT_EQ("raversal ", Comps[0].TypedText);
EXPECT_EQ("traversal", Comps[0].DisplayText);
Comps = QueryParser::complete("enable ", 7, QS);
ASSERT_EQ(1u, Comps.size());
EXPECT_EQ("output ", Comps[0].TypedText);
EXPECT_EQ("output", Comps[0].DisplayText);
Comps = QueryParser::complete("enable output ", 14, QS);
ASSERT_EQ(4u, Comps.size());
EXPECT_EQ("diag ", Comps[0].TypedText);
EXPECT_EQ("diag", Comps[0].DisplayText);
EXPECT_EQ("print ", Comps[1].TypedText);
EXPECT_EQ("print", Comps[1].DisplayText);
EXPECT_EQ("detailed-ast ", Comps[2].TypedText);
EXPECT_EQ("detailed-ast", Comps[2].DisplayText);
EXPECT_EQ("dump ", Comps[3].TypedText);
EXPECT_EQ("dump", Comps[3].DisplayText);
Comps = QueryParser::complete("set traversal ", 14, QS);
ASSERT_EQ(2u, Comps.size());
EXPECT_EQ("AsIs ", Comps[0].TypedText);
EXPECT_EQ("AsIs", Comps[0].DisplayText);
EXPECT_EQ("IgnoreUnlessSpelledInSource ", Comps[1].TypedText);
EXPECT_EQ("IgnoreUnlessSpelledInSource", Comps[1].DisplayText);
Comps = QueryParser::complete("match while", 11, QS);
ASSERT_EQ(1u, Comps.size());
EXPECT_EQ("Stmt(", Comps[0].TypedText);
EXPECT_EQ("Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)",
Comps[0].DisplayText);
Comps = QueryParser::complete("m", 1, QS);
ASSERT_EQ(1u, Comps.size());
EXPECT_EQ("atch ", Comps[0].TypedText);
EXPECT_EQ("match", Comps[0].DisplayText);
Comps = QueryParser::complete("l", 1, QS);
ASSERT_EQ(1u, Comps.size());
EXPECT_EQ("et ", Comps[0].TypedText);
EXPECT_EQ("let", Comps[0].DisplayText);
}
TEST_F(QueryParserTest, Multiline) {
// Single string with multiple commands
QueryRef Q = parse(R"matcher(
set bind-root false
set output dump
)matcher");
ASSERT_TRUE(isa<SetQuery<bool>>(Q));
Q = parse(Q->RemainingContent);
ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));
// Missing newline
Q = parse(R"matcher(
set bind-root false set output dump
)matcher");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("unexpected extra input: ' set output dump\n '",
cast<InvalidQuery>(Q)->ErrStr);
// Commands which do their own parsing
Q = parse(R"matcher(
let fn functionDecl(hasName("foo"))
match callExpr(callee(functionDecl()))
)matcher");
ASSERT_TRUE(isa<LetQuery>(Q));
Q = parse(Q->RemainingContent);
ASSERT_TRUE(isa<MatchQuery>(Q));
// Multi-line matcher
Q = parse(R"matcher(
match callExpr(callee(
functionDecl().bind("fn")
))
)matcher");
ASSERT_TRUE(isa<MatchQuery>(Q));
// Comment locations
Q = parse(R"matcher(
#nospacecomment
# Leading comment
match callExpr ( # Trailing comment
# Comment alone on line
callee(
functionDecl(
).bind(
"fn"
)
)) # Comment trailing close
# Comment after match
)matcher");
ASSERT_TRUE(isa<MatchQuery>(Q));
// \r\n
Q = parse("set bind-root false\r\nset output dump");
ASSERT_TRUE(isa<SetQuery<bool>>(Q));
Q = parse(Q->RemainingContent);
ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));
// Leading and trailing space in lines
Q = parse(" set bind-root false \r\n set output dump ");
ASSERT_TRUE(isa<SetQuery<bool>>(Q));
Q = parse(Q->RemainingContent);
ASSERT_TRUE(isa<SetExclusiveOutputQuery>(Q));
// Incomplete commands
Q = parse("set\nbind-root false");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
Q = parse("set bind-root\nfalse");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("expected 'true' or 'false', got ''",
cast<InvalidQuery>(Q)->ErrStr);
Q = parse(R"matcher(
match callExpr
(
)
)matcher");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("1:9: Error parsing matcher. Found token <NewLine> "
"while looking for '('.",
cast<InvalidQuery>(Q)->ErrStr);
Q = parse("let someMatcher\nm parmVarDecl()");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
cast<InvalidQuery>(Q)->ErrStr);
Q = parse("\nm parmVarDecl()\nlet someMatcher\nm parmVarDecl()");
ASSERT_TRUE(isa<MatchQuery>(Q));
Q = parse(Q->RemainingContent);
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
cast<InvalidQuery>(Q)->ErrStr);
Q = parse("\nlet someMatcher\n");
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
cast<InvalidQuery>(Q)->ErrStr);
Q = parse("\nm parmVarDecl()\nlet someMatcher\n");
ASSERT_TRUE(isa<MatchQuery>(Q));
Q = parse(Q->RemainingContent);
ASSERT_TRUE(isa<InvalidQuery>(Q));
EXPECT_EQ("1:1: Invalid token <NewLine> found when looking for a value.",
cast<InvalidQuery>(Q)->ErrStr);
Q = parse(R"matcher(
let Construct parmVarDecl()
m parmVarDecl(
Construct
)
)matcher");
ASSERT_TRUE(isa<LetQuery>(Q));
{
llvm::raw_null_ostream NullOutStream;
dyn_cast<LetQuery>(Q)->run(NullOutStream, QS);
}
Q = parse(Q->RemainingContent);
ASSERT_TRUE(isa<MatchQuery>(Q));
}