blob: ad2393a7f7af526d831a31e6a0de34ea0985083e [file] [log] [blame]
Alexander Kornienko04970842015-08-19 09:11:46 +00001//===--- 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
14using namespace clang;
15using namespace clang::ast_matchers;
16using namespace llvm;
17
18namespace clang {
19namespace tidy {
20namespace modernize {
21
22const char LoopNameArray[] = "forLoopArray";
23const char LoopNameIterator[] = "forLoopIterator";
24const char LoopNamePseudoArray[] = "forLoopPseudoArray";
25const char ConditionBoundName[] = "conditionBound";
26const char ConditionVarName[] = "conditionVar";
27const char IncrementVarName[] = "incrementVar";
28const char InitVarName[] = "initVar";
29const char BeginCallName[] = "beginCall";
30const char EndCallName[] = "endCall";
31const char ConditionEndVarName[] = "conditionEndVar";
32const char EndVarName[] = "endVar";
33const char DerefByValueResultName[] = "derefByValueResult";
34const char DerefByRefResultName[] = "derefByRefResult";
35
36// shared matchers
37static const TypeMatcher AnyType = anything();
38
39static const StatementMatcher IntegerComparisonMatcher =
40 expr(ignoringParenImpCasts(
41 declRefExpr(to(varDecl(hasType(isInteger())).bind(ConditionVarName)))));
42
43static const DeclarationMatcher InitToZeroMatcher =
44 varDecl(hasInitializer(ignoringParenImpCasts(integerLiteral(equals(0)))))
45 .bind(InitVarName);
46
47static const StatementMatcher IncrementVarMatcher =
48 declRefExpr(to(varDecl(hasType(isInteger())).bind(IncrementVarName)));
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 identifiers are bound to these 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(
75 binaryOperator(hasOperatorName("<"),
76 hasLHS(IntegerComparisonMatcher),
77 hasRHS(ArrayBoundMatcher)),
78 binaryOperator(hasOperatorName(">"), hasLHS(ArrayBoundMatcher),
79 hasRHS(IntegerComparisonMatcher)))),
80 hasIncrement(unaryOperator(hasOperatorName("++"),
81 hasUnaryOperand(IncrementVarMatcher))))
82 .bind(LoopNameArray);
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 =
114 memberCallExpr(argumentCountIs(0), callee(methodDecl(hasName("begin"))))
115 .bind(BeginCallName);
116
117 DeclarationMatcher InitDeclMatcher =
118 varDecl(hasInitializer(anyOf(ignoringParenImpCasts(BeginCallMatcher),
119 materializeTemporaryExpr(
120 ignoringParenImpCasts(BeginCallMatcher)),
121 hasDescendant(BeginCallMatcher))))
122 .bind(InitVarName);
123
124 DeclarationMatcher EndDeclMatcher =
125 varDecl(hasInitializer(anything())).bind(EndVarName);
126
127 StatementMatcher EndCallMatcher =
128 memberCallExpr(argumentCountIs(0), callee(methodDecl(hasName("end"))));
129
130 StatementMatcher IteratorBoundMatcher =
131 expr(anyOf(ignoringParenImpCasts(
132 declRefExpr(to(varDecl().bind(ConditionEndVarName)))),
133 ignoringParenImpCasts(expr(EndCallMatcher).bind(EndCallName)),
134 materializeTemporaryExpr(ignoringParenImpCasts(
135 expr(EndCallMatcher).bind(EndCallName)))));
136
137 StatementMatcher IteratorComparisonMatcher = expr(
138 ignoringParenImpCasts(declRefExpr(to(varDecl().bind(ConditionVarName)))));
139
140 StatementMatcher OverloadedNEQMatcher =
141 operatorCallExpr(hasOverloadedOperatorName("!="), argumentCountIs(2),
142 hasArgument(0, IteratorComparisonMatcher),
143 hasArgument(1, IteratorBoundMatcher));
144
145 // This matcher tests that a declaration is a CXXRecordDecl that has an
146 // overloaded operator*(). If the operator*() returns by value instead of by
147 // reference then the return type is tagged with DerefByValueResultName.
148 internal::Matcher<VarDecl> TestDerefReturnsByValue =
149 hasType(recordDecl(hasMethod(allOf(
150 hasOverloadedOperatorName("*"),
151 anyOf(
152 // Tag the return type if it's by value.
153 returns(qualType(unless(hasCanonicalType(referenceType())))
154 .bind(DerefByValueResultName)),
155 returns(
156 // Skip loops where the iterator's operator* returns an
157 // rvalue reference. This is just weird.
158 qualType(unless(hasCanonicalType(rValueReferenceType())))
159 .bind(DerefByRefResultName)))))));
160
161 return forStmt(
162 hasLoopInit(anyOf(declStmt(declCountIs(2),
163 containsDeclaration(0, InitDeclMatcher),
164 containsDeclaration(1, EndDeclMatcher)),
165 declStmt(hasSingleDecl(InitDeclMatcher)))),
166 hasCondition(
167 anyOf(binaryOperator(hasOperatorName("!="),
168 hasLHS(IteratorComparisonMatcher),
169 hasRHS(IteratorBoundMatcher)),
170 binaryOperator(hasOperatorName("!="),
171 hasLHS(IteratorBoundMatcher),
172 hasRHS(IteratorComparisonMatcher)),
173 OverloadedNEQMatcher)),
174 hasIncrement(anyOf(
175 unaryOperator(hasOperatorName("++"),
176 hasUnaryOperand(declRefExpr(
177 to(varDecl(hasType(pointsTo(AnyType)))
178 .bind(IncrementVarName))))),
179 operatorCallExpr(
180 hasOverloadedOperatorName("++"),
181 hasArgument(
182 0, declRefExpr(to(varDecl(TestDerefReturnsByValue)
183 .bind(IncrementVarName))))))))
184 .bind(LoopNameIterator);
185}
186
187/// \brief The matcher used for array-like containers (pseudoarrays).
188///
189/// This matcher is more flexible than array-based loops. It will match
190/// loops of the following textual forms (regardless of whether the
191/// iterator type is actually a pointer type or a class type):
192///
193/// Assuming f, g, and h are of type containerType::iterator,
194/// \code
195/// for (int i = 0, j = container.size(); f < g; ++h) { ... }
196/// for (int i = 0; f < container.size(); ++h) { ... }
197/// \endcode
198/// The following string identifiers are bound to the parts of the AST:
199/// InitVarName: 'i' (as a VarDecl)
200/// ConditionVarName: 'f' (as a VarDecl)
201/// LoopName: The entire for loop (as a ForStmt)
202/// In the first example only:
203/// EndVarName: 'j' (as a VarDecl)
204/// ConditionEndVarName: 'g' (as a VarDecl)
205/// In the second example only:
206/// EndCallName: 'container.size()' (as a CXXMemberCallExpr)
207///
208/// Client code will need to make sure that:
209/// - The index variables 'i', 'f', and 'h' are the same.
210/// - The containers on which 'size()' is called is the container indexed.
211/// - The index variable is only used in overloaded operator[] or
212/// container.at().
213/// - If the end iterator variable 'g' is defined, it is the same as 'j'.
214/// - The container's iterators would not be invalidated during the loop.
215StatementMatcher makePseudoArrayLoopMatcher() {
216 // Test that the incoming type has a record declaration that has methods
217 // called 'begin' and 'end'. If the incoming type is const, then make sure
218 // these methods are also marked const.
219 //
220 // FIXME: To be completely thorough this matcher should also ensure the
221 // return type of begin/end is an iterator that dereferences to the same as
222 // what operator[] or at() returns. Such a test isn't likely to fail except
223 // for pathological cases.
224 //
225 // FIXME: Also, a record doesn't necessarily need begin() and end(). Free
226 // functions called begin() and end() taking the container as an argument
227 // are also allowed.
228 TypeMatcher RecordWithBeginEnd = qualType(
229 anyOf(qualType(isConstQualified(),
230 hasDeclaration(recordDecl(
231 hasMethod(methodDecl(hasName("begin"), isConst())),
232 hasMethod(methodDecl(hasName("end"),
233 isConst())))) // hasDeclaration
234 ), // qualType
235 qualType(unless(isConstQualified()),
236 hasDeclaration(
237 recordDecl(hasMethod(hasName("begin")),
238 hasMethod(hasName("end"))))) // qualType
239 ));
240
241 StatementMatcher SizeCallMatcher = memberCallExpr(
242 argumentCountIs(0),
243 callee(methodDecl(anyOf(hasName("size"), hasName("length")))),
244 on(anyOf(hasType(pointsTo(RecordWithBeginEnd)),
245 hasType(RecordWithBeginEnd))));
246
247 StatementMatcher EndInitMatcher =
248 expr(anyOf(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(ignoringParenImpCasts(declRefExpr(to(
257 varDecl(hasType(isInteger())).bind(ConditionEndVarName)))),
258 EndInitMatcher));
259
260 return forStmt(
261 hasLoopInit(
262 anyOf(declStmt(declCountIs(2),
263 containsDeclaration(0, InitToZeroMatcher),
264 containsDeclaration(1, EndDeclMatcher)),
265 declStmt(hasSingleDecl(InitToZeroMatcher)))),
266 hasCondition(anyOf(
267 binaryOperator(hasOperatorName("<"),
268 hasLHS(IntegerComparisonMatcher),
269 hasRHS(IndexBoundMatcher)),
270 binaryOperator(hasOperatorName(">"), hasLHS(IndexBoundMatcher),
271 hasRHS(IntegerComparisonMatcher)))),
272 hasIncrement(unaryOperator(hasOperatorName("++"),
273 hasUnaryOperand(IncrementVarMatcher))))
274 .bind(LoopNamePseudoArray);
275}
276
277/// \brief Determine whether Init appears to be an initializing an iterator.
278///
279/// If it is, returns the object whose begin() or end() method is called, and
280/// the output parameter isArrow is set to indicate whether the initialization
281/// is called via . or ->.
282static const Expr *getContainerFromBeginEndCall(const Expr *Init, bool IsBegin,
283 bool *IsArrow) {
284 // FIXME: Maybe allow declaration/initialization outside of the for loop.
285 const auto *TheCall =
286 dyn_cast_or_null<CXXMemberCallExpr>(digThroughConstructors(Init));
287 if (!TheCall || TheCall->getNumArgs() != 0)
288 return nullptr;
289
290 const auto *Member = dyn_cast<MemberExpr>(TheCall->getCallee());
291 if (!Member)
292 return nullptr;
293 StringRef Name = Member->getMemberDecl()->getName();
294 StringRef TargetName = IsBegin ? "begin" : "end";
295 if (Name != TargetName)
296 return nullptr;
297
298 const Expr *SourceExpr = Member->getBase();
299 if (!SourceExpr)
300 return nullptr;
301
302 *IsArrow = Member->isArrow();
303 return SourceExpr;
304}
305
306/// \brief Determines the container whose begin() and end() functions are called
307/// for an iterator-based loop.
308///
309/// BeginExpr must be a member call to a function named "begin()", and EndExpr
310/// must be a member.
311static const Expr *findContainer(ASTContext *Context, const Expr *BeginExpr,
312 const Expr *EndExpr,
313 bool *ContainerNeedsDereference) {
314 // Now that we know the loop variable and test expression, make sure they are
315 // valid.
316 bool BeginIsArrow = false;
317 bool EndIsArrow = false;
318 const Expr *BeginContainerExpr =
319 getContainerFromBeginEndCall(BeginExpr, /*IsBegin=*/true, &BeginIsArrow);
320 if (!BeginContainerExpr)
321 return nullptr;
322
323 const Expr *EndContainerExpr =
324 getContainerFromBeginEndCall(EndExpr, /*IsBegin=*/false, &EndIsArrow);
325 // Disallow loops that try evil things like this (note the dot and arrow):
326 // for (IteratorType It = Obj.begin(), E = Obj->end(); It != E; ++It) { }
327 if (!EndContainerExpr || BeginIsArrow != EndIsArrow ||
328 !areSameExpr(Context, EndContainerExpr, BeginContainerExpr))
329 return nullptr;
330
331 *ContainerNeedsDereference = BeginIsArrow;
332 return BeginContainerExpr;
333}
334
335/// \brief Obtain the original source code text from a SourceRange.
336static StringRef getStringFromRange(SourceManager &SourceMgr,
337 const LangOptions &LangOpts,
338 SourceRange Range) {
339 if (SourceMgr.getFileID(Range.getBegin()) !=
340 SourceMgr.getFileID(Range.getEnd()))
341 return nullptr;
342
343 return Lexer::getSourceText(CharSourceRange(Range, true), SourceMgr,
344 LangOpts);
345}
346
347/// \brief If the given expression is actually a DeclRefExpr, find and return
348/// the underlying VarDecl; otherwise, return NULL.
349static const VarDecl *getReferencedVariable(const Expr *E) {
350 if (const DeclRefExpr *DRE = getDeclRef(E))
351 return dyn_cast<VarDecl>(DRE->getDecl());
352 return nullptr;
353}
354
355/// \brief Returns true when the given expression is a member expression
356/// whose base is `this` (implicitly or not).
357static bool isDirectMemberExpr(const Expr *E) {
358 if (const auto *Member = dyn_cast<MemberExpr>(E->IgnoreParenImpCasts()))
359 return isa<CXXThisExpr>(Member->getBase()->IgnoreParenImpCasts());
360 return false;
361}
362
363LoopConvertCheck::LoopConvertCheck(StringRef Name, ClangTidyContext *Context)
364 : ClangTidyCheck(Name, Context), TUInfo(new TUTrackingInfo),
365 MinConfidence(StringSwitch<Confidence::Level>(
366 Options.get("MinConfidence", "reasonable"))
367 .Case("safe", Confidence::CL_Safe)
368 .Case("risky", Confidence::CL_Risky)
369 .Default(Confidence::CL_Reasonable)) {}
370
371void LoopConvertCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
372 SmallVector<std::string, 3> Confs{"risky", "reasonable", "safe"};
373 Options.store(Opts, "MinConfidence", Confs[static_cast<int>(MinConfidence)]);
374}
375
376/// \brief Computes the changes needed to convert a given for loop, and
377/// applies it.
378void LoopConvertCheck::doConversion(
379 ASTContext *Context, const VarDecl *IndexVar, const VarDecl *MaybeContainer,
380 StringRef ContainerString, const UsageResult &Usages,
381 const DeclStmt *AliasDecl, bool AliasUseRequired, bool AliasFromForInit,
382 const ForStmt *TheLoop, bool ContainerNeedsDereference, bool DerefByValue,
383 bool DerefByConstRef) {
384 auto Diag = diag(TheLoop->getForLoc(), "use range-based for loop instead");
385
386 std::string VarName;
387 bool VarNameFromAlias = (Usages.size() == 1) && AliasDecl;
388 bool AliasVarIsRef = false;
389
390 if (VarNameFromAlias) {
391 const auto *AliasVar = cast<VarDecl>(AliasDecl->getSingleDecl());
392 VarName = AliasVar->getName().str();
393 AliasVarIsRef = AliasVar->getType()->isReferenceType();
394
395 // We keep along the entire DeclStmt to keep the correct range here.
396 const SourceRange &ReplaceRange = AliasDecl->getSourceRange();
397
398 std::string ReplacementText;
399 if (AliasUseRequired) {
400 ReplacementText = VarName;
401 } else if (AliasFromForInit) {
402 // FIXME: Clang includes the location of the ';' but only for DeclStmt's
403 // in a for loop's init clause. Need to put this ';' back while removing
404 // the declaration of the alias variable. This is probably a bug.
405 ReplacementText = ";";
406 }
407
408 Diag << FixItHint::CreateReplacement(
409 CharSourceRange::getTokenRange(ReplaceRange), ReplacementText);
410 // No further replacements are made to the loop, since the iterator or index
411 // was used exactly once - in the initialization of AliasVar.
412 } else {
413 VariableNamer Namer(&TUInfo->getGeneratedDecls(),
414 &TUInfo->getParentFinder().getStmtToParentStmtMap(),
415 TheLoop, IndexVar, MaybeContainer, Context);
416 VarName = Namer.createIndexName();
417 // First, replace all usages of the array subscript expression with our new
418 // variable.
419 for (const auto &I : Usages) {
Alexander Kornienkoe1292f82015-08-19 16:54:51 +0000420 std::string ReplaceText = I.IsArrow ? VarName + "." : VarName;
Alexander Kornienko04970842015-08-19 09:11:46 +0000421 TUInfo->getReplacedVars().insert(std::make_pair(TheLoop, IndexVar));
422 Diag << FixItHint::CreateReplacement(
423 CharSourceRange::getTokenRange(I.Range), ReplaceText);
424 }
425 }
426
427 // Now, we need to construct the new range expression.
428 SourceRange ParenRange(TheLoop->getLParenLoc(), TheLoop->getRParenLoc());
429
430 QualType AutoRefType = Context->getAutoDeductType();
431
432 // If the new variable name is from the aliased variable, then the reference
433 // type for the new variable should only be used if the aliased variable was
434 // declared as a reference.
435 if (!VarNameFromAlias || AliasVarIsRef) {
436 // If an iterator's operator*() returns a 'T&' we can bind that to 'auto&'.
437 // If operator*() returns 'T' we can bind that to 'auto&&' which will deduce
438 // to 'T&&&'.
439 if (DerefByValue) {
440 AutoRefType = Context->getRValueReferenceType(AutoRefType);
441 } else {
442 if (DerefByConstRef)
443 AutoRefType = Context->getConstType(AutoRefType);
444 AutoRefType = Context->getLValueReferenceType(AutoRefType);
445 }
446 }
447
448 StringRef MaybeDereference = ContainerNeedsDereference ? "*" : "";
Alexander Kornienkoe1292f82015-08-19 16:54:51 +0000449 std::string TypeString = AutoRefType.getAsString();
450 std::string Range = ("(" + TypeString + " " + VarName + " : " +
451 MaybeDereference + ContainerString + ")").str();
Alexander Kornienko04970842015-08-19 09:11:46 +0000452 Diag << FixItHint::CreateReplacement(
453 CharSourceRange::getTokenRange(ParenRange), Range);
454 TUInfo->getGeneratedDecls().insert(make_pair(TheLoop, VarName));
455}
456
457/// \brief Determine if the change should be deferred or rejected, returning
458/// text which refers to the container iterated over if the change should
459/// proceed.
460StringRef LoopConvertCheck::checkRejections(ASTContext *Context,
461 const Expr *ContainerExpr,
462 const ForStmt *TheLoop) {
463 // If we already modified the reange of this for loop, don't do any further
464 // updates on this iteration.
465 if (TUInfo->getReplacedVars().count(TheLoop))
466 return "";
467
468 Context->getTranslationUnitDecl();
469 TUInfo->getParentFinder();
470 TUInfo->getParentFinder().gatherAncestors(Context->getTranslationUnitDecl());
471 // Ensure that we do not try to move an expression dependent on a local
472 // variable declared inside the loop outside of it.
473 DependencyFinderASTVisitor DependencyFinder(
474 &TUInfo->getParentFinder().getStmtToParentStmtMap(),
475 &TUInfo->getParentFinder().getDeclToParentStmtMap(),
476 &TUInfo->getReplacedVars(), TheLoop);
477
478 // FIXME: Determine when the external dependency isn't an expression converted
479 // by another loop.
480 if (DependencyFinder.dependsOnInsideVariable(ContainerExpr))
481 return "";
482
483 StringRef ContainerString;
484 if (isa<CXXThisExpr>(ContainerExpr->IgnoreParenImpCasts())) {
485 ContainerString = "this";
486 } else {
487 ContainerString =
488 getStringFromRange(Context->getSourceManager(), Context->getLangOpts(),
489 ContainerExpr->getSourceRange());
490 }
491
492 return ContainerString;
493}
494
495/// \brief Given a loop header that would be convertible, discover all usages
496/// of the index variable and convert the loop if possible.
497void LoopConvertCheck::findAndVerifyUsages(
498 ASTContext *Context, const VarDecl *LoopVar, const VarDecl *EndVar,
499 const Expr *ContainerExpr, const Expr *BoundExpr,
500 bool ContainerNeedsDereference, bool DerefByValue, bool DerefByConstRef,
501 const ForStmt *TheLoop, LoopFixerKind FixerKind) {
502 ForLoopIndexUseVisitor Finder(Context, LoopVar, EndVar, ContainerExpr,
503 BoundExpr, ContainerNeedsDereference);
504
505 if (ContainerExpr) {
506 ComponentFinderASTVisitor ComponentFinder;
507 ComponentFinder.findExprComponents(ContainerExpr->IgnoreParenImpCasts());
508 Finder.addComponents(ComponentFinder.getComponents());
509 }
510
511 if (!Finder.findAndVerifyUsages(TheLoop->getBody()))
512 return;
513
514 Confidence ConfidenceLevel(Finder.getConfidenceLevel());
515 if (FixerKind == LFK_Array) {
516 // The array being indexed by IndexVar was discovered during traversal.
517 ContainerExpr = Finder.getContainerIndexed()->IgnoreParenImpCasts();
518 // Very few loops are over expressions that generate arrays rather than
519 // array variables. Consider loops over arrays that aren't just represented
520 // by a variable to be risky conversions.
521 if (!getReferencedVariable(ContainerExpr) &&
522 !isDirectMemberExpr(ContainerExpr))
523 ConfidenceLevel.lowerTo(Confidence::CL_Risky);
524 }
525
526 StringRef ContainerString = checkRejections(Context, ContainerExpr, TheLoop);
527
528 if (ContainerString.empty() || ConfidenceLevel.getLevel() < MinConfidence)
529 return;
530
531 doConversion(Context, LoopVar, getReferencedVariable(ContainerExpr),
532 ContainerString, Finder.getUsages(), Finder.getAliasDecl(),
533 Finder.aliasUseRequired(), Finder.aliasFromForInit(), TheLoop,
534 ContainerNeedsDereference, DerefByValue, DerefByConstRef);
535}
536
537void LoopConvertCheck::registerMatchers(MatchFinder *Finder) {
538 Finder->addMatcher(makeArrayLoopMatcher(), this);
539 Finder->addMatcher(makeIteratorLoopMatcher(), this);
540 Finder->addMatcher(makePseudoArrayLoopMatcher(), this);
541}
542
543void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
544 const BoundNodes &Nodes = Result.Nodes;
545 Confidence ConfidenceLevel(Confidence::CL_Safe);
546 ASTContext *Context = Result.Context;
547
548 const ForStmt *TheLoop;
549 LoopFixerKind FixerKind;
550
551 if ((TheLoop = Nodes.getStmtAs<ForStmt>(LoopNameArray))) {
552 FixerKind = LFK_Array;
553 } else if ((TheLoop = Nodes.getStmtAs<ForStmt>(LoopNameIterator))) {
554 FixerKind = LFK_Iterator;
555 } else {
556 TheLoop = Nodes.getStmtAs<ForStmt>(LoopNamePseudoArray);
557 assert(TheLoop && "Bad Callback. No for statement");
558 FixerKind = LFK_PseudoArray;
559 }
560
561 // Check that we have exactly one index variable and at most one end variable.
562 const auto *LoopVar = Nodes.getDeclAs<VarDecl>(IncrementVarName);
563 const auto *CondVar = Nodes.getDeclAs<VarDecl>(ConditionVarName);
564 const auto *InitVar = Nodes.getDeclAs<VarDecl>(InitVarName);
565 if (!areSameVariable(LoopVar, CondVar) || !areSameVariable(LoopVar, InitVar))
566 return;
567 const auto *EndVar = Nodes.getDeclAs<VarDecl>(EndVarName);
568 const auto *ConditionEndVar = Nodes.getDeclAs<VarDecl>(ConditionEndVarName);
569 if (EndVar && !areSameVariable(EndVar, ConditionEndVar))
570 return;
571
572 // If the end comparison isn't a variable, we can try to work with the
573 // expression the loop variable is being tested against instead.
574 const auto *EndCall = Nodes.getStmtAs<CXXMemberCallExpr>(EndCallName);
575 const auto *BoundExpr = Nodes.getStmtAs<Expr>(ConditionBoundName);
576 // If the loop calls end()/size() after each iteration, lower our confidence
577 // level.
578 if (FixerKind != LFK_Array && !EndVar)
579 ConfidenceLevel.lowerTo(Confidence::CL_Reasonable);
580
581 const Expr *ContainerExpr = nullptr;
582 bool DerefByValue = false;
583 bool DerefByConstRef = false;
584 bool ContainerNeedsDereference = false;
585 // FIXME: Try to put most of this logic inside a matcher. Currently, matchers
586 // don't allow the ight-recursive checks in digThroughConstructors.
587 if (FixerKind == LFK_Iterator) {
588 ContainerExpr = findContainer(Context, LoopVar->getInit(),
589 EndVar ? EndVar->getInit() : EndCall,
590 &ContainerNeedsDereference);
591
592 QualType InitVarType = InitVar->getType();
593 QualType CanonicalInitVarType = InitVarType.getCanonicalType();
594
595 const auto *BeginCall = Nodes.getNodeAs<CXXMemberCallExpr>(BeginCallName);
596 assert(BeginCall && "Bad Callback. No begin call expression");
597 QualType CanonicalBeginType =
598 BeginCall->getMethodDecl()->getReturnType().getCanonicalType();
599 if (CanonicalBeginType->isPointerType() &&
600 CanonicalInitVarType->isPointerType()) {
601 QualType BeginPointeeType = CanonicalBeginType->getPointeeType();
602 QualType InitPointeeType = CanonicalInitVarType->getPointeeType();
603 // If the initializer and the variable are both pointers check if the
604 // un-qualified pointee types match otherwise we don't use auto.
605 if (!Context->hasSameUnqualifiedType(InitPointeeType, BeginPointeeType))
606 return;
607 } else {
608 // Check for qualified types to avoid conversions from non-const to const
609 // iterator types.
610 if (!Context->hasSameType(CanonicalInitVarType, CanonicalBeginType))
611 return;
612 }
613
614 DerefByValue = Nodes.getNodeAs<QualType>(DerefByValueResultName) != nullptr;
615 if (!DerefByValue) {
616 if (const auto *DerefType =
617 Nodes.getNodeAs<QualType>(DerefByRefResultName)) {
618 // A node will only be bound with DerefByRefResultName if we're dealing
619 // with a user-defined iterator type. Test the const qualification of
620 // the reference type.
621 DerefByConstRef = (*DerefType)
622 ->getAs<ReferenceType>()
623 ->getPointeeType()
624 .isConstQualified();
625 } else {
626 // By nature of the matcher this case is triggered only for built-in
627 // iterator types (i.e. pointers).
628 assert(isa<PointerType>(CanonicalInitVarType) &&
629 "Non-class iterator type is not a pointer type");
630 QualType InitPointeeType = CanonicalInitVarType->getPointeeType();
631 QualType BeginPointeeType = CanonicalBeginType->getPointeeType();
632 // If the initializer and variable have both the same type just use auto
633 // otherwise we test for const qualification of the pointed-at type.
634 if (!Context->hasSameType(InitPointeeType, BeginPointeeType))
635 DerefByConstRef = InitPointeeType.isConstQualified();
636 }
637 } else {
638 // If the dereference operator returns by value then test for the
639 // canonical const qualification of the init variable type.
640 DerefByConstRef = CanonicalInitVarType.isConstQualified();
641 }
642 } else if (FixerKind == LFK_PseudoArray) {
643 if (!EndCall)
644 return;
645 ContainerExpr = EndCall->getImplicitObjectArgument();
646 const auto *Member = dyn_cast<MemberExpr>(EndCall->getCallee());
647 if (!Member)
648 return;
649 ContainerNeedsDereference = Member->isArrow();
650 }
651
652 // We must know the container or an array length bound.
653 if (!ContainerExpr && !BoundExpr)
654 return;
655
656 if (ConfidenceLevel.getLevel() < MinConfidence)
657 return;
658
659 findAndVerifyUsages(Context, LoopVar, EndVar, ContainerExpr, BoundExpr,
660 ContainerNeedsDereference, DerefByValue, DerefByConstRef,
661 TheLoop, FixerKind);
662}
663
664} // namespace modernize
665} // namespace tidy
666} // namespace clang