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/Lex/Preprocessor.h"
22#include "clang/Tooling/FixIt.h"
23#include "llvm/ADT/StringExtras.h"
24#include "llvm/Support/Debug.h"
27using namespace clang::analyze_printf;
30using clang::analyze_format_string::ConversionSpecifier;
35 using namespace clang;
36 const Type *DesugaredType = Ty->getUnqualifiedDesugaredType();
37 if (
const auto *BT = llvm::dyn_cast<BuiltinType>(DesugaredType))
38 return (BT->getKind() == BuiltinType::Char_U ||
39 BT->getKind() == BuiltinType::Char_S);
46static std::optional<std::string>
48 using namespace clang;
49 const auto UQT = QT.getUnqualifiedType();
50 if (
const auto *BT = llvm::dyn_cast<BuiltinType>(UQT)) {
51 switch (BT->getKind()) {
52 case BuiltinType::UChar:
53 case BuiltinType::Char_U:
54 case BuiltinType::SChar:
55 case BuiltinType::Char_S:
57 case BuiltinType::UShort:
58 case BuiltinType::Short:
60 case BuiltinType::UInt:
61 case BuiltinType::Int:
63 case BuiltinType::ULong:
64 case BuiltinType::Long:
66 case BuiltinType::ULongLong:
67 case BuiltinType::LongLong:
70 llvm::dbgs() <<
"Unknown corresponding signed type for BuiltinType '"
71 << QT.getAsString() <<
"'\n";
78 const std::string TypeName = UQT.getAsString();
79 StringRef SimplifiedTypeName{TypeName};
80 const bool InStd = SimplifiedTypeName.consume_front(
"std::");
81 const StringRef Prefix = InStd ?
"std::" :
"";
83 if (SimplifiedTypeName.starts_with(
"uint") &&
84 SimplifiedTypeName.ends_with(
"_t"))
85 return (Twine(Prefix) + SimplifiedTypeName.drop_front()).str();
87 if (SimplifiedTypeName ==
"size_t")
88 return (Twine(Prefix) +
"ssize_t").str();
90 llvm::dbgs() <<
"Unknown corresponding signed type for non-BuiltinType '"
91 << UQT.getAsString() <<
"'\n";
98static std::optional<std::string>
100 using namespace clang;
101 const auto UQT = QT.getUnqualifiedType();
102 if (
const auto *BT = llvm::dyn_cast<BuiltinType>(UQT)) {
103 switch (BT->getKind()) {
104 case BuiltinType::SChar:
105 case BuiltinType::Char_S:
106 case BuiltinType::UChar:
107 case BuiltinType::Char_U:
108 return "unsigned char";
109 case BuiltinType::Short:
110 case BuiltinType::UShort:
111 return "unsigned short";
112 case BuiltinType::Int:
113 case BuiltinType::UInt:
114 return "unsigned int";
115 case BuiltinType::Long:
116 case BuiltinType::ULong:
117 return "unsigned long";
118 case BuiltinType::LongLong:
119 case BuiltinType::ULongLong:
120 return "unsigned long long";
122 llvm::dbgs() <<
"Unknown corresponding unsigned type for BuiltinType '"
123 << UQT.getAsString() <<
"'\n";
130 const std::string TypeName = UQT.getAsString();
131 StringRef SimplifiedTypeName{TypeName};
132 const bool InStd = SimplifiedTypeName.consume_front(
"std::");
133 const StringRef Prefix = InStd ?
"std::" :
"";
135 if (SimplifiedTypeName.starts_with(
"int") &&
136 SimplifiedTypeName.ends_with(
"_t"))
137 return (Twine(Prefix) +
"u" + SimplifiedTypeName).str();
139 if (SimplifiedTypeName ==
"ssize_t")
140 return (Twine(Prefix) +
"size_t").str();
141 if (SimplifiedTypeName ==
"ptrdiff_t")
142 return (Twine(Prefix) +
"size_t").str();
144 llvm::dbgs() <<
"Unknown corresponding unsigned type for non-BuiltinType '"
145 << UQT.getAsString() <<
"'\n";
149static std::optional<std::string>
151 const clang::QualType &QT) {
152 if (ArgKind == ConversionSpecifier::Kind::uArg)
158 const clang::QualType &ArgType) {
159 if (
const auto *BT = llvm::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();
192 const FunctionDecl *
FuncDecl = Call->getDirectCallee();
200 ASTContext *ContextIn,
const CallExpr *Call,
unsigned FormatArgOffset,
201 const Configuration ConfigIn,
const LangOptions &LO, SourceManager &SM,
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());
212 if (!FormatExpr || !FormatExpr->isOrdinary()) {
214 conversionNotPossible(
"first argument is not a narrow string literal");
218 if (
const std::optional<StringRef> MaybeMacroName =
219 formatStringContainsUnreplaceableMacro(Call, FormatExpr, SM, PP);
221 conversionNotPossible(
222 (
"format string contains unreplaceable macro '" + *MaybeMacroName +
"'")
227 PrintfFormatString = FormatExpr->getString();
231 const size_t EstimatedGrowth = 8;
232 StandardFormatString.reserve(PrintfFormatString.size() + EstimatedGrowth);
233 StandardFormatString.push_back(
'\"');
235 const bool IsFreeBsdkPrintf =
false;
237 using clang::analyze_format_string::ParsePrintfString;
238 ParsePrintfString(*
this, PrintfFormatString.data(),
239 PrintfFormatString.data() + PrintfFormatString.size(),
240 LangOpts, Context->getTargetInfo(), IsFreeBsdkPrintf);
241 finalizeFormatText();
244std::optional<StringRef>
245FormatStringConverter::formatStringContainsUnreplaceableMacro(
246 const CallExpr *Call,
const StringLiteral *FormatExpr, SourceManager &SM,
251 std::optional<StringRef> MaybeSurroundingMacroName;
252 if (SourceLocation BeginCallLoc = Call->getBeginLoc();
253 BeginCallLoc.isMacroID())
254 MaybeSurroundingMacroName =
255 Lexer::getImmediateMacroName(BeginCallLoc, SM, PP.getLangOpts());
257 for (
auto I = FormatExpr->tokloc_begin(),
E = FormatExpr->tokloc_end();
259 const SourceLocation &TokenLoc = *I;
260 if (TokenLoc.isMacroID()) {
262 Lexer::getImmediateMacroName(TokenLoc, SM, PP.getLangOpts());
264 if (MaybeSurroundingMacroName !=
MacroName) {
270 const SourceLocation TokenSpellingLoc = SM.getSpellingLoc(TokenLoc);
271 const OptionalFileEntryRef MaybeFileEntry =
272 SM.getFileEntryRefForID(SM.getFileID(TokenSpellingLoc));
276 HeaderSearch &HS = PP.getHeaderSearchInfo();
278 if (!isSystem(HS.getFileDirFlavor(*MaybeFileEntry)) ||
279 llvm::sys::path::filename(MaybeFileEntry->getName()) !=
288void FormatStringConverter::emitAlignment(
const PrintfSpecifier &FS,
289 std::string &FormatSpec) {
290 ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind();
293 if (FS.getFieldWidth().getHowSpecified() != OptionalAmount::NotSpecified) {
294 if (ArgKind == ConversionSpecifier::sArg) {
297 if (!FS.isLeftJustified())
298 FormatSpec.push_back(
'>');
302 if (FS.isLeftJustified())
303 FormatSpec.push_back(
'<');
308void FormatStringConverter::emitSign(
const PrintfSpecifier &FS,
309 std::string &FormatSpec) {
315 if (Spec.isAnyIntArg() || Spec.isDoubleArg()) {
317 if (FS.hasPlusPrefix())
318 FormatSpec.push_back(
'+');
319 else if (FS.hasSpacePrefix())
320 FormatSpec.push_back(
' ');
324void FormatStringConverter::emitAlternativeForm(
const PrintfSpecifier &FS,
325 std::string &FormatSpec) {
326 if (FS.hasAlternativeForm()) {
327 switch (FS.getConversionSpecifier().getKind()) {
328 case ConversionSpecifier::Kind::aArg:
329 case ConversionSpecifier::Kind::AArg:
330 case ConversionSpecifier::Kind::eArg:
331 case ConversionSpecifier::Kind::EArg:
332 case ConversionSpecifier::Kind::fArg:
333 case ConversionSpecifier::Kind::FArg:
334 case ConversionSpecifier::Kind::gArg:
335 case ConversionSpecifier::Kind::GArg:
336 case ConversionSpecifier::Kind::xArg:
337 case ConversionSpecifier::Kind::XArg:
338 case ConversionSpecifier::Kind::oArg:
339 FormatSpec.push_back(
'#');
348void FormatStringConverter::emitFieldWidth(
const PrintfSpecifier &FS,
349 std::string &FormatSpec) {
351 const OptionalAmount FieldWidth = FS.getFieldWidth();
352 switch (FieldWidth.getHowSpecified()) {
353 case OptionalAmount::NotSpecified:
355 case OptionalAmount::Constant:
356 FormatSpec.append(llvm::utostr(FieldWidth.getConstantAmount()));
358 case OptionalAmount::Arg:
359 FormatSpec.push_back(
'{');
360 if (FieldWidth.usesPositionalArg()) {
363 assert(FieldWidth.getPositionalArgIndex() > 0U);
364 FormatSpec.append(llvm::utostr(FieldWidth.getPositionalArgIndex() - 1));
366 FormatSpec.push_back(
'}');
368 case OptionalAmount::Invalid:
374void FormatStringConverter::emitPrecision(
const PrintfSpecifier &FS,
375 std::string &FormatSpec) {
376 const OptionalAmount FieldPrecision = FS.getPrecision();
377 switch (FieldPrecision.getHowSpecified()) {
378 case OptionalAmount::NotSpecified:
380 case OptionalAmount::Constant:
381 FormatSpec.push_back(
'.');
382 FormatSpec.append(llvm::utostr(FieldPrecision.getConstantAmount()));
384 case OptionalAmount::Arg:
385 FormatSpec.push_back(
'.');
386 FormatSpec.push_back(
'{');
387 if (FieldPrecision.usesPositionalArg()) {
390 assert(FieldPrecision.getPositionalArgIndex() > 0U);
392 llvm::utostr(FieldPrecision.getPositionalArgIndex() - 1));
394 FormatSpec.push_back(
'}');
396 case OptionalAmount::Invalid:
401void FormatStringConverter::maybeRotateArguments(
const PrintfSpecifier &FS) {
402 unsigned ArgCount = 0;
403 const OptionalAmount FieldWidth = FS.getFieldWidth();
404 const OptionalAmount FieldPrecision = FS.getPrecision();
406 if (FieldWidth.getHowSpecified() == OptionalAmount::Arg &&
407 !FieldWidth.usesPositionalArg())
409 if (FieldPrecision.getHowSpecified() == OptionalAmount::Arg &&
410 !FieldPrecision.usesPositionalArg())
414 ArgRotates.emplace_back(FS.getArgIndex() + ArgsOffset, ArgCount);
417void FormatStringConverter::emitStringArgument(
unsigned ArgIndex,
425 if (!StringCStrCallExprMatcher) {
427 const auto StringDecl = type(hasUnqualifiedDesugaredType(recordType(
428 hasDeclaration(cxxRecordDecl(hasName(
"::std::basic_string"))))));
429 const auto StringExpr = expr(
430 anyOf(hasType(StringDecl), hasType(qualType(pointsTo(StringDecl)))));
432 StringCStrCallExprMatcher =
434 on(StringExpr.bind(
"arg")), callee(memberExpr().bind(
"member")),
435 callee(cxxMethodDecl(hasAnyName(
"c_str",
"data"),
436 returns(pointerType(pointee(isRealChar()))))))
440 auto CStrMatches =
match(*StringCStrCallExprMatcher, *Arg, *Context);
441 if (CStrMatches.size() == 1)
442 ArgCStrRemovals.push_back(CStrMatches.front());
443 else if (Arg->getType()->isPointerType()) {
444 const QualType
Pointee = Arg->getType()->getPointeeType();
448 ArgFixes.emplace_back(ArgIndex,
"reinterpret_cast<const char *>(");
452bool FormatStringConverter::emitIntegerArgument(
453 ConversionSpecifier::Kind ArgKind,
const Expr *Arg,
unsigned ArgIndex,
454 std::string &FormatSpec) {
455 const clang::QualType &ArgType = Arg->getType();
456 if (ArgType->isBooleanType()) {
460 FormatSpec.push_back(
'd');
461 }
else if (ArgType->isEnumeralType()) {
467 if (
const auto *ET = ArgType->getAs<EnumType>()) {
468 if (
const std::optional<std::string> MaybeCastType =
470 ArgFixes.emplace_back(
471 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
473 return conversionNotPossible(
474 (Twine(
"argument ") + Twine(ArgIndex) +
" has unexpected enum type")
477 }
else if (CastMismatchedIntegerTypes &&
482 if (
const std::optional<std::string> MaybeCastType =
484 ArgFixes.emplace_back(
485 ArgIndex, (Twine(
"static_cast<") + *MaybeCastType +
">(").str());
487 return conversionNotPossible(
488 (Twine(
"argument ") + Twine(ArgIndex) +
" cannot be cast to " +
489 Twine(ArgKind == ConversionSpecifier::Kind::uArg ?
"unsigned"
491 " integer type to match format"
492 " specifier and StrictMode is enabled")
494 }
else if (
isRealCharType(ArgType) || !ArgType->isIntegerType()) {
496 FormatSpec.push_back(
'd');
504bool FormatStringConverter::emitType(
const PrintfSpecifier &FS,
const Expr *Arg,
505 std::string &FormatSpec) {
506 ConversionSpecifier::Kind ArgKind = FS.getConversionSpecifier().getKind();
508 case ConversionSpecifier::Kind::sArg:
509 emitStringArgument(FS.getArgIndex() + ArgsOffset, Arg);
511 case ConversionSpecifier::Kind::cArg:
515 FormatSpec.push_back(
'c');
517 case ConversionSpecifier::Kind::dArg:
518 case ConversionSpecifier::Kind::iArg:
519 case ConversionSpecifier::Kind::uArg:
520 if (!emitIntegerArgument(ArgKind, Arg, FS.getArgIndex() + ArgsOffset,
524 case ConversionSpecifier::Kind::pArg: {
525 const clang::QualType &ArgType = Arg->getType();
527 if (!ArgType->isNullPtrType() && !ArgType->isVoidPointerType())
528 ArgFixes.emplace_back(FS.getArgIndex() + ArgsOffset,
529 "static_cast<const void *>(");
532 case ConversionSpecifier::Kind::xArg:
533 FormatSpec.push_back(
'x');
535 case ConversionSpecifier::Kind::XArg:
536 FormatSpec.push_back(
'X');
538 case ConversionSpecifier::Kind::oArg:
539 FormatSpec.push_back(
'o');
541 case ConversionSpecifier::Kind::aArg:
542 FormatSpec.push_back(
'a');
544 case ConversionSpecifier::Kind::AArg:
545 FormatSpec.push_back(
'A');
547 case ConversionSpecifier::Kind::eArg:
548 FormatSpec.push_back(
'e');
550 case ConversionSpecifier::Kind::EArg:
551 FormatSpec.push_back(
'E');
553 case ConversionSpecifier::Kind::fArg:
554 FormatSpec.push_back(
'f');
556 case ConversionSpecifier::Kind::FArg:
557 FormatSpec.push_back(
'F');
559 case ConversionSpecifier::Kind::gArg:
560 FormatSpec.push_back(
'g');
562 case ConversionSpecifier::Kind::GArg:
563 FormatSpec.push_back(
'G');
567 return conversionNotPossible((Twine(
"argument ") +
568 Twine(FS.getArgIndex() + ArgsOffset) +
569 " has an unsupported format specifier")
579bool FormatStringConverter::convertArgument(
const PrintfSpecifier &FS,
581 std::string &StandardFormatString) {
583 assert(FS.consumesDataArgument());
585 StandardFormatString.push_back(
'{');
587 if (FS.usesPositionalArg()) {
590 assert(FS.getPositionalArgIndex() > 0U);
591 StandardFormatString.append(llvm::utostr(FS.getPositionalArgIndex() - 1));
596 std::string FormatSpec;
601 emitAlignment(FS, FormatSpec);
602 emitSign(FS, FormatSpec);
603 emitAlternativeForm(FS, FormatSpec);
605 if (FS.hasLeadingZeros())
606 FormatSpec.push_back(
'0');
608 emitFieldWidth(FS, FormatSpec);
609 emitPrecision(FS, FormatSpec);
610 maybeRotateArguments(FS);
612 if (!emitType(FS, Arg, FormatSpec))
615 if (!FormatSpec.empty()) {
616 StandardFormatString.push_back(
':');
617 StandardFormatString.append(FormatSpec);
620 StandardFormatString.push_back(
'}');
625bool FormatStringConverter::HandlePrintfSpecifier(
const PrintfSpecifier &FS,
626 const char *StartSpecifier,
627 unsigned SpecifierLen,
628 const TargetInfo &Target) {
630 const size_t StartSpecifierPos = StartSpecifier - PrintfFormatString.data();
631 assert(StartSpecifierPos + SpecifierLen <= PrintfFormatString.size());
634 assert(StartSpecifierPos >= PrintfFormatStringPos);
636 appendFormatText(StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
637 StartSpecifierPos - PrintfFormatStringPos));
639 const ConversionSpecifier::Kind ArgKind =
640 FS.getConversionSpecifier().getKind();
643 PrintfFormatStringPos = StartSpecifierPos + SpecifierLen;
644 assert(PrintfFormatStringPos <= PrintfFormatString.size());
646 FormatStringNeededRewriting =
true;
648 if (ArgKind == ConversionSpecifier::Kind::nArg) {
650 return conversionNotPossible(
"'%n' is not supported in format string");
653 if (ArgKind == ConversionSpecifier::Kind::PrintErrno) {
658 return conversionNotPossible(
"'%m' is not supported in format string");
661 if (ArgKind == ConversionSpecifier::PercentArg) {
662 StandardFormatString.push_back(
'%');
666 const unsigned ArgIndex = FS.getArgIndex() + ArgsOffset;
667 if (ArgIndex >= NumArgs) {
669 return conversionNotPossible(
670 (Twine(
"argument index ") + Twine(ArgIndex) +
" is out of range")
674 return convertArgument(FS, Args[ArgIndex]->IgnoreImplicitAsWritten(),
675 StandardFormatString);
680void FormatStringConverter::finalizeFormatText() {
682 StringRef(PrintfFormatString.begin() + PrintfFormatStringPos,
683 PrintfFormatString.size() - PrintfFormatStringPos));
684 PrintfFormatStringPos = PrintfFormatString.size();
689 const auto StandardFormatStringRef = StringRef(StandardFormatString);
691 StandardFormatStringRef.ends_with(
"\\n") &&
692 !StandardFormatStringRef.ends_with(
"\\\\n") &&
693 !StandardFormatStringRef.ends_with(
"\\r\\n")) {
694 UsePrintNewlineFunction =
true;
695 FormatStringNeededRewriting =
true;
696 StandardFormatString.erase(StandardFormatString.end() - 2,
697 StandardFormatString.end());
700 StandardFormatString.push_back(
'\"');
704void FormatStringConverter::appendFormatText(
const StringRef
Text) {
705 for (
const char Ch :
Text) {
707 StandardFormatString +=
"\\a";
709 StandardFormatString +=
"\\b";
711 StandardFormatString +=
"\\f";
713 StandardFormatString +=
"\\n";
715 StandardFormatString +=
"\\r";
717 StandardFormatString +=
"\\t";
719 StandardFormatString +=
"\\v";
721 StandardFormatString +=
"\\\"";
723 StandardFormatString +=
"\\\\";
724 else if (Ch ==
'{') {
725 StandardFormatString +=
"{{";
726 FormatStringNeededRewriting =
true;
727 }
else if (Ch ==
'}') {
728 StandardFormatString +=
"}}";
729 FormatStringNeededRewriting =
true;
730 }
else if (Ch < 32) {
731 StandardFormatString +=
"\\x";
732 StandardFormatString += llvm::hexdigit(Ch >> 4,
true);
733 StandardFormatString += llvm::hexdigit(Ch & 0xf,
true);
735 StandardFormatString += Ch;
740 ASTContext &Context) {
741 const auto *Arg = CStrRemovalMatch.getNodeAs<Expr>(
"arg");
742 const auto *Member = CStrRemovalMatch.getNodeAs<MemberExpr>(
"member");
743 const bool Arrow = Member->isArrow();
745 : tooling::fixit::getText(*Arg, Context).str();
751 if (FormatStringNeededRewriting) {
752 Diag << FixItHint::CreateReplacement(
753 CharSourceRange::getTokenRange(FormatExpr->getBeginLoc(),
754 FormatExpr->getEndLoc()),
755 StandardFormatString);
759 for (
auto [ValueArgIndex, ArgCount] : ArgRotates) {
760 assert(ValueArgIndex < NumArgs);
761 assert(ValueArgIndex > ArgCount);
765 if (
const auto CStrRemovalMatch =
766 std::find_if(ArgCStrRemovals.cbegin(), ArgCStrRemovals.cend(),
767 [ArgStartPos = Args[ValueArgIndex]->getBeginLoc()](
768 const BoundNodes &Match) {
771 const Expr *CStrArg = Match.getNodeAs<Expr>(
"arg");
772 return ArgStartPos == CStrArg->getBeginLoc();
774 CStrRemovalMatch != ArgCStrRemovals.end()) {
775 const std::string ArgText =
777 assert(!ArgText.empty());
779 Diag << FixItHint::CreateReplacement(
780 Args[ValueArgIndex - ArgCount]->getSourceRange(), ArgText);
783 ArgCStrRemovals.erase(CStrRemovalMatch);
785 Diag << tooling::fixit::createReplacement(*Args[ValueArgIndex - ArgCount],
786 *Args[ValueArgIndex], *Context);
791 Diag << tooling::fixit::createReplacement(
792 *Args[ValueArgIndex -
Offset], *Args[ValueArgIndex -
Offset - 1],
798 for (
auto &ArgFix : ArgFixes) {
799 if (ArgFix.ArgIndex == ValueArgIndex)
800 ArgFix.ArgIndex = ValueArgIndex - ArgCount;
804 for (
const auto &[ArgIndex, Replacement] : ArgFixes) {
805 SourceLocation AfterOtherSide =
806 Lexer::findNextToken(Args[ArgIndex]->getEndLoc(), SM, LangOpts)
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);
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(Expr, isMacroID)
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[]