123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- //===--- ImplicitWideningOfMultiplicationResultCheck.cpp - clang-tidy -----===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "ImplicitWideningOfMultiplicationResultCheck.h"
- #include "clang/AST/ASTContext.h"
- #include "clang/ASTMatchers/ASTMatchFinder.h"
- #include <optional>
- using namespace clang::ast_matchers;
- namespace clang {
- namespace {
- AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {
- return Node.isPartOfExplicitCast();
- }
- } // namespace
- } // namespace clang
- namespace clang::tidy::bugprone {
- static const Expr *getLHSOfMulBinOp(const Expr *E) {
- assert(E == E->IgnoreParens() && "Already skipped all parens!");
- // Is this: long r = int(x) * int(y); ?
- // FIXME: shall we skip brackets/casts/etc?
- const auto *BO = dyn_cast<BinaryOperator>(E);
- if (!BO || BO->getOpcode() != BO_Mul)
- // FIXME: what about: long r = int(x) + (int(y) * int(z)); ?
- return nullptr;
- return BO->getLHS()->IgnoreParens();
- }
- ImplicitWideningOfMultiplicationResultCheck::
- ImplicitWideningOfMultiplicationResultCheck(StringRef Name,
- ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context),
- UseCXXStaticCastsInCppSources(
- Options.get("UseCXXStaticCastsInCppSources", true)),
- UseCXXHeadersInCppSources(Options.get("UseCXXHeadersInCppSources", true)),
- IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
- utils::IncludeSorter::IS_LLVM),
- areDiagsSelfContained()) {}
- void ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks(
- const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
- IncludeInserter.registerPreprocessor(PP);
- }
- void ImplicitWideningOfMultiplicationResultCheck::storeOptions(
- ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "UseCXXStaticCastsInCppSources",
- UseCXXStaticCastsInCppSources);
- Options.store(Opts, "UseCXXHeadersInCppSources", UseCXXHeadersInCppSources);
- Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
- }
- std::optional<FixItHint>
- ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(
- SourceLocation File) {
- return IncludeInserter.createIncludeInsertion(
- Result->SourceManager->getFileID(File),
- ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>");
- }
- void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
- const ImplicitCastExpr *ICE) {
- ASTContext *Context = Result->Context;
- const Expr *E = ICE->getSubExpr()->IgnoreParens();
- QualType Ty = ICE->getType();
- QualType ETy = E->getType();
- assert(!ETy->isDependentType() && !Ty->isDependentType() &&
- "Don't expect to ever get here in template Context.");
- // This must be a widening cast. Else we do not care.
- unsigned SrcWidth = Context->getIntWidth(ETy);
- unsigned TgtWidth = Context->getIntWidth(Ty);
- if (TgtWidth <= SrcWidth)
- return;
- // Does the index expression look like it might be unintentionally computed
- // in a narrower-than-wanted type?
- const Expr *LHS = getLHSOfMulBinOp(E);
- if (!LHS)
- return;
- // Ok, looks like we should diagnose this.
- diag(E->getBeginLoc(), "performing an implicit widening conversion to type "
- "%0 of a multiplication performed in type %1")
- << Ty << E->getType();
- {
- auto Diag = diag(E->getBeginLoc(),
- "make conversion explicit to silence this warning",
- DiagnosticIDs::Note)
- << E->getSourceRange();
- if (ShouldUseCXXStaticCast)
- Diag << FixItHint::CreateInsertion(
- E->getBeginLoc(), "static_cast<" + Ty.getAsString() + ">(")
- << FixItHint::CreateInsertion(E->getEndLoc(), ")");
- else
- Diag << FixItHint::CreateInsertion(E->getBeginLoc(),
- "(" + Ty.getAsString() + ")(")
- << FixItHint::CreateInsertion(E->getEndLoc(), ")");
- Diag << includeStddefHeader(E->getBeginLoc());
- }
- QualType WideExprTy;
- // Get Ty of the same signedness as ExprTy, because we only want to suggest
- // to widen the computation, but not change it's signedness domain.
- if (Ty->isSignedIntegerType() == ETy->isSignedIntegerType())
- WideExprTy = Ty;
- else if (Ty->isSignedIntegerType()) {
- assert(ETy->isUnsignedIntegerType() &&
- "Expected source type to be signed.");
- WideExprTy = Context->getCorrespondingUnsignedType(Ty);
- } else {
- assert(Ty->isUnsignedIntegerType() &&
- "Expected target type to be unsigned.");
- assert(ETy->isSignedIntegerType() &&
- "Expected source type to be unsigned.");
- WideExprTy = Context->getCorrespondingSignedType(Ty);
- }
- {
- auto Diag = diag(E->getBeginLoc(), "perform multiplication in a wider type",
- DiagnosticIDs::Note)
- << LHS->getSourceRange();
- if (ShouldUseCXXStaticCast)
- Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
- "static_cast<" +
- WideExprTy.getAsString() + ">(")
- << FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
- else
- Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
- "(" + WideExprTy.getAsString() + ")");
- Diag << includeStddefHeader(LHS->getBeginLoc());
- }
- }
- void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
- const Expr *E) {
- ASTContext *Context = Result->Context;
- // We are looking for a pointer offset operation,
- // with one hand being a pointer, and another one being an offset.
- const Expr *PointerExpr, *IndexExpr;
- if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
- PointerExpr = BO->getLHS();
- IndexExpr = BO->getRHS();
- } else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
- PointerExpr = ASE->getLHS();
- IndexExpr = ASE->getRHS();
- } else
- return;
- if (IndexExpr->getType()->isPointerType())
- std::swap(PointerExpr, IndexExpr);
- if (!PointerExpr->getType()->isPointerType() ||
- IndexExpr->getType()->isPointerType())
- return;
- IndexExpr = IndexExpr->IgnoreParens();
- QualType IndexExprType = IndexExpr->getType();
- // If the index expression's type is not known (i.e. we are in a template),
- // we can't do anything here.
- if (IndexExprType->isDependentType())
- return;
- QualType SSizeTy = Context->getPointerDiffType();
- QualType USizeTy = Context->getSizeType();
- QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
- // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t?
- // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t!
- StringRef TyAsString =
- IndexExprType->isSignedIntegerType() ? "ptrdiff_t" : "size_t";
- // So, is size_t actually wider than the result of the multiplication?
- if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))
- return;
- // Does the index expression look like it might be unintentionally computed
- // in a narrower-than-wanted type?
- const Expr *LHS = getLHSOfMulBinOp(IndexExpr);
- if (!LHS)
- return;
- // Ok, looks like we should diagnose this.
- diag(E->getBeginLoc(),
- "result of multiplication in type %0 is used as a pointer offset after "
- "an implicit widening conversion to type '%1'")
- << IndexExprType << TyAsString;
- {
- auto Diag = diag(IndexExpr->getBeginLoc(),
- "make conversion explicit to silence this warning",
- DiagnosticIDs::Note)
- << IndexExpr->getSourceRange();
- if (ShouldUseCXXStaticCast)
- Diag << FixItHint::CreateInsertion(
- IndexExpr->getBeginLoc(),
- (Twine("static_cast<") + TyAsString + ">(").str())
- << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
- else
- Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
- (Twine("(") + TyAsString + ")(").str())
- << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
- Diag << includeStddefHeader(IndexExpr->getBeginLoc());
- }
- {
- auto Diag =
- diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",
- DiagnosticIDs::Note)
- << LHS->getSourceRange();
- if (ShouldUseCXXStaticCast)
- Diag << FixItHint::CreateInsertion(
- LHS->getBeginLoc(),
- (Twine("static_cast<") + TyAsString + ">(").str())
- << FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
- else
- Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
- (Twine("(") + TyAsString + ")").str());
- Diag << includeStddefHeader(LHS->getBeginLoc());
- }
- }
- void ImplicitWideningOfMultiplicationResultCheck::registerMatchers(
- MatchFinder *Finder) {
- Finder->addMatcher(implicitCastExpr(unless(anyOf(isInTemplateInstantiation(),
- isPartOfExplicitCast())),
- hasCastKind(CK_IntegralCast))
- .bind("x"),
- this);
- Finder->addMatcher(
- arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);
- Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
- hasType(isAnyPointer()),
- hasAnyOperatorName("+", "-", "+=", "-="))
- .bind("x"),
- this);
- }
- void ImplicitWideningOfMultiplicationResultCheck::check(
- const MatchFinder::MatchResult &Result) {
- this->Result = &Result;
- ShouldUseCXXStaticCast =
- UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;
- ShouldUseCXXHeader =
- UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;
- if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>("x"))
- handleImplicitCastExpr(MatchedDecl);
- else if (const auto *MatchedDecl =
- Result.Nodes.getNodeAs<ArraySubscriptExpr>("x"))
- handlePointerOffsetting(MatchedDecl);
- else if (const auto *MatchedDecl =
- Result.Nodes.getNodeAs<BinaryOperator>("x"))
- handlePointerOffsetting(MatchedDecl);
- }
- } // namespace clang::tidy::bugprone
|