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