12#include "clang/AST/ASTDumper.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Frontend/ASTUnit.h"
15#include "clang/Frontend/TextDiagnostic.h"
16#include "llvm/Support/raw_ostream.h"
20using namespace clang::ast_matchers::dynamic;
37 OS <<
"Available commands:\n\n"
38 " match MATCHER, m MATCHER "
39 "Match the loaded ASTs against the given matcher.\n"
40 " let NAME MATCHER, l NAME MATCHER "
41 "Give a matcher expression a name, to be used later\n"
43 "as part of other expressions.\n"
44 " set bind-root (true|false) "
45 "Set whether to bind the root matcher to \"root\".\n"
46 " set print-matcher (true|false) "
47 "Set whether to print the current matcher.\n"
48 " set enable-profile (true|false) "
49 "Set whether to enable matcher profiling.\n"
50 " set traversal <kind> "
51 "Set traversal kind of clang-query session. Available kinds are:\n"
53 "Print and match the AST as clang sees it. This mode is the "
55 " IgnoreUnlessSpelledInSource "
56 "Omit AST nodes unless spelled in the source.\n"
57 " set output <feature> "
58 "Set whether to output only <feature> content.\n"
59 " enable output <feature> "
60 "Enable <feature> content non-exclusively.\n"
61 " disable output <feature> "
62 "Disable <feature> content non-exclusively.\n"
64 "Terminates the query session.\n\n"
65 "Several commands accept a <feature> parameter. The available features "
68 "Pretty-print bound nodes.\n"
70 "Diagnostic location for bound nodes.\n"
72 "Detailed AST output for bound nodes.\n"
74 "Detailed AST output for bound nodes (alias of detailed-ast).\n\n";
85struct CollectBoundNodes : MatchFinder::MatchCallback {
88 CollectBoundNodes(std::vector<BoundNodes> &Bindings, StringRef Unit)
90 void run(
const MatchFinder::MatchResult &Result)
override {
93 StringRef getID()
const override {
return Unit; }
97 llvm::StringMap<llvm::TimeRecord>
Records;
100 llvm::TimerGroup TG(
"clang-query",
"clang-query matcher profiling",
102 TG.print(llvm::errs());
103 llvm::errs().flush();
110 unsigned MatchCount = 0;
112 std::optional<QueryProfiler> Profiler;
116 for (
auto &AST : QS.
ASTs) {
117 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
118 std::optional<llvm::StringMap<llvm::TimeRecord>>
Records;
121 FinderOptions.CheckProfiling.emplace(*
Records);
124 MatchFinder Finder(FinderOptions);
125 std::vector<BoundNodes> Matches;
126 DynTypedMatcher MaybeBoundMatcher =
Matcher;
128 std::optional<DynTypedMatcher>
M =
Matcher.tryBind(
"root");
130 MaybeBoundMatcher = *
M;
132 StringRef OrigSrcName = AST->getOriginalSourceFileName();
133 CollectBoundNodes Collect(Matches, OrigSrcName);
134 if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
135 OS <<
"Not a valid top-level matcher.\n";
139 ASTContext &Ctx = AST->getASTContext();
140 Ctx.getParentMapContext().setTraversalKind(QS.
TK);
141 Finder.matchAST(Ctx);
143 Profiler->Records[OrigSrcName] += (*Records)[OrigSrcName];
146 SmallVector<StringRef, 4>
Lines;
148 auto FirstLine =
Lines[0];
150 while (!
Lines.empty() &&
Lines.back().empty()) {
153 unsigned MaxLength = FirstLine.size();
154 std::string PrefixText =
"Matcher: ";
155 OS <<
"\n " << PrefixText << FirstLine;
158 OS <<
"\n" << std::string(PrefixText.size() + 2,
' ') <<
Line;
159 MaxLength = std::max<int>(MaxLength,
Line.rtrim().size());
163 <<
" " << std::string(PrefixText.size() + MaxLength,
'=') <<
"\n\n";
166 for (
auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
167 OS <<
"\nMatch #" << ++MatchCount <<
":\n\n";
169 for (
auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;
172 clang::SourceRange R = BI->second.getSourceRange();
174 TextDiagnostic TD(
OS, AST->getASTContext().getLangOpts(),
175 &AST->getDiagnostics().getDiagnosticOptions());
177 FullSourceLoc(R.getBegin(), AST->getSourceManager()),
178 DiagnosticsEngine::Note,
"\"" + BI->first +
"\" binds here",
179 CharSourceRange::getTokenRange(R), {});
183 OS <<
"Binding for \"" << BI->first <<
"\":\n";
184 BI->second.print(
OS, AST->getASTContext().getPrintingPolicy());
188 OS <<
"Binding for \"" << BI->first <<
"\":\n";
189 ASTDumper Dumper(
OS, Ctx, AST->getDiagnostics().getShowColors());
190 Dumper.SetTraversalKind(QS.
TK);
191 Dumper.Visit(BI->second);
196 if (MI->getMap().empty())
197 OS <<
"No bindings.\n";
201 OS << MatchCount << (MatchCount == 1 ?
" match.\n" :
" matches.\n");
220 auto Buffer = llvm::MemoryBuffer::getFile(StringRef{File}.trim());
222 if (Prefix.has_value())
223 llvm::errs() << *Prefix <<
": ";
224 llvm::errs() <<
"cannot open " << File <<
": "
225 << Buffer.getError().message() <<
"\n";
229 StringRef FileContentRef(Buffer.get()->getBuffer());
231 while (!FileContentRef.empty()) {
233 if (!Q->run(llvm::outs(), QS))
235 FileContentRef = Q->RemainingContent;
llvm::StringMap< llvm::TimeRecord > Records
std::vector< BoundNodes > & Bindings
const google::protobuf::Message & M
static QueryRef parse(StringRef Line, const QuerySession &QS)
Parse Line as a query.
Represents the state for a particular clang-query session.
llvm::StringMap< ast_matchers::dynamic::VariantValue > NamedValues
llvm::ArrayRef< std::unique_ptr< ASTUnit > > ASTs
llvm::IntrusiveRefCntPtr< Query > QueryRef
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
ast_matchers::dynamic::VariantValue Value
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
ast_matchers::dynamic::DynTypedMatcher Matcher
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.