blob: a476f6c92c1ff5b1fa2b99601dccf6a7168197e7 [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(
Angel Garcia Gomez06d010c2015-08-25 15:44:00 +000073 unless(isInTemplateInstantiation()),
Alexander Kornienko04970842015-08-19 09:11:46 +000074 hasLoopInit(declStmt(hasSingleDecl(InitToZeroMatcher))),
75 hasCondition(anyOf(
76 binaryOperator(hasOperatorName("<"),
77 hasLHS(IntegerComparisonMatcher),
78 hasRHS(ArrayBoundMatcher)),
79 binaryOperator(hasOperatorName(">"), hasLHS(ArrayBoundMatcher),
80 hasRHS(IntegerComparisonMatcher)))),
81 hasIncrement(unaryOperator(hasOperatorName("++"),
82 hasUnaryOperand(IncrementVarMatcher))))
83 .bind(LoopNameArray);
84}
85
86/// \brief The matcher used for iterator-based for loops.
87///
88/// This matcher is more flexible than array-based loops. It will match
89/// catch loops of the following textual forms (regardless of whether the
90/// iterator type is actually a pointer type or a class type):
91///
92/// Assuming f, g, and h are of type containerType::iterator,
93/// \code
94/// for (containerType::iterator it = container.begin(),
95/// e = createIterator(); f != g; ++h) { ... }
96/// for (containerType::iterator it = container.begin();
97/// f != anotherContainer.end(); ++h) { ... }
98/// \endcode
99/// The following string identifiers are bound to the parts of the AST:
100/// InitVarName: 'it' (as a VarDecl)
101/// ConditionVarName: 'f' (as a VarDecl)
102/// LoopName: The entire for loop (as a ForStmt)
103/// In the first example only:
104/// EndVarName: 'e' (as a VarDecl)
105/// ConditionEndVarName: 'g' (as a VarDecl)
106/// In the second example only:
107/// EndCallName: 'container.end()' (as a CXXMemberCallExpr)
108///
109/// Client code will need to make sure that:
110/// - The iterator variables 'it', 'f', and 'h' are the same.
111/// - The two containers on which 'begin' and 'end' are called are the same.
112/// - If the end iterator variable 'g' is defined, it is the same as 'f'.
113StatementMatcher makeIteratorLoopMatcher() {
114 StatementMatcher BeginCallMatcher =
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000115 cxxMemberCallExpr(argumentCountIs(0),
116 callee(cxxMethodDecl(hasName("begin"))))
Alexander Kornienko04970842015-08-19 09:11:46 +0000117 .bind(BeginCallName);
118
119 DeclarationMatcher InitDeclMatcher =
120 varDecl(hasInitializer(anyOf(ignoringParenImpCasts(BeginCallMatcher),
121 materializeTemporaryExpr(
122 ignoringParenImpCasts(BeginCallMatcher)),
123 hasDescendant(BeginCallMatcher))))
124 .bind(InitVarName);
125
126 DeclarationMatcher EndDeclMatcher =
127 varDecl(hasInitializer(anything())).bind(EndVarName);
128
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000129 StatementMatcher EndCallMatcher = cxxMemberCallExpr(
130 argumentCountIs(0), callee(cxxMethodDecl(hasName("end"))));
Alexander Kornienko04970842015-08-19 09:11:46 +0000131
132 StatementMatcher IteratorBoundMatcher =
133 expr(anyOf(ignoringParenImpCasts(
134 declRefExpr(to(varDecl().bind(ConditionEndVarName)))),
135 ignoringParenImpCasts(expr(EndCallMatcher).bind(EndCallName)),
136 materializeTemporaryExpr(ignoringParenImpCasts(
137 expr(EndCallMatcher).bind(EndCallName)))));
138
139 StatementMatcher IteratorComparisonMatcher = expr(
140 ignoringParenImpCasts(declRefExpr(to(varDecl().bind(ConditionVarName)))));
141
142 StatementMatcher OverloadedNEQMatcher =
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000143 cxxOperatorCallExpr(hasOverloadedOperatorName("!="), argumentCountIs(2),
144 hasArgument(0, IteratorComparisonMatcher),
145 hasArgument(1, IteratorBoundMatcher));
Alexander Kornienko04970842015-08-19 09:11:46 +0000146
147 // This matcher tests that a declaration is a CXXRecordDecl that has an
148 // overloaded operator*(). If the operator*() returns by value instead of by
149 // reference then the return type is tagged with DerefByValueResultName.
150 internal::Matcher<VarDecl> TestDerefReturnsByValue =
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000151 hasType(cxxRecordDecl(hasMethod(allOf(
Alexander Kornienko04970842015-08-19 09:11:46 +0000152 hasOverloadedOperatorName("*"),
153 anyOf(
154 // Tag the return type if it's by value.
155 returns(qualType(unless(hasCanonicalType(referenceType())))
156 .bind(DerefByValueResultName)),
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 .bind(DerefByRefResultName)))))));
162
163 return forStmt(
Angel Garcia Gomez06d010c2015-08-25 15:44:00 +0000164 unless(isInTemplateInstantiation()),
Alexander Kornienko04970842015-08-19 09:11:46 +0000165 hasLoopInit(anyOf(declStmt(declCountIs(2),
166 containsDeclaration(0, InitDeclMatcher),
167 containsDeclaration(1, EndDeclMatcher)),
168 declStmt(hasSingleDecl(InitDeclMatcher)))),
169 hasCondition(
170 anyOf(binaryOperator(hasOperatorName("!="),
171 hasLHS(IteratorComparisonMatcher),
172 hasRHS(IteratorBoundMatcher)),
173 binaryOperator(hasOperatorName("!="),
174 hasLHS(IteratorBoundMatcher),
175 hasRHS(IteratorComparisonMatcher)),
176 OverloadedNEQMatcher)),
177 hasIncrement(anyOf(
178 unaryOperator(hasOperatorName("++"),
179 hasUnaryOperand(declRefExpr(
180 to(varDecl(hasType(pointsTo(AnyType)))
181 .bind(IncrementVarName))))),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000182 cxxOperatorCallExpr(
Alexander Kornienko04970842015-08-19 09:11:46 +0000183 hasOverloadedOperatorName("++"),
184 hasArgument(
185 0, declRefExpr(to(varDecl(TestDerefReturnsByValue)
186 .bind(IncrementVarName))))))))
187 .bind(LoopNameIterator);
188}
189
190/// \brief The matcher used for array-like containers (pseudoarrays).
191///
192/// This matcher is more flexible than array-based loops. It will match
193/// loops of the following textual forms (regardless of whether the
194/// iterator type is actually a pointer type or a class type):
195///
196/// Assuming f, g, and h are of type containerType::iterator,
197/// \code
198/// for (int i = 0, j = container.size(); f < g; ++h) { ... }
199/// for (int i = 0; f < container.size(); ++h) { ... }
200/// \endcode
201/// The following string identifiers are bound to the parts of the AST:
202/// InitVarName: 'i' (as a VarDecl)
203/// ConditionVarName: 'f' (as a VarDecl)
204/// LoopName: The entire for loop (as a ForStmt)
205/// In the first example only:
206/// EndVarName: 'j' (as a VarDecl)
207/// ConditionEndVarName: 'g' (as a VarDecl)
208/// In the second example only:
209/// EndCallName: 'container.size()' (as a CXXMemberCallExpr)
210///
211/// Client code will need to make sure that:
212/// - The index variables 'i', 'f', and 'h' are the same.
213/// - The containers on which 'size()' is called is the container indexed.
214/// - The index variable is only used in overloaded operator[] or
215/// container.at().
216/// - If the end iterator variable 'g' is defined, it is the same as 'j'.
217/// - The container's iterators would not be invalidated during the loop.
218StatementMatcher makePseudoArrayLoopMatcher() {
219 // Test that the incoming type has a record declaration that has methods
220 // called 'begin' and 'end'. If the incoming type is const, then make sure
221 // these methods are also marked const.
222 //
223 // FIXME: To be completely thorough this matcher should also ensure the
224 // return type of begin/end is an iterator that dereferences to the same as
225 // what operator[] or at() returns. Such a test isn't likely to fail except
226 // for pathological cases.
227 //
228 // FIXME: Also, a record doesn't necessarily need begin() and end(). Free
229 // functions called begin() and end() taking the container as an argument
230 // are also allowed.
231 TypeMatcher RecordWithBeginEnd = qualType(
232 anyOf(qualType(isConstQualified(),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000233 hasDeclaration(cxxRecordDecl(
234 hasMethod(cxxMethodDecl(hasName("begin"), isConst())),
235 hasMethod(cxxMethodDecl(hasName("end"),
236 isConst())))) // hasDeclaration
237 ), // qualType
Alexander Kornienko04970842015-08-19 09:11:46 +0000238 qualType(unless(isConstQualified()),
239 hasDeclaration(
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000240 cxxRecordDecl(hasMethod(hasName("begin")),
241 hasMethod(hasName("end"))))) // qualType
Alexander Kornienko04970842015-08-19 09:11:46 +0000242 ));
243
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000244 StatementMatcher SizeCallMatcher = cxxMemberCallExpr(
Alexander Kornienko04970842015-08-19 09:11:46 +0000245 argumentCountIs(0),
Aaron Ballmanb9ea09c2015-09-17 13:31:25 +0000246 callee(cxxMethodDecl(anyOf(hasName("size"), hasName("length")))),
Alexander Kornienko04970842015-08-19 09:11:46 +0000247 on(anyOf(hasType(pointsTo(RecordWithBeginEnd)),
248 hasType(RecordWithBeginEnd))));
249
250 StatementMatcher EndInitMatcher =
251 expr(anyOf(ignoringParenImpCasts(expr(SizeCallMatcher).bind(EndCallName)),
252 explicitCastExpr(hasSourceExpression(ignoringParenImpCasts(
253 expr(SizeCallMatcher).bind(EndCallName))))));
254
255 DeclarationMatcher EndDeclMatcher =
256 varDecl(hasInitializer(EndInitMatcher)).bind(EndVarName);
257
258 StatementMatcher IndexBoundMatcher =
259 expr(anyOf(ignoringParenImpCasts(declRefExpr(to(
260 varDecl(hasType(isInteger())).bind(ConditionEndVarName)))),
261 EndInitMatcher));
262
263 return forStmt(
Angel Garcia Gomez06d010c2015-08-25 15:44:00 +0000264 unless(isInTemplateInstantiation()),
Alexander Kornienko04970842015-08-19 09:11:46 +0000265 hasLoopInit(
266 anyOf(declStmt(declCountIs(2),
267 containsDeclaration(0, InitToZeroMatcher),
268 containsDeclaration(1, EndDeclMatcher)),
269 declStmt(hasSingleDecl(InitToZeroMatcher)))),
270 hasCondition(anyOf(
271 binaryOperator(hasOperatorName("<"),
272 hasLHS(IntegerComparisonMatcher),
273 hasRHS(IndexBoundMatcher)),
274 binaryOperator(hasOperatorName(">"), hasLHS(IndexBoundMatcher),
275 hasRHS(IntegerComparisonMatcher)))),
276 hasIncrement(unaryOperator(hasOperatorName("++"),
277 hasUnaryOperand(IncrementVarMatcher))))
278 .bind(LoopNamePseudoArray);
279}
280
281/// \brief Determine whether Init appears to be an initializing an iterator.
282///
283/// If it is, returns the object whose begin() or end() method is called, and
284/// the output parameter isArrow is set to indicate whether the initialization
285/// is called via . or ->.
286static const Expr *getContainerFromBeginEndCall(const Expr *Init, bool IsBegin,
287 bool *IsArrow) {
288 // FIXME: Maybe allow declaration/initialization outside of the for loop.
289 const auto *TheCall =
290 dyn_cast_or_null<CXXMemberCallExpr>(digThroughConstructors(Init));
291 if (!TheCall || TheCall->getNumArgs() != 0)
292 return nullptr;
293
294 const auto *Member = dyn_cast<MemberExpr>(TheCall->getCallee());
295 if (!Member)
296 return nullptr;
297 StringRef Name = Member->getMemberDecl()->getName();
298 StringRef TargetName = IsBegin ? "begin" : "end";
299 if (Name != TargetName)
300 return nullptr;
301
302 const Expr *SourceExpr = Member->getBase();
303 if (!SourceExpr)
304 return nullptr;
305
306 *IsArrow = Member->isArrow();
307 return SourceExpr;
308}
309
310/// \brief Determines the container whose begin() and end() functions are called
311/// for an iterator-based loop.
312///
313/// BeginExpr must be a member call to a function named "begin()", and EndExpr
314/// must be a member.
315static const Expr *findContainer(ASTContext *Context, const Expr *BeginExpr,
316 const Expr *EndExpr,
317 bool *ContainerNeedsDereference) {
318 // Now that we know the loop variable and test expression, make sure they are
319 // valid.
320 bool BeginIsArrow = false;
321 bool EndIsArrow = false;
322 const Expr *BeginContainerExpr =
323 getContainerFromBeginEndCall(BeginExpr, /*IsBegin=*/true, &BeginIsArrow);
324 if (!BeginContainerExpr)
325 return nullptr;
326
327 const Expr *EndContainerExpr =
328 getContainerFromBeginEndCall(EndExpr, /*IsBegin=*/false, &EndIsArrow);
329 // Disallow loops that try evil things like this (note the dot and arrow):
330 // for (IteratorType It = Obj.begin(), E = Obj->end(); It != E; ++It) { }
331 if (!EndContainerExpr || BeginIsArrow != EndIsArrow ||
332 !areSameExpr(Context, EndContainerExpr, BeginContainerExpr))
333 return nullptr;
334
335 *ContainerNeedsDereference = BeginIsArrow;
336 return BeginContainerExpr;
337}
338
339/// \brief Obtain the original source code text from a SourceRange.
340static StringRef getStringFromRange(SourceManager &SourceMgr,
341 const LangOptions &LangOpts,
342 SourceRange Range) {
343 if (SourceMgr.getFileID(Range.getBegin()) !=
Alexander Kornienko74a44d92015-08-20 13:18:23 +0000344 SourceMgr.getFileID(Range.getEnd())) {
345 return StringRef(); // Empty string.
346 }
Alexander Kornienko04970842015-08-19 09:11:46 +0000347
348 return Lexer::getSourceText(CharSourceRange(Range, true), SourceMgr,
349 LangOpts);
350}
351
352/// \brief If the given expression is actually a DeclRefExpr, find and return
353/// the underlying VarDecl; otherwise, return NULL.
354static const VarDecl *getReferencedVariable(const Expr *E) {
355 if (const DeclRefExpr *DRE = getDeclRef(E))
356 return dyn_cast<VarDecl>(DRE->getDecl());
357 return nullptr;
358}
359
360/// \brief Returns true when the given expression is a member expression
361/// whose base is `this` (implicitly or not).
362static bool isDirectMemberExpr(const Expr *E) {
363 if (const auto *Member = dyn_cast<MemberExpr>(E->IgnoreParenImpCasts()))
364 return isa<CXXThisExpr>(Member->getBase()->IgnoreParenImpCasts());
365 return false;
366}
367
Angel Garcia Gomez692cbb52015-09-01 15:05:15 +0000368/// \brief Returns true when it can be guaranteed that the elements of the
369/// container are not being modified.
370static bool usagesAreConst(const UsageResult &Usages) {
371 // FIXME: Make this function more generic.
372 return Usages.empty();
373}
374
375/// \brief Returns true if the elements of the container are never accessed
376/// by reference.
377static bool usagesReturnRValues(const UsageResult &Usages) {
378 for (const auto &U : Usages) {
Angel Garcia Gomezd930ef72015-09-08 09:01:21 +0000379 if (U.Expression && !U.Expression->isRValue())
Angel Garcia Gomez692cbb52015-09-01 15:05:15 +0000380 return false;
381 }
382 return true;
383}
384
Angel Garcia Gomezbb9ca542015-09-11 10:02:07 +0000385/// \brief Returns true if the container is const-qualified.
386static bool containerIsConst(const Expr *ContainerExpr, bool Dereference) {
387 if (const auto *VDec = getReferencedVariable(ContainerExpr)) {
388 QualType CType = VDec->getType();
389 if (Dereference) {
390 if (!CType->isPointerType())
391 return false;
392 CType = CType->getPointeeType();
393 }
Manuel Klimek143b6442015-09-23 18:40:47 +0000394 // If VDec is a reference to a container, Dereference is false,
395 // but we still need to check the const-ness of the underlying container
396 // type.
Angel Garcia Gomezaed6dde2015-09-24 13:26:28 +0000397 CType = CType.getNonReferenceType();
Angel Garcia Gomezbb9ca542015-09-11 10:02:07 +0000398 return CType.isConstQualified();
399 }
400 return false;
401}
402
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000403LoopConvertCheck::RangeDescriptor::RangeDescriptor()
404 : ContainerNeedsDereference(false), DerefByConstRef(false),
405 DerefByValue(false), IsTriviallyCopyable(false) {}
406
Alexander Kornienko04970842015-08-19 09:11:46 +0000407LoopConvertCheck::LoopConvertCheck(StringRef Name, ClangTidyContext *Context)
408 : ClangTidyCheck(Name, Context), TUInfo(new TUTrackingInfo),
409 MinConfidence(StringSwitch<Confidence::Level>(
410 Options.get("MinConfidence", "reasonable"))
411 .Case("safe", Confidence::CL_Safe)
412 .Case("risky", Confidence::CL_Risky)
413 .Default(Confidence::CL_Reasonable)) {}
414
415void LoopConvertCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
416 SmallVector<std::string, 3> Confs{"risky", "reasonable", "safe"};
417 Options.store(Opts, "MinConfidence", Confs[static_cast<int>(MinConfidence)]);
418}
419
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000420void LoopConvertCheck::registerMatchers(MatchFinder *Finder) {
421 // Only register the matchers for C++. Because this checker is used for
422 // modernization, it is reasonable to run it on any C++ standard with the
423 // assumption the user is trying to modernize their codebase.
424 if (!getLangOpts().CPlusPlus)
Angel Garcia Gomezbb9ca542015-09-11 10:02:07 +0000425 return;
426
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000427 Finder->addMatcher(makeArrayLoopMatcher(), this);
428 Finder->addMatcher(makeIteratorLoopMatcher(), this);
429 Finder->addMatcher(makePseudoArrayLoopMatcher(), this);
430}
431
432/// \brief Computes the changes needed to convert a given for loop, and
433/// applies them.
434void LoopConvertCheck::doConversion(
435 ASTContext *Context, const VarDecl *IndexVar, const VarDecl *MaybeContainer,
436 const UsageResult &Usages, const DeclStmt *AliasDecl, bool AliasUseRequired,
437 bool AliasFromForInit, const ForStmt *Loop, RangeDescriptor Descriptor) {
438 auto Diag = diag(Loop->getForLoc(), "use range-based for loop instead");
Alexander Kornienko04970842015-08-19 09:11:46 +0000439
440 std::string VarName;
441 bool VarNameFromAlias = (Usages.size() == 1) && AliasDecl;
442 bool AliasVarIsRef = false;
443
444 if (VarNameFromAlias) {
445 const auto *AliasVar = cast<VarDecl>(AliasDecl->getSingleDecl());
446 VarName = AliasVar->getName().str();
447 AliasVarIsRef = AliasVar->getType()->isReferenceType();
448
449 // We keep along the entire DeclStmt to keep the correct range here.
450 const SourceRange &ReplaceRange = AliasDecl->getSourceRange();
451
452 std::string ReplacementText;
453 if (AliasUseRequired) {
454 ReplacementText = VarName;
455 } else if (AliasFromForInit) {
456 // FIXME: Clang includes the location of the ';' but only for DeclStmt's
457 // in a for loop's init clause. Need to put this ';' back while removing
458 // the declaration of the alias variable. This is probably a bug.
459 ReplacementText = ";";
460 }
461
462 Diag << FixItHint::CreateReplacement(
463 CharSourceRange::getTokenRange(ReplaceRange), ReplacementText);
464 // No further replacements are made to the loop, since the iterator or index
465 // was used exactly once - in the initialization of AliasVar.
466 } else {
467 VariableNamer Namer(&TUInfo->getGeneratedDecls(),
468 &TUInfo->getParentFinder().getStmtToParentStmtMap(),
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000469 Loop, IndexVar, MaybeContainer, Context);
Alexander Kornienko04970842015-08-19 09:11:46 +0000470 VarName = Namer.createIndexName();
471 // First, replace all usages of the array subscript expression with our new
472 // variable.
Angel Garcia Gomezbb9ca542015-09-11 10:02:07 +0000473 for (const auto &Usage : Usages) {
474 std::string ReplaceText;
Angel Garcia Gomezbd432b22015-09-24 15:29:46 +0000475 SourceRange Range = Usage.Range;
Angel Garcia Gomezbb9ca542015-09-11 10:02:07 +0000476 if (Usage.Expression) {
477 // If this is an access to a member through the arrow operator, after
478 // the replacement it must be accessed through the '.' operator.
479 ReplaceText = Usage.Kind == Usage::UK_MemberThroughArrow ? VarName + "."
480 : VarName;
Angel Garcia Gomezbd432b22015-09-24 15:29:46 +0000481 auto Parents = Context->getParents(*Usage.Expression);
482 if (Parents.size() == 1) {
483 if (const auto *Paren = Parents[0].get<ParenExpr>()) {
484 // Usage.Expression will be replaced with the new index variable,
485 // and parenthesis around a simple DeclRefExpr can always be
486 // removed.
487 Range = Paren->getSourceRange();
488 }
489 }
Angel Garcia Gomezbb9ca542015-09-11 10:02:07 +0000490 } else {
491 // The Usage expression is only null in case of lambda captures (which
492 // are VarDecl). If the index is captured by value, add '&' to capture
493 // by reference instead.
494 ReplaceText =
495 Usage.Kind == Usage::UK_CaptureByCopy ? "&" + VarName : VarName;
496 }
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000497 TUInfo->getReplacedVars().insert(std::make_pair(Loop, IndexVar));
Alexander Kornienko04970842015-08-19 09:11:46 +0000498 Diag << FixItHint::CreateReplacement(
Angel Garcia Gomezbd432b22015-09-24 15:29:46 +0000499 CharSourceRange::getTokenRange(Range), ReplaceText);
Alexander Kornienko04970842015-08-19 09:11:46 +0000500 }
501 }
502
503 // Now, we need to construct the new range expression.
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000504 SourceRange ParenRange(Loop->getLParenLoc(), Loop->getRParenLoc());
Alexander Kornienko04970842015-08-19 09:11:46 +0000505
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000506 QualType AutoType = Context->getAutoDeductType();
Alexander Kornienko04970842015-08-19 09:11:46 +0000507
508 // If the new variable name is from the aliased variable, then the reference
509 // type for the new variable should only be used if the aliased variable was
510 // declared as a reference.
Manuel Klimekb457b682015-09-23 22:28:14 +0000511 bool UseCopy = (VarNameFromAlias && !AliasVarIsRef) ||
512 (Descriptor.DerefByConstRef && Descriptor.IsTriviallyCopyable);
513
514 if (!UseCopy) {
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000515 if (Descriptor.DerefByConstRef) {
516 AutoType =
517 Context->getLValueReferenceType(Context->getConstType(AutoType));
518 } else if (Descriptor.DerefByValue) {
Angel Garcia Gomezd930ef72015-09-08 09:01:21 +0000519 if (!Descriptor.IsTriviallyCopyable)
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000520 AutoType = Context->getRValueReferenceType(AutoType);
Alexander Kornienko04970842015-08-19 09:11:46 +0000521 } else {
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000522 AutoType = Context->getLValueReferenceType(AutoType);
Alexander Kornienko04970842015-08-19 09:11:46 +0000523 }
524 }
525
Angel Garcia Gomezd930ef72015-09-08 09:01:21 +0000526 StringRef MaybeDereference = Descriptor.ContainerNeedsDereference ? "*" : "";
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000527 std::string TypeString = AutoType.getAsString();
Alexander Kornienkoe1292f82015-08-19 16:54:51 +0000528 std::string Range = ("(" + TypeString + " " + VarName + " : " +
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000529 MaybeDereference + Descriptor.ContainerString + ")")
Angel Garcia Gomez692cbb52015-09-01 15:05:15 +0000530 .str();
Alexander Kornienko04970842015-08-19 09:11:46 +0000531 Diag << FixItHint::CreateReplacement(
532 CharSourceRange::getTokenRange(ParenRange), Range);
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000533 TUInfo->getGeneratedDecls().insert(make_pair(Loop, VarName));
Alexander Kornienko04970842015-08-19 09:11:46 +0000534}
535
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000536/// \brief Returns a string which refers to the container iterated over.
537StringRef LoopConvertCheck::getContainerString(ASTContext *Context,
538 const ForStmt *Loop,
539 const Expr *ContainerExpr) {
Alexander Kornienko04970842015-08-19 09:11:46 +0000540 StringRef ContainerString;
541 if (isa<CXXThisExpr>(ContainerExpr->IgnoreParenImpCasts())) {
542 ContainerString = "this";
543 } else {
544 ContainerString =
545 getStringFromRange(Context->getSourceManager(), Context->getLangOpts(),
546 ContainerExpr->getSourceRange());
547 }
548
549 return ContainerString;
550}
551
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000552/// \brief Determines what kind of 'auto' must be used after converting a for
553/// loop that iterates over an array or pseudoarray.
554void LoopConvertCheck::getArrayLoopQualifiers(ASTContext *Context,
555 const BoundNodes &Nodes,
556 const Expr *ContainerExpr,
557 const UsageResult &Usages,
558 RangeDescriptor &Descriptor) {
559 // On arrays and pseudoarrays, we must figure out the qualifiers from the
560 // usages.
Manuel Klimekb457b682015-09-23 22:28:14 +0000561 if (usagesAreConst(Usages) ||
562 containerIsConst(ContainerExpr, Descriptor.ContainerNeedsDereference)) {
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000563 Descriptor.DerefByConstRef = true;
Manuel Klimekb457b682015-09-23 22:28:14 +0000564 }
565 if (usagesReturnRValues(Usages)) {
566 // If the index usages (dereference, subscript, at, ...) return rvalues,
567 // then we should not use a reference, because we need to keep the code
568 // correct if it mutates the returned objects.
569 Descriptor.DerefByValue = true;
570 }
571 // Try to find the type of the elements on the container, to check if
572 // they are trivially copyable.
573 for (const Usage &U : Usages) {
574 if (!U.Expression || U.Expression->getType().isNull())
575 continue;
576 QualType Type = U.Expression->getType().getCanonicalType();
577 if (U.Kind == Usage::UK_MemberThroughArrow) {
578 if (!Type->isPointerType()) {
579 continue;
Angel Garcia Gomez692cbb52015-09-01 15:05:15 +0000580 }
Manuel Klimekb457b682015-09-23 22:28:14 +0000581 Type = Type->getPointeeType();
Angel Garcia Gomez692cbb52015-09-01 15:05:15 +0000582 }
Manuel Klimekb457b682015-09-23 22:28:14 +0000583 Descriptor.IsTriviallyCopyable = Type.isTriviallyCopyableType(*Context);
Alexander Kornienko04970842015-08-19 09:11:46 +0000584 }
Alexander Kornienko04970842015-08-19 09:11:46 +0000585}
586
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000587/// \brief Determines what kind of 'auto' must be used after converting an
588/// iterator based for loop.
589void LoopConvertCheck::getIteratorLoopQualifiers(ASTContext *Context,
590 const BoundNodes &Nodes,
591 RangeDescriptor &Descriptor) {
592 // The matchers for iterator loops provide bound nodes to obtain this
593 // information.
594 const auto *InitVar = Nodes.getDeclAs<VarDecl>(InitVarName);
595 QualType CanonicalInitVarType = InitVar->getType().getCanonicalType();
596 const auto *DerefByValueType =
597 Nodes.getNodeAs<QualType>(DerefByValueResultName);
598 Descriptor.DerefByValue = DerefByValueType;
Alexander Kornienko04970842015-08-19 09:11:46 +0000599
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000600 if (Descriptor.DerefByValue) {
601 // If the dereference operator returns by value then test for the
602 // canonical const qualification of the init variable type.
603 Descriptor.DerefByConstRef = CanonicalInitVarType.isConstQualified();
604 Descriptor.IsTriviallyCopyable =
605 DerefByValueType->isTriviallyCopyableType(*Context);
Alexander Kornienko04970842015-08-19 09:11:46 +0000606 } else {
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000607 if (const auto *DerefType =
608 Nodes.getNodeAs<QualType>(DerefByRefResultName)) {
609 // A node will only be bound with DerefByRefResultName if we're dealing
610 // with a user-defined iterator type. Test the const qualification of
611 // the reference type.
Manuel Klimeka88ce502015-09-24 00:16:38 +0000612 auto ValueType = DerefType->getNonReferenceType();
Manuel Klimekb457b682015-09-23 22:28:14 +0000613
614 Descriptor.DerefByConstRef = ValueType.isConstQualified();
615 Descriptor.IsTriviallyCopyable =
616 ValueType.isTriviallyCopyableType(*Context);
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000617 } else {
618 // By nature of the matcher this case is triggered only for built-in
619 // iterator types (i.e. pointers).
620 assert(isa<PointerType>(CanonicalInitVarType) &&
621 "Non-class iterator type is not a pointer type");
622
623 // We test for const qualification of the pointed-at type.
624 Descriptor.DerefByConstRef =
625 CanonicalInitVarType->getPointeeType().isConstQualified();
Manuel Klimekb457b682015-09-23 22:28:14 +0000626 Descriptor.IsTriviallyCopyable =
627 CanonicalInitVarType->getPointeeType().isTriviallyCopyableType(
628 *Context);
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000629 }
Alexander Kornienko04970842015-08-19 09:11:46 +0000630 }
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000631}
632
633/// \brief Determines the parameters needed to build the range replacement.
634void LoopConvertCheck::determineRangeDescriptor(
635 ASTContext *Context, const BoundNodes &Nodes, const ForStmt *Loop,
636 LoopFixerKind FixerKind, const Expr *ContainerExpr,
637 const UsageResult &Usages, RangeDescriptor &Descriptor) {
638 Descriptor.ContainerString = getContainerString(Context, Loop, ContainerExpr);
639
640 if (FixerKind == LFK_Iterator)
641 getIteratorLoopQualifiers(Context, Nodes, Descriptor);
642 else
643 getArrayLoopQualifiers(Context, Nodes, ContainerExpr, Usages, Descriptor);
644}
645
646/// \brief Check some of the conditions that must be met for the loop to be
647/// convertible.
648bool LoopConvertCheck::isConvertible(ASTContext *Context,
649 const ast_matchers::BoundNodes &Nodes,
650 const ForStmt *Loop,
651 LoopFixerKind FixerKind) {
652 // If we already modified the range of this for loop, don't do any further
653 // updates on this iteration.
654 if (TUInfo->getReplacedVars().count(Loop))
655 return false;
Alexander Kornienko04970842015-08-19 09:11:46 +0000656
657 // Check that we have exactly one index variable and at most one end variable.
658 const auto *LoopVar = Nodes.getDeclAs<VarDecl>(IncrementVarName);
659 const auto *CondVar = Nodes.getDeclAs<VarDecl>(ConditionVarName);
660 const auto *InitVar = Nodes.getDeclAs<VarDecl>(InitVarName);
661 if (!areSameVariable(LoopVar, CondVar) || !areSameVariable(LoopVar, InitVar))
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000662 return false;
Alexander Kornienko04970842015-08-19 09:11:46 +0000663 const auto *EndVar = Nodes.getDeclAs<VarDecl>(EndVarName);
664 const auto *ConditionEndVar = Nodes.getDeclAs<VarDecl>(ConditionEndVarName);
665 if (EndVar && !areSameVariable(EndVar, ConditionEndVar))
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000666 return false;
Alexander Kornienko04970842015-08-19 09:11:46 +0000667
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000668 // FIXME: Try to put most of this logic inside a matcher.
Alexander Kornienko04970842015-08-19 09:11:46 +0000669 if (FixerKind == LFK_Iterator) {
Alexander Kornienko04970842015-08-19 09:11:46 +0000670 QualType InitVarType = InitVar->getType();
671 QualType CanonicalInitVarType = InitVarType.getCanonicalType();
672
673 const auto *BeginCall = Nodes.getNodeAs<CXXMemberCallExpr>(BeginCallName);
674 assert(BeginCall && "Bad Callback. No begin call expression");
675 QualType CanonicalBeginType =
676 BeginCall->getMethodDecl()->getReturnType().getCanonicalType();
677 if (CanonicalBeginType->isPointerType() &&
678 CanonicalInitVarType->isPointerType()) {
Alexander Kornienko04970842015-08-19 09:11:46 +0000679 // If the initializer and the variable are both pointers check if the
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000680 // un-qualified pointee types match, otherwise we don't use auto.
681 if (!Context->hasSameUnqualifiedType(
682 CanonicalBeginType->getPointeeType(),
683 CanonicalInitVarType->getPointeeType()))
684 return false;
685 } else if (!Context->hasSameType(CanonicalInitVarType,
686 CanonicalBeginType)) {
Alexander Kornienko04970842015-08-19 09:11:46 +0000687 // Check for qualified types to avoid conversions from non-const to const
688 // iterator types.
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000689 return false;
Alexander Kornienko04970842015-08-19 09:11:46 +0000690 }
691 } else if (FixerKind == LFK_PseudoArray) {
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000692 // This call is required to obtain the container.
693 const auto *EndCall = Nodes.getStmtAs<CXXMemberCallExpr>(EndCallName);
694 if (!EndCall || !dyn_cast<MemberExpr>(EndCall->getCallee()))
695 return false;
696 }
697 return true;
698}
699
700void LoopConvertCheck::check(const MatchFinder::MatchResult &Result) {
701 const BoundNodes &Nodes = Result.Nodes;
702 Confidence ConfidenceLevel(Confidence::CL_Safe);
703 ASTContext *Context = Result.Context;
704
705 const ForStmt *Loop;
706 LoopFixerKind FixerKind;
707 RangeDescriptor Descriptor;
708
709 if ((Loop = Nodes.getStmtAs<ForStmt>(LoopNameArray))) {
710 FixerKind = LFK_Array;
711 } else if ((Loop = Nodes.getStmtAs<ForStmt>(LoopNameIterator))) {
712 FixerKind = LFK_Iterator;
713 } else {
714 Loop = Nodes.getStmtAs<ForStmt>(LoopNamePseudoArray);
715 assert(Loop && "Bad Callback. No for statement");
716 FixerKind = LFK_PseudoArray;
717 }
718
719 if (!isConvertible(Context, Nodes, Loop, FixerKind))
720 return;
721
722 const auto *LoopVar = Nodes.getDeclAs<VarDecl>(IncrementVarName);
723 const auto *EndVar = Nodes.getDeclAs<VarDecl>(EndVarName);
724
725 // If the loop calls end()/size() after each iteration, lower our confidence
726 // level.
727 if (FixerKind != LFK_Array && !EndVar)
728 ConfidenceLevel.lowerTo(Confidence::CL_Reasonable);
729
730 // If the end comparison isn't a variable, we can try to work with the
731 // expression the loop variable is being tested against instead.
732 const auto *EndCall = Nodes.getStmtAs<CXXMemberCallExpr>(EndCallName);
733 const auto *BoundExpr = Nodes.getStmtAs<Expr>(ConditionBoundName);
734
735 // Find container expression of iterators and pseudoarrays, and determine if
736 // this expression needs to be dereferenced to obtain the container.
737 // With array loops, the container is often discovered during the
738 // ForLoopIndexUseVisitor traversal.
739 const Expr *ContainerExpr = nullptr;
740 if (FixerKind == LFK_Iterator) {
741 ContainerExpr = findContainer(Context, LoopVar->getInit(),
742 EndVar ? EndVar->getInit() : EndCall,
743 &Descriptor.ContainerNeedsDereference);
744 } else if (FixerKind == LFK_PseudoArray) {
Alexander Kornienko04970842015-08-19 09:11:46 +0000745 ContainerExpr = EndCall->getImplicitObjectArgument();
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000746 Descriptor.ContainerNeedsDereference =
747 dyn_cast<MemberExpr>(EndCall->getCallee())->isArrow();
Alexander Kornienko04970842015-08-19 09:11:46 +0000748 }
749
750 // We must know the container or an array length bound.
751 if (!ContainerExpr && !BoundExpr)
752 return;
753
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000754 ForLoopIndexUseVisitor Finder(Context, LoopVar, EndVar, ContainerExpr,
755 BoundExpr,
756 Descriptor.ContainerNeedsDereference);
757
758 // Find expressions and variables on which the container depends.
759 if (ContainerExpr) {
760 ComponentFinderASTVisitor ComponentFinder;
761 ComponentFinder.findExprComponents(ContainerExpr->IgnoreParenImpCasts());
762 Finder.addComponents(ComponentFinder.getComponents());
763 }
764
765 // Find usages of the loop index. If they are not used in a convertible way,
766 // stop here.
767 if (!Finder.findAndVerifyUsages(Loop->getBody()))
768 return;
769 ConfidenceLevel.lowerTo(Finder.getConfidenceLevel());
770
771 // Obtain the container expression, if we don't have it yet.
772 if (FixerKind == LFK_Array) {
773 ContainerExpr = Finder.getContainerIndexed()->IgnoreParenImpCasts();
774
775 // Very few loops are over expressions that generate arrays rather than
776 // array variables. Consider loops over arrays that aren't just represented
777 // by a variable to be risky conversions.
778 if (!getReferencedVariable(ContainerExpr) &&
779 !isDirectMemberExpr(ContainerExpr))
780 ConfidenceLevel.lowerTo(Confidence::CL_Risky);
781 }
782
783 // Find out which qualifiers we have to use in the loop range.
784 const UsageResult &Usages = Finder.getUsages();
785 determineRangeDescriptor(Context, Nodes, Loop, FixerKind, ContainerExpr,
786 Usages, Descriptor);
787
788 // Ensure that we do not try to move an expression dependent on a local
789 // variable declared inside the loop outside of it.
790 // FIXME: Determine when the external dependency isn't an expression converted
791 // by another loop.
792 TUInfo->getParentFinder().gatherAncestors(Context->getTranslationUnitDecl());
793 DependencyFinderASTVisitor DependencyFinder(
794 &TUInfo->getParentFinder().getStmtToParentStmtMap(),
795 &TUInfo->getParentFinder().getDeclToParentStmtMap(),
796 &TUInfo->getReplacedVars(), Loop);
797
798 if (DependencyFinder.dependsOnInsideVariable(ContainerExpr) ||
799 Descriptor.ContainerString.empty() || Usages.empty() ||
800 ConfidenceLevel.getLevel() < MinConfidence)
Alexander Kornienko04970842015-08-19 09:11:46 +0000801 return;
802
Angel Garcia Gomezf41a6312015-09-21 09:32:59 +0000803 doConversion(Context, LoopVar, getReferencedVariable(ContainerExpr), Usages,
804 Finder.getAliasDecl(), Finder.aliasUseRequired(),
805 Finder.aliasFromForInit(), Loop, Descriptor);
Alexander Kornienko04970842015-08-19 09:11:46 +0000806}
807
808} // namespace modernize
809} // namespace tidy
810} // namespace clang