50 const CallExpr *Call) {
51 const FunctionDecl *Callee = Call->getDirectCallee();
52 assert(Callee && Callee->getNumParams() > 0 &&
53 "Matcher should ensure Callee has parameters");
56 const bool IsIterPair =
57 !Callee->getParamDecl(0)->getType()->isReferenceType();
60 if (Call->getNumArgs() < 3)
63 const Expr *EndArg = Call->getArg(2);
64 return tooling::fixit::getText(*EndArg, Context).str();
67 if (Call->getNumArgs() < 2)
70 const Expr *RangeArg = Call->getArg(1);
72 const Expr *InnerRange = RangeArg->IgnoreParenImpCasts();
73 if (isa<DeclRefExpr, MemberExpr>(InnerRange)) {
74 const StringRef RangeText = tooling::fixit::getText(*RangeArg, Context);
75 if (!RangeText.empty())
76 return (
"std::ranges::end(" + RangeText +
")").str();
82 const CallExpr *Call) {
83 if (Call->getNumArgs() < 2)
88 if (Call->getNumArgs() == 2) {
89 const Expr *Arg0 = Call->getArg(0);
90 const Expr *Arg1 = Call->getArg(1);
91 const QualType T0 = Arg0->getType().getCanonicalType();
92 const QualType T1 = Arg1->getType().getCanonicalType();
94 if (T0 != T1 && T0.getNonReferenceType()->isRecordType()) {
95 const StringRef ContainerText = tooling::fixit::getText(*Arg0, Context);
96 if (!ContainerText.empty())
97 return (
"std::end(" + ContainerText +
")").str();
102 const Expr *FirstArg = Call->getArg(0);
103 if (
const auto *Record =
104 FirstArg->getType().getNonReferenceType()->getAsCXXRecordDecl()) {
105 if (Record->getIdentifier() && Record->getName().ends_with(
"_policy"))
109 if (Call->getNumArgs() <= EndIdx)
112 const Expr *EndArg = Call->getArg(EndIdx);
114 if (EndArg->IgnoreParenCasts()->isNullPointerConstant(
115 Context, Expr::NPC_ValueDependentIsNull))
118 return tooling::fixit::getText(*EndArg, Context).str();
122 llvm::SmallVector<StringRef, 32> ExpandedIteratorAlgorithms;
125 ExpandedIteratorAlgorithms.append(ExtraAlgorithms.begin(),
126 ExtraAlgorithms.end());
128 const auto StdAlgoCall = callExpr(callee(functionDecl(
129 hasAnyName(ExpandedIteratorAlgorithms), unless(parameterCountIs(0)))));
132 const auto RangesCall = cxxOperatorCallExpr(
133 hasOverloadedOperatorName(
"()"),
134 callee(cxxMethodDecl(unless(parameterCountIs(0)))),
135 hasArgument(0, declRefExpr(to(
138 const auto AnyAlgoCall =
139 getLangOpts().CPlusPlus20
140 ? expr(anyOf(StdAlgoCall, RangesCall)).bind(
"algoCall")
141 : expr(StdAlgoCall).bind(
"algoCall");
144 const auto IsBoolUsage = anyOf(
145 implicitCastExpr(hasCastKind(CK_PointerToBoolean),
146 hasSourceExpression(ignoringParenImpCasts(AnyAlgoCall))),
147 cxxMemberCallExpr(callee(cxxConversionDecl(returns(booleanType()))),
148 on(ignoringParenImpCasts(AnyAlgoCall))));
153 const auto VarWithAlgoInit =
154 varDecl(decl().bind(
"initVar"),
155 hasInitializer(expr(ignoringParenImpCasts(AnyAlgoCall))),
156 optionally(hasParent(declStmt(
157 hasParent(mapAnyOf(ifStmt, whileStmt, forStmt)
158 .with(hasConditionVariableStatement(declStmt(
159 has(varDecl(equalsBoundNode(
"initVar"))))))
160 .bind(
"condVarParent"))))));
162 const auto IsVariableBoolUsage =
163 anyOf(implicitCastExpr(hasCastKind(CK_PointerToBoolean),
164 hasSourceExpression(ignoringParenImpCasts(
165 declRefExpr(to(VarWithAlgoInit))))),
167 callee(cxxConversionDecl(returns(booleanType()))),
168 on(ignoringParenImpCasts(declRefExpr(to(VarWithAlgoInit))))));
170 const auto BoolUsage =
171 expr(anyOf(IsBoolUsage, IsVariableBoolUsage),
172 optionally(hasParent(varDecl().bind(
"boolOpParentVar"))));
175 unaryOperator(hasOperatorName(
"!"),
176 hasUnaryOperand(ignoringParens(BoolUsage.bind(
"boolOp"))))
182 unless(hasAncestor(unaryOperator(
183 hasOperatorName(
"!"), hasUnaryOperand(ignoringParens(
184 expr(equalsBoundNode(
"boolOp"))))))))
190 const auto *BoolOp = Result.Nodes.getNodeAs<Expr>(
"boolOp");
193 std::optional<std::string> EndExprText;
195 if (Result.Nodes.getNodeAs<VarDecl>(
"cpo")) {
196 const auto *Call = Result.Nodes.getNodeAs<CallExpr>(
"algoCall");
198 }
else if (
const auto *Call = Result.Nodes.getNodeAs<CallExpr>(
"algoCall")) {
201 llvm_unreachable(
"Matcher should bind 'algoCall' or 'cpo'");
207 auto Diag = diag(BoolOp->getBeginLoc(),
208 "result of standard algorithm used as 'bool'; did you "
209 "mean to compare with the end iterator?");
211 if (EndExprText->empty())
217 if (
const auto *InitVar = Result.Nodes.getNodeAs<VarDecl>(
"initVar");
218 InitVar && (InitVar->getType()->isBooleanType() ||
219 Result.Nodes.getNodeAs<Stmt>(
"condVarParent")))
222 if (Result.Nodes.getNodeAs<VarDecl>(
"boolOpParentVar"))
225 const SourceLocation AfterBoolOp =
226 Lexer::getLocForEndOfToken(BoolOp->getEndLoc(), 0, *Result.SourceManager,
227 Result.Context->getLangOpts());
229 const auto *NotOp = Result.Nodes.getNodeAs<UnaryOperator>(
"Neg");
232 Diag << FixItHint::CreateReplacement(NotOp->getOperatorLoc(),
"(");
234 Diag << FixItHint::CreateInsertion(BoolOp->getBeginLoc(),
"(");
236 Diag << FixItHint::CreateInsertion(
238 (llvm::Twine(NotOp ?
" == " :
" != ") + *EndExprText +
")").str());
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.