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