18b63bfbfSqt-tatiana //===--- UseIntegerSignComparisonCheck.cpp - clang-tidy -------------------===// 28b63bfbfSqt-tatiana // 38b63bfbfSqt-tatiana // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 48b63bfbfSqt-tatiana // See https://llvm.org/LICENSE.txt for license information. 58b63bfbfSqt-tatiana // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 68b63bfbfSqt-tatiana // 78b63bfbfSqt-tatiana //===----------------------------------------------------------------------===// 88b63bfbfSqt-tatiana 98b63bfbfSqt-tatiana #include "UseIntegerSignComparisonCheck.h" 108b63bfbfSqt-tatiana #include "clang/AST/Expr.h" 118b63bfbfSqt-tatiana #include "clang/ASTMatchers/ASTMatchFinder.h" 128b63bfbfSqt-tatiana #include "clang/Lex/Lexer.h" 138b63bfbfSqt-tatiana 148b63bfbfSqt-tatiana using namespace clang::ast_matchers; 158b63bfbfSqt-tatiana using namespace clang::ast_matchers::internal; 168b63bfbfSqt-tatiana 178b63bfbfSqt-tatiana namespace clang::tidy::modernize { 188b63bfbfSqt-tatiana 198b63bfbfSqt-tatiana /// Find if the passed type is the actual "char" type, 208b63bfbfSqt-tatiana /// not applicable to explicit "signed char" or "unsigned char" types. 218b63bfbfSqt-tatiana static bool isActualCharType(const clang::QualType &Ty) { 228b63bfbfSqt-tatiana using namespace clang; 238b63bfbfSqt-tatiana const Type *DesugaredType = Ty->getUnqualifiedDesugaredType(); 248b63bfbfSqt-tatiana if (const auto *BT = llvm::dyn_cast<BuiltinType>(DesugaredType)) 258b63bfbfSqt-tatiana return (BT->getKind() == BuiltinType::Char_U || 268b63bfbfSqt-tatiana BT->getKind() == BuiltinType::Char_S); 278b63bfbfSqt-tatiana return false; 288b63bfbfSqt-tatiana } 298b63bfbfSqt-tatiana 308b63bfbfSqt-tatiana namespace { 318b63bfbfSqt-tatiana AST_MATCHER(clang::QualType, isActualChar) { 328b63bfbfSqt-tatiana return clang::tidy::modernize::isActualCharType(Node); 338b63bfbfSqt-tatiana } 348b63bfbfSqt-tatiana } // namespace 358b63bfbfSqt-tatiana 368b63bfbfSqt-tatiana static BindableMatcher<clang::Stmt> 378b63bfbfSqt-tatiana intCastExpression(bool IsSigned, 388b63bfbfSqt-tatiana const std::string &CastBindName = std::string()) { 398b63bfbfSqt-tatiana // std::cmp_{} functions trigger a compile-time error if either LHS or RHS 408b63bfbfSqt-tatiana // is a non-integer type, char, enum or bool 418b63bfbfSqt-tatiana // (unsigned char/ signed char are Ok and can be used). 428b63bfbfSqt-tatiana auto IntTypeExpr = expr(hasType(hasCanonicalType(qualType( 438b63bfbfSqt-tatiana isInteger(), IsSigned ? isSignedInteger() : isUnsignedInteger(), 448b63bfbfSqt-tatiana unless(isActualChar()), unless(booleanType()), unless(enumType()))))); 458b63bfbfSqt-tatiana 468b63bfbfSqt-tatiana const auto ImplicitCastExpr = 478b63bfbfSqt-tatiana CastBindName.empty() ? implicitCastExpr(hasSourceExpression(IntTypeExpr)) 488b63bfbfSqt-tatiana : implicitCastExpr(hasSourceExpression(IntTypeExpr)) 498b63bfbfSqt-tatiana .bind(CastBindName); 508b63bfbfSqt-tatiana 518b63bfbfSqt-tatiana const auto CStyleCastExpr = cStyleCastExpr(has(ImplicitCastExpr)); 528b63bfbfSqt-tatiana const auto StaticCastExpr = cxxStaticCastExpr(has(ImplicitCastExpr)); 538b63bfbfSqt-tatiana const auto FunctionalCastExpr = cxxFunctionalCastExpr(has(ImplicitCastExpr)); 548b63bfbfSqt-tatiana 558b63bfbfSqt-tatiana return expr(anyOf(ImplicitCastExpr, CStyleCastExpr, StaticCastExpr, 568b63bfbfSqt-tatiana FunctionalCastExpr)); 578b63bfbfSqt-tatiana } 588b63bfbfSqt-tatiana 598b63bfbfSqt-tatiana static StringRef parseOpCode(BinaryOperator::Opcode Code) { 608b63bfbfSqt-tatiana switch (Code) { 618b63bfbfSqt-tatiana case BO_LT: 628b63bfbfSqt-tatiana return "cmp_less"; 638b63bfbfSqt-tatiana case BO_GT: 648b63bfbfSqt-tatiana return "cmp_greater"; 658b63bfbfSqt-tatiana case BO_LE: 668b63bfbfSqt-tatiana return "cmp_less_equal"; 678b63bfbfSqt-tatiana case BO_GE: 688b63bfbfSqt-tatiana return "cmp_greater_equal"; 698b63bfbfSqt-tatiana case BO_EQ: 708b63bfbfSqt-tatiana return "cmp_equal"; 718b63bfbfSqt-tatiana case BO_NE: 728b63bfbfSqt-tatiana return "cmp_not_equal"; 738b63bfbfSqt-tatiana default: 748b63bfbfSqt-tatiana return ""; 758b63bfbfSqt-tatiana } 768b63bfbfSqt-tatiana } 778b63bfbfSqt-tatiana 788b63bfbfSqt-tatiana UseIntegerSignComparisonCheck::UseIntegerSignComparisonCheck( 798b63bfbfSqt-tatiana StringRef Name, ClangTidyContext *Context) 808b63bfbfSqt-tatiana : ClangTidyCheck(Name, Context), 818b63bfbfSqt-tatiana IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", 828b63bfbfSqt-tatiana utils::IncludeSorter::IS_LLVM), 83*aa580c2eSqt-tatiana areDiagsSelfContained()), 84*aa580c2eSqt-tatiana EnableQtSupport(Options.get("EnableQtSupport", false)) {} 858b63bfbfSqt-tatiana 868b63bfbfSqt-tatiana void UseIntegerSignComparisonCheck::storeOptions( 878b63bfbfSqt-tatiana ClangTidyOptions::OptionMap &Opts) { 888b63bfbfSqt-tatiana Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); 89*aa580c2eSqt-tatiana Options.store(Opts, "EnableQtSupport", EnableQtSupport); 908b63bfbfSqt-tatiana } 918b63bfbfSqt-tatiana 928b63bfbfSqt-tatiana void UseIntegerSignComparisonCheck::registerMatchers(MatchFinder *Finder) { 938b63bfbfSqt-tatiana const auto SignedIntCastExpr = intCastExpression(true, "sIntCastExpression"); 948b63bfbfSqt-tatiana const auto UnSignedIntCastExpr = intCastExpression(false); 958b63bfbfSqt-tatiana 968b63bfbfSqt-tatiana // Flag all operators "==", "<=", ">=", "<", ">", "!=" 978b63bfbfSqt-tatiana // that are used between signed/unsigned 988b63bfbfSqt-tatiana const auto CompareOperator = 998b63bfbfSqt-tatiana binaryOperator(hasAnyOperatorName("==", "<=", ">=", "<", ">", "!="), 1008b63bfbfSqt-tatiana hasOperands(SignedIntCastExpr, UnSignedIntCastExpr), 1018b63bfbfSqt-tatiana unless(isInTemplateInstantiation())) 1028b63bfbfSqt-tatiana .bind("intComparison"); 1038b63bfbfSqt-tatiana 1048b63bfbfSqt-tatiana Finder->addMatcher(CompareOperator, this); 1058b63bfbfSqt-tatiana } 1068b63bfbfSqt-tatiana 1078b63bfbfSqt-tatiana void UseIntegerSignComparisonCheck::registerPPCallbacks( 1088b63bfbfSqt-tatiana const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { 1098b63bfbfSqt-tatiana IncludeInserter.registerPreprocessor(PP); 1108b63bfbfSqt-tatiana } 1118b63bfbfSqt-tatiana 1128b63bfbfSqt-tatiana void UseIntegerSignComparisonCheck::check( 1138b63bfbfSqt-tatiana const MatchFinder::MatchResult &Result) { 1148b63bfbfSqt-tatiana const auto *SignedCastExpression = 1158b63bfbfSqt-tatiana Result.Nodes.getNodeAs<ImplicitCastExpr>("sIntCastExpression"); 1168b63bfbfSqt-tatiana assert(SignedCastExpression); 1178b63bfbfSqt-tatiana 1188b63bfbfSqt-tatiana // Ignore the match if we know that the signed int value is not negative. 1198b63bfbfSqt-tatiana Expr::EvalResult EVResult; 1208b63bfbfSqt-tatiana if (!SignedCastExpression->isValueDependent() && 1218b63bfbfSqt-tatiana SignedCastExpression->getSubExpr()->EvaluateAsInt(EVResult, 1228b63bfbfSqt-tatiana *Result.Context)) { 1238b63bfbfSqt-tatiana const llvm::APSInt SValue = EVResult.Val.getInt(); 1248b63bfbfSqt-tatiana if (SValue.isNonNegative()) 1258b63bfbfSqt-tatiana return; 1268b63bfbfSqt-tatiana } 1278b63bfbfSqt-tatiana 1288b63bfbfSqt-tatiana const auto *BinaryOp = 1298b63bfbfSqt-tatiana Result.Nodes.getNodeAs<BinaryOperator>("intComparison"); 1308b63bfbfSqt-tatiana if (BinaryOp == nullptr) 1318b63bfbfSqt-tatiana return; 1328b63bfbfSqt-tatiana 1338b63bfbfSqt-tatiana const BinaryOperator::Opcode OpCode = BinaryOp->getOpcode(); 1348b63bfbfSqt-tatiana 1358b63bfbfSqt-tatiana const Expr *LHS = BinaryOp->getLHS()->IgnoreImpCasts(); 1368b63bfbfSqt-tatiana const Expr *RHS = BinaryOp->getRHS()->IgnoreImpCasts(); 1378b63bfbfSqt-tatiana if (LHS == nullptr || RHS == nullptr) 1388b63bfbfSqt-tatiana return; 1398b63bfbfSqt-tatiana const Expr *SubExprLHS = nullptr; 1408b63bfbfSqt-tatiana const Expr *SubExprRHS = nullptr; 1418b63bfbfSqt-tatiana SourceRange R1 = SourceRange(LHS->getBeginLoc()); 1428b63bfbfSqt-tatiana SourceRange R2 = SourceRange(BinaryOp->getOperatorLoc()); 1438b63bfbfSqt-tatiana SourceRange R3 = SourceRange(Lexer::getLocForEndOfToken( 1448b63bfbfSqt-tatiana RHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts())); 1458b63bfbfSqt-tatiana if (const auto *LHSCast = llvm::dyn_cast<ExplicitCastExpr>(LHS)) { 1468b63bfbfSqt-tatiana SubExprLHS = LHSCast->getSubExpr(); 1478b63bfbfSqt-tatiana R1 = SourceRange(LHS->getBeginLoc(), 1488b63bfbfSqt-tatiana SubExprLHS->getBeginLoc().getLocWithOffset(-1)); 1498b63bfbfSqt-tatiana R2.setBegin(Lexer::getLocForEndOfToken( 1508b63bfbfSqt-tatiana SubExprLHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts())); 1518b63bfbfSqt-tatiana } 1528b63bfbfSqt-tatiana if (const auto *RHSCast = llvm::dyn_cast<ExplicitCastExpr>(RHS)) { 1538b63bfbfSqt-tatiana SubExprRHS = RHSCast->getSubExpr(); 1548b63bfbfSqt-tatiana R2.setEnd(SubExprRHS->getBeginLoc().getLocWithOffset(-1)); 1558b63bfbfSqt-tatiana } 1568b63bfbfSqt-tatiana DiagnosticBuilder Diag = 1578b63bfbfSqt-tatiana diag(BinaryOp->getBeginLoc(), 1588b63bfbfSqt-tatiana "comparison between 'signed' and 'unsigned' integers"); 159*aa580c2eSqt-tatiana std::string CmpNamespace; 160*aa580c2eSqt-tatiana llvm::StringRef CmpHeader; 161*aa580c2eSqt-tatiana 162*aa580c2eSqt-tatiana if (getLangOpts().CPlusPlus20) { 163*aa580c2eSqt-tatiana CmpHeader = "<utility>"; 164*aa580c2eSqt-tatiana CmpNamespace = llvm::Twine("std::" + parseOpCode(OpCode)).str(); 165*aa580c2eSqt-tatiana } else if (getLangOpts().CPlusPlus17 && EnableQtSupport) { 166*aa580c2eSqt-tatiana CmpHeader = "<QtCore/q20utility.h>"; 167*aa580c2eSqt-tatiana CmpNamespace = llvm::Twine("q20::" + parseOpCode(OpCode)).str(); 168*aa580c2eSqt-tatiana } 169*aa580c2eSqt-tatiana 1708b63bfbfSqt-tatiana // Prefer modernize-use-integer-sign-comparison when C++20 is available! 1718b63bfbfSqt-tatiana Diag << FixItHint::CreateReplacement( 1728b63bfbfSqt-tatiana CharSourceRange(R1, SubExprLHS != nullptr), 1738b63bfbfSqt-tatiana llvm::Twine(CmpNamespace + "(").str()); 1748b63bfbfSqt-tatiana Diag << FixItHint::CreateReplacement(R2, ","); 1758b63bfbfSqt-tatiana Diag << FixItHint::CreateReplacement(CharSourceRange::getCharRange(R3), ")"); 1768b63bfbfSqt-tatiana 1778b63bfbfSqt-tatiana // If there is no include for cmp_{*} functions, we'll add it. 1788b63bfbfSqt-tatiana Diag << IncludeInserter.createIncludeInsertion( 1798b63bfbfSqt-tatiana Result.SourceManager->getFileID(BinaryOp->getBeginLoc()), CmpHeader); 1808b63bfbfSqt-tatiana } 1818b63bfbfSqt-tatiana 1828b63bfbfSqt-tatiana } // namespace clang::tidy::modernize 183