1 //===--- ImplicitWideningOfMultiplicationResultCheck.cpp - clang-tidy -----===//
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
7 //===----------------------------------------------------------------------===//
9 #include "ImplicitWideningOfMultiplicationResultCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 using namespace clang::ast_matchers
;
18 AST_MATCHER(ImplicitCastExpr
, isPartOfExplicitCast
) {
19 return Node
.isPartOfExplicitCast();
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)); ?
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
)
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
);
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",
101 << E
->getSourceRange();
103 if (ShouldUseCXXStaticCast
)
104 Diag
<< FixItHint::CreateInsertion(
105 E
->getBeginLoc(), "static_cast<" + Ty
.getAsString() + ">(")
106 << FixItHint::CreateInsertion(E
->getEndLoc(), ")");
108 Diag
<< FixItHint::CreateInsertion(E
->getBeginLoc(),
109 "(" + Ty
.getAsString() + ")(")
110 << FixItHint::CreateInsertion(E
->getEndLoc(), ")");
111 Diag
<< includeStddefHeader(E
->getBeginLoc());
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())
119 else if (Ty
->isSignedIntegerType()) {
120 assert(ETy
->isUnsignedIntegerType() &&
121 "Expected source type to be signed.");
122 WideExprTy
= Context
->getCorrespondingUnsignedType(Ty
);
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",
134 << LHS
->getSourceRange();
136 if (ShouldUseCXXStaticCast
)
137 Diag
<< FixItHint::CreateInsertion(LHS
->getBeginLoc(),
139 WideExprTy
.getAsString() + ">(")
140 << FixItHint::CreateInsertion(LHS
->getEndLoc(), ")");
142 Diag
<< FixItHint::CreateInsertion(LHS
->getBeginLoc(),
143 "(" + WideExprTy
.getAsString() + ")");
144 Diag
<< includeStddefHeader(LHS
->getBeginLoc());
148 void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
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();
164 if (IndexExpr
->getType()->isPointerType())
165 std::swap(PointerExpr
, IndexExpr
);
167 if (!PointerExpr
->getType()->isPointerType() ||
168 IndexExpr
->getType()->isPointerType())
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())
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
))
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
);
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",
208 << IndexExpr
->getSourceRange();
210 if (ShouldUseCXXStaticCast
)
211 Diag
<< FixItHint::CreateInsertion(
212 IndexExpr
->getBeginLoc(),
213 (Twine("static_cast<") + TyAsString
+ ">(").str())
214 << FixItHint::CreateInsertion(IndexExpr
->getEndLoc(), ")");
216 Diag
<< FixItHint::CreateInsertion(IndexExpr
->getBeginLoc(),
217 (Twine("(") + TyAsString
+ ")(").str())
218 << FixItHint::CreateInsertion(IndexExpr
->getEndLoc(), ")");
219 Diag
<< includeStddefHeader(IndexExpr
->getBeginLoc());
224 diag(IndexExpr
->getBeginLoc(), "perform multiplication in a wider type",
226 << LHS
->getSourceRange();
228 if (ShouldUseCXXStaticCast
)
229 Diag
<< FixItHint::CreateInsertion(
231 (Twine("static_cast<") + TyAsString
+ ">(").str())
232 << FixItHint::CreateInsertion(LHS
->getEndLoc(), ")");
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
))
248 arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);
249 Finder
->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
250 hasType(isAnyPointer()),
251 hasAnyOperatorName("+", "-", "+=", "-="))
256 void ImplicitWideningOfMultiplicationResultCheck::check(
257 const MatchFinder::MatchResult
&Result
) {
258 this->Result
= &Result
;
259 ShouldUseCXXStaticCast
=
260 UseCXXStaticCastsInCppSources
&& Result
.Context
->getLangOpts().CPlusPlus
;
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