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"
26#include "llvm/Support/Path.h"
29using namespace clang::analyze_printf;
32using clang::analyze_format_string::ConversionSpecifier;
37 using namespace clang;
38 const Type *DesugaredType = Ty->getUnqualifiedDesugaredType();
39 if (
const auto *BT = dyn_cast<BuiltinType>(DesugaredType))
40 return (BT->getKind() == BuiltinType::Char_U ||
41 BT->getKind() == BuiltinType::Char_S);
48static std::optional<std::string>
50 using namespace clang;
51 const auto UQT = QT.getUnqualifiedType();
52 if (
const auto *BT = dyn_cast<BuiltinType>(UQT)) {
53 switch (BT->getKind()) {
54 case BuiltinType::UChar:
55 case BuiltinType::Char_U:
56 case BuiltinType::SChar:
57 case BuiltinType::Char_S:
59 case BuiltinType::UShort:
60 case BuiltinType::Short:
62 case BuiltinType::UInt:
63 case BuiltinType::Int:
65 case BuiltinType::ULong:
66 case BuiltinType::Long:
68 case BuiltinType::ULongLong:
69 case BuiltinType::LongLong:
72 llvm::dbgs() <<
"Unknown corresponding signed type for BuiltinType '"
73 << QT.getAsString() <<
"'\n";
80 const std::string TypeName = UQT.getAsString();
81 StringRef SimplifiedTypeName{TypeName};
82 const bool InStd = SimplifiedTypeName.consume_front(
"std::");
83 const StringRef Prefix = InStd ?
"std::" :
"";
85 if (SimplifiedTypeName.starts_with(
"uint") &&
86 SimplifiedTypeName.ends_with(
"_t"))
87 return (Twine(Prefix) + SimplifiedTypeName.drop_front()).str();
89 if (SimplifiedTypeName ==
"size_t")
90 return (Twine(Prefix) +
"ssize_t").str();
92 llvm::dbgs() <<
"Unknown corresponding signed type for non-BuiltinType '"
93 << UQT.getAsString() <<
"'\n";
100static std::optional<std::string>
102 using namespace clang;
103 const auto UQT = QT.getUnqualifiedType();
104 if (
const auto *BT = dyn_cast<BuiltinType>(UQT)) {
105 switch (BT->getKind()) {
106 case BuiltinType::SChar:
107 case BuiltinType::Char_S:
108 case BuiltinType::UChar:
109 case BuiltinType::Char_U:
110 return "unsigned char";
111 case BuiltinType::Short:
112 case BuiltinType::UShort:
113 return "unsigned short";
114 case BuiltinType::Int:
115 case BuiltinType::UInt:
116 return "unsigned int";
117 case BuiltinType::Long:
118 case BuiltinType::ULong:
119 return "unsigned long";
120 case BuiltinType::LongLong:
121 case BuiltinType::ULongLong:
122 return "unsigned long long";
124 llvm::dbgs() <<
"Unknown corresponding unsigned type for BuiltinType '"
125 << UQT.getAsString() <<
"'\n";
132 const std::string TypeName = UQT.getAsString();
133 StringRef SimplifiedTypeName{TypeName};
134 const bool InStd = SimplifiedTypeName.consume_front(
"std::");
135 const StringRef Prefix = InStd ?
"std::" :
"";
137 if (SimplifiedTypeName.starts_with(
"int") &&
138 SimplifiedTypeName.ends_with(
"_t"))
139 return (Twine(Prefix) +
"u" + SimplifiedTypeName).str();
141 if (SimplifiedTypeName ==
"ssize_t")
142 return (Twine(Prefix) +
"size_t").str();
143 if (SimplifiedTypeName ==
"ptrdiff_t")
144 return (Twine(Prefix) +
"size_t").str();
146 llvm::dbgs() <<
"Unknown corresponding unsigned type for non-BuiltinType '"
147 << UQT.getAsString() <<
"'\n";
151static std::optional<std::string>
153 if (ArgKind == ConversionSpecifier::Kind::uArg)
159 const QualType &ArgType) {
160 if (
const auto *BT = 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();
191 const FunctionDecl *
FuncDecl = Call->getDirectCallee();
199 ASTContext *ContextIn,
const CallExpr *Call,
unsigned FormatArgOffset,
200 const Configuration ConfigIn,
const LangOptions &LO, SourceManager &SM,
202 : Context(ContextIn), Config(ConfigIn),
203 CastMismatchedIntegerTypes(
205 Args(Call->getArgs()), NumArgs(Call->getNumArgs()),
206 ArgsOffset(FormatArgOffset + 1), LangOpts(LO) {
207 assert(ArgsOffset <= NumArgs);
208 FormatExpr = dyn_cast<StringLiteral>(
209 Args[FormatArgOffset]->IgnoreUnlessSpelledInSource());
211 assert(FormatExpr && FormatExpr->isOrdinary());
213 if (
const std::optional<StringRef> MaybeMacroName =
214 formatStringContainsUnreplaceableMacro(Call, FormatExpr, SM, PP);
216 conversionNotPossible(
217 (
"format string contains unreplaceable macro '" + *MaybeMacroName +
"'")
222 PrintfFormatString = FormatExpr->getString();
226 const size_t EstimatedGrowth = 8;
227 StandardFormatString.reserve(PrintfFormatString.size() + EstimatedGrowth);
228 StandardFormatString.push_back(
'\"');
230 const bool IsFreeBsdkPrintf =
false;
232 using clang::analyze_format_string::ParsePrintfString;
233 ParsePrintfString(*
this, PrintfFormatString.data(),
234 PrintfFormatString.data() + PrintfFormatString.size(),
235 LangOpts, Context->getTargetInfo(), IsFreeBsdkPrintf);
236 finalizeFormatText();
239std::optional<StringRef>
240FormatStringConverter::formatStringContainsUnreplaceableMacro(
241 const CallExpr *Call,
const StringLiteral *FormatExpr, SourceManager &SM,
246 std::optional<StringRef> MaybeSurroundingMacroName;
247 if (
const SourceLocation BeginCallLoc = Call->getBeginLoc();
248 BeginCallLoc.isMacroID())
249 MaybeSurroundingMacroName =
250 Lexer::getImmediateMacroName(BeginCallLoc, SM, PP.getLangOpts());
252 for (
auto I = FormatExpr->tokloc_begin(), E = FormatExpr->tokloc_end();
254 const SourceLocation &TokenLoc = *I;
255 if (TokenLoc.isMacroID()) {
256 const StringRef MacroName =
257 Lexer::getImmediateMacroName(TokenLoc, SM, PP.getLangOpts());
259 if (MaybeSurroundingMacroName != MacroName) {
262 if (!MacroName.starts_with(
"PRI") && !MacroName.starts_with(
"__PRI"))
265 const SourceLocation TokenSpellingLoc = SM.getSpellingLoc(TokenLoc);
266 const OptionalFileEntryRef MaybeFileEntry =
267 SM.getFileEntryRefForID(SM.getFileID(TokenSpellingLoc));
271 HeaderSearch &HS = PP.getHeaderSearchInfo();
273 if (!isSystem(HS.getFileDirFlavor(*MaybeFileEntry)) ||
274 llvm::sys::path::filename(MaybeFileEntry->getName()) !=
283void FormatStringConverter::emitAlignment(
const PrintfSpecifier &FS,
284 std::string &FormatSpec) {
285 const ConversionSpecifier::Kind ArgKind =
286 FS.getConversionSpecifier().getKind();
289 if (FS.getFieldWidth().getHowSpecified() != OptionalAmount::NotSpecified) {
290 if (ArgKind == ConversionSpecifier::sArg) {
293 if (!FS.isLeftJustified())
294 FormatSpec.push_back(
'>');
298 if (FS.isLeftJustified())
299 FormatSpec.push_back(
'<');
304void FormatStringConverter::emitSign(
const PrintfSpecifier &FS,
305 std::string &FormatSpec) {
311 if (Spec.isAnyIntArg() || Spec.isDoubleArg()) {
313 if (FS.hasPlusPrefix())
314 FormatSpec.push_back(
'+');
315 else if (FS.hasSpacePrefix())
316 FormatSpec.push_back(
' ');
320void FormatStringConverter::emitAlternativeForm(
const PrintfSpecifier &FS,
321 std::string &FormatSpec) {
322 if (FS.hasAlternativeForm()) {
323 switch (FS.getConversionSpecifier().getKind()) {
324 case ConversionSpecifier::Kind::aArg:
325 case ConversionSpecifier::Kind::AArg:
326 case ConversionSpecifier::Kind::eArg:
327 case ConversionSpecifier::Kind::EArg:
328 case ConversionSpecifier::Kind::fArg:
329 case ConversionSpecifier::Kind::FArg:
330 case ConversionSpecifier::Kind::gArg:
331 case ConversionSpecifier::Kind::GArg:
332 case ConversionSpecifier::Kind::xArg:
333 case ConversionSpecifier::Kind::XArg:
334 case ConversionSpecifier::Kind::oArg:
335 FormatSpec.push_back(
'#');
344void FormatStringConverter::emitFieldWidth(
const PrintfSpecifier &FS,
345 std::string &FormatSpec) {
347 const OptionalAmount FieldWidth = FS.getFieldWidth();
348 switch (FieldWidth.getHowSpecified()) {
349 case OptionalAmount::NotSpecified:
351 case OptionalAmount::Constant:
352 FormatSpec.append(llvm::utostr(FieldWidth.getConstantAmount()));
354 case OptionalAmount::Arg:
355 FormatSpec.push_back(
'{');
356 if (FieldWidth.usesPositionalArg()) {
359 assert(FieldWidth.getPositionalArgIndex() > 0U);
360 FormatSpec.append(llvm::utostr(FieldWidth.getPositionalArgIndex() - 1));
362 FormatSpec.push_back(
'}');
364 case OptionalAmount::Invalid:
370void FormatStringConverter::emitPrecision(
const PrintfSpecifier &FS,
371 std::string &FormatSpec) {
372 const OptionalAmount FieldPrecision = FS.getPrecision();
373 switch (FieldPrecision.getHowSpecified()) {
374 case OptionalAmount::NotSpecified:
376 case OptionalAmount::Constant:
377 FormatSpec.push_back(
'.');
378 FormatSpec.append(llvm::utostr(FieldPrecision.getConstantAmount()));
380 case OptionalAmount::Arg:
381 FormatSpec.push_back(
'.');
382 FormatSpec.push_back(
'{');
383 if (FieldPrecision.usesPositionalArg()) {
386 assert(FieldPrecision.getPositionalArgIndex() > 0U);
388 llvm::utostr(FieldPrecision.getPositionalArgIndex() - 1));
390 FormatSpec.push_back(
'}');
392 case OptionalAmount::Invalid:
397void FormatStringConverter::maybeRotateArguments(
const PrintfSpecifier &FS) {
398 unsigned ArgCount = 0;
399 const OptionalAmount FieldWidth = FS.getFieldWidth();
400 const OptionalAmount FieldPrecision = FS.getPrecision();
402 if (FieldWidth.getHowSpecified() == OptionalAmount::Arg &&
403 !FieldWidth.usesPositionalArg())
405 if (FieldPrecision.getHowSpecified() == OptionalAmount::Arg &&
406 !FieldPrecision.usesPositionalArg())
410 ArgRotates.emplace_back(FS.getArgIndex() + ArgsOffset, ArgCount);
413void FormatStringConverter::emitStringArgument(
unsigned ArgIndex,
421 if (!StringCStrCallExprMatcher) {
423 const auto StringDecl = type(hasUnqualifiedDesugaredType(recordType(
424 hasDeclaration(cxxRecordDecl(hasName(
"::std::basic_string"))))));
425 const auto StringExpr = expr(
426 anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));
428 StringCStrCallExprMatcher =
430 on(StringExpr.bind(
"arg")), callee(memberExpr().bind(
"member")),
431 callee(cxxMethodDecl(hasAnyName(
"c_str",
"data"),
432 returns(pointerType(pointee(isRealChar()))))))
436 auto CStrMatches =
match(*StringCStrCallExprMatcher, *Arg, *Context);
437 if (CStrMatches.size() == 1) {
438 ArgCStrRemovals.push_back(CStrMatches.front());
439 }
else if (Arg->getType()->isPointerType()) {
440 const QualType
Pointee = Arg->getType()->getPointeeType();
444 ArgFixes.emplace_back(ArgIndex,
"reinterpret_cast<const char *>(");
448bool FormatStringConverter::emitIntegerArgument(
449 ConversionSpecifier::Kind ArgKind,
const Expr *Arg,
unsigned ArgIndex,
450 std::string &FormatSpec) {
451 const QualType &ArgType = Arg->getType();
452 if (ArgType->isBooleanType()) {
456 FormatSpec.push_back(
'd');
457 }
else if (ArgType->isEnumeralType()) {
463 if (
const auto *ED = ArgType->getAsEnumDecl()) {
464 if (
const std::optional<std::string> MaybeCastType =
466 ArgFixes.emplace_back(
467 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
469 return conversionNotPossible(
470 (Twine(
"argument ") + Twine(ArgIndex) +
" has unexpected enum type")
473 }
else if (CastMismatchedIntegerTypes &&
478 if (
const std::optional<std::string> MaybeCastType =
480 ArgFixes.emplace_back(
481 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
483 return conversionNotPossible(
484 (Twine(
"argument ") + Twine(ArgIndex) +
" cannot be cast to " +
485 Twine(ArgKind == ConversionSpecifier::Kind::uArg ?
"unsigned"
487 " integer type to match format"
488 " specifier and StrictMode is enabled")
490 }
else if (
isRealCharType(ArgType) || !ArgType->isIntegerType()) {
492 FormatSpec.push_back(
'd');
500bool FormatStringConverter::emitType(
const PrintfSpecifier &FS,
const Expr *Arg,
501 std::string &FormatSpec) {
502 const ConversionSpecifier::Kind ArgKind =
503 FS.getConversionSpecifier().getKind();
505 case ConversionSpecifier::Kind::sArg:
506 emitStringArgument(FS.getArgIndex() + ArgsOffset, Arg);
508 case ConversionSpecifier::Kind::cArg:
512 FormatSpec.push_back(
'c');
514 case ConversionSpecifier::Kind::dArg:
515 case ConversionSpecifier::Kind::iArg:
516 case ConversionSpecifier::Kind::uArg:
517 if (!emitIntegerArgument(ArgKind, Arg, FS.getArgIndex() + ArgsOffset,
521 case ConversionSpecifier::Kind::pArg: {
522 const QualType &ArgType = Arg->getType();
524 if (!ArgType->isNullPtrType() && !ArgType->isVoidPointerType())
525 ArgFixes.emplace_back(FS.getArgIndex() + ArgsOffset,
526 "static_cast<const void *>(");
529 case ConversionSpecifier::Kind::xArg:
530 FormatSpec.push_back(
'x');
532 case ConversionSpecifier::Kind::XArg:
533 FormatSpec.push_back(
'X');
535 case ConversionSpecifier::Kind::oArg:
536 FormatSpec.push_back(
'o');
538 case ConversionSpecifier::Kind::aArg:
539 FormatSpec.push_back(
'a');
541 case ConversionSpecifier::Kind::AArg:
542 FormatSpec.push_back(
'A');
544 case ConversionSpecifier::Kind::eArg:
545 FormatSpec.push_back(
'e');
547 case ConversionSpecifier::Kind::EArg:
548 FormatSpec.push_back(
'E');
550 case ConversionSpecifier::Kind::fArg:
551 FormatSpec.push_back(
'f');
553 case ConversionSpecifier::Kind::FArg:
554 FormatSpec.push_back(
'F');
556 case ConversionSpecifier::Kind::gArg:
557 FormatSpec.push_back(
'g');
559 case ConversionSpecifier::Kind::GArg:
560 FormatSpec.push_back(
'G');
564 return conversionNotPossible((Twine(
"argument ") +
565 Twine(FS.getArgIndex() + ArgsOffset) +
566 " has an unsupported format specifier")
576bool FormatStringConverter::convertArgument(
const PrintfSpecifier &FS,
578 std::string &StandardFormatString) {
580 assert(FS.consumesDataArgument());
582 StandardFormatString.push_back(
'{');
584 if (FS.usesPositionalArg()) {
587 assert(FS.getPositionalArgIndex() > 0U);
588 StandardFormatString.append(llvm::utostr(FS.getPositionalArgIndex() - 1));
593 std::string FormatSpec;
598 emitAlignment(FS, FormatSpec);
599 emitSign(FS, FormatSpec);
600 emitAlternativeForm(FS, FormatSpec);
602 if (FS.hasLeadingZeros())
603 FormatSpec.push_back(
'0');
605 emitFieldWidth(FS, FormatSpec);
606 emitPrecision(FS, FormatSpec);
607 maybeRotateArguments(FS);
609 if (!emitType(FS, Arg, FormatSpec))
612 if (!FormatSpec.empty()) {
613 StandardFormatString.push_back(
':');
614 StandardFormatString.append(FormatSpec);
617 StandardFormatString.push_back(
'}');
622bool FormatStringConverter::HandlePrintfSpecifier(
const PrintfSpecifier &FS,
623 const char *StartSpecifier,
624 unsigned SpecifierLen,
625 const TargetInfo &Target) {
626 const size_t StartSpecifierPos = StartSpecifier - PrintfFormatString.data();
627 assert(StartSpecifierPos + SpecifierLen <= PrintfFormatString.size());
630 assert(StartSpecifierPos >= PrintfFormatStringPos);
632 appendFormatText(StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
633 StartSpecifierPos - PrintfFormatStringPos));
635 const ConversionSpecifier::Kind ArgKind =
636 FS.getConversionSpecifier().getKind();
639 PrintfFormatStringPos = StartSpecifierPos + SpecifierLen;
640 assert(PrintfFormatStringPos <= PrintfFormatString.size());
642 FormatStringNeededRewriting =
true;
644 if (ArgKind == ConversionSpecifier::Kind::nArg) {
646 return conversionNotPossible(
"'%n' is not supported in format string");
649 if (ArgKind == ConversionSpecifier::Kind::PrintErrno) {
654 return conversionNotPossible(
"'%m' is not supported in format string");
657 if (ArgKind == ConversionSpecifier::PercentArg) {
658 StandardFormatString.push_back(
'%');
662 const unsigned ArgIndex = FS.getArgIndex() + ArgsOffset;
663 if (ArgIndex >= NumArgs) {
665 return conversionNotPossible(
666 (Twine(
"argument index ") + Twine(ArgIndex) +
" is out of range")
670 return convertArgument(FS, Args[ArgIndex]->IgnoreImplicitAsWritten(),
671 StandardFormatString);
676void FormatStringConverter::finalizeFormatText() {
678 StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
679 PrintfFormatString.size() - PrintfFormatStringPos));
680 PrintfFormatStringPos = PrintfFormatString.size();
685 const auto StandardFormatStringRef = StringRef(StandardFormatString);
686 if (Config.AllowTrailingNewlineRemoval &&
687 StandardFormatStringRef.ends_with(
"\\n") &&
688 !StandardFormatStringRef.ends_with(
"\\\\n") &&
689 !StandardFormatStringRef.ends_with(
"\\r\\n")) {
690 UsePrintNewlineFunction =
true;
691 FormatStringNeededRewriting =
true;
692 StandardFormatString.erase(StandardFormatString.end() - 2,
693 StandardFormatString.end());
696 StandardFormatString.push_back(
'\"');
700void FormatStringConverter::appendFormatText(
const StringRef Text) {
701 for (
const char Ch : Text) {
702 const auto UCh =
static_cast<unsigned char>(Ch);
704 StandardFormatString +=
"\\a";
705 }
else if (Ch ==
'\b') {
706 StandardFormatString +=
"\\b";
707 }
else if (Ch ==
'\f') {
708 StandardFormatString +=
"\\f";
709 }
else if (Ch ==
'\n') {
710 StandardFormatString +=
"\\n";
711 }
else if (Ch ==
'\r') {
712 StandardFormatString +=
"\\r";
713 }
else if (Ch ==
'\t') {
714 StandardFormatString +=
"\\t";
715 }
else if (Ch ==
'\v') {
716 StandardFormatString +=
"\\v";
717 }
else if (Ch ==
'\"') {
718 StandardFormatString +=
"\\\"";
719 }
else if (Ch ==
'\\') {
720 StandardFormatString +=
"\\\\";
721 }
else if (Ch ==
'{') {
722 StandardFormatString +=
"{{";
723 FormatStringNeededRewriting =
true;
724 }
else if (Ch ==
'}') {
725 StandardFormatString +=
"}}";
726 FormatStringNeededRewriting =
true;
727 }
else if (UCh < 32) {
728 StandardFormatString +=
"\\x";
729 StandardFormatString += llvm::hexdigit(UCh >> 4,
true);
730 StandardFormatString += llvm::hexdigit(UCh & 0xf,
true);
732 StandardFormatString += Ch;
738 const ASTContext &Context) {
739 const auto *Arg = CStrRemovalMatch.getNodeAs<Expr>(
"arg");
740 const auto *Member = CStrRemovalMatch.getNodeAs<MemberExpr>(
"member");
741 const bool Arrow = Member->isArrow();
743 : tooling::fixit::getText(*Arg, Context).str();
749 if (FormatStringNeededRewriting) {
750 Diag << FixItHint::CreateReplacement(
751 CharSourceRange::getTokenRange(FormatExpr->getBeginLoc(),
752 FormatExpr->getEndLoc()),
753 StandardFormatString);
757 for (
auto [ValueArgIndex, ArgCount] : ArgRotates) {
758 assert(ValueArgIndex < NumArgs);
759 assert(ValueArgIndex > ArgCount);
763 if (
const auto CStrRemovalMatch =
764 llvm::find_if(ArgCStrRemovals,
765 [ArgStartPos = Args[ValueArgIndex]->getBeginLoc()](
766 const BoundNodes &Match) {
769 const Expr *CStrArg = Match.getNodeAs<Expr>(
"arg");
770 return ArgStartPos == CStrArg->getBeginLoc();
772 CStrRemovalMatch != ArgCStrRemovals.end()) {
773 const std::string ArgText =
775 assert(!ArgText.empty());
777 Diag << FixItHint::CreateReplacement(
778 Args[ValueArgIndex - ArgCount]->getSourceRange(), ArgText);
781 ArgCStrRemovals.erase(CStrRemovalMatch);
783 Diag << tooling::fixit::createReplacement(*Args[ValueArgIndex - ArgCount],
784 *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 std::optional<Token> NextToken =
808 const SourceLocation AfterOtherSide = NextToken->getLocation();
810 Diag << FixItHint::CreateInsertion(Args[ArgIndex]->getBeginLoc(),
812 << FixItHint::CreateInsertion(AfterOtherSide,
")",
true);
815 for (
const auto &Match : ArgCStrRemovals) {
816 const auto *Call = Match.getNodeAs<CallExpr>(
"call");
818 if (!ArgText.empty())
819 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 bool isMatchingSignedness(ConversionSpecifier::Kind ArgKind, const QualType &ArgType)
static std::string withoutCStrReplacement(const BoundNodes &CStrRemovalMatch, const ASTContext &Context)
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[]