File: | tools/clang/tools/extra/clang-tidy/modernize/LoopConvertCheck.cpp |
Warning: | line 655, column 24 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===--- LoopConvertCheck.cpp - clang-tidy---------------------------------===// | |||
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 | #include "LoopConvertCheck.h" | |||
11 | #include "clang/AST/ASTContext.h" | |||
12 | #include "clang/ASTMatchers/ASTMatchFinder.h" | |||
13 | #include "clang/Basic/LLVM.h" | |||
14 | #include "clang/Basic/LangOptions.h" | |||
15 | #include "clang/Basic/SourceLocation.h" | |||
16 | #include "clang/Basic/SourceManager.h" | |||
17 | #include "clang/Lex/Lexer.h" | |||
18 | #include "llvm/ADT/SmallVector.h" | |||
19 | #include "llvm/ADT/StringRef.h" | |||
20 | #include "llvm/ADT/StringSwitch.h" | |||
21 | #include "llvm/Support/Casting.h" | |||
22 | #include <cassert> | |||
23 | #include <cstring> | |||
24 | #include <utility> | |||
25 | ||||
26 | using namespace clang::ast_matchers; | |||
27 | using namespace llvm; | |||
28 | ||||
29 | namespace clang { | |||
30 | namespace tidy { | |||
31 | namespace modernize { | |||
32 | ||||
33 | static const char LoopNameArray[] = "forLoopArray"; | |||
34 | static const char LoopNameIterator[] = "forLoopIterator"; | |||
35 | static const char LoopNamePseudoArray[] = "forLoopPseudoArray"; | |||
36 | static const char ConditionBoundName[] = "conditionBound"; | |||
37 | static const char ConditionVarName[] = "conditionVar"; | |||
38 | static const char IncrementVarName[] = "incrementVar"; | |||
39 | static const char InitVarName[] = "initVar"; | |||
40 | static const char BeginCallName[] = "beginCall"; | |||
41 | static const char EndCallName[] = "endCall"; | |||
42 | static const char ConditionEndVarName[] = "conditionEndVar"; | |||
43 | static const char EndVarName[] = "endVar"; | |||
44 | static const char DerefByValueResultName[] = "derefByValueResult"; | |||
45 | static const char DerefByRefResultName[] = "derefByRefResult"; | |||
46 | ||||
47 | // shared matchers | |||
48 | static const TypeMatcher AnyType = anything(); | |||
49 | ||||
50 | static const StatementMatcher IntegerComparisonMatcher = | |||
51 | expr(ignoringParenImpCasts( | |||
52 | declRefExpr(to(varDecl(hasType(isInteger())).bind(ConditionVarName))))); | |||
53 | ||||
54 | static const DeclarationMatcher InitToZeroMatcher = | |||
55 | varDecl(hasInitializer(ignoringParenImpCasts(integerLiteral(equals(0))))) | |||
56 | .bind(InitVarName); | |||
57 | ||||
58 | static const StatementMatcher IncrementVarMatcher = | |||
59 | declRefExpr(to(varDecl(hasType(isInteger())).bind(IncrementVarName))); | |||
60 | ||||
61 | /// \brief The matcher for loops over arrays. | |||
62 | /// | |||
63 | /// In this general example, assuming 'j' and 'k' are of integral type: | |||
64 | /// \code | |||
65 | /// for (int i = 0; j < 3 + 2; ++k) { ... } | |||
66 | /// \endcode | |||
67 | /// The following string identifiers are bound to these parts of the AST: | |||
68 | /// ConditionVarName: 'j' (as a VarDecl) | |||
69 | /// ConditionBoundName: '3 + 2' (as an Expr) | |||
70 | /// InitVarName: 'i' (as a VarDecl) | |||
71 | /// IncrementVarName: 'k' (as a VarDecl) | |||
72 | /// LoopName: The entire for loop (as a ForStmt) | |||
73 | /// | |||
74 | /// Client code will need to make sure that: | |||
75 | /// - The three index variables identified by the matcher are the same | |||
76 | /// VarDecl. | |||
77 | /// - The index variable is only used as an array index. | |||
78 | /// - All arrays indexed by the loop are the same. | |||
79 | StatementMatcher makeArrayLoopMatcher() { | |||
80 | StatementMatcher ArrayBoundMatcher = | |||
81 | expr(hasType(isInteger())).bind(ConditionBoundName); | |||
82 | ||||
83 | return forStmt( | |||
84 | unless(isInTemplateInstantiation()), | |||
85 | hasLoopInit(declStmt(hasSingleDecl(InitToZeroMatcher))), | |||
86 | hasCondition(anyOf( | |||
87 | binaryOperator(hasOperatorName("<"), | |||
88 | hasLHS(IntegerComparisonMatcher), | |||
89 | hasRHS(ArrayBoundMatcher)), | |||
90 | binaryOperator(hasOperatorName(">"), hasLHS(ArrayBoundMatcher), | |||
91 | hasRHS(IntegerComparisonMatcher)))), | |||
92 | hasIncrement(unaryOperator(hasOperatorName("++"), | |||
93 | hasUnaryOperand(IncrementVarMatcher)))) | |||
94 | .bind(LoopNameArray); | |||
95 | } | |||
96 | ||||
97 | /// \brief The matcher used for iterator-based for loops. | |||
98 | /// | |||
99 | /// This matcher is more flexible than array-based loops. It will match | |||
100 | /// catch loops of the following textual forms (regardless of whether the | |||
101 | /// iterator type is actually a pointer type or a class type): | |||
102 | /// | |||
103 | /// Assuming f, g, and h are of type containerType::iterator, | |||
104 | /// \code | |||
105 | /// for (containerType::iterator it = container.begin(), | |||
106 | /// e = createIterator(); f != g; ++h) { ... } | |||
107 | /// for (containerType::iterator it = container.begin(); | |||
108 | /// f != anotherContainer.end(); ++h) { ... } | |||
109 | /// \endcode | |||
110 | /// The following string identifiers are bound to the parts of the AST: | |||
111 | /// InitVarName: 'it' (as a VarDecl) | |||
112 | /// ConditionVarName: 'f' (as a VarDecl) | |||
113 | /// LoopName: The entire for loop (as a ForStmt) | |||
114 | /// In the first example only: | |||
115 | /// EndVarName: 'e' (as a VarDecl) | |||
116 | /// ConditionEndVarName: 'g' (as a VarDecl) | |||
117 | /// In the second example only: | |||
118 | /// EndCallName: 'container.end()' (as a CXXMemberCallExpr) | |||
119 | /// | |||
120 | /// Client code will need to make sure that: | |||
121 | /// - The iterator variables 'it', 'f', and 'h' are the same. | |||
122 | /// - The two containers on which 'begin' and 'end' are called are the same. | |||
123 | /// - If the end iterator variable 'g' is defined, it is the same as 'f'. | |||
124 | StatementMatcher makeIteratorLoopMatcher() { | |||
125 | StatementMatcher BeginCallMatcher = | |||
126 | cxxMemberCallExpr( | |||
127 | argumentCountIs(0), | |||
128 | callee(cxxMethodDecl(anyOf(hasName("begin"), hasName("cbegin"))))) | |||
129 | .bind(BeginCallName); | |||
130 | ||||
131 | DeclarationMatcher InitDeclMatcher = | |||
132 | varDecl(hasInitializer(anyOf(ignoringParenImpCasts(BeginCallMatcher), | |||
133 | materializeTemporaryExpr( | |||
134 | ignoringParenImpCasts(BeginCallMatcher)), | |||
135 | hasDescendant(BeginCallMatcher)))) | |||
136 | .bind(InitVarName); | |||
137 | ||||
138 | DeclarationMatcher EndDeclMatcher = | |||
139 | varDecl(hasInitializer(anything())).bind(EndVarName); | |||
140 | ||||
141 | StatementMatcher EndCallMatcher = cxxMemberCallExpr( | |||
142 | argumentCountIs(0), | |||
143 | callee(cxxMethodDecl(anyOf(hasName("end"), hasName("cend"))))); | |||
144 | ||||
145 | StatementMatcher IteratorBoundMatcher = | |||
146 | expr(anyOf(ignoringParenImpCasts( | |||
147 | declRefExpr(to(varDecl().bind(ConditionEndVarName)))), | |||
148 | ignoringParenImpCasts(expr(EndCallMatcher).bind(EndCallName)), | |||
149 | materializeTemporaryExpr(ignoringParenImpCasts( | |||
150 | expr(EndCallMatcher).bind(EndCallName))))); | |||
151 | ||||
152 | StatementMatcher IteratorComparisonMatcher = expr( | |||
153 | ignoringParenImpCasts(declRefExpr(to(varDecl().bind(ConditionVarName))))); | |||
154 | ||||
155 | auto OverloadedNEQMatcher = ignoringImplicit( | |||
156 | cxxOperatorCallExpr(hasOverloadedOperatorName("!="), argumentCountIs(2), | |||
157 | hasArgument(0, IteratorComparisonMatcher), | |||
158 | hasArgument(1, IteratorBoundMatcher))); | |||
159 | ||||
160 | // This matcher tests that a declaration is a CXXRecordDecl that has an | |||
161 | // overloaded operator*(). If the operator*() returns by value instead of by | |||
162 | // reference then the return type is tagged with DerefByValueResultName. | |||
163 | internal::Matcher<VarDecl> TestDerefReturnsByValue = | |||
164 | hasType(hasUnqualifiedDesugaredType( | |||
165 | recordType(hasDeclaration(cxxRecordDecl(hasMethod(allOf( | |||
166 | hasOverloadedOperatorName("*"), | |||
167 | anyOf( | |||
168 | // Tag the return type if it's by value. | |||
169 | returns(qualType(unless(hasCanonicalType(referenceType()))) | |||
170 | .bind(DerefByValueResultName)), | |||
171 | returns( | |||
172 | // Skip loops where the iterator's operator* returns an | |||
173 | // rvalue reference. This is just weird. | |||
174 | qualType(unless(hasCanonicalType(rValueReferenceType()))) | |||
175 | .bind(DerefByRefResultName)))))))))); | |||
176 | ||||
177 | return forStmt( | |||
178 | unless(isInTemplateInstantiation()), | |||
179 | hasLoopInit(anyOf(declStmt(declCountIs(2), | |||
180 | containsDeclaration(0, InitDeclMatcher), | |||
181 | containsDeclaration(1, EndDeclMatcher)), | |||
182 | declStmt(hasSingleDecl(InitDeclMatcher)))), | |||
183 | hasCondition( | |||
184 | anyOf(binaryOperator(hasOperatorName("!="), | |||
185 | hasLHS(IteratorComparisonMatcher), | |||
186 | hasRHS(IteratorBoundMatcher)), | |||
187 | binaryOperator(hasOperatorName("!="), | |||
188 | hasLHS(IteratorBoundMatcher), | |||
189 | hasRHS(IteratorComparisonMatcher)), | |||
190 | OverloadedNEQMatcher)), | |||
191 | hasIncrement(anyOf( | |||
192 | unaryOperator(hasOperatorName("++"), | |||
193 | hasUnaryOperand(declRefExpr( | |||
194 | to(varDecl(hasType(pointsTo(AnyType))) | |||
195 | .bind(IncrementVarName))))), | |||
196 | cxxOperatorCallExpr( | |||
197 | hasOverloadedOperatorName("++"), | |||
198 | hasArgument( | |||
199 | 0, declRefExpr(to(varDecl(TestDerefReturnsByValue) | |||
200 | .bind(IncrementVarName)))))))) | |||
201 | .bind(LoopNameIterator); | |||
202 | } | |||
203 | ||||
204 | /// \brief The matcher used for array-like containers (pseudoarrays). | |||
205 | /// | |||
206 | /// This matcher is more flexible than array-based loops. It will match | |||
207 | /// loops of the following textual forms (regardless of whether the | |||
208 | /// iterator type is actually a pointer type or a class type): | |||
209 | /// | |||
210 | /// Assuming f, g, and h are of type containerType::iterator, | |||
211 | /// \code | |||
212 | /// for (int i = 0, j = container.size(); f < g; ++h) { ... } | |||
213 | /// for (int i = 0; f < container.size(); ++h) { ... } | |||
214 | /// \endcode | |||
215 | /// The following string identifiers are bound to the parts of the AST: | |||
216 | /// InitVarName: 'i' (as a VarDecl) | |||
217 | /// ConditionVarName: 'f' (as a VarDecl) | |||
218 | /// LoopName: The entire for loop (as a ForStmt) | |||
219 | /// In the first example only: | |||
220 | /// EndVarName: 'j' (as a VarDecl) | |||
221 | /// ConditionEndVarName: 'g' (as a VarDecl) | |||
222 | /// In the second example only: | |||
223 | /// EndCallName: 'container.size()' (as a CXXMemberCallExpr) | |||
224 | /// | |||
225 | /// Client code will need to make sure that: | |||
226 | /// - The index variables 'i', 'f', and 'h' are the same. | |||
227 | /// - The containers on which 'size()' is called is the container indexed. | |||
228 | /// - The index variable is only used in overloaded operator[] or | |||
229 | /// container.at(). | |||
230 | /// - If the end iterator variable 'g' is defined, it is the same as 'j'. | |||
231 | /// - The container's iterators would not be invalidated during the loop. | |||
232 | StatementMatcher makePseudoArrayLoopMatcher() { | |||
233 | // Test that the incoming type has a record declaration that has methods | |||
234 | // called 'begin' and 'end'. If the incoming type is const, then make sure | |||
235 | // these methods are also marked const. | |||
236 | // | |||
237 | // FIXME: To be completely thorough this matcher should also ensure the | |||
238 | // return type of begin/end is an iterator that dereferences to the same as | |||
239 | // what operator[] or at() returns. Such a test isn't likely to fail except | |||
240 | // for pathological cases. | |||
241 | // | |||
242 | // FIXME: Also, a record doesn't necessarily need begin() and end(). Free | |||
243 | // functions called begin() and end() taking the container as an argument | |||
244 | // are also allowed. | |||
245 | TypeMatcher RecordWithBeginEnd = qualType(anyOf( | |||
246 | qualType( | |||
247 | isConstQualified(), | |||
248 | hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl( | |||
249 | hasMethod(cxxMethodDecl(hasName("begin"), isConst())), | |||
250 | hasMethod(cxxMethodDecl(hasName("end"), | |||
251 | isConst())))) // hasDeclaration | |||
252 | ))), // qualType | |||
253 | qualType(unless(isConstQualified()), | |||
254 | hasUnqualifiedDesugaredType(recordType(hasDeclaration( | |||
255 | cxxRecordDecl(hasMethod(hasName("begin")), | |||
256 | hasMethod(hasName("end"))))))) // qualType | |||
257 | )); | |||
258 | ||||
259 | StatementMatcher SizeCallMatcher = cxxMemberCallExpr( | |||
260 | argumentCountIs(0), | |||
261 | callee(cxxMethodDecl(anyOf(hasName("size"), hasName("length")))), | |||
262 | on(anyOf(hasType(pointsTo(RecordWithBeginEnd)), | |||
263 | hasType(RecordWithBeginEnd)))); | |||
264 | ||||
265 | StatementMatcher EndInitMatcher = | |||
266 | expr(anyOf(ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)), | |||
267 | explicitCastExpr(hasSourceExpression(ignoringParenImpCasts( | |||
268 | expr(SizeCallMatcher).bind(EndCallName)))))); | |||
269 | ||||
270 | DeclarationMatcher EndDeclMatcher = | |||
271 | varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName); | |||
272 | ||||
273 | StatementMatcher IndexBoundMatcher = | |||
274 | expr(anyOf(ignoringParenImpCasts(declRefExpr(to( | |||
275 | varDecl(hasType(isInteger())).bind(ConditionEndVarName)))), | |||
276 | EndInitMatcher)); | |||
277 | ||||
278 | return forStmt( | |||
279 | unless(isInTemplateInstantiation()), | |||
280 | hasLoopInit( | |||
281 | anyOf(declStmt(declCountIs(2), | |||
282 | containsDeclaration(0, InitToZeroMatcher), | |||
283 | containsDeclaration(1, EndDeclMatcher)), | |||
284 | declStmt(hasSingleDecl(InitToZeroMatcher)))), | |||
285 | hasCondition(anyOf( | |||
286 | binaryOperator(hasOperatorName("<"), | |||
287 | hasLHS(IntegerComparisonMatcher), | |||
288 | hasRHS(IndexBoundMatcher)), | |||
289 | binaryOperator(hasOperatorName(">"), hasLHS(IndexBoundMatcher), | |||
290 | hasRHS(IntegerComparisonMatcher)))), | |||
291 | hasIncrement(unaryOperator(hasOperatorName("++"), | |||
292 | hasUnaryOperand(IncrementVarMatcher)))) | |||
293 | .bind(LoopNamePseudoArray); | |||
294 | } | |||
295 | ||||
296 | /// \brief Determine whether Init appears to be an initializing an iterator. | |||
297 | /// | |||
298 | /// If it is, returns the object whose begin() or end() method is called, and | |||
299 | /// the output parameter isArrow is set to indicate whether the initialization | |||
300 | /// is called via . or ->. | |||
301 | static const Expr *getContainerFromBeginEndCall(const Expr *Init, bool IsBegin, | |||
302 | bool *IsArrow) { | |||
303 | // FIXME: Maybe allow declaration/initialization outside of the for loop. | |||
304 | const auto *TheCall = | |||
305 | dyn_cast_or_null<CXXMemberCallExpr>(digThroughConstructors(Init)); | |||
306 | if (!TheCall || TheCall->getNumArgs() != 0) | |||
307 | return nullptr; | |||
308 | ||||
309 | const auto *Member = dyn_cast<MemberExpr>(TheCall->getCallee()); | |||
310 | if (!Member) | |||
311 | return nullptr; | |||
312 | StringRef Name = Member->getMemberDecl()->getName(); | |||
313 | StringRef TargetName = IsBegin ? "begin" : "end"; | |||
314 | StringRef ConstTargetName = IsBegin ? "cbegin" : "cend"; | |||
315 | if (Name != TargetName && Name != ConstTargetName) | |||
316 | return nullptr; | |||
317 | ||||
318 | const Expr *SourceExpr = Member->getBase(); | |||
319 | if (!SourceExpr) | |||
320 | return nullptr; | |||
321 | ||||
322 | *IsArrow = Member->isArrow(); | |||
323 | return SourceExpr; | |||
324 | } | |||
325 | ||||
326 | /// \brief Determines the container whose begin() and end() functions are called | |||
327 | /// for an iterator-based loop. | |||
328 | /// | |||
329 | /// BeginExpr must be a member call to a function named "begin()", and EndExpr | |||
330 | /// must be a member. | |||
331 | static const Expr *findContainer(ASTContext *Context, const Expr *BeginExpr, | |||
332 | const Expr *EndExpr, | |||
333 | bool *ContainerNeedsDereference) { | |||
334 | // Now that we know the loop variable and test expression, make sure they are | |||
335 | // valid. | |||
336 | bool BeginIsArrow = false; | |||
337 | bool EndIsArrow = false; | |||
338 | const Expr *BeginContainerExpr = | |||
339 | getContainerFromBeginEndCall(BeginExpr, /*IsBegin=*/true, &BeginIsArrow); | |||
340 | if (!BeginContainerExpr) | |||
341 | return nullptr; | |||
342 | ||||
343 | const Expr *EndContainerExpr = | |||
344 | getContainerFromBeginEndCall(EndExpr, /*IsBegin=*/false, &EndIsArrow); | |||
345 | // Disallow loops that try evil things like this (note the dot and arrow): | |||
346 | // for (IteratorType It = Obj.begin(), E = Obj->end(); It != E; ++It) { } | |||
347 | if (!EndContainerExpr || BeginIsArrow != EndIsArrow || | |||
348 | !areSameExpr(Context, EndContainerExpr, BeginContainerExpr)) | |||
349 | return nullptr; | |||
350 | ||||
351 | *ContainerNeedsDereference = BeginIsArrow; | |||
352 | return BeginContainerExpr; | |||
353 | } | |||
354 | ||||
355 | /// \brief Obtain the original source code text from a SourceRange. | |||
356 | static StringRef getStringFromRange(SourceManager &SourceMgr, | |||
357 | const LangOptions &LangOpts, | |||
358 | SourceRange Range) { | |||
359 | if (SourceMgr.getFileID(Range.getBegin()) != | |||
360 | SourceMgr.getFileID(Range.getEnd())) { | |||
361 | return StringRef(); // Empty string. | |||
362 | } | |||
363 | ||||
364 | return Lexer::getSourceText(CharSourceRange(Range, true), SourceMgr, | |||
365 | LangOpts); | |||
366 | } | |||
367 | ||||
368 | /// \brief If the given expression is actually a DeclRefExpr or a MemberExpr, | |||
369 | /// find and return the underlying ValueDecl; otherwise, return NULL. | |||
370 | static const ValueDecl *getReferencedVariable(const Expr *E) { | |||
371 | if (const DeclRefExpr *DRE = getDeclRef(E)) | |||
372 | return dyn_cast<VarDecl>(DRE->getDecl()); | |||
373 | if (const auto *Mem = dyn_cast<MemberExpr>(E->IgnoreParenImpCasts())) | |||
374 | return dyn_cast<FieldDecl>(Mem->getMemberDecl()); | |||
375 | return nullptr; | |||
376 | } | |||
377 | ||||
378 | /// \brief Returns true when the given expression is a member expression | |||
379 | /// whose base is `this` (implicitly or not). | |||
380 | static bool isDirectMemberExpr(const Expr *E) { | |||
381 | if (const auto *Member = dyn_cast<MemberExpr>(E->IgnoreParenImpCasts())) | |||
382 | return isa<CXXThisExpr>(Member->getBase()->IgnoreParenImpCasts()); | |||
383 | return false; | |||
384 | } | |||
385 | ||||
386 | /// \brief Given an expression that represents an usage of an element from the | |||
387 | /// containter that we are iterating over, returns false when it can be | |||
388 | /// guaranteed this element cannot be modified as a result of this usage. | |||
389 | static bool canBeModified(ASTContext *Context, const Expr *E) { | |||
390 | if (E->getType().isConstQualified()) | |||
391 | return false; | |||
392 | auto Parents = Context->getParents(*E); | |||
393 | if (Parents.size() != 1) | |||
394 | return true; | |||
395 | if (const auto *Cast = Parents[0].get<ImplicitCastExpr>()) { | |||
396 | if ((Cast->getCastKind() == CK_NoOp && | |||
397 | Cast->getType() == E->getType().withConst()) || | |||
398 | (Cast->getCastKind() == CK_LValueToRValue && | |||
399 | !Cast->getType().isNull() && Cast->getType()->isFundamentalType())) | |||
400 | return false; | |||
401 | } | |||
402 | // FIXME: Make this function more generic. | |||
403 | return true; | |||
404 | } | |||
405 | ||||
406 | /// \brief Returns true when it can be guaranteed that the elements of the | |||
407 | /// container are not being modified. | |||
408 | static bool usagesAreConst(ASTContext *Context, const UsageResult &Usages) { | |||
409 | for (const Usage &U : Usages) { | |||
410 | // Lambda captures are just redeclarations (VarDecl) of the same variable, | |||
411 | // not expressions. If we want to know if a variable that is captured by | |||
412 | // reference can be modified in an usage inside the lambda's body, we need | |||
413 | // to find the expression corresponding to that particular usage, later in | |||
414 | // this loop. | |||
415 | if (U.Kind != Usage::UK_CaptureByCopy && U.Kind != Usage::UK_CaptureByRef && | |||
416 | canBeModified(Context, U.Expression)) | |||
417 | return false; | |||
418 | } | |||
419 | return true; | |||
420 | } | |||
421 | ||||
422 | /// \brief Returns true if the elements of the container are never accessed | |||
423 | /// by reference. | |||
424 | static bool usagesReturnRValues(const UsageResult &Usages) { | |||
425 | for (const auto &U : Usages) { | |||
426 | if (U.Expression && !U.Expression->isRValue()) | |||
427 | return false; | |||
428 | } | |||
429 | return true; | |||
430 | } | |||
431 | ||||
432 | /// \brief Returns true if the container is const-qualified. | |||
433 | static bool containerIsConst(const Expr *ContainerExpr, bool Dereference) { | |||
434 | if (const auto *VDec = getReferencedVariable(ContainerExpr)) { | |||
435 | QualType CType = VDec->getType(); | |||
436 | if (Dereference) { | |||
437 | if (!CType->isPointerType()) | |||
438 | return false; | |||
439 | CType = CType->getPointeeType(); | |||
440 | } | |||
441 | // If VDec is a reference to a container, Dereference is false, | |||
442 | // but we still need to check the const-ness of the underlying container | |||
443 | // type. | |||
444 | CType = CType.getNonReferenceType(); | |||
445 | return CType.isConstQualified(); | |||
446 | } | |||
447 | return false; | |||
448 | } | |||
449 | ||||
450 | LoopConvertCheck::RangeDescriptor::RangeDescriptor() | |||
451 | : ContainerNeedsDereference(false), DerefByConstRef(false), | |||
452 | DerefByValue(false) {} | |||
453 | ||||
454 | LoopConvertCheck::LoopConvertCheck(StringRef Name, ClangTidyContext *Context) | |||
455 | : ClangTidyCheck(Name, Context), TUInfo(new TUTrackingInfo), | |||
456 | MaxCopySize(std::stoull(Options.get("MaxCopySize", "16"))), | |||
457 | MinConfidence(StringSwitch<Confidence::Level>( | |||
458 | Options.get("MinConfidence", "reasonable")) | |||
459 | .Case("safe", Confidence::CL_Safe) | |||
460 | .Case("risky", Confidence::CL_Risky) | |||
461 | .Default(Confidence::CL_Reasonable)), | |||
462 | NamingStyle(StringSwitch<VariableNamer::NamingStyle>( | |||
463 | Options.get("NamingStyle", "CamelCase")) | |||
464 | .Case("camelBack", VariableNamer::NS_CamelBack) | |||
465 | .Case("lower_case", VariableNamer::NS_LowerCase) | |||
466 | .Case("UPPER_CASE", VariableNamer::NS_UpperCase) | |||
467 | .Default(VariableNamer::NS_CamelCase)) {} | |||
468 | ||||
469 | void LoopConvertCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { | |||
470 | Options.store(Opts, "MaxCopySize", std::to_string(MaxCopySize)); | |||
471 | SmallVector<std::string, 3> Confs{"risky", "reasonable", "safe"}; | |||
472 | Options.store(Opts, "MinConfidence", Confs[static_cast<int>(MinConfidence)]); | |||
473 | ||||
474 | SmallVector<std::string, 4> Styles{"camelBack", "CamelCase", "lower_case", | |||
475 | "UPPER_CASE"}; | |||
476 | Options.store(Opts, "NamingStyle", Styles[static_cast<int>(NamingStyle)]); | |||
477 | } | |||
478 | ||||
479 | void LoopConvertCheck::registerMatchers(MatchFinder *Finder) { | |||
480 | // Only register the matchers for C++. Because this checker is used for | |||
481 | // modernization, it is reasonable to run it on any C++ standard with the | |||
482 | // assumption the user is trying to modernize their codebase. | |||
483 | if (!getLangOpts().CPlusPlus) | |||
484 | return; | |||
485 | ||||
486 | Finder->addMatcher(makeArrayLoopMatcher(), this); | |||
487 | Finder->addMatcher(makeIteratorLoopMatcher(), this); | |||
488 | Finder->addMatcher(makePseudoArrayLoopMatcher(), this); | |||
489 | } | |||
490 | ||||
491 | /// \brief Given the range of a single declaration, such as: | |||
492 | /// \code | |||
493 | /// unsigned &ThisIsADeclarationThatCanSpanSeveralLinesOfCode = | |||
494 | /// InitializationValues[I]; | |||
495 | /// next_instruction; | |||
496 | /// \endcode | |||
497 | /// Finds the range that has to be erased to remove this declaration without | |||
498 | /// leaving empty lines, by extending the range until the beginning of the | |||
499 | /// next instruction. | |||
500 | /// | |||
501 | /// We need to delete a potential newline after the deleted alias, as | |||
502 | /// clang-format will leave empty lines untouched. For all other formatting we | |||
503 | /// rely on clang-format to fix it. | |||
504 | void LoopConvertCheck::getAliasRange(SourceManager &SM, SourceRange &Range) { | |||
505 | bool Invalid = false; | |||
506 | const char *TextAfter = | |||
507 | SM.getCharacterData(Range.getEnd().getLocWithOffset(1), &Invalid); | |||
508 | if (Invalid) | |||
509 | return; | |||
510 | unsigned Offset = std::strspn(TextAfter, " \t\r\n"); | |||
511 | Range = | |||
512 | SourceRange(Range.getBegin(), Range.getEnd().getLocWithOffset(Offset)); | |||
513 | } | |||
514 | ||||
515 | /// \brief Computes the changes needed to convert a given for loop, and | |||
516 | /// applies them. | |||
517 | void LoopConvertCheck::doConversion( | |||
518 | ASTContext *Context, const VarDecl *IndexVar, | |||
519 | const ValueDecl *MaybeContainer, const UsageResult &Usages, | |||
520 | const DeclStmt *AliasDecl, bool AliasUseRequired, bool AliasFromForInit, | |||
521 | const ForStmt *Loop, RangeDescriptor Descriptor) { | |||
522 | auto Diag = diag(Loop->getForLoc(), "use range-based for loop instead"); | |||
523 | ||||
524 | std::string VarName; | |||
525 | bool VarNameFromAlias = (Usages.size() == 1) && AliasDecl; | |||
526 | bool AliasVarIsRef = false; | |||
527 | bool CanCopy = true; | |||
528 | ||||
529 | if (VarNameFromAlias) { | |||
530 | const auto *AliasVar = cast<VarDecl>(AliasDecl->getSingleDecl()); | |||
531 | VarName = AliasVar->getName().str(); | |||
532 | ||||
533 | // Use the type of the alias if it's not the same | |||
534 | QualType AliasVarType = AliasVar->getType(); | |||
535 | assert(!AliasVarType.isNull() && "Type in VarDecl is null")((!AliasVarType.isNull() && "Type in VarDecl is null" ) ? static_cast<void> (0) : __assert_fail ("!AliasVarType.isNull() && \"Type in VarDecl is null\"" , "/build/llvm-toolchain-snapshot-8~svn345461/tools/clang/tools/extra/clang-tidy/modernize/LoopConvertCheck.cpp" , 535, __PRETTY_FUNCTION__)); | |||
536 | if (AliasVarType->isReferenceType()) { | |||
537 | AliasVarType = AliasVarType.getNonReferenceType(); | |||
538 | AliasVarIsRef = true; | |||
539 | } | |||
540 | if (Descriptor.ElemType.isNull() || | |||
541 | !Context->hasSameUnqualifiedType(AliasVarType, Descriptor.ElemType)) | |||
542 | Descriptor.ElemType = AliasVarType; | |||
543 | ||||
544 | // We keep along the entire DeclStmt to keep the correct range here. | |||
545 | SourceRange ReplaceRange = AliasDecl->getSourceRange(); | |||
546 | ||||
547 | std::string ReplacementText; | |||
548 | if (AliasUseRequired) { | |||
549 | ReplacementText = VarName; | |||
550 | } else if (AliasFromForInit) { | |||
551 | // FIXME: Clang includes the location of the ';' but only for DeclStmt's | |||
552 | // in a for loop's init clause. Need to put this ';' back while removing | |||
553 | // the declaration of the alias variable. This is probably a bug. | |||
554 | ReplacementText = ";"; | |||
555 | } else { | |||
556 | // Avoid leaving empty lines or trailing whitespaces. | |||
557 | getAliasRange(Context->getSourceManager(), ReplaceRange); | |||
558 | } | |||
559 | ||||
560 | Diag << FixItHint::CreateReplacement( | |||
561 | CharSourceRange::getTokenRange(ReplaceRange), ReplacementText); | |||
562 | // No further replacements are made to the loop, since the iterator or index | |||
563 | // was used exactly once - in the initialization of AliasVar. | |||
564 | } else { | |||
565 | VariableNamer Namer(&TUInfo->getGeneratedDecls(), | |||
566 | &TUInfo->getParentFinder().getStmtToParentStmtMap(), | |||
567 | Loop, IndexVar, MaybeContainer, Context, NamingStyle); | |||
568 | VarName = Namer.createIndexName(); | |||
569 | // First, replace all usages of the array subscript expression with our new | |||
570 | // variable. | |||
571 | for (const auto &Usage : Usages) { | |||
572 | std::string ReplaceText; | |||
573 | SourceRange Range = Usage.Range; | |||
574 | if (Usage.Expression) { | |||
575 | // If this is an access to a member through the arrow operator, after | |||
576 | // the replacement it must be accessed through the '.' operator. | |||
577 | ReplaceText = Usage.Kind == Usage::UK_MemberThroughArrow ? VarName + "." | |||
578 | : VarName; | |||
579 | auto Parents = Context->getParents(*Usage.Expression); | |||
580 | if (Parents.size() == 1) { | |||
581 | if (const auto *Paren = Parents[0].get<ParenExpr>()) { | |||
582 | // Usage.Expression will be replaced with the new index variable, | |||
583 | // and parenthesis around a simple DeclRefExpr can always be | |||
584 | // removed. | |||
585 | Range = Paren->getSourceRange(); | |||
586 | } else if (const auto *UOP = Parents[0].get<UnaryOperator>()) { | |||
587 | // If we are taking the address of the loop variable, then we must | |||
588 | // not use a copy, as it would mean taking the address of the loop's | |||
589 | // local index instead. | |||
590 | // FIXME: This won't catch cases where the address is taken outside | |||
591 | // of the loop's body (for instance, in a function that got the | |||
592 | // loop's index as a const reference parameter), or where we take | |||
593 | // the address of a member (like "&Arr[i].A.B.C"). | |||
594 | if (UOP->getOpcode() == UO_AddrOf) | |||
595 | CanCopy = false; | |||
596 | } | |||
597 | } | |||
598 | } else { | |||
599 | // The Usage expression is only null in case of lambda captures (which | |||
600 | // are VarDecl). If the index is captured by value, add '&' to capture | |||
601 | // by reference instead. | |||
602 | ReplaceText = | |||
603 | Usage.Kind == Usage::UK_CaptureByCopy ? "&" + VarName : VarName; | |||
604 | } | |||
605 | TUInfo->getReplacedVars().insert(std::make_pair(Loop, IndexVar)); | |||
606 | Diag << FixItHint::CreateReplacement( | |||
607 | CharSourceRange::getTokenRange(Range), ReplaceText); | |||
608 | } | |||
609 | } | |||
610 | ||||
611 | // Now, we need to construct the new range expression. | |||
612 | SourceRange ParenRange(Loop->getLParenLoc(), Loop->getRParenLoc()); | |||
613 | ||||
614 | QualType Type = Context->getAutoDeductType(); | |||
615 | if (!Descriptor.ElemType.isNull() && Descriptor.ElemType->isFundamentalType()) | |||
616 | Type = Descriptor.ElemType.getUnqualifiedType(); | |||
617 | ||||
618 | // If the new variable name is from the aliased variable, then the reference | |||
619 | // type for the new variable should only be used if the aliased variable was | |||
620 | // declared as a reference. | |||
621 | bool IsCheapToCopy = | |||
622 | !Descriptor.ElemType.isNull() && | |||
623 | Descriptor.ElemType.isTriviallyCopyableType(*Context) && | |||
624 | // TypeInfo::Width is in bits. | |||
625 | Context->getTypeInfo(Descriptor.ElemType).Width <= 8 * MaxCopySize; | |||
626 | bool UseCopy = CanCopy && ((VarNameFromAlias && !AliasVarIsRef) || | |||
627 | (Descriptor.DerefByConstRef && IsCheapToCopy)); | |||
628 | ||||
629 | if (!UseCopy) { | |||
630 | if (Descriptor.DerefByConstRef) { | |||
631 | Type = Context->getLValueReferenceType(Context->getConstType(Type)); | |||
632 | } else if (Descriptor.DerefByValue) { | |||
633 | if (!IsCheapToCopy) | |||
634 | Type = Context->getRValueReferenceType(Type); | |||
635 | } else { | |||
636 | Type = Context->getLValueReferenceType(Type); | |||
637 | } | |||
638 | } | |||
639 | ||||
640 | StringRef MaybeDereference = Descriptor.ContainerNeedsDereference ? "*" : ""; | |||
641 | std::string TypeString = Type.getAsString(getLangOpts()); | |||
642 | std::string Range = ("(" + TypeString + " " + VarName + " : " + | |||
643 | MaybeDereference + Descriptor.ContainerString + ")") | |||
644 | .str(); | |||
645 | Diag << FixItHint::CreateReplacement( | |||
646 | CharSourceRange::getTokenRange(ParenRange), Range); | |||
647 | TUInfo->getGeneratedDecls().insert(make_pair(Loop, VarName)); | |||
648 | } | |||
649 | ||||
650 | /// \brief Returns a string which refers to the container iterated over. | |||
651 | StringRef LoopConvertCheck::getContainerString(ASTContext *Context, | |||
652 | const ForStmt *Loop, | |||
653 | const Expr *ContainerExpr) { | |||
654 | StringRef ContainerString; | |||
655 | if (isa<CXXThisExpr>(ContainerExpr->IgnoreParenImpCasts())) { | |||
| ||||
656 | ContainerString = "this"; | |||
657 | } else { | |||
658 | ContainerString = | |||
659 | getStringFromRange(Context->getSourceManager(), Context->getLangOpts(), | |||
660 | ContainerExpr->getSourceRange()); | |||
661 | } | |||
662 | ||||
663 | return ContainerString; | |||
664 | } | |||
665 | ||||
666 | /// \brief Determines what kind of 'auto' must be used after converting a for | |||
667 | /// loop that iterates over an array or pseudoarray. | |||
668 | void LoopConvertCheck::getArrayLoopQualifiers(ASTContext *Context, | |||
669 | const BoundNodes &Nodes, | |||
670 | const Expr *ContainerExpr, | |||
671 | const UsageResult &Usages, | |||
672 | RangeDescriptor &Descriptor) { | |||
673 | // On arrays and pseudoarrays, we must figure out the qualifiers from the | |||
674 | // usages. | |||
675 | if (usagesAreConst(Context, Usages) || | |||
676 | containerIsConst(ContainerExpr, Descriptor.ContainerNeedsDereference)) { | |||
677 | Descriptor.DerefByConstRef = true; | |||
678 | } | |||
679 | if (usagesReturnRValues(Usages)) { | |||
680 | // If the index usages (dereference, subscript, at, ...) return rvalues, | |||
681 | // then we should not use a reference, because we need to keep the code | |||
682 | // correct if it mutates the returned objects. | |||
683 | Descriptor.DerefByValue = true; | |||
684 | } | |||
685 | // Try to find the type of the elements on the container, to check if | |||
686 | // they are trivially copyable. | |||
687 | for (const Usage &U : Usages) { | |||
688 | if (!U.Expression || U.Expression->getType().isNull()) | |||
689 | continue; | |||
690 | QualType Type = U.Expression->getType().getCanonicalType(); | |||
691 | if (U.Kind == Usage::UK_MemberThroughArrow) { | |||
692 | if (!Type->isPointerType()) { | |||
693 | continue; | |||
694 | } | |||
695 | Type = Type->getPointeeType(); | |||
696 | } | |||
697 | Descriptor.ElemType = Type; | |||
698 | } | |||
699 | } | |||
700 | ||||
701 | /// \brief Determines what kind of 'auto' must be used after converting an | |||
702 | /// iterator based for loop. | |||
703 | void LoopConvertCheck::getIteratorLoopQualifiers(ASTContext *Context, | |||
704 | const BoundNodes &Nodes, | |||
705 | RangeDescriptor &Descriptor) { | |||
706 | // The matchers for iterator loops provide bound nodes to obtain this | |||
707 | // information. | |||
708 | const auto *InitVar = Nodes.getNodeAs<VarDecl>(InitVarName); | |||
709 | QualType CanonicalInitVarType = InitVar->getType().getCanonicalType(); | |||
710 | const auto *DerefByValueType = | |||
711 | Nodes.getNodeAs<QualType>(DerefByValueResultName); | |||
712 | Descriptor.DerefByValue = DerefByValueType; | |||
713 | ||||
714 | if (Descriptor.DerefByValue) { | |||
715 | // If the dereference operator returns by value then test for the | |||
716 | // canonical const qualification of the init variable type. | |||
717 | Descriptor.DerefByConstRef = CanonicalInitVarType.isConstQualified(); | |||
718 | Descriptor.ElemType = *DerefByValueType; | |||
719 | } else { | |||
720 | if (const auto *DerefType = | |||
721 | Nodes.getNodeAs<QualType>(DerefByRefResultName)) { | |||
722 | // A node will only be bound with DerefByRefResultName if we're dealing | |||
723 | // with a user-defined iterator type. Test the const qualification of | |||
724 | // the reference type. | |||
725 | auto ValueType = DerefType->getNonReferenceType(); | |||
726 | ||||
727 | Descriptor.DerefByConstRef = ValueType.isConstQualified(); | |||
728 | Descriptor.ElemType = ValueType; | |||
729 | } else { | |||
730 | // By nature of the matcher this case is triggered only for built-in | |||
731 | // iterator types (i.e. pointers). | |||
732 | assert(isa<PointerType>(CanonicalInitVarType) &&((isa<PointerType>(CanonicalInitVarType) && "Non-class iterator type is not a pointer type" ) ? static_cast<void> (0) : __assert_fail ("isa<PointerType>(CanonicalInitVarType) && \"Non-class iterator type is not a pointer type\"" , "/build/llvm-toolchain-snapshot-8~svn345461/tools/clang/tools/extra/clang-tidy/modernize/LoopConvertCheck.cpp" , 733, __PRETTY_FUNCTION__)) | |||
733 | "Non-class iterator type is not a pointer type")((isa<PointerType>(CanonicalInitVarType) && "Non-class iterator type is not a pointer type" ) ? static_cast<void> (0) : __assert_fail ("isa<PointerType>(CanonicalInitVarType) && \"Non-class iterator type is not a pointer type\"" , "/build/llvm-toolchain-snapshot-8~svn345461/tools/clang/tools/extra/clang-tidy/modernize/LoopConvertCheck.cpp" , 733, __PRETTY_FUNCTION__)); | |||
734 | ||||
735 | // We test for const qualification of the pointed-at type. | |||
736 | Descriptor.DerefByConstRef = | |||
737 | CanonicalInitVarType->getPointeeType().isConstQualified(); | |||
738 | Descriptor.ElemType = CanonicalInitVarType->getPointeeType(); | |||
739 | } | |||
740 | } | |||
741 | } | |||
742 | ||||
743 | /// \brief Determines the parameters needed to build the range replacement. | |||
744 | void LoopConvertCheck::determineRangeDescriptor( | |||
745 | ASTContext *Context, const BoundNodes &Nodes, const ForStmt *Loop, | |||
746 | LoopFixerKind FixerKind, const Expr *ContainerExpr, | |||
747 | const UsageResult &Usages, RangeDescriptor &Descriptor) { | |||
748 | Descriptor.ContainerString = getContainerString(Context, Loop, ContainerExpr); | |||
749 | ||||
750 | if (FixerKind == LFK_Iterator) | |||
751 | getIteratorLoopQualifiers(Context, Nodes, Descriptor); | |||
752 | else | |||
753 | getArrayLoopQualifiers(Context, Nodes, ContainerExpr, Usages, Descriptor); | |||
754 | } | |||
755 | ||||
756 | /// \brief Check some of the conditions that must be met for the loop to be | |||
757 | /// convertible. | |||
758 | bool LoopConvertCheck::isConvertible(ASTContext *Context, | |||
759 | const ast_matchers::BoundNodes &Nodes, | |||
760 | const ForStmt *Loop, | |||
761 | LoopFixerKind FixerKind) { | |||
762 | // If we already modified the range of this for loop, don't do any further | |||
763 | // updates on this iteration. | |||
764 | if (TUInfo->getReplacedVars().count(Loop)) | |||
765 | return false; | |||
766 | ||||
767 | // Check that we have exactly one index variable and at most one end variable. | |||
768 | const auto *LoopVar = Nodes.getNodeAs<VarDecl>(IncrementVarName); | |||
769 | const auto *CondVar = Nodes.getNodeAs<VarDecl>(ConditionVarName); | |||
770 | const auto *InitVar = Nodes.getNodeAs<VarDecl>(InitVarName); | |||
771 | if (!areSameVariable(LoopVar, CondVar) || !areSameVariable(LoopVar, InitVar)) | |||
772 | return false; | |||
773 | const auto *EndVar = Nodes.getNodeAs<VarDecl>(EndVarName); | |||
774 | const auto *ConditionEndVar = Nodes.getNodeAs<VarDecl>(ConditionEndVarName); | |||
775 | if (EndVar && !areSameVariable(EndVar, ConditionEndVar)) | |||
776 | return false; | |||
777 | ||||
778 | // FIXME: Try to put most of this logic inside a matcher. | |||
779 | if (FixerKind == LFK_Iterator) { | |||
780 | QualType InitVarType = InitVar->getType(); | |||
781 | QualType CanonicalInitVarType = InitVarType.getCanonicalType(); | |||
782 | ||||
783 | const auto *BeginCall = Nodes.getNodeAs<CXXMemberCallExpr>(BeginCallName); | |||
784 | assert(BeginCall && "Bad Callback. No begin call expression")((BeginCall && "Bad Callback. No begin call expression" ) ? static_cast<void> (0) : __assert_fail ("BeginCall && \"Bad Callback. No begin call expression\"" , "/build/llvm-toolchain-snapshot-8~svn345461/tools/clang/tools/extra/clang-tidy/modernize/LoopConvertCheck.cpp" , 784, __PRETTY_FUNCTION__)); | |||
785 | QualType CanonicalBeginType = | |||
786 | BeginCall->getMethodDecl()->getReturnType().getCanonicalType(); | |||
787 | if (CanonicalBeginType->isPointerType() && | |||
788 | CanonicalInitVarType->isPointerType()) { | |||
789 | // If the initializer and the variable are both pointers check if the | |||
790 | // un-qualified pointee types match, otherwise we don't use auto. | |||
791 | if (!Context->hasSameUnqualifiedType( | |||
792 | CanonicalBeginType->getPointeeType(), | |||
793 | CanonicalInitVarType->getPointeeType())) | |||
794 | return false; | |||
795 | } else if (!Context->hasSameType(CanonicalInitVarType, | |||
796 | CanonicalBeginType)) { | |||
797 | // Check for qualified types to avoid conversions from non-const to const | |||
798 | // iterator types. | |||
799 | return false; | |||
800 | } | |||
801 | } else if (FixerKind == LFK_PseudoArray) { | |||
802 | // This call is required to obtain the container. | |||
803 | const auto *EndCall = Nodes.getNodeAs<CXXMemberCallExpr>(EndCallName); | |||
804 | if (!EndCall || !dyn_cast<MemberExpr>(EndCall->getCallee())) | |||
805 | return false; | |||
806 | } | |||
807 | return true; | |||
808 | } | |||
809 | ||||
810 | void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) { | |||
811 | const BoundNodes &Nodes = Result.Nodes; | |||
812 | Confidence ConfidenceLevel(Confidence::CL_Safe); | |||
813 | ASTContext *Context = Result.Context; | |||
814 | ||||
815 | const ForStmt *Loop; | |||
816 | LoopFixerKind FixerKind; | |||
817 | RangeDescriptor Descriptor; | |||
818 | ||||
819 | if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameArray))) { | |||
| ||||
820 | FixerKind = LFK_Array; | |||
821 | } else if ((Loop = Nodes.getNodeAs<ForStmt>(LoopNameIterator))) { | |||
822 | FixerKind = LFK_Iterator; | |||
823 | } else { | |||
824 | Loop = Nodes.getNodeAs<ForStmt>(LoopNamePseudoArray); | |||
825 | assert(Loop && "Bad Callback. No for statement")((Loop && "Bad Callback. No for statement") ? static_cast <void> (0) : __assert_fail ("Loop && \"Bad Callback. No for statement\"" , "/build/llvm-toolchain-snapshot-8~svn345461/tools/clang/tools/extra/clang-tidy/modernize/LoopConvertCheck.cpp" , 825, __PRETTY_FUNCTION__)); | |||
826 | FixerKind = LFK_PseudoArray; | |||
827 | } | |||
828 | ||||
829 | if (!isConvertible(Context, Nodes, Loop, FixerKind)) | |||
830 | return; | |||
831 | ||||
832 | const auto *LoopVar = Nodes.getNodeAs<VarDecl>(IncrementVarName); | |||
833 | const auto *EndVar = Nodes.getNodeAs<VarDecl>(EndVarName); | |||
834 | ||||
835 | // If the loop calls end()/size() after each iteration, lower our confidence | |||
836 | // level. | |||
837 | if (FixerKind != LFK_Array && !EndVar) | |||
838 | ConfidenceLevel.lowerTo(Confidence::CL_Reasonable); | |||
839 | ||||
840 | // If the end comparison isn't a variable, we can try to work with the | |||
841 | // expression the loop variable is being tested against instead. | |||
842 | const auto *EndCall = Nodes.getNodeAs<CXXMemberCallExpr>(EndCallName); | |||
843 | const auto *BoundExpr = Nodes.getNodeAs<Expr>(ConditionBoundName); | |||
844 | ||||
845 | // Find container expression of iterators and pseudoarrays, and determine if | |||
846 | // this expression needs to be dereferenced to obtain the container. | |||
847 | // With array loops, the container is often discovered during the | |||
848 | // ForLoopIndexUseVisitor traversal. | |||
849 | const Expr *ContainerExpr = nullptr; | |||
850 | if (FixerKind == LFK_Iterator) { | |||
851 | ContainerExpr = findContainer(Context, LoopVar->getInit(), | |||
852 | EndVar ? EndVar->getInit() : EndCall, | |||
853 | &Descriptor.ContainerNeedsDereference); | |||
854 | } else if (FixerKind == LFK_PseudoArray) { | |||
855 | ContainerExpr = EndCall->getImplicitObjectArgument(); | |||
856 | Descriptor.ContainerNeedsDereference = | |||
857 | dyn_cast<MemberExpr>(EndCall->getCallee())->isArrow(); | |||
858 | } | |||
859 | ||||
860 | // We must know the container or an array length bound. | |||
861 | if (!ContainerExpr && !BoundExpr) | |||
862 | return; | |||
863 | ||||
864 | ForLoopIndexUseVisitor Finder(Context, LoopVar, EndVar, ContainerExpr, | |||
865 | BoundExpr, | |||
866 | Descriptor.ContainerNeedsDereference); | |||
867 | ||||
868 | // Find expressions and variables on which the container depends. | |||
869 | if (ContainerExpr) { | |||
870 | ComponentFinderASTVisitor ComponentFinder; | |||
871 | ComponentFinder.findExprComponents(ContainerExpr->IgnoreParenImpCasts()); | |||
872 | Finder.addComponents(ComponentFinder.getComponents()); | |||
873 | } | |||
874 | ||||
875 | // Find usages of the loop index. If they are not used in a convertible way, | |||
876 | // stop here. | |||
877 | if (!Finder.findAndVerifyUsages(Loop->getBody())) | |||
878 | return; | |||
879 | ConfidenceLevel.lowerTo(Finder.getConfidenceLevel()); | |||
880 | ||||
881 | // Obtain the container expression, if we don't have it yet. | |||
882 | if (FixerKind == LFK_Array) { | |||
883 | ContainerExpr = Finder.getContainerIndexed()->IgnoreParenImpCasts(); | |||
884 | ||||
885 | // Very few loops are over expressions that generate arrays rather than | |||
886 | // array variables. Consider loops over arrays that aren't just represented | |||
887 | // by a variable to be risky conversions. | |||
888 | if (!getReferencedVariable(ContainerExpr) && | |||
889 | !isDirectMemberExpr(ContainerExpr)) | |||
890 | ConfidenceLevel.lowerTo(Confidence::CL_Risky); | |||
891 | } | |||
892 | ||||
893 | // Find out which qualifiers we have to use in the loop range. | |||
894 | const UsageResult &Usages = Finder.getUsages(); | |||
895 | determineRangeDescriptor(Context, Nodes, Loop, FixerKind, ContainerExpr, | |||
896 | Usages, Descriptor); | |||
897 | ||||
898 | // Ensure that we do not try to move an expression dependent on a local | |||
899 | // variable declared inside the loop outside of it. | |||
900 | // FIXME: Determine when the external dependency isn't an expression converted | |||
901 | // by another loop. | |||
902 | TUInfo->getParentFinder().gatherAncestors(Context->getTranslationUnitDecl()); | |||
903 | DependencyFinderASTVisitor DependencyFinder( | |||
904 | &TUInfo->getParentFinder().getStmtToParentStmtMap(), | |||
905 | &TUInfo->getParentFinder().getDeclToParentStmtMap(), | |||
906 | &TUInfo->getReplacedVars(), Loop); | |||
907 | ||||
908 | if (DependencyFinder.dependsOnInsideVariable(ContainerExpr) || | |||
909 | Descriptor.ContainerString.empty() || Usages.empty() || | |||
910 | ConfidenceLevel.getLevel() < MinConfidence) | |||
911 | return; | |||
912 | ||||
913 | doConversion(Context, LoopVar, getReferencedVariable(ContainerExpr), Usages, | |||
914 | Finder.getAliasDecl(), Finder.aliasUseRequired(), | |||
915 | Finder.aliasFromForInit(), Loop, Descriptor); | |||
916 | } | |||
917 | ||||
918 | } // namespace modernize | |||
919 | } // namespace tidy | |||
920 | } // namespace clang |