clang-tools  14.0.0git
Query.cpp
Go to the documentation of this file.
1 //===---- Query.cpp - clang-query query -----------------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Query.h"
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"
17 
18 using namespace clang::ast_matchers;
19 using namespace clang::ast_matchers::dynamic;
20 
21 namespace clang {
22 namespace query {
23 
24 Query::~Query() {}
25 
26 bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
27  OS << ErrStr << "\n";
28  return false;
29 }
30 
31 bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
32  return true;
33 }
34 
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"
41  " "
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"
49  " AsIs "
50  "Print and match the AST as clang sees it. This mode is the "
51  "default.\n"
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"
60  " quit, q "
61  "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"
68  " detailed-ast "
69  "Detailed AST output for bound nodes.\n"
70  " srcloc "
71  "Source locations and ranges for bound nodes.\n"
72  " dump "
73  "Detailed AST output for bound nodes (alias of detailed-ast).\n\n";
74  return true;
75 }
76 
77 bool QuitQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
78  QS.Terminate = true;
79  return true;
80 }
81 
82 namespace {
83 
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);
89  }
90 };
91 
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);
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 
176 } // namespace
177 
178 bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
179  unsigned MatchCount = 0;
180 
181  for (auto &AST : QS.ASTs) {
182  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  }
195 
196  auto &Ctx = AST->getASTContext();
197  const auto &SM = Ctx.getSourceManager();
198  Ctx.getParentMapContext().setTraversalKind(QS.TK);
199  Finder.matchAST(Ctx);
200 
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);
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";
220  }
221 
222  for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
223  OS << "\nMatch #" << ++MatchCount << ":\n\n";
224 
225  for (auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;
226  ++BI) {
227  if (QS.DiagOutput) {
228  clang::SourceRange R = BI->second.getSourceRange();
229  if (R.isValid()) {
230  TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
231  &AST->getDiagnostics().getDiagnosticOptions());
232  TD.emitDiagnostic(
233  FullSourceLoc(R.getBegin(), AST->getSourceManager()),
234  DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here",
235  CharSourceRange::getTokenRange(R), None);
236  }
237  }
238  if (QS.PrintOutput) {
239  OS << "Binding for \"" << BI->first << "\":\n";
240  BI->second.print(OS, AST->getASTContext().getPrintingPolicy());
241  OS << "\n";
242  }
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);
249  OS << "\n";
250  }
251  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  }
258  }
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 
269 bool 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 
278 #ifndef _MSC_VER
281 #endif
282 
283 } // namespace query
284 } // namespace clang
clang::query::QuerySession::DiagOutput
bool DiagOutput
Definition: QuerySession.h:34
clang::query::QuerySession::TK
TraversalKind TK
Definition: QuerySession.h:42
clang::query::QuerySession::SrcLocOutput
bool SrcLocOutput
Definition: QuerySession.h:36
Ctx
Context Ctx
Definition: TUScheduler.cpp:460
clang::query::QuerySession::PrintMatcher
bool PrintMatcher
Definition: QuerySession.h:39
Query.h
clang::ast_matchers
Definition: AbseilMatcher.h:14
M
const google::protobuf::Message & M
Definition: Server.cpp:309
Line
int Line
Definition: PreprocessorTracker.cpp:514
clang::query::QuerySession::PrintOutput
bool PrintOutput
Definition: QuerySession.h:33
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
clang::query::QuerySession::ASTs
llvm::ArrayRef< std::unique_ptr< ASTUnit > > ASTs
Definition: QuerySession.h:31
clang::query::QuerySession::Terminate
bool Terminate
Definition: QuerySession.h:40
clang::query::QuerySession::NamedValues
llvm::StringMap< ast_matchers::dynamic::VariantValue > NamedValues
Definition: QuerySession.h:43
clang::query::QueryKind
QueryKind
Definition: Query.h:23
clang::query::SetQueryKind
Definition: Query.h:113
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
OS
llvm::raw_string_ostream OS
Definition: TraceTests.cpp:163
Bindings
std::vector< BoundNodes > & Bindings
Definition: Query.cpp:85
Diags
CapturedDiags Diags
Definition: ConfigCompileTests.cpp:38
clang::query::QuerySession::DetailedASTOutput
bool DetailedASTOutput
Definition: QuerySession.h:35
Lines
unsigned Lines
Definition: FunctionSizeCheck.cpp:113
clang::query::QuerySession
Represents the state for a particular clang-query session.
Definition: QuerySession.h:24
clang::query::QuerySession::BindRoot
bool BindRoot
Definition: QuerySession.h:38
QuerySession.h