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"
26using namespace clang::analyze_printf;
29using clang::analyze_format_string::ConversionSpecifier;
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);
45static std::optional<std::string>
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:
56 case BuiltinType::UShort:
57 case BuiltinType::Short:
59 case BuiltinType::UInt:
60 case BuiltinType::Int:
62 case BuiltinType::ULong:
63 case BuiltinType::Long:
65 case BuiltinType::ULongLong:
66 case BuiltinType::LongLong:
69 llvm::dbgs() <<
"Unknown corresponding signed type for BuiltinType '"
70 << QT.getAsString() <<
"'\n";
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::" :
"";
82 if (SimplifiedTypeName.starts_with(
"uint") &&
83 SimplifiedTypeName.ends_with(
"_t"))
84 return (Twine(Prefix) + SimplifiedTypeName.drop_front()).str();
86 if (SimplifiedTypeName ==
"size_t")
87 return (Twine(Prefix) +
"ssize_t").str();
89 llvm::dbgs() <<
"Unknown corresponding signed type for non-BuiltinType '"
90 << UQT.getAsString() <<
"'\n";
97static std::optional<std::string>
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";
121 llvm::dbgs() <<
"Unknown corresponding unsigned type for BuiltinType '"
122 << UQT.getAsString() <<
"'\n";
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::" :
"";
134 if (SimplifiedTypeName.starts_with(
"int") &&
135 SimplifiedTypeName.ends_with(
"_t"))
136 return (Twine(Prefix) +
"u" + SimplifiedTypeName).str();
138 if (SimplifiedTypeName ==
"ssize_t")
139 return (Twine(Prefix) +
"size_t").str();
140 if (SimplifiedTypeName ==
"ptrdiff_t")
141 return (Twine(Prefix) +
"size_t").str();
143 llvm::dbgs() <<
"Unknown corresponding unsigned type for non-BuiltinType '"
144 << UQT.getAsString() <<
"'\n";
148static std::optional<std::string>
150 const clang::QualType &QT) {
151 if (ArgKind == ConversionSpecifier::Kind::uArg)
157 const clang::QualType &ArgType) {
158 if (
const auto *BT = llvm::dyn_cast<BuiltinType>(ArgType)) {
161 const auto ArgTypeKind = BT->getKind();
162 if (ArgTypeKind == BuiltinType::Char_U ||
163 ArgTypeKind == BuiltinType::Char_S)
167 if (ArgKind == ConversionSpecifier::Kind::uArg)
168 return ArgType->isUnsignedIntegerType();
169 return ArgType->isSignedIntegerType();
191 const FunctionDecl *FuncDecl = Call->getDirectCallee();
193 return FuncDecl->isVariadic();
199 const CallExpr *Call,
200 unsigned FormatArgOffset,
202 const LangOptions &LO)
203 : Context(ContextIn),
205 Args(Call->getArgs()), NumArgs(Call->getNumArgs()),
206 ArgsOffset(FormatArgOffset + 1), LangOpts(LO) {
207 assert(ArgsOffset <= NumArgs);
208 FormatExpr = llvm::dyn_cast<StringLiteral>(
209 Args[FormatArgOffset]->IgnoreImplicitAsWritten());
211 if (!FormatExpr->isOrdinary())
213 PrintfFormatString = FormatExpr->getString();
217 const size_t EstimatedGrowth = 8;
218 StandardFormatString.reserve(PrintfFormatString.size() + EstimatedGrowth);
219 StandardFormatString.push_back(
'\"');
221 const bool IsFreeBsdkPrintf =
false;
223 using clang::analyze_format_string::ParsePrintfString;
224 ParsePrintfString(*
this, PrintfFormatString.data(),
225 PrintfFormatString.data() + PrintfFormatString.size(),
226 LangOpts, Context->getTargetInfo(), IsFreeBsdkPrintf);
227 finalizeFormatText();
230void FormatStringConverter::emitAlignment(
const PrintfSpecifier &FS,
231 std::string &FormatSpec) {
232 ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind();
235 if (FS.getFieldWidth().getHowSpecified() != OptionalAmount::NotSpecified) {
236 if (ArgKind == ConversionSpecifier::sArg) {
239 if (!FS.isLeftJustified())
240 FormatSpec.push_back(
'>');
244 if (FS.isLeftJustified())
245 FormatSpec.push_back(
'<');
250void FormatStringConverter::emitSign(
const PrintfSpecifier &FS,
251 std::string &FormatSpec) {
257 if (Spec.isAnyIntArg() || Spec.isDoubleArg()) {
259 if (FS.hasPlusPrefix())
260 FormatSpec.push_back(
'+');
261 else if (FS.hasSpacePrefix())
262 FormatSpec.push_back(
' ');
266void FormatStringConverter::emitAlternativeForm(
const PrintfSpecifier &FS,
267 std::string &FormatSpec) {
268 if (FS.hasAlternativeForm()) {
269 switch (FS.getConversionSpecifier().getKind()) {
270 case ConversionSpecifier::Kind::aArg:
271 case ConversionSpecifier::Kind::AArg:
272 case ConversionSpecifier::Kind::eArg:
273 case ConversionSpecifier::Kind::EArg:
274 case ConversionSpecifier::Kind::fArg:
275 case ConversionSpecifier::Kind::FArg:
276 case ConversionSpecifier::Kind::gArg:
277 case ConversionSpecifier::Kind::GArg:
278 case ConversionSpecifier::Kind::xArg:
279 case ConversionSpecifier::Kind::XArg:
280 case ConversionSpecifier::Kind::oArg:
281 FormatSpec.push_back(
'#');
290void FormatStringConverter::emitFieldWidth(
const PrintfSpecifier &FS,
291 std::string &FormatSpec) {
293 const OptionalAmount FieldWidth = FS.getFieldWidth();
294 switch (FieldWidth.getHowSpecified()) {
295 case OptionalAmount::NotSpecified:
297 case OptionalAmount::Constant:
298 FormatSpec.append(llvm::utostr(FieldWidth.getConstantAmount()));
300 case OptionalAmount::Arg:
301 FormatSpec.push_back(
'{');
302 if (FieldWidth.usesPositionalArg()) {
305 assert(FieldWidth.getPositionalArgIndex() > 0U);
306 FormatSpec.append(llvm::utostr(FieldWidth.getPositionalArgIndex() - 1));
308 FormatSpec.push_back(
'}');
310 case OptionalAmount::Invalid:
316void FormatStringConverter::emitPrecision(
const PrintfSpecifier &FS,
317 std::string &FormatSpec) {
318 const OptionalAmount FieldPrecision = FS.getPrecision();
319 switch (FieldPrecision.getHowSpecified()) {
320 case OptionalAmount::NotSpecified:
322 case OptionalAmount::Constant:
323 FormatSpec.push_back(
'.');
324 FormatSpec.append(llvm::utostr(FieldPrecision.getConstantAmount()));
326 case OptionalAmount::Arg:
327 FormatSpec.push_back(
'.');
328 FormatSpec.push_back(
'{');
329 if (FieldPrecision.usesPositionalArg()) {
332 assert(FieldPrecision.getPositionalArgIndex() > 0U);
334 llvm::utostr(FieldPrecision.getPositionalArgIndex() - 1));
336 FormatSpec.push_back(
'}');
338 case OptionalAmount::Invalid:
343void FormatStringConverter::maybeRotateArguments(
const PrintfSpecifier &FS) {
344 unsigned ArgCount = 0;
345 const OptionalAmount FieldWidth = FS.getFieldWidth();
346 const OptionalAmount FieldPrecision = FS.getPrecision();
348 if (FieldWidth.getHowSpecified() == OptionalAmount::Arg &&
349 !FieldWidth.usesPositionalArg())
351 if (FieldPrecision.getHowSpecified() == OptionalAmount::Arg &&
352 !FieldPrecision.usesPositionalArg())
356 ArgRotates.emplace_back(FS.getArgIndex() + ArgsOffset, ArgCount);
359void FormatStringConverter::emitStringArgument(
unsigned ArgIndex,
367 if (!StringCStrCallExprMatcher) {
369 const auto StringDecl = type(hasUnqualifiedDesugaredType(recordType(
370 hasDeclaration(cxxRecordDecl(hasName(
"::std::basic_string"))))));
371 const auto StringExpr = expr(
372 anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));
374 StringCStrCallExprMatcher =
376 on(StringExpr.bind(
"arg")), callee(memberExpr().bind(
"member")),
377 callee(cxxMethodDecl(hasAnyName(
"c_str",
"data"),
378 returns(pointerType(pointee(isRealChar()))))))
382 auto CStrMatches =
match(*StringCStrCallExprMatcher, *Arg, *Context);
383 if (CStrMatches.size() == 1)
384 ArgCStrRemovals.push_back(CStrMatches.front());
385 else if (Arg->getType()->isPointerType()) {
386 const QualType
Pointee = Arg->getType()->getPointeeType();
390 ArgFixes.emplace_back(ArgIndex,
"reinterpret_cast<const char *>(");
394bool FormatStringConverter::emitIntegerArgument(
395 ConversionSpecifier::Kind ArgKind,
const Expr *Arg,
unsigned ArgIndex,
396 std::string &FormatSpec) {
397 const clang::QualType &ArgType = Arg->getType();
398 if (ArgType->isBooleanType()) {
402 FormatSpec.push_back(
'd');
403 }
else if (ArgType->isEnumeralType()) {
409 if (
const auto *ET = ArgType->getAs<EnumType>()) {
410 if (
const std::optional<std::string> MaybeCastType =
412 ArgFixes.emplace_back(
413 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
415 return conversionNotPossible(
416 (Twine(
"argument ") + Twine(ArgIndex) +
" has unexpected enum type")
419 }
else if (CastMismatchedIntegerTypes &&
424 if (
const std::optional<std::string> MaybeCastType =
426 ArgFixes.emplace_back(
427 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
429 return conversionNotPossible(
430 (Twine(
"argument ") + Twine(ArgIndex) +
" cannot be cast to " +
431 Twine(ArgKind == ConversionSpecifier::Kind::uArg ?
"unsigned"
433 " integer type to match format"
434 " specifier and StrictMode is enabled")
436 }
else if (
isRealCharType(ArgType) || !ArgType->isIntegerType()) {
438 FormatSpec.push_back(
'd');
446bool FormatStringConverter::emitType(
const PrintfSpecifier &FS,
const Expr *Arg,
447 std::string &FormatSpec) {
448 ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind();
450 case ConversionSpecifier::Kind::sArg:
451 emitStringArgument(FS.getArgIndex() + ArgsOffset, Arg);
453 case ConversionSpecifier::Kind::cArg:
457 FormatSpec.push_back(
'c');
459 case ConversionSpecifier::Kind::dArg:
460 case ConversionSpecifier::Kind::iArg:
461 case ConversionSpecifier::Kind::uArg:
462 if (!emitIntegerArgument(ArgKind, Arg, FS.getArgIndex() + ArgsOffset,
466 case ConversionSpecifier::Kind::pArg: {
467 const clang::QualType &ArgType = Arg->getType();
469 if (!ArgType->isNullPtrType() && !ArgType->isVoidPointerType())
470 ArgFixes.emplace_back(FS.getArgIndex() + ArgsOffset,
471 "static_cast<const void *>(");
474 case ConversionSpecifier::Kind::xArg:
475 FormatSpec.push_back(
'x');
477 case ConversionSpecifier::Kind::XArg:
478 FormatSpec.push_back(
'X');
480 case ConversionSpecifier::Kind::oArg:
481 FormatSpec.push_back(
'o');
483 case ConversionSpecifier::Kind::aArg:
484 FormatSpec.push_back(
'a');
486 case ConversionSpecifier::Kind::AArg:
487 FormatSpec.push_back(
'A');
489 case ConversionSpecifier::Kind::eArg:
490 FormatSpec.push_back(
'e');
492 case ConversionSpecifier::Kind::EArg:
493 FormatSpec.push_back(
'E');
495 case ConversionSpecifier::Kind::fArg:
496 FormatSpec.push_back(
'f');
498 case ConversionSpecifier::Kind::FArg:
499 FormatSpec.push_back(
'F');
501 case ConversionSpecifier::Kind::gArg:
502 FormatSpec.push_back(
'g');
504 case ConversionSpecifier::Kind::GArg:
505 FormatSpec.push_back(
'G');
509 return conversionNotPossible((Twine(
"argument ") +
510 Twine(FS.getArgIndex() + ArgsOffset) +
511 " has an unsupported format specifier")
521bool FormatStringConverter::convertArgument(
const PrintfSpecifier &FS,
523 std::string &StandardFormatString) {
525 assert(FS.consumesDataArgument());
527 StandardFormatString.push_back(
'{');
529 if (FS.usesPositionalArg()) {
532 assert(FS.getPositionalArgIndex() > 0U);
533 StandardFormatString.append(llvm::utostr(FS.getPositionalArgIndex() - 1));
538 std::string FormatSpec;
543 emitAlignment(FS, FormatSpec);
544 emitSign(FS, FormatSpec);
545 emitAlternativeForm(FS, FormatSpec);
547 if (FS.hasLeadingZeros())
548 FormatSpec.push_back(
'0');
550 emitFieldWidth(FS, FormatSpec);
551 emitPrecision(FS, FormatSpec);
552 maybeRotateArguments(FS);
554 if (!emitType(FS, Arg, FormatSpec))
557 if (!FormatSpec.empty()) {
558 StandardFormatString.push_back(
':');
559 StandardFormatString.append(FormatSpec);
562 StandardFormatString.push_back(
'}');
567bool FormatStringConverter::HandlePrintfSpecifier(
const PrintfSpecifier &FS,
568 const char *StartSpecifier,
569 unsigned SpecifierLen,
570 const TargetInfo &Target) {
572 const size_t StartSpecifierPos = StartSpecifier - PrintfFormatString.data();
573 assert(StartSpecifierPos + SpecifierLen <= PrintfFormatString.size());
576 assert(StartSpecifierPos >= PrintfFormatStringPos);
578 appendFormatText(StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
579 StartSpecifierPos - PrintfFormatStringPos));
581 const ConversionSpecifier::Kind ArgKind =
582 FS.getConversionSpecifier().getKind();
585 PrintfFormatStringPos = StartSpecifierPos + SpecifierLen;
586 assert(PrintfFormatStringPos <= PrintfFormatString.size());
588 FormatStringNeededRewriting =
true;
590 if (ArgKind == ConversionSpecifier::Kind::nArg) {
592 return conversionNotPossible(
"'%n' is not supported in format string");
595 if (ArgKind == ConversionSpecifier::Kind::PrintErrno) {
600 return conversionNotPossible(
"'%m' is not supported in format string");
603 if (ArgKind == ConversionSpecifier::PercentArg) {
604 StandardFormatString.push_back(
'%');
608 const unsigned ArgIndex = FS.getArgIndex() + ArgsOffset;
609 if (ArgIndex >= NumArgs) {
611 return conversionNotPossible(
612 (Twine(
"argument index ") + Twine(ArgIndex) +
" is out of range")
616 return convertArgument(FS, Args[ArgIndex]->IgnoreImplicitAsWritten(),
617 StandardFormatString);
622void FormatStringConverter::finalizeFormatText() {
624 StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
625 PrintfFormatString.size() - PrintfFormatStringPos));
626 PrintfFormatStringPos = PrintfFormatString.size();
630 if (StringRef(StandardFormatString).ends_with(
"\\n") &&
631 !StringRef(StandardFormatString).ends_with(
"\\\\n") &&
632 !StringRef(StandardFormatString).ends_with(
"\\r\\n")) {
633 UsePrintNewlineFunction =
true;
634 FormatStringNeededRewriting =
true;
635 StandardFormatString.erase(StandardFormatString.end() - 2,
636 StandardFormatString.end());
639 StandardFormatString.push_back(
'\"');
643void FormatStringConverter::appendFormatText(
const StringRef
Text) {
644 for (
const char Ch :
Text) {
646 StandardFormatString +=
"\\a";
648 StandardFormatString +=
"\\b";
650 StandardFormatString +=
"\\f";
652 StandardFormatString +=
"\\n";
654 StandardFormatString +=
"\\r";
656 StandardFormatString +=
"\\t";
658 StandardFormatString +=
"\\v";
660 StandardFormatString +=
"\\\"";
662 StandardFormatString +=
"\\\\";
663 else if (Ch ==
'{') {
664 StandardFormatString +=
"{{";
665 FormatStringNeededRewriting =
true;
666 }
else if (Ch ==
'}') {
667 StandardFormatString +=
"}}";
668 FormatStringNeededRewriting =
true;
669 }
else if (Ch < 32) {
670 StandardFormatString +=
"\\x";
671 StandardFormatString += llvm::hexdigit(Ch >> 4,
true);
672 StandardFormatString += llvm::hexdigit(Ch & 0xf,
true);
674 StandardFormatString += Ch;
679 ASTContext &Context) {
680 const auto *Arg = CStrRemovalMatch.getNodeAs<Expr>(
"arg");
681 const auto *Member = CStrRemovalMatch.getNodeAs<MemberExpr>(
"member");
682 const bool Arrow = Member->isArrow();
684 : tooling::fixit::getText(*Arg, Context).str();
690 if (FormatStringNeededRewriting) {
691 Diag << FixItHint::CreateReplacement(
692 CharSourceRange::getTokenRange(FormatExpr->getBeginLoc(),
693 FormatExpr->getEndLoc()),
694 StandardFormatString);
698 for (
auto [ValueArgIndex, ArgCount] : ArgRotates) {
699 assert(ValueArgIndex < NumArgs);
700 assert(ValueArgIndex > ArgCount);
704 if (
const auto CStrRemovalMatch =
705 std::find_if(ArgCStrRemovals.cbegin(), ArgCStrRemovals.cend(),
706 [ArgStartPos = Args[ValueArgIndex]->getBeginLoc()](
707 const BoundNodes &Match) {
710 const Expr *CStrArg = Match.getNodeAs<Expr>(
"arg");
711 return ArgStartPos == CStrArg->getBeginLoc();
713 CStrRemovalMatch != ArgCStrRemovals.end()) {
714 const std::string ArgText =
716 assert(!ArgText.empty());
718 Diag << FixItHint::CreateReplacement(
719 Args[ValueArgIndex - ArgCount]->getSourceRange(), ArgText);
722 ArgCStrRemovals.erase(CStrRemovalMatch);
724 Diag << tooling::fixit::createReplacement(*Args[ValueArgIndex - ArgCount],
725 *Args[ValueArgIndex], *Context);
730 Diag << tooling::fixit::createReplacement(
731 *Args[ValueArgIndex -
Offset], *Args[ValueArgIndex -
Offset - 1],
737 for (
auto &ArgFix : ArgFixes) {
738 if (ArgFix.ArgIndex == ValueArgIndex)
739 ArgFix.ArgIndex = ValueArgIndex - ArgCount;
743 for (
const auto &[ArgIndex, Replacement] : ArgFixes) {
744 SourceLocation AfterOtherSide =
745 Lexer::findNextToken(Args[ArgIndex]->getEndLoc(), SM, LangOpts)
748 Diag << FixItHint::CreateInsertion(Args[ArgIndex]->getBeginLoc(),
750 << FixItHint::CreateInsertion(AfterOtherSide,
")",
true);
753 for (
const auto &Match : ArgCStrRemovals) {
754 const auto *Call = Match.getNodeAs<CallExpr>(
"call");
756 if (!ArgText.empty())
757 Diag << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
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)
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++ -*-===//