Hi klimek,

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.

http://llvm-reviews.chandlerc.com/D2098

Files:
  CMakeLists.txt
  Makefile
  clang-query/CMakeLists.txt
  clang-query/Makefile
  unittests/Makefile
  clang-query/Query.h
  clang-query/QueryEngine.cpp
  clang-query/QueryEngine.h
  clang-query/QueryParser.cpp
  clang-query/QueryParser.h
  clang-query/tool/CMakeLists.txt
  clang-query/tool/ClangQuery.cpp
  test/CMakeLists.txt
  test/clang-query/function-decl.c
  test/lit.cfg
  test/lit.site.cfg.in
  unittests/CMakeLists.txt
  unittests/clang-query/CMakeLists.txt
  unittests/clang-query/Makefile
  unittests/clang-query/QueryEngineTest.cpp
  unittests/clang-query/QueryParserTest.cpp
Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt
+++ CMakeLists.txt
@@ -1,5 +1,8 @@
+check_library_exists(edit el_init "" HAVE_LIBEDIT)
+
 add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-modernize)
+add_subdirectory(clang-query)
 add_subdirectory(clang-tidy)
 add_subdirectory(modularize)
 add_subdirectory(pp-trace)
Index: Makefile
===================================================================
--- Makefile
+++ Makefile
@@ -12,7 +12,8 @@
 include $(CLANG_LEVEL)/../../Makefile.config
 
 PARALLEL_DIRS := remove-cstr-calls tool-template modularize pp-trace
-DIRS := clang-apply-replacements clang-modernize clang-tidy unittests
+DIRS := clang-apply-replacements clang-modernize clang-tidy clang-query \
+	unittests
 
 include $(CLANG_LEVEL)/Makefile
 
Index: clang-query/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang-query/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_clang_library(clangQuery
+  QueryEngine.cpp
+  QueryParser.cpp
+  )
+target_link_libraries(clangQuery
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangDynamicASTMatchers
+  clangFrontend
+  )
+
+add_subdirectory(tool)
Index: clang-query/Makefile
===================================================================
--- clang-query/Makefile
+++ clang-query/Makefile
@@ -1,4 +1,4 @@
-##===- tools/extra/test/Unit/Makefile ----------------------*- Makefile -*-===##
+##===- tools/extra/clang-query/Makefile --------------------*- Makefile -*-===##
 #
 #                     The LLVM Compiler Infrastructure
 #
@@ -8,8 +8,7 @@
 ##===----------------------------------------------------------------------===##
 
 CLANG_LEVEL := ../../..
+LIBRARYNAME := clangQuery
 include $(CLANG_LEVEL)/../../Makefile.config
 
-PARALLEL_DIRS := clang-apply-replacements clang-modernize clang-tidy
-
 include $(CLANG_LEVEL)/Makefile
Index: unittests/Makefile
===================================================================
--- unittests/Makefile
+++ unittests/Makefile
@@ -10,6 +10,6 @@
 CLANG_LEVEL := ../../..
 include $(CLANG_LEVEL)/../../Makefile.config
 
-PARALLEL_DIRS := clang-apply-replacements clang-modernize clang-tidy
+PARALLEL_DIRS := clang-apply-replacements clang-modernize clang-query clang-tidy
 
 include $(CLANG_LEVEL)/Makefile
Index: clang-query/Query.h
===================================================================
--- /dev/null
+++ clang-query/Query.h
@@ -0,0 +1,57 @@
+//===--- Query.h - clang-query ----------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_H
+
+#include <string>
+#include "clang/ASTMatchers/Dynamic/VariantValue.h"
+#include "llvm/ADT/Optional.h"
+
+namespace clang {
+namespace query {
+
+enum OutputKind {
+  OK_Diag,
+  OK_Print,
+  OK_Dump
+};
+
+enum QueryKind {
+  QK_Invalid,
+  QK_NoOp,
+  QK_Match,
+  QK_Set
+};
+
+enum QueryVariable {
+  QV_Output,
+  QV_BindRoot
+};
+
+struct Query {
+  QueryKind Kind;
+
+  // QK_Invalid
+  std::string ErrStr;
+
+  // QK_Match
+  llvm::Optional<clang::ast_matchers::dynamic::DynTypedMatcher> Matcher;
+
+  // QK_Set
+  QueryVariable Var;
+
+  OutputKind OutKind;
+  bool BindRoot;
+};
+
+} // namespace query
+} // namespace clang
+
+#endif
Index: clang-query/QueryEngine.cpp
===================================================================
--- /dev/null
+++ clang-query/QueryEngine.cpp
@@ -0,0 +1,126 @@
+//===---- QueryEngine.cpp - clang-query engine ----------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "QueryEngine.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;
+
+namespace clang {
+namespace query {
+
+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);
+  }
+};
+
+}
+
+void QueryEngine::ActOnQuery(const Query &Q) {
+  switch (Q.Kind) {
+  case QK_Invalid:
+    Out << Q.ErrStr << "\n";
+    break;
+
+  case QK_NoOp:
+    break;
+
+  case QK_Match: {
+    unsigned MatchCount = 0;
+
+    for (llvm::ArrayRef<ASTUnit *>::iterator I = ASTs.begin(), E = ASTs.end();
+         I != E; ++I) {
+      ASTUnit *AST = *I;
+      MatchFinder Finder;
+      std::vector<BoundNodes> Matches;
+      CollectBoundNodes Collect(Matches);
+      if (Q.Matcher->canConvertTo<Decl>()) {
+        DeclarationMatcher M = Q.Matcher->convertTo<Decl>();
+        if (BindRoot)
+          M = decl(M).bind("root");
+        Finder.addMatcher(decl(forEachDescendant(M)), &Collect);
+      } else if (Q.Matcher->canConvertTo<Stmt>()) {
+        StatementMatcher M = Q.Matcher->convertTo<Stmt>();
+        if (BindRoot)
+          M = stmt(M).bind("root");
+        Finder.addMatcher(decl(forEachDescendant(M)), &Collect);
+      }
+      Finder.match(*AST->getASTContext().getTranslationUnitDecl(),
+                   AST->getASTContext());
+
+      for (std::vector<BoundNodes>::iterator MI = Matches.begin(),
+                                             ME = Matches.end();
+           MI != ME; ++MI) {
+        Out << "\nMatch #" << ++MatchCount << ":\n\n";
+
+        for (BoundNodes::IDToNodeMap::const_iterator BI = MI->getMap().begin(),
+                                                     BE = MI->getMap().end();
+             BI != BE; ++BI) {
+          switch (OutKind) {
+          case OK_Diag: {
+            clang::SourceRange R = BI->second.getSourceRange();
+            if (R.isValid()) {
+              TextDiagnostic TD(Out, 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: {
+            Out << "Binding for \"" << BI->first << "\":\n";
+            BI->second.print(Out, AST->getASTContext().getPrintingPolicy());
+            Out << "\n";
+            break;
+          }
+          case OK_Dump: {
+            Out << "Binding for \"" << BI->first << "\":\n";
+            BI->second.dump(Out, AST->getSourceManager());
+            Out << "\n";
+            break;
+          }
+          }
+        }
+
+        if (MI->getMap().empty())
+          Out << "No bindings.\n";
+      }
+    }
+
+    Out << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");
+    break;
+  }
+
+  case QK_Set:
+    switch (Q.Var) {
+    case QV_Output:
+      OutKind = Q.OutKind;
+      break;
+
+    case QV_BindRoot:
+      BindRoot = Q.BindRoot;
+      break;
+    }
+    break;
+  }
+}
+
+} // namespace query
+} // namespace clang
Index: clang-query/QueryEngine.h
===================================================================
--- /dev/null
+++ clang-query/QueryEngine.h
@@ -0,0 +1,43 @@
+//===--- QueryEngine.h - clang-query ----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_ENGINE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_ENGINE_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "Query.h"
+
+namespace llvm {
+
+class raw_ostream;
+
+}
+
+namespace clang {
+
+class ASTUnit;
+
+namespace query {
+
+class QueryEngine {
+  llvm::raw_ostream &Out;
+  llvm::ArrayRef<ASTUnit *> ASTs;
+  OutputKind OutKind;
+  bool BindRoot;
+
+public:
+  QueryEngine(llvm::raw_ostream &Out, llvm::ArrayRef<ASTUnit *> ASTs)
+      : Out(Out), ASTs(ASTs), OutKind(OK_Diag), BindRoot(true) {}
+  void ActOnQuery(const Query &Q);
+};
+
+} // namespace query
+} // namespace clang
+
+#endif
Index: clang-query/QueryParser.cpp
===================================================================
--- /dev/null
+++ clang-query/QueryParser.cpp
@@ -0,0 +1,154 @@
+//===---- QueryParser.cpp - clang-query command parser --------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "QueryParser.h"
+#include "Query.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "clang/ASTMatchers/Dynamic/Parser.h"
+
+using namespace llvm;
+using namespace clang::ast_matchers::dynamic;
+
+namespace clang {
+namespace query {
+
+static bool IsWhitespace(char C) {
+  return C == ' ' || C == '\t' || C == '\n' || C == '\r';
+}
+
+// Lex any amount of whitespace followed by a "word" (any sequence of
+// non-whitespace characters).  If no word is found before the end of input,
+// return StringRef().
+static StringRef LexWord(const char *&Line) {
+  while (true) {
+    if (*Line == '\0')
+      return StringRef();
+
+    if (!IsWhitespace(*Line))
+      break;
+
+    ++Line;
+  }
+
+  const char *Start = Line;
+  ++Line;
+
+  while (true) {
+    if (*Line == '\0' || IsWhitespace(*Line))
+      return StringRef(Start, Line-Start);
+
+    ++Line;
+  }
+}
+
+Query ParseQuery(const char *Line) {
+  Query Q;
+  StringRef CommandStr = LexWord(Line);
+  Q.Kind = StringSwitch<QueryKind>(CommandStr)
+      .Case("", QK_NoOp)
+      .Case("m", QK_Match)
+      .Case("match", QK_Match)
+      .Case("set", QK_Set)
+      .Default(QK_Invalid);
+
+  switch (Q.Kind) {
+  case QK_NoOp:
+    break;
+
+  case QK_Match: {
+    Diagnostics Diag;
+    Optional<DynTypedMatcher> Matcher =
+        Parser::parseMatcherExpression(Line, &Diag);
+    if (!Matcher) {
+      llvm::raw_string_ostream OS(Q.ErrStr);
+      Diag.printToStreamFull(OS);
+      Q.Kind = QK_Invalid;
+    }
+    Q.Matcher = *Matcher;
+    break;
+  }
+
+  case QK_Set: {
+    StringRef VarStr = LexWord(Line);
+    if (VarStr.empty()) {
+      Q.ErrStr = "expected variable name";
+      Q.Kind = QK_Invalid;
+      return Q;
+    }
+    unsigned Var = StringSwitch<unsigned>(VarStr)
+                .Case("output", QV_Output)
+                .Case("bind-root", QV_BindRoot)
+                .Default(~0u);
+    if (Var == ~0u) {
+      Q.ErrStr = ("unknown variable: '" + VarStr + "'").str();
+      Q.Kind = QK_Invalid;
+      return Q;
+    }
+    Q.Var = QueryVariable(Var);
+
+    StringRef ValStr = LexWord(Line);
+    if (ValStr.empty()) {
+      Q.ErrStr = "expected variable value";
+      Q.Kind = QK_Invalid;
+      return Q;
+    }
+
+    switch (Q.Var) {
+    case QV_Output: {
+      unsigned OutKind = StringSwitch<unsigned>(ValStr)
+                             .Case("diag", OK_Diag)
+                             .Case("print", OK_Print)
+                             .Case("dump", OK_Dump)
+                             .Default(~0u);
+      if (OutKind == ~0u) {
+        Q.ErrStr =
+            ("expected 'diag', 'print' or 'dump', got '" + ValStr + "'").str();
+        Q.Kind = QK_Invalid;
+        return Q;
+      }
+      Q.OutKind = OutputKind(OutKind);
+      break;
+    }
+    case QV_BindRoot: {
+      unsigned BindRoot = StringSwitch<unsigned>(ValStr)
+                              .Case("false", 0)
+                              .Case("true", 1)
+                              .Default(~0u);
+      if (BindRoot == ~0u) {
+        Q.ErrStr = ("expected 'true' or 'false', got '" + ValStr + "'").str();
+        Q.Kind = QK_Invalid;
+        return Q;
+      }
+      Q.BindRoot = BindRoot;
+      break;
+    }
+    }
+
+    const char *End = Line;
+    if (!LexWord(Line).empty()) {
+      Q.ErrStr = std::string("unexpected extra input: '") + End + "'";
+      Q.Kind = QK_Invalid;
+      return Q;
+    }
+
+    break;
+  }
+
+  case QK_Invalid: {
+    Q.ErrStr = ("unknown command: " + CommandStr).str();
+    break;
+  }
+  }
+
+  return Q;
+}
+
+} // namespace query
+} // namespace clang
Index: clang-query/QueryParser.h
===================================================================
--- /dev/null
+++ clang-query/QueryParser.h
@@ -0,0 +1,23 @@
+//===--- QueryParser.h - clang-query ----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_PARSER_H
+
+namespace clang {
+namespace query {
+
+struct Query;
+
+Query ParseQuery(const char *Line);
+
+} // namespace query
+} // namespace clang
+
+#endif
Index: clang-query/tool/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang-query/tool/CMakeLists.txt
@@ -0,0 +1,11 @@
+if(HAVE_LIBEDIT)
+  include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
+
+  add_clang_executable(clang-query ClangQuery.cpp)
+  target_link_libraries(clang-query
+    ${LIBEDIT}
+    clangFrontend
+    clangQuery
+    clangTooling
+    )
+endif()
Index: clang-query/tool/ClangQuery.cpp
===================================================================
--- /dev/null
+++ clang-query/tool/ClangQuery.cpp
@@ -0,0 +1,117 @@
+//===---- ClangQuery.cpp - clang-query tool -------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+
+#include "QueryEngine.h"
+#include "QueryParser.h"
+
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/OwningPtr.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Signals.h"
+
+#include <histedit.h>
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::dynamic;
+using namespace clang::query;
+using namespace clang::tooling;
+using namespace llvm;
+
+// Set up the command line options
+cl::opt<std::string> BuildPath(
+  cl::Positional,
+  cl::desc("<build-path>"));
+
+cl::list<std::string> SourcePaths(
+  cl::Positional,
+  cl::desc("<source0> [... <sourceN>]"),
+  cl::OneOrMore);
+
+static char *ReturnPrompt(EditLine *EL) {
+  static char Prompt[] = "clang-query> ";
+  return Prompt;
+}
+
+int main(int argc, const char **argv) {
+  llvm::sys::PrintStackTraceOnErrorSignal();
+  llvm::OwningPtr<CompilationDatabase> Compilations(
+        FixedCompilationDatabase::loadFromCommandLine(argc, argv));
+  cl::ParseCommandLineOptions(argc, argv);
+  if (!Compilations) {  // Couldn't find a compilation DB from the command line
+    std::string ErrorMessage;
+    Compilations.reset(
+      !BuildPath.empty() ?
+        CompilationDatabase::autoDetectFromDirectory(BuildPath, ErrorMessage) :
+        CompilationDatabase::autoDetectFromSource(SourcePaths[0], ErrorMessage)
+      );
+
+    // Still no compilation DB? - bail.
+    if (!Compilations)
+      llvm::report_fatal_error(ErrorMessage);
+  }
+
+  ClangTool Tool(*Compilations, SourcePaths);
+  std::vector<ASTUnit *> ASTs;
+  if (Tool.buildASTs(ASTs) != 0)
+    return 1;
+
+  QueryEngine QE(llvm::outs(), ASTs);
+
+  History *Hist = history_init();
+  HistEvent Event;
+  history(Hist, &Event, H_SETSIZE, 100);
+
+  EditLine *EL = el_init("clang-query", stdin, stdout, stderr);
+  el_set(EL, EL_PROMPT, ReturnPrompt);
+  el_set(EL, EL_EDITOR, "emacs");
+  el_set(EL, EL_HIST, history, Hist);
+
+  int Count;
+  while (const char *Line = el_gets(EL, &Count)) {
+    if (Count == 0)
+      break;
+
+    history(Hist, &Event, H_ENTER, Line);
+
+    Query Q = ParseQuery(Line);
+    QE.ActOnQuery(Q);
+  }
+
+  llvm::DeleteContainerPointers(ASTs);
+
+  history_end(Hist);
+  el_end(EL);
+
+  llvm::outs() << "\n";
+
+  return 0;
+}
Index: test/CMakeLists.txt
===================================================================
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -38,6 +38,10 @@
   ExtraToolsUnitTests
   )
 
+if(HAVE_LIBEDIT)
+  list(APPEND CLANG_TOOLS_TEST_DEPS clang-query)
+endif()
+
 add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests"
   ${CMAKE_CURRENT_BINARY_DIR}
   DEPENDS ${CLANG_TOOLS_TEST_DEPS}
Index: test/clang-query/function-decl.c
===================================================================
--- /dev/null
+++ test/clang-query/function-decl.c
@@ -0,0 +1,5 @@
+// RUN: echo 'match functionDecl(hasName("foo"))' | clang-query %s -- | FileCheck %s
+// REQUIRES: libedit
+
+// CHECK: function-decl.c:5:1: note: "root" binds here
+void foo(void) {}
Index: test/lit.cfg
===================================================================
--- test/lit.cfg
+++ test/lit.cfg
@@ -229,3 +229,6 @@
 # ANSI escape sequences in non-dumb terminal
 if platform.system() not in ['Windows']:
     config.available_features.add('ansi-escape-sequences')
+
+if config.have_libedit == "1":
+  config.available_features.add('libedit')
Index: test/lit.site.cfg.in
===================================================================
--- test/lit.site.cfg.in
+++ test/lit.site.cfg.in
@@ -7,6 +7,7 @@
 config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
 config.clang_tools_binary_dir = "@CLANG_TOOLS_BINARY_DIR@"
 config.target_triple = "@TARGET_TRIPLE@"
+config.have_libedit = "@HAVE_LIBEDIT@"
 
 # Support substitution of the tools and libs dirs with user parameters. This is
 # used when we can't determine the tool dir at configuration time.
Index: unittests/CMakeLists.txt
===================================================================
--- unittests/CMakeLists.txt
+++ unittests/CMakeLists.txt
@@ -7,4 +7,5 @@
 
 add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-modernize)
+add_subdirectory(clang-query)
 add_subdirectory(clang-tidy)
Index: unittests/clang-query/CMakeLists.txt
===================================================================
--- /dev/null
+++ unittests/clang-query/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+include_directories(
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-query 
+  )
+
+add_extra_unittest(ClangQueryTests
+  QueryEngineTest.cpp
+  QueryParserTest.cpp
+  )
+
+target_link_libraries(ClangQueryTests
+  clangASTMatchers
+  clangQuery
+  clangTooling
+  )
Index: unittests/clang-query/Makefile
===================================================================
--- /dev/null
+++ unittests/clang-query/Makefile
@@ -0,0 +1,24 @@
+##===- unittests/clang-query/Makefile ----------------------*- Makefile -*-===##
+#
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL = ../../../..
+include $(CLANG_LEVEL)/../../Makefile.config
+
+TESTNAME = ClangQuery
+LINK_COMPONENTS := asmparser bitreader support MC MCParser option \
+		 TransformUtils
+USEDLIBS = clangQuery.a clangTooling.a clangFrontend.a clangSerialization.a \
+	   clangDriver.a clangParse.a clangSema.a clangEdit.a clangAnalysis.a \
+	   clangAST.a clangASTMatchers.a clangDynamicASTMatchers.a clangLex.a \
+	   clangBasic.a
+
+include $(CLANG_LEVEL)/Makefile
+MAKEFILE_UNITTEST_NO_INCLUDE_COMMON := 1
+CPP.Flags += -I$(PROJ_SRC_DIR)/../../clang-query
+include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
Index: unittests/clang-query/QueryEngineTest.cpp
===================================================================
--- /dev/null
+++ unittests/clang-query/QueryEngineTest.cpp
@@ -0,0 +1,104 @@
+//===---- QueryEngineTest.cpp - clang-query test --------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "QueryEngine.h"
+#include "Query.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+#include <string>
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::query;
+using namespace clang::tooling;
+
+TEST(QueryEngine, Basic) {
+  OwningPtr<ASTUnit> FooAST(
+      buildASTFromCode("void foo1(void) {}\nvoid foo2(void) {}", "foo.cc"));
+  ASSERT_TRUE(FooAST.get());
+  OwningPtr<ASTUnit> BarAST(
+      buildASTFromCode("void bar1(void) {}\nvoid bar2(void) {}", "bar.cc"));
+  ASSERT_TRUE(BarAST.get());
+
+  ASTUnit *ASTs[] = { FooAST.get(), BarAST.get() };
+
+  std::string Str;
+  llvm::raw_string_ostream OS(Str);
+  QueryEngine E(OS, ASTs);
+
+  DeclarationMatcher FnMatcher = functionDecl();
+  DeclarationMatcher FooMatcher = functionDecl(hasName("foo1"));
+  Query Q;
+  Q.Kind = QK_Match;
+  Q.Matcher = FnMatcher;
+  E.ActOnQuery(Q);
+
+  EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("foo.cc:2:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("bar.cc:1:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("bar.cc:2:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("4 matches.") != std::string::npos);
+
+  Str.clear();
+
+  Q.Matcher = FooMatcher;
+  E.ActOnQuery(Q);
+
+  EXPECT_TRUE(OS.str().find("foo.cc:1:1: note: \"root\" binds here") !=
+              std::string::npos);
+  EXPECT_TRUE(OS.str().find("1 match.") != std::string::npos);
+
+  Str.clear();
+
+  Q.Kind = QK_Set;
+  Q.Var = QV_Output;
+  Q.OutKind = OK_Print;
+  E.ActOnQuery(Q);
+
+  Q.Kind = QK_Match;
+  Q.Matcher = FooMatcher;
+  E.ActOnQuery(Q);
+
+  EXPECT_TRUE(OS.str().find("Binding for \"root\":\nvoid foo1()") !=
+              std::string::npos);
+
+  Str.clear();
+
+  Q.Kind = QK_Set;
+  Q.Var = QV_Output;
+  Q.OutKind = OK_Dump;
+  E.ActOnQuery(Q);
+
+  Q.Kind = QK_Match;
+  Q.Matcher = FooMatcher;
+  E.ActOnQuery(Q);
+
+  EXPECT_TRUE(OS.str().find("FunctionDecl") != std::string::npos);
+
+  Str.clear();
+
+  Q.Kind = QK_Set;
+  Q.Var = QV_BindRoot;
+  Q.BindRoot = false;
+  E.ActOnQuery(Q);
+
+  Q.Kind = QK_Match;
+  Q.Matcher = FooMatcher;
+  E.ActOnQuery(Q);
+
+  EXPECT_TRUE(OS.str().find("No bindings.") != std::string::npos);
+}
Index: unittests/clang-query/QueryParserTest.cpp
===================================================================
--- /dev/null
+++ unittests/clang-query/QueryParserTest.cpp
@@ -0,0 +1,69 @@
+//===---- QueryParserTest.cpp - clang-query test --------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "QueryParser.h"
+#include "Query.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::query;
+
+TEST(QueryParser, NoOp) {
+  Query Q = ParseQuery("");
+  EXPECT_EQ(QK_NoOp, Q.Kind);
+
+  Q = ParseQuery("\n");
+  EXPECT_EQ(QK_NoOp, Q.Kind);
+}
+
+TEST(QueryParser, Set) {
+  Query Q = ParseQuery("set");
+  EXPECT_EQ(QK_Invalid, Q.Kind);
+  EXPECT_EQ("expected variable name", Q.ErrStr);
+
+  Q = ParseQuery("set foo bar");
+  EXPECT_EQ(QK_Invalid, Q.Kind);
+  EXPECT_EQ("unknown variable: 'foo'", Q.ErrStr);
+
+  Q = ParseQuery("set output");
+  EXPECT_EQ(QK_Invalid, Q.Kind);
+  EXPECT_EQ("expected variable value", Q.ErrStr);
+
+  Q = ParseQuery("set bind-root true foo");
+  EXPECT_EQ(QK_Invalid, Q.Kind);
+  EXPECT_EQ("unexpected extra input: ' foo'", Q.ErrStr);
+
+  Q = ParseQuery("set output foo");
+  EXPECT_EQ(QK_Invalid, Q.Kind);
+  EXPECT_EQ("expected 'diag', 'print' or 'dump', got 'foo'", Q.ErrStr);
+
+  Q = ParseQuery("set output dump");
+  EXPECT_EQ(QK_Set, Q.Kind);
+  EXPECT_EQ(QV_Output, Q.Var);
+  EXPECT_EQ(OK_Dump, Q.OutKind);
+
+  Q = ParseQuery("set bind-root foo");
+  EXPECT_EQ(QK_Invalid, Q.Kind);
+  EXPECT_EQ("expected 'true' or 'false', got 'foo'", Q.ErrStr);
+
+  Q = ParseQuery("set bind-root true");
+  EXPECT_EQ(QK_Set, Q.Kind);
+  EXPECT_EQ(QV_BindRoot, Q.Var);
+  EXPECT_EQ(true, Q.BindRoot);
+}
+
+TEST(QueryParser, Match) {
+  Query Q = ParseQuery("match decl()");
+  ASSERT_EQ(QK_Match, Q.Kind);
+  EXPECT_TRUE(Q.Matcher->canConvertTo<Decl>());
+
+  Q = ParseQuery("m stmt()");
+  ASSERT_EQ(QK_Match, Q.Kind);
+  EXPECT_TRUE(Q.Matcher->canConvertTo<Stmt>());
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to