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