10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/ASTMatchers/ASTMatchersMacros.h"
13#include "clang/Lex/Lexer.h"
21AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {
22 return Node.isPartOfExplicitCast();
24AST_MATCHER(Expr, containsErrors) {
return Node.containsErrors(); }
28 assert(E == E->IgnoreParens() &&
"Already skipped all parens!");
31 const auto *BO = dyn_cast<BinaryOperator>(E);
32 if (!BO || BO->getOpcode() != BO_Mul)
35 return BO->getLHS()->IgnoreParens();
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()) {}
51 const SourceManager &SM, Preprocessor *
PP, Preprocessor *ModuleExpanderPP) {
52 IncludeInserter.registerPreprocessor(
PP);
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());
64std::optional<FixItHint>
65ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(
66 SourceLocation File) {
67 return IncludeInserter.createIncludeInsertion(
68 Result->SourceManager->getFileID(File),
69 ShouldUseCXXHeader ?
"<cstddef>" :
"<stddef.h>");
72void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
73 const ImplicitCastExpr *ICE) {
74 const ASTContext *Context = Result->Context;
76 const Expr *E = ICE->getSubExpr()->IgnoreParens();
77 const QualType Ty = ICE->getType();
78 const QualType ETy = E->getType();
80 assert(!ETy->isDependentType() && !Ty->isDependentType() &&
81 "Don't expect to ever get here in template Context.");
84 const unsigned SrcWidth = Context->getIntWidth(ETy);
85 const unsigned TgtWidth = Context->getIntWidth(Ty);
86 if (TgtWidth <= SrcWidth)
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))
107 const Expr *RHS = cast<BinaryOperator>(E)->getRHS()->IgnoreParens();
110 diag(E->getBeginLoc(),
"performing an implicit widening conversion to type "
111 "%0 of a multiplication performed in type %1")
112 << Ty << E->getType();
115 const auto Diag = diag(E->getBeginLoc(),
116 "make conversion explicit to silence this warning",
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,
")");
126 Diag << FixItHint::CreateInsertion(E->getBeginLoc(),
127 "(" + Ty.getAsString() +
")(")
128 << FixItHint::CreateInsertion(EndLoc,
")");
129 Diag << includeStddefHeader(E->getBeginLoc());
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) {
147 }
else if (Ty->isSignedIntegerType()) {
148 WideExprTy = Context->getCorrespondingUnsignedType(Ty);
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);
158 diag(E->getBeginLoc(),
"perform multiplication in a wider type",
160 << LHS->getSourceRange();
162 if (ShouldUseCXXStaticCast)
163 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
165 WideExprTy.getAsString() +
">(")
166 << FixItHint::CreateInsertion(
167 Lexer::getLocForEndOfToken(LHS->getEndLoc(), 0,
168 *Result->SourceManager,
172 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
173 "(" + WideExprTy.getAsString() +
")");
174 Diag << includeStddefHeader(LHS->getBeginLoc());
178void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
180 const ASTContext *Context = Result->Context;
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();
195 if (IndexExpr->getType()->isPointerType())
196 std::swap(PointerExpr, IndexExpr);
198 if (!PointerExpr->getType()->isPointerType() ||
199 IndexExpr->getType()->isPointerType())
202 IndexExpr = IndexExpr->IgnoreParens();
204 const QualType IndexExprType = IndexExpr->getType();
208 if (IndexExprType->isDependentType())
211 const QualType SSizeTy = Context->getPointerDiffType();
212 const QualType USizeTy = Context->getSizeType();
213 const QualType SizeTy =
214 IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
217 const StringRef TyAsString =
218 IndexExprType->isSignedIntegerType() ?
"ptrdiff_t" :
"size_t";
221 if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))
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;
237 const auto Diag = diag(IndexExpr->getBeginLoc(),
238 "make conversion explicit to silence this warning",
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,
")");
249 Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
250 (Twine(
"(") + TyAsString +
")(").str())
251 << FixItHint::CreateInsertion(EndLoc,
")");
252 Diag << includeStddefHeader(IndexExpr->getBeginLoc());
257 diag(IndexExpr->getBeginLoc(),
"perform multiplication in a wider type",
259 << LHS->getSourceRange();
261 if (ShouldUseCXXStaticCast)
262 Diag << FixItHint::CreateInsertion(
264 (Twine(
"static_cast<") + TyAsString +
">(").str())
265 << FixItHint::CreateInsertion(
266 Lexer::getLocForEndOfToken(IndexExpr->getEndLoc(), 0,
267 *Result->SourceManager,
271 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
272 (Twine(
"(") + TyAsString +
")").str());
273 Diag << includeStddefHeader(LHS->getBeginLoc());
278 MatchFinder *Finder) {
279 Finder->addMatcher(implicitCastExpr(unless(anyOf(containsErrors(),
280 isInTemplateInstantiation(),
281 isPartOfExplicitCast())),
282 hasCastKind(CK_IntegralCast))
286 arraySubscriptExpr(unless(isInTemplateInstantiation())).bind(
"x"),
this);
287 Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
288 hasType(isAnyPointer()),
289 hasAnyOperatorName(
"+",
"-",
"+=",
"-="))
295 const MatchFinder::MatchResult &Result) {
296 this->Result = &Result;
297 ShouldUseCXXStaticCast =
298 UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;
300 UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;
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);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
ImplicitWideningOfMultiplicationResultCheck(StringRef Name, ClangTidyContext *Context)
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerMatchers(ast_matchers::MatchFinder *Finder) 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