blob: a7ca15f3eb3ddc01385477ef1e11ac2e7f5489e3 [file] [log] [blame]
Edwin Vanedde168b2013-01-04 18:25:18 +00001//===-- LoopConvert/LoopMatchers.h - Matchers for for loops -----*- C++ -*-===//
2//
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//===----------------------------------------------------------------------===//
15#include "LoopMatchers.h"
16
17using namespace clang::ast_matchers;
18using namespace clang;
19
20const char LoopName[] = "forLoop";
21const char ConditionBoundName[] = "conditionBound";
22const char ConditionVarName[] = "conditionVar";
23const char IncrementVarName[] = "incrementVar";
24const char InitVarName[] = "initVar";
Ariel J. Bernal34290282013-05-09 17:46:20 +000025const char BeginCallName[] = "beginCall";
Edwin Vanedde168b2013-01-04 18:25:18 +000026const char EndCallName[] = "endCall";
27const char ConditionEndVarName[] = "conditionEndVar";
28const char EndVarName[] = "endVar";
Edwin Vane4f05d712013-03-07 16:22:05 +000029const char DerefByValueResultName[] = "derefByValueResult";
Ariel J. Bernal34290282013-05-09 17:46:20 +000030const char DerefByRefResultName[] = "derefByRefResult";
Edwin Vanedde168b2013-01-04 18:25:18 +000031
32// shared matchers
33static const TypeMatcher AnyType = anything();
34
35static const StatementMatcher IntegerComparisonMatcher =
36 expr(ignoringParenImpCasts(declRefExpr(to(
37 varDecl(hasType(isInteger())).bind(ConditionVarName)))));
38
39static const DeclarationMatcher InitToZeroMatcher =
40 varDecl(hasInitializer(ignoringParenImpCasts(
41 integerLiteral(equals(0))))).bind(InitVarName);
42
43static const StatementMatcher IncrementVarMatcher =
44 declRefExpr(to(
45 varDecl(hasType(isInteger())).bind(IncrementVarName)));
46
47// FIXME: How best to document complicated matcher expressions? They're fairly
48// self-documenting...but there may be some unintuitive parts.
49
50/// \brief The matcher for loops over arrays.
51///
52/// In this general example, assuming 'j' and 'k' are of integral type:
53/// \code
54/// for (int i = 0; j < 3 + 2; ++k) { ... }
55/// \endcode
56/// The following string identifers are bound to the parts of the AST:
57/// ConditionVarName: 'j' (as a VarDecl)
58/// ConditionBoundName: '3 + 2' (as an Expr)
59/// InitVarName: 'i' (as a VarDecl)
60/// IncrementVarName: 'k' (as a VarDecl)
61/// LoopName: The entire for loop (as a ForStmt)
62///
63/// Client code will need to make sure that:
64/// - The three index variables identified by the matcher are the same
65/// VarDecl.
66/// - The index variable is only used as an array index.
67/// - All arrays indexed by the loop are the same.
68StatementMatcher makeArrayLoopMatcher() {
69 StatementMatcher ArrayBoundMatcher =
70 expr(hasType(isInteger())).bind(ConditionBoundName);
71
72 return forStmt(
73 hasLoopInit(declStmt(hasSingleDecl(InitToZeroMatcher))),
74 hasCondition(anyOf(binaryOperator(hasOperatorName("<"),
75 hasLHS(IntegerComparisonMatcher),
76 hasRHS(ArrayBoundMatcher)),
77 binaryOperator(hasOperatorName(">"),
78 hasLHS(ArrayBoundMatcher),
79 hasRHS(IntegerComparisonMatcher)))),
80 hasIncrement(unaryOperator(hasOperatorName("++"),
81 hasUnaryOperand(IncrementVarMatcher))))
82 .bind(LoopName);
83}
84
85/// \brief The matcher used for iterator-based for loops.
86///
87/// This matcher is more flexible than array-based loops. It will match
88/// catch loops of the following textual forms (regardless of whether the
89/// iterator type is actually a pointer type or a class type):
90///
91/// Assuming f, g, and h are of type containerType::iterator,
92/// \code
93/// for (containerType::iterator it = container.begin(),
94/// e = createIterator(); f != g; ++h) { ... }
95/// for (containerType::iterator it = container.begin();
96/// f != anotherContainer.end(); ++h) { ... }
97/// \endcode
98/// The following string identifiers are bound to the parts of the AST:
99/// InitVarName: 'it' (as a VarDecl)
100/// ConditionVarName: 'f' (as a VarDecl)
101/// LoopName: The entire for loop (as a ForStmt)
102/// In the first example only:
103/// EndVarName: 'e' (as a VarDecl)
104/// ConditionEndVarName: 'g' (as a VarDecl)
105/// In the second example only:
106/// EndCallName: 'container.end()' (as a CXXMemberCallExpr)
107///
108/// Client code will need to make sure that:
109/// - The iterator variables 'it', 'f', and 'h' are the same
110/// - The two containers on which 'begin' and 'end' are called are the same
111/// - If the end iterator variable 'g' is defined, it is the same as 'f'
112StatementMatcher makeIteratorLoopMatcher() {
113 StatementMatcher BeginCallMatcher =
Ariel J. Bernal34290282013-05-09 17:46:20 +0000114 memberCallExpr(
115 argumentCountIs(0),
116 callee(
117 methodDecl(hasName("begin"))
118 )
119 ).bind(BeginCallName);
Edwin Vanedde168b2013-01-04 18:25:18 +0000120
121 DeclarationMatcher InitDeclMatcher =
Ariel J. Bernal34290282013-05-09 17:46:20 +0000122 varDecl(
123 hasInitializer(
124 anyOf(
125 ignoringParenImpCasts(BeginCallMatcher),
126 materializeTemporaryExpr(ignoringParenImpCasts(BeginCallMatcher)),
127 hasDescendant(BeginCallMatcher)
128 )
129 )
130 ).bind(InitVarName);
Edwin Vanedde168b2013-01-04 18:25:18 +0000131
132 DeclarationMatcher EndDeclMatcher =
133 varDecl(hasInitializer(anything())).bind(EndVarName);
134
135 StatementMatcher EndCallMatcher =
136 memberCallExpr(argumentCountIs(0), callee(methodDecl(hasName("end"))));
137
138 StatementMatcher IteratorBoundMatcher =
139 expr(anyOf(ignoringParenImpCasts(declRefExpr(to(
140 varDecl().bind(ConditionEndVarName)))),
141 ignoringParenImpCasts(
142 expr(EndCallMatcher).bind(EndCallName)),
143 materializeTemporaryExpr(ignoringParenImpCasts(
144 expr(EndCallMatcher).bind(EndCallName)))));
145
146 StatementMatcher IteratorComparisonMatcher =
147 expr(ignoringParenImpCasts(declRefExpr(to(
148 varDecl().bind(ConditionVarName)))));
149
150 StatementMatcher OverloadedNEQMatcher = operatorCallExpr(
151 hasOverloadedOperatorName("!="),
152 argumentCountIs(2),
153 hasArgument(0, IteratorComparisonMatcher),
154 hasArgument(1, IteratorBoundMatcher));
155
Edwin Vane4f05d712013-03-07 16:22:05 +0000156 // This matcher tests that a declaration is a CXXRecordDecl that has an
157 // overloaded operator*(). If the operator*() returns by value instead of by
158 // reference then the return type is tagged with DerefByValueResultName.
159 internal::Matcher<VarDecl> TestDerefReturnsByValue =
160 hasType(
161 recordDecl(
162 hasMethod(
163 allOf(
164 hasOverloadedOperatorName("*"),
165 anyOf(
166 // Tag the return type if it's by value.
167 returns(
168 qualType(
169 unless(hasCanonicalType(referenceType()))
170 ).bind(DerefByValueResultName)
171 ),
172 returns(
173 // Skip loops where the iterator's operator* returns an
174 // rvalue reference. This is just weird.
Ariel J. Bernal34290282013-05-09 17:46:20 +0000175 qualType(
176 unless(
177 hasCanonicalType(rValueReferenceType())
178 )
179 ).bind(DerefByRefResultName)
Edwin Vane4f05d712013-03-07 16:22:05 +0000180 )
181 )
182 )
183 )
184 )
185 );
186
Ariel J. Bernal34290282013-05-09 17:46:20 +0000187
Edwin Vane4f05d712013-03-07 16:22:05 +0000188 return
189 forStmt(
Edwin Vanedde168b2013-01-04 18:25:18 +0000190 hasLoopInit(anyOf(
Edwin Vane4f05d712013-03-07 16:22:05 +0000191 declStmt(
192 declCountIs(2),
193 containsDeclaration(0, InitDeclMatcher),
194 containsDeclaration(1, EndDeclMatcher)
195 ),
196 declStmt(hasSingleDecl(InitDeclMatcher))
197 )),
Edwin Vanedde168b2013-01-04 18:25:18 +0000198 hasCondition(anyOf(
Edwin Vane4f05d712013-03-07 16:22:05 +0000199 binaryOperator(
200 hasOperatorName("!="),
201 hasLHS(IteratorComparisonMatcher),
202 hasRHS(IteratorBoundMatcher)
203 ),
204 binaryOperator(
205 hasOperatorName("!="),
206 hasLHS(IteratorBoundMatcher),
207 hasRHS(IteratorComparisonMatcher)
208 ),
209 OverloadedNEQMatcher
210 )),
Edwin Vanedde168b2013-01-04 18:25:18 +0000211 hasIncrement(anyOf(
Edwin Vane4f05d712013-03-07 16:22:05 +0000212 unaryOperator(
213 hasOperatorName("++"),
214 hasUnaryOperand(
215 declRefExpr(to(
216 varDecl(hasType(pointsTo(AnyType))).bind(IncrementVarName)
217 ))
218 )
219 ),
220 operatorCallExpr(
221 hasOverloadedOperatorName("++"),
222 hasArgument(0,
223 declRefExpr(to(
224 varDecl(TestDerefReturnsByValue).bind(IncrementVarName)
225 ))
226 )
227 )
228 ))
229 ).bind(LoopName);
Edwin Vanedde168b2013-01-04 18:25:18 +0000230}
231
232/// \brief The matcher used for array-like containers (pseudoarrays).
233///
234/// This matcher is more flexible than array-based loops. It will match
235/// loops of the following textual forms (regardless of whether the
236/// iterator type is actually a pointer type or a class type):
237///
238/// Assuming f, g, and h are of type containerType::iterator,
239/// \code
240/// for (int i = 0, j = container.size(); f < g; ++h) { ... }
241/// for (int i = 0; f < container.size(); ++h) { ... }
242/// \endcode
243/// The following string identifiers are bound to the parts of the AST:
244/// InitVarName: 'i' (as a VarDecl)
245/// ConditionVarName: 'f' (as a VarDecl)
246/// LoopName: The entire for loop (as a ForStmt)
247/// In the first example only:
248/// EndVarName: 'j' (as a VarDecl)
249/// ConditionEndVarName: 'g' (as a VarDecl)
250/// In the second example only:
251/// EndCallName: 'container.size()' (as a CXXMemberCallExpr)
252///
253/// Client code will need to make sure that:
254/// - The index variables 'i', 'f', and 'h' are the same
255/// - The containers on which 'size()' is called is the container indexed
256/// - The index variable is only used in overloaded operator[] or
257/// container.at()
258/// - If the end iterator variable 'g' is defined, it is the same as 'j'
259/// - The container's iterators would not be invalidated during the loop
260StatementMatcher makePseudoArrayLoopMatcher() {
261 StatementMatcher SizeCallMatcher =
262 memberCallExpr(argumentCountIs(0),
263 callee(methodDecl(anyOf(hasName("size"),
264 hasName("length")))));
265
266 StatementMatcher EndInitMatcher =
267 expr(anyOf(
268 ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)),
269 explicitCastExpr(hasSourceExpression(ignoringParenImpCasts(
270 expr(SizeCallMatcher).bind(EndCallName))))));
271
272 DeclarationMatcher EndDeclMatcher =
273 varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName);
274
275 StatementMatcher IndexBoundMatcher =
276 expr(anyOf(
277 ignoringParenImpCasts(declRefExpr(to(
278 varDecl(hasType(isInteger())).bind(ConditionEndVarName)))),
279 EndInitMatcher));
280
281 return forStmt(
282 hasLoopInit(anyOf(
283 declStmt(declCountIs(2),
284 containsDeclaration(0, InitToZeroMatcher),
285 containsDeclaration(1, EndDeclMatcher)),
286 declStmt(hasSingleDecl(InitToZeroMatcher)))),
287 hasCondition(anyOf(
288 binaryOperator(hasOperatorName("<"),
289 hasLHS(IntegerComparisonMatcher),
290 hasRHS(IndexBoundMatcher)),
291 binaryOperator(hasOperatorName(">"),
292 hasLHS(IndexBoundMatcher),
293 hasRHS(IntegerComparisonMatcher)))),
294 hasIncrement(unaryOperator(
295 hasOperatorName("++"),
296 hasUnaryOperand(IncrementVarMatcher))))
297 .bind(LoopName);
298}