1 //===---- Query.cpp - clang-query query -----------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 #include "QuerySession.h"
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"
18 using namespace clang::ast_matchers
;
19 using namespace clang::ast_matchers::dynamic
;
26 bool InvalidQuery::run(llvm::raw_ostream
&OS
, QuerySession
&QS
) const {
31 bool NoOpQuery::run(llvm::raw_ostream
&OS
, QuerySession
&QS
) const {
35 bool HelpQuery::run(llvm::raw_ostream
&OS
, QuerySession
&QS
) const {
36 OS
<< "Available commands:\n\n"
37 " match MATCHER, m MATCHER "
38 "Match the loaded ASTs against the given matcher.\n"
39 " let NAME MATCHER, l NAME MATCHER "
40 "Give a matcher expression a name, to be used later\n"
42 "as part of other expressions.\n"
43 " set bind-root (true|false) "
44 "Set whether to bind the root matcher to \"root\".\n"
45 " set print-matcher (true|false) "
46 "Set whether to print the current matcher,\n"
47 " set traversal <kind> "
48 "Set traversal kind of clang-query session. Available kinds are:\n"
50 "Print and match the AST as clang sees it. This mode is the "
52 " IgnoreUnlessSpelledInSource "
53 "Omit AST nodes unless spelled in the source.\n"
54 " set output <feature> "
55 "Set whether to output only <feature> content.\n"
56 " enable output <feature> "
57 "Enable <feature> content non-exclusively.\n"
58 " disable output <feature> "
59 "Disable <feature> content non-exclusively.\n"
61 "Terminates the query session.\n\n"
62 "Several commands accept a <feature> parameter. The available features "
65 "Pretty-print bound nodes.\n"
67 "Diagnostic location for bound nodes.\n"
69 "Detailed AST output for bound nodes.\n"
71 "Source locations and ranges for bound nodes.\n"
73 "Detailed AST output for bound nodes (alias of detailed-ast).\n\n";
77 bool QuitQuery::run(llvm::raw_ostream
&OS
, QuerySession
&QS
) const {
84 struct CollectBoundNodes
: MatchFinder::MatchCallback
{
85 std::vector
<BoundNodes
> &Bindings
;
86 CollectBoundNodes(std::vector
<BoundNodes
> &Bindings
) : Bindings(Bindings
) {}
87 void run(const MatchFinder::MatchResult
&Result
) override
{
88 Bindings
.push_back(Result
.Nodes
);
92 void dumpLocations(llvm::raw_ostream
&OS
, DynTypedNode Node
, ASTContext
&Ctx
,
93 const DiagnosticsEngine
&Diags
, SourceManager
const &SM
) {
94 auto Locs
= clang::tooling::NodeIntrospection::GetLocations(Node
);
96 auto PrintLocations
= [](llvm::raw_ostream
&OS
, auto Iter
, auto End
) {
97 auto CommonEntry
= Iter
->first
;
99 SmallVector
<std::string
> LocationStrings
;
100 while (Scout
->first
== CommonEntry
) {
101 LocationStrings
.push_back(
102 tooling::LocationCallFormatterCpp::format(*Iter
->second
));
106 if (Scout
->first
== CommonEntry
)
109 llvm::sort(LocationStrings
);
110 for (auto &LS
: LocationStrings
) {
111 OS
<< " * \"" << LS
<< "\"\n";
116 TextDiagnostic
TD(OS
, Ctx
.getLangOpts(), &Diags
.getDiagnosticOptions());
118 for (auto Iter
= Locs
.LocationAccessors
.begin();
119 Iter
!= Locs
.LocationAccessors
.end(); ++Iter
) {
120 if (!Iter
->first
.isValid())
123 TD
.emitDiagnostic(FullSourceLoc(Iter
->first
, SM
), DiagnosticsEngine::Note
,
124 "source locations here", None
, None
);
126 Iter
= PrintLocations(OS
, Iter
, Locs
.LocationAccessors
.end());
130 for (auto Iter
= Locs
.RangeAccessors
.begin();
131 Iter
!= Locs
.RangeAccessors
.end(); ++Iter
) {
133 if (!Iter
->first
.getBegin().isValid())
136 if (SM
.getPresumedLineNumber(Iter
->first
.getBegin()) !=
137 SM
.getPresumedLineNumber(Iter
->first
.getEnd()))
140 TD
.emitDiagnostic(FullSourceLoc(Iter
->first
.getBegin(), SM
),
141 DiagnosticsEngine::Note
,
142 "source ranges here " + Iter
->first
.printToString(SM
),
143 CharSourceRange::getTokenRange(Iter
->first
), None
);
145 Iter
= PrintLocations(OS
, Iter
, Locs
.RangeAccessors
.end());
147 for (auto Iter
= Locs
.RangeAccessors
.begin();
148 Iter
!= Locs
.RangeAccessors
.end(); ++Iter
) {
150 if (!Iter
->first
.getBegin().isValid())
153 if (SM
.getPresumedLineNumber(Iter
->first
.getBegin()) ==
154 SM
.getPresumedLineNumber(Iter
->first
.getEnd()))
158 FullSourceLoc(Iter
->first
.getBegin(), SM
), DiagnosticsEngine::Note
,
159 "source range " + Iter
->first
.printToString(SM
) + " starting here...",
160 CharSourceRange::getTokenRange(Iter
->first
), None
);
162 auto ColNum
= SM
.getPresumedColumnNumber(Iter
->first
.getEnd());
163 auto LastLineLoc
= Iter
->first
.getEnd().getLocWithOffset(-(ColNum
- 1));
165 TD
.emitDiagnostic(FullSourceLoc(Iter
->first
.getEnd(), SM
),
166 DiagnosticsEngine::Note
, "... ending here",
167 CharSourceRange::getTokenRange(
168 SourceRange(LastLineLoc
, Iter
->first
.getEnd())),
171 Iter
= PrintLocations(OS
, Iter
, Locs
.RangeAccessors
.end());
178 bool MatchQuery::run(llvm::raw_ostream
&OS
, QuerySession
&QS
) const {
179 unsigned MatchCount
= 0;
181 for (auto &AST
: QS
.ASTs
) {
183 std::vector
<BoundNodes
> Matches
;
184 DynTypedMatcher MaybeBoundMatcher
= Matcher
;
186 llvm::Optional
<DynTypedMatcher
> M
= Matcher
.tryBind("root");
188 MaybeBoundMatcher
= *M
;
190 CollectBoundNodes
Collect(Matches
);
191 if (!Finder
.addDynamicMatcher(MaybeBoundMatcher
, &Collect
)) {
192 OS
<< "Not a valid top-level matcher.\n";
196 auto &Ctx
= AST
->getASTContext();
197 const auto &SM
= Ctx
.getSourceManager();
198 Ctx
.getParentMapContext().setTraversalKind(QS
.TK
);
199 Finder
.matchAST(Ctx
);
201 if (QS
.PrintMatcher
) {
202 SmallVector
<StringRef
, 4> Lines
;
203 Source
.split(Lines
, "\n");
204 auto FirstLine
= Lines
[0];
205 Lines
.erase(Lines
.begin(), Lines
.begin() + 1);
206 while (!Lines
.empty() && Lines
.back().empty()) {
207 Lines
.resize(Lines
.size() - 1);
209 unsigned MaxLength
= FirstLine
.size();
210 std::string PrefixText
= "Matcher: ";
211 OS
<< "\n " << PrefixText
<< FirstLine
;
213 for (auto Line
: Lines
) {
214 OS
<< "\n" << std::string(PrefixText
.size() + 2, ' ') << Line
;
215 MaxLength
= std::max
<int>(MaxLength
, Line
.rtrim().size());
219 << " " << std::string(PrefixText
.size() + MaxLength
, '=') << "\n\n";
222 for (auto MI
= Matches
.begin(), ME
= Matches
.end(); MI
!= ME
; ++MI
) {
223 OS
<< "\nMatch #" << ++MatchCount
<< ":\n\n";
225 for (auto BI
= MI
->getMap().begin(), BE
= MI
->getMap().end(); BI
!= BE
;
228 clang::SourceRange R
= BI
->second
.getSourceRange();
230 TextDiagnostic
TD(OS
, AST
->getASTContext().getLangOpts(),
231 &AST
->getDiagnostics().getDiagnosticOptions());
233 FullSourceLoc(R
.getBegin(), AST
->getSourceManager()),
234 DiagnosticsEngine::Note
, "\"" + BI
->first
+ "\" binds here",
235 CharSourceRange::getTokenRange(R
), None
);
238 if (QS
.PrintOutput
) {
239 OS
<< "Binding for \"" << BI
->first
<< "\":\n";
240 BI
->second
.print(OS
, AST
->getASTContext().getPrintingPolicy());
243 if (QS
.DetailedASTOutput
) {
244 OS
<< "Binding for \"" << BI
->first
<< "\":\n";
245 const ASTContext
&Ctx
= AST
->getASTContext();
246 ASTDumper
Dumper(OS
, Ctx
, AST
->getDiagnostics().getShowColors());
247 Dumper
.SetTraversalKind(QS
.TK
);
248 Dumper
.Visit(BI
->second
);
251 if (QS
.SrcLocOutput
) {
252 OS
<< "\n \"" << BI
->first
<< "\" Source locations\n";
253 OS
<< " " << std::string(19 + BI
->first
.size(), '-') << '\n';
255 dumpLocations(OS
, BI
->second
, Ctx
, AST
->getDiagnostics(), SM
);
260 if (MI
->getMap().empty())
261 OS
<< "No bindings.\n";
265 OS
<< MatchCount
<< (MatchCount
== 1 ? " match.\n" : " matches.\n");
269 bool LetQuery::run(llvm::raw_ostream
&OS
, QuerySession
&QS
) const {
271 QS
.NamedValues
[Name
] = Value
;
273 QS
.NamedValues
.erase(Name
);
279 const QueryKind SetQueryKind
<bool>::value
;
280 const QueryKind SetQueryKind
<OutputKind
>::value
;