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 = llvm::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 = llvm::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 = llvm::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 const clang::QualType &QT) {
153 if (ArgKind == ConversionSpecifier::Kind::uArg)
159 const clang::QualType &ArgType) {
160 if (
const auto *BT = llvm::dyn_cast<BuiltinType>(ArgType)) {
163 const auto ArgTypeKind = BT->getKind();
164 if (ArgTypeKind == BuiltinType::Char_U ||
165 ArgTypeKind == BuiltinType::Char_S)
169 if (ArgKind == ConversionSpecifier::Kind::uArg)
170 return ArgType->isUnsignedIntegerType();
171 return ArgType->isSignedIntegerType();
175AST_MATCHER(clang::QualType, isRealChar) {
193 const FunctionDecl *
FuncDecl = Call->getDirectCallee();
201 ASTContext *ContextIn,
const CallExpr *Call,
unsigned FormatArgOffset,
202 const Configuration ConfigIn,
const LangOptions &LO, SourceManager &SM,
204 : Context(ContextIn), Config(ConfigIn),
205 CastMismatchedIntegerTypes(
207 Args(Call->getArgs()), NumArgs(Call->getNumArgs()),
208 ArgsOffset(FormatArgOffset + 1), LangOpts(LO) {
209 assert(ArgsOffset <= NumArgs);
210 FormatExpr = llvm::dyn_cast<StringLiteral>(
211 Args[FormatArgOffset]->IgnoreUnlessSpelledInSource());
213 assert(FormatExpr && FormatExpr->isOrdinary());
215 if (
const std::optional<StringRef> MaybeMacroName =
216 formatStringContainsUnreplaceableMacro(Call, FormatExpr, SM, PP);
218 conversionNotPossible(
219 (
"format string contains unreplaceable macro '" + *MaybeMacroName +
"'")
224 PrintfFormatString = FormatExpr->getString();
228 const size_t EstimatedGrowth = 8;
229 StandardFormatString.reserve(PrintfFormatString.size() + EstimatedGrowth);
230 StandardFormatString.push_back(
'\"');
232 const bool IsFreeBsdkPrintf =
false;
234 using clang::analyze_format_string::ParsePrintfString;
235 ParsePrintfString(*
this, PrintfFormatString.data(),
236 PrintfFormatString.data() + PrintfFormatString.size(),
237 LangOpts, Context->getTargetInfo(), IsFreeBsdkPrintf);
238 finalizeFormatText();
241std::optional<StringRef>
242FormatStringConverter::formatStringContainsUnreplaceableMacro(
243 const CallExpr *Call,
const StringLiteral *FormatExpr, SourceManager &SM,
248 std::optional<StringRef> MaybeSurroundingMacroName;
249 if (
const SourceLocation BeginCallLoc = Call->getBeginLoc();
250 BeginCallLoc.isMacroID())
251 MaybeSurroundingMacroName =
252 Lexer::getImmediateMacroName(BeginCallLoc, SM, PP.getLangOpts());
254 for (
auto I = FormatExpr->tokloc_begin(), E = FormatExpr->tokloc_end();
256 const SourceLocation &TokenLoc = *I;
257 if (TokenLoc.isMacroID()) {
258 const StringRef MacroName =
259 Lexer::getImmediateMacroName(TokenLoc, SM, PP.getLangOpts());
261 if (MaybeSurroundingMacroName != MacroName) {
264 if (!MacroName.starts_with(
"PRI") && !MacroName.starts_with(
"__PRI"))
267 const SourceLocation TokenSpellingLoc = SM.getSpellingLoc(TokenLoc);
268 const OptionalFileEntryRef MaybeFileEntry =
269 SM.getFileEntryRefForID(SM.getFileID(TokenSpellingLoc));
273 HeaderSearch &HS = PP.getHeaderSearchInfo();
275 if (!isSystem(HS.getFileDirFlavor(*MaybeFileEntry)) ||
276 llvm::sys::path::filename(MaybeFileEntry->getName()) !=
285void FormatStringConverter::emitAlignment(
const PrintfSpecifier &FS,
286 std::string &FormatSpec) {
287 const ConversionSpecifier::Kind ArgKind =
288 FS.getConversionSpecifier().getKind();
291 if (FS.getFieldWidth().getHowSpecified() != OptionalAmount::NotSpecified) {
292 if (ArgKind == ConversionSpecifier::sArg) {
295 if (!FS.isLeftJustified())
296 FormatSpec.push_back(
'>');
300 if (FS.isLeftJustified())
301 FormatSpec.push_back(
'<');
306void FormatStringConverter::emitSign(
const PrintfSpecifier &FS,
307 std::string &FormatSpec) {
313 if (Spec.isAnyIntArg() || Spec.isDoubleArg()) {
315 if (FS.hasPlusPrefix())
316 FormatSpec.push_back(
'+');
317 else if (FS.hasSpacePrefix())
318 FormatSpec.push_back(
' ');
322void FormatStringConverter::emitAlternativeForm(
const PrintfSpecifier &FS,
323 std::string &FormatSpec) {
324 if (FS.hasAlternativeForm()) {
325 switch (FS.getConversionSpecifier().getKind()) {
326 case ConversionSpecifier::Kind::aArg:
327 case ConversionSpecifier::Kind::AArg:
328 case ConversionSpecifier::Kind::eArg:
329 case ConversionSpecifier::Kind::EArg:
330 case ConversionSpecifier::Kind::fArg:
331 case ConversionSpecifier::Kind::FArg:
332 case ConversionSpecifier::Kind::gArg:
333 case ConversionSpecifier::Kind::GArg:
334 case ConversionSpecifier::Kind::xArg:
335 case ConversionSpecifier::Kind::XArg:
336 case ConversionSpecifier::Kind::oArg:
337 FormatSpec.push_back(
'#');
346void FormatStringConverter::emitFieldWidth(
const PrintfSpecifier &FS,
347 std::string &FormatSpec) {
349 const OptionalAmount FieldWidth = FS.getFieldWidth();
350 switch (FieldWidth.getHowSpecified()) {
351 case OptionalAmount::NotSpecified:
353 case OptionalAmount::Constant:
354 FormatSpec.append(llvm::utostr(FieldWidth.getConstantAmount()));
356 case OptionalAmount::Arg:
357 FormatSpec.push_back(
'{');
358 if (FieldWidth.usesPositionalArg()) {
361 assert(FieldWidth.getPositionalArgIndex() > 0U);
362 FormatSpec.append(llvm::utostr(FieldWidth.getPositionalArgIndex() - 1));
364 FormatSpec.push_back(
'}');
366 case OptionalAmount::Invalid:
372void FormatStringConverter::emitPrecision(
const PrintfSpecifier &FS,
373 std::string &FormatSpec) {
374 const OptionalAmount FieldPrecision = FS.getPrecision();
375 switch (FieldPrecision.getHowSpecified()) {
376 case OptionalAmount::NotSpecified:
378 case OptionalAmount::Constant:
379 FormatSpec.push_back(
'.');
380 FormatSpec.append(llvm::utostr(FieldPrecision.getConstantAmount()));
382 case OptionalAmount::Arg:
383 FormatSpec.push_back(
'.');
384 FormatSpec.push_back(
'{');
385 if (FieldPrecision.usesPositionalArg()) {
388 assert(FieldPrecision.getPositionalArgIndex() > 0U);
390 llvm::utostr(FieldPrecision.getPositionalArgIndex() - 1));
392 FormatSpec.push_back(
'}');
394 case OptionalAmount::Invalid:
399void FormatStringConverter::maybeRotateArguments(
const PrintfSpecifier &FS) {
400 unsigned ArgCount = 0;
401 const OptionalAmount FieldWidth = FS.getFieldWidth();
402 const OptionalAmount FieldPrecision = FS.getPrecision();
404 if (FieldWidth.getHowSpecified() == OptionalAmount::Arg &&
405 !FieldWidth.usesPositionalArg())
407 if (FieldPrecision.getHowSpecified() == OptionalAmount::Arg &&
408 !FieldPrecision.usesPositionalArg())
412 ArgRotates.emplace_back(FS.getArgIndex() + ArgsOffset, ArgCount);
415void FormatStringConverter::emitStringArgument(
unsigned ArgIndex,
423 if (!StringCStrCallExprMatcher) {
425 const auto StringDecl = type(hasUnqualifiedDesugaredType(recordType(
426 hasDeclaration(cxxRecordDecl(hasName(
"::std::basic_string"))))));
427 const auto StringExpr = expr(
428 anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));
430 StringCStrCallExprMatcher =
432 on(StringExpr.bind(
"arg")), callee(memberExpr().bind(
"member")),
433 callee(cxxMethodDecl(hasAnyName(
"c_str",
"data"),
434 returns(pointerType(pointee(isRealChar()))))))
438 auto CStrMatches =
match(*StringCStrCallExprMatcher, *Arg, *Context);
439 if (CStrMatches.size() == 1)
440 ArgCStrRemovals.push_back(CStrMatches.front());
441 else if (Arg->getType()->isPointerType()) {
442 const QualType
Pointee = Arg->getType()->getPointeeType();
446 ArgFixes.emplace_back(ArgIndex,
"reinterpret_cast<const char *>(");
450bool FormatStringConverter::emitIntegerArgument(
451 ConversionSpecifier::Kind ArgKind,
const Expr *Arg,
unsigned ArgIndex,
452 std::string &FormatSpec) {
453 const clang::QualType &ArgType = Arg->getType();
454 if (ArgType->isBooleanType()) {
458 FormatSpec.push_back(
'd');
459 }
else if (ArgType->isEnumeralType()) {
465 if (
const auto *ED = ArgType->getAsEnumDecl()) {
466 if (
const std::optional<std::string> MaybeCastType =
468 ArgFixes.emplace_back(
469 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
471 return conversionNotPossible(
472 (Twine(
"argument ") + Twine(ArgIndex) +
" has unexpected enum type")
475 }
else if (CastMismatchedIntegerTypes &&
480 if (
const std::optional<std::string> MaybeCastType =
482 ArgFixes.emplace_back(
483 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
485 return conversionNotPossible(
486 (Twine(
"argument ") + Twine(ArgIndex) +
" cannot be cast to " +
487 Twine(ArgKind == ConversionSpecifier::Kind::uArg ?
"unsigned"
489 " integer type to match format"
490 " specifier and StrictMode is enabled")
492 }
else if (
isRealCharType(ArgType) || !ArgType->isIntegerType()) {
494 FormatSpec.push_back(
'd');
502bool FormatStringConverter::emitType(
const PrintfSpecifier &FS,
const Expr *Arg,
503 std::string &FormatSpec) {
504 const ConversionSpecifier::Kind ArgKind =
505 FS.getConversionSpecifier().getKind();
507 case ConversionSpecifier::Kind::sArg:
508 emitStringArgument(FS.getArgIndex() + ArgsOffset, Arg);
510 case ConversionSpecifier::Kind::cArg:
514 FormatSpec.push_back(
'c');
516 case ConversionSpecifier::Kind::dArg:
517 case ConversionSpecifier::Kind::iArg:
518 case ConversionSpecifier::Kind::uArg:
519 if (!emitIntegerArgument(ArgKind, Arg, FS.getArgIndex() + ArgsOffset,
523 case ConversionSpecifier::Kind::pArg: {
524 const clang::QualType &ArgType = Arg->getType();
526 if (!ArgType->isNullPtrType() && !ArgType->isVoidPointerType())
527 ArgFixes.emplace_back(FS.getArgIndex() + ArgsOffset,
528 "static_cast<const void *>(");
531 case ConversionSpecifier::Kind::xArg:
532 FormatSpec.push_back(
'x');
534 case ConversionSpecifier::Kind::XArg:
535 FormatSpec.push_back(
'X');
537 case ConversionSpecifier::Kind::oArg:
538 FormatSpec.push_back(
'o');
540 case ConversionSpecifier::Kind::aArg:
541 FormatSpec.push_back(
'a');
543 case ConversionSpecifier::Kind::AArg:
544 FormatSpec.push_back(
'A');
546 case ConversionSpecifier::Kind::eArg:
547 FormatSpec.push_back(
'e');
549 case ConversionSpecifier::Kind::EArg:
550 FormatSpec.push_back(
'E');
552 case ConversionSpecifier::Kind::fArg:
553 FormatSpec.push_back(
'f');
555 case ConversionSpecifier::Kind::FArg:
556 FormatSpec.push_back(
'F');
558 case ConversionSpecifier::Kind::gArg:
559 FormatSpec.push_back(
'g');
561 case ConversionSpecifier::Kind::GArg:
562 FormatSpec.push_back(
'G');
566 return conversionNotPossible((Twine(
"argument ") +
567 Twine(FS.getArgIndex() + ArgsOffset) +
568 " has an unsupported format specifier")
578bool FormatStringConverter::convertArgument(
const PrintfSpecifier &FS,
580 std::string &StandardFormatString) {
582 assert(FS.consumesDataArgument());
584 StandardFormatString.push_back(
'{');
586 if (FS.usesPositionalArg()) {
589 assert(FS.getPositionalArgIndex() > 0U);
590 StandardFormatString.append(llvm::utostr(FS.getPositionalArgIndex() - 1));
595 std::string FormatSpec;
600 emitAlignment(FS, FormatSpec);
601 emitSign(FS, FormatSpec);
602 emitAlternativeForm(FS, FormatSpec);
604 if (FS.hasLeadingZeros())
605 FormatSpec.push_back(
'0');
607 emitFieldWidth(FS, FormatSpec);
608 emitPrecision(FS, FormatSpec);
609 maybeRotateArguments(FS);
611 if (!emitType(FS, Arg, FormatSpec))
614 if (!FormatSpec.empty()) {
615 StandardFormatString.push_back(
':');
616 StandardFormatString.append(FormatSpec);
619 StandardFormatString.push_back(
'}');
624bool FormatStringConverter::HandlePrintfSpecifier(
const PrintfSpecifier &FS,
625 const char *StartSpecifier,
626 unsigned SpecifierLen,
627 const TargetInfo &Target) {
628 const size_t StartSpecifierPos = StartSpecifier - PrintfFormatString.data();
629 assert(StartSpecifierPos + SpecifierLen <= PrintfFormatString.size());
632 assert(StartSpecifierPos >= PrintfFormatStringPos);
634 appendFormatText(StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
635 StartSpecifierPos - PrintfFormatStringPos));
637 const ConversionSpecifier::Kind ArgKind =
638 FS.getConversionSpecifier().getKind();
641 PrintfFormatStringPos = StartSpecifierPos + SpecifierLen;
642 assert(PrintfFormatStringPos <= PrintfFormatString.size());
644 FormatStringNeededRewriting =
true;
646 if (ArgKind == ConversionSpecifier::Kind::nArg) {
648 return conversionNotPossible(
"'%n' is not supported in format string");
651 if (ArgKind == ConversionSpecifier::Kind::PrintErrno) {
656 return conversionNotPossible(
"'%m' is not supported in format string");
659 if (ArgKind == ConversionSpecifier::PercentArg) {
660 StandardFormatString.push_back(
'%');
664 const unsigned ArgIndex = FS.getArgIndex() + ArgsOffset;
665 if (ArgIndex >= NumArgs) {
667 return conversionNotPossible(
668 (Twine(
"argument index ") + Twine(ArgIndex) +
" is out of range")
672 return convertArgument(FS, Args[ArgIndex]->IgnoreImplicitAsWritten(),
673 StandardFormatString);
678void FormatStringConverter::finalizeFormatText() {
680 StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
681 PrintfFormatString.size() - PrintfFormatStringPos));
682 PrintfFormatStringPos = PrintfFormatString.size();
687 const auto StandardFormatStringRef = StringRef(StandardFormatString);
688 if (Config.AllowTrailingNewlineRemoval &&
689 StandardFormatStringRef.ends_with(
"\\n") &&
690 !StandardFormatStringRef.ends_with(
"\\\\n") &&
691 !StandardFormatStringRef.ends_with(
"\\r\\n")) {
692 UsePrintNewlineFunction =
true;
693 FormatStringNeededRewriting =
true;
694 StandardFormatString.erase(StandardFormatString.end() - 2,
695 StandardFormatString.end());
698 StandardFormatString.push_back(
'\"');
702void FormatStringConverter::appendFormatText(
const StringRef Text) {
703 for (
const char Ch : Text) {
704 const auto UCh =
static_cast<unsigned char>(Ch);
706 StandardFormatString +=
"\\a";
708 StandardFormatString +=
"\\b";
710 StandardFormatString +=
"\\f";
712 StandardFormatString +=
"\\n";
714 StandardFormatString +=
"\\r";
716 StandardFormatString +=
"\\t";
718 StandardFormatString +=
"\\v";
720 StandardFormatString +=
"\\\"";
722 StandardFormatString +=
"\\\\";
723 else if (Ch ==
'{') {
724 StandardFormatString +=
"{{";
725 FormatStringNeededRewriting =
true;
726 }
else if (Ch ==
'}') {
727 StandardFormatString +=
"}}";
728 FormatStringNeededRewriting =
true;
729 }
else if (UCh < 32) {
730 StandardFormatString +=
"\\x";
731 StandardFormatString += llvm::hexdigit(UCh >> 4,
true);
732 StandardFormatString += llvm::hexdigit(UCh & 0xf,
true);
734 StandardFormatString += Ch;
739 ASTContext &Context) {
740 const auto *Arg = CStrRemovalMatch.getNodeAs<Expr>(
"arg");
741 const auto *Member = CStrRemovalMatch.getNodeAs<MemberExpr>(
"member");
742 const bool Arrow = Member->isArrow();
744 : tooling::fixit::getText(*Arg, Context).str();
750 if (FormatStringNeededRewriting) {
751 Diag << FixItHint::CreateReplacement(
752 CharSourceRange::getTokenRange(FormatExpr->getBeginLoc(),
753 FormatExpr->getEndLoc()),
754 StandardFormatString);
758 for (
auto [ValueArgIndex, ArgCount] : ArgRotates) {
759 assert(ValueArgIndex < NumArgs);
760 assert(ValueArgIndex > ArgCount);
764 if (
const auto CStrRemovalMatch =
765 std::find_if(ArgCStrRemovals.cbegin(), ArgCStrRemovals.cend(),
766 [ArgStartPos = Args[ValueArgIndex]->getBeginLoc()](
767 const BoundNodes &Match) {
770 const Expr *CStrArg = Match.getNodeAs<Expr>(
"arg");
771 return ArgStartPos == CStrArg->getBeginLoc();
773 CStrRemovalMatch != ArgCStrRemovals.end()) {
774 const std::string ArgText =
776 assert(!ArgText.empty());
778 Diag << FixItHint::CreateReplacement(
779 Args[ValueArgIndex - ArgCount]->getSourceRange(), ArgText);
782 ArgCStrRemovals.erase(CStrRemovalMatch);
784 Diag << tooling::fixit::createReplacement(*Args[ValueArgIndex - ArgCount],
785 *Args[ValueArgIndex], *Context);
789 for (
size_t Offset = 0; Offset < ArgCount; ++Offset)
790 Diag << tooling::fixit::createReplacement(
791 *Args[ValueArgIndex - Offset], *Args[ValueArgIndex - Offset - 1],
797 for (
auto &ArgFix : ArgFixes)
798 if (ArgFix.ArgIndex == ValueArgIndex)
799 ArgFix.ArgIndex = ValueArgIndex - ArgCount;
802 for (
const auto &[ArgIndex, Replacement] : ArgFixes) {
803 const SourceLocation AfterOtherSide =
808 Diag << FixItHint::CreateInsertion(Args[ArgIndex]->getBeginLoc(),
810 << FixItHint::CreateInsertion(AfterOtherSide,
")",
true);
813 for (
const auto &Match : ArgCStrRemovals) {
814 const auto *Call = Match.getNodeAs<CallExpr>(
"call");
816 if (!ArgText.empty())
817 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 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[]