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();
199 const CallExpr *Call,
200 unsigned FormatArgOffset,
202 const LangOptions &LO)
203 : Context(ContextIn),
Config(ConfigIn),
204 CastMismatchedIntegerTypes(
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()) {
213 conversionNotPossible(
"first argument is not a narrow string literal");
216 PrintfFormatString = FormatExpr->getString();
220 const size_t EstimatedGrowth = 8;
221 StandardFormatString.reserve(PrintfFormatString.size() + EstimatedGrowth);
222 StandardFormatString.push_back(
'\"');
224 const bool IsFreeBsdkPrintf =
false;
226 using clang::analyze_format_string::ParsePrintfString;
227 ParsePrintfString(*
this, PrintfFormatString.data(),
228 PrintfFormatString.data() + PrintfFormatString.size(),
229 LangOpts, Context->getTargetInfo(), IsFreeBsdkPrintf);
230 finalizeFormatText();
233void FormatStringConverter::emitAlignment(
const PrintfSpecifier &FS,
234 std::string &FormatSpec) {
235 ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind();
238 if (FS.getFieldWidth().getHowSpecified() != OptionalAmount::NotSpecified) {
239 if (ArgKind == ConversionSpecifier::sArg) {
242 if (!FS.isLeftJustified())
243 FormatSpec.push_back(
'>');
247 if (FS.isLeftJustified())
248 FormatSpec.push_back(
'<');
253void FormatStringConverter::emitSign(
const PrintfSpecifier &FS,
254 std::string &FormatSpec) {
260 if (Spec.isAnyIntArg() || Spec.isDoubleArg()) {
262 if (FS.hasPlusPrefix())
263 FormatSpec.push_back(
'+');
264 else if (FS.hasSpacePrefix())
265 FormatSpec.push_back(
' ');
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(
'#');
293void FormatStringConverter::emitFieldWidth(
const PrintfSpecifier &FS,
294 std::string &FormatSpec) {
296 const OptionalAmount FieldWidth = FS.getFieldWidth();
297 switch (FieldWidth.getHowSpecified()) {
298 case OptionalAmount::NotSpecified:
300 case OptionalAmount::Constant:
301 FormatSpec.append(llvm::utostr(FieldWidth.getConstantAmount()));
303 case OptionalAmount::Arg:
304 FormatSpec.push_back(
'{');
305 if (FieldWidth.usesPositionalArg()) {
308 assert(FieldWidth.getPositionalArgIndex() > 0U);
309 FormatSpec.append(llvm::utostr(FieldWidth.getPositionalArgIndex() - 1));
311 FormatSpec.push_back(
'}');
313 case OptionalAmount::Invalid:
319void FormatStringConverter::emitPrecision(
const PrintfSpecifier &FS,
320 std::string &FormatSpec) {
321 const OptionalAmount FieldPrecision = FS.getPrecision();
322 switch (FieldPrecision.getHowSpecified()) {
323 case OptionalAmount::NotSpecified:
325 case OptionalAmount::Constant:
326 FormatSpec.push_back(
'.');
327 FormatSpec.append(llvm::utostr(FieldPrecision.getConstantAmount()));
329 case OptionalAmount::Arg:
330 FormatSpec.push_back(
'.');
331 FormatSpec.push_back(
'{');
332 if (FieldPrecision.usesPositionalArg()) {
335 assert(FieldPrecision.getPositionalArgIndex() > 0U);
337 llvm::utostr(FieldPrecision.getPositionalArgIndex() - 1));
339 FormatSpec.push_back(
'}');
341 case OptionalAmount::Invalid:
346void FormatStringConverter::maybeRotateArguments(
const PrintfSpecifier &FS) {
347 unsigned ArgCount = 0;
348 const OptionalAmount FieldWidth = FS.getFieldWidth();
349 const OptionalAmount FieldPrecision = FS.getPrecision();
351 if (FieldWidth.getHowSpecified() == OptionalAmount::Arg &&
352 !FieldWidth.usesPositionalArg())
354 if (FieldPrecision.getHowSpecified() == OptionalAmount::Arg &&
355 !FieldPrecision.usesPositionalArg())
359 ArgRotates.emplace_back(FS.getArgIndex() + ArgsOffset, ArgCount);
362void FormatStringConverter::emitStringArgument(
unsigned ArgIndex,
370 if (!StringCStrCallExprMatcher) {
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)))));
377 StringCStrCallExprMatcher =
379 on(StringExpr.bind(
"arg")), callee(memberExpr().bind(
"member")),
380 callee(cxxMethodDecl(hasAnyName(
"c_str",
"data"),
381 returns(pointerType(pointee(isRealChar()))))))
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();
393 ArgFixes.emplace_back(ArgIndex,
"reinterpret_cast<const char *>(");
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()) {
405 FormatSpec.push_back(
'd');
406 }
else if (ArgType->isEnumeralType()) {
412 if (
const auto *ET = ArgType->getAs<EnumType>()) {
413 if (
const std::optional<std::string> MaybeCastType =
415 ArgFixes.emplace_back(
416 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
418 return conversionNotPossible(
419 (Twine(
"argument ") + Twine(ArgIndex) +
" has unexpected enum type")
422 }
else if (CastMismatchedIntegerTypes &&
427 if (
const std::optional<std::string> MaybeCastType =
429 ArgFixes.emplace_back(
430 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
432 return conversionNotPossible(
433 (Twine(
"argument ") + Twine(ArgIndex) +
" cannot be cast to " +
434 Twine(ArgKind == ConversionSpecifier::Kind::uArg ?
"unsigned"
436 " integer type to match format"
437 " specifier and StrictMode is enabled")
439 }
else if (
isRealCharType(ArgType) || !ArgType->isIntegerType()) {
441 FormatSpec.push_back(
'd');
449bool FormatStringConverter::emitType(
const PrintfSpecifier &FS,
const Expr *Arg,
450 std::string &FormatSpec) {
451 ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind();
453 case ConversionSpecifier::Kind::sArg:
454 emitStringArgument(FS.getArgIndex() + ArgsOffset, Arg);
456 case ConversionSpecifier::Kind::cArg:
460 FormatSpec.push_back(
'c');
462 case ConversionSpecifier::Kind::dArg:
463 case ConversionSpecifier::Kind::iArg:
464 case ConversionSpecifier::Kind::uArg:
465 if (!emitIntegerArgument(ArgKind, Arg, FS.getArgIndex() + ArgsOffset,
469 case ConversionSpecifier::Kind::pArg: {
470 const clang::QualType &ArgType = Arg->getType();
472 if (!ArgType->isNullPtrType() && !ArgType->isVoidPointerType())
473 ArgFixes.emplace_back(FS.getArgIndex() + ArgsOffset,
474 "static_cast<const void *>(");
477 case ConversionSpecifier::Kind::xArg:
478 FormatSpec.push_back(
'x');
480 case ConversionSpecifier::Kind::XArg:
481 FormatSpec.push_back(
'X');
483 case ConversionSpecifier::Kind::oArg:
484 FormatSpec.push_back(
'o');
486 case ConversionSpecifier::Kind::aArg:
487 FormatSpec.push_back(
'a');
489 case ConversionSpecifier::Kind::AArg:
490 FormatSpec.push_back(
'A');
492 case ConversionSpecifier::Kind::eArg:
493 FormatSpec.push_back(
'e');
495 case ConversionSpecifier::Kind::EArg:
496 FormatSpec.push_back(
'E');
498 case ConversionSpecifier::Kind::fArg:
499 FormatSpec.push_back(
'f');
501 case ConversionSpecifier::Kind::FArg:
502 FormatSpec.push_back(
'F');
504 case ConversionSpecifier::Kind::gArg:
505 FormatSpec.push_back(
'g');
507 case ConversionSpecifier::Kind::GArg:
508 FormatSpec.push_back(
'G');
512 return conversionNotPossible((Twine(
"argument ") +
513 Twine(FS.getArgIndex() + ArgsOffset) +
514 " has an unsupported format specifier")
524bool FormatStringConverter::convertArgument(
const PrintfSpecifier &FS,
526 std::string &StandardFormatString) {
528 assert(FS.consumesDataArgument());
530 StandardFormatString.push_back(
'{');
532 if (FS.usesPositionalArg()) {
535 assert(FS.getPositionalArgIndex() > 0U);
536 StandardFormatString.append(llvm::utostr(FS.getPositionalArgIndex() - 1));
541 std::string FormatSpec;
546 emitAlignment(FS, FormatSpec);
547 emitSign(FS, FormatSpec);
548 emitAlternativeForm(FS, FormatSpec);
550 if (FS.hasLeadingZeros())
551 FormatSpec.push_back(
'0');
553 emitFieldWidth(FS, FormatSpec);
554 emitPrecision(FS, FormatSpec);
555 maybeRotateArguments(FS);
557 if (!emitType(FS, Arg, FormatSpec))
560 if (!FormatSpec.empty()) {
561 StandardFormatString.push_back(
':');
562 StandardFormatString.append(FormatSpec);
565 StandardFormatString.push_back(
'}');
570bool FormatStringConverter::HandlePrintfSpecifier(
const PrintfSpecifier &FS,
571 const char *StartSpecifier,
572 unsigned SpecifierLen,
573 const TargetInfo &Target) {
575 const size_t StartSpecifierPos = StartSpecifier - PrintfFormatString.data();
576 assert(StartSpecifierPos + SpecifierLen <= PrintfFormatString.size());
579 assert(StartSpecifierPos >= PrintfFormatStringPos);
581 appendFormatText(StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
582 StartSpecifierPos - PrintfFormatStringPos));
584 const ConversionSpecifier::Kind ArgKind =
585 FS.getConversionSpecifier().getKind();
588 PrintfFormatStringPos = StartSpecifierPos + SpecifierLen;
589 assert(PrintfFormatStringPos <= PrintfFormatString.size());
591 FormatStringNeededRewriting =
true;
593 if (ArgKind == ConversionSpecifier::Kind::nArg) {
595 return conversionNotPossible(
"'%n' is not supported in format string");
598 if (ArgKind == ConversionSpecifier::Kind::PrintErrno) {
603 return conversionNotPossible(
"'%m' is not supported in format string");
606 if (ArgKind == ConversionSpecifier::PercentArg) {
607 StandardFormatString.push_back(
'%');
611 const unsigned ArgIndex = FS.getArgIndex() + ArgsOffset;
612 if (ArgIndex >= NumArgs) {
614 return conversionNotPossible(
615 (Twine(
"argument index ") + Twine(ArgIndex) +
" is out of range")
619 return convertArgument(FS, Args[ArgIndex]->IgnoreImplicitAsWritten(),
620 StandardFormatString);
625void FormatStringConverter::finalizeFormatText() {
627 StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
628 PrintfFormatString.size() - PrintfFormatStringPos));
629 PrintfFormatStringPos = PrintfFormatString.size();
634 const auto StandardFormatStringRef = StringRef(StandardFormatString);
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());
645 StandardFormatString.push_back(
'\"');
649void FormatStringConverter::appendFormatText(
const StringRef
Text) {
650 for (
const char Ch :
Text) {
652 StandardFormatString +=
"\\a";
654 StandardFormatString +=
"\\b";
656 StandardFormatString +=
"\\f";
658 StandardFormatString +=
"\\n";
660 StandardFormatString +=
"\\r";
662 StandardFormatString +=
"\\t";
664 StandardFormatString +=
"\\v";
666 StandardFormatString +=
"\\\"";
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);
680 StandardFormatString += Ch;
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();
690 : tooling::fixit::getText(*Arg, Context).str();
696 if (FormatStringNeededRewriting) {
697 Diag << FixItHint::CreateReplacement(
698 CharSourceRange::getTokenRange(FormatExpr->getBeginLoc(),
699 FormatExpr->getEndLoc()),
700 StandardFormatString);
704 for (
auto [ValueArgIndex, ArgCount] : ArgRotates) {
705 assert(ValueArgIndex < NumArgs);
706 assert(ValueArgIndex > ArgCount);
710 if (
const auto CStrRemovalMatch =
711 std::find_if(ArgCStrRemovals.cbegin(), ArgCStrRemovals.cend(),
712 [ArgStartPos = Args[ValueArgIndex]->getBeginLoc()](
713 const BoundNodes &Match) {
716 const Expr *CStrArg = Match.getNodeAs<Expr>(
"arg");
717 return ArgStartPos == CStrArg->getBeginLoc();
719 CStrRemovalMatch != ArgCStrRemovals.end()) {
720 const std::string ArgText =
722 assert(!ArgText.empty());
724 Diag << FixItHint::CreateReplacement(
725 Args[ValueArgIndex - ArgCount]->getSourceRange(), ArgText);
728 ArgCStrRemovals.erase(CStrRemovalMatch);
730 Diag << tooling::fixit::createReplacement(*Args[ValueArgIndex - ArgCount],
731 *Args[ValueArgIndex], *Context);
736 Diag << tooling::fixit::createReplacement(
737 *Args[ValueArgIndex -
Offset], *Args[ValueArgIndex -
Offset - 1],
743 for (
auto &ArgFix : ArgFixes) {
744 if (ArgFix.ArgIndex == ValueArgIndex)
745 ArgFix.ArgIndex = ValueArgIndex - ArgCount;
749 for (
const auto &[ArgIndex, Replacement] : ArgFixes) {
750 SourceLocation AfterOtherSide =
751 Lexer::findNextToken(Args[ArgIndex]->getEndLoc(), SM, LangOpts)
754 Diag << FixItHint::CreateInsertion(Args[ArgIndex]->getBeginLoc(),
756 << FixItHint::CreateInsertion(AfterOtherSide,
")",
true);
759 for (
const auto &Match : ArgCStrRemovals) {
760 const auto *Call = Match.getNodeAs<CallExpr>(
"call");
762 if (!ArgText.empty())
763 Diag << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
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))
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++ -*-===//
static constexpr const char FuncDecl[]