blob: d6652f33e129d6f9fe523b1960d751939440f1e5 [file] [log] [blame]
Edwin Vaneaae33672013-07-08 12:17:37 +00001//===-- LoopConvert/LoopMatchers.cpp - Matchers for for loops -------------===//
Edwin Vanedde168b2013-01-04 18:25:18 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11/// \brief This file contains definitions of the matchers for use in migrating
12/// C++ for loops.
13///
14//===----------------------------------------------------------------------===//
Edwin Vaneaae33672013-07-08 12:17:37 +000015
Edwin Vanedde168b2013-01-04 18:25:18 +000016#include "LoopMatchers.h"
17
18using namespace clang::ast_matchers;
19using namespace clang;
20
21const char LoopName[] = "forLoop";
22const char ConditionBoundName[] = "conditionBound";
23const char ConditionVarName[] = "conditionVar";
24const char IncrementVarName[] = "incrementVar";
25const char InitVarName[] = "initVar";
Ariel J. Bernal34290282013-05-09 17:46:20 +000026const char BeginCallName[] = "beginCall";
Edwin Vanedde168b2013-01-04 18:25:18 +000027const char EndCallName[] = "endCall";
28const char ConditionEndVarName[] = "conditionEndVar";
29const char EndVarName[] = "endVar";
Edwin Vane4f05d712013-03-07 16:22:05 +000030const char DerefByValueResultName[] = "derefByValueResult";
Ariel J. Bernal34290282013-05-09 17:46:20 +000031const char DerefByRefResultName[] = "derefByRefResult";
Edwin Vanedde168b2013-01-04 18:25:18 +000032
33// shared matchers
34static const TypeMatcher AnyType = anything();
35
36static const StatementMatcher IntegerComparisonMatcher =
37 expr(ignoringParenImpCasts(declRefExpr(to(
38 varDecl(hasType(isInteger())).bind(ConditionVarName)))));
39
40static const DeclarationMatcher InitToZeroMatcher =
41 varDecl(hasInitializer(ignoringParenImpCasts(
42 integerLiteral(equals(0))))).bind(InitVarName);
43
44static const StatementMatcher IncrementVarMatcher =
45 declRefExpr(to(
46 varDecl(hasType(isInteger())).bind(IncrementVarName)));
47
48// FIXME: How best to document complicated matcher expressions? They're fairly
49// self-documenting...but there may be some unintuitive parts.
50
51/// \brief The matcher for loops over arrays.
52///
53/// In this general example, assuming 'j' and 'k' are of integral type:
54/// \code
55/// for (int i = 0; j < 3 + 2; ++k) { ... }
56/// \endcode
Alp Toker9a5134e2013-12-01 05:08:12 +000057/// The following string identifiers are bound to these parts of the AST:
Edwin Vanedde168b2013-01-04 18:25:18 +000058/// ConditionVarName: 'j' (as a VarDecl)
59/// ConditionBoundName: '3 + 2' (as an Expr)
60/// InitVarName: 'i' (as a VarDecl)
61/// IncrementVarName: 'k' (as a VarDecl)
62/// LoopName: The entire for loop (as a ForStmt)
63///
64/// Client code will need to make sure that:
65/// - The three index variables identified by the matcher are the same
66/// VarDecl.
67/// - The index variable is only used as an array index.
68/// - All arrays indexed by the loop are the same.
69StatementMatcher makeArrayLoopMatcher() {
70 StatementMatcher ArrayBoundMatcher =
71 expr(hasType(isInteger())).bind(ConditionBoundName);
72
73 return forStmt(
74 hasLoopInit(declStmt(hasSingleDecl(InitToZeroMatcher))),
75 hasCondition(anyOf(binaryOperator(hasOperatorName("<"),
76 hasLHS(IntegerComparisonMatcher),
77 hasRHS(ArrayBoundMatcher)),
78 binaryOperator(hasOperatorName(">"),
79 hasLHS(ArrayBoundMatcher),
80 hasRHS(IntegerComparisonMatcher)))),
81 hasIncrement(unaryOperator(hasOperatorName("++"),
82 hasUnaryOperand(IncrementVarMatcher))))
83 .bind(LoopName);
84}
85
86/// \brief The matcher used for iterator-based for loops.
87///
88/// This matcher is more flexible than array-based loops. It will match
89/// catch loops of the following textual forms (regardless of whether the
90/// iterator type is actually a pointer type or a class type):
91///
92/// Assuming f, g, and h are of type containerType::iterator,
93/// \code
94/// for (containerType::iterator it = container.begin(),
95/// e = createIterator(); f != g; ++h) { ... }
96/// for (containerType::iterator it = container.begin();
97/// f != anotherContainer.end(); ++h) { ... }
98/// \endcode
99/// The following string identifiers are bound to the parts of the AST:
100/// InitVarName: 'it' (as a VarDecl)
101/// ConditionVarName: 'f' (as a VarDecl)
102/// LoopName: The entire for loop (as a ForStmt)
103/// In the first example only:
104/// EndVarName: 'e' (as a VarDecl)
105/// ConditionEndVarName: 'g' (as a VarDecl)
106/// In the second example only:
107/// EndCallName: 'container.end()' (as a CXXMemberCallExpr)
108///
109/// Client code will need to make sure that:
110/// - The iterator variables 'it', 'f', and 'h' are the same
111/// - The two containers on which 'begin' and 'end' are called are the same
112/// - If the end iterator variable 'g' is defined, it is the same as 'f'
113StatementMatcher makeIteratorLoopMatcher() {
114 StatementMatcher BeginCallMatcher =
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000115 cxxMemberCallExpr(
Ariel J. Bernal34290282013-05-09 17:46:20 +0000116 argumentCountIs(0),
117 callee(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000118 cxxMethodDecl(hasName("begin"))
Ariel J. Bernal34290282013-05-09 17:46:20 +0000119 )
120 ).bind(BeginCallName);
Edwin Vanedde168b2013-01-04 18:25:18 +0000121
122 DeclarationMatcher InitDeclMatcher =
Ariel J. Bernal34290282013-05-09 17:46:20 +0000123 varDecl(
124 hasInitializer(
125 anyOf(
126 ignoringParenImpCasts(BeginCallMatcher),
127 materializeTemporaryExpr(ignoringParenImpCasts(BeginCallMatcher)),
128 hasDescendant(BeginCallMatcher)
129 )
130 )
131 ).bind(InitVarName);
Edwin Vanedde168b2013-01-04 18:25:18 +0000132
133 DeclarationMatcher EndDeclMatcher =
134 varDecl(hasInitializer(anything())).bind(EndVarName);
135
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000136 StatementMatcher EndCallMatcher = cxxMemberCallExpr(
137 argumentCountIs(0), callee(cxxMethodDecl(hasName("end"))));
Edwin Vanedde168b2013-01-04 18:25:18 +0000138
139 StatementMatcher IteratorBoundMatcher =
140 expr(anyOf(ignoringParenImpCasts(declRefExpr(to(
141 varDecl().bind(ConditionEndVarName)))),
142 ignoringParenImpCasts(
143 expr(EndCallMatcher).bind(EndCallName)),
144 materializeTemporaryExpr(ignoringParenImpCasts(
145 expr(EndCallMatcher).bind(EndCallName)))));
146
147 StatementMatcher IteratorComparisonMatcher =
148 expr(ignoringParenImpCasts(declRefExpr(to(
149 varDecl().bind(ConditionVarName)))));
150
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000151 StatementMatcher OverloadedNEQMatcher = cxxOperatorCallExpr(
Edwin Vanedde168b2013-01-04 18:25:18 +0000152 hasOverloadedOperatorName("!="),
153 argumentCountIs(2),
154 hasArgument(0, IteratorComparisonMatcher),
155 hasArgument(1, IteratorBoundMatcher));
156
Edwin Vane4f05d712013-03-07 16:22:05 +0000157 // This matcher tests that a declaration is a CXXRecordDecl that has an
158 // overloaded operator*(). If the operator*() returns by value instead of by
159 // reference then the return type is tagged with DerefByValueResultName.
160 internal::Matcher<VarDecl> TestDerefReturnsByValue =
161 hasType(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000162 cxxRecordDecl(
Edwin Vane4f05d712013-03-07 16:22:05 +0000163 hasMethod(
164 allOf(
165 hasOverloadedOperatorName("*"),
166 anyOf(
167 // Tag the return type if it's by value.
168 returns(
169 qualType(
170 unless(hasCanonicalType(referenceType()))
171 ).bind(DerefByValueResultName)
172 ),
173 returns(
174 // Skip loops where the iterator's operator* returns an
175 // rvalue reference. This is just weird.
Ariel J. Bernal34290282013-05-09 17:46:20 +0000176 qualType(
177 unless(
178 hasCanonicalType(rValueReferenceType())
179 )
180 ).bind(DerefByRefResultName)
Edwin Vane4f05d712013-03-07 16:22:05 +0000181 )
182 )
183 )
184 )
185 )
186 );
187
Ariel J. Bernal34290282013-05-09 17:46:20 +0000188
Edwin Vane4f05d712013-03-07 16:22:05 +0000189 return
190 forStmt(
Edwin Vanedde168b2013-01-04 18:25:18 +0000191 hasLoopInit(anyOf(
Edwin Vane4f05d712013-03-07 16:22:05 +0000192 declStmt(
193 declCountIs(2),
194 containsDeclaration(0, InitDeclMatcher),
195 containsDeclaration(1, EndDeclMatcher)
196 ),
197 declStmt(hasSingleDecl(InitDeclMatcher))
198 )),
Edwin Vanedde168b2013-01-04 18:25:18 +0000199 hasCondition(anyOf(
Edwin Vane4f05d712013-03-07 16:22:05 +0000200 binaryOperator(
201 hasOperatorName("!="),
202 hasLHS(IteratorComparisonMatcher),
203 hasRHS(IteratorBoundMatcher)
204 ),
205 binaryOperator(
206 hasOperatorName("!="),
207 hasLHS(IteratorBoundMatcher),
208 hasRHS(IteratorComparisonMatcher)
209 ),
210 OverloadedNEQMatcher
211 )),
Edwin Vanedde168b2013-01-04 18:25:18 +0000212 hasIncrement(anyOf(
Edwin Vane4f05d712013-03-07 16:22:05 +0000213 unaryOperator(
214 hasOperatorName("++"),
215 hasUnaryOperand(
216 declRefExpr(to(
217 varDecl(hasType(pointsTo(AnyType))).bind(IncrementVarName)
218 ))
219 )
220 ),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000221 cxxOperatorCallExpr(
Edwin Vane4f05d712013-03-07 16:22:05 +0000222 hasOverloadedOperatorName("++"),
223 hasArgument(0,
224 declRefExpr(to(
225 varDecl(TestDerefReturnsByValue).bind(IncrementVarName)
226 ))
227 )
228 )
229 ))
230 ).bind(LoopName);
Edwin Vanedde168b2013-01-04 18:25:18 +0000231}
232
233/// \brief The matcher used for array-like containers (pseudoarrays).
234///
235/// This matcher is more flexible than array-based loops. It will match
236/// loops of the following textual forms (regardless of whether the
237/// iterator type is actually a pointer type or a class type):
238///
239/// Assuming f, g, and h are of type containerType::iterator,
240/// \code
241/// for (int i = 0, j = container.size(); f < g; ++h) { ... }
242/// for (int i = 0; f < container.size(); ++h) { ... }
243/// \endcode
244/// The following string identifiers are bound to the parts of the AST:
245/// InitVarName: 'i' (as a VarDecl)
246/// ConditionVarName: 'f' (as a VarDecl)
247/// LoopName: The entire for loop (as a ForStmt)
248/// In the first example only:
249/// EndVarName: 'j' (as a VarDecl)
250/// ConditionEndVarName: 'g' (as a VarDecl)
251/// In the second example only:
252/// EndCallName: 'container.size()' (as a CXXMemberCallExpr)
253///
254/// Client code will need to make sure that:
255/// - The index variables 'i', 'f', and 'h' are the same
256/// - The containers on which 'size()' is called is the container indexed
257/// - The index variable is only used in overloaded operator[] or
258/// container.at()
259/// - If the end iterator variable 'g' is defined, it is the same as 'j'
260/// - The container's iterators would not be invalidated during the loop
261StatementMatcher makePseudoArrayLoopMatcher() {
Edwin Vaneb40bf832013-05-09 20:03:52 +0000262 // Test that the incoming type has a record declaration that has methods
263 // called 'begin' and 'end'. If the incoming type is const, then make sure
264 // these methods are also marked const.
Alp Toker9a5134e2013-12-01 05:08:12 +0000265 //
Edwin Vaneb40bf832013-05-09 20:03:52 +0000266 // FIXME: To be completely thorough this matcher should also ensure the
267 // return type of begin/end is an iterator that dereferences to the same as
268 // what operator[] or at() returns. Such a test isn't likely to fail except
269 // for pathological cases.
270 //
271 // FIXME: Also, a record doesn't necessarily need begin() and end(). Free
272 // functions called begin() and end() taking the container as an argument
273 // are also allowed.
274 TypeMatcher RecordWithBeginEnd =
275 qualType(anyOf(
276 qualType(
277 isConstQualified(),
278 hasDeclaration(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000279 cxxRecordDecl(
Edwin Vaneb40bf832013-05-09 20:03:52 +0000280 hasMethod(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000281 cxxMethodDecl(
Edwin Vaneb40bf832013-05-09 20:03:52 +0000282 hasName("begin"),
283 isConst()
284 )
285 ),
286 hasMethod(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000287 cxxMethodDecl(
Edwin Vaneb40bf832013-05-09 20:03:52 +0000288 hasName("end"),
289 isConst()
290 )
291 )
292 )
293 ) // hasDeclaration
294 ), // qualType
295 qualType(
296 unless(isConstQualified()),
297 hasDeclaration(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000298 cxxRecordDecl(
Edwin Vaneb40bf832013-05-09 20:03:52 +0000299 hasMethod(hasName("begin")),
300 hasMethod(hasName("end"))
301 )
302 )
303 ) // qualType
304 )
305 );
306
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000307 StatementMatcher SizeCallMatcher = cxxMemberCallExpr(
308 argumentCountIs(0),
309 callee(cxxMethodDecl(anyOf(hasName("size"), hasName("length")))),
310 on(anyOf(hasType(pointsTo(RecordWithBeginEnd)),
311 hasType(RecordWithBeginEnd))));
Edwin Vanedde168b2013-01-04 18:25:18 +0000312
313 StatementMatcher EndInitMatcher =
314 expr(anyOf(
315 ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)),
316 explicitCastExpr(hasSourceExpression(ignoringParenImpCasts(
317 expr(SizeCallMatcher).bind(EndCallName))))));
318
319 DeclarationMatcher EndDeclMatcher =
320 varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName);
321
322 StatementMatcher IndexBoundMatcher =
323 expr(anyOf(
324 ignoringParenImpCasts(declRefExpr(to(
325 varDecl(hasType(isInteger())).bind(ConditionEndVarName)))),
326 EndInitMatcher));
327
328 return forStmt(
329 hasLoopInit(anyOf(
330 declStmt(declCountIs(2),
331 containsDeclaration(0, InitToZeroMatcher),
332 containsDeclaration(1, EndDeclMatcher)),
333 declStmt(hasSingleDecl(InitToZeroMatcher)))),
334 hasCondition(anyOf(
335 binaryOperator(hasOperatorName("<"),
336 hasLHS(IntegerComparisonMatcher),
337 hasRHS(IndexBoundMatcher)),
338 binaryOperator(hasOperatorName(">"),
339 hasLHS(IndexBoundMatcher),
340 hasRHS(IntegerComparisonMatcher)))),
341 hasIncrement(unaryOperator(
342 hasOperatorName("++"),
343 hasUnaryOperand(IncrementVarMatcher))))
344 .bind(LoopName);
345}