11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "../utils/Matchers.h"
15 using namespace clang::ast_matchers;
21 MisplacedWideningCastCheck::MisplacedWideningCastCheck(
24 CheckImplicitCasts(Options.get(
"CheckImplicitCasts", true)) {}
28 Options.
store(Opts,
"CheckImplicitCasts", CheckImplicitCasts);
33 expr(anyOf(binaryOperator(
34 anyOf(hasOperatorName(
"+"), hasOperatorName(
"-"),
35 hasOperatorName(
"*"), hasOperatorName(
"<<"))),
36 unaryOperator(hasOperatorName(
"~"))),
40 const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
41 has(ignoringParenImpCasts(Calc)));
42 const auto ImplicitCast =
43 implicitCastExpr(hasImplicitDestinationType(isInteger()),
44 has(ignoringParenImpCasts(Calc)));
45 const auto Cast = expr(anyOf(ExplicitCast, ImplicitCast)).bind(
"Cast");
47 Finder->addMatcher(varDecl(hasInitializer(Cast)),
this);
48 Finder->addMatcher(returnStmt(hasReturnValue(Cast)),
this);
49 Finder->addMatcher(callExpr(hasAnyArgument(Cast)),
this);
50 Finder->addMatcher(binaryOperator(hasOperatorName(
"="), hasRHS(Cast)),
this);
52 binaryOperator(matchers::isComparisonOperator(),
53 hasEitherOperand(Cast)),
59 E = E->IgnoreParenImpCasts();
61 if (
const auto *Bop = dyn_cast<BinaryOperator>(E)) {
64 if (Bop->getOpcode() == BO_Mul)
65 return LHSWidth + RHSWidth;
66 if (Bop->getOpcode() == BO_Add)
67 return std::max(LHSWidth, RHSWidth) + 1;
68 if (Bop->getOpcode() == BO_Rem) {
70 if (Bop->getRHS()->EvaluateAsInt(Val, Context))
71 return Val.getActiveBits();
72 }
else if (Bop->getOpcode() == BO_Shl) {
74 if (Bop->getRHS()->EvaluateAsInt(Bits, Context)) {
78 return LHSWidth + Bits.getExtValue();
84 }
else if (
const auto *Uop = dyn_cast<UnaryOperator>(E)) {
86 if (Uop->getOpcode() == UO_Not)
89 QualType T = Uop->getType();
90 return T->isIntegerType() ? Context.getIntWidth(T) : 1024U;
91 }
else if (
const auto *I = dyn_cast<IntegerLiteral>(E)) {
92 return I->getValue().getActiveBits();
95 return Context.getIntWidth(E->getType());
100 case BuiltinType::UChar:
102 case BuiltinType::SChar:
104 case BuiltinType::Char_U:
106 case BuiltinType::Char_S:
108 case BuiltinType::UShort:
110 case BuiltinType::Short:
112 case BuiltinType::UInt:
114 case BuiltinType::Int:
116 case BuiltinType::ULong:
118 case BuiltinType::Long:
120 case BuiltinType::ULongLong:
122 case BuiltinType::LongLong:
124 case BuiltinType::UInt128:
126 case BuiltinType::Int128:
135 case BuiltinType::UChar:
137 case BuiltinType::SChar:
139 case BuiltinType::Char_U:
141 case BuiltinType::Char_S:
143 case BuiltinType::Char16:
145 case BuiltinType::Char32:
154 case BuiltinType::UChar:
156 case BuiltinType::SChar:
158 case BuiltinType::Char_U:
160 case BuiltinType::Char_S:
162 case BuiltinType::WChar_U:
164 case BuiltinType::WChar_S:
172 int FirstSize, SecondSize;
175 return FirstSize > SecondSize;
178 return FirstSize > SecondSize;
181 return FirstSize > SecondSize;
186 const auto *Cast = Result.Nodes.getNodeAs<CastExpr>(
"Cast");
187 if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))
189 if (Cast->getLocStart().isMacroID())
192 const auto *Calc = Result.Nodes.getNodeAs<Expr>(
"Calc");
193 if (Calc->getLocStart().isMacroID())
196 ASTContext &
Context = *Result.Context;
198 QualType CastType = Cast->getType();
199 QualType CalcType = Calc->getType();
202 if (Context.getIntWidth(CastType) < Context.getIntWidth(CalcType))
208 if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {
209 const auto *CastBuiltinType =
210 dyn_cast<BuiltinType>(CastType->getUnqualifiedDesugaredType());
211 const auto *CalcBuiltinType =
212 dyn_cast<BuiltinType>(CalcType->getUnqualifiedDesugaredType());
213 if (CastBuiltinType && CalcBuiltinType &&
214 !
isFirstWider(CastBuiltinType->getKind(), CalcBuiltinType->getKind()))
223 diag(Cast->getLocStart(),
"either cast from %0 to %1 is ineffective, or "
224 "there is loss of precision before the conversion")
225 << CalcType << CastType;
std::unique_ptr< ast_matchers::MatchFinder > Finder
static unsigned getMaxCalculationWidth(const ASTContext &Context, const Expr *E)
static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second)
Base class for all clang-tidy checks.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static int relativeIntSizes(BuiltinType::Kind Kind)
static int relativeCharSizes(BuiltinType::Kind Kind)
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
std::map< std::string, std::string > OptionMap
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static int relativeCharSizesW(BuiltinType::Kind Kind)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.