10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Basic/LLVM.h"
13 #include "clang/Basic/LangOptions.h"
14 #include "clang/Basic/SourceLocation.h"
15 #include "clang/Basic/SourceManager.h"
16 #include "clang/Lex/Lexer.h"
17 #include "llvm/ADT/ArrayRef.h"
18 #include "llvm/ADT/SmallVector.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/ADT/StringSwitch.h"
21 #include "llvm/Support/Casting.h"
33 static llvm::ArrayRef<std::pair<modernize::Confidence::Level, StringRef>>
35 static constexpr std::pair<modernize::Confidence::Level, StringRef>
36 Mapping[] = {{modernize::Confidence::CL_Reasonable,
"reasonable"},
37 {modernize::Confidence::CL_Safe,
"safe"},
38 {modernize::Confidence::CL_Risky,
"risky"}};
39 return makeArrayRef(Mapping);
44 static llvm::ArrayRef<
45 std::pair<modernize::VariableNamer::NamingStyle, StringRef>>
47 static constexpr std::pair<modernize::VariableNamer::NamingStyle, StringRef>
48 Mapping[] = {{modernize::VariableNamer::NS_CamelCase,
"CamelCase"},
49 {modernize::VariableNamer::NS_CamelBack,
"camelBack"},
50 {modernize::VariableNamer::NS_LowerCase,
"lower_case"},
51 {modernize::VariableNamer::NS_UpperCase,
"UPPER_CASE"}};
52 return makeArrayRef(Mapping);
73 static const TypeMatcher
AnyType() {
return anything(); }
76 return expr(ignoringParenImpCasts(
82 hasInitializer(ignoringParenImpCasts(integerLiteral(equals(0)))))
87 return declRefExpr(to(varDecl(hasType(isInteger())).bind(
IncrementVarName)));
109 StatementMatcher ArrayBoundMatcher =
113 unless(isInTemplateInstantiation()),
116 binaryOperator(hasOperatorName(
"<"),
118 hasRHS(ArrayBoundMatcher)),
119 binaryOperator(hasOperatorName(
">"), hasLHS(ArrayBoundMatcher),
121 hasIncrement(unaryOperator(hasOperatorName(
"++"),
154 StatementMatcher BeginCallMatcher =
155 cxxMemberCallExpr(argumentCountIs(0),
156 callee(cxxMethodDecl(hasAnyName(
"begin",
"cbegin"))))
159 DeclarationMatcher InitDeclMatcher =
160 varDecl(hasInitializer(anyOf(ignoringParenImpCasts(BeginCallMatcher),
161 materializeTemporaryExpr(
162 ignoringParenImpCasts(BeginCallMatcher)),
163 hasDescendant(BeginCallMatcher))))
166 DeclarationMatcher EndDeclMatcher =
167 varDecl(hasInitializer(anything())).bind(
EndVarName);
169 StatementMatcher EndCallMatcher = cxxMemberCallExpr(
170 argumentCountIs(0), callee(cxxMethodDecl(hasAnyName(
"end",
"cend"))));
172 StatementMatcher IteratorBoundMatcher =
173 expr(anyOf(ignoringParenImpCasts(
175 ignoringParenImpCasts(expr(EndCallMatcher).bind(
EndCallName)),
176 materializeTemporaryExpr(ignoringParenImpCasts(
179 StatementMatcher IteratorComparisonMatcher = expr(
182 auto OverloadedNEQMatcher = ignoringImplicit(
183 cxxOperatorCallExpr(hasOverloadedOperatorName(
"!="), argumentCountIs(2),
184 hasArgument(0, IteratorComparisonMatcher),
185 hasArgument(1, IteratorBoundMatcher)));
190 internal::Matcher<VarDecl> TestDerefReturnsByValue =
191 hasType(hasUnqualifiedDesugaredType(
192 recordType(hasDeclaration(cxxRecordDecl(hasMethod(cxxMethodDecl(
193 hasOverloadedOperatorName(
"*"),
196 returns(qualType(unless(hasCanonicalType(referenceType())))
201 qualType(unless(hasCanonicalType(rValueReferenceType())))
205 unless(isInTemplateInstantiation()),
206 hasLoopInit(anyOf(declStmt(declCountIs(2),
207 containsDeclaration(0, InitDeclMatcher),
208 containsDeclaration(1, EndDeclMatcher)),
209 declStmt(hasSingleDecl(InitDeclMatcher)))),
211 anyOf(binaryOperator(hasOperatorName(
"!="),
212 hasLHS(IteratorComparisonMatcher),
213 hasRHS(IteratorBoundMatcher)),
214 binaryOperator(hasOperatorName(
"!="),
215 hasLHS(IteratorBoundMatcher),
216 hasRHS(IteratorComparisonMatcher)),
217 OverloadedNEQMatcher)),
219 unaryOperator(hasOperatorName(
"++"),
220 hasUnaryOperand(declRefExpr(
221 to(varDecl(hasType(pointsTo(
AnyType())))
224 hasOverloadedOperatorName(
"++"),
226 0, declRefExpr(to(varDecl(TestDerefReturnsByValue)
272 TypeMatcher RecordWithBeginEnd = qualType(anyOf(
275 hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
276 hasMethod(cxxMethodDecl(hasName(
"begin"), isConst())),
277 hasMethod(cxxMethodDecl(hasName(
"end"),
280 qualType(unless(isConstQualified()),
281 hasUnqualifiedDesugaredType(recordType(hasDeclaration(
282 cxxRecordDecl(hasMethod(hasName(
"begin")),
283 hasMethod(hasName(
"end")))))))
286 StatementMatcher SizeCallMatcher = cxxMemberCallExpr(
287 argumentCountIs(0), callee(cxxMethodDecl(hasAnyName(
"size",
"length"))),
288 on(anyOf(hasType(pointsTo(RecordWithBeginEnd)),
289 hasType(RecordWithBeginEnd))));
291 StatementMatcher EndInitMatcher =
292 expr(anyOf(ignoringParenImpCasts(expr(SizeCallMatcher).bind(
EndCallName)),
293 explicitCastExpr(hasSourceExpression(ignoringParenImpCasts(
296 DeclarationMatcher EndDeclMatcher =
297 varDecl(hasInitializer(EndInitMatcher)).bind(
EndVarName);
299 StatementMatcher IndexBoundMatcher =
300 expr(anyOf(ignoringParenImpCasts(declRefExpr(to(
305 unless(isInTemplateInstantiation()),
307 anyOf(declStmt(declCountIs(2),
309 containsDeclaration(1, EndDeclMatcher)),
312 binaryOperator(hasOperatorName(
"<"),
314 hasRHS(IndexBoundMatcher)),
315 binaryOperator(hasOperatorName(
">"), hasLHS(IndexBoundMatcher),
317 hasIncrement(unaryOperator(hasOperatorName(
"++"),
330 const auto *TheCall =
332 if (!TheCall || TheCall->getNumArgs() != 0)
335 const auto *Member = dyn_cast<MemberExpr>(TheCall->getCallee());
338 StringRef
Name = Member->getMemberDecl()->getName();
339 StringRef TargetName = IsBegin ?
"begin" :
"end";
340 StringRef ConstTargetName = IsBegin ?
"cbegin" :
"cend";
341 if (
Name != TargetName &&
Name != ConstTargetName)
344 const Expr *SourceExpr = Member->getBase();
348 *IsArrow = Member->isArrow();
357 static const Expr *
findContainer(ASTContext *Context,
const Expr *BeginExpr,
359 bool *ContainerNeedsDereference) {
362 bool BeginIsArrow =
false;
363 bool EndIsArrow =
false;
364 const Expr *BeginContainerExpr =
366 if (!BeginContainerExpr)
369 const Expr *EndContainerExpr =
373 if (!EndContainerExpr || BeginIsArrow != EndIsArrow ||
374 !
areSameExpr(Context, EndContainerExpr, BeginContainerExpr))
377 *ContainerNeedsDereference = BeginIsArrow;
378 return BeginContainerExpr;
383 const LangOptions &LangOpts,
390 return Lexer::getSourceText(CharSourceRange(
Range,
true),
SourceMgr,
398 return dyn_cast<VarDecl>(DRE->getDecl());
399 if (
const auto *Mem = dyn_cast<MemberExpr>(
E->IgnoreParenImpCasts()))
400 return dyn_cast<FieldDecl>(Mem->getMemberDecl());
407 if (
const auto *Member = dyn_cast<MemberExpr>(
E->IgnoreParenImpCasts()))
408 return isa<CXXThisExpr>(Member->getBase()->IgnoreParenImpCasts());
416 if (
E->getType().isConstQualified())
418 auto Parents = Context->getParents(*
E);
419 if (Parents.size() != 1)
421 if (
const auto *Cast = Parents[0].get<ImplicitCastExpr>()) {
422 if ((Cast->getCastKind() == CK_NoOp &&
423 Cast->getType() ==
E->getType().withConst()) ||
424 (Cast->getCastKind() == CK_LValueToRValue &&
425 !Cast->getType().isNull() && Cast->getType()->isFundamentalType()))
435 for (
const Usage &U : Usages) {
441 if (U.Kind != Usage::UK_CaptureByCopy && U.Kind != Usage::UK_CaptureByRef &&
451 for (
const auto &U : Usages) {
452 if (U.Expression && !U.Expression->isRValue())
461 QualType CType = VDec->getType();
463 if (!CType->isPointerType())
465 CType = CType->getPointeeType();
470 CType = CType.getNonReferenceType();
471 return CType.isConstQualified();
476 LoopConvertCheck::RangeDescriptor::RangeDescriptor()
477 : ContainerNeedsDereference(false), DerefByConstRef(false),
478 DerefByValue(false) {}
482 MaxCopySize(Options.get(
"MaxCopySize", 16ULL)),
483 MinConfidence(Options.get(
"MinConfidence",
Confidence::CL_Reasonable)),
484 NamingStyle(Options.get(
"NamingStyle",
VariableNamer::NS_CamelCase)) {}
487 Options.
store(Opts,
"MaxCopySize", std::to_string(MaxCopySize));
514 void LoopConvertCheck::getAliasRange(SourceManager &SM, SourceRange &
Range) {
515 bool Invalid =
false;
516 const char *TextAfter =
517 SM.getCharacterData(
Range.getEnd().getLocWithOffset(1), &Invalid);
520 unsigned Offset = std::strspn(TextAfter,
" \t\r\n");
527 void LoopConvertCheck::doConversion(
528 ASTContext *Context,
const VarDecl *IndexVar,
529 const ValueDecl *MaybeContainer,
const UsageResult &Usages,
530 const DeclStmt *AliasDecl,
bool AliasUseRequired,
bool AliasFromForInit,
531 const ForStmt *Loop, RangeDescriptor Descriptor) {
533 bool VarNameFromAlias = (Usages.size() == 1) && AliasDecl;
534 bool AliasVarIsRef =
false;
536 std::vector<FixItHint> FixIts;
537 if (VarNameFromAlias) {
538 const auto *AliasVar = cast<VarDecl>(AliasDecl->getSingleDecl());
539 VarName = AliasVar->getName().str();
542 QualType AliasVarType = AliasVar->getType();
543 assert(!AliasVarType.isNull() &&
"Type in VarDecl is null");
544 if (AliasVarType->isReferenceType()) {
545 AliasVarType = AliasVarType.getNonReferenceType();
546 AliasVarIsRef =
true;
548 if (Descriptor.ElemType.isNull() ||
549 !Context->hasSameUnqualifiedType(AliasVarType, Descriptor.ElemType))
550 Descriptor.ElemType = AliasVarType;
553 SourceRange ReplaceRange = AliasDecl->getSourceRange();
555 std::string ReplacementText;
556 if (AliasUseRequired) {
557 ReplacementText = VarName;
558 }
else if (AliasFromForInit) {
562 ReplacementText =
";";
565 getAliasRange(Context->getSourceManager(), ReplaceRange);
568 FixIts.push_back(FixItHint::CreateReplacement(
569 CharSourceRange::getTokenRange(ReplaceRange), ReplacementText));
573 VariableNamer Namer(&TUInfo->getGeneratedDecls(),
574 &TUInfo->getParentFinder().getStmtToParentStmtMap(),
575 Loop, IndexVar, MaybeContainer, Context, NamingStyle);
576 VarName = Namer.createIndexName();
579 for (
const auto &
Usage : Usages) {
580 std::string ReplaceText;
582 if (
Usage.Expression) {
589 if (
const auto *Paren = Parents[0].get<ParenExpr>()) {
593 Range = Paren->getSourceRange();
594 }
else if (
const auto *UOP = Parents[0].get<UnaryOperator>()) {
602 if (UOP->getOpcode() == UO_AddrOf)
613 TUInfo->getReplacedVars().insert(std::make_pair(Loop, IndexVar));
614 FixIts.push_back(FixItHint::CreateReplacement(
615 CharSourceRange::getTokenRange(
Range), ReplaceText));
620 SourceRange ParenRange(Loop->getLParenLoc(), Loop->getRParenLoc());
622 QualType
Type = Context->getAutoDeductType();
623 if (!Descriptor.ElemType.isNull() && Descriptor.ElemType->isFundamentalType())
624 Type = Descriptor.ElemType.getUnqualifiedType();
625 Type =
Type.getDesugaredType(*Context);
631 !Descriptor.ElemType.isNull() &&
632 Descriptor.ElemType.isTriviallyCopyableType(*Context) &&
634 Context->getTypeInfo(Descriptor.ElemType).Width <= 8 * MaxCopySize;
635 bool UseCopy = CanCopy && ((VarNameFromAlias && !AliasVarIsRef) ||
636 (Descriptor.DerefByConstRef && IsCheapToCopy));
639 if (Descriptor.DerefByConstRef) {
640 Type = Context->getLValueReferenceType(Context->getConstType(
Type));
641 }
else if (Descriptor.DerefByValue) {
643 Type = Context->getRValueReferenceType(
Type);
645 Type = Context->getLValueReferenceType(
Type);
649 StringRef MaybeDereference = Descriptor.ContainerNeedsDereference ?
"*" :
"";
651 std::string
Range = (
"(" + TypeString +
" " + VarName +
" : " +
652 MaybeDereference + Descriptor.ContainerString +
")")
654 FixIts.push_back(FixItHint::CreateReplacement(
655 CharSourceRange::getTokenRange(ParenRange),
Range));
656 diag(Loop->getForLoc(),
"use range-based for loop instead") << FixIts;
657 TUInfo->getGeneratedDecls().insert(make_pair(Loop, VarName));
661 StringRef LoopConvertCheck::getContainerString(ASTContext *Context,
663 const Expr *ContainerExpr) {
664 StringRef ContainerString;
665 ContainerExpr = ContainerExpr->IgnoreParenImpCasts();
666 if (isa<CXXThisExpr>(ContainerExpr)) {
667 ContainerString =
"this";
671 if (
const auto*
E = dyn_cast<CXXOperatorCallExpr>(ContainerExpr))
672 ContainerExpr =
E->getArg(0);
675 ContainerExpr->getSourceRange());
678 return ContainerString;
683 void LoopConvertCheck::getArrayLoopQualifiers(ASTContext *Context,
684 const BoundNodes &Nodes,
685 const Expr *ContainerExpr,
687 RangeDescriptor &Descriptor) {
692 Descriptor.DerefByConstRef =
true;
698 Descriptor.DerefByValue =
true;
702 for (
const Usage &U : Usages) {
703 if (!U.Expression || U.Expression->getType().isNull())
705 QualType
Type = U.Expression->getType().getCanonicalType();
707 if (!
Type->isPointerType()) {
712 Descriptor.ElemType =
Type;
718 void LoopConvertCheck::getIteratorLoopQualifiers(ASTContext *Context,
719 const BoundNodes &Nodes,
720 RangeDescriptor &Descriptor) {
723 const auto *InitVar = Nodes.getNodeAs<VarDecl>(
InitVarName);
724 QualType CanonicalInitVarType = InitVar->getType().getCanonicalType();
725 const auto *DerefByValueType =
727 Descriptor.DerefByValue = DerefByValueType;
729 if (Descriptor.DerefByValue) {
732 Descriptor.DerefByConstRef = CanonicalInitVarType.isConstQualified();
733 Descriptor.ElemType = *DerefByValueType;
735 if (
const auto *DerefType =
740 auto ValueType = DerefType->getNonReferenceType();
742 Descriptor.DerefByConstRef = ValueType.isConstQualified();
743 Descriptor.ElemType = ValueType;
747 assert(isa<PointerType>(CanonicalInitVarType) &&
748 "Non-class iterator type is not a pointer type");
751 Descriptor.DerefByConstRef =
752 CanonicalInitVarType->getPointeeType().isConstQualified();
753 Descriptor.ElemType = CanonicalInitVarType->getPointeeType();
759 void LoopConvertCheck::determineRangeDescriptor(
760 ASTContext *Context,
const BoundNodes &Nodes,
const ForStmt *Loop,
762 const UsageResult &Usages, RangeDescriptor &Descriptor) {
763 Descriptor.ContainerString =
764 std::string(getContainerString(Context, Loop, ContainerExpr));
767 getIteratorLoopQualifiers(Context, Nodes, Descriptor);
769 getArrayLoopQualifiers(Context, Nodes, ContainerExpr, Usages, Descriptor);
774 bool LoopConvertCheck::isConvertible(ASTContext *Context,
775 const ast_matchers::BoundNodes &Nodes,
780 if (TUInfo->getReplacedVars().count(Loop))
786 const auto *InitVar = Nodes.getNodeAs<VarDecl>(
InitVarName);
789 const auto *EndVar = Nodes.getNodeAs<VarDecl>(
EndVarName);
796 QualType InitVarType = InitVar->getType();
797 QualType CanonicalInitVarType = InitVarType.getCanonicalType();
799 const auto *BeginCall = Nodes.getNodeAs<CXXMemberCallExpr>(
BeginCallName);
800 assert(BeginCall &&
"Bad Callback. No begin call expression");
801 QualType CanonicalBeginType =
802 BeginCall->getMethodDecl()->getReturnType().getCanonicalType();
803 if (CanonicalBeginType->isPointerType() &&
804 CanonicalInitVarType->isPointerType()) {
807 if (!Context->hasSameUnqualifiedType(
808 CanonicalBeginType->getPointeeType(),
809 CanonicalInitVarType->getPointeeType()))
814 const auto *EndCall = Nodes.getNodeAs<CXXMemberCallExpr>(
EndCallName);
815 if (!EndCall || !dyn_cast<MemberExpr>(EndCall->getCallee()))
822 const BoundNodes &Nodes = Result.Nodes;
824 ASTContext *Context = Result.Context;
828 RangeDescriptor Descriptor;
836 assert(Loop &&
"Bad Callback. No for statement");
840 if (!isConvertible(Context, Nodes, Loop, FixerKind))
844 const auto *EndVar = Nodes.getNodeAs<VarDecl>(
EndVarName);
853 const auto *EndCall = Nodes.getNodeAs<CXXMemberCallExpr>(
EndCallName);
860 const Expr *ContainerExpr =
nullptr;
863 EndVar ? EndVar->getInit() : EndCall,
864 &Descriptor.ContainerNeedsDereference);
866 ContainerExpr = EndCall->getImplicitObjectArgument();
867 Descriptor.ContainerNeedsDereference =
868 dyn_cast<MemberExpr>(EndCall->getCallee())->isArrow();
872 if (!ContainerExpr && !BoundExpr)
877 Descriptor.ContainerNeedsDereference);
888 if (!Finder.findAndVerifyUsages(Loop->getBody()))
890 ConfidenceLevel.
lowerTo(Finder.getConfidenceLevel());
894 ContainerExpr = Finder.getContainerIndexed()->IgnoreParenImpCasts();
905 TraversalKindScope RAII(*Context, ast_type_traits::TK_AsIs);
907 determineRangeDescriptor(Context, Nodes, Loop, FixerKind, ContainerExpr,
914 TUInfo->getParentFinder().gatherAncestors(*Context);
916 &TUInfo->getParentFinder().getStmtToParentStmtMap(),
917 &TUInfo->getParentFinder().getDeclToParentStmtMap(),
918 &TUInfo->getReplacedVars(), Loop);
920 if (DependencyFinder.dependsOnInsideVariable(ContainerExpr) ||
921 Descriptor.ContainerString.empty() || Usages.empty() ||
922 ConfidenceLevel.
getLevel() < MinConfidence)
926 Finder.getAliasDecl(), Finder.aliasUseRequired(),
927 Finder.aliasFromForInit(), Loop, Descriptor);