aboutsummaryrefslogtreecommitdiff
path: root/clang-query/Query.cpp
diff options
context:
space:
mode:
authorPeter Collingbourne <peter@pcc.me.uk>2013-11-08 00:08:23 +0000
committerPeter Collingbourne <peter@pcc.me.uk>2013-11-08 00:08:23 +0000
commit26c59a1880ca9070b0b914a31667dbe218f0f74d (patch)
tree82e6f26d7de3399518c215aa384363aae416235d /clang-query/Query.cpp
parentc169fd8048b20d6a652a0d777b5eecc037db08aa (diff)
Introduce clang-query tool.
This tool is for interactive exploration of the Clang AST using AST matchers. It currently allows the user to enter a matcher at an interactive prompt and view the resulting bindings as diagnostics, AST pretty prints or AST dumps. Example session: $ cat foo.c void foo(void) {} $ clang-query foo.c -- clang-query> match functionDecl() Match #1: foo.c:1:1: note: "root" binds here void foo(void) {} ^~~~~~~~~~~~~~~~~ 1 match. Differential Revision: http://llvm-reviews.chandlerc.com/D2098 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@194227 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'clang-query/Query.cpp')
-rw-r--r--clang-query/Query.cpp131
1 files changed, 131 insertions, 0 deletions
diff --git a/clang-query/Query.cpp b/clang-query/Query.cpp
new file mode 100644
index 00000000..5a46f6f9
--- /dev/null
+++ b/clang-query/Query.cpp
@@ -0,0 +1,131 @@
+//===---- Query.cpp - clang-query query -----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Query.h"
+#include "QuerySession.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/TextDiagnostic.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::dynamic;
+
+namespace clang {
+namespace query {
+
+Query::~Query() {}
+
+bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+ OS << ErrStr << "\n";
+ return false;
+}
+
+bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+ return true;
+}
+
+bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+ OS << "Available commands:\n\n"
+ " match MATCHER, m MATCHER "
+ "Match the loaded ASTs against the given matcher.\n"
+ " set bind-root (true|false) "
+ "Set whether to bind the root matcher to \"root\".\n"
+ " set output (diag|print|dump) "
+ "Set whether to print bindings as diagnostics,\n"
+ " "
+ "AST pretty prints or AST dumps.\n\n";
+ return true;
+}
+
+namespace {
+
+struct CollectBoundNodes : MatchFinder::MatchCallback {
+ std::vector<BoundNodes> &Bindings;
+ CollectBoundNodes(std::vector<BoundNodes> &Bindings) : Bindings(Bindings) {}
+ void run(const MatchFinder::MatchResult &Result) {
+ Bindings.push_back(Result.Nodes);
+ }
+};
+
+}
+
+bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
+ unsigned MatchCount = 0;
+
+ for (llvm::ArrayRef<ASTUnit *>::iterator I = QS.ASTs.begin(),
+ E = QS.ASTs.end();
+ I != E; ++I) {
+ ASTUnit *AST = *I;
+ MatchFinder Finder;
+ std::vector<BoundNodes> Matches;
+ DynTypedMatcher MaybeBoundMatcher = Matcher;
+ if (QS.BindRoot) {
+ llvm::Optional<DynTypedMatcher> M = Matcher.tryBind("root");
+ if (M)
+ MaybeBoundMatcher = *M;
+ }
+ CollectBoundNodes Collect(Matches);
+ if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
+ OS << "Not a valid top-level matcher.\n";
+ return false;
+ }
+ Finder.matchAST(AST->getASTContext());
+
+ for (std::vector<BoundNodes>::iterator MI = Matches.begin(),
+ ME = Matches.end();
+ MI != ME; ++MI) {
+ OS << "\nMatch #" << ++MatchCount << ":\n\n";
+
+ for (BoundNodes::IDToNodeMap::const_iterator BI = MI->getMap().begin(),
+ BE = MI->getMap().end();
+ BI != BE; ++BI) {
+ switch (QS.OutKind) {
+ case OK_Diag: {
+ clang::SourceRange R = BI->second.getSourceRange();
+ if (R.isValid()) {
+ TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
+ &AST->getDiagnostics().getDiagnosticOptions());
+ TD.emitDiagnostic(
+ R.getBegin(), DiagnosticsEngine::Note,
+ "\"" + BI->first + "\" binds here",
+ ArrayRef<CharSourceRange>(CharSourceRange::getTokenRange(R)),
+ ArrayRef<FixItHint>(), &AST->getSourceManager());
+ }
+ break;
+ }
+ case OK_Print: {
+ OS << "Binding for \"" << BI->first << "\":\n";
+ BI->second.print(OS, AST->getASTContext().getPrintingPolicy());
+ OS << "\n";
+ break;
+ }
+ case OK_Dump: {
+ OS << "Binding for \"" << BI->first << "\":\n";
+ BI->second.dump(OS, AST->getSourceManager());
+ OS << "\n";
+ break;
+ }
+ }
+ }
+
+ if (MI->getMap().empty())
+ OS << "No bindings.\n";
+ }
+ }
+
+ OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");
+ return true;
+}
+
+const QueryKind SetQueryKind<bool>::value;
+const QueryKind SetQueryKind<OutputKind>::value;
+
+} // namespace query
+} // namespace clang