18#include "clang/AST/Expr.h"
19#include "clang/ASTMatchers/ASTMatchFinder.h"
20#include "clang/Basic/LangOptions.h"
21#include "clang/Lex/Lexer.h"
22#include "clang/Lex/Preprocessor.h"
23#include "clang/Tooling/FixIt.h"
24#include "llvm/ADT/StringExtras.h"
25#include "llvm/Support/Debug.h"
28using namespace clang::analyze_printf;
31using clang::analyze_format_string::ConversionSpecifier;
36 using namespace clang;
37 const Type *DesugaredType = Ty->getUnqualifiedDesugaredType();
38 if (
const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
39 return (BT->getKind() == BuiltinType::Char_U ||
40 BT->getKind() == BuiltinType::Char_S);
47static std::optional<std::string>
49 using namespace clang;
50 const auto UQT = QT.getUnqualifiedType();
51 if (
const auto *BT = dyn_cast<BuiltinType>(UQT)) {
52 switch (BT->getKind()) {
53 case BuiltinType::UChar:
54 case BuiltinType::Char_U:
55 case BuiltinType::SChar:
56 case BuiltinType::Char_S:
58 case BuiltinType::UShort:
59 case BuiltinType::Short:
61 case BuiltinType::UInt:
62 case BuiltinType::Int:
64 case BuiltinType::ULong:
65 case BuiltinType::Long:
67 case BuiltinType::ULongLong:
68 case BuiltinType::LongLong:
71 llvm::dbgs() <<
"Unknown corresponding signed type for BuiltinType '"
72 << QT.getAsString() <<
"'\n";
79 const std::string TypeName = UQT.getAsString();
80 StringRef SimplifiedTypeName{TypeName};
81 const bool InStd = SimplifiedTypeName.consume_front(
"std::");
82 const StringRef Prefix = InStd ?
"std::" :
"";
84 if (SimplifiedTypeName.starts_with(
"uint") &&
85 SimplifiedTypeName.ends_with(
"_t"))
86 return (Twine(Prefix) + SimplifiedTypeName.drop_front()).str();
88 if (SimplifiedTypeName ==
"size_t")
89 return (Twine(Prefix) +
"ssize_t").str();
91 llvm::dbgs() <<
"Unknown corresponding signed type for non-BuiltinType '"
92 << UQT.getAsString() <<
"'\n";
99static std::optional<std::string>
101 using namespace clang;
102 const auto UQT = QT.getUnqualifiedType();
103 if (
const auto *BT = dyn_cast<BuiltinType>(UQT)) {
104 switch (BT->getKind()) {
105 case BuiltinType::SChar:
106 case BuiltinType::Char_S:
107 case BuiltinType::UChar:
108 case BuiltinType::Char_U:
109 return "unsigned char";
110 case BuiltinType::Short:
111 case BuiltinType::UShort:
112 return "unsigned short";
113 case BuiltinType::Int:
114 case BuiltinType::UInt:
115 return "unsigned int";
116 case BuiltinType::Long:
117 case BuiltinType::ULong:
118 return "unsigned long";
119 case BuiltinType::LongLong:
120 case BuiltinType::ULongLong:
121 return "unsigned long long";
123 llvm::dbgs() <<
"Unknown corresponding unsigned type for BuiltinType '"
124 << UQT.getAsString() <<
"'\n";
131 const std::string TypeName = UQT.getAsString();
132 StringRef SimplifiedTypeName{TypeName};
133 const bool InStd = SimplifiedTypeName.consume_front(
"std::");
134 const StringRef Prefix = InStd ?
"std::" :
"";
136 if (SimplifiedTypeName.starts_with(
"int") &&
137 SimplifiedTypeName.ends_with(
"_t"))
138 return (Twine(Prefix) +
"u" + SimplifiedTypeName).str();
140 if (SimplifiedTypeName ==
"ssize_t")
141 return (Twine(Prefix) +
"size_t").str();
142 if (SimplifiedTypeName ==
"ptrdiff_t")
143 return (Twine(Prefix) +
"size_t").str();
145 llvm::dbgs() <<
"Unknown corresponding unsigned type for non-BuiltinType '"
146 << UQT.getAsString() <<
"'\n";
150static std::optional<std::string>
152 if (ArgKind == ConversionSpecifier::Kind::uArg)
158 const QualType &ArgType) {
159 if (
const auto *BT = dyn_cast<BuiltinType>(ArgType)) {
162 const auto ArgTypeKind = BT->getKind();
163 if (ArgTypeKind == BuiltinType::Char_U ||
164 ArgTypeKind == BuiltinType::Char_S)
168 if (ArgKind == ConversionSpecifier::Kind::uArg)
169 return ArgType->isUnsignedIntegerType();
170 return ArgType->isSignedIntegerType();
190 const FunctionDecl *
FuncDecl = Call->getDirectCallee();
198 ASTContext *ContextIn,
const CallExpr *Call,
unsigned FormatArgOffset,
199 const Configuration ConfigIn,
const LangOptions &LO, SourceManager &SM,
201 : Context(ContextIn), Config(ConfigIn),
202 CastMismatchedIntegerTypes(
204 Args(Call->getArgs()), NumArgs(Call->getNumArgs()),
205 ArgsOffset(FormatArgOffset + 1), LangOpts(LO) {
206 assert(ArgsOffset <= NumArgs);
207 FormatExpr = dyn_cast<StringLiteral>(
208 Args[FormatArgOffset]->IgnoreUnlessSpelledInSource());
210 assert(FormatExpr && FormatExpr->isOrdinary());
212 if (
const std::optional<StringRef> MaybeMacroName =
213 formatStringContainsUnreplaceableMacro(Call, FormatExpr, SM, PP);
215 conversionNotPossible(
216 (
"format string contains unreplaceable macro '" + *MaybeMacroName +
"'")
221 PrintfFormatString = FormatExpr->getString();
225 const size_t EstimatedGrowth = 8;
226 StandardFormatString.reserve(PrintfFormatString.size() + EstimatedGrowth);
227 StandardFormatString.push_back(
'\"');
229 const bool IsFreeBsdkPrintf =
false;
231 using clang::analyze_format_string::ParsePrintfString;
232 ParsePrintfString(*
this, PrintfFormatString.data(),
233 PrintfFormatString.data() + PrintfFormatString.size(),
234 LangOpts, Context->getTargetInfo(), IsFreeBsdkPrintf);
235 finalizeFormatText();
238std::optional<StringRef>
239FormatStringConverter::formatStringContainsUnreplaceableMacro(
240 const CallExpr *Call,
const StringLiteral *FormatExpr, SourceManager &SM,
245 std::optional<StringRef> MaybeSurroundingMacroName;
246 if (
const SourceLocation BeginCallLoc = Call->getBeginLoc();
247 BeginCallLoc.isMacroID())
248 MaybeSurroundingMacroName =
249 Lexer::getImmediateMacroName(BeginCallLoc, SM, PP.getLangOpts());
251 for (
auto I = FormatExpr->tokloc_begin(), E = FormatExpr->tokloc_end();
253 const SourceLocation &TokenLoc = *I;
254 if (TokenLoc.isMacroID()) {
255 const StringRef MacroName =
256 Lexer::getImmediateMacroName(TokenLoc, SM, PP.getLangOpts());
258 if (MaybeSurroundingMacroName != MacroName) {
261 if (!MacroName.starts_with(
"PRI") && !MacroName.starts_with(
"__PRI"))
264 const SourceLocation TokenSpellingLoc = SM.getSpellingLoc(TokenLoc);
265 const OptionalFileEntryRef MaybeFileEntry =
266 SM.getFileEntryRefForID(SM.getFileID(TokenSpellingLoc));
270 HeaderSearch &HS = PP.getHeaderSearchInfo();
272 if (!isSystem(HS.getFileDirFlavor(*MaybeFileEntry)) ||
273 llvm::sys::path::filename(MaybeFileEntry->getName()) !=
282void FormatStringConverter::emitAlignment(
const PrintfSpecifier &FS,
283 std::string &FormatSpec) {
284 const ConversionSpecifier::Kind ArgKind =
285 FS.getConversionSpecifier().getKind();
288 if (FS.getFieldWidth().getHowSpecified() != OptionalAmount::NotSpecified) {
289 if (ArgKind == ConversionSpecifier::sArg) {
292 if (!FS.isLeftJustified())
293 FormatSpec.push_back(
'>');
297 if (FS.isLeftJustified())
298 FormatSpec.push_back(
'<');
303void FormatStringConverter::emitSign(
const PrintfSpecifier &FS,
304 std::string &FormatSpec) {
310 if (Spec.isAnyIntArg() || Spec.isDoubleArg()) {
312 if (FS.hasPlusPrefix())
313 FormatSpec.push_back(
'+');
314 else if (FS.hasSpacePrefix())
315 FormatSpec.push_back(
' ');
319void FormatStringConverter::emitAlternativeForm(
const PrintfSpecifier &FS,
320 std::string &FormatSpec) {
321 if (FS.hasAlternativeForm()) {
322 switch (FS.getConversionSpecifier().getKind()) {
323 case ConversionSpecifier::Kind::aArg:
324 case ConversionSpecifier::Kind::AArg:
325 case ConversionSpecifier::Kind::eArg:
326 case ConversionSpecifier::Kind::EArg:
327 case ConversionSpecifier::Kind::fArg:
328 case ConversionSpecifier::Kind::FArg:
329 case ConversionSpecifier::Kind::gArg:
330 case ConversionSpecifier::Kind::GArg:
331 case ConversionSpecifier::Kind::xArg:
332 case ConversionSpecifier::Kind::XArg:
333 case ConversionSpecifier::Kind::oArg:
334 FormatSpec.push_back(
'#');
343void FormatStringConverter::emitFieldWidth(
const PrintfSpecifier &FS,
344 std::string &FormatSpec) {
346 const OptionalAmount FieldWidth = FS.getFieldWidth();
347 switch (FieldWidth.getHowSpecified()) {
348 case OptionalAmount::NotSpecified:
350 case OptionalAmount::Constant:
351 FormatSpec.append(llvm::utostr(FieldWidth.getConstantAmount()));
353 case OptionalAmount::Arg:
354 FormatSpec.push_back(
'{');
355 if (FieldWidth.usesPositionalArg()) {
358 assert(FieldWidth.getPositionalArgIndex() > 0U);
359 FormatSpec.append(llvm::utostr(FieldWidth.getPositionalArgIndex() - 1));
361 FormatSpec.push_back(
'}');
363 case OptionalAmount::Invalid:
369void FormatStringConverter::emitPrecision(
const PrintfSpecifier &FS,
370 std::string &FormatSpec) {
371 const OptionalAmount FieldPrecision = FS.getPrecision();
372 switch (FieldPrecision.getHowSpecified()) {
373 case OptionalAmount::NotSpecified:
375 case OptionalAmount::Constant:
376 FormatSpec.push_back(
'.');
377 FormatSpec.append(llvm::utostr(FieldPrecision.getConstantAmount()));
379 case OptionalAmount::Arg:
380 FormatSpec.push_back(
'.');
381 FormatSpec.push_back(
'{');
382 if (FieldPrecision.usesPositionalArg()) {
385 assert(FieldPrecision.getPositionalArgIndex() > 0U);
387 llvm::utostr(FieldPrecision.getPositionalArgIndex() - 1));
389 FormatSpec.push_back(
'}');
391 case OptionalAmount::Invalid:
396void FormatStringConverter::maybeRotateArguments(
const PrintfSpecifier &FS) {
397 unsigned ArgCount = 0;
398 const OptionalAmount FieldWidth = FS.getFieldWidth();
399 const OptionalAmount FieldPrecision = FS.getPrecision();
401 if (FieldWidth.getHowSpecified() == OptionalAmount::Arg &&
402 !FieldWidth.usesPositionalArg())
404 if (FieldPrecision.getHowSpecified() == OptionalAmount::Arg &&
405 !FieldPrecision.usesPositionalArg())
409 ArgRotates.emplace_back(FS.getArgIndex() + ArgsOffset, ArgCount);
412void FormatStringConverter::emitStringArgument(
unsigned ArgIndex,
420 if (!StringCStrCallExprMatcher) {
422 const auto StringDecl = type(hasUnqualifiedDesugaredType(recordType(
423 hasDeclaration(cxxRecordDecl(hasName(
"::std::basic_string"))))));
424 const auto StringExpr = expr(
425 anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));
427 StringCStrCallExprMatcher =
429 on(StringExpr.bind(
"arg")), callee(memberExpr().bind(
"member")),
430 callee(cxxMethodDecl(hasAnyName(
"c_str",
"data"),
431 returns(pointerType(pointee(isRealChar()))))))
435 auto CStrMatches =
match(*StringCStrCallExprMatcher, *Arg, *Context);
436 if (CStrMatches.size() == 1) {
437 ArgCStrRemovals.push_back(CStrMatches.front());
438 }
else if (Arg->getType()->isPointerType()) {
439 const QualType
Pointee = Arg->getType()->getPointeeType();
443 ArgFixes.emplace_back(ArgIndex,
"reinterpret_cast<const char *>(");
447bool FormatStringConverter::emitIntegerArgument(
448 ConversionSpecifier::Kind ArgKind,
const Expr *Arg,
unsigned ArgIndex,
449 std::string &FormatSpec) {
450 const QualType &ArgType = Arg->getType();
451 if (ArgType->isBooleanType()) {
455 FormatSpec.push_back(
'd');
456 }
else if (ArgType->isEnumeralType()) {
462 if (
const auto *ED = ArgType->getAsEnumDecl()) {
463 if (
const std::optional<std::string> MaybeCastType =
465 ArgFixes.emplace_back(
466 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
468 return conversionNotPossible(
469 (Twine(
"argument ") + Twine(ArgIndex) +
" has unexpected enum type")
472 }
else if (CastMismatchedIntegerTypes &&
477 if (
const std::optional<std::string> MaybeCastType =
479 ArgFixes.emplace_back(
480 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
482 return conversionNotPossible(
483 (Twine(
"argument ") + Twine(ArgIndex) +
" cannot be cast to " +
484 Twine(ArgKind == ConversionSpecifier::Kind::uArg ?
"unsigned"
486 " integer type to match format"
487 " specifier and StrictMode is enabled")
489 }
else if (
isRealCharType(ArgType) || !ArgType->isIntegerType()) {
491 FormatSpec.push_back(
'd');
499bool FormatStringConverter::emitType(
const PrintfSpecifier &FS,
const Expr *Arg,
500 std::string &FormatSpec) {
501 const ConversionSpecifier::Kind ArgKind =
502 FS.getConversionSpecifier().getKind();
504 case ConversionSpecifier::Kind::sArg:
505 emitStringArgument(FS.getArgIndex() + ArgsOffset, Arg);
507 case ConversionSpecifier::Kind::cArg:
511 FormatSpec.push_back(
'c');
513 case ConversionSpecifier::Kind::dArg:
514 case ConversionSpecifier::Kind::iArg:
515 case ConversionSpecifier::Kind::uArg:
516 if (!emitIntegerArgument(ArgKind, Arg, FS.getArgIndex() + ArgsOffset,
520 case ConversionSpecifier::Kind::pArg: {
521 const QualType &ArgType = Arg->getType();
523 if (!ArgType->isNullPtrType() && !ArgType->isVoidPointerType())
524 ArgFixes.emplace_back(FS.getArgIndex() + ArgsOffset,
525 "static_cast<const void *>(");
528 case ConversionSpecifier::Kind::xArg:
529 FormatSpec.push_back(
'x');
531 case ConversionSpecifier::Kind::XArg:
532 FormatSpec.push_back(
'X');
534 case ConversionSpecifier::Kind::oArg:
535 FormatSpec.push_back(
'o');
537 case ConversionSpecifier::Kind::aArg:
538 FormatSpec.push_back(
'a');
540 case ConversionSpecifier::Kind::AArg:
541 FormatSpec.push_back(
'A');
543 case ConversionSpecifier::Kind::eArg:
544 FormatSpec.push_back(
'e');
546 case ConversionSpecifier::Kind::EArg:
547 FormatSpec.push_back(
'E');
549 case ConversionSpecifier::Kind::fArg:
550 FormatSpec.push_back(
'f');
552 case ConversionSpecifier::Kind::FArg:
553 FormatSpec.push_back(
'F');
555 case ConversionSpecifier::Kind::gArg:
556 FormatSpec.push_back(
'g');
558 case ConversionSpecifier::Kind::GArg:
559 FormatSpec.push_back(
'G');
563 return conversionNotPossible((Twine(
"argument ") +
564 Twine(FS.getArgIndex() + ArgsOffset) +
565 " has an unsupported format specifier")
575bool FormatStringConverter::convertArgument(
const PrintfSpecifier &FS,
577 std::string &StandardFormatString) {
579 assert(FS.consumesDataArgument());
581 StandardFormatString.push_back(
'{');
583 if (FS.usesPositionalArg()) {
586 assert(FS.getPositionalArgIndex() > 0U);
587 StandardFormatString.append(llvm::utostr(FS.getPositionalArgIndex() - 1));
592 std::string FormatSpec;
597 emitAlignment(FS, FormatSpec);
598 emitSign(FS, FormatSpec);
599 emitAlternativeForm(FS, FormatSpec);
601 if (FS.hasLeadingZeros())
602 FormatSpec.push_back(
'0');
604 emitFieldWidth(FS, FormatSpec);
605 emitPrecision(FS, FormatSpec);
606 maybeRotateArguments(FS);
608 if (!emitType(FS, Arg, FormatSpec))
611 if (!FormatSpec.empty()) {
612 StandardFormatString.push_back(
':');
613 StandardFormatString.append(FormatSpec);
616 StandardFormatString.push_back(
'}');
621bool FormatStringConverter::HandlePrintfSpecifier(
const PrintfSpecifier &FS,
622 const char *StartSpecifier,
623 unsigned SpecifierLen,
624 const TargetInfo &Target) {
625 const size_t StartSpecifierPos = StartSpecifier - PrintfFormatString.data();
626 assert(StartSpecifierPos + SpecifierLen <= PrintfFormatString.size());
629 assert(StartSpecifierPos >= PrintfFormatStringPos);
631 appendFormatText(StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
632 StartSpecifierPos - PrintfFormatStringPos));
634 const ConversionSpecifier::Kind ArgKind =
635 FS.getConversionSpecifier().getKind();
638 PrintfFormatStringPos = StartSpecifierPos + SpecifierLen;
639 assert(PrintfFormatStringPos <= PrintfFormatString.size());
641 FormatStringNeededRewriting =
true;
643 if (ArgKind == ConversionSpecifier::Kind::nArg) {
645 return conversionNotPossible(
"'%n' is not supported in format string");
648 if (ArgKind == ConversionSpecifier::Kind::PrintErrno) {
653 return conversionNotPossible(
"'%m' is not supported in format string");
656 if (ArgKind == ConversionSpecifier::PercentArg) {
657 StandardFormatString.push_back(
'%');
661 const unsigned ArgIndex = FS.getArgIndex() + ArgsOffset;
662 if (ArgIndex >= NumArgs) {
664 return conversionNotPossible(
665 (Twine(
"argument index ") + Twine(ArgIndex) +
" is out of range")
669 return convertArgument(FS, Args[ArgIndex]->IgnoreImplicitAsWritten(),
670 StandardFormatString);
675void FormatStringConverter::finalizeFormatText() {
677 StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
678 PrintfFormatString.size() - PrintfFormatStringPos));
679 PrintfFormatStringPos = PrintfFormatString.size();
684 const auto StandardFormatStringRef = StringRef(StandardFormatString);
685 if (Config.AllowTrailingNewlineRemoval &&
686 StandardFormatStringRef.ends_with(
"\\n") &&
687 !StandardFormatStringRef.ends_with(
"\\\\n") &&
688 !StandardFormatStringRef.ends_with(
"\\r\\n")) {
689 UsePrintNewlineFunction =
true;
690 FormatStringNeededRewriting =
true;
691 StandardFormatString.erase(StandardFormatString.end() - 2,
692 StandardFormatString.end());
695 StandardFormatString.push_back(
'\"');
699void FormatStringConverter::appendFormatText(
const StringRef Text) {
700 for (
const char Ch : Text) {
701 const auto UCh =
static_cast<unsigned char>(Ch);
703 StandardFormatString +=
"\\a";
704 }
else if (Ch ==
'\b') {
705 StandardFormatString +=
"\\b";
706 }
else if (Ch ==
'\f') {
707 StandardFormatString +=
"\\f";
708 }
else if (Ch ==
'\n') {
709 StandardFormatString +=
"\\n";
710 }
else if (Ch ==
'\r') {
711 StandardFormatString +=
"\\r";
712 }
else if (Ch ==
'\t') {
713 StandardFormatString +=
"\\t";
714 }
else if (Ch ==
'\v') {
715 StandardFormatString +=
"\\v";
716 }
else if (Ch ==
'\"') {
717 StandardFormatString +=
"\\\"";
718 }
else if (Ch ==
'\\') {
719 StandardFormatString +=
"\\\\";
720 }
else if (Ch ==
'{') {
721 StandardFormatString +=
"{{";
722 FormatStringNeededRewriting =
true;
723 }
else if (Ch ==
'}') {
724 StandardFormatString +=
"}}";
725 FormatStringNeededRewriting =
true;
726 }
else if (UCh < 32) {
727 StandardFormatString +=
"\\x";
728 StandardFormatString += llvm::hexdigit(UCh >> 4,
true);
729 StandardFormatString += llvm::hexdigit(UCh & 0xf,
true);
731 StandardFormatString += Ch;
737 ASTContext &Context) {
738 const auto *Arg = CStrRemovalMatch.getNodeAs<Expr>(
"arg");
739 const auto *Member = CStrRemovalMatch.getNodeAs<MemberExpr>(
"member");
740 const bool Arrow = Member->isArrow();
742 : tooling::fixit::getText(*Arg, Context).str();
748 if (FormatStringNeededRewriting) {
749 Diag << FixItHint::CreateReplacement(
750 CharSourceRange::getTokenRange(FormatExpr->getBeginLoc(),
751 FormatExpr->getEndLoc()),
752 StandardFormatString);
756 for (
auto [ValueArgIndex, ArgCount] : ArgRotates) {
757 assert(ValueArgIndex < NumArgs);
758 assert(ValueArgIndex > ArgCount);
762 if (
const auto CStrRemovalMatch =
763 llvm::find_if(ArgCStrRemovals,
764 [ArgStartPos = Args[ValueArgIndex]->getBeginLoc()](
765 const BoundNodes &Match) {
768 const Expr *CStrArg = Match.getNodeAs<Expr>(
"arg");
769 return ArgStartPos == CStrArg->getBeginLoc();
771 CStrRemovalMatch != ArgCStrRemovals.end()) {
772 const std::string ArgText =
774 assert(!ArgText.empty());
776 Diag << FixItHint::CreateReplacement(
777 Args[ValueArgIndex - ArgCount]->getSourceRange(), ArgText);
780 ArgCStrRemovals.erase(CStrRemovalMatch);
782 Diag << tooling::fixit::createReplacement(*Args[ValueArgIndex - ArgCount],
783 *Args[ValueArgIndex], *Context);
788 for (
size_t Offset = 0; Offset < ArgCount; ++Offset)
789 Diag << tooling::fixit::createReplacement(
790 *Args[ValueArgIndex - Offset], *Args[ValueArgIndex - Offset - 1],
796 for (
auto &ArgFix : ArgFixes)
797 if (ArgFix.ArgIndex == ValueArgIndex)
798 ArgFix.ArgIndex = ValueArgIndex - ArgCount;
801 for (
const auto &[ArgIndex, Replacement] : ArgFixes) {
802 const std::optional<Token> NextToken =
807 const SourceLocation AfterOtherSide = NextToken->getLocation();
809 Diag << FixItHint::CreateInsertion(Args[ArgIndex]->getBeginLoc(),
811 << FixItHint::CreateInsertion(AfterOtherSide,
")",
true);
814 for (
const auto &Match : ArgCStrRemovals) {
815 const auto *Call = Match.getNodeAs<CallExpr>(
"call");
817 if (!ArgText.empty())
818 Diag << FixItHint::CreateReplacement(Call->getSourceRange(), ArgText);
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
std::string formatDereference(const Expr &ExprNode, const ASTContext &Context)
std::optional< Token > findNextTokenSkippingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
static std::string withoutCStrReplacement(const BoundNodes &CStrRemovalMatch, ASTContext &Context)
static bool isMatchingSignedness(ConversionSpecifier::Kind ArgKind, const QualType &ArgType)
static bool isRealCharType(const QualType &Ty)
Is the passed type the actual "char" type, whether that be signed or unsigned, rather than explicit s...
static bool castMismatchedIntegerTypes(const CallExpr *Call, bool StrictMode)
static std::optional< std::string > castTypeForArgument(ConversionSpecifier::Kind ArgKind, const QualType &QT)
static std::optional< std::string > getCorrespondingSignedTypeName(const QualType &QT)
If possible, return the text name of the signed type that corresponds to the passed integer type.
static std::optional< std::string > getCorrespondingUnsignedTypeName(const QualType &QT)
If possible, return the text name of the unsigned type that corresponds to the passed integer type.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static constexpr const char FuncDecl[]