clang-tools  14.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(hasAnyOperatorName("+", "-", "*", "<<")),
33  unaryOperator(hasOperatorName("~"))),
34  hasType(isInteger()))
35  .bind("Calc");
36 
37  const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
38  has(ignoringParenImpCasts(Calc)));
39  const auto ImplicitCast =
40  implicitCastExpr(hasImplicitDestinationType(isInteger()),
41  has(ignoringParenImpCasts(Calc)));
42  const auto Cast =
43  traverse(TK_AsIs, expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast"));
44 
45  Finder->addMatcher(varDecl(hasInitializer(Cast)), this);
46  Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);
47  Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);
48  Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
49  Finder->addMatcher(
50  binaryOperator(isComparisonOperator(), hasEitherOperand(Cast)), this);
51 }
52 
53 static unsigned getMaxCalculationWidth(const ASTContext &Context,
54  const Expr *E) {
55  E = E->IgnoreParenImpCasts();
56 
57  if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {
58  unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());
59  unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());
60  if (Bop->getOpcode() == BO_Mul)
61  return LHSWidth + RHSWidth;
62  if (Bop->getOpcode() == BO_Add)
63  return std::max(LHSWidth, RHSWidth) + 1;
64  if (Bop->getOpcode() == BO_Rem) {
65  Expr::EvalResult Result;
66  if (Bop->getRHS()->EvaluateAsInt(Result, Context))
67  return Result.Val.getInt().getActiveBits();
68  } else if (Bop->getOpcode() == BO_Shl) {
69  Expr::EvalResult Result;
70  if (Bop->getRHS()->EvaluateAsInt(Result, Context)) {
71  // We don't handle negative values and large values well. It is assumed
72  // that compiler warnings are written for such values so the user will
73  // fix that.
74  return LHSWidth + Result.Val.getInt().getExtValue();
75  }
76 
77  // Unknown bitcount, assume there is truncation.
78  return 1024U;
79  }
80  } else if (const auto *Uop = dyn_cast<UnaryOperator>(E)) {
81  // There is truncation when ~ is used.
82  if (Uop->getOpcode() == UO_Not)
83  return 1024U;
84 
85  QualType T = Uop->getType();
86  return T->isIntegerType() ? Context.getIntWidth(T) : 1024U;
87  } else if (const auto *I = dyn_cast<IntegerLiteral>(E)) {
88  return I->getValue().getActiveBits();
89  }
90 
91  return Context.getIntWidth(E->getType());
92 }
93 
95  switch (Kind) {
96  case BuiltinType::UChar:
97  return 1;
98  case BuiltinType::SChar:
99  return 1;
100  case BuiltinType::Char_U:
101  return 1;
102  case BuiltinType::Char_S:
103  return 1;
104  case BuiltinType::UShort:
105  return 2;
106  case BuiltinType::Short:
107  return 2;
108  case BuiltinType::UInt:
109  return 3;
110  case BuiltinType::Int:
111  return 3;
112  case BuiltinType::ULong:
113  return 4;
114  case BuiltinType::Long:
115  return 4;
116  case BuiltinType::ULongLong:
117  return 5;
118  case BuiltinType::LongLong:
119  return 5;
120  case BuiltinType::UInt128:
121  return 6;
122  case BuiltinType::Int128:
123  return 6;
124  default:
125  return 0;
126  }
127 }
128 
130  switch (Kind) {
131  case BuiltinType::UChar:
132  return 1;
133  case BuiltinType::SChar:
134  return 1;
135  case BuiltinType::Char_U:
136  return 1;
137  case BuiltinType::Char_S:
138  return 1;
139  case BuiltinType::Char16:
140  return 2;
141  case BuiltinType::Char32:
142  return 3;
143  default:
144  return 0;
145  }
146 }
147 
149  switch (Kind) {
150  case BuiltinType::UChar:
151  return 1;
152  case BuiltinType::SChar:
153  return 1;
154  case BuiltinType::Char_U:
155  return 1;
156  case BuiltinType::Char_S:
157  return 1;
158  case BuiltinType::WChar_U:
159  return 2;
160  case BuiltinType::WChar_S:
161  return 2;
162  default:
163  return 0;
164  }
165 }
166 
168  int FirstSize, SecondSize;
169  if ((FirstSize = relativeIntSizes(First)) != 0 &&
170  (SecondSize = relativeIntSizes(Second)) != 0)
171  return FirstSize > SecondSize;
172  if ((FirstSize = relativeCharSizes(First)) != 0 &&
173  (SecondSize = relativeCharSizes(Second)) != 0)
174  return FirstSize > SecondSize;
175  if ((FirstSize = relativeCharSizesW(First)) != 0 &&
176  (SecondSize = relativeCharSizesW(Second)) != 0)
177  return FirstSize > SecondSize;
178  return false;
179 }
180 
181 void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
182  const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("Cast");
183  if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))
184  return;
185  if (Cast->getBeginLoc().isMacroID())
186  return;
187 
188  const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
189  if (Calc->getBeginLoc().isMacroID())
190  return;
191 
192  if (Cast->isTypeDependent() || Cast->isValueDependent() ||
193  Calc->isTypeDependent() || Calc->isValueDependent())
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  return;
215  if (!isFirstWider(CastBuiltinType->getKind(), CalcBuiltinType->getKind()))
216  return;
217  }
218 
219  // Don't write a warning if we can easily see that the result is not
220  // truncated.
221  if (Context.getIntWidth(CalcType) >= getMaxCalculationWidth(Context, Calc))
222  return;
223 
224  diag(Cast->getBeginLoc(), "either cast from %0 to %1 is ineffective, or "
225  "there is loss of precision before the conversion")
226  << CalcType << CastType;
227 }
228 
229 } // namespace bugprone
230 } // namespace tidy
231 } // namespace clang
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:59
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:54
MisplacedWideningCastCheck.h
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::bugprone::getMaxCalculationWidth
static unsigned getMaxCalculationWidth(const ASTContext &Context, const Expr *E)
Definition: MisplacedWideningCastCheck.cpp:53
clang::tidy::bugprone::relativeCharSizes
static int relativeCharSizes(BuiltinType::Kind Kind)
Definition: MisplacedWideningCastCheck.cpp:129
clang::tidy::bugprone::MisplacedWideningCastCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: MisplacedWideningCastCheck.cpp:181
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:416
clang::tidy::bugprone::isFirstWider
static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second)
Definition: MisplacedWideningCastCheck.cpp:167
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:71
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
clang::tidy::bugprone::MisplacedWideningCastCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: MisplacedWideningCastCheck.cpp:30
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:25
clang::tidy::bugprone::MisplacedWideningCastCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: MisplacedWideningCastCheck.cpp:25
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::ClangTidyCheck::OptionsView::store
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: ClangTidyCheck.cpp:120
clang::tidy::bugprone::relativeCharSizesW
static int relativeCharSizesW(BuiltinType::Kind Kind)
Definition: MisplacedWideningCastCheck.cpp:148
clang::tidy::bugprone::relativeIntSizes
static int relativeIntSizes(BuiltinType::Kind Kind)
Definition: MisplacedWideningCastCheck.cpp:94