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();
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) {
58 UseCXXStaticCastsInCppSources);
59 Options.
store(Opts,
"UseCXXHeadersInCppSources", UseCXXHeadersInCppSources);
60 Options.
store(Opts,
"IgnoreConstantIntExpr", IgnoreConstantIntExpr);
64std::optional<FixItHint>
65ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(
66 SourceLocation File) {
68 Result->SourceManager->getFileID(File),
69 ShouldUseCXXHeader ?
"<cstddef>" :
"<stddef.h>");
72void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
73 const ImplicitCastExpr *ICE) {
74 ASTContext *Context = Result->Context;
76 const Expr *
E = ICE->getSubExpr()->IgnoreParens();
77 QualType Ty = ICE->getType();
78 QualType ETy =
E->getType();
80 assert(!ETy->isDependentType() && !Ty->isDependentType() &&
81 "Don't expect to ever get here in template Context.");
84 unsigned SrcWidth = Context->getIntWidth(ETy);
85 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 llvm::APSInt WidenedResult = ConstExprResult->extOrTrunc(TypeSize);
96 if (WidenedResult <= llvm::APSInt::getMaxValue(TypeSize,
false) &&
97 WidenedResult >= llvm::APSInt::getMinValue(TypeSize,
false))
109 diag(
E->getBeginLoc(),
"performing an implicit widening conversion to type "
110 "%0 of a multiplication performed in type %1")
111 << Ty <<
E->getType();
114 auto Diag =
diag(
E->getBeginLoc(),
115 "make conversion explicit to silence this warning",
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,
")");
125 Diag << FixItHint::CreateInsertion(
E->getBeginLoc(),
126 "(" + Ty.getAsString() +
")(")
127 << FixItHint::CreateInsertion(EndLoc,
")");
128 Diag << includeStddefHeader(
E->getBeginLoc());
134 if (Ty->isSignedIntegerType() == ETy->isSignedIntegerType())
136 else if (Ty->isSignedIntegerType()) {
137 assert(ETy->isUnsignedIntegerType() &&
138 "Expected source type to be signed.");
139 WideExprTy = Context->getCorrespondingUnsignedType(Ty);
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);
149 auto Diag =
diag(
E->getBeginLoc(),
"perform multiplication in a wider type",
151 << LHS->getSourceRange();
153 if (ShouldUseCXXStaticCast)
154 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
156 WideExprTy.getAsString() +
">(")
157 << FixItHint::CreateInsertion(
158 Lexer::getLocForEndOfToken(LHS->getEndLoc(), 0,
159 *Result->SourceManager,
163 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
164 "(" + WideExprTy.getAsString() +
")");
165 Diag << includeStddefHeader(LHS->getBeginLoc());
169void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
171 ASTContext *Context = Result->Context;
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();
185 if (IndexExpr->getType()->isPointerType())
186 std::swap(PointerExpr, IndexExpr);
188 if (!PointerExpr->getType()->isPointerType() ||
189 IndexExpr->getType()->isPointerType())
192 IndexExpr = IndexExpr->IgnoreParens();
194 QualType IndexExprType = IndexExpr->getType();
198 if (IndexExprType->isDependentType())
201 QualType SSizeTy = Context->getPointerDiffType();
202 QualType USizeTy = Context->getSizeType();
203 QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
206 StringRef TyAsString =
207 IndexExprType->isSignedIntegerType() ?
"ptrdiff_t" :
"size_t";
210 if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))
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;
226 auto Diag =
diag(IndexExpr->getBeginLoc(),
227 "make conversion explicit to silence this warning",
229 << IndexExpr->getSourceRange();
230 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
231 IndexExpr->getEndLoc(), 0, *Result->SourceManager,
getLangOpts());
232 if (ShouldUseCXXStaticCast)
233 Diag << FixItHint::CreateInsertion(
234 IndexExpr->getBeginLoc(),
235 (Twine(
"static_cast<") + TyAsString +
">(").str())
236 << FixItHint::CreateInsertion(EndLoc,
")");
238 Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
239 (Twine(
"(") + TyAsString +
")(").str())
240 << FixItHint::CreateInsertion(EndLoc,
")");
241 Diag << includeStddefHeader(IndexExpr->getBeginLoc());
246 diag(IndexExpr->getBeginLoc(),
"perform multiplication in a wider type",
248 << LHS->getSourceRange();
250 if (ShouldUseCXXStaticCast)
251 Diag << FixItHint::CreateInsertion(
253 (Twine(
"static_cast<") + TyAsString +
">(").str())
254 << FixItHint::CreateInsertion(
255 Lexer::getLocForEndOfToken(IndexExpr->getEndLoc(), 0,
256 *Result->SourceManager,
260 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
261 (Twine(
"(") + TyAsString +
")").str());
262 Diag << includeStddefHeader(LHS->getBeginLoc());
267 MatchFinder *Finder) {
268 Finder->addMatcher(implicitCastExpr(unless(anyOf(containsErrors(),
269 isInTemplateInstantiation(),
270 isPartOfExplicitCast())),
271 hasCastKind(CK_IntegralCast))
275 arraySubscriptExpr(unless(isInTemplateInstantiation())).bind(
"x"),
this);
276 Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
277 hasType(isAnyPointer()),
278 hasAnyOperatorName(
"+",
"-",
"+=",
"-="))
284 const MatchFinder::MatchResult &Result) {
285 this->Result = &Result;
286 ShouldUseCXXStaticCast =
287 UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;
289 UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;
291 if (
const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>(
"x"))
292 handleImplicitCastExpr(MatchedDecl);
293 else if (
const auto *MatchedDecl =
294 Result.Nodes.getNodeAs<ArraySubscriptExpr>(
"x"))
295 handlePointerOffsetting(MatchedDecl);
296 else if (
const auto *MatchedDecl =
297 Result.Nodes.getNodeAs<BinaryOperator>(
"x"))
298 handlePointerOffsetting(MatchedDecl);
llvm::SmallString< 256U > Name
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
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
Override this to register PPCallbacks in the preprocessor.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerPreprocessor(Preprocessor *PP)
Registers this with the Preprocessor PP, must be called before this class is used.
std::optional< FixItHint > createIncludeInsertion(FileID FileID, llvm::StringRef Header)
Creates a Header inclusion directive fixit in the File FileID.
IncludeSorter::IncludeStyle getStyle() const
AST_MATCHER(clang::VarDecl, hasConstantDeclaration)
static const Expr * getLHSOfMulBinOp(const Expr *E)
llvm::StringMap< ClangTidyValue > OptionMap