1 //===--- UseIntegerSignComparisonCheck.cpp - clang-tidy -------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "UseIntegerSignComparisonCheck.h"
10 #include "clang/AST/Expr.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
14 using namespace clang::ast_matchers
;
15 using namespace clang::ast_matchers::internal
;
17 namespace clang::tidy::modernize
{
19 /// Find if the passed type is the actual "char" type,
20 /// not applicable to explicit "signed char" or "unsigned char" types.
21 static 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
);
31 AST_MATCHER(clang::QualType
, isActualChar
) {
32 return clang::tidy::modernize::isActualCharType(Node
);
36 static BindableMatcher
<clang::Stmt
>
37 intCastExpression(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())))));
46 const auto ImplicitCastExpr
=
47 CastBindName
.empty() ? implicitCastExpr(hasSourceExpression(IntTypeExpr
))
48 : implicitCastExpr(hasSourceExpression(IntTypeExpr
))
51 const auto CStyleCastExpr
= cStyleCastExpr(has(ImplicitCastExpr
));
52 const auto StaticCastExpr
= cxxStaticCastExpr(has(ImplicitCastExpr
));
53 const auto FunctionalCastExpr
= cxxFunctionalCastExpr(has(ImplicitCastExpr
));
55 return expr(anyOf(ImplicitCastExpr
, CStyleCastExpr
, StaticCastExpr
,
59 static StringRef
parseOpCode(BinaryOperator::Opcode Code
) {
66 return "cmp_less_equal";
68 return "cmp_greater_equal";
72 return "cmp_not_equal";
78 UseIntegerSignComparisonCheck::UseIntegerSignComparisonCheck(
79 StringRef Name
, ClangTidyContext
*Context
)
80 : ClangTidyCheck(Name
, Context
),
81 IncludeInserter(Options
.getLocalOrGlobal("IncludeStyle",
82 utils::IncludeSorter::IS_LLVM
),
83 areDiagsSelfContained()) {}
85 void UseIntegerSignComparisonCheck::storeOptions(
86 ClangTidyOptions::OptionMap
&Opts
) {
87 Options
.store(Opts
, "IncludeStyle", IncludeInserter
.getStyle());
90 void UseIntegerSignComparisonCheck::registerMatchers(MatchFinder
*Finder
) {
91 const auto SignedIntCastExpr
= intCastExpression(true, "sIntCastExpression");
92 const auto UnSignedIntCastExpr
= intCastExpression(false);
94 // Flag all operators "==", "<=", ">=", "<", ">", "!="
95 // that are used between signed/unsigned
96 const auto CompareOperator
=
97 binaryOperator(hasAnyOperatorName("==", "<=", ">=", "<", ">", "!="),
98 hasOperands(SignedIntCastExpr
, UnSignedIntCastExpr
),
99 unless(isInTemplateInstantiation()))
100 .bind("intComparison");
102 Finder
->addMatcher(CompareOperator
, this);
105 void UseIntegerSignComparisonCheck::registerPPCallbacks(
106 const SourceManager
&SM
, Preprocessor
*PP
, Preprocessor
*ModuleExpanderPP
) {
107 IncludeInserter
.registerPreprocessor(PP
);
110 void UseIntegerSignComparisonCheck::check(
111 const MatchFinder::MatchResult
&Result
) {
112 const auto *SignedCastExpression
=
113 Result
.Nodes
.getNodeAs
<ImplicitCastExpr
>("sIntCastExpression");
114 assert(SignedCastExpression
);
116 // Ignore the match if we know that the signed int value is not negative.
117 Expr::EvalResult EVResult
;
118 if (!SignedCastExpression
->isValueDependent() &&
119 SignedCastExpression
->getSubExpr()->EvaluateAsInt(EVResult
,
121 const llvm::APSInt SValue
= EVResult
.Val
.getInt();
122 if (SValue
.isNonNegative())
126 const auto *BinaryOp
=
127 Result
.Nodes
.getNodeAs
<BinaryOperator
>("intComparison");
128 if (BinaryOp
== nullptr)
131 const BinaryOperator::Opcode OpCode
= BinaryOp
->getOpcode();
133 const Expr
*LHS
= BinaryOp
->getLHS()->IgnoreImpCasts();
134 const Expr
*RHS
= BinaryOp
->getRHS()->IgnoreImpCasts();
135 if (LHS
== nullptr || RHS
== nullptr)
137 const Expr
*SubExprLHS
= nullptr;
138 const Expr
*SubExprRHS
= nullptr;
139 SourceRange R1
= SourceRange(LHS
->getBeginLoc());
140 SourceRange R2
= SourceRange(BinaryOp
->getOperatorLoc());
141 SourceRange R3
= SourceRange(Lexer::getLocForEndOfToken(
142 RHS
->getEndLoc(), 0, *Result
.SourceManager
, getLangOpts()));
143 if (const auto *LHSCast
= llvm::dyn_cast
<ExplicitCastExpr
>(LHS
)) {
144 SubExprLHS
= LHSCast
->getSubExpr();
145 R1
= SourceRange(LHS
->getBeginLoc(),
146 SubExprLHS
->getBeginLoc().getLocWithOffset(-1));
147 R2
.setBegin(Lexer::getLocForEndOfToken(
148 SubExprLHS
->getEndLoc(), 0, *Result
.SourceManager
, getLangOpts()));
150 if (const auto *RHSCast
= llvm::dyn_cast
<ExplicitCastExpr
>(RHS
)) {
151 SubExprRHS
= RHSCast
->getSubExpr();
152 R2
.setEnd(SubExprRHS
->getBeginLoc().getLocWithOffset(-1));
154 DiagnosticBuilder Diag
=
155 diag(BinaryOp
->getBeginLoc(),
156 "comparison between 'signed' and 'unsigned' integers");
157 const std::string CmpNamespace
= ("std::" + parseOpCode(OpCode
)).str();
158 const std::string CmpHeader
= "<utility>";
159 // Prefer modernize-use-integer-sign-comparison when C++20 is available!
160 Diag
<< FixItHint::CreateReplacement(
161 CharSourceRange(R1
, SubExprLHS
!= nullptr),
162 llvm::Twine(CmpNamespace
+ "(").str());
163 Diag
<< FixItHint::CreateReplacement(R2
, ",");
164 Diag
<< FixItHint::CreateReplacement(CharSourceRange::getCharRange(R3
), ")");
166 // If there is no include for cmp_{*} functions, we'll add it.
167 Diag
<< IncludeInserter
.createIncludeInsertion(
168 Result
.SourceManager
->getFileID(BinaryOp
->getBeginLoc()), CmpHeader
);
171 } // namespace clang::tidy::modernize