brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.7 KiB · 574cbea Raw
176 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 "UseIntegerSignComparisonCheck.h"10#include "clang/AST/Expr.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/Lex/Lexer.h"13 14using namespace clang::ast_matchers;15using namespace clang::ast_matchers::internal;16 17namespace clang::tidy::modernize {18 19/// Find if the passed type is the actual "char" type,20/// not applicable to explicit "signed char" or "unsigned char" types.21static bool isActualCharType(const clang::QualType &Ty) {22  using namespace clang;23  const Type *DesugaredType = Ty->getUnqualifiedDesugaredType();24  if (const auto *BT = llvm::dyn_cast<BuiltinType>(DesugaredType))25    return (BT->getKind() == BuiltinType::Char_U ||26            BT->getKind() == BuiltinType::Char_S);27  return false;28}29 30namespace {31AST_MATCHER(clang::QualType, isActualChar) {32  return clang::tidy::modernize::isActualCharType(Node);33}34} // namespace35 36static BindableMatcher<clang::Stmt>37intCastExpression(bool IsSigned, StringRef CastBindName = {}) {38  // std::cmp_{} functions trigger a compile-time error if either LHS or RHS39  // is a non-integer type, char, enum or bool40  // (unsigned char/ signed char are Ok and can be used).41  auto IntTypeExpr = expr(hasType(hasCanonicalType(qualType(42      IsSigned ? isSignedInteger() : isUnsignedInteger(),43      unless(isActualChar()), unless(booleanType()), unless(enumType())))));44 45  const auto ImplicitCastExpr =46      CastBindName.empty() ? implicitCastExpr(hasSourceExpression(IntTypeExpr))47                           : implicitCastExpr(hasSourceExpression(IntTypeExpr))48                                 .bind(CastBindName);49 50  const auto CStyleCastExpr = cStyleCastExpr(has(ImplicitCastExpr));51  const auto StaticCastExpr = cxxStaticCastExpr(has(ImplicitCastExpr));52  const auto FunctionalCastExpr = cxxFunctionalCastExpr(has(ImplicitCastExpr));53 54  return expr(anyOf(ImplicitCastExpr, CStyleCastExpr, StaticCastExpr,55                    FunctionalCastExpr));56}57 58static StringRef parseOpCode(BinaryOperator::Opcode Code) {59  switch (Code) {60  case BO_LT:61    return "cmp_less";62  case BO_GT:63    return "cmp_greater";64  case BO_LE:65    return "cmp_less_equal";66  case BO_GE:67    return "cmp_greater_equal";68  case BO_EQ:69    return "cmp_equal";70  case BO_NE:71    return "cmp_not_equal";72  default:73    llvm_unreachable("invalid opcode");74  }75}76 77UseIntegerSignComparisonCheck::UseIntegerSignComparisonCheck(78    StringRef Name, ClangTidyContext *Context)79    : ClangTidyCheck(Name, Context),80      IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",81                                               utils::IncludeSorter::IS_LLVM),82                      areDiagsSelfContained()),83      EnableQtSupport(Options.get("EnableQtSupport", false)) {}84 85void UseIntegerSignComparisonCheck::storeOptions(86    ClangTidyOptions::OptionMap &Opts) {87  Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());88  Options.store(Opts, "EnableQtSupport", EnableQtSupport);89}90 91void UseIntegerSignComparisonCheck::registerMatchers(MatchFinder *Finder) {92  const auto SignedIntCastExpr = intCastExpression(true, "sIntCastExpression");93  const auto UnSignedIntCastExpr = intCastExpression(false);94 95  // Flag all operators "==", "<=", ">=", "<", ">", "!="96  // that are used between signed/unsigned97  const auto CompareOperator =98      binaryOperator(hasAnyOperatorName("==", "<=", ">=", "<", ">", "!="),99                     hasOperands(SignedIntCastExpr, UnSignedIntCastExpr),100                     unless(isInTemplateInstantiation()))101          .bind("intComparison");102 103  Finder->addMatcher(CompareOperator, this);104}105 106void UseIntegerSignComparisonCheck::registerPPCallbacks(107    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {108  IncludeInserter.registerPreprocessor(PP);109}110 111void UseIntegerSignComparisonCheck::check(112    const MatchFinder::MatchResult &Result) {113  const auto *SignedCastExpression =114      Result.Nodes.getNodeAs<ImplicitCastExpr>("sIntCastExpression");115  assert(SignedCastExpression);116 117  // Ignore the match if we know that the signed int value is not negative.118  Expr::EvalResult EVResult;119  if (!SignedCastExpression->isValueDependent() &&120      SignedCastExpression->getSubExpr()->EvaluateAsInt(EVResult,121                                                        *Result.Context) &&122      EVResult.Val.getInt().isNonNegative())123    return;124 125  const auto *BinaryOp =126      Result.Nodes.getNodeAs<BinaryOperator>("intComparison");127  assert(BinaryOp);128 129  const Expr *LHS = BinaryOp->getLHS()->IgnoreImpCasts();130  const Expr *RHS = BinaryOp->getRHS()->IgnoreImpCasts();131  const Expr *SubExprLHS = nullptr;132  const Expr *SubExprRHS = nullptr;133  SourceRange R1(LHS->getBeginLoc());134  SourceRange R2(BinaryOp->getOperatorLoc());135  SourceRange R3(Lexer::getLocForEndOfToken(136      RHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));137  if (const auto *LHSCast = llvm::dyn_cast<ExplicitCastExpr>(LHS)) {138    SubExprLHS = LHSCast->getSubExpr();139    R1.setEnd(SubExprLHS->getBeginLoc().getLocWithOffset(-1));140    R2.setBegin(Lexer::getLocForEndOfToken(141        SubExprLHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));142  }143  if (const auto *RHSCast = llvm::dyn_cast<ExplicitCastExpr>(RHS)) {144    SubExprRHS = RHSCast->getSubExpr();145    R2.setEnd(SubExprRHS->getBeginLoc().getLocWithOffset(-1));146    R3.setBegin(Lexer::getLocForEndOfToken(147        SubExprRHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));148  }149  const DiagnosticBuilder Diag =150      diag(BinaryOp->getBeginLoc(),151           "comparison between 'signed' and 'unsigned' integers");152  StringRef CmpNamespace;153  StringRef CmpHeader;154 155  if (getLangOpts().CPlusPlus20) {156    CmpHeader = "<utility>";157    CmpNamespace = "std::";158  } else if (getLangOpts().CPlusPlus17 && EnableQtSupport) {159    CmpHeader = "<QtCore/q20utility.h>";160    CmpNamespace = "q20::";161  }162 163  // Prefer modernize-use-integer-sign-comparison when C++20 is available!164  Diag << FixItHint::CreateReplacement(165      CharSourceRange(R1, SubExprLHS != nullptr),166      Twine(CmpNamespace + parseOpCode(BinaryOp->getOpcode()) + "(").str());167  Diag << FixItHint::CreateReplacement(R2, ",");168  Diag << FixItHint::CreateReplacement(CharSourceRange::getCharRange(R3), ")");169 170  // If there is no include for cmp_{*} functions, we'll add it.171  Diag << IncludeInserter.createIncludeInsertion(172      Result.SourceManager->getFileID(BinaryOp->getBeginLoc()), CmpHeader);173}174 175} // namespace clang::tidy::modernize176