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

source code of clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp