[clang-query] Add non-exclusive output API

Summary:
Add granular options for AST dumping, text printing and diagnostics.

This makes it possible to

* Have both diag and dump active at once
* Extend the output with other queryable content in the future.

Reviewers: aaron.ballman, pcc, ioeric, ilya-biryukov, klimek, sammccall

Reviewed By: aaron.ballman

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D52857

llvm-svn: 345522
This commit is contained in:
Stephen Kelly 2018-10-29 18:59:56 +00:00
parent eb15d00193
commit a49fe5d878
6 changed files with 118 additions and 10 deletions

View File

@ -45,6 +45,10 @@ bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
"Set whether to print the current matcher,\n"
" set output <feature> "
"Set whether to output only <feature> content.\n"
" enable output <feature> "
"Enable <feature> content non-exclusively.\n"
" disable output <feature> "
"Disable <feature> content non-exclusively.\n"
" quit, q "
"Terminates the query session.\n\n"
"Several commands accept a <feature> parameter. The available features "

View File

@ -29,6 +29,8 @@ enum QueryKind {
QK_Match,
QK_SetBool,
QK_SetOutputKind,
QK_EnableOutputKind,
QK_DisableOutputKind,
QK_Quit
};
@ -151,6 +153,36 @@ struct SetExclusiveOutputQuery : Query {
bool QuerySession::*Var;
};
// Implements the non-exclusive 'set output dump|diag|print' options.
struct SetNonExclusiveOutputQuery : Query {
SetNonExclusiveOutputQuery(QueryKind Kind, bool QuerySession::*Var,
bool Value)
: Query(Kind), Var(Var), Value(Value) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override {
QS.*Var = Value;
return true;
}
bool QuerySession::*Var;
bool Value;
};
struct EnableOutputQuery : SetNonExclusiveOutputQuery {
EnableOutputQuery(bool QuerySession::*Var)
: SetNonExclusiveOutputQuery(QK_EnableOutputKind, Var, true) {}
static bool classof(const Query *Q) { return Q->Kind == QK_EnableOutputKind; }
};
struct DisableOutputQuery : SetNonExclusiveOutputQuery {
DisableOutputQuery(bool QuerySession::*Var)
: SetNonExclusiveOutputQuery(QK_DisableOutputKind, Var, false) {}
static bool classof(const Query *Q) {
return Q->Kind == QK_DisableOutputKind;
}
};
} // namespace query
} // namespace clang

View File

@ -106,7 +106,7 @@ QueryRef QueryParser::parseSetBool(bool QuerySession::*Var) {
return new SetQuery<bool>(Var, Value);
}
QueryRef QueryParser::parseSetOutputKind() {
template <typename QueryType> QueryRef QueryParser::parseSetOutputKind() {
StringRef ValStr;
unsigned OutKind = LexOrCompleteWord<unsigned>(this, ValStr)
.Case("diag", OK_Diag)
@ -122,11 +122,11 @@ QueryRef QueryParser::parseSetOutputKind() {
switch (OutKind) {
case OK_DetailedAST:
return new SetExclusiveOutputQuery(&QuerySession::DetailedASTOutput);
return new QueryType(&QuerySession::DetailedASTOutput);
case OK_Diag:
return new SetExclusiveOutputQuery(&QuerySession::DiagOutput);
return new QueryType(&QuerySession::DiagOutput);
case OK_Print:
return new SetExclusiveOutputQuery(&QuerySession::PrintOutput);
return new QueryType(&QuerySession::PrintOutput);
}
llvm_unreachable("Invalid output kind");
@ -151,7 +151,9 @@ enum ParsedQueryKind {
PQK_Match,
PQK_Set,
PQK_Unlet,
PQK_Quit
PQK_Quit,
PQK_Enable,
PQK_Disable
};
enum ParsedQueryVariable {
@ -193,6 +195,8 @@ QueryRef QueryParser::doParse() {
.Case("q", PQK_Quit, /*IsCompletion=*/false)
.Case("quit", PQK_Quit)
.Case("set", PQK_Set)
.Case("enable", PQK_Enable)
.Case("disable", PQK_Disable)
.Case("unlet", PQK_Unlet)
.Default(PQK_Invalid);
@ -256,7 +260,7 @@ QueryRef QueryParser::doParse() {
QueryRef Q;
switch (Var) {
case PQV_Output:
Q = parseSetOutputKind();
Q = parseSetOutputKind<SetExclusiveOutputQuery>();
break;
case PQV_BindRoot:
Q = parseSetBool(&QuerySession::BindRoot);
@ -270,6 +274,28 @@ QueryRef QueryParser::doParse() {
return endQuery(Q);
}
case PQK_Enable:
case PQK_Disable: {
StringRef VarStr;
ParsedQueryVariable Var =
LexOrCompleteWord<ParsedQueryVariable>(this, VarStr)
.Case("output", PQV_Output)
.Default(PQV_Invalid);
if (VarStr.empty())
return new InvalidQuery("expected variable name");
if (Var == PQV_Invalid)
return new InvalidQuery("unknown variable: '" + VarStr + "'");
QueryRef Q;
if (QKind == PQK_Enable)
Q = parseSetOutputKind<EnableOutputQuery>();
else if (QKind == PQK_Disable)
Q = parseSetOutputKind<DisableOutputQuery>();
else
llvm_unreachable("Invalid query kind");
return endQuery(Q);
}
case PQK_Unlet: {
StringRef Name = lexWord();

View File

@ -44,7 +44,7 @@ private:
template <typename T> struct LexOrCompleteWord;
QueryRef parseSetBool(bool QuerySession::*Var);
QueryRef parseSetOutputKind();
template <typename QueryType> QueryRef parseSetOutputKind();
QueryRef completeMatcherExpression();
QueryRef endQuery(QueryRef Q);

View File

@ -111,6 +111,19 @@ TEST_F(QueryEngineTest, Basic) {
Str.clear();
EXPECT_TRUE(EnableOutputQuery(&QuerySession::DiagOutput).run(OS, S));
EXPECT_TRUE(EnableOutputQuery(&QuerySession::DetailedASTOutput).run(OS, S));
EXPECT_TRUE(MatchQuery(FooMatcherString, FooMatcher).run(OS, S));
{
auto Output = OS.str();
EXPECT_TRUE(Output.find("FunctionDecl") != std::string::npos);
EXPECT_TRUE(Output.find("foo.cc:1:1: note: \"root\" binds here") !=
std::string::npos);
}
Str.clear();
EXPECT_TRUE(SetQuery<bool>(&QuerySession::BindRoot, false).run(OS, S));
EXPECT_TRUE(MatchQuery(FooMatcherString, FooMatcher).run(OS, S));

View File

@ -90,6 +90,18 @@ TEST_F(QueryParserTest, Set) {
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'",
@ -163,7 +175,7 @@ TEST_F(QueryParserTest, Comment) {
TEST_F(QueryParserTest, Complete) {
std::vector<llvm::LineEditor::Completion> Comps =
QueryParser::complete("", 0, QS);
ASSERT_EQ(6u, Comps.size());
ASSERT_EQ(8u, Comps.size());
EXPECT_EQ("help ", Comps[0].TypedText);
EXPECT_EQ("help", Comps[0].DisplayText);
EXPECT_EQ("let ", Comps[1].TypedText);
@ -174,14 +186,35 @@ TEST_F(QueryParserTest, Complete) {
EXPECT_EQ("quit", Comps[3].DisplayText);
EXPECT_EQ("set ", Comps[4].TypedText);
EXPECT_EQ("set", Comps[4].DisplayText);
EXPECT_EQ("unlet ", Comps[5].TypedText);
EXPECT_EQ("unlet", Comps[5].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);
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("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("match while", 11, QS);
ASSERT_EQ(1u, Comps.size());
EXPECT_EQ("Stmt(", Comps[0].TypedText);