[Workflow] Try to fix code-formatter failing to find changes in some cases.
[llvm-project.git] / clang-tools-extra / clang-tidy / bugprone / ImplicitWideningOfMultiplicationResultCheck.cpp
blob95a7c521eabb0ed55bef4b4084002b282e1b8116
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 //===----------------------------------------------------------------------===//
9 #include "ImplicitWideningOfMultiplicationResultCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include <optional>
14 using namespace clang::ast_matchers;
16 namespace clang {
17 namespace {
18 AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {
19 return Node.isPartOfExplicitCast();
21 } // namespace
22 } // namespace clang
24 namespace clang::tidy::bugprone {
26 static const Expr *getLHSOfMulBinOp(const Expr *E) {
27 assert(E == E->IgnoreParens() && "Already skipped all parens!");
28 // Is this: long r = int(x) * int(y); ?
29 // FIXME: shall we skip brackets/casts/etc?
30 const auto *BO = dyn_cast<BinaryOperator>(E);
31 if (!BO || BO->getOpcode() != BO_Mul)
32 // FIXME: what about: long r = int(x) + (int(y) * int(z)); ?
33 return nullptr;
34 return BO->getLHS()->IgnoreParens();
37 ImplicitWideningOfMultiplicationResultCheck::
38 ImplicitWideningOfMultiplicationResultCheck(StringRef Name,
39 ClangTidyContext *Context)
40 : ClangTidyCheck(Name, Context),
41 UseCXXStaticCastsInCppSources(
42 Options.get("UseCXXStaticCastsInCppSources", true)),
43 UseCXXHeadersInCppSources(Options.get("UseCXXHeadersInCppSources", true)),
44 IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
45 utils::IncludeSorter::IS_LLVM),
46 areDiagsSelfContained()) {}
48 void ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks(
49 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
50 IncludeInserter.registerPreprocessor(PP);
53 void ImplicitWideningOfMultiplicationResultCheck::storeOptions(
54 ClangTidyOptions::OptionMap &Opts) {
55 Options.store(Opts, "UseCXXStaticCastsInCppSources",
56 UseCXXStaticCastsInCppSources);
57 Options.store(Opts, "UseCXXHeadersInCppSources", UseCXXHeadersInCppSources);
58 Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
61 std::optional<FixItHint>
62 ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(
63 SourceLocation File) {
64 return IncludeInserter.createIncludeInsertion(
65 Result->SourceManager->getFileID(File),
66 ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>");
69 void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
70 const ImplicitCastExpr *ICE) {
71 ASTContext *Context = Result->Context;
73 const Expr *E = ICE->getSubExpr()->IgnoreParens();
74 QualType Ty = ICE->getType();
75 QualType ETy = E->getType();
77 assert(!ETy->isDependentType() && !Ty->isDependentType() &&
78 "Don't expect to ever get here in template Context.");
80 // This must be a widening cast. Else we do not care.
81 unsigned SrcWidth = Context->getIntWidth(ETy);
82 unsigned TgtWidth = Context->getIntWidth(Ty);
83 if (TgtWidth <= SrcWidth)
84 return;
86 // Does the index expression look like it might be unintentionally computed
87 // in a narrower-than-wanted type?
88 const Expr *LHS = getLHSOfMulBinOp(E);
89 if (!LHS)
90 return;
92 // Ok, looks like we should diagnose this.
93 diag(E->getBeginLoc(), "performing an implicit widening conversion to type "
94 "%0 of a multiplication performed in type %1")
95 << Ty << E->getType();
98 auto Diag = diag(E->getBeginLoc(),
99 "make conversion explicit to silence this warning",
100 DiagnosticIDs::Note)
101 << E->getSourceRange();
103 if (ShouldUseCXXStaticCast)
104 Diag << FixItHint::CreateInsertion(
105 E->getBeginLoc(), "static_cast<" + Ty.getAsString() + ">(")
106 << FixItHint::CreateInsertion(E->getEndLoc(), ")");
107 else
108 Diag << FixItHint::CreateInsertion(E->getBeginLoc(),
109 "(" + Ty.getAsString() + ")(")
110 << FixItHint::CreateInsertion(E->getEndLoc(), ")");
111 Diag << includeStddefHeader(E->getBeginLoc());
114 QualType WideExprTy;
115 // Get Ty of the same signedness as ExprTy, because we only want to suggest
116 // to widen the computation, but not change it's signedness domain.
117 if (Ty->isSignedIntegerType() == ETy->isSignedIntegerType())
118 WideExprTy = Ty;
119 else if (Ty->isSignedIntegerType()) {
120 assert(ETy->isUnsignedIntegerType() &&
121 "Expected source type to be signed.");
122 WideExprTy = Context->getCorrespondingUnsignedType(Ty);
123 } else {
124 assert(Ty->isUnsignedIntegerType() &&
125 "Expected target type to be unsigned.");
126 assert(ETy->isSignedIntegerType() &&
127 "Expected source type to be unsigned.");
128 WideExprTy = Context->getCorrespondingSignedType(Ty);
132 auto Diag = diag(E->getBeginLoc(), "perform multiplication in a wider type",
133 DiagnosticIDs::Note)
134 << LHS->getSourceRange();
136 if (ShouldUseCXXStaticCast)
137 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
138 "static_cast<" +
139 WideExprTy.getAsString() + ">(")
140 << FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
141 else
142 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
143 "(" + WideExprTy.getAsString() + ")");
144 Diag << includeStddefHeader(LHS->getBeginLoc());
148 void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
149 const Expr *E) {
150 ASTContext *Context = Result->Context;
152 // We are looking for a pointer offset operation,
153 // with one hand being a pointer, and another one being an offset.
154 const Expr *PointerExpr = nullptr, *IndexExpr = nullptr;
155 if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
156 PointerExpr = BO->getLHS();
157 IndexExpr = BO->getRHS();
158 } else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
159 PointerExpr = ASE->getLHS();
160 IndexExpr = ASE->getRHS();
161 } else
162 return;
164 if (IndexExpr->getType()->isPointerType())
165 std::swap(PointerExpr, IndexExpr);
167 if (!PointerExpr->getType()->isPointerType() ||
168 IndexExpr->getType()->isPointerType())
169 return;
171 IndexExpr = IndexExpr->IgnoreParens();
173 QualType IndexExprType = IndexExpr->getType();
175 // If the index expression's type is not known (i.e. we are in a template),
176 // we can't do anything here.
177 if (IndexExprType->isDependentType())
178 return;
180 QualType SSizeTy = Context->getPointerDiffType();
181 QualType USizeTy = Context->getSizeType();
182 QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
183 // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t?
184 // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t!
185 StringRef TyAsString =
186 IndexExprType->isSignedIntegerType() ? "ptrdiff_t" : "size_t";
188 // So, is size_t actually wider than the result of the multiplication?
189 if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))
190 return;
192 // Does the index expression look like it might be unintentionally computed
193 // in a narrower-than-wanted type?
194 const Expr *LHS = getLHSOfMulBinOp(IndexExpr);
195 if (!LHS)
196 return;
198 // Ok, looks like we should diagnose this.
199 diag(E->getBeginLoc(),
200 "result of multiplication in type %0 is used as a pointer offset after "
201 "an implicit widening conversion to type '%1'")
202 << IndexExprType << TyAsString;
205 auto Diag = diag(IndexExpr->getBeginLoc(),
206 "make conversion explicit to silence this warning",
207 DiagnosticIDs::Note)
208 << IndexExpr->getSourceRange();
210 if (ShouldUseCXXStaticCast)
211 Diag << FixItHint::CreateInsertion(
212 IndexExpr->getBeginLoc(),
213 (Twine("static_cast<") + TyAsString + ">(").str())
214 << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
215 else
216 Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
217 (Twine("(") + TyAsString + ")(").str())
218 << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
219 Diag << includeStddefHeader(IndexExpr->getBeginLoc());
223 auto Diag =
224 diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",
225 DiagnosticIDs::Note)
226 << LHS->getSourceRange();
228 if (ShouldUseCXXStaticCast)
229 Diag << FixItHint::CreateInsertion(
230 LHS->getBeginLoc(),
231 (Twine("static_cast<") + TyAsString + ">(").str())
232 << FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
233 else
234 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
235 (Twine("(") + TyAsString + ")").str());
236 Diag << includeStddefHeader(LHS->getBeginLoc());
240 void ImplicitWideningOfMultiplicationResultCheck::registerMatchers(
241 MatchFinder *Finder) {
242 Finder->addMatcher(implicitCastExpr(unless(anyOf(isInTemplateInstantiation(),
243 isPartOfExplicitCast())),
244 hasCastKind(CK_IntegralCast))
245 .bind("x"),
246 this);
247 Finder->addMatcher(
248 arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);
249 Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
250 hasType(isAnyPointer()),
251 hasAnyOperatorName("+", "-", "+=", "-="))
252 .bind("x"),
253 this);
256 void ImplicitWideningOfMultiplicationResultCheck::check(
257 const MatchFinder::MatchResult &Result) {
258 this->Result = &Result;
259 ShouldUseCXXStaticCast =
260 UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;
261 ShouldUseCXXHeader =
262 UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;
264 if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>("x"))
265 handleImplicitCastExpr(MatchedDecl);
266 else if (const auto *MatchedDecl =
267 Result.Nodes.getNodeAs<ArraySubscriptExpr>("x"))
268 handlePointerOffsetting(MatchedDecl);
269 else if (const auto *MatchedDecl =
270 Result.Nodes.getNodeAs<BinaryOperator>("x"))
271 handlePointerOffsetting(MatchedDecl);
274 } // namespace clang::tidy::bugprone