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

llvm-svn: 194227
diff --git a/clang-tools-extra/clang-query/Query.cpp b/clang-tools-extra/clang-query/Query.cpp
new file mode 100644
index 0000000..5a46f6f
--- /dev/null
+++ b/clang-tools-extra/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