annotate clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp @ 150:1d019706d866

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