clang-tools 24.0.0git
ImplicitWideningOfMultiplicationResultCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
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
16using namespace clang::ast_matchers;
17
18namespace clang::tidy::bugprone {
19
20namespace {
21AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {
22 return Node.isPartOfExplicitCast();
23}
24AST_MATCHER(Expr, containsErrors) { return Node.containsErrors(); }
25} // namespace
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>(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
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
51 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
52 IncludeInserter.registerPreprocessor(PP);
53}
54
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
64std::optional<FixItHint>
65ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(
66 SourceLocation File) {
67 return IncludeInserter.createIncludeInsertion(
68 Result->SourceManager->getFileID(File),
69 ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>");
70}
71
72void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
73 const ImplicitCastExpr *ICE) {
74 const ASTContext *Context = Result->Context;
75
76 const Expr *E = ICE->getSubExpr()->IgnoreParens();
77 const QualType Ty = ICE->getType();
78 const 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 const unsigned SrcWidth = Context->getIntWidth(ETy);
85 const 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 const 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 const Expr *RHS = cast<BinaryOperator>(E)->getRHS()->IgnoreParens();
108
109 // Ok, looks like we should diagnose this.
110 diag(E->getBeginLoc(), "performing an implicit widening conversion to type "
111 "%0 of a multiplication performed in type %1")
112 << Ty << E->getType();
113
114 {
115 const auto Diag = diag(E->getBeginLoc(),
116 "make conversion explicit to silence this warning",
117 DiagnosticIDs::Note)
118 << E->getSourceRange();
119 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
120 E->getEndLoc(), 0, *Result->SourceManager, getLangOpts());
121 if (ShouldUseCXXStaticCast)
122 Diag << FixItHint::CreateInsertion(
123 E->getBeginLoc(), "static_cast<" + Ty.getAsString() + ">(")
124 << FixItHint::CreateInsertion(EndLoc, ")");
125 else
126 Diag << FixItHint::CreateInsertion(E->getBeginLoc(),
127 "(" + Ty.getAsString() + ")(")
128 << FixItHint::CreateInsertion(EndLoc, ")");
129 Diag << includeStddefHeader(E->getBeginLoc());
130 }
131
132 QualType WideExprTy;
133 // Get Ty of the same signedness as ExprTy, because we only want to suggest
134 // to widen the computation, but not change it's signedness domain.
135 // However, if ETy is only signed because both operands were of an
136 // unsigned type narrower than int (and thus got integer-promoted to the
137 // signed type int), the multiplication was never really operating in a
138 // signed domain to begin with, so don't force a signed widened type in
139 // that case either.
140 const bool BothOperandsWereUnsigned =
141 LHS->IgnoreImpCasts()->getType()->isUnsignedIntegerType() &&
142 RHS->IgnoreImpCasts()->getType()->isUnsignedIntegerType();
143 const bool EffectiveETyIsSigned =
144 ETy->isSignedIntegerType() && !BothOperandsWereUnsigned;
145 if (Ty->isSignedIntegerType() == EffectiveETyIsSigned) {
146 WideExprTy = Ty;
147 } else if (Ty->isSignedIntegerType()) {
148 WideExprTy = Context->getCorrespondingUnsignedType(Ty);
149 } else {
150 assert(Ty->isUnsignedIntegerType() &&
151 "Expected target type to be unsigned.");
152 assert(ETy->isSignedIntegerType() && "Expected source type to be signed.");
153 WideExprTy = Context->getCorrespondingSignedType(Ty);
154 }
155
156 {
157 const auto Diag =
158 diag(E->getBeginLoc(), "perform multiplication in a wider type",
159 DiagnosticIDs::Note)
160 << LHS->getSourceRange();
161
162 if (ShouldUseCXXStaticCast)
163 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
164 "static_cast<" +
165 WideExprTy.getAsString() + ">(")
166 << FixItHint::CreateInsertion(
167 Lexer::getLocForEndOfToken(LHS->getEndLoc(), 0,
168 *Result->SourceManager,
169 getLangOpts()),
170 ")");
171 else
172 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
173 "(" + WideExprTy.getAsString() + ")");
174 Diag << includeStddefHeader(LHS->getBeginLoc());
175 }
176}
177
178void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
179 const Expr *E) {
180 const ASTContext *Context = Result->Context;
181
182 // We are looking for a pointer offset operation,
183 // with one hand being a pointer, and another one being an offset.
184 const Expr *PointerExpr = nullptr, *IndexExpr = nullptr;
185 if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
186 PointerExpr = BO->getLHS();
187 IndexExpr = BO->getRHS();
188 } else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
189 PointerExpr = ASE->getLHS();
190 IndexExpr = ASE->getRHS();
191 } else {
192 return;
193 }
194
195 if (IndexExpr->getType()->isPointerType())
196 std::swap(PointerExpr, IndexExpr);
197
198 if (!PointerExpr->getType()->isPointerType() ||
199 IndexExpr->getType()->isPointerType())
200 return;
201
202 IndexExpr = IndexExpr->IgnoreParens();
203
204 const QualType IndexExprType = IndexExpr->getType();
205
206 // If the index expression's type is not known (i.e. we are in a template),
207 // we can't do anything here.
208 if (IndexExprType->isDependentType())
209 return;
210
211 const QualType SSizeTy = Context->getPointerDiffType();
212 const QualType USizeTy = Context->getSizeType();
213 const QualType SizeTy =
214 IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
215 // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t?
216 // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t!
217 const StringRef TyAsString =
218 IndexExprType->isSignedIntegerType() ? "ptrdiff_t" : "size_t";
219
220 // So, is size_t actually wider than the result of the multiplication?
221 if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))
222 return;
223
224 // Does the index expression look like it might be unintentionally computed
225 // in a narrower-than-wanted type?
226 const Expr *LHS = getLHSOfMulBinOp(IndexExpr);
227 if (!LHS)
228 return;
229
230 // Ok, looks like we should diagnose this.
231 diag(E->getBeginLoc(),
232 "result of multiplication in type %0 is used as a pointer offset after "
233 "an implicit widening conversion to type '%1'")
234 << IndexExprType << TyAsString;
235
236 {
237 const auto Diag = diag(IndexExpr->getBeginLoc(),
238 "make conversion explicit to silence this warning",
239 DiagnosticIDs::Note)
240 << IndexExpr->getSourceRange();
241 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
242 IndexExpr->getEndLoc(), 0, *Result->SourceManager, getLangOpts());
243 if (ShouldUseCXXStaticCast)
244 Diag << FixItHint::CreateInsertion(
245 IndexExpr->getBeginLoc(),
246 (Twine("static_cast<") + TyAsString + ">(").str())
247 << FixItHint::CreateInsertion(EndLoc, ")");
248 else
249 Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
250 (Twine("(") + TyAsString + ")(").str())
251 << FixItHint::CreateInsertion(EndLoc, ")");
252 Diag << includeStddefHeader(IndexExpr->getBeginLoc());
253 }
254
255 {
256 const auto Diag =
257 diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",
258 DiagnosticIDs::Note)
259 << LHS->getSourceRange();
260
261 if (ShouldUseCXXStaticCast)
262 Diag << FixItHint::CreateInsertion(
263 LHS->getBeginLoc(),
264 (Twine("static_cast<") + TyAsString + ">(").str())
265 << FixItHint::CreateInsertion(
266 Lexer::getLocForEndOfToken(IndexExpr->getEndLoc(), 0,
267 *Result->SourceManager,
268 getLangOpts()),
269 ")");
270 else
271 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
272 (Twine("(") + TyAsString + ")").str());
273 Diag << includeStddefHeader(LHS->getBeginLoc());
274 }
275}
276
278 MatchFinder *Finder) {
279 Finder->addMatcher(implicitCastExpr(unless(anyOf(containsErrors(),
280 isInTemplateInstantiation(),
281 isPartOfExplicitCast())),
282 hasCastKind(CK_IntegralCast))
283 .bind("x"),
284 this);
285 Finder->addMatcher(
286 arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);
287 Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
288 hasType(isAnyPointer()),
289 hasAnyOperatorName("+", "-", "+=", "-="))
290 .bind("x"),
291 this);
292}
293
295 const MatchFinder::MatchResult &Result) {
296 this->Result = &Result;
297 ShouldUseCXXStaticCast =
298 UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;
299 ShouldUseCXXHeader =
300 UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;
301
302 if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>("x"))
303 handleImplicitCastExpr(MatchedDecl);
304 else if (const auto *MatchedDecl =
305 Result.Nodes.getNodeAs<ArraySubscriptExpr>("x"))
306 handlePointerOffsetting(MatchedDecl);
307 else if (const auto *MatchedDecl =
308 Result.Nodes.getNodeAs<BinaryOperator>("x"))
309 handlePointerOffsetting(MatchedDecl);
310}
311
312} // namespace clang::tidy::bugprone
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
static const Expr * getLHSOfMulBinOp(const Expr *E)
AST_MATCHER(BinaryOperator, isRelationalOperator)
llvm::StringMap< ClangTidyValue > OptionMap