1 //===--- ImplicitWideningOfMultiplicationResultCheck.cpp - clang-tidy -----===// 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-exception 6 // 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 16 using namespace clang::ast_matchers; 17 18 namespace clang::tidy::bugprone { 19 20 namespace { 21 AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) { 22 return Node.isPartOfExplicitCast(); 23 } 24 AST_MATCHER(Expr, containsErrors) { return Node.containsErrors(); } 25 } // namespace 26 27 static 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 38 ImplicitWideningOfMultiplicationResultCheck:: 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 50 void ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks( 51 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { 52 IncludeInserter.registerPreprocessor(PP); 53 } 54 55 void 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 64 std::optional<FixItHint> 65 ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader( 66 SourceLocation File) { 67 return IncludeInserter.createIncludeInsertion( 68 Result->SourceManager->getFileID(File), 69 ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>"); 70 } 71 72 void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr( 73 const ImplicitCastExpr *ICE) { 74 ASTContext *Context = Result->Context; 75 76 const Expr *E = ICE->getSubExpr()->IgnoreParens(); 77 QualType Ty = ICE->getType(); 78 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 unsigned SrcWidth = Context->getIntWidth(ETy); 85 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 the 90 // 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 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 computed 103 // 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 else 125 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 suggest 133 // 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 else 163 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(), 164 "(" + WideExprTy.getAsString() + ")"); 165 Diag << includeStddefHeader(LHS->getBeginLoc()); 166 } 167 } 168 169 void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting( 170 const Expr *E) { 171 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 } else 183 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 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 QualType SSizeTy = Context->getPointerDiffType(); 202 QualType USizeTy = Context->getSizeType(); 203 QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy; 204 // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t? 205 // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t! 206 StringRef TyAsString = 207 IndexExprType->isSignedIntegerType() ? "ptrdiff_t" : "size_t"; 208 209 // So, is size_t actually wider than the result of the multiplication? 210 if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy)) 211 return; 212 213 // Does the index expression look like it might be unintentionally computed 214 // in a narrower-than-wanted type? 215 const Expr *LHS = getLHSOfMulBinOp(IndexExpr); 216 if (!LHS) 217 return; 218 219 // Ok, looks like we should diagnose this. 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; 224 225 { 226 auto Diag = diag(IndexExpr->getBeginLoc(), 227 "make conversion explicit to silence this warning", 228 DiagnosticIDs::Note) 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, ")"); 237 else 238 Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(), 239 (Twine("(") + TyAsString + ")(").str()) 240 << FixItHint::CreateInsertion(EndLoc, ")"); 241 Diag << includeStddefHeader(IndexExpr->getBeginLoc()); 242 } 243 244 { 245 auto Diag = 246 diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type", 247 DiagnosticIDs::Note) 248 << LHS->getSourceRange(); 249 250 if (ShouldUseCXXStaticCast) 251 Diag << FixItHint::CreateInsertion( 252 LHS->getBeginLoc(), 253 (Twine("static_cast<") + TyAsString + ">(").str()) 254 << FixItHint::CreateInsertion( 255 Lexer::getLocForEndOfToken(IndexExpr->getEndLoc(), 0, 256 *Result->SourceManager, 257 getLangOpts()), 258 ")"); 259 else 260 Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(), 261 (Twine("(") + TyAsString + ")").str()); 262 Diag << includeStddefHeader(LHS->getBeginLoc()); 263 } 264 } 265 266 void ImplicitWideningOfMultiplicationResultCheck::registerMatchers( 267 MatchFinder *Finder) { 268 Finder->addMatcher(implicitCastExpr(unless(anyOf(containsErrors(), 269 isInTemplateInstantiation(), 270 isPartOfExplicitCast())), 271 hasCastKind(CK_IntegralCast)) 272 .bind("x"), 273 this); 274 Finder->addMatcher( 275 arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this); 276 Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()), 277 hasType(isAnyPointer()), 278 hasAnyOperatorName("+", "-", "+=", "-=")) 279 .bind("x"), 280 this); 281 } 282 283 void ImplicitWideningOfMultiplicationResultCheck::check( 284 const MatchFinder::MatchResult &Result) { 285 this->Result = &Result; 286 ShouldUseCXXStaticCast = 287 UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus; 288 ShouldUseCXXHeader = 289 UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus; 290 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); 299 } 300 301 } // namespace clang::tidy::bugprone 302