303 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "ImplicitWideningOfMultiplicationResultCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/ASTMatchers/ASTMatchersMacros.h"13#include "clang/Lex/Lexer.h"14#include <optional>15 16using namespace clang::ast_matchers;17 18namespace clang::tidy::bugprone {19 20namespace {21AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {22 return Node.isPartOfExplicitCast();23}24AST_MATCHER(Expr, containsErrors) { return Node.containsErrors(); }25} // namespace26 27static 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 38ImplicitWideningOfMultiplicationResultCheck::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 IgnoreConstantIntExpr(Options.get("IgnoreConstantIntExpr", false)),46 IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",47 utils::IncludeSorter::IS_LLVM),48 areDiagsSelfContained()) {}49 50void ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks(51 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {52 IncludeInserter.registerPreprocessor(PP);53}54 55void ImplicitWideningOfMultiplicationResultCheck::storeOptions(56 ClangTidyOptions::OptionMap &Opts) {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());62}63 64std::optional<FixItHint>65ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(66 SourceLocation File) {67 return IncludeInserter.createIncludeInsertion(68 Result->SourceManager->getFileID(File),69 ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>");70}71 72void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(73 const ImplicitCastExpr *ICE) {74 const ASTContext *Context = Result->Context;75 76 const Expr *E = ICE->getSubExpr()->IgnoreParens();77 const QualType Ty = ICE->getType();78 const QualType ETy = E->getType();79 80 assert(!ETy->isDependentType() && !Ty->isDependentType() &&81 "Don't expect to ever get here in template Context.");82 83 // This must be a widening cast. Else we do not care.84 const unsigned SrcWidth = Context->getIntWidth(ETy);85 const unsigned TgtWidth = Context->getIntWidth(Ty);86 if (TgtWidth <= SrcWidth)87 return;88 89 // Is the expression a compile-time constexpr that we know can fit in the90 // source type?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))98 return;99 }100 }101 102 // Does the index expression look like it might be unintentionally computed103 // in a narrower-than-wanted type?104 const Expr *LHS = getLHSOfMulBinOp(E);105 if (!LHS)106 return;107 108 // Ok, looks like we should diagnose this.109 diag(E->getBeginLoc(), "performing an implicit widening conversion to type "110 "%0 of a multiplication performed in type %1")111 << Ty << E->getType();112 113 {114 auto Diag = diag(E->getBeginLoc(),115 "make conversion explicit to silence this warning",116 DiagnosticIDs::Note)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, ")");124 else125 Diag << FixItHint::CreateInsertion(E->getBeginLoc(),126 "(" + Ty.getAsString() + ")(")127 << FixItHint::CreateInsertion(EndLoc, ")");128 Diag << includeStddefHeader(E->getBeginLoc());129 }130 131 QualType WideExprTy;132 // Get Ty of the same signedness as ExprTy, because we only want to suggest133 // to widen the computation, but not change it's signedness domain.134 if (Ty->isSignedIntegerType() == ETy->isSignedIntegerType())135 WideExprTy = Ty;136 else if (Ty->isSignedIntegerType()) {137 assert(ETy->isUnsignedIntegerType() &&138 "Expected source type to be signed.");139 WideExprTy = Context->getCorrespondingUnsignedType(Ty);140 } else {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);146 }147 148 {149 auto Diag = diag(E->getBeginLoc(), "perform multiplication in a wider type",150 DiagnosticIDs::Note)151 << LHS->getSourceRange();152 153 if (ShouldUseCXXStaticCast)154 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),155 "static_cast<" +156 WideExprTy.getAsString() + ">(")157 << FixItHint::CreateInsertion(158 Lexer::getLocForEndOfToken(LHS->getEndLoc(), 0,159 *Result->SourceManager,160 getLangOpts()),161 ")");162 else163 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),164 "(" + WideExprTy.getAsString() + ")");165 Diag << includeStddefHeader(LHS->getBeginLoc());166 }167}168 169void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(170 const Expr *E) {171 const ASTContext *Context = Result->Context;172 173 // We are looking for a pointer offset operation,174 // with one hand being a pointer, and another one being an offset.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();182 } else183 return;184 185 if (IndexExpr->getType()->isPointerType())186 std::swap(PointerExpr, IndexExpr);187 188 if (!PointerExpr->getType()->isPointerType() ||189 IndexExpr->getType()->isPointerType())190 return;191 192 IndexExpr = IndexExpr->IgnoreParens();193 194 const QualType IndexExprType = IndexExpr->getType();195 196 // If the index expression's type is not known (i.e. we are in a template),197 // we can't do anything here.198 if (IndexExprType->isDependentType())199 return;200 201 const QualType SSizeTy = Context->getPointerDiffType();202 const QualType USizeTy = Context->getSizeType();203 const QualType SizeTy =204 IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;205 // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t?206 // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t!207 const StringRef TyAsString =208 IndexExprType->isSignedIntegerType() ? "ptrdiff_t" : "size_t";209 210 // So, is size_t actually wider than the result of the multiplication?211 if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))212 return;213 214 // Does the index expression look like it might be unintentionally computed215 // in a narrower-than-wanted type?216 const Expr *LHS = getLHSOfMulBinOp(IndexExpr);217 if (!LHS)218 return;219 220 // Ok, looks like we should diagnose this.221 diag(E->getBeginLoc(),222 "result of multiplication in type %0 is used as a pointer offset after "223 "an implicit widening conversion to type '%1'")224 << IndexExprType << TyAsString;225 226 {227 auto Diag = diag(IndexExpr->getBeginLoc(),228 "make conversion explicit to silence this warning",229 DiagnosticIDs::Note)230 << IndexExpr->getSourceRange();231 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(232 IndexExpr->getEndLoc(), 0, *Result->SourceManager, getLangOpts());233 if (ShouldUseCXXStaticCast)234 Diag << FixItHint::CreateInsertion(235 IndexExpr->getBeginLoc(),236 (Twine("static_cast<") + TyAsString + ">(").str())237 << FixItHint::CreateInsertion(EndLoc, ")");238 else239 Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),240 (Twine("(") + TyAsString + ")(").str())241 << FixItHint::CreateInsertion(EndLoc, ")");242 Diag << includeStddefHeader(IndexExpr->getBeginLoc());243 }244 245 {246 auto Diag =247 diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",248 DiagnosticIDs::Note)249 << LHS->getSourceRange();250 251 if (ShouldUseCXXStaticCast)252 Diag << FixItHint::CreateInsertion(253 LHS->getBeginLoc(),254 (Twine("static_cast<") + TyAsString + ">(").str())255 << FixItHint::CreateInsertion(256 Lexer::getLocForEndOfToken(IndexExpr->getEndLoc(), 0,257 *Result->SourceManager,258 getLangOpts()),259 ")");260 else261 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),262 (Twine("(") + TyAsString + ")").str());263 Diag << includeStddefHeader(LHS->getBeginLoc());264 }265}266 267void ImplicitWideningOfMultiplicationResultCheck::registerMatchers(268 MatchFinder *Finder) {269 Finder->addMatcher(implicitCastExpr(unless(anyOf(containsErrors(),270 isInTemplateInstantiation(),271 isPartOfExplicitCast())),272 hasCastKind(CK_IntegralCast))273 .bind("x"),274 this);275 Finder->addMatcher(276 arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);277 Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),278 hasType(isAnyPointer()),279 hasAnyOperatorName("+", "-", "+=", "-="))280 .bind("x"),281 this);282}283 284void ImplicitWideningOfMultiplicationResultCheck::check(285 const MatchFinder::MatchResult &Result) {286 this->Result = &Result;287 ShouldUseCXXStaticCast =288 UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;289 ShouldUseCXXHeader =290 UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;291 292 if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>("x"))293 handleImplicitCastExpr(MatchedDecl);294 else if (const auto *MatchedDecl =295 Result.Nodes.getNodeAs<ArraySubscriptExpr>("x"))296 handlePointerOffsetting(MatchedDecl);297 else if (const auto *MatchedDecl =298 Result.Nodes.getNodeAs<BinaryOperator>("x"))299 handlePointerOffsetting(MatchedDecl);300}301 302} // namespace clang::tidy::bugprone303