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