clang-tools 22.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
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
169void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
170 const Expr *E) {
171 const 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 const 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 const QualType SSizeTy = Context->getPointerDiffType();
202 const QualType USizeTy = Context->getSizeType();
203 const QualType SizeTy =
204 IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
205 // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t?
206 // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t!
207 const StringRef TyAsString =
208 IndexExprType->isSignedIntegerType() ? "ptrdiff_t" : "size_t";
209
210 // So, is size_t actually wider than the result of the multiplication?
211 if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))
212 return;
213
214 // Does the index expression look like it might be unintentionally computed
215 // in a narrower-than-wanted type?
216 const Expr *LHS = getLHSOfMulBinOp(IndexExpr);
217 if (!LHS)
218 return;
219
220 // Ok, looks like we should diagnose this.
221 diag(E->getBeginLoc(),
222 "result of multiplication in type %0 is used as a pointer offset after "
223 "an implicit widening conversion to type '%1'")
224 << IndexExprType << TyAsString;
225
226 {
227 auto Diag = diag(IndexExpr->getBeginLoc(),
228 "make conversion explicit to silence this warning",
229 DiagnosticIDs::Note)
230 << IndexExpr->getSourceRange();
231 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
232 IndexExpr->getEndLoc(), 0, *Result->SourceManager, getLangOpts());
233 if (ShouldUseCXXStaticCast)
234 Diag << FixItHint::CreateInsertion(
235 IndexExpr->getBeginLoc(),
236 (Twine("static_cast<") + TyAsString + ">(").str())
237 << FixItHint::CreateInsertion(EndLoc, ")");
238 else
239 Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
240 (Twine("(") + TyAsString + ")(").str())
241 << FixItHint::CreateInsertion(EndLoc, ")");
242 Diag << includeStddefHeader(IndexExpr->getBeginLoc());
243 }
244
245 {
246 auto Diag =
247 diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",
248 DiagnosticIDs::Note)
249 << LHS->getSourceRange();
250
251 if (ShouldUseCXXStaticCast)
252 Diag << FixItHint::CreateInsertion(
253 LHS->getBeginLoc(),
254 (Twine("static_cast<") + TyAsString + ">(").str())
255 << FixItHint::CreateInsertion(
256 Lexer::getLocForEndOfToken(IndexExpr->getEndLoc(), 0,
257 *Result->SourceManager,
258 getLangOpts()),
259 ")");
260 else
261 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
262 (Twine("(") + TyAsString + ")").str());
263 Diag << includeStddefHeader(LHS->getBeginLoc());
264 }
265}
266
268 MatchFinder *Finder) {
269 Finder->addMatcher(implicitCastExpr(unless(anyOf(containsErrors(),
270 isInTemplateInstantiation(),
271 isPartOfExplicitCast())),
272 hasCastKind(CK_IntegralCast))
273 .bind("x"),
274 this);
275 Finder->addMatcher(
276 arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);
277 Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
278 hasType(isAnyPointer()),
279 hasAnyOperatorName("+", "-", "+=", "-="))
280 .bind("x"),
281 this);
282}
283
285 const MatchFinder::MatchResult &Result) {
286 this->Result = &Result;
287 ShouldUseCXXStaticCast =
288 UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;
289 ShouldUseCXXHeader =
290 UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;
291
292 if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>("x"))
293 handleImplicitCastExpr(MatchedDecl);
294 else if (const auto *MatchedDecl =
295 Result.Nodes.getNodeAs<ArraySubscriptExpr>("x"))
296 handlePointerOffsetting(MatchedDecl);
297 else if (const auto *MatchedDecl =
298 Result.Nodes.getNodeAs<BinaryOperator>("x"))
299 handlePointerOffsetting(MatchedDecl);
300}
301
302} // 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