10#include "../utils/ASTUtils.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Preprocessor.h"
23 return Node.getIfLoc().isMacroID() ||
Node.getEndLoc().isMacroID();
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) &&
65 if (
const TypedefType *TT = dyn_cast<TypedefType>(QT)) {
67 if (!TT->getDecl()->getDescribedTemplate() &&
68 !TT->getDecl()->getDeclContext()->isDependentContext())
70 QT = TT->getDecl()->getUnderlyingType();
73 else if (
const ElaboratedType *ET = dyn_cast<ElaboratedType>(QT)) {
74 QT = ET->getNamedType();
83 const Expr *AssignLhs,
84 const SourceManager &Source,
85 const LangOptions &LO,
86 StringRef FunctionName,
87 const BinaryOperator *BO) {
88 const llvm::StringRef CondLhsStr = Lexer::getSourceText(
89 Source.getExpansionRange(CondLhs->getSourceRange()), Source, LO);
90 const llvm::StringRef CondRhsStr = Lexer::getSourceText(
91 Source.getExpansionRange(CondRhs->getSourceRange()), Source, LO);
92 const llvm::StringRef AssignLhsStr = Lexer::getSourceText(
93 Source.getExpansionRange(AssignLhs->getSourceRange()), Source, LO);
95 clang::QualType GlobalImplicitCastType;
96 clang::QualType LhsType = CondLhs->getType()
98 .getNonReferenceType()
99 .getUnqualifiedType();
100 clang::QualType RhsType = CondRhs->getType()
102 .getNonReferenceType()
103 .getUnqualifiedType();
104 if (LhsType != RhsType) {
108 return (AssignLhsStr +
" = " + FunctionName +
109 (!GlobalImplicitCastType.isNull()
110 ?
"<" + GlobalImplicitCastType.getAsString() +
">("
112 CondLhsStr +
", " + CondRhsStr +
");")
118 IncludeInserter(Options.getLocalOrGlobal(
"IncludeStyle",
119 utils::IncludeSorter::IS_LLVM),
120 areDiagsSelfContained()) {}
127 auto AssignOperator =
128 binaryOperator(hasOperatorName(
"="),
129 hasLHS(expr(unless(isTypeDependent())).bind(
"AssignLhs")),
130 hasRHS(expr(unless(isTypeDependent())).bind(
"AssignRhs")));
131 auto BinaryOperator =
132 binaryOperator(hasAnyOperatorName(
"<",
">",
"<=",
">="),
133 hasLHS(expr(unless(isTypeDependent())).bind(
"CondLhs")),
134 hasRHS(expr(unless(isTypeDependent())).bind(
"CondRhs")))
137 ifStmt(stmt().bind(
"if"), unless(isIfInMacro()),
138 unless(hasElse(stmt())),
139 hasCondition(BinaryOperator),
141 anyOf(stmt(AssignOperator),
142 compoundStmt(statementCountIs(1), has(AssignOperator)))),
143 hasParent(stmt(unless(ifStmt(hasElse(
144 equalsBoundNode(
"if"))))))),
150 Preprocessor *ModuleExpanderPP) {
155 const auto *If = Result.Nodes.getNodeAs<IfStmt>(
"if");
156 const clang::LangOptions &LO = Result.Context->getLangOpts();
157 const auto *CondLhs = Result.Nodes.getNodeAs<Expr>(
"CondLhs");
158 const auto *CondRhs = Result.Nodes.getNodeAs<Expr>(
"CondRhs");
159 const auto *AssignLhs = Result.Nodes.getNodeAs<Expr>(
"AssignLhs");
160 const auto *AssignRhs = Result.Nodes.getNodeAs<Expr>(
"AssignRhs");
161 const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>(
"binaryOp");
162 const clang::BinaryOperatorKind BinaryOpcode = BinaryOp->getOpcode();
163 const SourceLocation IfLocation = If->getIfLoc();
164 const SourceLocation ThenLocation = If->getEndLoc();
166 auto ReplaceAndDiagnose = [&](
const llvm::StringRef FunctionName) {
167 const SourceManager &Source = *Result.SourceManager;
168 diag(IfLocation,
"use `%0` instead of `%1`")
169 << FunctionName << BinaryOp->getOpcodeStr()
170 << FixItHint::CreateReplacement(
171 SourceRange(IfLocation, Lexer::getLocForEndOfToken(
172 ThenLocation, 0, Source, LO)),
174 FunctionName, BinaryOp))
179 if (
minCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,
180 (*Result.Context))) {
181 ReplaceAndDiagnose(
"std::min");
182 }
else if (
maxCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,
183 (*Result.Context))) {
184 ReplaceAndDiagnose(
"std::max");
llvm::SmallString< 256U > Name
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
UseStdMinMaxCheck(StringRef Name, ClangTidyContext *Context)
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerPreprocessor(Preprocessor *PP)
Registers this with the Preprocessor PP, must be called before this class is used.
std::optional< FixItHint > createIncludeInsertion(FileID FileID, llvm::StringRef Header)
Creates a Header inclusion directive fixit in the File FileID.
IncludeSorter::IncludeStyle getStyle() const
static std::string createReplacement(const Expr *CondLhs, const Expr *CondRhs, const Expr *AssignLhs, const SourceManager &Source, const LangOptions &LO, StringRef FunctionName, const BinaryOperator *BO)
AST_MATCHER(CXXMethodDecl, isStatic)
static bool minCondition(const BinaryOperator::Opcode Op, const Expr *CondLhs, const Expr *CondRhs, const Expr *AssignLhs, const Expr *AssignRhs, const ASTContext &Context)
static bool maxCondition(const BinaryOperator::Opcode Op, const Expr *CondLhs, const Expr *CondRhs, const Expr *AssignLhs, const Expr *AssignRhs, const ASTContext &Context)
QualType getNonTemplateAlias(QualType QT)
static const llvm::StringRef AlgorithmHeader("<algorithm>")
bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt, const ASTContext &Context, bool Canonical)
llvm::StringMap< ClangTidyValue > OptionMap