150
|
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
|