brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.0 KiB · 5a7add8 Raw
196 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "UseStdMinMaxCheck.h"10#include "../utils/ASTUtils.h"11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/Lex/Preprocessor.h"14 15using namespace clang::ast_matchers;16 17namespace clang::tidy::readability {18 19namespace {20 21// Ignore if statements that are inside macros.22AST_MATCHER(IfStmt, isIfInMacro) {23  return Node.getIfLoc().isMacroID() || Node.getEndLoc().isMacroID();24}25 26} // namespace27 28static const llvm::StringRef AlgorithmHeader("<algorithm>");29 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) &&34      (tidy::utils::areStatementsIdentical(CondLhs, AssignRhs, Context) &&35       tidy::utils::areStatementsIdentical(CondRhs, AssignLhs, Context)))36    return true;37 38  if ((Op == BO_GT || Op == BO_GE) &&39      (tidy::utils::areStatementsIdentical(CondLhs, AssignLhs, Context) &&40       tidy::utils::areStatementsIdentical(CondRhs, AssignRhs, Context)))41    return true;42 43  return false;44}45 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) &&50      (tidy::utils::areStatementsIdentical(CondLhs, AssignLhs, Context) &&51       tidy::utils::areStatementsIdentical(CondRhs, AssignRhs, Context)))52    return true;53 54  if ((Op == BO_GT || Op == BO_GE) &&55      (tidy::utils::areStatementsIdentical(CondLhs, AssignRhs, Context) &&56       tidy::utils::areStatementsIdentical(CondRhs, AssignLhs, Context)))57    return true;58 59  return false;60}61 62static QualType getNonTemplateAlias(QualType QT) {63  while (true) {64    // cast to a TypedefType65    if (const auto *TT = dyn_cast<TypedefType>(QT)) {66      // check if the typedef is a template and if it is dependent67      if (!TT->getDecl()->getDescribedTemplate() &&68          !TT->getDecl()->getDeclContext()->isDependentContext())69        return QT;70      QT = TT->desugar();71    } else {72      break;73    }74  }75  return QT;76}77 78static QualType getReplacementCastType(const Expr *CondLhs, const Expr *CondRhs,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)) {89      GlobalImplicitCastType = getNonTemplateAlias(LhsType);90    } else if (llvm::isa<IntegerLiteral>(CondLhs)) {91      GlobalImplicitCastType = getNonTemplateAlias(RhsType);92    } else {93      GlobalImplicitCastType = getNonTemplateAlias(ComparedType);94    }95  }96  return GlobalImplicitCastType;97}98 99static std::string createReplacement(const Expr *CondLhs, const Expr *CondRhs,100                                     const Expr *AssignLhs,101                                     const SourceManager &Source,102                                     const LangOptions &LO,103                                     StringRef FunctionName,104                                     const BinaryOperator *BO) {105  const llvm::StringRef CondLhsStr = Lexer::getSourceText(106      Source.getExpansionRange(CondLhs->getSourceRange()), Source, LO);107  const llvm::StringRef CondRhsStr = Lexer::getSourceText(108      Source.getExpansionRange(CondRhs->getSourceRange()), Source, LO);109  const llvm::StringRef AssignLhsStr = Lexer::getSourceText(110      Source.getExpansionRange(AssignLhs->getSourceRange()), Source, LO);111 112  const QualType GlobalImplicitCastType =113      getReplacementCastType(CondLhs, CondRhs, BO->getLHS()->getType());114 115  return (AssignLhsStr + " = " + FunctionName +116          (!GlobalImplicitCastType.isNull()117               ? "<" + GlobalImplicitCastType.getAsString() + ">("118               : "(") +119          CondLhsStr + ", " + CondRhsStr + ");")120      .str();121}122 123UseStdMinMaxCheck::UseStdMinMaxCheck(StringRef Name, ClangTidyContext *Context)124    : ClangTidyCheck(Name, Context),125      IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",126                                               utils::IncludeSorter::IS_LLVM),127                      areDiagsSelfContained()) {}128 129void UseStdMinMaxCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {130  Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());131}132 133void UseStdMinMaxCheck::registerMatchers(MatchFinder *Finder) {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")))142          .bind("binaryOp");143  Finder->addMatcher(144      ifStmt(stmt().bind("if"), unless(isIfInMacro()),145             unless(hasElse(stmt())), // Ensure `if` has no `else`146             hasCondition(BinaryOperator),147             hasThen(148                 anyOf(stmt(AssignOperator),149                       compoundStmt(statementCountIs(1), has(AssignOperator)))),150             hasParent(stmt(unless(ifStmt(hasElse(151                 equalsBoundNode("if"))))))), // Ensure `if` has no `else if`152      this);153}154 155void UseStdMinMaxCheck::registerPPCallbacks(const SourceManager &SM,156                                            Preprocessor *PP,157                                            Preprocessor *ModuleExpanderPP) {158  IncludeInserter.registerPreprocessor(PP);159}160 161void UseStdMinMaxCheck::check(const MatchFinder::MatchResult &Result) {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();172 173  auto ReplaceAndDiagnose = [&](const llvm::StringRef FunctionName) {174    const SourceManager &Source = *Result.SourceManager;175    diag(IfLocation, "use `%0` instead of `%1`")176        << FunctionName << BinaryOp->getOpcodeStr()177        << FixItHint::CreateReplacement(178               SourceRange(IfLocation, Lexer::getLocForEndOfToken(179                                           ThenLocation, 0, Source, LO)),180               createReplacement(CondLhs, CondRhs, AssignLhs, Source, LO,181                                 FunctionName, BinaryOp))182        << IncludeInserter.createIncludeInsertion(183               Source.getFileID(If->getBeginLoc()), AlgorithmHeader);184  };185 186  if (minCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,187                   (*Result.Context))) {188    ReplaceAndDiagnose("std::min");189  } else if (maxCondition(BinaryOpcode, CondLhs, CondRhs, AssignLhs, AssignRhs,190                          (*Result.Context))) {191    ReplaceAndDiagnose("std::max");192  }193}194 195} // namespace clang::tidy::readability196