clang-tools 20.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 "QueryParser.h"
11#include "QuerySession.h"
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"
17#include <optional>
18
19using namespace clang::ast_matchers;
20using namespace clang::ast_matchers::dynamic;
21
22namespace clang {
23namespace query {
24
26
27bool InvalidQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
28 OS << ErrStr << "\n";
29 return false;
30}
31
32bool NoOpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
33 return true;
34}
35
36bool HelpQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
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"
42 " "
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"
52 " AsIs "
53 "Print and match the AST as clang sees it. This mode is the "
54 "default.\n"
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"
63 " quit, q "
64 "Terminates the query session.\n\n"
65 "Several commands accept a <feature> parameter. The available features "
66 "are:\n\n"
67 " print "
68 "Pretty-print bound nodes.\n"
69 " diag "
70 "Diagnostic location for bound nodes.\n"
71 " detailed-ast "
72 "Detailed AST output for bound nodes.\n"
73 " dump "
74 "Detailed AST output for bound nodes (alias of detailed-ast).\n\n";
75 return true;
76}
77
78bool QuitQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
79 QS.Terminate = true;
80 return true;
81}
82
83namespace {
84
85struct CollectBoundNodes : MatchFinder::MatchCallback {
86 std::vector<BoundNodes> &Bindings;
87 StringRef Unit;
88 CollectBoundNodes(std::vector<BoundNodes> &Bindings, StringRef Unit)
90 void run(const MatchFinder::MatchResult &Result) override {
91 Bindings.push_back(Result.Nodes);
92 }
93 StringRef getID() const override { return Unit; }
94};
95
96struct QueryProfiler {
97 llvm::StringMap<llvm::TimeRecord> Records;
98
99 ~QueryProfiler() {
100 llvm::TimerGroup TG("clang-query", "clang-query matcher profiling",
101 Records);
102 TG.print(llvm::errs());
103 llvm::errs().flush();
104 }
105};
106
107} // namespace
108
109bool MatchQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
110 unsigned MatchCount = 0;
111
112 std::optional<QueryProfiler> Profiler;
113 if (QS.EnableProfile)
114 Profiler.emplace();
115
116 for (auto &AST : QS.ASTs) {
117 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
118 std::optional<llvm::StringMap<llvm::TimeRecord>> Records;
119 if (QS.EnableProfile) {
120 Records.emplace();
121 FinderOptions.CheckProfiling.emplace(*Records);
122 }
123
124 MatchFinder Finder(FinderOptions);
125 std::vector<BoundNodes> Matches;
126 DynTypedMatcher MaybeBoundMatcher = Matcher;
127 if (QS.BindRoot) {
128 std::optional<DynTypedMatcher> M = Matcher.tryBind("root");
129 if (M)
130 MaybeBoundMatcher = *M;
131 }
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";
136 return false;
137 }
138
139 ASTContext &Ctx = AST->getASTContext();
140 Ctx.getParentMapContext().setTraversalKind(QS.TK);
141 Finder.matchAST(Ctx);
142 if (QS.EnableProfile)
143 Profiler->Records[OrigSrcName] += (*Records)[OrigSrcName];
144
145 if (QS.PrintMatcher) {
146 SmallVector<StringRef, 4> Lines;
147 Source.split(Lines, "\n");
148 auto FirstLine = Lines[0];
149 Lines.erase(Lines.begin(), Lines.begin() + 1);
150 while (!Lines.empty() && Lines.back().empty()) {
151 Lines.resize(Lines.size() - 1);
152 }
153 unsigned MaxLength = FirstLine.size();
154 std::string PrefixText = "Matcher: ";
155 OS << "\n " << PrefixText << FirstLine;
156
157 for (auto Line : Lines) {
158 OS << "\n" << std::string(PrefixText.size() + 2, ' ') << Line;
159 MaxLength = std::max<int>(MaxLength, Line.rtrim().size());
160 }
161
162 OS << "\n"
163 << " " << std::string(PrefixText.size() + MaxLength, '=') << "\n\n";
164 }
165
166 for (auto MI = Matches.begin(), ME = Matches.end(); MI != ME; ++MI) {
167 OS << "\nMatch #" << ++MatchCount << ":\n\n";
168
169 for (auto BI = MI->getMap().begin(), BE = MI->getMap().end(); BI != BE;
170 ++BI) {
171 if (QS.DiagOutput) {
172 clang::SourceRange R = BI->second.getSourceRange();
173 if (R.isValid()) {
174 TextDiagnostic TD(OS, AST->getASTContext().getLangOpts(),
175 &AST->getDiagnostics().getDiagnosticOptions());
176 TD.emitDiagnostic(
177 FullSourceLoc(R.getBegin(), AST->getSourceManager()),
178 DiagnosticsEngine::Note, "\"" + BI->first + "\" binds here",
179 CharSourceRange::getTokenRange(R), {});
180 }
181 }
182 if (QS.PrintOutput) {
183 OS << "Binding for \"" << BI->first << "\":\n";
184 BI->second.print(OS, AST->getASTContext().getPrintingPolicy());
185 OS << "\n";
186 }
187 if (QS.DetailedASTOutput) {
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);
192 OS << "\n";
193 }
194 }
195
196 if (MI->getMap().empty())
197 OS << "No bindings.\n";
198 }
199 }
200
201 OS << MatchCount << (MatchCount == 1 ? " match.\n" : " matches.\n");
202 return true;
203}
204
205bool LetQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
206 if (Value) {
207 QS.NamedValues[Name] = Value;
208 } else {
209 QS.NamedValues.erase(Name);
210 }
211 return true;
212}
213
214#ifndef _MSC_VER
217#endif
218
219bool FileQuery::run(llvm::raw_ostream &OS, QuerySession &QS) const {
220 auto Buffer = llvm::MemoryBuffer::getFile(StringRef{File}.trim());
221 if (!Buffer) {
222 if (Prefix.has_value())
223 llvm::errs() << *Prefix << ": ";
224 llvm::errs() << "cannot open " << File << ": "
225 << Buffer.getError().message() << "\n";
226 return false;
227 }
228
229 StringRef FileContentRef(Buffer.get()->getBuffer());
230
231 while (!FileContentRef.empty()) {
232 QueryRef Q = QueryParser::parse(FileContentRef, QS);
233 if (!Q->run(llvm::outs(), QS))
234 return false;
235 FileContentRef = Q->RemainingContent;
236 }
237 return true;
238}
239
240} // namespace query
241} // namespace clang
unsigned Line
Definition: Bracket.cpp:77
llvm::raw_ostream & OS
unsigned Lines
StringRef Unit
Definition: Query.cpp:87
llvm::StringMap< llvm::TimeRecord > Records
Definition: Query.cpp:97
std::vector< BoundNodes > & Bindings
Definition: Query.cpp:86
const google::protobuf::Message & M
Definition: Server.cpp:356
static QueryRef parse(StringRef Line, const QuerySession &QS)
Parse Line as a query.
Represents the state for a particular clang-query session.
Definition: QuerySession.h:24
llvm::StringMap< ast_matchers::dynamic::VariantValue > NamedValues
Definition: QuerySession.h:43
llvm::ArrayRef< std::unique_ptr< ASTUnit > > ASTs
Definition: QuerySession.h:31
llvm::IntrusiveRefCntPtr< Query > QueryRef
Definition: Query.h:52
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
-clang-tidy
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
Definition: Query.cpp:219
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
Definition: Query.cpp:36
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
Definition: Query.cpp:27
std::string ErrStr
Definition: Query.h:59
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
Definition: Query.cpp:205
ast_matchers::dynamic::VariantValue Value
Definition: Query.h:108
std::string Name
Definition: Query.h:107
StringRef Source
Definition: Query.h:97
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
Definition: Query.cpp:109
ast_matchers::dynamic::DynTypedMatcher Matcher
Definition: Query.h:95
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
Definition: Query.cpp:32
virtual ~Query()
Definition: Query.cpp:25
bool run(llvm::raw_ostream &OS, QuerySession &QS) const override
Perform the query on QS and print output to OS.
Definition: Query.cpp:78