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 (llvm::isa<IntegerLiteral>(CondRhs)) {
90 }
else if (llvm::isa<IntegerLiteral>(CondLhs)) {
96 return GlobalImplicitCastType;
101 const Expr *AssignLhs,
const SourceManager &Source,
102 const LangOptions &LO, StringRef FunctionName,
103 const BinaryOperator *BO, StringRef Comment =
"") {
104 const llvm::StringRef CondLhsStr = Lexer::getSourceText(
105 Source.getExpansionRange(CondLhs->getSourceRange()), Source, LO);
106 const llvm::StringRef CondRhsStr = Lexer::getSourceText(
107 Source.getExpansionRange(CondRhs->getSourceRange()), Source, LO);
108 const llvm::StringRef AssignLhsStr = Lexer::getSourceText(
109 Source.getExpansionRange(AssignLhs->getSourceRange()), Source, LO);
111 const QualType GlobalImplicitCastType =
114 return (AssignLhsStr +
" = " + FunctionName +
115 (!GlobalImplicitCastType.isNull()
116 ?
"<" + GlobalImplicitCastType.getAsString() +
">("
118 CondLhsStr +
", " + CondRhsStr +
");" + (Comment.empty() ?
"" :
" ") +
134 auto AssignOperator =
135 binaryOperator(hasOperatorName(
"="),
136 hasLHS(expr(unless(isTypeDependent())).bind(
"AssignLhs")),
137 hasRHS(expr(unless(isTypeDependent())).bind(
"AssignRhs")));
138 auto BinaryOperator =
139 binaryOperator(hasAnyOperatorName(
"<",
">",
"<=",
">="),
140 hasLHS(expr(unless(isTypeDependent())).bind(
"CondLhs")),
141 hasRHS(expr(unless(isTypeDependent())).bind(
"CondRhs")))
144 ifStmt(stmt().bind(
"if"), unless(isIfInMacro()),
145 unless(hasElse(stmt())),
146 hasCondition(BinaryOperator),
148 anyOf(stmt(AssignOperator),
149 compoundStmt(statementCountIs(1), has(AssignOperator)))),
150 hasParent(stmt(unless(ifStmt(hasElse(
151 equalsBoundNode(
"if"))))))),
162 const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if");
163 const clang::LangOptions &LO = Result.Context->getLangOpts();
164 const auto *CondLhs = Result.Nodes.getNodeAs<Expr>(
"CondLhs");
165 const auto *CondRhs = Result.Nodes.getNodeAs<Expr>(
"CondRhs");
166 const auto *AssignLhs = Result.Nodes.getNodeAs<Expr>(
"AssignLhs");
167 const auto *AssignRhs = Result.Nodes.getNodeAs<Expr>(
"AssignRhs");
168 const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>(
"binaryOp");
169 const clang::BinaryOperatorKind BinaryOpcode = BinaryOp->getOpcode();
170 const SourceLocation IfLocation = If->getIfLoc();
171 const SourceLocation ThenLocation = If->getEndLoc();
173 auto ReplaceAndDiagnose = [&](
const llvm::StringRef FunctionName) {
174 const SourceManager &Source = *Result.SourceManager;
175 llvm::SmallString<64> Comment;
177 const auto AppendNormalized = [&](llvm::StringRef Text) {
180 if (!Comment.empty())
186 const auto GetSourceText = [&](SourceLocation StartLoc,
187 SourceLocation EndLoc) {
188 return Lexer::getSourceText(
189 CharSourceRange::getCharRange(
190 Lexer::getLocForEndOfToken(StartLoc, 0, Source, LO), EndLoc),
199 GetSourceText(If->getRParenLoc(), If->getThen()->getBeginLoc()));
201 if (
const auto *CS = dyn_cast<CompoundStmt>(If->getThen())) {
202 const Stmt *Inner = CS->body_front();
209 AppendNormalized(GetSourceText(CS->getBeginLoc(), Inner->getBeginLoc()));
214 llvm::StringRef PostInner =
215 GetSourceText(Inner->getEndLoc(), CS->getEndLoc());
219 const size_t Semi = PostInner.find(
';');
220 if (Semi != llvm::StringRef::npos &&
221 PostInner.take_front(Semi).trim().empty()) {
222 PostInner = PostInner.drop_front(Semi + 1);
224 AppendNormalized(PostInner);
227 diag(IfLocation,
"use `%0` instead of `%1`")
228 << FunctionName << BinaryOp->getOpcodeStr()
229 << FixItHint::CreateReplacement(
230 SourceRange(IfLocation, Lexer::getLocForEndOfToken(
231 ThenLocation, 0, Source, LO)),
233 FunctionName, BinaryOp, Comment))
234 << IncludeInserter.createIncludeInsertion(
238 if (
minCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,
239 (*Result.Context))) {
240 ReplaceAndDiagnose(
"std::min");
241 }
else if (
maxCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,
242 (*Result.Context))) {
243 ReplaceAndDiagnose(
"std::max");
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.