clang-tools 6.0.0

MisplacedWideningCastCheck.cpp
Go to the documentation of this file.
1 //===--- MisplacedWideningCastCheck.cpp - clang-tidy-----------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "MisplacedWideningCastCheck.h"
11 #include "../utils/Matchers.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14
15 using namespace clang::ast_matchers;
16
17 namespace clang {
18 namespace tidy {
19 namespace misc {
20
21 MisplacedWideningCastCheck::MisplacedWideningCastCheck(
22 StringRef Name, ClangTidyContext *Context)
23 : ClangTidyCheck(Name, Context),
24 CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}
25
26 void MisplacedWideningCastCheck::storeOptions(
27 ClangTidyOptions::OptionMap &Opts) {
28 Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);
29 }
30
31 void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
32 const auto Calc =
33 expr(anyOf(binaryOperator(
34 anyOf(hasOperatorName("+"), hasOperatorName("-"),
35 hasOperatorName("*"), hasOperatorName("<<"))),
36 unaryOperator(hasOperatorName("~"))),
37 hasType(isInteger()))
38 .bind("Calc");
39
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");
46
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);
51 Finder->addMatcher(
52 binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(Cast)),
53 this);
54 }
55
56 static unsigned getMaxCalculationWidth(const ASTContext &Context,
57 const Expr *E) {
58 E = E->IgnoreParenImpCasts();
59
60 if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {
61 unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());
62 unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());
63 if (Bop->getOpcode() == BO_Mul)
64 return LHSWidth + RHSWidth;
65 if (Bop->getOpcode() == BO_Add)
66 return std::max(LHSWidth, RHSWidth) + 1;
67 if (Bop->getOpcode() == BO_Rem) {
68 llvm::APSInt Val;
69 if (Bop->getRHS()->EvaluateAsInt(Val, Context))
70 return Val.getActiveBits();
71 } else if (Bop->getOpcode() == BO_Shl) {
72 llvm::APSInt Bits;
73 if (Bop->getRHS()->EvaluateAsInt(Bits, Context)) {
74 // We don't handle negative values and large values well. It is assumed
75 // that compiler warnings are written for such values so the user will
76 // fix that.
77 return LHSWidth + Bits.getExtValue();
78 }
79
80 // Unknown bitcount, assume there is truncation.
81 return 1024U;
82 }
83 } else if (const auto *Uop = dyn_cast<UnaryOperator>(E)) {
84 // There is truncation when ~ is used.
85 if (Uop->getOpcode() == UO_Not)
86 return 1024U;
87
88 QualType T = Uop->getType();
89 return T->isIntegerType() ? Context.getIntWidth(T) : 1024U;
90 } else if (const auto *I = dyn_cast<IntegerLiteral>(E)) {
91 return I->getValue().getActiveBits();
92 }
93
94 return Context.getIntWidth(E->getType());
95 }
96
97 static int relativeIntSizes(BuiltinType::Kind Kind) {
98 switch (Kind) {
99 case BuiltinType::UChar:
100 return 1;
101 case BuiltinType::SChar:
102 return 1;
103 case BuiltinType::Char_U:
104 return 1;
105 case BuiltinType::Char_S:
106 return 1;
107 case BuiltinType::UShort:
108 return 2;
109 case BuiltinType::Short:
110 return 2;
111 case BuiltinType::UInt:
112 return 3;
113 case BuiltinType::Int:
114 return 3;
115 case BuiltinType::ULong:
116 return 4;
117 case BuiltinType::Long:
118 return 4;
119 case BuiltinType::ULongLong:
120 return 5;
121 case BuiltinType::LongLong:
122 return 5;
123 case BuiltinType::UInt128:
124 return 6;
125 case BuiltinType::Int128:
126 return 6;
127 default:
128 return 0;
129 }
130 }
131
132 static int relativeCharSizes(BuiltinType::Kind Kind) {
133 switch (Kind) {
134 case BuiltinType::UChar:
135 return 1;
136 case BuiltinType::SChar:
137 return 1;
138 case BuiltinType::Char_U:
139 return 1;
140 case BuiltinType::Char_S:
141 return 1;
142 case BuiltinType::Char16:
143 return 2;
144 case BuiltinType::Char32:
145 return 3;
146 default:
147 return 0;
148 }
149 }
150
151 static int relativeCharSizesW(BuiltinType::Kind Kind) {
152 switch (Kind) {
153 case BuiltinType::UChar:
154 return 1;
155 case BuiltinType::SChar:
156 return 1;
157 case BuiltinType::Char_U:
158 return 1;
159 case BuiltinType::Char_S:
160 return 1;
161 case BuiltinType::WChar_U:
162 return 2;
163 case BuiltinType::WChar_S:
164 return 2;
165 default:
166 return 0;
167 }
168 }
169
170 static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second) {
171 int FirstSize, SecondSize;
172 if ((FirstSize = relativeIntSizes(First)) != 0 &&
173 (SecondSize = relativeIntSizes(Second)) != 0)
174 return FirstSize > SecondSize;
175 if ((FirstSize = relativeCharSizes(First)) != 0 &&
176 (SecondSize = relativeCharSizes(Second)) != 0)
177 return FirstSize > SecondSize;
178 if ((FirstSize = relativeCharSizesW(First)) != 0 &&
179 (SecondSize = relativeCharSizesW(Second)) != 0)
180 return FirstSize > SecondSize;
181 return false;
182 }
183
184 void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
185 const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("Cast");
186 if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))
187 return;
188 if (Cast->getLocStart().isMacroID())
189 return;
190
191 const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
192 if (Calc->getLocStart().isMacroID())
193 return;
194
195 if (Cast->isTypeDependent() || Cast->isValueDependent() ||
196 Calc->isTypeDependent() || Calc->isValueDependent())
197 return;
198
199 ASTContext &Context = *Result.Context;
200
201 QualType CastType = Cast->getType();
202 QualType CalcType = Calc->getType();
203
204 // Explicit truncation using cast.
205 if (Context.getIntWidth(CastType) < Context.getIntWidth(CalcType))
206 return;
207
208 // If CalcType and CastType have same size then there is no real danger, but
209 // there can be a portability problem.
210
211 if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {
212 const auto *CastBuiltinType =
213 dyn_cast<BuiltinType>(CastType->getUnqualifiedDesugaredType());
214 const auto *CalcBuiltinType =
215 dyn_cast<BuiltinType>(CalcType->getUnqualifiedDesugaredType());
216 if (CastBuiltinType && CalcBuiltinType &&
217 !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->getLocStart(), "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 misc
232 } // namespace tidy
233 } // namespace clang
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::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: ClangTidy.cpp:449
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
clang::tidy::misc::getMaxCalculationWidth
static unsigned getMaxCalculationWidth(const ASTContext &Context, const Expr *E)
Definition: MisplacedWideningCastCheck.cpp:56
clang::tidy::misc::isFirstWider
static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second)
Definition: MisplacedWideningCastCheck.cpp:170
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
clang::tidy::misc::MisplacedWideningCastCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: MisplacedWideningCastCheck.cpp:31
clang::tidy::misc::relativeIntSizes
static int relativeIntSizes(BuiltinType::Kind Kind)
Definition: MisplacedWideningCastCheck.cpp:97
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidy.h:183
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:41
clang::tidy::misc::relativeCharSizes
static int relativeCharSizes(BuiltinType::Kind Kind)
Definition: MisplacedWideningCastCheck.cpp:132
MisplacedWideningCastCheck.h
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:99
clang
Definition: AndroidTidyModule.cpp:28
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:104
clang::tidy::misc::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:184
clang::tidy::misc::relativeCharSizesW
static int relativeCharSizesW(BuiltinType::Kind Kind)
Definition: MisplacedWideningCastCheck.cpp:151
clang::tidy::misc::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:26
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: ClangTidy.cpp:416