1 //===--- MisplacedWideningCastCheck.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 "MisplacedWideningCastCheck.h"
10 #include "../utils/Matchers.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 using namespace clang::ast_matchers
;
16 namespace clang::tidy::bugprone
{
18 MisplacedWideningCastCheck::MisplacedWideningCastCheck(
19 StringRef Name
, ClangTidyContext
*Context
)
20 : ClangTidyCheck(Name
, Context
),
21 CheckImplicitCasts(Options
.get("CheckImplicitCasts", false)) {}
23 void MisplacedWideningCastCheck::storeOptions(
24 ClangTidyOptions::OptionMap
&Opts
) {
25 Options
.store(Opts
, "CheckImplicitCasts", CheckImplicitCasts
);
28 void MisplacedWideningCastCheck::registerMatchers(MatchFinder
*Finder
) {
30 expr(anyOf(binaryOperator(hasAnyOperatorName("+", "-", "*", "<<")),
31 unaryOperator(hasOperatorName("~"))),
35 const auto ExplicitCast
= explicitCastExpr(hasDestinationType(isInteger()),
36 has(ignoringParenImpCasts(Calc
)));
37 const auto ImplicitCast
=
38 implicitCastExpr(hasImplicitDestinationType(isInteger()),
39 has(ignoringParenImpCasts(Calc
)));
41 traverse(TK_AsIs
, expr(anyOf(ExplicitCast
, ImplicitCast
)).bind("Cast"));
43 Finder
->addMatcher(varDecl(hasInitializer(Cast
)), this);
44 Finder
->addMatcher(returnStmt(hasReturnValue(Cast
)), this);
45 Finder
->addMatcher(callExpr(hasAnyArgument(Cast
)), this);
46 Finder
->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast
)), this);
48 binaryOperator(isComparisonOperator(), hasEitherOperand(Cast
)), this);
51 static unsigned getMaxCalculationWidth(const ASTContext
&Context
,
53 E
= E
->IgnoreParenImpCasts();
55 if (const auto *Bop
= dyn_cast
<BinaryOperator
>(E
)) {
56 unsigned LHSWidth
= getMaxCalculationWidth(Context
, Bop
->getLHS());
57 unsigned RHSWidth
= getMaxCalculationWidth(Context
, Bop
->getRHS());
58 if (Bop
->getOpcode() == BO_Mul
)
59 return LHSWidth
+ RHSWidth
;
60 if (Bop
->getOpcode() == BO_Add
)
61 return std::max(LHSWidth
, RHSWidth
) + 1;
62 if (Bop
->getOpcode() == BO_Rem
) {
63 Expr::EvalResult Result
;
64 if (Bop
->getRHS()->EvaluateAsInt(Result
, Context
))
65 return Result
.Val
.getInt().getActiveBits();
66 } else if (Bop
->getOpcode() == BO_Shl
) {
67 Expr::EvalResult Result
;
68 if (Bop
->getRHS()->EvaluateAsInt(Result
, Context
)) {
69 // We don't handle negative values and large values well. It is assumed
70 // that compiler warnings are written for such values so the user will
72 return LHSWidth
+ Result
.Val
.getInt().getExtValue();
75 // Unknown bitcount, assume there is truncation.
78 } else if (const auto *Uop
= dyn_cast
<UnaryOperator
>(E
)) {
79 // There is truncation when ~ is used.
80 if (Uop
->getOpcode() == UO_Not
)
83 QualType T
= Uop
->getType();
84 return T
->isIntegerType() ? Context
.getIntWidth(T
) : 1024U;
85 } else if (const auto *I
= dyn_cast
<IntegerLiteral
>(E
)) {
86 return I
->getValue().getActiveBits();
89 return Context
.getIntWidth(E
->getType());
92 static int relativeIntSizes(BuiltinType::Kind Kind
) {
94 case BuiltinType::UChar
:
96 case BuiltinType::SChar
:
98 case BuiltinType::Char_U
:
100 case BuiltinType::Char_S
:
102 case BuiltinType::UShort
:
104 case BuiltinType::Short
:
106 case BuiltinType::UInt
:
108 case BuiltinType::Int
:
110 case BuiltinType::ULong
:
112 case BuiltinType::Long
:
114 case BuiltinType::ULongLong
:
116 case BuiltinType::LongLong
:
118 case BuiltinType::UInt128
:
120 case BuiltinType::Int128
:
127 static int relativeCharSizes(BuiltinType::Kind Kind
) {
129 case BuiltinType::UChar
:
131 case BuiltinType::SChar
:
133 case BuiltinType::Char_U
:
135 case BuiltinType::Char_S
:
137 case BuiltinType::Char16
:
139 case BuiltinType::Char32
:
146 static int relativeCharSizesW(BuiltinType::Kind Kind
) {
148 case BuiltinType::UChar
:
150 case BuiltinType::SChar
:
152 case BuiltinType::Char_U
:
154 case BuiltinType::Char_S
:
156 case BuiltinType::WChar_U
:
158 case BuiltinType::WChar_S
:
165 static bool isFirstWider(BuiltinType::Kind First
, BuiltinType::Kind Second
) {
166 int FirstSize
= 0, SecondSize
= 0;
167 if ((FirstSize
= relativeIntSizes(First
)) != 0 &&
168 (SecondSize
= relativeIntSizes(Second
)) != 0)
169 return FirstSize
> SecondSize
;
170 if ((FirstSize
= relativeCharSizes(First
)) != 0 &&
171 (SecondSize
= relativeCharSizes(Second
)) != 0)
172 return FirstSize
> SecondSize
;
173 if ((FirstSize
= relativeCharSizesW(First
)) != 0 &&
174 (SecondSize
= relativeCharSizesW(Second
)) != 0)
175 return FirstSize
> SecondSize
;
179 void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult
&Result
) {
180 const auto *Cast
= Result
.Nodes
.getNodeAs
<CastExpr
>("Cast");
181 if (!CheckImplicitCasts
&& isa
<ImplicitCastExpr
>(Cast
))
183 if (Cast
->getBeginLoc().isMacroID())
186 const auto *Calc
= Result
.Nodes
.getNodeAs
<Expr
>("Calc");
187 if (Calc
->getBeginLoc().isMacroID())
190 if (Cast
->isTypeDependent() || Cast
->isValueDependent() ||
191 Calc
->isTypeDependent() || Calc
->isValueDependent())
194 ASTContext
&Context
= *Result
.Context
;
196 QualType CastType
= Cast
->getType();
197 QualType CalcType
= Calc
->getType();
199 // Explicit truncation using cast.
200 if (Context
.getIntWidth(CastType
) < Context
.getIntWidth(CalcType
))
203 // If CalcType and CastType have same size then there is no real danger, but
204 // there can be a portability problem.
206 if (Context
.getIntWidth(CastType
) == Context
.getIntWidth(CalcType
)) {
207 const auto *CastBuiltinType
=
208 dyn_cast
<BuiltinType
>(CastType
->getUnqualifiedDesugaredType());
209 const auto *CalcBuiltinType
=
210 dyn_cast
<BuiltinType
>(CalcType
->getUnqualifiedDesugaredType());
211 if (!CastBuiltinType
|| !CalcBuiltinType
)
213 if (!isFirstWider(CastBuiltinType
->getKind(), CalcBuiltinType
->getKind()))
217 // Don't write a warning if we can easily see that the result is not
219 if (Context
.getIntWidth(CalcType
) >= getMaxCalculationWidth(Context
, Calc
))
222 diag(Cast
->getBeginLoc(), "either cast from %0 to %1 is ineffective, or "
223 "there is loss of precision before the conversion")
224 << CalcType
<< CastType
;
227 } // namespace clang::tidy::bugprone