//===--- 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 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(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 ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader( SourceLocation File) { return IncludeInserter.createIncludeInsertion( Result->SourceManager->getFileID(File), ShouldUseCXXHeader ? "" : ""); } 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(E)) { PointerExpr = BO->getLHS(); IndexExpr = BO->getRHS(); } else if (const auto *ASE = dyn_cast(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("x")) handleImplicitCastExpr(MatchedDecl); else if (const auto *MatchedDecl = Result.Nodes.getNodeAs("x")) handlePointerOffsetting(MatchedDecl); else if (const auto *MatchedDecl = Result.Nodes.getNodeAs("x")) handlePointerOffsetting(MatchedDecl); } } // namespace clang::tidy::bugprone