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