================ @@ -0,0 +1,188 @@ +//===--- UseStdMinMaxCheck.cpp - clang-tidy -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "UseStdMinMaxCheck.h" +#include "../utils/ASTUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Preprocessor.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::readability { + +namespace { + +// Ignore if statements that are inside macros. +AST_MATCHER(IfStmt, isIfInMacro) { + return Node.getIfLoc().isMacroID() || Node.getEndLoc().isMacroID(); +} + +} // namespace + +static const llvm::StringRef AlgorithmHeader("<algorithm>"); + +static bool minCondition(const BinaryOperator::Opcode Op, const Expr *CondLhs, + const Expr *CondRhs, const Expr *AssignLhs, + const Expr *AssignRhs, const ASTContext &Context) { + if ((Op == BO_LT || Op == BO_LE) && + (tidy::utils::areStatementsIdentical(CondLhs, AssignRhs, Context) && + tidy::utils::areStatementsIdentical(CondRhs, AssignLhs, Context))) + return true; + + if ((Op == BO_GT || Op == BO_GE) && + (tidy::utils::areStatementsIdentical(CondLhs, AssignLhs, Context) && + tidy::utils::areStatementsIdentical(CondRhs, AssignRhs, Context))) + return true; + + return false; +} + +static bool maxCondition(const BinaryOperator::Opcode Op, const Expr *CondLhs, + const Expr *CondRhs, const Expr *AssignLhs, + const Expr *AssignRhs, const ASTContext &Context) { + if ((Op == BO_LT || Op == BO_LE) && + (tidy::utils::areStatementsIdentical(CondLhs, AssignLhs, Context) && + tidy::utils::areStatementsIdentical(CondRhs, AssignRhs, Context))) + return true; + + if ((Op == BO_GT || Op == BO_GE) && + (tidy::utils::areStatementsIdentical(CondLhs, AssignRhs, Context) && + tidy::utils::areStatementsIdentical(CondRhs, AssignLhs, Context))) + return true; + + return false; +} + +QualType getNonTemplateAlias(QualType QT) { + while (true) { + // cast to a TypedefType + if (const TypedefType *TT = dyn_cast<TypedefType>(QT)) { + // check if the typedef is a template and if it is dependent + if (!TT->getDecl()->getDescribedTemplate() && + !TT->getDecl()->getDeclContext()->isDependentContext()) + return QT; + QT = TT->getDecl()->getUnderlyingType(); + } + // cast to elaborated type + else if (const ElaboratedType *ET = dyn_cast<ElaboratedType>(QT)) { + QT = ET->getNamedType(); + } else { + break; + } + } + return QT; +} + +static std::string createReplacement(const Expr *CondLhs, const Expr *CondRhs, + const Expr *AssignLhs, + const SourceManager &Source, + const LangOptions &LO, + StringRef FunctionName, + const BinaryOperator *BO) { + const llvm::StringRef CondLhsStr = Lexer::getSourceText( + Source.getExpansionRange(CondLhs->getSourceRange()), Source, LO); + const llvm::StringRef CondRhsStr = Lexer::getSourceText( + Source.getExpansionRange(CondRhs->getSourceRange()), Source, LO); + const llvm::StringRef AssignLhsStr = Lexer::getSourceText( + Source.getExpansionRange(AssignLhs->getSourceRange()), Source, LO); + + clang::QualType GlobalImplicitCastType; + clang::QualType LhsType = CondLhs->getType() + .getCanonicalType() + .getNonReferenceType() + .getUnqualifiedType(); + clang::QualType RhsType = CondRhs->getType() + .getCanonicalType() + .getNonReferenceType() + .getUnqualifiedType(); + if (LhsType != RhsType) { + GlobalImplicitCastType = getNonTemplateAlias(BO->getLHS()->getType()); + } + + return (AssignLhsStr + " = " + FunctionName + + (!GlobalImplicitCastType.isNull() + ? "<" + GlobalImplicitCastType.getAsString() + ">(" + : "(") + + CondLhsStr + ", " + CondRhsStr + ");") + .str(); +} + +UseStdMinMaxCheck::UseStdMinMaxCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", + utils::IncludeSorter::IS_LLVM), + areDiagsSelfContained()) {} + +void UseStdMinMaxCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); +} + +void UseStdMinMaxCheck::registerMatchers(MatchFinder *Finder) { + auto AssignOperator = + binaryOperator(hasOperatorName("="), + hasLHS(expr(unless(isTypeDependent())).bind("AssignLhs")), + hasRHS(expr(unless(isTypeDependent())).bind("AssignRhs"))); + auto BinaryOperator = + binaryOperator(hasAnyOperatorName("<", ">", "<=", ">="), + hasLHS(expr(unless(isTypeDependent())).bind("CondLhs")), + hasRHS(expr(unless(isTypeDependent())).bind("CondRhs"))) + .bind("binaryOp"); + Finder->addMatcher( + ifStmt(stmt().bind("if"), unless(isIfInMacro()), + unless(hasElse(stmt())), // Ensure `if` has no `else` + hasCondition(BinaryOperator), + hasThen( + anyOf(stmt(AssignOperator), + compoundStmt(statementCountIs(1), has(AssignOperator)))), + hasParent(stmt(unless(ifStmt(hasElse( + equalsBoundNode("if"))))))), // Ensure `if` has no `else if` + this); +} + +void UseStdMinMaxCheck::registerPPCallbacks(const SourceManager &SM, + Preprocessor *PP, + Preprocessor *ModuleExpanderPP) { + IncludeInserter.registerPreprocessor(PP); +} + +void UseStdMinMaxCheck::check(const MatchFinder::MatchResult &Result) { + const auto *If = Result.Nodes.getNodeAs<IfStmt>("if"); + const auto &LO = Result.Context->getLangOpts(); + const auto *CondLhs = Result.Nodes.getNodeAs<Expr>("CondLhs"); + const auto *CondRhs = Result.Nodes.getNodeAs<Expr>("CondRhs"); + const auto *AssignLhs = Result.Nodes.getNodeAs<Expr>("AssignLhs"); + const auto *AssignRhs = Result.Nodes.getNodeAs<Expr>("AssignRhs"); + const auto *BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("binaryOp"); + const auto BinaryOpcode = BinaryOp->getOpcode(); ---------------- 11happy wrote:
done https://github.com/llvm/llvm-project/pull/77816 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits