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