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