blob: 719c2069fbe7290f67bb7dbf2c91dcc977216d3a [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
57/// The following string identifers are bound to the parts of the AST:
58/// 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 =
Ariel J. Bernal34290282013-05-09 17:46:20 +0000115 memberCallExpr(
116 argumentCountIs(0),
117 callee(
118 methodDecl(hasName("begin"))
119 )
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
136 StatementMatcher EndCallMatcher =
137 memberCallExpr(argumentCountIs(0), callee(methodDecl(hasName("end"))));
138
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
151 StatementMatcher OverloadedNEQMatcher = operatorCallExpr(
152 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(
162 recordDecl(
163 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 ),
221 operatorCallExpr(
222 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.
265 //
266 // 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(
279 recordDecl(
280 hasMethod(
281 methodDecl(
282 hasName("begin"),
283 isConst()
284 )
285 ),
286 hasMethod(
287 methodDecl(
288 hasName("end"),
289 isConst()
290 )
291 )
292 )
293 ) // hasDeclaration
294 ), // qualType
295 qualType(
296 unless(isConstQualified()),
297 hasDeclaration(
298 recordDecl(
299 hasMethod(hasName("begin")),
300 hasMethod(hasName("end"))
301 )
302 )
303 ) // qualType
304 )
305 );
306
Edwin Vanedde168b2013-01-04 18:25:18 +0000307 StatementMatcher SizeCallMatcher =
308 memberCallExpr(argumentCountIs(0),
309 callee(methodDecl(anyOf(hasName("size"),
Edwin Vaneb40bf832013-05-09 20:03:52 +0000310 hasName("length")))),
311 on(anyOf(hasType(pointsTo(RecordWithBeginEnd)),
312 hasType(RecordWithBeginEnd))));
Edwin Vanedde168b2013-01-04 18:25:18 +0000313
314 StatementMatcher EndInitMatcher =
315 expr(anyOf(
316 ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)),
317 explicitCastExpr(hasSourceExpression(ignoringParenImpCasts(
318 expr(SizeCallMatcher).bind(EndCallName))))));
319
320 DeclarationMatcher EndDeclMatcher =
321 varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName);
322
323 StatementMatcher IndexBoundMatcher =
324 expr(anyOf(
325 ignoringParenImpCasts(declRefExpr(to(
326 varDecl(hasType(isInteger())).bind(ConditionEndVarName)))),
327 EndInitMatcher));
328
329 return forStmt(
330 hasLoopInit(anyOf(
331 declStmt(declCountIs(2),
332 containsDeclaration(0, InitToZeroMatcher),
333 containsDeclaration(1, EndDeclMatcher)),
334 declStmt(hasSingleDecl(InitToZeroMatcher)))),
335 hasCondition(anyOf(
336 binaryOperator(hasOperatorName("<"),
337 hasLHS(IntegerComparisonMatcher),
338 hasRHS(IndexBoundMatcher)),
339 binaryOperator(hasOperatorName(">"),
340 hasLHS(IndexBoundMatcher),
341 hasRHS(IntegerComparisonMatcher)))),
342 hasIncrement(unaryOperator(
343 hasOperatorName("++"),
344 hasUnaryOperand(IncrementVarMatcher))))
345 .bind(LoopName);
346}