mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-03-04 08:27:50 +00:00
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
This commit is contained in:
parent
6408301421
commit
8b1265b353
@ -1,5 +1,8 @@
|
|||||||
|
check_library_exists(edit el_init "" HAVE_LIBEDIT)
|
||||||
|
|
||||||
add_subdirectory(clang-apply-replacements)
|
add_subdirectory(clang-apply-replacements)
|
||||||
add_subdirectory(clang-modernize)
|
add_subdirectory(clang-modernize)
|
||||||
|
add_subdirectory(clang-query)
|
||||||
add_subdirectory(clang-tidy)
|
add_subdirectory(clang-tidy)
|
||||||
add_subdirectory(modularize)
|
add_subdirectory(modularize)
|
||||||
add_subdirectory(pp-trace)
|
add_subdirectory(pp-trace)
|
||||||
|
@ -12,7 +12,8 @@ CLANG_LEVEL := ../..
|
|||||||
include $(CLANG_LEVEL)/../../Makefile.config
|
include $(CLANG_LEVEL)/../../Makefile.config
|
||||||
|
|
||||||
PARALLEL_DIRS := remove-cstr-calls tool-template modularize pp-trace
|
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
|
include $(CLANG_LEVEL)/Makefile
|
||||||
|
|
||||||
|
13
clang-tools-extra/clang-query/CMakeLists.txt
Normal file
13
clang-tools-extra/clang-query/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
add_clang_library(clangQuery
|
||||||
|
Query.cpp
|
||||||
|
QueryParser.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(clangQuery
|
||||||
|
clangAST
|
||||||
|
clangASTMatchers
|
||||||
|
clangBasic
|
||||||
|
clangDynamicASTMatchers
|
||||||
|
clangFrontend
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(tool)
|
14
clang-tools-extra/clang-query/Makefile
Normal file
14
clang-tools-extra/clang-query/Makefile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
##===- tools/extra/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 := ../../..
|
||||||
|
LIBRARYNAME := clangQuery
|
||||||
|
include $(CLANG_LEVEL)/../../Makefile.config
|
||||||
|
|
||||||
|
include $(CLANG_LEVEL)/Makefile
|
131
clang-tools-extra/clang-query/Query.cpp
Normal file
131
clang-tools-extra/clang-query/Query.cpp
Normal file
@ -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
|
119
clang-tools-extra/clang-query/Query.h
Normal file
119
clang-tools-extra/clang-query/Query.h
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
//===--- 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/IntrusiveRefCntPtr.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_Help,
|
||||||
|
QK_Match,
|
||||||
|
QK_SetBool,
|
||||||
|
QK_SetOutputKind
|
||||||
|
};
|
||||||
|
|
||||||
|
class QuerySession;
|
||||||
|
|
||||||
|
struct Query : llvm::RefCountedBase<Query> {
|
||||||
|
Query(QueryKind Kind) : Kind(Kind) {}
|
||||||
|
virtual ~Query();
|
||||||
|
|
||||||
|
/// Perform the query on \p QS and print output to \p OS.
|
||||||
|
///
|
||||||
|
/// \return false if an error occurs, otherwise return true.
|
||||||
|
virtual bool run(llvm::raw_ostream &OS, QuerySession &QS) const = 0;
|
||||||
|
|
||||||
|
const QueryKind Kind;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef llvm::IntrusiveRefCntPtr<Query> QueryRef;
|
||||||
|
|
||||||
|
/// Any query which resulted in a parse error. The error message is in ErrStr.
|
||||||
|
struct InvalidQuery : Query {
|
||||||
|
InvalidQuery(const Twine &ErrStr) : Query(QK_Invalid), ErrStr(ErrStr.str()) {}
|
||||||
|
bool run(llvm::raw_ostream &OS, QuerySession &QS) const LLVM_OVERRIDE;
|
||||||
|
|
||||||
|
std::string ErrStr;
|
||||||
|
|
||||||
|
static bool classof(const Query *Q) { return Q->Kind == QK_Invalid; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// No-op query (i.e. a blank line).
|
||||||
|
struct NoOpQuery : Query {
|
||||||
|
NoOpQuery() : Query(QK_NoOp) {}
|
||||||
|
bool run(llvm::raw_ostream &OS, QuerySession &QS) const LLVM_OVERRIDE;
|
||||||
|
|
||||||
|
static bool classof(const Query *Q) { return Q->Kind == QK_NoOp; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Query for "help".
|
||||||
|
struct HelpQuery : Query {
|
||||||
|
HelpQuery() : Query(QK_Help) {}
|
||||||
|
bool run(llvm::raw_ostream &OS, QuerySession &QS) const LLVM_OVERRIDE;
|
||||||
|
|
||||||
|
static bool classof(const Query *Q) { return Q->Kind == QK_Help; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Query for "match MATCHER".
|
||||||
|
struct MatchQuery : Query {
|
||||||
|
MatchQuery(const ast_matchers::dynamic::DynTypedMatcher &Matcher)
|
||||||
|
: Query(QK_Match), Matcher(Matcher) {}
|
||||||
|
bool run(llvm::raw_ostream &OS, QuerySession &QS) const LLVM_OVERRIDE;
|
||||||
|
|
||||||
|
ast_matchers::dynamic::DynTypedMatcher Matcher;
|
||||||
|
|
||||||
|
static bool classof(const Query *Q) { return Q->Kind == QK_Match; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct SetQueryKind {};
|
||||||
|
|
||||||
|
template <> struct SetQueryKind<bool> {
|
||||||
|
static const QueryKind value = QK_SetBool;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct SetQueryKind<OutputKind> {
|
||||||
|
static const QueryKind value = QK_SetOutputKind;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Query for "set VAR VALUE".
|
||||||
|
template <typename T> struct SetQuery : Query {
|
||||||
|
SetQuery(T QuerySession::*Var, T Value)
|
||||||
|
: Query(SetQueryKind<T>::value), Var(Var), Value(Value) {}
|
||||||
|
bool run(llvm::raw_ostream &OS, QuerySession &QS) const LLVM_OVERRIDE {
|
||||||
|
QS.*Var = Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool classof(const Query *Q) {
|
||||||
|
return Q->Kind == SetQueryKind<T>::value;
|
||||||
|
}
|
||||||
|
|
||||||
|
T QuerySession::*Var;
|
||||||
|
T Value;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace query
|
||||||
|
} // namespace clang
|
||||||
|
|
||||||
|
#endif
|
165
clang-tools-extra/clang-query/QueryParser.cpp
Normal file
165
clang-tools-extra/clang-query/QueryParser.cpp
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
//===---- 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 "QuerySession.h"
|
||||||
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
#include "llvm/ADT/StringSwitch.h"
|
||||||
|
#include "clang/ASTMatchers/Dynamic/Parser.h"
|
||||||
|
#include "clang/Basic/CharInfo.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
using namespace clang::ast_matchers::dynamic;
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace query {
|
||||||
|
|
||||||
|
// Lex any amount of whitespace followed by a "word" (any sequence of
|
||||||
|
// non-whitespace characters) from the start of region [Begin,End). If no word
|
||||||
|
// is found before End, return StringRef(). Begin is adjusted to exclude the
|
||||||
|
// lexed region.
|
||||||
|
static StringRef LexWord(const char *&Begin, const char *End) {
|
||||||
|
while (true) {
|
||||||
|
if (Begin == End)
|
||||||
|
return StringRef();
|
||||||
|
|
||||||
|
if (!isWhitespace(*Begin))
|
||||||
|
break;
|
||||||
|
|
||||||
|
++Begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *WordBegin = Begin;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
++Begin;
|
||||||
|
|
||||||
|
if (Begin == End || isWhitespace(*Begin))
|
||||||
|
return StringRef(WordBegin, Begin - WordBegin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QueryRef ParseSetBool(bool QuerySession::*Var, StringRef ValStr) {
|
||||||
|
unsigned Value = StringSwitch<unsigned>(ValStr)
|
||||||
|
.Case("false", 0)
|
||||||
|
.Case("true", 1)
|
||||||
|
.Default(~0u);
|
||||||
|
if (Value == ~0u) {
|
||||||
|
return new InvalidQuery("expected 'true' or 'false', got '" + ValStr + "'");
|
||||||
|
}
|
||||||
|
return new SetQuery<bool>(Var, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QueryRef ParseSetOutputKind(StringRef ValStr) {
|
||||||
|
unsigned OutKind = StringSwitch<unsigned>(ValStr)
|
||||||
|
.Case("diag", OK_Diag)
|
||||||
|
.Case("print", OK_Print)
|
||||||
|
.Case("dump", OK_Dump)
|
||||||
|
.Default(~0u);
|
||||||
|
if (OutKind == ~0u) {
|
||||||
|
return new InvalidQuery("expected 'diag', 'print' or 'dump', got '" +
|
||||||
|
ValStr + "'");
|
||||||
|
}
|
||||||
|
return new SetQuery<OutputKind>(&QuerySession::OutKind, OutputKind(OutKind));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QueryRef EndQuery(const char *Begin, const char *End, QueryRef Q) {
|
||||||
|
const char *Extra = Begin;
|
||||||
|
if (!LexWord(Begin, End).empty())
|
||||||
|
return new InvalidQuery("unexpected extra input: '" +
|
||||||
|
StringRef(Extra, End - Extra) + "'");
|
||||||
|
return Q;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ParsedQueryKind {
|
||||||
|
PQK_Invalid,
|
||||||
|
PQK_NoOp,
|
||||||
|
PQK_Help,
|
||||||
|
PQK_Match,
|
||||||
|
PQK_Set
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ParsedQueryVariable {
|
||||||
|
PQV_Invalid,
|
||||||
|
PQV_Output,
|
||||||
|
PQV_BindRoot
|
||||||
|
};
|
||||||
|
|
||||||
|
QueryRef ParseQuery(StringRef Line) {
|
||||||
|
const char *Begin = Line.data();
|
||||||
|
const char *End = Line.data() + Line.size();
|
||||||
|
|
||||||
|
StringRef CommandStr = LexWord(Begin, End);
|
||||||
|
ParsedQueryKind QKind = StringSwitch<ParsedQueryKind>(CommandStr)
|
||||||
|
.Case("", PQK_NoOp)
|
||||||
|
.Case("help", PQK_Help)
|
||||||
|
.Case("m", PQK_Match)
|
||||||
|
.Case("match", PQK_Match)
|
||||||
|
.Case("set", PQK_Set)
|
||||||
|
.Default(PQK_Invalid);
|
||||||
|
|
||||||
|
switch (QKind) {
|
||||||
|
case PQK_NoOp:
|
||||||
|
return new NoOpQuery;
|
||||||
|
|
||||||
|
case PQK_Help:
|
||||||
|
return EndQuery(Begin, End, new HelpQuery);
|
||||||
|
|
||||||
|
case PQK_Match: {
|
||||||
|
Diagnostics Diag;
|
||||||
|
Optional<DynTypedMatcher> Matcher =
|
||||||
|
Parser::parseMatcherExpression(StringRef(Begin, End - Begin), &Diag);
|
||||||
|
if (!Matcher) {
|
||||||
|
std::string ErrStr;
|
||||||
|
llvm::raw_string_ostream OS(ErrStr);
|
||||||
|
Diag.printToStreamFull(OS);
|
||||||
|
return new InvalidQuery(OS.str());
|
||||||
|
}
|
||||||
|
return new MatchQuery(*Matcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
case PQK_Set: {
|
||||||
|
StringRef VarStr = LexWord(Begin, End);
|
||||||
|
if (VarStr.empty())
|
||||||
|
return new InvalidQuery("expected variable name");
|
||||||
|
|
||||||
|
ParsedQueryVariable Var = StringSwitch<ParsedQueryVariable>(VarStr)
|
||||||
|
.Case("output", PQV_Output)
|
||||||
|
.Case("bind-root", PQV_BindRoot)
|
||||||
|
.Default(PQV_Invalid);
|
||||||
|
if (Var == PQV_Invalid)
|
||||||
|
return new InvalidQuery("unknown variable: '" + VarStr + "'");
|
||||||
|
|
||||||
|
StringRef ValStr = LexWord(Begin, End);
|
||||||
|
if (ValStr.empty())
|
||||||
|
return new InvalidQuery("expected variable value");
|
||||||
|
|
||||||
|
QueryRef Q;
|
||||||
|
switch (Var) {
|
||||||
|
case PQV_Output:
|
||||||
|
Q = ParseSetOutputKind(ValStr);
|
||||||
|
break;
|
||||||
|
case PQV_BindRoot:
|
||||||
|
Q = ParseSetBool(&QuerySession::BindRoot, ValStr);
|
||||||
|
break;
|
||||||
|
case PQV_Invalid:
|
||||||
|
llvm_unreachable("Invalid query kind");
|
||||||
|
}
|
||||||
|
|
||||||
|
return EndQuery(Begin, End, Q);
|
||||||
|
}
|
||||||
|
|
||||||
|
case PQK_Invalid:
|
||||||
|
return new InvalidQuery("unknown command: " + CommandStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace query
|
||||||
|
} // namespace clang
|
27
clang-tools-extra/clang-query/QueryParser.h
Normal file
27
clang-tools-extra/clang-query/QueryParser.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
//===--- 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
|
||||||
|
|
||||||
|
#include "Query.h"
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
namespace query {
|
||||||
|
|
||||||
|
/// \brief Parse \p Line.
|
||||||
|
///
|
||||||
|
/// \return A reference to the parsed query object, which may be an
|
||||||
|
/// \c InvalidQuery if a parse error occurs.
|
||||||
|
QueryRef ParseQuery(StringRef Line);
|
||||||
|
|
||||||
|
} // namespace query
|
||||||
|
} // namespace clang
|
||||||
|
|
||||||
|
#endif
|
36
clang-tools-extra/clang-query/QuerySession.h
Normal file
36
clang-tools-extra/clang-query/QuerySession.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//===--- QuerySession.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_SESSION_H
|
||||||
|
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_QUERY_QUERY_SESSION_H
|
||||||
|
|
||||||
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
|
#include "Query.h"
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
|
||||||
|
class ASTUnit;
|
||||||
|
|
||||||
|
namespace query {
|
||||||
|
|
||||||
|
/// Represents the state for a particular clang-query session.
|
||||||
|
class QuerySession {
|
||||||
|
public:
|
||||||
|
QuerySession(llvm::ArrayRef<ASTUnit *> ASTs)
|
||||||
|
: ASTs(ASTs), OutKind(OK_Diag), BindRoot(true) {}
|
||||||
|
|
||||||
|
llvm::ArrayRef<ASTUnit *> ASTs;
|
||||||
|
OutputKind OutKind;
|
||||||
|
bool BindRoot;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace query
|
||||||
|
} // namespace clang
|
||||||
|
|
||||||
|
#endif
|
11
clang-tools-extra/clang-query/tool/CMakeLists.txt
Normal file
11
clang-tools-extra/clang-query/tool/CMakeLists.txt
Normal file
@ -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
|
||||||
|
edit
|
||||||
|
clangFrontend
|
||||||
|
clangQuery
|
||||||
|
clangTooling
|
||||||
|
)
|
||||||
|
endif()
|
158
clang-tools-extra/clang-query/tool/ClangQuery.cpp
Normal file
158
clang-tools-extra/clang-query/tool/ClangQuery.cpp
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
//===---- 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 "Query.h"
|
||||||
|
#include "QuerySession.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 <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
static cl::opt<std::string> BuildPath("b", cl::desc("Specify build path"),
|
||||||
|
cl::value_desc("<path>"));
|
||||||
|
|
||||||
|
static cl::list<std::string> Commands("c", cl::desc("Specify command to run"),
|
||||||
|
cl::value_desc("<command>"));
|
||||||
|
|
||||||
|
static cl::list<std::string> CommandFiles("f",
|
||||||
|
cl::desc("Read commands from file"),
|
||||||
|
cl::value_desc("<file>"));
|
||||||
|
|
||||||
|
static 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();
|
||||||
|
cl::ParseCommandLineOptions(argc, argv);
|
||||||
|
|
||||||
|
if (!Commands.empty() && !CommandFiles.empty()) {
|
||||||
|
llvm::errs() << argv[0] << ": cannot specify both -c and -f\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::OwningPtr<CompilationDatabase> Compilations(
|
||||||
|
FixedCompilationDatabase::loadFromCommandLine(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;
|
||||||
|
|
||||||
|
QuerySession QS(ASTs);
|
||||||
|
|
||||||
|
if (!Commands.empty()) {
|
||||||
|
for (cl::list<std::string>::iterator I = Commands.begin(),
|
||||||
|
E = Commands.end();
|
||||||
|
I != E; ++I) {
|
||||||
|
QueryRef Q = ParseQuery(I->c_str());
|
||||||
|
if (!Q->run(llvm::outs(), QS))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else if (!CommandFiles.empty()) {
|
||||||
|
for (cl::list<std::string>::iterator I = CommandFiles.begin(),
|
||||||
|
E = CommandFiles.end();
|
||||||
|
I != E; ++I) {
|
||||||
|
std::ifstream Input(I->c_str());
|
||||||
|
if (!Input.is_open()) {
|
||||||
|
llvm::errs() << argv[0] << ": cannot open " << *I << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
while (Input.good()) {
|
||||||
|
std::string Line;
|
||||||
|
std::getline(Input, Line);
|
||||||
|
|
||||||
|
QueryRef Q = ParseQuery(Line.c_str());
|
||||||
|
if (!Q->run(llvm::outs(), QS))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
|
||||||
|
QueryRef Q = ParseQuery(Line);
|
||||||
|
Q->run(llvm::outs(), QS);
|
||||||
|
}
|
||||||
|
|
||||||
|
history_end(Hist);
|
||||||
|
el_end(EL);
|
||||||
|
|
||||||
|
llvm::outs() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::DeleteContainerPointers(ASTs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -38,6 +38,10 @@ set(CLANG_TOOLS_TEST_DEPS
|
|||||||
ExtraToolsUnitTests
|
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"
|
add_lit_testsuite(check-clang-tools "Running the Clang extra tools' regression tests"
|
||||||
${CMAKE_CURRENT_BINARY_DIR}
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
DEPENDS ${CLANG_TOOLS_TEST_DEPS}
|
DEPENDS ${CLANG_TOOLS_TEST_DEPS}
|
||||||
|
2
clang-tools-extra/test/clang-query/Inputs/foo.script
Normal file
2
clang-tools-extra/test/clang-query/Inputs/foo.script
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
foo
|
||||||
|
bar
|
11
clang-tools-extra/test/clang-query/errors.c
Normal file
11
clang-tools-extra/test/clang-query/errors.c
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// RUN: not clang-query -c foo -c bar %s -- | FileCheck %s
|
||||||
|
// RUN: not clang-query -f %S/Inputs/foo.script %s -- | FileCheck %s
|
||||||
|
// RUN: not clang-query -f %S/Inputs/nonexistent.script %s -- 2>&1 | FileCheck --check-prefix=CHECK-NONEXISTENT %s
|
||||||
|
// RUN: not clang-query -c foo -f foo %s -- 2>&1 | FileCheck --check-prefix=CHECK-BOTH %s
|
||||||
|
// REQUIRES: libedit
|
||||||
|
|
||||||
|
// CHECK: unknown command: foo
|
||||||
|
// CHECK-NOT: unknown command: bar
|
||||||
|
|
||||||
|
// CHECK-NONEXISTENT: cannot open {{.*}}nonexistent.script
|
||||||
|
// CHECK-BOTH: cannot specify both -c and -f
|
5
clang-tools-extra/test/clang-query/function-decl.c
Normal file
5
clang-tools-extra/test/clang-query/function-decl.c
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// RUN: clang-query -c "match functionDecl()" %s -- | FileCheck %s
|
||||||
|
// REQUIRES: libedit
|
||||||
|
|
||||||
|
// CHECK: function-decl.c:5:1: note: "root" binds here
|
||||||
|
void foo(void) {}
|
@ -229,3 +229,6 @@ if platform.system() not in ['Windows'] or lit_config.getBashPath() != '':
|
|||||||
# ANSI escape sequences in non-dumb terminal
|
# ANSI escape sequences in non-dumb terminal
|
||||||
if platform.system() not in ['Windows']:
|
if platform.system() not in ['Windows']:
|
||||||
config.available_features.add('ansi-escape-sequences')
|
config.available_features.add('ansi-escape-sequences')
|
||||||
|
|
||||||
|
if config.have_libedit == "1":
|
||||||
|
config.available_features.add('libedit')
|
||||||
|
@ -7,6 +7,7 @@ config.llvm_libs_dir = "@LLVM_LIBS_DIR@"
|
|||||||
config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
|
config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
|
||||||
config.clang_tools_binary_dir = "@CLANG_TOOLS_BINARY_DIR@"
|
config.clang_tools_binary_dir = "@CLANG_TOOLS_BINARY_DIR@"
|
||||||
config.target_triple = "@TARGET_TRIPLE@"
|
config.target_triple = "@TARGET_TRIPLE@"
|
||||||
|
config.have_libedit = "@HAVE_LIBEDIT@"
|
||||||
|
|
||||||
# Support substitution of the tools and libs dirs with user parameters. This is
|
# 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.
|
# used when we can't determine the tool dir at configuration time.
|
||||||
|
@ -7,4 +7,5 @@ endfunction()
|
|||||||
|
|
||||||
add_subdirectory(clang-apply-replacements)
|
add_subdirectory(clang-apply-replacements)
|
||||||
add_subdirectory(clang-modernize)
|
add_subdirectory(clang-modernize)
|
||||||
|
add_subdirectory(clang-query)
|
||||||
add_subdirectory(clang-tidy)
|
add_subdirectory(clang-tidy)
|
||||||
|
@ -10,6 +10,6 @@
|
|||||||
CLANG_LEVEL := ../../..
|
CLANG_LEVEL := ../../..
|
||||||
include $(CLANG_LEVEL)/../../Makefile.config
|
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
|
include $(CLANG_LEVEL)/Makefile
|
||||||
|
18
clang-tools-extra/unittests/clang-query/CMakeLists.txt
Normal file
18
clang-tools-extra/unittests/clang-query/CMakeLists.txt
Normal file
@ -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
|
||||||
|
)
|
24
clang-tools-extra/unittests/clang-query/Makefile
Normal file
24
clang-tools-extra/unittests/clang-query/Makefile
Normal file
@ -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
|
110
clang-tools-extra/unittests/clang-query/QueryEngineTest.cpp
Normal file
110
clang-tools-extra/unittests/clang-query/QueryEngineTest.cpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
//===---- QueryTest.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 "Query.h"
|
||||||
|
#include "QuerySession.h"
|
||||||
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||||
|
#include "clang/ASTMatchers/Dynamic/VariantValue.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::ast_matchers::dynamic;
|
||||||
|
using namespace clang::query;
|
||||||
|
using namespace clang::tooling;
|
||||||
|
|
||||||
|
TEST(Query, 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);
|
||||||
|
QuerySession S(ASTs);
|
||||||
|
|
||||||
|
DynTypedMatcher FnMatcher = functionDecl();
|
||||||
|
DynTypedMatcher FooMatcher = functionDecl(hasName("foo1"));
|
||||||
|
|
||||||
|
EXPECT_TRUE(NoOpQuery().run(OS, S));
|
||||||
|
|
||||||
|
EXPECT_EQ("", OS.str());
|
||||||
|
|
||||||
|
Str.clear();
|
||||||
|
|
||||||
|
EXPECT_FALSE(InvalidQuery("Parse error").run(OS, S));
|
||||||
|
|
||||||
|
EXPECT_EQ("Parse error\n", OS.str());
|
||||||
|
|
||||||
|
Str.clear();
|
||||||
|
|
||||||
|
EXPECT_TRUE(HelpQuery().run(OS, S));
|
||||||
|
|
||||||
|
EXPECT_TRUE(OS.str().find("Available commands:") != std::string::npos);
|
||||||
|
|
||||||
|
Str.clear();
|
||||||
|
|
||||||
|
EXPECT_TRUE(MatchQuery(FnMatcher).run(OS, S));
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
EXPECT_TRUE(
|
||||||
|
SetQuery<OutputKind>(&QuerySession::OutKind, OK_Print).run(OS, S));
|
||||||
|
EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
|
||||||
|
|
||||||
|
EXPECT_TRUE(OS.str().find("Binding for \"root\":\nvoid foo1()") !=
|
||||||
|
std::string::npos);
|
||||||
|
|
||||||
|
Str.clear();
|
||||||
|
|
||||||
|
EXPECT_TRUE(SetQuery<OutputKind>(&QuerySession::OutKind, OK_Dump).run(OS, S));
|
||||||
|
EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
|
||||||
|
|
||||||
|
EXPECT_TRUE(OS.str().find("FunctionDecl") != std::string::npos);
|
||||||
|
|
||||||
|
Str.clear();
|
||||||
|
|
||||||
|
EXPECT_TRUE(SetQuery<bool>(&QuerySession::BindRoot, false).run(OS, S));
|
||||||
|
EXPECT_TRUE(MatchQuery(FooMatcher).run(OS, S));
|
||||||
|
|
||||||
|
EXPECT_TRUE(OS.str().find("No bindings.") != std::string::npos);
|
||||||
|
|
||||||
|
Str.clear();
|
||||||
|
|
||||||
|
EXPECT_FALSE(MatchQuery(isArrow()).run(OS, S));
|
||||||
|
|
||||||
|
EXPECT_EQ("Not a valid top-level matcher.\n", OS.str());
|
||||||
|
}
|
87
clang-tools-extra/unittests/clang-query/QueryParserTest.cpp
Normal file
87
clang-tools-extra/unittests/clang-query/QueryParserTest.cpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
//===---- 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 "QuerySession.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
using namespace clang;
|
||||||
|
using namespace clang::query;
|
||||||
|
|
||||||
|
TEST(QueryParser, NoOp) {
|
||||||
|
QueryRef Q = ParseQuery("");
|
||||||
|
EXPECT_TRUE(isa<NoOpQuery>(Q));
|
||||||
|
|
||||||
|
Q = ParseQuery("\n");
|
||||||
|
EXPECT_TRUE(isa<NoOpQuery>(Q));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(QueryParser, Invalid) {
|
||||||
|
QueryRef Q = ParseQuery("foo");
|
||||||
|
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||||
|
EXPECT_EQ("unknown command: foo", cast<InvalidQuery>(Q)->ErrStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(QueryParser, Help) {
|
||||||
|
QueryRef Q = ParseQuery("help");
|
||||||
|
ASSERT_TRUE(isa<HelpQuery>(Q));
|
||||||
|
|
||||||
|
Q = ParseQuery("help me");
|
||||||
|
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||||
|
EXPECT_EQ("unexpected extra input: ' me'", cast<InvalidQuery>(Q)->ErrStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(QueryParser, Set) {
|
||||||
|
QueryRef Q = ParseQuery("set");
|
||||||
|
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||||
|
EXPECT_EQ("expected variable name", cast<InvalidQuery>(Q)->ErrStr);
|
||||||
|
|
||||||
|
Q = ParseQuery("set foo bar");
|
||||||
|
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||||
|
EXPECT_EQ("unknown variable: 'foo'", cast<InvalidQuery>(Q)->ErrStr);
|
||||||
|
|
||||||
|
Q = ParseQuery("set output");
|
||||||
|
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||||
|
EXPECT_EQ("expected variable value", cast<InvalidQuery>(Q)->ErrStr);
|
||||||
|
|
||||||
|
Q = ParseQuery("set bind-root true foo");
|
||||||
|
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||||
|
EXPECT_EQ("unexpected extra input: ' foo'", cast<InvalidQuery>(Q)->ErrStr);
|
||||||
|
|
||||||
|
Q = ParseQuery("set output foo");
|
||||||
|
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||||
|
EXPECT_EQ("expected 'diag', 'print' or 'dump', got 'foo'",
|
||||||
|
cast<InvalidQuery>(Q)->ErrStr);
|
||||||
|
|
||||||
|
Q = ParseQuery("set output dump");
|
||||||
|
ASSERT_TRUE(isa<SetQuery<OutputKind> >(Q));
|
||||||
|
EXPECT_EQ(&QuerySession::OutKind, cast<SetQuery<OutputKind> >(Q)->Var);
|
||||||
|
EXPECT_EQ(OK_Dump, cast<SetQuery<OutputKind> >(Q)->Value);
|
||||||
|
|
||||||
|
Q = ParseQuery("set bind-root foo");
|
||||||
|
ASSERT_TRUE(isa<InvalidQuery>(Q));
|
||||||
|
EXPECT_EQ("expected 'true' or 'false', got 'foo'",
|
||||||
|
cast<InvalidQuery>(Q)->ErrStr);
|
||||||
|
|
||||||
|
Q = ParseQuery("set bind-root true");
|
||||||
|
ASSERT_TRUE(isa<SetQuery<bool> >(Q));
|
||||||
|
EXPECT_EQ(&QuerySession::BindRoot, cast<SetQuery<bool> >(Q)->Var);
|
||||||
|
EXPECT_EQ(true, cast<SetQuery<bool> >(Q)->Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(QueryParser, Match) {
|
||||||
|
QueryRef Q = ParseQuery("match decl()");
|
||||||
|
ASSERT_TRUE(isa<MatchQuery>(Q));
|
||||||
|
EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Decl>());
|
||||||
|
|
||||||
|
Q = ParseQuery("m stmt()");
|
||||||
|
ASSERT_TRUE(isa<MatchQuery>(Q));
|
||||||
|
EXPECT_TRUE(cast<MatchQuery>(Q)->Matcher.canConvertTo<Stmt>());
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user