26 StrictMode(Options.get(
"StrictMode", false)),
27 PrintfLikeFunctions(
utils::options::parseStringList(
28 Options.get(
"PrintfLikeFunctions",
""))),
29 FprintfLikeFunctions(
utils::options::parseStringList(
30 Options.get(
"FprintfLikeFunctions",
""))),
31 ReplacementPrintFunction(
32 Options.get(
"ReplacementPrintFunction",
"std::print")),
33 ReplacementPrintlnFunction(
34 Options.get(
"ReplacementPrintlnFunction",
"std::println")),
35 IncludeInserter(Options.getLocalOrGlobal(
"IncludeStyle",
36 utils::IncludeSorter::IS_LLVM),
37 areDiagsSelfContained()),
38 MaybeHeaderToInclude(Options.get(
"PrintHeader")) {
39 if (PrintfLikeFunctions.empty() && FprintfLikeFunctions.empty()) {
40 PrintfLikeFunctions.emplace_back(
"::printf");
41 PrintfLikeFunctions.emplace_back(
"absl::PrintF");
42 FprintfLikeFunctions.emplace_back(
"::fprintf");
43 FprintfLikeFunctions.emplace_back(
"absl::FPrintF");
46 if (!MaybeHeaderToInclude && (ReplacementPrintFunction ==
"std::print" ||
47 ReplacementPrintlnFunction ==
"std::println"))
48 MaybeHeaderToInclude =
"<print>";
53 Options.store(Opts,
"StrictMode", StrictMode);
54 Options.store(Opts,
"PrintfLikeFunctions",
55 serializeStringList(PrintfLikeFunctions));
56 Options.store(Opts,
"FprintfLikeFunctions",
57 serializeStringList(FprintfLikeFunctions));
58 Options.store(Opts,
"ReplacementPrintFunction", ReplacementPrintFunction);
59 Options.store(Opts,
"ReplacementPrintlnFunction", ReplacementPrintlnFunction);
60 Options.store(Opts,
"IncludeStyle", IncludeInserter.getStyle());
61 if (MaybeHeaderToInclude)
62 Options.store(Opts,
"PrintHeader", *MaybeHeaderToInclude);
73 const clang::ast_matchers::StatementMatcher &MatchedCallExpr) {
74 auto UnusedInCompoundStmt =
75 compoundStmt(forEach(MatchedCallExpr),
80 unless(hasParent(stmtExpr())));
82 ifStmt(eachOf(hasThen(MatchedCallExpr), hasElse(MatchedCallExpr)));
83 auto UnusedInWhileStmt = whileStmt(hasBody(MatchedCallExpr));
84 auto UnusedInDoStmt = doStmt(hasBody(MatchedCallExpr));
85 auto UnusedInForStmt =
86 forStmt(eachOf(hasLoopInit(MatchedCallExpr),
87 hasIncrement(MatchedCallExpr), hasBody(MatchedCallExpr)));
88 auto UnusedInRangeForStmt = cxxForRangeStmt(hasBody(MatchedCallExpr));
89 auto UnusedInCaseStmt = switchCase(forEach(MatchedCallExpr));
91 return stmt(anyOf(UnusedInCompoundStmt, UnusedInIfStmt, UnusedInWhileStmt,
92 UnusedInDoStmt, UnusedInForStmt, UnusedInRangeForStmt,
97 if (!PrintfLikeFunctions.empty())
100 callExpr(argumentCountAtLeast(1),
101 hasArgument(0, stringLiteral(isOrdinary())),
103 PrintfLikeFunctions))
108 if (!FprintfLikeFunctions.empty())
111 callExpr(argumentCountAtLeast(2),
112 hasArgument(1, stringLiteral(isOrdinary())),
114 FprintfLikeFunctions))
121 unsigned FormatArgOffset = 0;
122 const auto *OldFunction = Result.Nodes.getNodeAs<FunctionDecl>(
"func_decl");
123 const auto *Printf = Result.Nodes.getNodeAs<CallExpr>(
"printf");
125 Printf = Result.Nodes.getNodeAs<CallExpr>(
"fprintf");
132 assert(PP &&
"Preprocessor should be set by registerPPCallbacks");
134 Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts(),
135 *Result.SourceManager, *PP);
136 const Expr *PrintfCall = Printf->getCallee();
138 ? ReplacementPrintlnFunction
139 : ReplacementPrintFunction;
141 diag(PrintfCall->getBeginLoc(),
142 "unable to use '%0' instead of %1 because %2")
143 << PrintfCall->getSourceRange() << ReplacementFunction
144 << OldFunction->getIdentifier()
149 DiagnosticBuilder Diag =
150 diag(PrintfCall->getBeginLoc(),
"use '%0' instead of %1")
151 << ReplacementFunction << OldFunction->getIdentifier();
153 Diag << FixItHint::CreateReplacement(
154 CharSourceRange::getTokenRange(PrintfCall->getExprLoc(),
155 PrintfCall->getEndLoc()),
156 ReplacementFunction);
157 Converter.
applyFixes(Diag, *Result.SourceManager);
159 if (MaybeHeaderToInclude)
160 Diag << IncludeInserter.createIncludeInsertion(
161 Result.Context->getSourceManager().getFileID(PrintfCall->getBeginLoc()),
162 *MaybeHeaderToInclude);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.