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 
9 #include "ImplicitWideningOfMultiplicationResultCheck.h"
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 {
AST_MATCHER(ImplicitCastExpr,isPartOfExplicitCast)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 
getLHSOfMulBinOp(const Expr * E)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::
ImplicitWideningOfMultiplicationResultCheck(StringRef Name,ClangTidyContext * Context)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 
registerPPCallbacks(const SourceManager & SM,Preprocessor * PP,Preprocessor * ModuleExpanderPP)49 void ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks(
50     const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
51   IncludeInserter.registerPreprocessor(PP);
52 }
53 
storeOptions(ClangTidyOptions::OptionMap & Opts)54 void ImplicitWideningOfMultiplicationResultCheck::storeOptions(
55     ClangTidyOptions::OptionMap &Opts) {
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>
includeStddefHeader(SourceLocation File)63 ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(
64     SourceLocation File) {
65   return IncludeInserter.createIncludeInsertion(
66       Result->SourceManager->getFileID(File),
67       ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>");
68 }
69 
handleImplicitCastExpr(const ImplicitCastExpr * ICE)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 
handlePointerOffsetting(const Expr * E)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 
registerMatchers(MatchFinder * Finder)241 void ImplicitWideningOfMultiplicationResultCheck::registerMatchers(
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 
check(const MatchFinder::MatchResult & Result)257 void ImplicitWideningOfMultiplicationResultCheck::check(
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
278