30static bool minCondition(
const BinaryOperator::Opcode Op,
const Expr *CondLhs,
31 const Expr *CondRhs,
const Expr *AssignLhs,
32 const Expr *AssignRhs,
const ASTContext &Context) {
33 if ((Op == BO_LT || Op == BO_LE) &&
38 if ((Op == BO_GT || Op == BO_GE) &&
46static bool maxCondition(
const BinaryOperator::Opcode Op,
const Expr *CondLhs,
47 const Expr *CondRhs,
const Expr *AssignLhs,
48 const Expr *AssignRhs,
const ASTContext &Context) {
49 if ((Op == BO_LT || Op == BO_LE) &&
54 if ((Op == BO_GT || Op == BO_GE) &&
79 QualType ComparedType) {
80 const QualType LhsType = CondLhs->getType();
81 const QualType RhsType = CondRhs->getType();
82 const QualType LhsCanonicalType =
83 LhsType.getCanonicalType().getNonReferenceType().getUnqualifiedType();
84 const QualType RhsCanonicalType =
85 RhsType.getCanonicalType().getNonReferenceType().getUnqualifiedType();
86 QualType GlobalImplicitCastType;
87 if (LhsCanonicalType != RhsCanonicalType) {
88 if (isa<IntegerLiteral>(CondRhs))
90 else if (isa<IntegerLiteral>(CondLhs))
95 return GlobalImplicitCastType;
100 const Expr *AssignLhs,
const SourceManager &Source,
101 const LangOptions &LO, StringRef FunctionName,
102 const BinaryOperator *BO, StringRef Comment =
"") {
103 const StringRef CondLhsStr = Lexer::getSourceText(
104 Source.getExpansionRange(CondLhs->getSourceRange()), Source, LO);
105 const StringRef CondRhsStr = Lexer::getSourceText(
106 Source.getExpansionRange(CondRhs->getSourceRange()), Source, LO);
107 const StringRef AssignLhsStr = Lexer::getSourceText(
108 Source.getExpansionRange(AssignLhs->getSourceRange()), Source, LO);
110 const QualType GlobalImplicitCastType =
113 return (AssignLhsStr +
" = " + FunctionName +
114 (!GlobalImplicitCastType.isNull()
115 ?
"<" + GlobalImplicitCastType.getAsString() +
">("
117 CondLhsStr +
", " + CondRhsStr +
");" + (Comment.empty() ?
"" :
" ") +
133 auto AssignOperator =
134 binaryOperator(hasOperatorName(
"="),
135 hasLHS(expr(unless(isTypeDependent())).bind(
"AssignLhs")),
136 hasRHS(expr(unless(isTypeDependent())).bind(
"AssignRhs")));
137 auto BinaryOperator =
138 binaryOperator(hasAnyOperatorName(
"<",
">",
"<=",
">="),
139 hasLHS(expr(unless(isTypeDependent())).bind(
"CondLhs")),
140 hasRHS(expr(unless(isTypeDependent())).bind(
"CondRhs")))
143 ifStmt(stmt().bind(
"if"), unless(isIfInMacro()),
144 unless(hasElse(stmt())),
145 hasCondition(BinaryOperator),
147 anyOf(stmt(AssignOperator),
148 compoundStmt(statementCountIs(1), has(AssignOperator)))),
149 hasParent(stmt(unless(ifStmt(hasElse(
150 equalsBoundNode(
"if"))))))),
161 const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if");
162 const LangOptions &LO = Result.Context->getLangOpts();
163 const auto *CondLhs = Result.Nodes.getNodeAs<Expr>(
"CondLhs");
164 const auto *CondRhs = Result.Nodes.getNodeAs<Expr>(
"CondRhs");
165 const auto *AssignLhs = Result.Nodes.getNodeAs<Expr>(
"AssignLhs");
166 const auto *AssignRhs = Result.Nodes.getNodeAs<Expr>(
"AssignRhs");
167 const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>(
"binaryOp");
168 const BinaryOperatorKind BinaryOpcode = BinaryOp->getOpcode();
169 const SourceLocation IfLocation = If->getIfLoc();
170 const SourceLocation ThenLocation = If->getEndLoc();
172 auto ReplaceAndDiagnose = [&](
const StringRef FunctionName) {
173 const SourceManager &Source = *Result.SourceManager;
174 SmallString<64> Comment;
176 const auto AppendNormalized = [&](StringRef Text) {
179 if (!Comment.empty())
185 const auto GetSourceText = [&](SourceLocation StartLoc,
186 SourceLocation EndLoc) {
187 return Lexer::getSourceText(
188 CharSourceRange::getCharRange(
189 Lexer::getLocForEndOfToken(StartLoc, 0, Source, LO), EndLoc),
198 GetSourceText(If->getRParenLoc(), If->getThen()->getBeginLoc()));
200 if (
const auto *CS = dyn_cast<CompoundStmt>(If->getThen())) {
201 const Stmt *Inner = CS->body_front();
208 AppendNormalized(GetSourceText(CS->getBeginLoc(), Inner->getBeginLoc()));
213 StringRef PostInner = GetSourceText(Inner->getEndLoc(), CS->getEndLoc());
217 const size_t Semi = PostInner.find(
';');
218 if (Semi != StringRef::npos && PostInner.take_front(Semi).trim().empty())
219 PostInner = PostInner.drop_front(Semi + 1);
220 AppendNormalized(PostInner);
223 diag(IfLocation,
"use `%0` instead of `%1`")
224 << FunctionName << BinaryOp->getOpcodeStr()
225 << FixItHint::CreateReplacement(
226 SourceRange(IfLocation, Lexer::getLocForEndOfToken(
227 ThenLocation, 0, Source, LO)),
229 FunctionName, BinaryOp, Comment))
230 << IncludeInserter.createIncludeInsertion(
234 if (
minCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,
235 (*Result.Context))) {
236 ReplaceAndDiagnose(
"std::min");
237 }
else if (
maxCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,
238 (*Result.Context))) {
239 ReplaceAndDiagnose(
"std::max");
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static std::string createReplacement(const Expr *CondLhs, const Expr *CondRhs, const Expr *AssignLhs, const SourceManager &Source, const LangOptions &LO, StringRef FunctionName, const BinaryOperator *BO, StringRef Comment="")