clang-tools 10.0.0

MisplacedWideningCastCheck.cpp
Go to the documentation of this file.
1 //===--- MisplacedWideningCastCheck.cpp - clang-tidy-----------------------===//
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
9 #include "MisplacedWideningCastCheck.h"
10 #include "../utils/Matchers.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13
14 using namespace clang::ast_matchers;
15
16 namespace clang {
17 namespace tidy {
18 namespace bugprone {
19
20 MisplacedWideningCastCheck::MisplacedWideningCastCheck(
21 StringRef Name, ClangTidyContext *Context)
22 : ClangTidyCheck(Name, Context),
23 CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}
24
25 void MisplacedWideningCastCheck::storeOptions(
26 ClangTidyOptions::OptionMap &Opts) {
27 Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);
28 }
29
30 void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
31 const auto Calc =
32 expr(anyOf(binaryOperator(
33 anyOf(hasOperatorName("+"), hasOperatorName("-"),
34 hasOperatorName("*"), hasOperatorName("<<"))),
35 unaryOperator(hasOperatorName("~"))),
36 hasType(isInteger()))
37 .bind("Calc");
38
39 const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
40 has(ignoringParenImpCasts(Calc)));
41 const auto ImplicitCast =
42 implicitCastExpr(hasImplicitDestinationType(isInteger()),
43 has(ignoringParenImpCasts(Calc)));
44 const auto Cast = expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast");
45
46 Finder->addMatcher(varDecl(hasInitializer(Cast)), this);
47 Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);
48 Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);
49 Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
50 Finder->addMatcher(
51 binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(Cast)),
52 this);
53 }
54
55 static unsigned getMaxCalculationWidth(const ASTContext &Context,
56 const Expr *E) {
57 E = E->IgnoreParenImpCasts();
58
59 if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {
60 unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());
61 unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());
62 if (Bop->getOpcode() == BO_Mul)
63 return LHSWidth + RHSWidth;
64 if (Bop->getOpcode() == BO_Add)
65 return std::max(LHSWidth, RHSWidth) + 1;
66 if (Bop->getOpcode() == BO_Rem) {
67 Expr::EvalResult Result;
68 if (Bop->getRHS()->EvaluateAsInt(Result, Context))
69 return Result.Val.getInt().getActiveBits();
70 } else if (Bop->getOpcode() == BO_Shl) {
71 Expr::EvalResult Result;
72 if (Bop->getRHS()->EvaluateAsInt(Result, Context)) {
73 // We don't handle negative values and large values well. It is assumed
74 // that compiler warnings are written for such values so the user will
75 // fix that.
76 return LHSWidth + Result.Val.getInt().getExtValue();
77 }
78
79 // Unknown bitcount, assume there is truncation.
80 return 1024U;
81 }
82 } else if (const auto *Uop = dyn_cast<UnaryOperator>(E)) {
83 // There is truncation when ~ is used.
84 if (Uop->getOpcode() == UO_Not)
85 return 1024U;
86
87 QualType T = Uop->getType();
88 return T->isIntegerType() ? Context.getIntWidth(T) : 1024U;
89 } else if (const auto *I = dyn_cast<IntegerLiteral>(E)) {
90 return I->getValue().getActiveBits();
91 }
92
93 return Context.getIntWidth(E->getType());
94 }
95
96 static int relativeIntSizes(BuiltinType::Kind Kind) {
97 switch (Kind) {
98 case BuiltinType::UChar:
99 return 1;
100 case BuiltinType::SChar:
101 return 1;
102 case BuiltinType::Char_U:
103 return 1;
104 case BuiltinType::Char_S:
105 return 1;
106 case BuiltinType::UShort:
107 return 2;
108 case BuiltinType::Short:
109 return 2;
110 case BuiltinType::UInt:
111 return 3;
112 case BuiltinType::Int:
113 return 3;
114 case BuiltinType::ULong:
115 return 4;
116 case BuiltinType::Long:
117 return 4;
118 case BuiltinType::ULongLong:
119 return 5;
120 case BuiltinType::LongLong:
121 return 5;
122 case BuiltinType::UInt128:
123 return 6;
124 case BuiltinType::Int128:
125 return 6;
126 default:
127 return 0;
128 }
129 }
130
131 static int relativeCharSizes(BuiltinType::Kind Kind) {
132 switch (Kind) {
133 case BuiltinType::UChar:
134 return 1;
135 case BuiltinType::SChar:
136 return 1;
137 case BuiltinType::Char_U:
138 return 1;
139 case BuiltinType::Char_S:
140 return 1;
141 case BuiltinType::Char16:
142 return 2;
143 case BuiltinType::Char32:
144 return 3;
145 default:
146 return 0;
147 }
148 }
149
150 static int relativeCharSizesW(BuiltinType::Kind Kind) {
151 switch (Kind) {
152 case BuiltinType::UChar:
153 return 1;
154 case BuiltinType::SChar:
155 return 1;
156 case BuiltinType::Char_U:
157 return 1;
158 case BuiltinType::Char_S:
159 return 1;
160 case BuiltinType::WChar_U:
161 return 2;
162 case BuiltinType::WChar_S:
163 return 2;
164 default:
165 return 0;
166 }
167 }
168
169 static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second) {
170 int FirstSize, SecondSize;
171 if ((FirstSize = relativeIntSizes(First)) != 0 &&
172 (SecondSize = relativeIntSizes(Second)) != 0)
173 return FirstSize > SecondSize;
174 if ((FirstSize = relativeCharSizes(First)) != 0 &&
175 (SecondSize = relativeCharSizes(Second)) != 0)
176 return FirstSize > SecondSize;
177 if ((FirstSize = relativeCharSizesW(First)) != 0 &&
178 (SecondSize = relativeCharSizesW(Second)) != 0)
179 return FirstSize > SecondSize;
180 return false;
181 }
182
183 void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
184 const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("Cast");
185 if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))
186 return;
187 if (Cast->getBeginLoc().isMacroID())
188 return;
189
190 const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
191 if (Calc->getBeginLoc().isMacroID())
192 return;
193
194 if (Cast->isTypeDependent() || Cast->isValueDependent() ||
195 Calc->isTypeDependent() || Calc->isValueDependent())
196 return;
197
198 ASTContext &Context = *Result.Context;
199
200 QualType CastType = Cast->getType();
201 QualType CalcType = Calc->getType();
202
203 // Explicit truncation using cast.
204 if (Context.getIntWidth(CastType) < Context.getIntWidth(CalcType))
205 return;
206
207 // If CalcType and CastType have same size then there is no real danger, but
208 // there can be a portability problem.
209
210 if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {
211 const auto *CastBuiltinType =
212 dyn_cast<BuiltinType>(CastType->getUnqualifiedDesugaredType());
213 const auto *CalcBuiltinType =
214 dyn_cast<BuiltinType>(CalcType->getUnqualifiedDesugaredType());
215 if (!CastBuiltinType || !CalcBuiltinType)
216 return;
217 if (!isFirstWider(CastBuiltinType->getKind(), CalcBuiltinType->getKind()))
218 return;
219 }
220
221 // Don't write a warning if we can easily see that the result is not
222 // truncated.
223 if (Context.getIntWidth(CalcType) >= getMaxCalculationWidth(Context, Calc))
224 return;
225
226 diag(Cast->getBeginLoc(), "either cast from %0 to %1 is ineffective, or "
227 "there is loss of precision before the conversion")
228 << CalcType << CastType;
229 }
230
231 } // namespace bugprone
232 } // namespace tidy
233 } // namespace clang
clang::tidy::bugprone::relativeIntSizes
static int relativeIntSizes(BuiltinType::Kind Kind)
Definition: MisplacedWideningCastCheck.cpp:96
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::bugprone::relativeCharSizesW
static int relativeCharSizesW(BuiltinType::Kind Kind)
Definition: MisplacedWideningCastCheck.cpp:150
clang::tidy::bugprone::getMaxCalculationWidth
static unsigned getMaxCalculationWidth(const ASTContext &Context, const Expr *E)
Definition: MisplacedWideningCastCheck.cpp:55
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:47
clang::tidy::bugprone::MisplacedWideningCastCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: MisplacedWideningCastCheck.cpp:25
clang::tidy::bugprone::relativeCharSizes
static int relativeCharSizes(BuiltinType::Kind Kind)
Definition: MisplacedWideningCastCheck.cpp:131
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:184
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:57
clang::tidy::bugprone::isFirstWider
static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second)
Definition: MisplacedWideningCastCheck.cpp:169
clang::tidy::bugprone::MisplacedWideningCastCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: MisplacedWideningCastCheck.cpp:30
MisplacedWideningCastCheck.h
clang::tidy::ClangTidyCheck::OptionsView::store
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.
Definition: ClangTidyCheck.cpp:58
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:97
clang::tidy::bugprone::MisplacedWideningCastCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: MisplacedWideningCastCheck.cpp:183
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:77
E
const Expr * E
Definition: AvoidBindCheck.cpp:82
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidyCheck.cpp:21