clang-tools 22.0.0git
UseIntegerSignComparisonCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
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) {
33}
34} // namespace
35
36static BindableMatcher<clang::Stmt>
37intCastExpression(bool IsSigned,
38 const std::string &CastBindName = std::string()) {
39 // std::cmp_{} functions trigger a compile-time error if either LHS or RHS
40 // is a non-integer type, char, enum or bool
41 // (unsigned char/ signed char are Ok and can be used).
42 auto IntTypeExpr = expr(hasType(hasCanonicalType(qualType(
43 isInteger(), IsSigned ? isSignedInteger() : isUnsignedInteger(),
44 unless(isActualChar()), unless(booleanType()), unless(enumType())))));
45
46 const auto ImplicitCastExpr =
47 CastBindName.empty() ? implicitCastExpr(hasSourceExpression(IntTypeExpr))
48 : implicitCastExpr(hasSourceExpression(IntTypeExpr))
49 .bind(CastBindName);
50
51 const auto CStyleCastExpr = cStyleCastExpr(has(ImplicitCastExpr));
52 const auto StaticCastExpr = cxxStaticCastExpr(has(ImplicitCastExpr));
53 const auto FunctionalCastExpr = cxxFunctionalCastExpr(has(ImplicitCastExpr));
54
55 return expr(anyOf(ImplicitCastExpr, CStyleCastExpr, StaticCastExpr,
56 FunctionalCastExpr));
57}
58
59static StringRef parseOpCode(BinaryOperator::Opcode Code) {
60 switch (Code) {
61 case BO_LT:
62 return "cmp_less";
63 case BO_GT:
64 return "cmp_greater";
65 case BO_LE:
66 return "cmp_less_equal";
67 case BO_GE:
68 return "cmp_greater_equal";
69 case BO_EQ:
70 return "cmp_equal";
71 case BO_NE:
72 return "cmp_not_equal";
73 default:
74 return "";
75 }
76}
77
79 StringRef Name, ClangTidyContext *Context)
80 : ClangTidyCheck(Name, Context),
81 IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
82 utils::IncludeSorter::IS_LLVM),
83 areDiagsSelfContained()),
84 EnableQtSupport(Options.get("EnableQtSupport", false)) {}
85
88 Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
89 Options.store(Opts, "EnableQtSupport", EnableQtSupport);
90}
91
93 const auto SignedIntCastExpr = intCastExpression(true, "sIntCastExpression");
94 const auto UnSignedIntCastExpr = intCastExpression(false);
95
96 // Flag all operators "==", "<=", ">=", "<", ">", "!="
97 // that are used between signed/unsigned
98 const auto CompareOperator =
99 binaryOperator(hasAnyOperatorName("==", "<=", ">=", "<", ">", "!="),
100 hasOperands(SignedIntCastExpr, UnSignedIntCastExpr),
101 unless(isInTemplateInstantiation()))
102 .bind("intComparison");
103
104 Finder->addMatcher(CompareOperator, this);
105}
106
108 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
109 IncludeInserter.registerPreprocessor(PP);
110}
111
113 const MatchFinder::MatchResult &Result) {
114 const auto *SignedCastExpression =
115 Result.Nodes.getNodeAs<ImplicitCastExpr>("sIntCastExpression");
116 assert(SignedCastExpression);
117
118 // Ignore the match if we know that the signed int value is not negative.
119 Expr::EvalResult EVResult;
120 if (!SignedCastExpression->isValueDependent() &&
121 SignedCastExpression->getSubExpr()->EvaluateAsInt(EVResult,
122 *Result.Context)) {
123 const llvm::APSInt SValue = EVResult.Val.getInt();
124 if (SValue.isNonNegative())
125 return;
126 }
127
128 const auto *BinaryOp =
129 Result.Nodes.getNodeAs<BinaryOperator>("intComparison");
130 if (BinaryOp == nullptr)
131 return;
132
133 const BinaryOperator::Opcode OpCode = BinaryOp->getOpcode();
134
135 const Expr *LHS = BinaryOp->getLHS()->IgnoreImpCasts();
136 const Expr *RHS = BinaryOp->getRHS()->IgnoreImpCasts();
137 if (LHS == nullptr || RHS == nullptr)
138 return;
139 const Expr *SubExprLHS = nullptr;
140 const Expr *SubExprRHS = nullptr;
141 SourceRange R1(LHS->getBeginLoc());
142 SourceRange R2(BinaryOp->getOperatorLoc());
143 SourceRange R3(Lexer::getLocForEndOfToken(
144 RHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));
145 if (const auto *LHSCast = llvm::dyn_cast<ExplicitCastExpr>(LHS)) {
146 SubExprLHS = LHSCast->getSubExpr();
147 R1 = SourceRange(LHS->getBeginLoc(),
148 SubExprLHS->getBeginLoc().getLocWithOffset(-1));
149 R2.setBegin(Lexer::getLocForEndOfToken(
150 SubExprLHS->getEndLoc(), 0, *Result.SourceManager, getLangOpts()));
151 }
152 if (const auto *RHSCast = llvm::dyn_cast<ExplicitCastExpr>(RHS)) {
153 SubExprRHS = RHSCast->getSubExpr();
154 R2.setEnd(SubExprRHS->getBeginLoc().getLocWithOffset(-1));
155 }
156 DiagnosticBuilder Diag =
157 diag(BinaryOp->getBeginLoc(),
158 "comparison between 'signed' and 'unsigned' integers");
159 std::string CmpNamespace;
160 llvm::StringRef CmpHeader;
161
162 if (getLangOpts().CPlusPlus20) {
163 CmpHeader = "<utility>";
164 CmpNamespace = llvm::Twine("std::" + parseOpCode(OpCode)).str();
165 } else if (getLangOpts().CPlusPlus17 && EnableQtSupport) {
166 CmpHeader = "<QtCore/q20utility.h>";
167 CmpNamespace = llvm::Twine("q20::" + parseOpCode(OpCode)).str();
168 }
169
170 // Prefer modernize-use-integer-sign-comparison when C++20 is available!
171 Diag << FixItHint::CreateReplacement(
172 CharSourceRange(R1, SubExprLHS != nullptr),
173 llvm::Twine(CmpNamespace + "(").str());
174 Diag << FixItHint::CreateReplacement(R2, ",");
175 Diag << FixItHint::CreateReplacement(CharSourceRange::getCharRange(R3), ")");
176
177 // If there is no include for cmp_{*} functions, we'll add it.
178 Diag << IncludeInserter.createIncludeInsertion(
179 Result.SourceManager->getFileID(BinaryOp->getBeginLoc()), CmpHeader);
180}
181
182} // namespace clang::tidy::modernize
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
UseIntegerSignComparisonCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
static BindableMatcher< clang::Stmt > intCastExpression(bool IsSigned, const std::string &CastBindName=std::string())
static bool isActualCharType(const clang::QualType &Ty)
Find if the passed type is the actual "char" type, not applicable to explicit "signed char" or "unsig...
static StringRef parseOpCode(BinaryOperator::Opcode Code)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
llvm::StringMap< ClangTidyValue > OptionMap