blob: 2c169b1f9a7fb6e607cdc9da4f10e0899c37ce8b [file] [log] [blame]
Peter Collingbourne8b1265b2013-11-08 00:08:231//===---- Query.cpp - clang-query query -----------------------------------===//
2//
Chandler Carruth2946cd72019-01-19 08:50:563// 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
Peter Collingbourne8b1265b2013-11-08 00:08:236//
7//===----------------------------------------------------------------------===//
8
9#include "Query.h"
10#include "QuerySession.h"
Stephen Kellyb22d8ae2019-12-06 23:26:5911#include "clang/AST/ASTDumper.h"
Peter Collingbourne8b1265b2013-11-08 00:08:2312#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Frontend/ASTUnit.h"
14#include "clang/Frontend/TextDiagnostic.h"
Stephen Kelly8d018c72020-12-12 18:39:4915#include "clang/Tooling/NodeIntrospection.h"
Peter Collingbourne8b1265b2013-11-08 00:08:2316#include "llvm/Support/raw_ostream.h"
17
18using namespace clang::ast_matchers;
19using namespace clang::ast_matchers::dynamic;
20
21namespace clang {
22namespace query {
23
David Blaikiee04a3da2015-10-20 21:45:5224Query::~Query() {}
Peter Collingbourne8b1265b2013-11-08 00:08:2325
26bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
27 OS << ErrStr << "\n";
28 return false;
29}
30
31bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
32 return true;
33}
34
35bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
36 OS << "Available commands:\n\n"
Samuel Benzaquene39269e2015-02-27 17:53:2337 " match MATCHER, m MATCHER "
Peter Collingbourne8b1265b2013-11-08 00:08:2338 "Match the loaded ASTs against the given matcher.\n"
Samuel Benzaquene39269e2015-02-27 17:53:2339 " let NAME MATCHER, l NAME MATCHER "
40 "Give a matcher expression a name, to be used later\n"
41 " "
42 "as part of other expressions.\n"
43 " set bind-root (true|false) "
Peter Collingbourne8b1265b2013-11-08 00:08:2344 "Set whether to bind the root matcher to \"root\".\n"
Stephen Kelly4a5b01d2018-10-20 09:13:5945 " set print-matcher (true|false) "
46 "Set whether to print the current matcher,\n"
Stephen Kelly10f0f982019-12-29 19:26:1147 " set traversal <kind> "
48 "Set traversal kind of clang-query session. Available kinds are:\n"
49 " AsIs "
Stephen Kelly551092b2020-07-02 19:46:2750 "Print and match the AST as clang sees it. This mode is the "
51 "default.\n"
Stephen Kelly10f0f982019-12-29 19:26:1152 " IgnoreUnlessSpelledInSource "
Stephen Kelly551092b2020-07-02 19:46:2753 "Omit AST nodes unless spelled in the source.\n"
Stephen Kelly4c3d7a92018-10-24 20:33:1454 " set output <feature> "
55 "Set whether to output only <feature> content.\n"
Stephen Kellya49fe5d2018-10-29 18:59:5656 " enable output <feature> "
57 "Enable <feature> content non-exclusively.\n"
58 " disable output <feature> "
59 "Disable <feature> content non-exclusively.\n"
Stephen Kelly42668a42018-10-03 07:52:4460 " quit, q "
Stephen Kelly4c3d7a92018-10-24 20:33:1461 "Terminates the query session.\n\n"
62 "Several commands accept a <feature> parameter. The available features "
63 "are:\n\n"
64 " print "
65 "Pretty-print bound nodes.\n"
66 " diag "
67 "Diagnostic location for bound nodes.\n"
Stephen Kelly51707b22018-10-24 20:33:4568 " detailed-ast "
69 "Detailed AST output for bound nodes.\n"
Stephen Kelly8d018c72020-12-12 18:39:4970 " srcloc "
71 "Source locations and ranges for bound nodes.\n"
Stephen Kelly4c3d7a92018-10-24 20:33:1472 " dump "
Stephen Kelly51707b22018-10-24 20:33:4573 "Detailed AST output for bound nodes (alias of detailed-ast).\n\n";
Aaron Ballman58907172015-08-06 11:56:5774 return true;
75}
76
77bool QuitQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
78 QS.Terminate = true;
Peter Collingbourne8b1265b2013-11-08 00:08:2379 return true;
80}
81
82namespace {
83
84struct CollectBoundNodes : MatchFinder::MatchCallback {
85 std::vector<BoundNodes> &Bindings;
86 CollectBoundNodes(std::vector<BoundNodes> &Bindings) : Bindings(Bindings) {}
Alexander Kornienko87638f62015-04-11 07:59:3387 void run(const MatchFinder::MatchResult &Result) override {
Peter Collingbourne8b1265b2013-11-08 00:08:2388 Bindings.push_back(Result.Nodes);
89 }
90};
91
Stephen Kelly8d018c72020-12-12 18:39:4992void dumpLocations(llvm::raw_ostream &OS, DynTypedNode Node, ASTContext &Ctx,
93 const DiagnosticsEngine &Diags, SourceManager const &SM) {
94 auto Locs = clang::tooling::NodeIntrospection::GetLocations(Node);
95
96 auto PrintLocations = [](llvm::raw_ostream &OS, auto Iter, auto End) {
97 auto CommonEntry = Iter->first;
98 auto Scout = Iter;
99 SmallVector<std::string> LocationStrings;
100 while (Scout->first == CommonEntry) {
101 LocationStrings.push_back(
102 tooling::LocationCallFormatterCpp::format(*Iter->second));
103 if (Scout == End)
104 break;
105 ++Scout;
106 if (Scout->first == CommonEntry)
107 ++Iter;
108 }
109 llvm::sort(LocationStrings);
110 for (auto &LS : LocationStrings) {
111 OS << " * \"" << LS << "\"\n";
112 }
113 return Iter;
114 };
115
116 TextDiagnostic TD(OS, Ctx.getLangOpts(), &Diags.getDiagnosticOptions());
117
118 for (auto Iter = Locs.LocationAccessors.begin();
119 Iter != Locs.LocationAccessors.end(); ++Iter) {
120 if (!Iter->first.isValid())
121 continue;
122
123 TD.emitDiagnostic(FullSourceLoc(Iter->first, SM), DiagnosticsEngine::Note,
124 "source locations here", None, None);
125
126 Iter = PrintLocations(OS, Iter, Locs.LocationAccessors.end());
127 OS << '\n';
128 }
129
130 for (auto Iter = Locs.RangeAccessors.begin();
131 Iter != Locs.RangeAccessors.end(); ++Iter) {
132
133 if (!Iter->first.getBegin().isValid())
134 continue;
135
136 if (SM.getPresumedLineNumber(Iter->first.getBegin()) !=
137 SM.getPresumedLineNumber(Iter->first.getEnd()))
138 continue;
139
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);
144
145 Iter = PrintLocations(OS, Iter, Locs.RangeAccessors.end());
146 }
147 for (auto Iter = Locs.RangeAccessors.begin();
148 Iter != Locs.RangeAccessors.end(); ++Iter) {
149
150 if (!Iter->first.getBegin().isValid())
151 continue;
152
153 if (SM.getPresumedLineNumber(Iter->first.getBegin()) ==
154 SM.getPresumedLineNumber(Iter->first.getEnd()))
155 continue;
156
157 TD.emitDiagnostic(
158 FullSourceLoc(Iter->first.getBegin(), SM), DiagnosticsEngine::Note,
159 "source range " + Iter->first.printToString(SM) + " starting here...",
160 CharSourceRange::getTokenRange(Iter->first), None);
161
162 auto ColNum = SM.getPresumedColumnNumber(Iter->first.getEnd());
163 auto LastLineLoc = Iter->first.getEnd().getLocWithOffset(-(ColNum - 1));
164
165 TD.emitDiagnostic(FullSourceLoc(Iter->first.getEnd(), SM),
166 DiagnosticsEngine::Note, "... ending here",
167 CharSourceRange::getTokenRange(
168 SourceRange(LastLineLoc, Iter->first.getEnd())),
169 None);
170
171 Iter = PrintLocations(OS, Iter, Locs.RangeAccessors.end());
172 }
173 OS << "\n";
174}
175
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19176} // namespace
Peter Collingbourne8b1265b2013-11-08 00:08:23177
178bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
179 unsigned MatchCount = 0;
180
David Blaikie35013fa2014-04-25 15:21:43181 for (auto &AST : QS.ASTs) {
Peter Collingbourne8b1265b2013-11-08 00:08:23182 MatchFinder Finder;
183 std::vector<BoundNodes> Matches;
184 DynTypedMatcher MaybeBoundMatcher = Matcher;
185 if (QS.BindRoot) {
186 llvm::Optional<DynTypedMatcher> M = Matcher.tryBind("root");
187 if (M)
188 MaybeBoundMatcher = *M;
189 }
190 CollectBoundNodes Collect(Matches);
191 if (!Finder.addDynamicMatcher(MaybeBoundMatcher, &Collect)) {
192 OS << "Not a valid top-level matcher.\n";
193 return false;
194 }
Stephen Kelly10f0f982019-12-29 19:26:11195
Stephen Kelly8d018c72020-12-12 18:39:49196 auto &Ctx = AST->getASTContext();
197 const auto &SM = Ctx.getSourceManager();
198 Ctx.getParentMapContext().setTraversalKind(QS.TK);
199 Finder.matchAST(Ctx);
Peter Collingbourne8b1265b2013-11-08 00:08:23200
Stephen Kelly4a5b01d2018-10-20 09:13:59201 if (QS.PrintMatcher) {
Stephen Kellyf0722332019-12-18 22:35:46202 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);
208 }
209 unsigned MaxLength = FirstLine.size();
210 std::string PrefixText = "Matcher: ";
211 OS << "\n " << PrefixText << FirstLine;
212
213 for (auto Line : Lines) {
214 OS << "\n" << std::string(PrefixText.size() + 2, ' ') << Line;
215 MaxLength = std::max<int>(MaxLength, Line.rtrim().size());
216 }
217
218 OS << "\n"
219 << " " << std::string(PrefixText.size() + MaxLength, '=') << "\n\n";
Stephen Kelly4a5b01d2018-10-20 09:13:59220 }
221
Piotr Padlewski08124b12016-12-14 15:29:23222 for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
Peter Collingbourne8b1265b2013-11-08 00:08:23223 OS << "\nMatch #" << ++MatchCount << ":\n\n";
224
Piotr Padlewski08124b12016-12-14 15:29:23225 for (auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;
226 ++BI) {
Stephen Kelly70d77172018-10-24 20:33:55227 if (QS.DiagOutput) {
Peter Collingbourne8b1265b2013-11-08 00:08:23228 clang::SourceRange R = BI->second.getSourceRange();
229 if (R.isValid()) {
230 TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
231 &AST->getDiagnostics().getDiagnosticOptions());
Peter Smithd34a65d2017-06-27 10:04:04232 TD.emitDiagnostic(
233 FullSourceLoc(R.getBegin(), AST->getSourceManager()),
234 DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here",
235 CharSourceRange::getTokenRange(R), None);
Peter Collingbourne8b1265b2013-11-08 00:08:23236 }
Peter Collingbourne8b1265b2013-11-08 00:08:23237 }
Stephen Kelly70d77172018-10-24 20:33:55238 if (QS.PrintOutput) {
Peter Collingbourne8b1265b2013-11-08 00:08:23239 OS << "Binding for \"" << BI->first << "\":\n";
240 BI->second.print(OS, AST->getASTContext().getPrintingPolicy());
241 OS << "\n";
Peter Collingbourne8b1265b2013-11-08 00:08:23242 }
Stephen Kelly70d77172018-10-24 20:33:55243 if (QS.DetailedASTOutput) {
Peter Collingbourne8b1265b2013-11-08 00:08:23244 OS << "Binding for \"" << BI->first << "\":\n";
Stephen Kellyb22d8ae2019-12-06 23:26:59245 const ASTContext &Ctx = AST->getASTContext();
Tom Ritterd6d36ba2021-01-25 13:06:54246 ASTDumper Dumper(OS, Ctx, AST->getDiagnostics().getShowColors());
Stephen Kelly10f0f982019-12-29 19:26:11247 Dumper.SetTraversalKind(QS.TK);
Stephen Kellyb22d8ae2019-12-06 23:26:59248 Dumper.Visit(BI->second);
Peter Collingbourne8b1265b2013-11-08 00:08:23249 OS << "\n";
Peter Collingbourne8b1265b2013-11-08 00:08:23250 }
Stephen Kelly8d018c72020-12-12 18:39:49251 if (QS.SrcLocOutput) {
252 OS << "\n \"" << BI->first << "\" Source locations\n";
253 OS << " " << std::string(19 + BI->first.size(), '-') << '\n';
254
255 dumpLocations(OS, BI->second, Ctx, AST->getDiagnostics(), SM);
256 OS << "\n";
257 }
Peter Collingbourne8b1265b2013-11-08 00:08:23258 }
259
260 if (MI->getMap().empty())
261 OS << "No bindings.\n";
262 }
263 }
264
265 OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");
266 return true;
267}
268
Samuel Benzaquen1f6066c2014-04-23 14:04:52269bool LetQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
270 if (Value) {
271 QS.NamedValues[Name] = Value;
272 } else {
273 QS.NamedValues.erase(Name);
274 }
275 return true;
276}
277
Peter Collingbournec7ae6102013-11-08 08:54:53278#ifndef _MSC_VER
279const QueryKind SetQueryKind<bool>::value;
280const QueryKind SetQueryKind<OutputKind>::value;
281#endif
282
Peter Collingbourne8b1265b2013-11-08 00:08:23283} // namespace query
284} // namespace clang