blob: e263366b664b4be8e8a8c22b5e750bd30de36512 [file] [log] [blame]
Daniel Marjamakiad329372016-02-09 14:08:49 +00001//===--- 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"
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +000011#include "../utils/Matchers.h"
Daniel Marjamakiad329372016-02-09 14:08:49 +000012#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14
15using namespace clang::ast_matchers;
16
17namespace clang {
18namespace tidy {
Alexander Kornienkoa1a29332018-02-28 14:47:20 +000019namespace bugprone {
Daniel Marjamakiad329372016-02-09 14:08:49 +000020
Gabor Horvath5273f612016-04-06 12:04:51 +000021MisplacedWideningCastCheck::MisplacedWideningCastCheck(
22 StringRef Name, ClangTidyContext *Context)
23 : ClangTidyCheck(Name, Context),
Gabor Horvath1a925062017-04-19 14:55:58 +000024 CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}
Gabor Horvath5273f612016-04-06 12:04:51 +000025
26void MisplacedWideningCastCheck::storeOptions(
27 ClangTidyOptions::OptionMap &Opts) {
28 Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);
29}
30
Daniel Marjamakiad329372016-02-09 14:08:49 +000031void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
Etienne Bergeron53f7c0e2016-04-07 14:52:52 +000032 const auto Calc =
33 expr(anyOf(binaryOperator(
34 anyOf(hasOperatorName("+"), hasOperatorName("-"),
35 hasOperatorName("*"), hasOperatorName("<<"))),
36 unaryOperator(hasOperatorName("~"))),
37 hasType(isInteger()))
38 .bind("Calc");
Daniel Marjamakiad329372016-02-09 14:08:49 +000039
Piotr Padlewskie93a73f2016-05-31 15:26:56 +000040 const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
41 has(ignoringParenImpCasts(Calc)));
Etienne Bergeron53f7c0e2016-04-07 14:52:52 +000042 const auto ImplicitCast =
Piotr Padlewskie93a73f2016-05-31 15:26:56 +000043 implicitCastExpr(hasImplicitDestinationType(isInteger()),
44 has(ignoringParenImpCasts(Calc)));
Etienne Bergeron53f7c0e2016-04-07 14:52:52 +000045 const auto Cast = expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast");
Daniel Marjamakiad329372016-02-09 14:08:49 +000046
Gabor Horvath5273f612016-04-06 12:04:51 +000047 Finder->addMatcher(varDecl(hasInitializer(Cast)), this);
48 Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);
49 Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);
Daniel Marjamakiad329372016-02-09 14:08:49 +000050 Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
Gabor Horvath5273f612016-04-06 12:04:51 +000051 Finder->addMatcher(
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +000052 binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(Cast)),
Gabor Horvath5273f612016-04-06 12:04:51 +000053 this);
Daniel Marjamakiad329372016-02-09 14:08:49 +000054}
55
Etienne Bergeron53f7c0e2016-04-07 14:52:52 +000056static unsigned getMaxCalculationWidth(const ASTContext &Context,
57 const Expr *E) {
Daniel Marjamakiad329372016-02-09 14:08:49 +000058 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
Etienne Bergeron53f7c0e2016-04-07 14:52:52 +000097static 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 }
Gabor Horvath5273f612016-04-06 12:04:51 +0000130}
131
Etienne Bergeron53f7c0e2016-04-07 14:52:52 +0000132static 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 }
Gabor Horvath5273f612016-04-06 12:04:51 +0000149}
150
Etienne Bergeron53f7c0e2016-04-07 14:52:52 +0000151static 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 }
Gabor Horvath5273f612016-04-06 12:04:51 +0000168}
169
170static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second) {
Gabor Horvath5273f612016-04-06 12:04:51 +0000171 int FirstSize, SecondSize;
Etienne Bergeron53f7c0e2016-04-07 14:52:52 +0000172 if ((FirstSize = relativeIntSizes(First)) != 0 &&
173 (SecondSize = relativeIntSizes(Second)) != 0)
Gabor Horvath5273f612016-04-06 12:04:51 +0000174 return FirstSize > SecondSize;
Etienne Bergeron53f7c0e2016-04-07 14:52:52 +0000175 if ((FirstSize = relativeCharSizes(First)) != 0 &&
176 (SecondSize = relativeCharSizes(Second)) != 0)
Gabor Horvath5273f612016-04-06 12:04:51 +0000177 return FirstSize > SecondSize;
Etienne Bergeron53f7c0e2016-04-07 14:52:52 +0000178 if ((FirstSize = relativeCharSizesW(First)) != 0 &&
179 (SecondSize = relativeCharSizesW(Second)) != 0)
Gabor Horvath5273f612016-04-06 12:04:51 +0000180 return FirstSize > SecondSize;
181 return false;
182}
183
Daniel Marjamakiad329372016-02-09 14:08:49 +0000184void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
Gabor Horvath5273f612016-04-06 12:04:51 +0000185 const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("Cast");
186 if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))
187 return;
Daniel Marjamakiad329372016-02-09 14:08:49 +0000188 if (Cast->getLocStart().isMacroID())
189 return;
190
191 const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
192 if (Calc->getLocStart().isMacroID())
193 return;
194
Daniel Marjamakicad84432017-08-29 06:25:24 +0000195 if (Cast->isTypeDependent() || Cast->isValueDependent() ||
196 Calc->isTypeDependent() || Calc->isValueDependent())
197 return;
198
Daniel Marjamakiad329372016-02-09 14:08:49 +0000199 ASTContext &Context = *Result.Context;
200
201 QualType CastType = Cast->getType();
202 QualType CalcType = Calc->getType();
203
Daniel Marjamaki79355772016-02-12 07:51:10 +0000204 // Explicit truncation using cast.
205 if (Context.getIntWidth(CastType) < Context.getIntWidth(CalcType))
Daniel Marjamakiad329372016-02-09 14:08:49 +0000206 return;
207
Daniel Marjamaki79355772016-02-12 07:51:10 +0000208 // If CalcType and CastType have same size then there is no real danger, but
209 // there can be a portability problem.
Gabor Horvath5273f612016-04-06 12:04:51 +0000210
Daniel Marjamaki79355772016-02-12 07:51:10 +0000211 if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {
Gabor Horvath5273f612016-04-06 12:04:51 +0000212 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()))
Daniel Marjamaki79355772016-02-12 07:51:10 +0000218 return;
Daniel Marjamaki79355772016-02-12 07:51:10 +0000219 }
220
221 // Don't write a warning if we can easily see that the result is not
222 // truncated.
Daniel Marjamakiad329372016-02-09 14:08:49 +0000223 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
Alexander Kornienkoa1a29332018-02-28 14:47:20 +0000231} // namespace bugprone
Daniel Marjamakiad329372016-02-09 14:08:49 +0000232} // namespace tidy
233} // namespace clang