clang-tools  14.0.0git
ImplicitWideningOfMultiplicationResultCheck.cpp
Go to the documentation of this file.
1 //===--- ImplicitWideningOfMultiplicationResultCheck.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 "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 
13 using namespace clang::ast_matchers;
14 
15 namespace clang {
16 namespace {
17 AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {
18  return Node.isPartOfExplicitCast();
19 }
20 } // namespace
21 } // namespace clang
22 
23 namespace clang {
24 namespace tidy {
25 namespace bugprone {
26 
27 static const Expr *getLHSOfMulBinOp(const Expr *E) {
28  assert(E == E->IgnoreParens() && "Already skipped all parens!");
29  // Is this: long r = int(x) * int(y); ?
30  // FIXME: shall we skip brackets/casts/etc?
31  const auto *BO = dyn_cast<BinaryOperator>(E);
32  if (!BO || BO->getOpcode() != BO_Mul)
33  // FIXME: what about: long r = int(x) + (int(y) * int(z)); ?
34  return nullptr;
35  return BO->getLHS()->IgnoreParens();
36 }
37 
38 ImplicitWideningOfMultiplicationResultCheck::
39  ImplicitWideningOfMultiplicationResultCheck(StringRef Name,
40  ClangTidyContext *Context)
41  : ClangTidyCheck(Name, Context),
42  UseCXXStaticCastsInCppSources(
43  Options.get("UseCXXStaticCastsInCppSources", true)),
44  UseCXXHeadersInCppSources(Options.get("UseCXXHeadersInCppSources", true)),
45  IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
46  utils::IncludeSorter::IS_LLVM)) {
47 }
48 
50  const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
51  IncludeInserter.registerPreprocessor(PP);
52 }
53 
56  Options.store(Opts, "UseCXXStaticCastsInCppSources",
57  UseCXXStaticCastsInCppSources);
58  Options.store(Opts, "UseCXXHeadersInCppSources", UseCXXHeadersInCppSources);
59  Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
60 }
61 
62 llvm::Optional<FixItHint>
63 ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(
64  SourceLocation File) {
65  return IncludeInserter.createIncludeInsertion(
66  Result->SourceManager->getFileID(File),
67  ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>");
68 }
69 
70 void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
71  const ImplicitCastExpr *ICE) {
72  ASTContext *Context = Result->Context;
73 
74  const Expr *E = ICE->getSubExpr()->IgnoreParens();
75  QualType Ty = ICE->getType();
76  QualType ETy = E->getType();
77 
78  assert(!ETy->isDependentType() && !Ty->isDependentType() &&
79  "Don't expect to ever get here in template Context.");
80 
81  // This must be a widening cast. Else we do not care.
82  unsigned SrcWidth = Context->getIntWidth(ETy);
83  unsigned TgtWidth = Context->getIntWidth(Ty);
84  if (TgtWidth <= SrcWidth)
85  return;
86 
87  // Does the index expression look like it might be unintentionally computed
88  // in a narrower-than-wanted type?
89  const Expr *LHS = getLHSOfMulBinOp(E);
90  if (!LHS)
91  return;
92 
93  // Ok, looks like we should diagnose this.
94  diag(E->getBeginLoc(), "performing an implicit widening conversion to type "
95  "%0 of a multiplication performed in type %1")
96  << Ty << E->getType();
97 
98  {
99  auto Diag = diag(E->getBeginLoc(),
100  "make conversion explicit to silence this warning",
101  DiagnosticIDs::Note)
102  << E->getSourceRange();
103 
104  if (ShouldUseCXXStaticCast)
105  Diag << FixItHint::CreateInsertion(
106  E->getBeginLoc(), "static_cast<" + Ty.getAsString() + ">(")
107  << FixItHint::CreateInsertion(E->getEndLoc(), ")");
108  else
109  Diag << FixItHint::CreateInsertion(E->getBeginLoc(),
110  "(" + Ty.getAsString() + ")(")
111  << FixItHint::CreateInsertion(E->getEndLoc(), ")");
112  Diag << includeStddefHeader(E->getBeginLoc());
113  }
114 
115  QualType WideExprTy;
116  // Get Ty of the same signedness as ExprTy, because we only want to suggest
117  // to widen the computation, but not change it's signedness domain.
118  if (Ty->isSignedIntegerType() == ETy->isSignedIntegerType())
119  WideExprTy = Ty;
120  else if (Ty->isSignedIntegerType()) {
121  assert(ETy->isUnsignedIntegerType() &&
122  "Expected source type to be signed.");
123  WideExprTy = Context->getCorrespondingUnsignedType(Ty);
124  } else {
125  assert(Ty->isUnsignedIntegerType() &&
126  "Expected target type to be unsigned.");
127  assert(ETy->isSignedIntegerType() &&
128  "Expected source type to be unsigned.");
129  WideExprTy = Context->getCorrespondingSignedType(Ty);
130  }
131 
132  {
133  auto Diag = diag(E->getBeginLoc(), "perform multiplication in a wider type",
134  DiagnosticIDs::Note)
135  << LHS->getSourceRange();
136 
137  if (ShouldUseCXXStaticCast)
138  Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
139  "static_cast<" +
140  WideExprTy.getAsString() + ">(")
141  << FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
142  else
143  Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
144  "(" + WideExprTy.getAsString() + ")");
145  Diag << includeStddefHeader(LHS->getBeginLoc());
146  }
147 }
148 
149 void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
150  const Expr *E) {
151  ASTContext *Context = Result->Context;
152 
153  // We are looking for a pointer offset operation,
154  // with one hand being a pointer, and another one being an offset.
155  const Expr *PointerExpr, *IndexExpr;
156  if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
157  PointerExpr = BO->getLHS();
158  IndexExpr = BO->getRHS();
159  } else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
160  PointerExpr = ASE->getLHS();
161  IndexExpr = ASE->getRHS();
162  } else
163  return;
164 
165  if (IndexExpr->getType()->isPointerType())
166  std::swap(PointerExpr, IndexExpr);
167 
168  if (!PointerExpr->getType()->isPointerType() ||
169  IndexExpr->getType()->isPointerType())
170  return;
171 
172  IndexExpr = IndexExpr->IgnoreParens();
173 
174  QualType IndexExprType = IndexExpr->getType();
175 
176  // If the index expression's type is not known (i.e. we are in a template),
177  // we can't do anything here.
178  if (IndexExprType->isDependentType())
179  return;
180 
181  QualType SSizeTy = Context->getPointerDiffType();
182  QualType USizeTy = Context->getSizeType();
183  QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
184  // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t?
185  // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t!
186  StringRef TyAsString =
187  IndexExprType->isSignedIntegerType() ? "ptrdiff_t" : "size_t";
188 
189  // So, is size_t actually wider than the result of the multiplication?
190  if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))
191  return;
192 
193  // Does the index expression look like it might be unintentionally computed
194  // in a narrower-than-wanted type?
195  const Expr *LHS = getLHSOfMulBinOp(IndexExpr);
196  if (!LHS)
197  return;
198 
199  // Ok, looks like we should diagnose this.
200  diag(E->getBeginLoc(),
201  "result of multiplication in type %0 is used as a pointer offset after "
202  "an implicit widening conversion to type '%1'")
203  << IndexExprType << TyAsString;
204 
205  {
206  auto Diag = diag(IndexExpr->getBeginLoc(),
207  "make conversion explicit to silence this warning",
208  DiagnosticIDs::Note)
209  << IndexExpr->getSourceRange();
210 
211  if (ShouldUseCXXStaticCast)
212  Diag << FixItHint::CreateInsertion(
213  IndexExpr->getBeginLoc(),
214  (Twine("static_cast<") + TyAsString + ">(").str())
215  << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
216  else
217  Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
218  (Twine("(") + TyAsString + ")(").str())
219  << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
220  Diag << includeStddefHeader(IndexExpr->getBeginLoc());
221  }
222 
223  {
224  auto Diag =
225  diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",
226  DiagnosticIDs::Note)
227  << LHS->getSourceRange();
228 
229  if (ShouldUseCXXStaticCast)
230  Diag << FixItHint::CreateInsertion(
231  LHS->getBeginLoc(),
232  (Twine("static_cast<") + TyAsString + ">(").str())
233  << FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
234  else
235  Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
236  (Twine("(") + TyAsString + ")").str());
237  Diag << includeStddefHeader(LHS->getBeginLoc());
238  }
239 }
240 
242  MatchFinder *Finder) {
243  Finder->addMatcher(implicitCastExpr(unless(anyOf(isInTemplateInstantiation(),
244  isPartOfExplicitCast())),
245  hasCastKind(CK_IntegralCast))
246  .bind("x"),
247  this);
248  Finder->addMatcher(
249  arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);
250  Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
251  hasType(isAnyPointer()),
252  hasAnyOperatorName("+", "-", "+=", "-="))
253  .bind("x"),
254  this);
255 }
256 
258  const MatchFinder::MatchResult &Result) {
259  this->Result = &Result;
260  ShouldUseCXXStaticCast =
261  UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;
262  ShouldUseCXXHeader =
263  UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;
264 
265  if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>("x"))
266  handleImplicitCastExpr(MatchedDecl);
267  else if (const auto *MatchedDecl =
268  Result.Nodes.getNodeAs<ArraySubscriptExpr>("x"))
269  handlePointerOffsetting(MatchedDecl);
270  else if (const auto *MatchedDecl =
271  Result.Nodes.getNodeAs<BinaryOperator>("x"))
272  handlePointerOffsetting(MatchedDecl);
273 }
274 
275 } // namespace bugprone
276 } // namespace tidy
277 } // namespace clang
clang::tidy::bugprone::ImplicitWideningOfMultiplicationResultCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: ImplicitWideningOfMultiplicationResultCheck.cpp:241
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
clang::tidy::bugprone::getLHSOfMulBinOp
static const Expr * getLHSOfMulBinOp(const Expr *E)
Definition: ImplicitWideningOfMultiplicationResultCheck.cpp:27
clang::tidy::utils::IncludeInserter::registerPreprocessor
void registerPreprocessor(Preprocessor *PP)
Registers this with the Preprocessor PP, must be called before this class is used.
Definition: IncludeInserter.cpp:42
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:54
clang::tidy::bugprone::ImplicitWideningOfMultiplicationResultCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: ImplicitWideningOfMultiplicationResultCheck.cpp:257
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:416
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:72
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
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::ast_matchers::AST_MATCHER
AST_MATCHER(Expr, isMacroID)
Definition: PreferIsaOrDynCastInConditionalsCheck.cpp:19
clang::tidy::bugprone::PP
static Preprocessor * PP
Definition: BadSignalToKillThreadCheck.cpp:29
ImplicitWideningOfMultiplicationResultCheck.h
clang::tidy::utils::IncludeInserter::getStyle
IncludeSorter::IncludeStyle getStyle() const
Definition: IncludeInserter.h:84
clang::tidy::bugprone::ImplicitWideningOfMultiplicationResultCheck::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: ImplicitWideningOfMultiplicationResultCheck.cpp:54
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::utils::IncludeInserter::createIncludeInsertion
llvm::Optional< FixItHint > createIncludeInsertion(FileID FileID, llvm::StringRef Header)
Creates a Header inclusion directive fixit in the File FileID.
Definition: IncludeInserter.cpp:70
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::ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
Definition: ImplicitWideningOfMultiplicationResultCheck.cpp:49