[clang-query] Add option to print matcher expression

Summary:
This is useful if using clang-query -f with a file containing multiple
matchers.

Reviewers: aaron.ballman

Subscribers: cfe-commits

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

llvm-svn: 344840
This commit is contained in:
Stephen Kelly 2018-10-20 09:13:59 +00:00
parent d0e5eca0fd
commit 4a5b01ddd7
5 changed files with 42 additions and 17 deletions

View File

@ -41,6 +41,8 @@ bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
"as part of other expressions.\n" "as part of other expressions.\n"
" set bind-root (true|false) " " set bind-root (true|false) "
"Set whether to bind the root matcher to \"root\".\n" "Set whether to bind the root matcher to \"root\".\n"
" set print-matcher (true|false) "
"Set whether to print the current matcher,\n"
" set output (diag|print|dump) " " set output (diag|print|dump) "
"Set whether to print bindings as diagnostics,\n" "Set whether to print bindings as diagnostics,\n"
" " " "
@ -86,6 +88,12 @@ bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
} }
Finder.matchAST(AST->getASTContext()); Finder.matchAST(AST->getASTContext());
if (QS.PrintMatcher) {
std::string prefixText = "Matcher: ";
OS << "\n " << prefixText << Source << "\n";
OS << " " << std::string(prefixText.size() + Source.size(), '=') << '\n';
}
for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) { for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
OS << "\nMatch #" << ++MatchCount << ":\n\n"; OS << "\nMatch #" << ++MatchCount << ":\n\n";

View File

@ -83,12 +83,15 @@ struct QuitQuery : Query {
/// Query for "match MATCHER". /// Query for "match MATCHER".
struct MatchQuery : Query { struct MatchQuery : Query {
MatchQuery(const ast_matchers::dynamic::DynTypedMatcher &Matcher) MatchQuery(StringRef Source,
: Query(QK_Match), Matcher(Matcher) {} const ast_matchers::dynamic::DynTypedMatcher &Matcher)
: Query(QK_Match), Matcher(Matcher), Source(Source) {}
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override; bool run(llvm::raw_ostream &OS, QuerySession &QS) const override;
ast_matchers::dynamic::DynTypedMatcher Matcher; ast_matchers::dynamic::DynTypedMatcher Matcher;
StringRef Source;
static bool classof(const Query *Q) { return Q->Kind == QK_Match; } static bool classof(const Query *Q) { return Q->Kind == QK_Match; }
}; };

View File

@ -142,7 +142,12 @@ enum ParsedQueryKind {
PQK_Quit PQK_Quit
}; };
enum ParsedQueryVariable { PQV_Invalid, PQV_Output, PQV_BindRoot }; enum ParsedQueryVariable {
PQV_Invalid,
PQV_Output,
PQV_BindRoot,
PQV_PrintMatcher
};
QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) { QueryRef makeInvalidQueryFromDiagnostics(const Diagnostics &Diag) {
std::string ErrStr; std::string ErrStr;
@ -214,21 +219,23 @@ QueryRef QueryParser::doParse() {
return completeMatcherExpression(); return completeMatcherExpression();
Diagnostics Diag; Diagnostics Diag;
auto MatcherSource = StringRef(Begin, End - Begin).trim();
Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression( Optional<DynTypedMatcher> Matcher = Parser::parseMatcherExpression(
StringRef(Begin, End - Begin), nullptr, &QS.NamedValues, &Diag); MatcherSource, nullptr, &QS.NamedValues, &Diag);
if (!Matcher) { if (!Matcher) {
return makeInvalidQueryFromDiagnostics(Diag); return makeInvalidQueryFromDiagnostics(Diag);
} }
return new MatchQuery(*Matcher); return new MatchQuery(MatcherSource, *Matcher);
} }
case PQK_Set: { case PQK_Set: {
StringRef VarStr; StringRef VarStr;
ParsedQueryVariable Var = LexOrCompleteWord<ParsedQueryVariable>(this, ParsedQueryVariable Var =
VarStr) LexOrCompleteWord<ParsedQueryVariable>(this, VarStr)
.Case("output", PQV_Output) .Case("output", PQV_Output)
.Case("bind-root", PQV_BindRoot) .Case("bind-root", PQV_BindRoot)
.Default(PQV_Invalid); .Case("print-matcher", PQV_PrintMatcher)
.Default(PQV_Invalid);
if (VarStr.empty()) if (VarStr.empty())
return new InvalidQuery("expected variable name"); return new InvalidQuery("expected variable name");
if (Var == PQV_Invalid) if (Var == PQV_Invalid)
@ -242,6 +249,9 @@ QueryRef QueryParser::doParse() {
case PQV_BindRoot: case PQV_BindRoot:
Q = parseSetBool(&QuerySession::BindRoot); Q = parseSetBool(&QuerySession::BindRoot);
break; break;
case PQV_PrintMatcher:
Q = parseSetBool(&QuerySession::PrintMatcher);
break;
case PQV_Invalid: case PQV_Invalid:
llvm_unreachable("Invalid query kind"); llvm_unreachable("Invalid query kind");
} }

View File

@ -25,11 +25,13 @@ namespace query {
class QuerySession { class QuerySession {
public: public:
QuerySession(llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs) QuerySession(llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs)
: ASTs(ASTs), OutKind(OK_Diag), BindRoot(true), Terminate(false) {} : ASTs(ASTs), OutKind(OK_Diag), BindRoot(true), PrintMatcher(false),
Terminate(false) {}
llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs; llvm::ArrayRef<std::unique_ptr<ASTUnit>> ASTs;
OutputKind OutKind; OutputKind OutKind;
bool BindRoot; bool BindRoot;
bool PrintMatcher;
bool Terminate; bool Terminate;
llvm::StringMap<ast_matchers::dynamic::VariantValue> NamedValues; llvm::StringMap<ast_matchers::dynamic::VariantValue> NamedValues;
}; };

View File

@ -52,6 +52,8 @@ TEST_F(QueryEngineTest, Basic) {
DynTypedMatcher FnMatcher = functionDecl(); DynTypedMatcher FnMatcher = functionDecl();
DynTypedMatcher FooMatcher = functionDecl(hasName("foo1")); DynTypedMatcher FooMatcher = functionDecl(hasName("foo1"));
std::string FooMatcherString = "functionDecl(hasName(\"foo1\"))";
EXPECT_TRUE(NoOpQuery().run(OS, S)); EXPECT_TRUE(NoOpQuery().run(OS, S));
EXPECT_EQ("", OS.str()); EXPECT_EQ("", OS.str());
@ -70,7 +72,7 @@ TEST_F(QueryEngineTest, Basic) {
Str.clear(); Str.clear();
EXPECT_TRUE(MatchQuery(FnMatcher).run(OS, S)); EXPECT_TRUE(MatchQuery("functionDecl()", FnMatcher).run(OS, S));
EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") != EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
std::string::npos); std::string::npos);
@ -84,7 +86,7 @@ TEST_F(QueryEngineTest, Basic) {
Str.clear(); Str.clear();
EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S)); EXPECT_TRUE(MatchQuery(FooMatcherString, FooMatcher).run(OS, S));
EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") != EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
std::string::npos); std::string::npos);
@ -94,7 +96,7 @@ TEST_F(QueryEngineTest, Basic) {
EXPECT_TRUE( EXPECT_TRUE(
SetQuery<OutputKind>(&QuerySession::OutKind, OK_Print).run(OS, S)); SetQuery<OutputKind>(&QuerySession::OutKind, OK_Print).run(OS, S));
EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S)); EXPECT_TRUE(MatchQuery(FooMatcherString, FooMatcher).run(OS, S));
EXPECT_TRUE(OS.str().find("Binding for \"root\":\nvoid foo1()") != EXPECT_TRUE(OS.str().find("Binding for \"root\":\nvoid foo1()") !=
std::string::npos); std::string::npos);
@ -102,20 +104,20 @@ TEST_F(QueryEngineTest, Basic) {
Str.clear(); Str.clear();
EXPECT_TRUE(SetQuery<OutputKind>(&QuerySession::OutKind, OK_Dump).run(OS, S)); EXPECT_TRUE(SetQuery<OutputKind>(&QuerySession::OutKind, OK_Dump).run(OS, S));
EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S)); EXPECT_TRUE(MatchQuery(FooMatcherString, FooMatcher).run(OS, S));
EXPECT_TRUE(OS.str().find("FunctionDecl") != std::string::npos); EXPECT_TRUE(OS.str().find("FunctionDecl") != std::string::npos);
Str.clear(); Str.clear();
EXPECT_TRUE(SetQuery<bool>(&QuerySession::BindRoot, false).run(OS, S)); EXPECT_TRUE(SetQuery<bool>(&QuerySession::BindRoot, false).run(OS, S));
EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S)); EXPECT_TRUE(MatchQuery(FooMatcherString, FooMatcher).run(OS, S));
EXPECT_TRUE(OS.str().find("No bindings.") != std::string::npos); EXPECT_TRUE(OS.str().find("No bindings.") != std::string::npos);
Str.clear(); Str.clear();
EXPECT_FALSE(MatchQuery(isMain()).run(OS, S)); EXPECT_FALSE(MatchQuery("isMain()", isMain()).run(OS, S));
EXPECT_EQ("Not a valid top-level matcher.\n", OS.str()); EXPECT_EQ("Not a valid top-level matcher.\n", OS.str());
} }