clang-tools 20.0.0git
FormatStringConverter.cpp
Go to the documentation of this file.
1//===--- FormatStringConverter.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/// \file
10/// Implementation of the FormatStringConverter class which is used to convert
11/// printf format strings to C++ std::formatter format strings.
12///
13//===----------------------------------------------------------------------===//
14
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/Tooling/FixIt.h"
22#include "llvm/ADT/StringExtras.h"
23#include "llvm/Support/Debug.h"
24
25using namespace clang::ast_matchers;
26using namespace clang::analyze_printf;
27
28namespace clang::tidy::utils {
29using clang::analyze_format_string::ConversionSpecifier;
30
31/// Is the passed type the actual "char" type, whether that be signed or
32/// unsigned, rather than explicit signed char or unsigned char types.
33static bool isRealCharType(const clang::QualType &Ty) {
34 using namespace clang;
35 const Type *DesugaredType = Ty->getUnqualifiedDesugaredType();
36 if (const auto *BT = llvm::dyn_cast<BuiltinType>(DesugaredType))
37 return (BT->getKind() == BuiltinType::Char_U ||
38 BT->getKind() == BuiltinType::Char_S);
39 return false;
40}
41
42/// If possible, return the text name of the signed type that corresponds to the
43/// passed integer type. If the passed type is already signed then its name is
44/// just returned. Only supports BuiltinTypes.
45static std::optional<std::string>
46getCorrespondingSignedTypeName(const clang::QualType &QT) {
47 using namespace clang;
48 const auto UQT = QT.getUnqualifiedType();
49 if (const auto *BT = llvm::dyn_cast<BuiltinType>(UQT)) {
50 switch (BT->getKind()) {
51 case BuiltinType::UChar:
52 case BuiltinType::Char_U:
53 case BuiltinType::SChar:
54 case BuiltinType::Char_S:
55 return "signed char";
56 case BuiltinType::UShort:
57 case BuiltinType::Short:
58 return "short";
59 case BuiltinType::UInt:
60 case BuiltinType::Int:
61 return "int";
62 case BuiltinType::ULong:
63 case BuiltinType::Long:
64 return "long";
65 case BuiltinType::ULongLong:
66 case BuiltinType::LongLong:
67 return "long long";
68 default:
69 llvm::dbgs() << "Unknown corresponding signed type for BuiltinType '"
70 << QT.getAsString() << "'\n";
71 return std::nullopt;
72 }
73 }
74
75 // Deal with fixed-width integer types from <cstdint>. Use std:: prefix only
76 // if the argument type does.
77 const std::string TypeName = UQT.getAsString();
78 StringRef SimplifiedTypeName{TypeName};
79 const bool InStd = SimplifiedTypeName.consume_front("std::");
80 const StringRef Prefix = InStd ? "std::" : "";
81
82 if (SimplifiedTypeName.starts_with("uint") &&
83 SimplifiedTypeName.ends_with("_t"))
84 return (Twine(Prefix) + SimplifiedTypeName.drop_front()).str();
85
86 if (SimplifiedTypeName == "size_t")
87 return (Twine(Prefix) + "ssize_t").str();
88
89 llvm::dbgs() << "Unknown corresponding signed type for non-BuiltinType '"
90 << UQT.getAsString() << "'\n";
91 return std::nullopt;
92}
93
94/// If possible, return the text name of the unsigned type that corresponds to
95/// the passed integer type. If the passed type is already unsigned then its
96/// name is just returned. Only supports BuiltinTypes.
97static std::optional<std::string>
98getCorrespondingUnsignedTypeName(const clang::QualType &QT) {
99 using namespace clang;
100 const auto UQT = QT.getUnqualifiedType();
101 if (const auto *BT = llvm::dyn_cast<BuiltinType>(UQT)) {
102 switch (BT->getKind()) {
103 case BuiltinType::SChar:
104 case BuiltinType::Char_S:
105 case BuiltinType::UChar:
106 case BuiltinType::Char_U:
107 return "unsigned char";
108 case BuiltinType::Short:
109 case BuiltinType::UShort:
110 return "unsigned short";
111 case BuiltinType::Int:
112 case BuiltinType::UInt:
113 return "unsigned int";
114 case BuiltinType::Long:
115 case BuiltinType::ULong:
116 return "unsigned long";
117 case BuiltinType::LongLong:
118 case BuiltinType::ULongLong:
119 return "unsigned long long";
120 default:
121 llvm::dbgs() << "Unknown corresponding unsigned type for BuiltinType '"
122 << UQT.getAsString() << "'\n";
123 return std::nullopt;
124 }
125 }
126
127 // Deal with fixed-width integer types from <cstdint>. Use std:: prefix only
128 // if the argument type does.
129 const std::string TypeName = UQT.getAsString();
130 StringRef SimplifiedTypeName{TypeName};
131 const bool InStd = SimplifiedTypeName.consume_front("std::");
132 const StringRef Prefix = InStd ? "std::" : "";
133
134 if (SimplifiedTypeName.starts_with("int") &&
135 SimplifiedTypeName.ends_with("_t"))
136 return (Twine(Prefix) + "u" + SimplifiedTypeName).str();
137
138 if (SimplifiedTypeName == "ssize_t")
139 return (Twine(Prefix) + "size_t").str();
140 if (SimplifiedTypeName == "ptrdiff_t")
141 return (Twine(Prefix) + "size_t").str();
142
143 llvm::dbgs() << "Unknown corresponding unsigned type for non-BuiltinType '"
144 << UQT.getAsString() << "'\n";
145 return std::nullopt;
146}
147
148static std::optional<std::string>
149castTypeForArgument(ConversionSpecifier::Kind ArgKind,
150 const clang::QualType &QT) {
151 if (ArgKind == ConversionSpecifier::Kind::uArg)
154}
155
156static bool isMatchingSignedness(ConversionSpecifier::Kind ArgKind,
157 const clang::QualType &ArgType) {
158 if (const auto *BT = llvm::dyn_cast<BuiltinType>(ArgType)) {
159 // Unadorned char never matches any expected signedness since it
160 // could be signed or unsigned.
161 const auto ArgTypeKind = BT->getKind();
162 if (ArgTypeKind == BuiltinType::Char_U ||
163 ArgTypeKind == BuiltinType::Char_S)
164 return false;
165 }
166
167 if (ArgKind == ConversionSpecifier::Kind::uArg)
168 return ArgType->isUnsignedIntegerType();
169 return ArgType->isSignedIntegerType();
170}
171
172namespace {
173AST_MATCHER(clang::QualType, isRealChar) {
175}
176} // namespace
177
178static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode) {
179 /// For printf-style functions, the signedness of the type printed is
180 /// indicated by the corresponding type in the format string.
181 /// std::print will determine the signedness from the type of the
182 /// argument. This means that it is necessary to generate a cast in
183 /// StrictMode to ensure that the exact behaviour is maintained.
184 /// However, for templated functions like absl::PrintF and
185 /// fmt::printf, the signedness of the type printed is also taken from
186 /// the actual argument like std::print, so such casts are never
187 /// necessary. printf-style functions are variadic, whereas templated
188 /// ones aren't, so we can use that to distinguish between the two
189 /// cases.
190 if (StrictMode) {
191 const FunctionDecl *FuncDecl = Call->getDirectCallee();
192 assert(FuncDecl);
193 return FuncDecl->isVariadic();
194 }
195 return false;
196}
197
199 const CallExpr *Call,
200 unsigned FormatArgOffset,
201 const Configuration ConfigIn,
202 const LangOptions &LO)
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]->IgnoreImplicitAsWritten());
211 if (!FormatExpr || !FormatExpr->isOrdinary()) {
212 // Function must have a narrow string literal as its first argument.
213 conversionNotPossible("first argument is not a narrow string literal");
214 return;
215 }
216 PrintfFormatString = FormatExpr->getString();
217
218 // Assume that the output will be approximately the same size as the input,
219 // but perhaps with a few escapes expanded.
220 const size_t EstimatedGrowth = 8;
221 StandardFormatString.reserve(PrintfFormatString.size() + EstimatedGrowth);
222 StandardFormatString.push_back('\"');
223
224 const bool IsFreeBsdkPrintf = false;
225
226 using clang::analyze_format_string::ParsePrintfString;
227 ParsePrintfString(*this, PrintfFormatString.data(),
228 PrintfFormatString.data() + PrintfFormatString.size(),
229 LangOpts, Context->getTargetInfo(), IsFreeBsdkPrintf);
230 finalizeFormatText();
231}
232
233void FormatStringConverter::emitAlignment(const PrintfSpecifier &FS,
234 std::string &FormatSpec) {
235 ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind();
236
237 // We only care about alignment if a field width is specified
238 if (FS.getFieldWidth().getHowSpecified() != OptionalAmount::NotSpecified) {
239 if (ArgKind == ConversionSpecifier::sArg) {
240 // Strings are left-aligned by default with std::format, so we only
241 // need to emit an alignment if this one needs to be right aligned.
242 if (!FS.isLeftJustified())
243 FormatSpec.push_back('>');
244 } else {
245 // Numbers are right-aligned by default with std::format, so we only
246 // need to emit an alignment if this one needs to be left aligned.
247 if (FS.isLeftJustified())
248 FormatSpec.push_back('<');
249 }
250 }
251}
252
253void FormatStringConverter::emitSign(const PrintfSpecifier &FS,
254 std::string &FormatSpec) {
255 const ConversionSpecifier Spec = FS.getConversionSpecifier();
256
257 // Ignore on something that isn't numeric. For printf it's would be a
258 // compile-time warning but ignored at runtime, but for std::format it
259 // ought to be a compile-time error.
260 if (Spec.isAnyIntArg() || Spec.isDoubleArg()) {
261 // + is preferred to ' '
262 if (FS.hasPlusPrefix())
263 FormatSpec.push_back('+');
264 else if (FS.hasSpacePrefix())
265 FormatSpec.push_back(' ');
266 }
267}
268
269void FormatStringConverter::emitAlternativeForm(const PrintfSpecifier &FS,
270 std::string &FormatSpec) {
271 if (FS.hasAlternativeForm()) {
272 switch (FS.getConversionSpecifier().getKind()) {
273 case ConversionSpecifier::Kind::aArg:
274 case ConversionSpecifier::Kind::AArg:
275 case ConversionSpecifier::Kind::eArg:
276 case ConversionSpecifier::Kind::EArg:
277 case ConversionSpecifier::Kind::fArg:
278 case ConversionSpecifier::Kind::FArg:
279 case ConversionSpecifier::Kind::gArg:
280 case ConversionSpecifier::Kind::GArg:
281 case ConversionSpecifier::Kind::xArg:
282 case ConversionSpecifier::Kind::XArg:
283 case ConversionSpecifier::Kind::oArg:
284 FormatSpec.push_back('#');
285 break;
286 default:
287 // Alternative forms don't exist for other argument kinds
288 break;
289 }
290 }
291}
292
293void FormatStringConverter::emitFieldWidth(const PrintfSpecifier &FS,
294 std::string &FormatSpec) {
295 {
296 const OptionalAmount FieldWidth = FS.getFieldWidth();
297 switch (FieldWidth.getHowSpecified()) {
298 case OptionalAmount::NotSpecified:
299 break;
300 case OptionalAmount::Constant:
301 FormatSpec.append(llvm::utostr(FieldWidth.getConstantAmount()));
302 break;
303 case OptionalAmount::Arg:
304 FormatSpec.push_back('{');
305 if (FieldWidth.usesPositionalArg()) {
306 // std::format argument identifiers are zero-based, whereas printf
307 // ones are one based.
308 assert(FieldWidth.getPositionalArgIndex() > 0U);
309 FormatSpec.append(llvm::utostr(FieldWidth.getPositionalArgIndex() - 1));
310 }
311 FormatSpec.push_back('}');
312 break;
313 case OptionalAmount::Invalid:
314 break;
315 }
316 }
317}
318
319void FormatStringConverter::emitPrecision(const PrintfSpecifier &FS,
320 std::string &FormatSpec) {
321 const OptionalAmount FieldPrecision = FS.getPrecision();
322 switch (FieldPrecision.getHowSpecified()) {
323 case OptionalAmount::NotSpecified:
324 break;
325 case OptionalAmount::Constant:
326 FormatSpec.push_back('.');
327 FormatSpec.append(llvm::utostr(FieldPrecision.getConstantAmount()));
328 break;
329 case OptionalAmount::Arg:
330 FormatSpec.push_back('.');
331 FormatSpec.push_back('{');
332 if (FieldPrecision.usesPositionalArg()) {
333 // std::format argument identifiers are zero-based, whereas printf
334 // ones are one based.
335 assert(FieldPrecision.getPositionalArgIndex() > 0U);
336 FormatSpec.append(
337 llvm::utostr(FieldPrecision.getPositionalArgIndex() - 1));
338 }
339 FormatSpec.push_back('}');
340 break;
341 case OptionalAmount::Invalid:
342 break;
343 }
344}
345
346void FormatStringConverter::maybeRotateArguments(const PrintfSpecifier &FS) {
347 unsigned ArgCount = 0;
348 const OptionalAmount FieldWidth = FS.getFieldWidth();
349 const OptionalAmount FieldPrecision = FS.getPrecision();
350
351 if (FieldWidth.getHowSpecified() == OptionalAmount::Arg &&
352 !FieldWidth.usesPositionalArg())
353 ++ArgCount;
354 if (FieldPrecision.getHowSpecified() == OptionalAmount::Arg &&
355 !FieldPrecision.usesPositionalArg())
356 ++ArgCount;
357
358 if (ArgCount)
359 ArgRotates.emplace_back(FS.getArgIndex() + ArgsOffset, ArgCount);
360}
361
362void FormatStringConverter::emitStringArgument(unsigned ArgIndex,
363 const Expr *Arg) {
364 // If the argument is the result of a call to std::string::c_str() or
365 // data() with a return type of char then we can remove that call and
366 // pass the std::string directly. We don't want to do so if the return
367 // type is not a char pointer (though it's unlikely that such code would
368 // compile without warnings anyway.) See RedundantStringCStrCheck.
369
370 if (!StringCStrCallExprMatcher) {
371 // Lazily create the matcher
372 const auto StringDecl = type(hasUnqualifiedDesugaredType(recordType(
373 hasDeclaration(cxxRecordDecl(hasName("::std::basic_string"))))));
374 const auto StringExpr = expr(
375 anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));
376
377 StringCStrCallExprMatcher =
378 cxxMemberCallExpr(
379 on(StringExpr.bind("arg")), callee(memberExpr().bind("member")),
380 callee(cxxMethodDecl(hasAnyName("c_str", "data"),
381 returns(pointerType(pointee(isRealChar()))))))
382 .bind("call");
383 }
384
385 auto CStrMatches = match(*StringCStrCallExprMatcher, *Arg, *Context);
386 if (CStrMatches.size() == 1)
387 ArgCStrRemovals.push_back(CStrMatches.front());
388 else if (Arg->getType()->isPointerType()) {
389 const QualType Pointee = Arg->getType()->getPointeeType();
390 // printf is happy to print signed char and unsigned char strings, but
391 // std::format only likes char strings.
392 if (Pointee->isCharType() && !isRealCharType(Pointee))
393 ArgFixes.emplace_back(ArgIndex, "reinterpret_cast<const char *>(");
394 }
395}
396
397bool FormatStringConverter::emitIntegerArgument(
398 ConversionSpecifier::Kind ArgKind, const Expr *Arg, unsigned ArgIndex,
399 std::string &FormatSpec) {
400 const clang::QualType &ArgType = Arg->getType();
401 if (ArgType->isBooleanType()) {
402 // std::format will print bool as either "true" or "false" by default,
403 // but printf prints them as "0" or "1". Be compatible with printf by
404 // requesting decimal output.
405 FormatSpec.push_back('d');
406 } else if (ArgType->isEnumeralType()) {
407 // std::format will try to find a specialization to print the enum
408 // (and probably fail), whereas printf would have just expected it to
409 // be passed as its underlying type. However, printf will have forced
410 // the signedness based on the format string, so we need to do the
411 // same.
412 if (const auto *ET = ArgType->getAs<EnumType>()) {
413 if (const std::optional<std::string> MaybeCastType =
414 castTypeForArgument(ArgKind, ET->getDecl()->getIntegerType()))
415 ArgFixes.emplace_back(
416 ArgIndex, (Twine("static_cast<") + *MaybeCastType + ">(").str());
417 else
418 return conversionNotPossible(
419 (Twine("argument ") + Twine(ArgIndex) + " has unexpected enum type")
420 .str());
421 }
422 } else if (CastMismatchedIntegerTypes &&
423 !isMatchingSignedness(ArgKind, ArgType)) {
424 // printf will happily print an unsigned type as signed if told to.
425 // Even -Wformat doesn't warn for this. std::format will format as
426 // unsigned unless we cast it.
427 if (const std::optional<std::string> MaybeCastType =
428 castTypeForArgument(ArgKind, ArgType))
429 ArgFixes.emplace_back(
430 ArgIndex, (Twine("static_cast<") + *MaybeCastType + ">(").str());
431 else
432 return conversionNotPossible(
433 (Twine("argument ") + Twine(ArgIndex) + " cannot be cast to " +
434 Twine(ArgKind == ConversionSpecifier::Kind::uArg ? "unsigned"
435 : "signed") +
436 " integer type to match format"
437 " specifier and StrictMode is enabled")
438 .str());
439 } else if (isRealCharType(ArgType) || !ArgType->isIntegerType()) {
440 // Only specify integer if the argument is of a different type
441 FormatSpec.push_back('d');
442 }
443 return true;
444}
445
446/// Append the corresponding standard format string type fragment to FormatSpec,
447/// and store any argument fixes for later application.
448/// @returns true on success, false on failure
449bool FormatStringConverter::emitType(const PrintfSpecifier &FS, const Expr *Arg,
450 std::string &FormatSpec) {
451 ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind();
452 switch (ArgKind) {
453 case ConversionSpecifier::Kind::sArg:
454 emitStringArgument(FS.getArgIndex() + ArgsOffset, Arg);
455 break;
456 case ConversionSpecifier::Kind::cArg:
457 // The type must be "c" to get a character unless the type is exactly
458 // char (whether that be signed or unsigned for the target.)
459 if (!isRealCharType(Arg->getType()))
460 FormatSpec.push_back('c');
461 break;
462 case ConversionSpecifier::Kind::dArg:
463 case ConversionSpecifier::Kind::iArg:
464 case ConversionSpecifier::Kind::uArg:
465 if (!emitIntegerArgument(ArgKind, Arg, FS.getArgIndex() + ArgsOffset,
466 FormatSpec))
467 return false;
468 break;
469 case ConversionSpecifier::Kind::pArg: {
470 const clang::QualType &ArgType = Arg->getType();
471 // std::format knows how to format void pointers and nullptrs
472 if (!ArgType->isNullPtrType() && !ArgType->isVoidPointerType())
473 ArgFixes.emplace_back(FS.getArgIndex() + ArgsOffset,
474 "static_cast<const void *>(");
475 break;
476 }
477 case ConversionSpecifier::Kind::xArg:
478 FormatSpec.push_back('x');
479 break;
480 case ConversionSpecifier::Kind::XArg:
481 FormatSpec.push_back('X');
482 break;
483 case ConversionSpecifier::Kind::oArg:
484 FormatSpec.push_back('o');
485 break;
486 case ConversionSpecifier::Kind::aArg:
487 FormatSpec.push_back('a');
488 break;
489 case ConversionSpecifier::Kind::AArg:
490 FormatSpec.push_back('A');
491 break;
492 case ConversionSpecifier::Kind::eArg:
493 FormatSpec.push_back('e');
494 break;
495 case ConversionSpecifier::Kind::EArg:
496 FormatSpec.push_back('E');
497 break;
498 case ConversionSpecifier::Kind::fArg:
499 FormatSpec.push_back('f');
500 break;
501 case ConversionSpecifier::Kind::FArg:
502 FormatSpec.push_back('F');
503 break;
504 case ConversionSpecifier::Kind::gArg:
505 FormatSpec.push_back('g');
506 break;
507 case ConversionSpecifier::Kind::GArg:
508 FormatSpec.push_back('G');
509 break;
510 default:
511 // Something we don't understand
512 return conversionNotPossible((Twine("argument ") +
513 Twine(FS.getArgIndex() + ArgsOffset) +
514 " has an unsupported format specifier")
515 .str());
516 }
517
518 return true;
519}
520
521/// Append the standard format string equivalent of the passed PrintfSpecifier
522/// to StandardFormatString and store any argument fixes for later application.
523/// @returns true on success, false on failure
524bool FormatStringConverter::convertArgument(const PrintfSpecifier &FS,
525 const Expr *Arg,
526 std::string &StandardFormatString) {
527 // The specifier must have an associated argument
528 assert(FS.consumesDataArgument());
529
530 StandardFormatString.push_back('{');
531
532 if (FS.usesPositionalArg()) {
533 // std::format argument identifiers are zero-based, whereas printf ones
534 // are one based.
535 assert(FS.getPositionalArgIndex() > 0U);
536 StandardFormatString.append(llvm::utostr(FS.getPositionalArgIndex() - 1));
537 }
538
539 // std::format format argument parts to potentially emit:
540 // [[fill]align][sign]["#"]["0"][width]["."precision][type]
541 std::string FormatSpec;
542
543 // printf doesn't support specifying the fill character - it's always a
544 // space, so we never need to generate one.
545
546 emitAlignment(FS, FormatSpec);
547 emitSign(FS, FormatSpec);
548 emitAlternativeForm(FS, FormatSpec);
549
550 if (FS.hasLeadingZeros())
551 FormatSpec.push_back('0');
552
553 emitFieldWidth(FS, FormatSpec);
554 emitPrecision(FS, FormatSpec);
555 maybeRotateArguments(FS);
556
557 if (!emitType(FS, Arg, FormatSpec))
558 return false;
559
560 if (!FormatSpec.empty()) {
561 StandardFormatString.push_back(':');
562 StandardFormatString.append(FormatSpec);
563 }
564
565 StandardFormatString.push_back('}');
566 return true;
567}
568
569/// Called for each format specifier by ParsePrintfString.
570bool FormatStringConverter::HandlePrintfSpecifier(const PrintfSpecifier &FS,
571 const char *StartSpecifier,
572 unsigned SpecifierLen,
573 const TargetInfo &Target) {
574
575 const size_t StartSpecifierPos = StartSpecifier - PrintfFormatString.data();
576 assert(StartSpecifierPos + SpecifierLen <= PrintfFormatString.size());
577
578 // Everything before the specifier needs copying verbatim
579 assert(StartSpecifierPos >= PrintfFormatStringPos);
580
581 appendFormatText(StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
582 StartSpecifierPos - PrintfFormatStringPos));
583
584 const ConversionSpecifier::Kind ArgKind =
585 FS.getConversionSpecifier().getKind();
586
587 // Skip over specifier
588 PrintfFormatStringPos = StartSpecifierPos + SpecifierLen;
589 assert(PrintfFormatStringPos <= PrintfFormatString.size());
590
591 FormatStringNeededRewriting = true;
592
593 if (ArgKind == ConversionSpecifier::Kind::nArg) {
594 // std::print doesn't do the equivalent of %n
595 return conversionNotPossible("'%n' is not supported in format string");
596 }
597
598 if (ArgKind == ConversionSpecifier::Kind::PrintErrno) {
599 // std::print doesn't support %m. In theory we could insert a
600 // strerror(errno) parameter (assuming that libc has a thread-safe
601 // implementation, which glibc does), but that would require keeping track
602 // of the input and output parameter indices for position arguments too.
603 return conversionNotPossible("'%m' is not supported in format string");
604 }
605
606 if (ArgKind == ConversionSpecifier::PercentArg) {
607 StandardFormatString.push_back('%');
608 return true;
609 }
610
611 const unsigned ArgIndex = FS.getArgIndex() + ArgsOffset;
612 if (ArgIndex >= NumArgs) {
613 // Argument index out of range. Give up.
614 return conversionNotPossible(
615 (Twine("argument index ") + Twine(ArgIndex) + " is out of range")
616 .str());
617 }
618
619 return convertArgument(FS, Args[ArgIndex]->IgnoreImplicitAsWritten(),
620 StandardFormatString);
621}
622
623/// Called at the very end just before applying fixes to capture the last part
624/// of the format string.
625void FormatStringConverter::finalizeFormatText() {
626 appendFormatText(
627 StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
628 PrintfFormatString.size() - PrintfFormatStringPos));
629 PrintfFormatStringPos = PrintfFormatString.size();
630
631 // It's clearer to convert printf("Hello\r\n"); to std::print("Hello\r\n")
632 // than to std::println("Hello\r");
633 // Use StringRef until C++20 std::string::ends_with() is available.
634 const auto StandardFormatStringRef = StringRef(StandardFormatString);
635 if (Config.AllowTrailingNewlineRemoval &&
636 StandardFormatStringRef.ends_with("\\n") &&
637 !StandardFormatStringRef.ends_with("\\\\n") &&
638 !StandardFormatStringRef.ends_with("\\r\\n")) {
639 UsePrintNewlineFunction = true;
640 FormatStringNeededRewriting = true;
641 StandardFormatString.erase(StandardFormatString.end() - 2,
642 StandardFormatString.end());
643 }
644
645 StandardFormatString.push_back('\"');
646}
647
648/// Append literal parts of the format text, reinstating escapes as required.
649void FormatStringConverter::appendFormatText(const StringRef Text) {
650 for (const char Ch : Text) {
651 if (Ch == '\a')
652 StandardFormatString += "\\a";
653 else if (Ch == '\b')
654 StandardFormatString += "\\b";
655 else if (Ch == '\f')
656 StandardFormatString += "\\f";
657 else if (Ch == '\n')
658 StandardFormatString += "\\n";
659 else if (Ch == '\r')
660 StandardFormatString += "\\r";
661 else if (Ch == '\t')
662 StandardFormatString += "\\t";
663 else if (Ch == '\v')
664 StandardFormatString += "\\v";
665 else if (Ch == '\"')
666 StandardFormatString += "\\\"";
667 else if (Ch == '\\')
668 StandardFormatString += "\\\\";
669 else if (Ch == '{') {
670 StandardFormatString += "{{";
671 FormatStringNeededRewriting = true;
672 } else if (Ch == '}') {
673 StandardFormatString += "}}";
674 FormatStringNeededRewriting = true;
675 } else if (Ch < 32) {
676 StandardFormatString += "\\x";
677 StandardFormatString += llvm::hexdigit(Ch >> 4, true);
678 StandardFormatString += llvm::hexdigit(Ch & 0xf, true);
679 } else
680 StandardFormatString += Ch;
681 }
682}
683
684static std::string withoutCStrReplacement(const BoundNodes &CStrRemovalMatch,
685 ASTContext &Context) {
686 const auto *Arg = CStrRemovalMatch.getNodeAs<Expr>("arg");
687 const auto *Member = CStrRemovalMatch.getNodeAs<MemberExpr>("member");
688 const bool Arrow = Member->isArrow();
689 return Arrow ? utils::fixit::formatDereference(*Arg, Context)
690 : tooling::fixit::getText(*Arg, Context).str();
691}
692
693/// Called by the check when it is ready to apply the fixes.
694void FormatStringConverter::applyFixes(DiagnosticBuilder &Diag,
695 SourceManager &SM) {
696 if (FormatStringNeededRewriting) {
697 Diag << FixItHint::CreateReplacement(
698 CharSourceRange::getTokenRange(FormatExpr->getBeginLoc(),
699 FormatExpr->getEndLoc()),
700 StandardFormatString);
701 }
702
703 // ArgCount is one less than the number of arguments to be rotated.
704 for (auto [ValueArgIndex, ArgCount] : ArgRotates) {
705 assert(ValueArgIndex < NumArgs);
706 assert(ValueArgIndex > ArgCount);
707
708 // First move the value argument to the right place. But if there's a
709 // pending c_str() removal then we must do that at the same time.
710 if (const auto CStrRemovalMatch =
711 std::find_if(ArgCStrRemovals.cbegin(), ArgCStrRemovals.cend(),
712 [ArgStartPos = Args[ValueArgIndex]->getBeginLoc()](
713 const BoundNodes &Match) {
714 // This c_str() removal corresponds to the argument
715 // being moved if they start at the same location.
716 const Expr *CStrArg = Match.getNodeAs<Expr>("arg");
717 return ArgStartPos == CStrArg->getBeginLoc();
718 });
719 CStrRemovalMatch != ArgCStrRemovals.end()) {
720 const std::string ArgText =
721 withoutCStrReplacement(*CStrRemovalMatch, *Context);
722 assert(!ArgText.empty());
723
724 Diag << FixItHint::CreateReplacement(
725 Args[ValueArgIndex - ArgCount]->getSourceRange(), ArgText);
726
727 // That c_str() removal is now dealt with, so we don't need to do it again
728 ArgCStrRemovals.erase(CStrRemovalMatch);
729 } else
730 Diag << tooling::fixit::createReplacement(*Args[ValueArgIndex - ArgCount],
731 *Args[ValueArgIndex], *Context);
732
733 // Now shift down the field width and precision (if either are present) to
734 // accommodate it.
735 for (size_t Offset = 0; Offset < ArgCount; ++Offset)
736 Diag << tooling::fixit::createReplacement(
737 *Args[ValueArgIndex - Offset], *Args[ValueArgIndex - Offset - 1],
738 *Context);
739
740 // Now we need to modify the ArgFix index too so that we fix the right
741 // argument. We don't need to care about the width and precision indices
742 // since they never need fixing.
743 for (auto &ArgFix : ArgFixes) {
744 if (ArgFix.ArgIndex == ValueArgIndex)
745 ArgFix.ArgIndex = ValueArgIndex - ArgCount;
746 }
747 }
748
749 for (const auto &[ArgIndex, Replacement] : ArgFixes) {
750 SourceLocation AfterOtherSide =
751 Lexer::findNextToken(Args[ArgIndex]->getEndLoc(), SM, LangOpts)
752 ->getLocation();
753
754 Diag << FixItHint::CreateInsertion(Args[ArgIndex]->getBeginLoc(),
755 Replacement, true)
756 << FixItHint::CreateInsertion(AfterOtherSide, ")", true);
757 }
758
759 for (const auto &Match : ArgCStrRemovals) {
760 const auto *Call = Match.getNodeAs<CallExpr>("call");
761 const std::string ArgText = withoutCStrReplacement(Match, *Context);
762 if (!ArgText.empty())
763 Diag << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
764 }
765}
766} // namespace clang::tidy::utils
static cl::opt< std::string > Config("config", desc(R"( Specifies a configuration in YAML/JSON format: -config="{Checks:' *', CheckOptions:{x:y}}" When the value is empty, clang-tidy will attempt to find a file named .clang-tidy for each source file in its parent directories. )"), cl::init(""), cl::cat(ClangTidyCategory))
size_t Offset
Declaration of the FormatStringConverter class which is used to convert printf format strings to C++ ...
NodeType Type
std::string Text
llvm::json::Object Args
Definition: Trace.cpp:138
void applyFixes(DiagnosticBuilder &Diag, SourceManager &SM)
Called by the check when it is ready to apply the fixes.
clang::analyze_format_string::ConversionSpecifier ConversionSpecifier
FormatStringConverter(ASTContext *Context, const CallExpr *Call, unsigned FormatArgOffset, Configuration Config, const LangOptions &LO)
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
Definition: TestIndex.cpp:139
std::string formatDereference(const Expr &ExprNode, const ASTContext &Context)
static std::string withoutCStrReplacement(const BoundNodes &CStrRemovalMatch, ASTContext &Context)
static std::optional< std::string > getCorrespondingSignedTypeName(const clang::QualType &QT)
If possible, return the text name of the signed type that corresponds to the passed integer type.
static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode)
static bool isRealCharType(const clang::QualType &Ty)
Is the passed type the actual "char" type, whether that be signed or unsigned, rather than explicit s...
static std::optional< std::string > castTypeForArgument(ConversionSpecifier::Kind ArgKind, const clang::QualType &QT)
static std::optional< std::string > getCorrespondingUnsignedTypeName(const clang::QualType &QT)
If possible, return the text name of the unsigned type that corresponds to the passed integer type.
static bool isMatchingSignedness(ConversionSpecifier::Kind ArgKind, const clang::QualType &ArgType)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static constexpr const char FuncDecl[]