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/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>(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 IgnoreConstantIntExpr(Options.get(LocalName: "IgnoreConstantIntExpr", Default: false)),
46 IncludeInserter(Options.getLocalOrGlobal(LocalName: "IncludeStyle",
47 Default: utils::IncludeSorter::IS_LLVM),
48 areDiagsSelfContained()) {}
49
50void ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks(
51 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
52 IncludeInserter.registerPreprocessor(PP);
53}
54
55void ImplicitWideningOfMultiplicationResultCheck::storeOptions(
56 ClangTidyOptions::OptionMap &Opts) {
57 Options.store(Options&: Opts, LocalName: "UseCXXStaticCastsInCppSources",
58 Value: UseCXXStaticCastsInCppSources);
59 Options.store(Options&: Opts, LocalName: "UseCXXHeadersInCppSources", Value: UseCXXHeadersInCppSources);
60 Options.store(Options&: Opts, LocalName: "IgnoreConstantIntExpr", Value: IgnoreConstantIntExpr);
61 Options.store(Options&: Opts, LocalName: "IncludeStyle", Value: IncludeInserter.getStyle());
62}
63
64std::optional<FixItHint>
65ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(
66 SourceLocation File) {
67 return IncludeInserter.createIncludeInsertion(
68 FileID: Result->SourceManager->getFileID(SpellingLoc: File),
69 Header: ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>");
70}
71
72void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
73 const ImplicitCastExpr *ICE) {
74 ASTContext *Context = Result->Context;
75
76 const Expr *E = ICE->getSubExpr()->IgnoreParens();
77 QualType Ty = ICE->getType();
78 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 unsigned SrcWidth = Context->getIntWidth(T: ETy);
85 unsigned TgtWidth = Context->getIntWidth(T: 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(T: ETy);
95 llvm::APSInt WidenedResult = ConstExprResult->extOrTrunc(TypeSize);
96 if (WidenedResult <= llvm::APSInt::getMaxValue(numBits: TypeSize, Unsigned: false) &&
97 WidenedResult >= llvm::APSInt::getMinValue(numBits: TypeSize, Unsigned: 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 Loc: E->getEndLoc(), Offset: 0, SM: *Result->SourceManager, LangOpts: getLangOpts());
120 if (ShouldUseCXXStaticCast)
121 Diag << FixItHint::CreateInsertion(
122 InsertionLoc: E->getBeginLoc(), Code: "static_cast<" + Ty.getAsString() + ">(")
123 << FixItHint::CreateInsertion(InsertionLoc: EndLoc, Code: ")");
124 else
125 Diag << FixItHint::CreateInsertion(InsertionLoc: E->getBeginLoc(),
126 Code: "(" + Ty.getAsString() + ")(")
127 << FixItHint::CreateInsertion(InsertionLoc: EndLoc, Code: ")");
128 Diag << includeStddefHeader(File: 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(T: 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(T: 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(InsertionLoc: LHS->getBeginLoc(),
155 Code: "static_cast<" +
156 WideExprTy.getAsString() + ">(")
157 << FixItHint::CreateInsertion(
158 InsertionLoc: Lexer::getLocForEndOfToken(Loc: LHS->getEndLoc(), Offset: 0,
159 SM: *Result->SourceManager,
160 LangOpts: getLangOpts()),
161 Code: ")");
162 else
163 Diag << FixItHint::CreateInsertion(InsertionLoc: LHS->getBeginLoc(),
164 Code: "(" + WideExprTy.getAsString() + ")");
165 Diag << includeStddefHeader(File: LHS->getBeginLoc());
166 }
167}
168
169void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
170 const Expr *E) {
171 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>(Val: E)) {
177 PointerExpr = BO->getLHS();
178 IndexExpr = BO->getRHS();
179 } else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(Val: E)) {
180 PointerExpr = ASE->getLHS();
181 IndexExpr = ASE->getRHS();
182 } else
183 return;
184
185 if (IndexExpr->getType()->isPointerType())
186 std::swap(a&: PointerExpr, b&: IndexExpr);
187
188 if (!PointerExpr->getType()->isPointerType() ||
189 IndexExpr->getType()->isPointerType())
190 return;
191
192 IndexExpr = IndexExpr->IgnoreParens();
193
194 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 QualType SSizeTy = Context->getPointerDiffType();
202 QualType USizeTy = Context->getSizeType();
203 QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
204 // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t?
205 // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t!
206 StringRef TyAsString =
207 IndexExprType->isSignedIntegerType() ? "ptrdiff_t" : "size_t";
208
209 // So, is size_t actually wider than the result of the multiplication?
210 if (Context->getIntWidth(T: IndexExprType) >= Context->getIntWidth(T: SizeTy))
211 return;
212
213 // Does the index expression look like it might be unintentionally computed
214 // in a narrower-than-wanted type?
215 const Expr *LHS = getLHSOfMulBinOp(E: IndexExpr);
216 if (!LHS)
217 return;
218
219 // Ok, looks like we should diagnose this.
220 diag(E->getBeginLoc(),
221 "result of multiplication in type %0 is used as a pointer offset after "
222 "an implicit widening conversion to type '%1'")
223 << IndexExprType << TyAsString;
224
225 {
226 auto Diag = diag(IndexExpr->getBeginLoc(),
227 "make conversion explicit to silence this warning",
228 DiagnosticIDs::Note)
229 << IndexExpr->getSourceRange();
230 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
231 Loc: IndexExpr->getEndLoc(), Offset: 0, SM: *Result->SourceManager, LangOpts: getLangOpts());
232 if (ShouldUseCXXStaticCast)
233 Diag << FixItHint::CreateInsertion(
234 InsertionLoc: IndexExpr->getBeginLoc(),
235 Code: (Twine("static_cast<") + TyAsString + ">(").str())
236 << FixItHint::CreateInsertion(InsertionLoc: EndLoc, Code: ")");
237 else
238 Diag << FixItHint::CreateInsertion(InsertionLoc: IndexExpr->getBeginLoc(),
239 Code: (Twine("(") + TyAsString + ")(").str())
240 << FixItHint::CreateInsertion(InsertionLoc: EndLoc, Code: ")");
241 Diag << includeStddefHeader(File: IndexExpr->getBeginLoc());
242 }
243
244 {
245 auto Diag =
246 diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",
247 DiagnosticIDs::Note)
248 << LHS->getSourceRange();
249
250 if (ShouldUseCXXStaticCast)
251 Diag << FixItHint::CreateInsertion(
252 InsertionLoc: LHS->getBeginLoc(),
253 Code: (Twine("static_cast<") + TyAsString + ">(").str())
254 << FixItHint::CreateInsertion(
255 InsertionLoc: Lexer::getLocForEndOfToken(Loc: IndexExpr->getEndLoc(), Offset: 0,
256 SM: *Result->SourceManager,
257 LangOpts: getLangOpts()),
258 Code: ")");
259 else
260 Diag << FixItHint::CreateInsertion(InsertionLoc: LHS->getBeginLoc(),
261 Code: (Twine("(") + TyAsString + ")").str());
262 Diag << includeStddefHeader(File: LHS->getBeginLoc());
263 }
264}
265
266void ImplicitWideningOfMultiplicationResultCheck::registerMatchers(
267 MatchFinder *Finder) {
268 Finder->addMatcher(NodeMatch: implicitCastExpr(unless(anyOf(containsErrors(),
269 isInTemplateInstantiation(),
270 isPartOfExplicitCast())),
271 hasCastKind(Kind: CK_IntegralCast))
272 .bind(ID: "x"),
273 Action: this);
274 Finder->addMatcher(
275 NodeMatch: arraySubscriptExpr(unless(isInTemplateInstantiation())).bind(ID: "x"), Action: this);
276 Finder->addMatcher(NodeMatch: binaryOperator(unless(isInTemplateInstantiation()),
277 hasType(InnerMatcher: isAnyPointer()),
278 hasAnyOperatorName("+", "-", "+=", "-="))
279 .bind(ID: "x"),
280 Action: this);
281}
282
283void ImplicitWideningOfMultiplicationResultCheck::check(
284 const MatchFinder::MatchResult &Result) {
285 this->Result = &Result;
286 ShouldUseCXXStaticCast =
287 UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;
288 ShouldUseCXXHeader =
289 UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;
290
291 if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>(ID: "x"))
292 handleImplicitCastExpr(ICE: MatchedDecl);
293 else if (const auto *MatchedDecl =
294 Result.Nodes.getNodeAs<ArraySubscriptExpr>(ID: "x"))
295 handlePointerOffsetting(MatchedDecl);
296 else if (const auto *MatchedDecl =
297 Result.Nodes.getNodeAs<BinaryOperator>(ID: "x"))
298 handlePointerOffsetting(MatchedDecl);
299}
300
301} // namespace clang::tidy::bugprone
302

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

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