820 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/// \file10/// Implementation of the FormatStringConverter class which is used to convert11/// printf format strings to C++ std::formatter format strings.12///13//===----------------------------------------------------------------------===//14 15#include "FormatStringConverter.h"16#include "../utils/FixItHintUtils.h"17#include "clang/AST/Expr.h"18#include "clang/ASTMatchers/ASTMatchFinder.h"19#include "clang/Basic/LangOptions.h"20#include "clang/Lex/Lexer.h"21#include "clang/Lex/Preprocessor.h"22#include "clang/Tooling/FixIt.h"23#include "llvm/ADT/StringExtras.h"24#include "llvm/Support/Debug.h"25 26using namespace clang::ast_matchers;27using namespace clang::analyze_printf;28 29namespace clang::tidy::utils {30using clang::analyze_format_string::ConversionSpecifier;31 32/// Is the passed type the actual "char" type, whether that be signed or33/// unsigned, rather than explicit signed char or unsigned char types.34static bool isRealCharType(const clang::QualType &Ty) {35 using namespace clang;36 const Type *DesugaredType = Ty->getUnqualifiedDesugaredType();37 if (const auto *BT = llvm::dyn_cast<BuiltinType>(DesugaredType))38 return (BT->getKind() == BuiltinType::Char_U ||39 BT->getKind() == BuiltinType::Char_S);40 return false;41}42 43/// If possible, return the text name of the signed type that corresponds to the44/// passed integer type. If the passed type is already signed then its name is45/// just returned. Only supports BuiltinTypes.46static std::optional<std::string>47getCorrespondingSignedTypeName(const clang::QualType &QT) {48 using namespace clang;49 const auto UQT = QT.getUnqualifiedType();50 if (const auto *BT = llvm::dyn_cast<BuiltinType>(UQT)) {51 switch (BT->getKind()) {52 case BuiltinType::UChar:53 case BuiltinType::Char_U:54 case BuiltinType::SChar:55 case BuiltinType::Char_S:56 return "signed char";57 case BuiltinType::UShort:58 case BuiltinType::Short:59 return "short";60 case BuiltinType::UInt:61 case BuiltinType::Int:62 return "int";63 case BuiltinType::ULong:64 case BuiltinType::Long:65 return "long";66 case BuiltinType::ULongLong:67 case BuiltinType::LongLong:68 return "long long";69 default:70 llvm::dbgs() << "Unknown corresponding signed type for BuiltinType '"71 << QT.getAsString() << "'\n";72 return std::nullopt;73 }74 }75 76 // Deal with fixed-width integer types from <cstdint>. Use std:: prefix only77 // if the argument type does.78 const std::string TypeName = UQT.getAsString();79 StringRef SimplifiedTypeName{TypeName};80 const bool InStd = SimplifiedTypeName.consume_front("std::");81 const StringRef Prefix = InStd ? "std::" : "";82 83 if (SimplifiedTypeName.starts_with("uint") &&84 SimplifiedTypeName.ends_with("_t"))85 return (Twine(Prefix) + SimplifiedTypeName.drop_front()).str();86 87 if (SimplifiedTypeName == "size_t")88 return (Twine(Prefix) + "ssize_t").str();89 90 llvm::dbgs() << "Unknown corresponding signed type for non-BuiltinType '"91 << UQT.getAsString() << "'\n";92 return std::nullopt;93}94 95/// If possible, return the text name of the unsigned type that corresponds to96/// the passed integer type. If the passed type is already unsigned then its97/// name is just returned. Only supports BuiltinTypes.98static std::optional<std::string>99getCorrespondingUnsignedTypeName(const clang::QualType &QT) {100 using namespace clang;101 const auto UQT = QT.getUnqualifiedType();102 if (const auto *BT = llvm::dyn_cast<BuiltinType>(UQT)) {103 switch (BT->getKind()) {104 case BuiltinType::SChar:105 case BuiltinType::Char_S:106 case BuiltinType::UChar:107 case BuiltinType::Char_U:108 return "unsigned char";109 case BuiltinType::Short:110 case BuiltinType::UShort:111 return "unsigned short";112 case BuiltinType::Int:113 case BuiltinType::UInt:114 return "unsigned int";115 case BuiltinType::Long:116 case BuiltinType::ULong:117 return "unsigned long";118 case BuiltinType::LongLong:119 case BuiltinType::ULongLong:120 return "unsigned long long";121 default:122 llvm::dbgs() << "Unknown corresponding unsigned type for BuiltinType '"123 << UQT.getAsString() << "'\n";124 return std::nullopt;125 }126 }127 128 // Deal with fixed-width integer types from <cstdint>. Use std:: prefix only129 // if the argument type does.130 const std::string TypeName = UQT.getAsString();131 StringRef SimplifiedTypeName{TypeName};132 const bool InStd = SimplifiedTypeName.consume_front("std::");133 const StringRef Prefix = InStd ? "std::" : "";134 135 if (SimplifiedTypeName.starts_with("int") &&136 SimplifiedTypeName.ends_with("_t"))137 return (Twine(Prefix) + "u" + SimplifiedTypeName).str();138 139 if (SimplifiedTypeName == "ssize_t")140 return (Twine(Prefix) + "size_t").str();141 if (SimplifiedTypeName == "ptrdiff_t")142 return (Twine(Prefix) + "size_t").str();143 144 llvm::dbgs() << "Unknown corresponding unsigned type for non-BuiltinType '"145 << UQT.getAsString() << "'\n";146 return std::nullopt;147}148 149static std::optional<std::string>150castTypeForArgument(ConversionSpecifier::Kind ArgKind,151 const clang::QualType &QT) {152 if (ArgKind == ConversionSpecifier::Kind::uArg)153 return getCorrespondingUnsignedTypeName(QT);154 return getCorrespondingSignedTypeName(QT);155}156 157static bool isMatchingSignedness(ConversionSpecifier::Kind ArgKind,158 const clang::QualType &ArgType) {159 if (const auto *BT = llvm::dyn_cast<BuiltinType>(ArgType)) {160 // Unadorned char never matches any expected signedness since it161 // could be signed or unsigned.162 const auto ArgTypeKind = BT->getKind();163 if (ArgTypeKind == BuiltinType::Char_U ||164 ArgTypeKind == BuiltinType::Char_S)165 return false;166 }167 168 if (ArgKind == ConversionSpecifier::Kind::uArg)169 return ArgType->isUnsignedIntegerType();170 return ArgType->isSignedIntegerType();171}172 173namespace {174AST_MATCHER(clang::QualType, isRealChar) {175 return clang::tidy::utils::isRealCharType(Node);176}177} // namespace178 179static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode) {180 /// For printf-style functions, the signedness of the type printed is181 /// indicated by the corresponding type in the format string.182 /// std::print will determine the signedness from the type of the183 /// argument. This means that it is necessary to generate a cast in184 /// StrictMode to ensure that the exact behaviour is maintained.185 /// However, for templated functions like absl::PrintF and186 /// fmt::printf, the signedness of the type printed is also taken from187 /// the actual argument like std::print, so such casts are never188 /// necessary. printf-style functions are variadic, whereas templated189 /// ones aren't, so we can use that to distinguish between the two190 /// cases.191 if (StrictMode) {192 const FunctionDecl *FuncDecl = Call->getDirectCallee();193 assert(FuncDecl);194 return FuncDecl->isVariadic();195 }196 return false;197}198 199FormatStringConverter::FormatStringConverter(200 ASTContext *ContextIn, const CallExpr *Call, unsigned FormatArgOffset,201 const Configuration ConfigIn, const LangOptions &LO, SourceManager &SM,202 Preprocessor &PP)203 : Context(ContextIn), Config(ConfigIn),204 CastMismatchedIntegerTypes(205 castMismatchedIntegerTypes(Call, ConfigIn.StrictMode)),206 Args(Call->getArgs()), NumArgs(Call->getNumArgs()),207 ArgsOffset(FormatArgOffset + 1), LangOpts(LO) {208 assert(ArgsOffset <= NumArgs);209 FormatExpr = llvm::dyn_cast<StringLiteral>(210 Args[FormatArgOffset]->IgnoreUnlessSpelledInSource());211 212 assert(FormatExpr && FormatExpr->isOrdinary());213 214 if (const std::optional<StringRef> MaybeMacroName =215 formatStringContainsUnreplaceableMacro(Call, FormatExpr, SM, PP);216 MaybeMacroName) {217 conversionNotPossible(218 ("format string contains unreplaceable macro '" + *MaybeMacroName + "'")219 .str());220 return;221 }222 223 PrintfFormatString = FormatExpr->getString();224 225 // Assume that the output will be approximately the same size as the input,226 // but perhaps with a few escapes expanded.227 const size_t EstimatedGrowth = 8;228 StandardFormatString.reserve(PrintfFormatString.size() + EstimatedGrowth);229 StandardFormatString.push_back('\"');230 231 const bool IsFreeBsdkPrintf = false;232 233 using clang::analyze_format_string::ParsePrintfString;234 ParsePrintfString(*this, PrintfFormatString.data(),235 PrintfFormatString.data() + PrintfFormatString.size(),236 LangOpts, Context->getTargetInfo(), IsFreeBsdkPrintf);237 finalizeFormatText();238}239 240std::optional<StringRef>241FormatStringConverter::formatStringContainsUnreplaceableMacro(242 const CallExpr *Call, const StringLiteral *FormatExpr, SourceManager &SM,243 Preprocessor &PP) {244 // If a macro invocation surrounds the entire call then we don't want that to245 // inhibit conversion. The whole format string will appear to come from that246 // macro, as will the function call.247 std::optional<StringRef> MaybeSurroundingMacroName;248 if (const SourceLocation BeginCallLoc = Call->getBeginLoc();249 BeginCallLoc.isMacroID())250 MaybeSurroundingMacroName =251 Lexer::getImmediateMacroName(BeginCallLoc, SM, PP.getLangOpts());252 253 for (auto I = FormatExpr->tokloc_begin(), E = FormatExpr->tokloc_end();254 I != E; ++I) {255 const SourceLocation &TokenLoc = *I;256 if (TokenLoc.isMacroID()) {257 const StringRef MacroName =258 Lexer::getImmediateMacroName(TokenLoc, SM, PP.getLangOpts());259 260 if (MaybeSurroundingMacroName != MacroName) {261 // glibc uses __PRI64_PREFIX and __PRIPTR_PREFIX to define the prefixes262 // for types that change size so we must look for multiple prefixes.263 if (!MacroName.starts_with("PRI") && !MacroName.starts_with("__PRI"))264 return MacroName;265 266 const SourceLocation TokenSpellingLoc = SM.getSpellingLoc(TokenLoc);267 const OptionalFileEntryRef MaybeFileEntry =268 SM.getFileEntryRefForID(SM.getFileID(TokenSpellingLoc));269 if (!MaybeFileEntry)270 return MacroName;271 272 HeaderSearch &HS = PP.getHeaderSearchInfo();273 // Check if the file is a system header274 if (!isSystem(HS.getFileDirFlavor(*MaybeFileEntry)) ||275 llvm::sys::path::filename(MaybeFileEntry->getName()) !=276 "inttypes.h")277 return MacroName;278 }279 }280 }281 return std::nullopt;282}283 284void FormatStringConverter::emitAlignment(const PrintfSpecifier &FS,285 std::string &FormatSpec) {286 const ConversionSpecifier::Kind ArgKind =287 FS.getConversionSpecifier().getKind();288 289 // We only care about alignment if a field width is specified290 if (FS.getFieldWidth().getHowSpecified() != OptionalAmount::NotSpecified) {291 if (ArgKind == ConversionSpecifier::sArg) {292 // Strings are left-aligned by default with std::format, so we only293 // need to emit an alignment if this one needs to be right aligned.294 if (!FS.isLeftJustified())295 FormatSpec.push_back('>');296 } else {297 // Numbers are right-aligned by default with std::format, so we only298 // need to emit an alignment if this one needs to be left aligned.299 if (FS.isLeftJustified())300 FormatSpec.push_back('<');301 }302 }303}304 305void FormatStringConverter::emitSign(const PrintfSpecifier &FS,306 std::string &FormatSpec) {307 const ConversionSpecifier Spec = FS.getConversionSpecifier();308 309 // Ignore on something that isn't numeric. For printf it's would be a310 // compile-time warning but ignored at runtime, but for std::format it311 // ought to be a compile-time error.312 if (Spec.isAnyIntArg() || Spec.isDoubleArg()) {313 // + is preferred to ' '314 if (FS.hasPlusPrefix())315 FormatSpec.push_back('+');316 else if (FS.hasSpacePrefix())317 FormatSpec.push_back(' ');318 }319}320 321void FormatStringConverter::emitAlternativeForm(const PrintfSpecifier &FS,322 std::string &FormatSpec) {323 if (FS.hasAlternativeForm()) {324 switch (FS.getConversionSpecifier().getKind()) {325 case ConversionSpecifier::Kind::aArg:326 case ConversionSpecifier::Kind::AArg:327 case ConversionSpecifier::Kind::eArg:328 case ConversionSpecifier::Kind::EArg:329 case ConversionSpecifier::Kind::fArg:330 case ConversionSpecifier::Kind::FArg:331 case ConversionSpecifier::Kind::gArg:332 case ConversionSpecifier::Kind::GArg:333 case ConversionSpecifier::Kind::xArg:334 case ConversionSpecifier::Kind::XArg:335 case ConversionSpecifier::Kind::oArg:336 FormatSpec.push_back('#');337 break;338 default:339 // Alternative forms don't exist for other argument kinds340 break;341 }342 }343}344 345void FormatStringConverter::emitFieldWidth(const PrintfSpecifier &FS,346 std::string &FormatSpec) {347 {348 const OptionalAmount FieldWidth = FS.getFieldWidth();349 switch (FieldWidth.getHowSpecified()) {350 case OptionalAmount::NotSpecified:351 break;352 case OptionalAmount::Constant:353 FormatSpec.append(llvm::utostr(FieldWidth.getConstantAmount()));354 break;355 case OptionalAmount::Arg:356 FormatSpec.push_back('{');357 if (FieldWidth.usesPositionalArg()) {358 // std::format argument identifiers are zero-based, whereas printf359 // ones are one based.360 assert(FieldWidth.getPositionalArgIndex() > 0U);361 FormatSpec.append(llvm::utostr(FieldWidth.getPositionalArgIndex() - 1));362 }363 FormatSpec.push_back('}');364 break;365 case OptionalAmount::Invalid:366 break;367 }368 }369}370 371void FormatStringConverter::emitPrecision(const PrintfSpecifier &FS,372 std::string &FormatSpec) {373 const OptionalAmount FieldPrecision = FS.getPrecision();374 switch (FieldPrecision.getHowSpecified()) {375 case OptionalAmount::NotSpecified:376 break;377 case OptionalAmount::Constant:378 FormatSpec.push_back('.');379 FormatSpec.append(llvm::utostr(FieldPrecision.getConstantAmount()));380 break;381 case OptionalAmount::Arg:382 FormatSpec.push_back('.');383 FormatSpec.push_back('{');384 if (FieldPrecision.usesPositionalArg()) {385 // std::format argument identifiers are zero-based, whereas printf386 // ones are one based.387 assert(FieldPrecision.getPositionalArgIndex() > 0U);388 FormatSpec.append(389 llvm::utostr(FieldPrecision.getPositionalArgIndex() - 1));390 }391 FormatSpec.push_back('}');392 break;393 case OptionalAmount::Invalid:394 break;395 }396}397 398void FormatStringConverter::maybeRotateArguments(const PrintfSpecifier &FS) {399 unsigned ArgCount = 0;400 const OptionalAmount FieldWidth = FS.getFieldWidth();401 const OptionalAmount FieldPrecision = FS.getPrecision();402 403 if (FieldWidth.getHowSpecified() == OptionalAmount::Arg &&404 !FieldWidth.usesPositionalArg())405 ++ArgCount;406 if (FieldPrecision.getHowSpecified() == OptionalAmount::Arg &&407 !FieldPrecision.usesPositionalArg())408 ++ArgCount;409 410 if (ArgCount)411 ArgRotates.emplace_back(FS.getArgIndex() + ArgsOffset, ArgCount);412}413 414void FormatStringConverter::emitStringArgument(unsigned ArgIndex,415 const Expr *Arg) {416 // If the argument is the result of a call to std::string::c_str() or417 // data() with a return type of char then we can remove that call and418 // pass the std::string directly. We don't want to do so if the return419 // type is not a char pointer (though it's unlikely that such code would420 // compile without warnings anyway.) See RedundantStringCStrCheck.421 422 if (!StringCStrCallExprMatcher) {423 // Lazily create the matcher424 const auto StringDecl = type(hasUnqualifiedDesugaredType(recordType(425 hasDeclaration(cxxRecordDecl(hasName("::std::basic_string"))))));426 const auto StringExpr = expr(427 anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));428 429 StringCStrCallExprMatcher =430 cxxMemberCallExpr(431 on(StringExpr.bind("arg")), callee(memberExpr().bind("member")),432 callee(cxxMethodDecl(hasAnyName("c_str", "data"),433 returns(pointerType(pointee(isRealChar()))))))434 .bind("call");435 }436 437 auto CStrMatches = match(*StringCStrCallExprMatcher, *Arg, *Context);438 if (CStrMatches.size() == 1)439 ArgCStrRemovals.push_back(CStrMatches.front());440 else if (Arg->getType()->isPointerType()) {441 const QualType Pointee = Arg->getType()->getPointeeType();442 // printf is happy to print signed char and unsigned char strings, but443 // std::format only likes char strings.444 if (Pointee->isCharType() && !isRealCharType(Pointee))445 ArgFixes.emplace_back(ArgIndex, "reinterpret_cast<const char *>(");446 }447}448 449bool FormatStringConverter::emitIntegerArgument(450 ConversionSpecifier::Kind ArgKind, const Expr *Arg, unsigned ArgIndex,451 std::string &FormatSpec) {452 const clang::QualType &ArgType = Arg->getType();453 if (ArgType->isBooleanType()) {454 // std::format will print bool as either "true" or "false" by default,455 // but printf prints them as "0" or "1". Be compatible with printf by456 // requesting decimal output.457 FormatSpec.push_back('d');458 } else if (ArgType->isEnumeralType()) {459 // std::format will try to find a specialization to print the enum460 // (and probably fail), whereas printf would have just expected it to461 // be passed as its underlying type. However, printf will have forced462 // the signedness based on the format string, so we need to do the463 // same.464 if (const auto *ED = ArgType->getAsEnumDecl()) {465 if (const std::optional<std::string> MaybeCastType =466 castTypeForArgument(ArgKind, ED->getIntegerType()))467 ArgFixes.emplace_back(468 ArgIndex, (Twine("static_cast<") + *MaybeCastType + ">(").str());469 else470 return conversionNotPossible(471 (Twine("argument ") + Twine(ArgIndex) + " has unexpected enum type")472 .str());473 }474 } else if (CastMismatchedIntegerTypes &&475 !isMatchingSignedness(ArgKind, ArgType)) {476 // printf will happily print an unsigned type as signed if told to.477 // Even -Wformat doesn't warn for this. std::format will format as478 // unsigned unless we cast it.479 if (const std::optional<std::string> MaybeCastType =480 castTypeForArgument(ArgKind, ArgType))481 ArgFixes.emplace_back(482 ArgIndex, (Twine("static_cast<") + *MaybeCastType + ">(").str());483 else484 return conversionNotPossible(485 (Twine("argument ") + Twine(ArgIndex) + " cannot be cast to " +486 Twine(ArgKind == ConversionSpecifier::Kind::uArg ? "unsigned"487 : "signed") +488 " integer type to match format"489 " specifier and StrictMode is enabled")490 .str());491 } else if (isRealCharType(ArgType) || !ArgType->isIntegerType()) {492 // Only specify integer if the argument is of a different type493 FormatSpec.push_back('d');494 }495 return true;496}497 498/// Append the corresponding standard format string type fragment to FormatSpec,499/// and store any argument fixes for later application.500/// @returns true on success, false on failure501bool FormatStringConverter::emitType(const PrintfSpecifier &FS, const Expr *Arg,502 std::string &FormatSpec) {503 const ConversionSpecifier::Kind ArgKind =504 FS.getConversionSpecifier().getKind();505 switch (ArgKind) {506 case ConversionSpecifier::Kind::sArg:507 emitStringArgument(FS.getArgIndex() + ArgsOffset, Arg);508 break;509 case ConversionSpecifier::Kind::cArg:510 // The type must be "c" to get a character unless the type is exactly511 // char (whether that be signed or unsigned for the target.)512 if (!isRealCharType(Arg->getType()))513 FormatSpec.push_back('c');514 break;515 case ConversionSpecifier::Kind::dArg:516 case ConversionSpecifier::Kind::iArg:517 case ConversionSpecifier::Kind::uArg:518 if (!emitIntegerArgument(ArgKind, Arg, FS.getArgIndex() + ArgsOffset,519 FormatSpec))520 return false;521 break;522 case ConversionSpecifier::Kind::pArg: {523 const clang::QualType &ArgType = Arg->getType();524 // std::format knows how to format void pointers and nullptrs525 if (!ArgType->isNullPtrType() && !ArgType->isVoidPointerType())526 ArgFixes.emplace_back(FS.getArgIndex() + ArgsOffset,527 "static_cast<const void *>(");528 break;529 }530 case ConversionSpecifier::Kind::xArg:531 FormatSpec.push_back('x');532 break;533 case ConversionSpecifier::Kind::XArg:534 FormatSpec.push_back('X');535 break;536 case ConversionSpecifier::Kind::oArg:537 FormatSpec.push_back('o');538 break;539 case ConversionSpecifier::Kind::aArg:540 FormatSpec.push_back('a');541 break;542 case ConversionSpecifier::Kind::AArg:543 FormatSpec.push_back('A');544 break;545 case ConversionSpecifier::Kind::eArg:546 FormatSpec.push_back('e');547 break;548 case ConversionSpecifier::Kind::EArg:549 FormatSpec.push_back('E');550 break;551 case ConversionSpecifier::Kind::fArg:552 FormatSpec.push_back('f');553 break;554 case ConversionSpecifier::Kind::FArg:555 FormatSpec.push_back('F');556 break;557 case ConversionSpecifier::Kind::gArg:558 FormatSpec.push_back('g');559 break;560 case ConversionSpecifier::Kind::GArg:561 FormatSpec.push_back('G');562 break;563 default:564 // Something we don't understand565 return conversionNotPossible((Twine("argument ") +566 Twine(FS.getArgIndex() + ArgsOffset) +567 " has an unsupported format specifier")568 .str());569 }570 571 return true;572}573 574/// Append the standard format string equivalent of the passed PrintfSpecifier575/// to StandardFormatString and store any argument fixes for later application.576/// @returns true on success, false on failure577bool FormatStringConverter::convertArgument(const PrintfSpecifier &FS,578 const Expr *Arg,579 std::string &StandardFormatString) {580 // The specifier must have an associated argument581 assert(FS.consumesDataArgument());582 583 StandardFormatString.push_back('{');584 585 if (FS.usesPositionalArg()) {586 // std::format argument identifiers are zero-based, whereas printf ones587 // are one based.588 assert(FS.getPositionalArgIndex() > 0U);589 StandardFormatString.append(llvm::utostr(FS.getPositionalArgIndex() - 1));590 }591 592 // std::format format argument parts to potentially emit:593 // [[fill]align][sign]["#"]["0"][width]["."precision][type]594 std::string FormatSpec;595 596 // printf doesn't support specifying the fill character - it's always a597 // space, so we never need to generate one.598 599 emitAlignment(FS, FormatSpec);600 emitSign(FS, FormatSpec);601 emitAlternativeForm(FS, FormatSpec);602 603 if (FS.hasLeadingZeros())604 FormatSpec.push_back('0');605 606 emitFieldWidth(FS, FormatSpec);607 emitPrecision(FS, FormatSpec);608 maybeRotateArguments(FS);609 610 if (!emitType(FS, Arg, FormatSpec))611 return false;612 613 if (!FormatSpec.empty()) {614 StandardFormatString.push_back(':');615 StandardFormatString.append(FormatSpec);616 }617 618 StandardFormatString.push_back('}');619 return true;620}621 622/// Called for each format specifier by ParsePrintfString.623bool FormatStringConverter::HandlePrintfSpecifier(const PrintfSpecifier &FS,624 const char *StartSpecifier,625 unsigned SpecifierLen,626 const TargetInfo &Target) {627 const size_t StartSpecifierPos = StartSpecifier - PrintfFormatString.data();628 assert(StartSpecifierPos + SpecifierLen <= PrintfFormatString.size());629 630 // Everything before the specifier needs copying verbatim631 assert(StartSpecifierPos >= PrintfFormatStringPos);632 633 appendFormatText(StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,634 StartSpecifierPos - PrintfFormatStringPos));635 636 const ConversionSpecifier::Kind ArgKind =637 FS.getConversionSpecifier().getKind();638 639 // Skip over specifier640 PrintfFormatStringPos = StartSpecifierPos + SpecifierLen;641 assert(PrintfFormatStringPos <= PrintfFormatString.size());642 643 FormatStringNeededRewriting = true;644 645 if (ArgKind == ConversionSpecifier::Kind::nArg) {646 // std::print doesn't do the equivalent of %n647 return conversionNotPossible("'%n' is not supported in format string");648 }649 650 if (ArgKind == ConversionSpecifier::Kind::PrintErrno) {651 // std::print doesn't support %m. In theory we could insert a652 // strerror(errno) parameter (assuming that libc has a thread-safe653 // implementation, which glibc does), but that would require keeping track654 // of the input and output parameter indices for position arguments too.655 return conversionNotPossible("'%m' is not supported in format string");656 }657 658 if (ArgKind == ConversionSpecifier::PercentArg) {659 StandardFormatString.push_back('%');660 return true;661 }662 663 const unsigned ArgIndex = FS.getArgIndex() + ArgsOffset;664 if (ArgIndex >= NumArgs) {665 // Argument index out of range. Give up.666 return conversionNotPossible(667 (Twine("argument index ") + Twine(ArgIndex) + " is out of range")668 .str());669 }670 671 return convertArgument(FS, Args[ArgIndex]->IgnoreImplicitAsWritten(),672 StandardFormatString);673}674 675/// Called at the very end just before applying fixes to capture the last part676/// of the format string.677void FormatStringConverter::finalizeFormatText() {678 appendFormatText(679 StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,680 PrintfFormatString.size() - PrintfFormatStringPos));681 PrintfFormatStringPos = PrintfFormatString.size();682 683 // It's clearer to convert printf("Hello\r\n"); to std::print("Hello\r\n")684 // than to std::println("Hello\r");685 // Use StringRef until C++20 std::string::ends_with() is available.686 const auto StandardFormatStringRef = StringRef(StandardFormatString);687 if (Config.AllowTrailingNewlineRemoval &&688 StandardFormatStringRef.ends_with("\\n") &&689 !StandardFormatStringRef.ends_with("\\\\n") &&690 !StandardFormatStringRef.ends_with("\\r\\n")) {691 UsePrintNewlineFunction = true;692 FormatStringNeededRewriting = true;693 StandardFormatString.erase(StandardFormatString.end() - 2,694 StandardFormatString.end());695 }696 697 StandardFormatString.push_back('\"');698}699 700/// Append literal parts of the format text, reinstating escapes as required.701void FormatStringConverter::appendFormatText(const StringRef Text) {702 for (const char Ch : Text) {703 const auto UCh = static_cast<unsigned char>(Ch);704 if (Ch == '\a')705 StandardFormatString += "\\a";706 else if (Ch == '\b')707 StandardFormatString += "\\b";708 else if (Ch == '\f')709 StandardFormatString += "\\f";710 else if (Ch == '\n')711 StandardFormatString += "\\n";712 else if (Ch == '\r')713 StandardFormatString += "\\r";714 else if (Ch == '\t')715 StandardFormatString += "\\t";716 else if (Ch == '\v')717 StandardFormatString += "\\v";718 else if (Ch == '\"')719 StandardFormatString += "\\\"";720 else if (Ch == '\\')721 StandardFormatString += "\\\\";722 else if (Ch == '{') {723 StandardFormatString += "{{";724 FormatStringNeededRewriting = true;725 } else if (Ch == '}') {726 StandardFormatString += "}}";727 FormatStringNeededRewriting = true;728 } else if (UCh < 32) {729 StandardFormatString += "\\x";730 StandardFormatString += llvm::hexdigit(UCh >> 4, true);731 StandardFormatString += llvm::hexdigit(UCh & 0xf, true);732 } else733 StandardFormatString += Ch;734 }735}736 737static std::string withoutCStrReplacement(const BoundNodes &CStrRemovalMatch,738 ASTContext &Context) {739 const auto *Arg = CStrRemovalMatch.getNodeAs<Expr>("arg");740 const auto *Member = CStrRemovalMatch.getNodeAs<MemberExpr>("member");741 const bool Arrow = Member->isArrow();742 return Arrow ? utils::fixit::formatDereference(*Arg, Context)743 : tooling::fixit::getText(*Arg, Context).str();744}745 746/// Called by the check when it is ready to apply the fixes.747void FormatStringConverter::applyFixes(DiagnosticBuilder &Diag,748 SourceManager &SM) {749 if (FormatStringNeededRewriting) {750 Diag << FixItHint::CreateReplacement(751 CharSourceRange::getTokenRange(FormatExpr->getBeginLoc(),752 FormatExpr->getEndLoc()),753 StandardFormatString);754 }755 756 // ArgCount is one less than the number of arguments to be rotated.757 for (auto [ValueArgIndex, ArgCount] : ArgRotates) {758 assert(ValueArgIndex < NumArgs);759 assert(ValueArgIndex > ArgCount);760 761 // First move the value argument to the right place. But if there's a762 // pending c_str() removal then we must do that at the same time.763 if (const auto CStrRemovalMatch =764 std::find_if(ArgCStrRemovals.cbegin(), ArgCStrRemovals.cend(),765 [ArgStartPos = Args[ValueArgIndex]->getBeginLoc()](766 const BoundNodes &Match) {767 // This c_str() removal corresponds to the argument768 // being moved if they start at the same location.769 const Expr *CStrArg = Match.getNodeAs<Expr>("arg");770 return ArgStartPos == CStrArg->getBeginLoc();771 });772 CStrRemovalMatch != ArgCStrRemovals.end()) {773 const std::string ArgText =774 withoutCStrReplacement(*CStrRemovalMatch, *Context);775 assert(!ArgText.empty());776 777 Diag << FixItHint::CreateReplacement(778 Args[ValueArgIndex - ArgCount]->getSourceRange(), ArgText);779 780 // That c_str() removal is now dealt with, so we don't need to do it again781 ArgCStrRemovals.erase(CStrRemovalMatch);782 } else783 Diag << tooling::fixit::createReplacement(*Args[ValueArgIndex - ArgCount],784 *Args[ValueArgIndex], *Context);785 786 // Now shift down the field width and precision (if either are present) to787 // accommodate it.788 for (size_t Offset = 0; Offset < ArgCount; ++Offset)789 Diag << tooling::fixit::createReplacement(790 *Args[ValueArgIndex - Offset], *Args[ValueArgIndex - Offset - 1],791 *Context);792 793 // Now we need to modify the ArgFix index too so that we fix the right794 // argument. We don't need to care about the width and precision indices795 // since they never need fixing.796 for (auto &ArgFix : ArgFixes) {797 if (ArgFix.ArgIndex == ValueArgIndex)798 ArgFix.ArgIndex = ValueArgIndex - ArgCount;799 }800 }801 802 for (const auto &[ArgIndex, Replacement] : ArgFixes) {803 const SourceLocation AfterOtherSide =804 Lexer::findNextToken(Args[ArgIndex]->getEndLoc(), SM, LangOpts)805 ->getLocation();806 807 Diag << FixItHint::CreateInsertion(Args[ArgIndex]->getBeginLoc(),808 Replacement, true)809 << FixItHint::CreateInsertion(AfterOtherSide, ")", true);810 }811 812 for (const auto &Match : ArgCStrRemovals) {813 const auto *Call = Match.getNodeAs<CallExpr>("call");814 const std::string ArgText = withoutCStrReplacement(Match, *Context);815 if (!ArgText.empty())816 Diag << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);817 }818}819} // namespace clang::tidy::utils820