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 #include "clang/ASTMatchers/ASTMatchersMacros.h"
13 #include "clang/Lex/Lexer.h"
14 #include <optional>
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang::tidy::bugprone {
19 
20 namespace {
21 AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {
22   return Node.isPartOfExplicitCast();
23 }
24 AST_MATCHER(Expr, containsErrors) { return Node.containsErrors(); }
25 } // namespace
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       IgnoreConstantIntExpr(Options.get("IgnoreConstantIntExpr", false)),
46       IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
47                                                utils::IncludeSorter::IS_LLVM),
48                       areDiagsSelfContained()) {}
49 
50 void ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks(
51     const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
52   IncludeInserter.registerPreprocessor(PP);
53 }
54 
55 void ImplicitWideningOfMultiplicationResultCheck::storeOptions(
56     ClangTidyOptions::OptionMap &Opts) {
57   Options.store(Opts, "UseCXXStaticCastsInCppSources",
58                 UseCXXStaticCastsInCppSources);
59   Options.store(Opts, "UseCXXHeadersInCppSources", UseCXXHeadersInCppSources);
60   Options.store(Opts, "IgnoreConstantIntExpr", IgnoreConstantIntExpr);
61   Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
62 }
63 
64 std::optional<FixItHint>
65 ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(
66     SourceLocation File) {
67   return IncludeInserter.createIncludeInsertion(
68       Result->SourceManager->getFileID(File),
69       ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>");
70 }
71 
72 void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
73     const ImplicitCastExpr *ICE) {
74   ASTContext *Context = Result->Context;
75 
76   const Expr *E = ICE->getSubExpr()->IgnoreParens();
77   QualType Ty = ICE->getType();
78   QualType ETy = E->getType();
79 
80   assert(!ETy->isDependentType() && !Ty->isDependentType() &&
81          "Don't expect to ever get here in template Context.");
82 
83   // This must be a widening cast. Else we do not care.
84   unsigned SrcWidth = Context->getIntWidth(ETy);
85   unsigned TgtWidth = Context->getIntWidth(Ty);
86   if (TgtWidth <= SrcWidth)
87     return;
88 
89   // Is the expression a compile-time constexpr that we know can fit in the
90   // source type?
91   if (IgnoreConstantIntExpr && ETy->isIntegerType() &&
92       !ETy->isUnsignedIntegerType()) {
93     if (const auto ConstExprResult = E->getIntegerConstantExpr(*Context)) {
94       const auto TypeSize = Context->getTypeSize(ETy);
95       llvm::APSInt WidenedResult = ConstExprResult->extOrTrunc(TypeSize);
96       if (WidenedResult <= llvm::APSInt::getMaxValue(TypeSize, false) &&
97           WidenedResult >= llvm::APSInt::getMinValue(TypeSize, false))
98         return;
99     }
100   }
101 
102   // Does the index expression look like it might be unintentionally computed
103   // in a narrower-than-wanted type?
104   const Expr *LHS = getLHSOfMulBinOp(E);
105   if (!LHS)
106     return;
107 
108   // Ok, looks like we should diagnose this.
109   diag(E->getBeginLoc(), "performing an implicit widening conversion to type "
110                          "%0 of a multiplication performed in type %1")
111       << Ty << E->getType();
112 
113   {
114     auto Diag = diag(E->getBeginLoc(),
115                      "make conversion explicit to silence this warning",
116                      DiagnosticIDs::Note)
117                 << E->getSourceRange();
118     const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
119         E->getEndLoc(), 0, *Result->SourceManager, getLangOpts());
120     if (ShouldUseCXXStaticCast)
121       Diag << FixItHint::CreateInsertion(
122                   E->getBeginLoc(), "static_cast<" + Ty.getAsString() + ">(")
123            << FixItHint::CreateInsertion(EndLoc, ")");
124     else
125       Diag << FixItHint::CreateInsertion(E->getBeginLoc(),
126                                          "(" + Ty.getAsString() + ")(")
127            << FixItHint::CreateInsertion(EndLoc, ")");
128     Diag << includeStddefHeader(E->getBeginLoc());
129   }
130 
131   QualType WideExprTy;
132   // Get Ty of the same signedness as ExprTy, because we only want to suggest
133   // to widen the computation, but not change it's signedness domain.
134   if (Ty->isSignedIntegerType() == ETy->isSignedIntegerType())
135     WideExprTy = Ty;
136   else if (Ty->isSignedIntegerType()) {
137     assert(ETy->isUnsignedIntegerType() &&
138            "Expected source type to be signed.");
139     WideExprTy = Context->getCorrespondingUnsignedType(Ty);
140   } else {
141     assert(Ty->isUnsignedIntegerType() &&
142            "Expected target type to be unsigned.");
143     assert(ETy->isSignedIntegerType() &&
144            "Expected source type to be unsigned.");
145     WideExprTy = Context->getCorrespondingSignedType(Ty);
146   }
147 
148   {
149     auto Diag = diag(E->getBeginLoc(), "perform multiplication in a wider type",
150                      DiagnosticIDs::Note)
151                 << LHS->getSourceRange();
152 
153     if (ShouldUseCXXStaticCast)
154       Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
155                                          "static_cast<" +
156                                              WideExprTy.getAsString() + ">(")
157            << FixItHint::CreateInsertion(
158                   Lexer::getLocForEndOfToken(LHS->getEndLoc(), 0,
159                                              *Result->SourceManager,
160                                              getLangOpts()),
161                   ")");
162     else
163       Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
164                                          "(" + WideExprTy.getAsString() + ")");
165     Diag << includeStddefHeader(LHS->getBeginLoc());
166   }
167 }
168 
169 void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
170     const Expr *E) {
171   ASTContext *Context = Result->Context;
172 
173   // We are looking for a pointer offset operation,
174   // with one hand being a pointer, and another one being an offset.
175   const Expr *PointerExpr = nullptr, *IndexExpr = nullptr;
176   if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
177     PointerExpr = BO->getLHS();
178     IndexExpr = BO->getRHS();
179   } else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
180     PointerExpr = ASE->getLHS();
181     IndexExpr = ASE->getRHS();
182   } else
183     return;
184 
185   if (IndexExpr->getType()->isPointerType())
186     std::swap(PointerExpr, IndexExpr);
187 
188   if (!PointerExpr->getType()->isPointerType() ||
189       IndexExpr->getType()->isPointerType())
190     return;
191 
192   IndexExpr = IndexExpr->IgnoreParens();
193 
194   QualType IndexExprType = IndexExpr->getType();
195 
196   // If the index expression's type is not known (i.e. we are in a template),
197   // we can't do anything here.
198   if (IndexExprType->isDependentType())
199     return;
200 
201   QualType SSizeTy = Context->getPointerDiffType();
202   QualType USizeTy = Context->getSizeType();
203   QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
204   // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t?
205   // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t!
206   StringRef TyAsString =
207       IndexExprType->isSignedIntegerType() ? "ptrdiff_t" : "size_t";
208 
209   // So, is size_t actually wider than the result of the multiplication?
210   if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))
211     return;
212 
213   // Does the index expression look like it might be unintentionally computed
214   // in a narrower-than-wanted type?
215   const Expr *LHS = getLHSOfMulBinOp(IndexExpr);
216   if (!LHS)
217     return;
218 
219   // Ok, looks like we should diagnose this.
220   diag(E->getBeginLoc(),
221        "result of multiplication in type %0 is used as a pointer offset after "
222        "an implicit widening conversion to type '%1'")
223       << IndexExprType << TyAsString;
224 
225   {
226     auto Diag = diag(IndexExpr->getBeginLoc(),
227                      "make conversion explicit to silence this warning",
228                      DiagnosticIDs::Note)
229                 << IndexExpr->getSourceRange();
230     const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
231         IndexExpr->getEndLoc(), 0, *Result->SourceManager, getLangOpts());
232     if (ShouldUseCXXStaticCast)
233       Diag << FixItHint::CreateInsertion(
234                   IndexExpr->getBeginLoc(),
235                   (Twine("static_cast<") + TyAsString + ">(").str())
236            << FixItHint::CreateInsertion(EndLoc, ")");
237     else
238       Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
239                                          (Twine("(") + TyAsString + ")(").str())
240            << FixItHint::CreateInsertion(EndLoc, ")");
241     Diag << includeStddefHeader(IndexExpr->getBeginLoc());
242   }
243 
244   {
245     auto Diag =
246         diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",
247              DiagnosticIDs::Note)
248         << LHS->getSourceRange();
249 
250     if (ShouldUseCXXStaticCast)
251       Diag << FixItHint::CreateInsertion(
252                   LHS->getBeginLoc(),
253                   (Twine("static_cast<") + TyAsString + ">(").str())
254            << FixItHint::CreateInsertion(
255                   Lexer::getLocForEndOfToken(IndexExpr->getEndLoc(), 0,
256                                              *Result->SourceManager,
257                                              getLangOpts()),
258                   ")");
259     else
260       Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
261                                          (Twine("(") + TyAsString + ")").str());
262     Diag << includeStddefHeader(LHS->getBeginLoc());
263   }
264 }
265 
266 void ImplicitWideningOfMultiplicationResultCheck::registerMatchers(
267     MatchFinder *Finder) {
268   Finder->addMatcher(implicitCastExpr(unless(anyOf(containsErrors(),
269                                                    isInTemplateInstantiation(),
270                                                    isPartOfExplicitCast())),
271                                       hasCastKind(CK_IntegralCast))
272                          .bind("x"),
273                      this);
274   Finder->addMatcher(
275       arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);
276   Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
277                                     hasType(isAnyPointer()),
278                                     hasAnyOperatorName("+", "-", "+=", "-="))
279                          .bind("x"),
280                      this);
281 }
282 
283 void ImplicitWideningOfMultiplicationResultCheck::check(
284     const MatchFinder::MatchResult &Result) {
285   this->Result = &Result;
286   ShouldUseCXXStaticCast =
287       UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;
288   ShouldUseCXXHeader =
289       UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;
290 
291   if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>("x"))
292     handleImplicitCastExpr(MatchedDecl);
293   else if (const auto *MatchedDecl =
294                Result.Nodes.getNodeAs<ArraySubscriptExpr>("x"))
295     handlePointerOffsetting(MatchedDecl);
296   else if (const auto *MatchedDecl =
297                Result.Nodes.getNodeAs<BinaryOperator>("x"))
298     handlePointerOffsetting(MatchedDecl);
299 }
300 
301 } // namespace clang::tidy::bugprone
302