clang-tools  10.0.0git
MisplacedWideningCastCheck.cpp
Go to the documentation of this file.
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 
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 
27  Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);
28 }
29 
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 
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 
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 
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 
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
static int relativeIntSizes(BuiltinType::Kind Kind)
static int relativeCharSizesW(BuiltinType::Kind Kind)
static unsigned getMaxCalculationWidth(const ASTContext &Context, const Expr *E)
Base class for all clang-tidy checks.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
static int relativeCharSizes(BuiltinType::Kind Kind)
BindArgumentKind Kind
static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
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.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
const Expr * E
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.