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