/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Transformer/RangeSelector.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- RangeSelector.cpp - RangeSelector implementations ------*- C++ -*-===// |
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 "clang/Tooling/Transformer/RangeSelector.h" |
10 | | #include "clang/AST/Expr.h" |
11 | | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | | #include "clang/Basic/SourceLocation.h" |
13 | | #include "clang/Lex/Lexer.h" |
14 | | #include "clang/Tooling/Transformer/SourceCode.h" |
15 | | #include "llvm/ADT/StringRef.h" |
16 | | #include "llvm/Support/Errc.h" |
17 | | #include "llvm/Support/Error.h" |
18 | | #include <string> |
19 | | #include <utility> |
20 | | #include <vector> |
21 | | |
22 | | using namespace clang; |
23 | | using namespace transformer; |
24 | | |
25 | | using ast_matchers::MatchFinder; |
26 | | using llvm::Error; |
27 | | using llvm::StringError; |
28 | | |
29 | | using MatchResult = MatchFinder::MatchResult; |
30 | | |
31 | 13 | static Error invalidArgumentError(Twine Message) { |
32 | 13 | return llvm::make_error<StringError>(llvm::errc::invalid_argument, Message); |
33 | 13 | } |
34 | | |
35 | 3 | static Error typeError(StringRef ID, const ASTNodeKind &Kind) { |
36 | 3 | return invalidArgumentError("mismatched type (node id=" + ID + |
37 | 3 | " kind=" + Kind.asStringRef() + ")"); |
38 | 3 | } |
39 | | |
40 | | static Error typeError(StringRef ID, const ASTNodeKind &Kind, |
41 | 1 | Twine ExpectedType) { |
42 | 1 | return invalidArgumentError("mismatched type: expected one of " + |
43 | 1 | ExpectedType + " (node id=" + ID + |
44 | 1 | " kind=" + Kind.asStringRef() + ")"); |
45 | 1 | } |
46 | | |
47 | | static Error missingPropertyError(StringRef ID, Twine Description, |
48 | 3 | StringRef Property) { |
49 | 3 | return invalidArgumentError(Description + " requires property '" + Property + |
50 | 3 | "' (node id=" + ID + ")"); |
51 | 3 | } |
52 | | |
53 | | static Expected<DynTypedNode> getNode(const ast_matchers::BoundNodes &Nodes, |
54 | 244 | StringRef ID) { |
55 | 244 | auto &NodesMap = Nodes.getMap(); |
56 | 244 | auto It = NodesMap.find(ID); |
57 | 244 | if (It == NodesMap.end()) |
58 | 6 | return invalidArgumentError("ID not bound: " + ID); |
59 | 238 | return It->second; |
60 | 238 | } |
61 | | |
62 | | // FIXME: handling of macros should be configurable. |
63 | | static SourceLocation findPreviousTokenStart(SourceLocation Start, |
64 | | const SourceManager &SM, |
65 | 24 | const LangOptions &LangOpts) { |
66 | 24 | if (Start.isInvalid() || Start.isMacroID()) |
67 | 0 | return SourceLocation(); |
68 | | |
69 | 24 | SourceLocation BeforeStart = Start.getLocWithOffset(-1); |
70 | 24 | if (BeforeStart.isInvalid() || BeforeStart.isMacroID()) |
71 | 0 | return SourceLocation(); |
72 | | |
73 | 24 | return Lexer::GetBeginningOfToken(BeforeStart, SM, LangOpts); |
74 | 24 | } |
75 | | |
76 | | // Finds the start location of the previous token of kind \p TK. |
77 | | // FIXME: handling of macros should be configurable. |
78 | | static SourceLocation findPreviousTokenKind(SourceLocation Start, |
79 | | const SourceManager &SM, |
80 | | const LangOptions &LangOpts, |
81 | 7 | tok::TokenKind TK) { |
82 | 24 | while (true) { |
83 | 24 | SourceLocation L = findPreviousTokenStart(Start, SM, LangOpts); |
84 | 24 | if (L.isInvalid() || L.isMacroID()) |
85 | 0 | return SourceLocation(); |
86 | | |
87 | 24 | Token T; |
88 | 24 | if (Lexer::getRawToken(L, T, SM, LangOpts, /*IgnoreWhiteSpace=*/true)) |
89 | 0 | return SourceLocation(); |
90 | | |
91 | 24 | if (T.is(TK)) |
92 | 7 | return T.getLocation(); |
93 | | |
94 | 17 | Start = L; |
95 | 17 | } |
96 | 7 | } |
97 | | |
98 | | static SourceLocation findOpenParen(const CallExpr &E, const SourceManager &SM, |
99 | 7 | const LangOptions &LangOpts) { |
100 | 7 | SourceLocation EndLoc = |
101 | 5 | E.getNumArgs() == 0 ? E.getRParenLoc()2 : E.getArg(0)->getBeginLoc(); |
102 | 7 | return findPreviousTokenKind(EndLoc, SM, LangOpts, tok::TokenKind::l_paren); |
103 | 7 | } |
104 | | |
105 | 8 | RangeSelector transformer::before(RangeSelector Selector) { |
106 | 11 | return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> { |
107 | 11 | Expected<CharSourceRange> SelectedRange = Selector(Result); |
108 | 11 | if (!SelectedRange) |
109 | 0 | return SelectedRange.takeError(); |
110 | 11 | return CharSourceRange::getCharRange(SelectedRange->getBegin()); |
111 | 11 | }; |
112 | 8 | } |
113 | | |
114 | 10 | RangeSelector transformer::after(RangeSelector Selector) { |
115 | 10 | return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> { |
116 | 10 | Expected<CharSourceRange> SelectedRange = Selector(Result); |
117 | 10 | if (!SelectedRange) |
118 | 0 | return SelectedRange.takeError(); |
119 | 10 | SourceLocation End = SelectedRange->getEnd(); |
120 | 10 | if (SelectedRange->isTokenRange()) { |
121 | | // We need to find the actual (exclusive) end location from which to |
122 | | // create a new source range. However, that's not guaranteed to be valid, |
123 | | // even if the token location itself is valid. So, we create a token range |
124 | | // consisting only of the last token, then map that range back to the |
125 | | // source file. If that succeeds, we have a valid location for the end of |
126 | | // the generated range. |
127 | 9 | CharSourceRange Range = Lexer::makeFileCharRange( |
128 | 9 | CharSourceRange::getTokenRange(SelectedRange->getEnd()), |
129 | 9 | *Result.SourceManager, Result.Context->getLangOpts()); |
130 | 9 | if (Range.isInvalid()) |
131 | 0 | return invalidArgumentError( |
132 | 0 | "after: can't resolve sub-range to valid source range"); |
133 | 9 | End = Range.getEnd(); |
134 | 9 | } |
135 | | |
136 | 10 | return CharSourceRange::getCharRange(End); |
137 | 10 | }; |
138 | 10 | } |
139 | | |
140 | 107 | RangeSelector transformer::node(std::string ID) { |
141 | 166 | return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { |
142 | 166 | Expected<DynTypedNode> Node = getNode(Result.Nodes, ID); |
143 | 166 | if (!Node) |
144 | 2 | return Node.takeError(); |
145 | 164 | return (Node->get<Decl>() != nullptr || |
146 | 136 | (Node->get<Stmt>() != nullptr && Node->get<Expr>() == nullptr132 )) |
147 | 35 | ? tooling::getExtendedRange(*Node, tok::TokenKind::semi, |
148 | 35 | *Result.Context) |
149 | 129 | : CharSourceRange::getTokenRange(Node->getSourceRange()); |
150 | 164 | }; |
151 | 107 | } |
152 | | |
153 | 16 | RangeSelector transformer::statement(std::string ID) { |
154 | 16 | return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { |
155 | 16 | Expected<DynTypedNode> Node = getNode(Result.Nodes, ID); |
156 | 16 | if (!Node) |
157 | 0 | return Node.takeError(); |
158 | 16 | return tooling::getExtendedRange(*Node, tok::TokenKind::semi, |
159 | 16 | *Result.Context); |
160 | 16 | }; |
161 | 16 | } |
162 | | |
163 | 10 | RangeSelector transformer::enclose(RangeSelector Begin, RangeSelector End) { |
164 | 22 | return [Begin, End](const MatchResult &Result) -> Expected<CharSourceRange> { |
165 | 22 | Expected<CharSourceRange> BeginRange = Begin(Result); |
166 | 22 | if (!BeginRange) |
167 | 0 | return BeginRange.takeError(); |
168 | 22 | Expected<CharSourceRange> EndRange = End(Result); |
169 | 22 | if (!EndRange) |
170 | 0 | return EndRange.takeError(); |
171 | 22 | SourceLocation B = BeginRange->getBegin(); |
172 | 22 | SourceLocation E = EndRange->getEnd(); |
173 | | // Note: we are precluding the possibility of sub-token ranges in the case |
174 | | // that EndRange is a token range. |
175 | 22 | if (Result.SourceManager->isBeforeInTranslationUnit(E, B)) { |
176 | 0 | return invalidArgumentError("Bad range: out of order"); |
177 | 0 | } |
178 | 22 | return CharSourceRange(SourceRange(B, E), EndRange->isTokenRange()); |
179 | 22 | }; |
180 | 10 | } |
181 | | |
182 | | RangeSelector transformer::encloseNodes(std::string BeginID, |
183 | 3 | std::string EndID) { |
184 | 3 | return transformer::enclose(node(std::move(BeginID)), node(std::move(EndID))); |
185 | 3 | } |
186 | | |
187 | 7 | RangeSelector transformer::member(std::string ID) { |
188 | 8 | return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { |
189 | 8 | Expected<DynTypedNode> Node = getNode(Result.Nodes, ID); |
190 | 8 | if (!Node) |
191 | 0 | return Node.takeError(); |
192 | 8 | if (auto *M = Node->get<clang::MemberExpr>()) |
193 | 8 | return CharSourceRange::getTokenRange( |
194 | 8 | M->getMemberNameInfo().getSourceRange()); |
195 | 0 | return typeError(ID, Node->getNodeKind(), "MemberExpr"); |
196 | 0 | }; |
197 | 7 | } |
198 | | |
199 | 17 | RangeSelector transformer::name(std::string ID) { |
200 | 35 | return [ID](const MatchResult &Result) -> Expected<CharSourceRange> { |
201 | 35 | Expected<DynTypedNode> N = getNode(Result.Nodes, ID); |
202 | 35 | if (!N) |
203 | 1 | return N.takeError(); |
204 | 34 | auto &Node = *N; |
205 | 34 | if (const auto *D = Node.get<NamedDecl>()) { |
206 | 23 | if (!D->getDeclName().isIdentifier()) |
207 | 0 | return missingPropertyError(ID, "name", "identifier"); |
208 | 23 | SourceLocation L = D->getLocation(); |
209 | 23 | auto R = CharSourceRange::getTokenRange(L, L); |
210 | | // Verify that the range covers exactly the name. |
211 | | // FIXME: extend this code to support cases like `operator +` or |
212 | | // `foo<int>` for which this range will be too short. Doing so will |
213 | | // require subcasing `NamedDecl`, because it doesn't provide virtual |
214 | | // access to the \c DeclarationNameInfo. |
215 | 23 | if (tooling::getText(R, *Result.Context) != D->getName()) |
216 | 2 | return CharSourceRange(); |
217 | 21 | return R; |
218 | 21 | } |
219 | 11 | if (const auto *E = Node.get<DeclRefExpr>()) { |
220 | 9 | if (!E->getNameInfo().getName().isIdentifier()) |
221 | 3 | return missingPropertyError(ID, "name", "identifier"); |
222 | 6 | SourceLocation L = E->getLocation(); |
223 | 6 | return CharSourceRange::getTokenRange(L, L); |
224 | 6 | } |
225 | 2 | if (const auto *I = Node.get<CXXCtorInitializer>()) { |
226 | 1 | if (!I->isMemberInitializer() && I->isWritten()0 ) |
227 | 0 | return missingPropertyError(ID, "name", "explicit member initializer"); |
228 | 1 | SourceLocation L = I->getMemberLocation(); |
229 | 1 | return CharSourceRange::getTokenRange(L, L); |
230 | 1 | } |
231 | 1 | return typeError(ID, Node.getNodeKind(), |
232 | 1 | "DeclRefExpr, NamedDecl, CXXCtorInitializer"); |
233 | 1 | }; |
234 | 17 | } |
235 | | |
236 | | namespace { |
237 | | // FIXME: make this available in the public API for users to easily create their |
238 | | // own selectors. |
239 | | |
240 | | // Creates a selector from a range-selection function \p Func, which selects a |
241 | | // range that is relative to a bound node id. \c T is the node type expected by |
242 | | // \p Func. |
243 | | template <typename T, CharSourceRange (*Func)(const MatchResult &, const T &)> |
244 | | class RelativeSelector { |
245 | | std::string ID; |
246 | | |
247 | | public: |
248 | 19 | RelativeSelector(std::string ID) : ID(std::move(ID)) {} RangeSelector.cpp:(anonymous namespace)::RelativeSelector<clang::CompoundStmt, &((anonymous namespace)::getStatementsRange(clang::ast_matchers::MatchFinder::MatchResult const&, clang::CompoundStmt const&))>::RelativeSelector(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) Line | Count | Source | 248 | 4 | RelativeSelector(std::string ID) : ID(std::move(ID)) {} |
RangeSelector.cpp:(anonymous namespace)::RelativeSelector<clang::CallExpr, &((anonymous namespace)::getCallArgumentsRange(clang::ast_matchers::MatchFinder::MatchResult const&, clang::CallExpr const&))>::RelativeSelector(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) Line | Count | Source | 248 | 9 | RelativeSelector(std::string ID) : ID(std::move(ID)) {} |
RangeSelector.cpp:(anonymous namespace)::RelativeSelector<clang::InitListExpr, &((anonymous namespace)::getElementsRange(clang::ast_matchers::MatchFinder::MatchResult const&, clang::InitListExpr const&))>::RelativeSelector(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) Line | Count | Source | 248 | 4 | RelativeSelector(std::string ID) : ID(std::move(ID)) {} |
RangeSelector.cpp:(anonymous namespace)::RelativeSelector<clang::IfStmt, &((anonymous namespace)::getElseRange(clang::ast_matchers::MatchFinder::MatchResult const&, clang::IfStmt const&))>::RelativeSelector(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) Line | Count | Source | 248 | 2 | RelativeSelector(std::string ID) : ID(std::move(ID)) {} |
|
249 | | |
250 | 19 | Expected<CharSourceRange> operator()(const MatchResult &Result) { |
251 | 19 | Expected<DynTypedNode> N = getNode(Result.Nodes, ID); |
252 | 19 | if (!N) |
253 | 3 | return N.takeError(); |
254 | 16 | if (const auto *Arg = N->get<T>()) |
255 | 13 | return Func(Result, *Arg); |
256 | 3 | return typeError(ID, N->getNodeKind()); |
257 | 3 | } RangeSelector.cpp:(anonymous namespace)::RelativeSelector<clang::CompoundStmt, &((anonymous namespace)::getStatementsRange(clang::ast_matchers::MatchFinder::MatchResult const&, clang::CompoundStmt const&))>::operator()(clang::ast_matchers::MatchFinder::MatchResult const&) Line | Count | Source | 250 | 4 | Expected<CharSourceRange> operator()(const MatchResult &Result) { | 251 | 4 | Expected<DynTypedNode> N = getNode(Result.Nodes, ID); | 252 | 4 | if (!N) | 253 | 1 | return N.takeError(); | 254 | 3 | if (const auto *Arg = N->get<T>()) | 255 | 2 | return Func(Result, *Arg); | 256 | 1 | return typeError(ID, N->getNodeKind()); | 257 | 1 | } |
RangeSelector.cpp:(anonymous namespace)::RelativeSelector<clang::CallExpr, &((anonymous namespace)::getCallArgumentsRange(clang::ast_matchers::MatchFinder::MatchResult const&, clang::CallExpr const&))>::operator()(clang::ast_matchers::MatchFinder::MatchResult const&) Line | Count | Source | 250 | 9 | Expected<CharSourceRange> operator()(const MatchResult &Result) { | 251 | 9 | Expected<DynTypedNode> N = getNode(Result.Nodes, ID); | 252 | 9 | if (!N) | 253 | 1 | return N.takeError(); | 254 | 8 | if (const auto *Arg = N->get<T>()) | 255 | 7 | return Func(Result, *Arg); | 256 | 1 | return typeError(ID, N->getNodeKind()); | 257 | 1 | } |
RangeSelector.cpp:(anonymous namespace)::RelativeSelector<clang::InitListExpr, &((anonymous namespace)::getElementsRange(clang::ast_matchers::MatchFinder::MatchResult const&, clang::InitListExpr const&))>::operator()(clang::ast_matchers::MatchFinder::MatchResult const&) Line | Count | Source | 250 | 4 | Expected<CharSourceRange> operator()(const MatchResult &Result) { | 251 | 4 | Expected<DynTypedNode> N = getNode(Result.Nodes, ID); | 252 | 4 | if (!N) | 253 | 1 | return N.takeError(); | 254 | 3 | if (const auto *Arg = N->get<T>()) | 255 | 2 | return Func(Result, *Arg); | 256 | 1 | return typeError(ID, N->getNodeKind()); | 257 | 1 | } |
RangeSelector.cpp:(anonymous namespace)::RelativeSelector<clang::IfStmt, &((anonymous namespace)::getElseRange(clang::ast_matchers::MatchFinder::MatchResult const&, clang::IfStmt const&))>::operator()(clang::ast_matchers::MatchFinder::MatchResult const&) Line | Count | Source | 250 | 2 | Expected<CharSourceRange> operator()(const MatchResult &Result) { | 251 | 2 | Expected<DynTypedNode> N = getNode(Result.Nodes, ID); | 252 | 2 | if (!N) | 253 | 0 | return N.takeError(); | 254 | 2 | if (const auto *Arg = N->get<T>()) | 255 | 2 | return Func(Result, *Arg); | 256 | 0 | return typeError(ID, N->getNodeKind()); | 257 | 0 | } |
|
258 | | }; |
259 | | } // namespace |
260 | | |
261 | | // FIXME: Change the following functions from being in an anonymous namespace |
262 | | // to static functions, after the minimum Visual C++ has _MSC_VER >= 1915 |
263 | | // (equivalent to Visual Studio 2017 v15.8 or higher). Using the anonymous |
264 | | // namespace works around a bug in earlier versions. |
265 | | namespace { |
266 | | // Returns the range of the statements (all source between the braces). |
267 | | CharSourceRange getStatementsRange(const MatchResult &, |
268 | 2 | const CompoundStmt &CS) { |
269 | 2 | return CharSourceRange::getCharRange(CS.getLBracLoc().getLocWithOffset(1), |
270 | 2 | CS.getRBracLoc()); |
271 | 2 | } |
272 | | } // namespace |
273 | | |
274 | 4 | RangeSelector transformer::statements(std::string ID) { |
275 | 4 | return RelativeSelector<CompoundStmt, getStatementsRange>(std::move(ID)); |
276 | 4 | } |
277 | | |
278 | | namespace { |
279 | | // Returns the range of the source between the call's parentheses. |
280 | | CharSourceRange getCallArgumentsRange(const MatchResult &Result, |
281 | 7 | const CallExpr &CE) { |
282 | 7 | return CharSourceRange::getCharRange( |
283 | 7 | findOpenParen(CE, *Result.SourceManager, Result.Context->getLangOpts()) |
284 | 7 | .getLocWithOffset(1), |
285 | 7 | CE.getRParenLoc()); |
286 | 7 | } |
287 | | } // namespace |
288 | | |
289 | 9 | RangeSelector transformer::callArgs(std::string ID) { |
290 | 9 | return RelativeSelector<CallExpr, getCallArgumentsRange>(std::move(ID)); |
291 | 9 | } |
292 | | |
293 | | namespace { |
294 | | // Returns the range of the elements of the initializer list. Includes all |
295 | | // source between the braces. |
296 | | CharSourceRange getElementsRange(const MatchResult &, |
297 | 2 | const InitListExpr &E) { |
298 | 2 | return CharSourceRange::getCharRange(E.getLBraceLoc().getLocWithOffset(1), |
299 | 2 | E.getRBraceLoc()); |
300 | 2 | } |
301 | | } // namespace |
302 | | |
303 | 4 | RangeSelector transformer::initListElements(std::string ID) { |
304 | 4 | return RelativeSelector<InitListExpr, getElementsRange>(std::move(ID)); |
305 | 4 | } |
306 | | |
307 | | namespace { |
308 | | // Returns the range of the else branch, including the `else` keyword. |
309 | 2 | CharSourceRange getElseRange(const MatchResult &Result, const IfStmt &S) { |
310 | 2 | return tooling::maybeExtendRange( |
311 | 2 | CharSourceRange::getTokenRange(S.getElseLoc(), S.getEndLoc()), |
312 | 2 | tok::TokenKind::semi, *Result.Context); |
313 | 2 | } |
314 | | } // namespace |
315 | | |
316 | 2 | RangeSelector transformer::elseBranch(std::string ID) { |
317 | 2 | return RelativeSelector<IfStmt, getElseRange>(std::move(ID)); |
318 | 2 | } |
319 | | |
320 | 6 | RangeSelector transformer::expansion(RangeSelector S) { |
321 | 6 | return [S](const MatchResult &Result) -> Expected<CharSourceRange> { |
322 | 6 | Expected<CharSourceRange> SRange = S(Result); |
323 | 6 | if (!SRange) |
324 | 0 | return SRange.takeError(); |
325 | 6 | return Result.SourceManager->getExpansionRange(*SRange); |
326 | 6 | }; |
327 | 6 | } |