11#include "clang/AST/ASTDumper.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Frontend/ASTUnit.h"
14#include "clang/Frontend/TextDiagnostic.h"
15#include "clang/Tooling/NodeIntrospection.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 traversal <kind> "
49 "Set traversal kind of clang-query session. Available kinds are:\n"
51 "Print and match the AST as clang sees it. This mode is the "
53 " IgnoreUnlessSpelledInSource "
54 "Omit AST nodes unless spelled in the source.\n"
55 " set output <feature> "
56 "Set whether to output only <feature> content.\n"
57 " enable output <feature> "
58 "Enable <feature> content non-exclusively.\n"
59 " disable output <feature> "
60 "Disable <feature> content non-exclusively.\n"
62 "Terminates the query session.\n\n"
63 "Several commands accept a <feature> parameter. The available features "
66 "Pretty-print bound nodes.\n"
68 "Diagnostic location for bound nodes.\n"
70 "Detailed AST output for bound nodes.\n"
72 "Source locations and ranges for bound nodes.\n"
74 "Detailed AST output for bound nodes (alias of detailed-ast).\n\n";
85struct CollectBoundNodes : MatchFinder::MatchCallback {
88 void run(
const MatchFinder::MatchResult &Result)
override {
93void dumpLocations(llvm::raw_ostream &
OS, DynTypedNode Node, ASTContext &Ctx,
94 const DiagnosticsEngine &Diags, SourceManager
const &SM) {
95 auto Locs = clang::tooling::NodeIntrospection::GetLocations(Node);
97 auto PrintLocations = [](llvm::raw_ostream &
OS,
auto Iter,
auto End) {
98 auto CommonEntry = Iter->first;
100 SmallVector<std::string> LocationStrings;
101 while (Scout->first == CommonEntry) {
102 LocationStrings.push_back(
103 tooling::LocationCallFormatterCpp::format(*Iter->second));
107 if (Scout->first == CommonEntry)
110 llvm::sort(LocationStrings);
111 for (
auto &LS : LocationStrings) {
112 OS <<
" * \"" << LS <<
"\"\n";
117 TextDiagnostic TD(
OS, Ctx.getLangOpts(), &Diags.getDiagnosticOptions());
119 for (
auto Iter = Locs.LocationAccessors.begin();
120 Iter != Locs.LocationAccessors.end(); ++Iter) {
121 if (!Iter->first.isValid())
124 TD.emitDiagnostic(FullSourceLoc(Iter->first, SM), DiagnosticsEngine::Note,
125 "source locations here", std::nullopt, std::nullopt);
127 Iter = PrintLocations(
OS, Iter, Locs.LocationAccessors.end());
131 for (
auto Iter = Locs.RangeAccessors.begin();
132 Iter != Locs.RangeAccessors.end(); ++Iter) {
134 if (!Iter->first.getBegin().isValid())
137 if (SM.getPresumedLineNumber(Iter->first.getBegin()) !=
138 SM.getPresumedLineNumber(Iter->first.getEnd()))
142 FullSourceLoc(Iter->first.getBegin(), SM), DiagnosticsEngine::Note,
143 "source ranges here " + Iter->first.printToString(SM),
144 CharSourceRange::getTokenRange(Iter->first), std::nullopt);
146 Iter = PrintLocations(
OS, Iter, Locs.RangeAccessors.end());
148 for (
auto Iter = Locs.RangeAccessors.begin();
149 Iter != Locs.RangeAccessors.end(); ++Iter) {
151 if (!Iter->first.getBegin().isValid())
154 if (SM.getPresumedLineNumber(Iter->first.getBegin()) ==
155 SM.getPresumedLineNumber(Iter->first.getEnd()))
159 FullSourceLoc(Iter->first.getBegin(), SM), DiagnosticsEngine::Note,
160 "source range " + Iter->first.printToString(SM) +
" starting here...",
161 CharSourceRange::getTokenRange(Iter->first), std::nullopt);
163 auto ColNum = SM.getPresumedColumnNumber(Iter->first.getEnd());
164 auto LastLineLoc = Iter->first.getEnd().getLocWithOffset(-(ColNum - 1));
166 TD.emitDiagnostic(FullSourceLoc(Iter->first.getEnd(), SM),
167 DiagnosticsEngine::Note,
"... ending here",
168 CharSourceRange::getTokenRange(
169 SourceRange(LastLineLoc, Iter->first.getEnd())),
172 Iter = PrintLocations(
OS, Iter, Locs.RangeAccessors.end());
180 unsigned MatchCount = 0;
182 for (
auto &AST : QS.
ASTs) {
184 std::vector<BoundNodes> Matches;
185 DynTypedMatcher MaybeBoundMatcher =
Matcher;
187 std::optional<DynTypedMatcher>
M =
Matcher.tryBind(
"root");
189 MaybeBoundMatcher = *
M;
191 CollectBoundNodes Collect(Matches);
192 if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
193 OS <<
"Not a valid top-level matcher.\n";
197 auto &Ctx = AST->getASTContext();
198 const auto &SM = Ctx.getSourceManager();
199 Ctx.getParentMapContext().setTraversalKind(QS.
TK);
200 Finder.matchAST(Ctx);
203 SmallVector<StringRef, 4>
Lines;
205 auto FirstLine =
Lines[0];
207 while (!
Lines.empty() &&
Lines.back().empty()) {
210 unsigned MaxLength = FirstLine.size();
211 std::string PrefixText =
"Matcher: ";
212 OS <<
"\n " << PrefixText << FirstLine;
215 OS <<
"\n" << std::string(PrefixText.size() + 2,
' ') <<
Line;
216 MaxLength = std::max<int>(MaxLength,
Line.rtrim().size());
220 <<
" " << std::string(PrefixText.size() + MaxLength,
'=') <<
"\n\n";
223 for (
auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
224 OS <<
"\nMatch #" << ++MatchCount <<
":\n\n";
226 for (
auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;
229 clang::SourceRange R = BI->second.getSourceRange();
231 TextDiagnostic TD(
OS, AST->getASTContext().getLangOpts(),
232 &AST->getDiagnostics().getDiagnosticOptions());
234 FullSourceLoc(R.getBegin(), AST->getSourceManager()),
235 DiagnosticsEngine::Note,
"\"" + BI->first +
"\" binds here",
236 CharSourceRange::getTokenRange(R), std::nullopt);
240 OS <<
"Binding for \"" << BI->first <<
"\":\n";
241 BI->second.print(
OS, AST->getASTContext().getPrintingPolicy());
245 OS <<
"Binding for \"" << BI->first <<
"\":\n";
246 const ASTContext &Ctx = AST->getASTContext();
247 ASTDumper Dumper(
OS, Ctx, AST->getDiagnostics().getShowColors());
248 Dumper.SetTraversalKind(QS.
TK);
249 Dumper.Visit(BI->second);
253 OS <<
"\n \"" << BI->first <<
"\" Source locations\n";
254 OS <<
" " << std::string(19 + BI->first.size(),
'-') <<
'\n';
256 dumpLocations(
OS, BI->second, Ctx, AST->getDiagnostics(), SM);
261 if (MI->getMap().empty())
262 OS <<
"No bindings.\n";
266 OS << MatchCount << (MatchCount == 1 ?
" match.\n" :
" matches.\n");
std::vector< BoundNodes > & Bindings
const google::protobuf::Message & M
llvm::raw_string_ostream OS
Represents the state for a particular clang-query session.
llvm::StringMap< ast_matchers::dynamic::VariantValue > NamedValues
llvm::ArrayRef< std::unique_ptr< ASTUnit > > ASTs
===– 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.
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.