10#include "../utils/FormatStringConverter.h"
11#include "../utils/Matchers.h"
12#include "../utils/OptionsUtils.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Lexer.h"
15#include "clang/Tooling/FixIt.h"
27 StrictMode(Options.getLocalOrGlobal(
"StrictMode", false)),
28 PrintfLikeFunctions(utils::options::parseStringList(
29 Options.get(
"PrintfLikeFunctions",
""))),
30 FprintfLikeFunctions(utils::options::parseStringList(
31 Options.get(
"FprintfLikeFunctions",
""))),
32 ReplacementPrintFunction(
33 Options.get(
"ReplacementPrintFunction",
"std::print")),
34 ReplacementPrintlnFunction(
35 Options.get(
"ReplacementPrintlnFunction",
"std::println")),
36 IncludeInserter(Options.getLocalOrGlobal(
"IncludeStyle",
37 utils::IncludeSorter::IS_LLVM),
38 areDiagsSelfContained()),
39 MaybeHeaderToInclude(Options.get(
"PrintHeader")) {
41 if (PrintfLikeFunctions.empty() && FprintfLikeFunctions.empty()) {
42 PrintfLikeFunctions.emplace_back(
"::printf");
43 PrintfLikeFunctions.emplace_back(
"absl::PrintF");
44 FprintfLikeFunctions.emplace_back(
"::fprintf");
45 FprintfLikeFunctions.emplace_back(
"absl::FPrintF");
48 if (!MaybeHeaderToInclude && (ReplacementPrintFunction ==
"std::print" ||
49 ReplacementPrintlnFunction ==
"std::println"))
50 MaybeHeaderToInclude =
"<print>";
57 serializeStringList(PrintfLikeFunctions));
59 serializeStringList(FprintfLikeFunctions));
60 Options.
store(Opts,
"ReplacementPrintFunction", ReplacementPrintFunction);
61 Options.
store(Opts,
"ReplacementPrintlnFunction", ReplacementPrintlnFunction);
63 if (MaybeHeaderToInclude)
64 Options.
store(Opts,
"PrintHeader", *MaybeHeaderToInclude);
69 Preprocessor *ModuleExpanderPP) {
73static clang::ast_matchers::StatementMatcher
75 auto UnusedInCompoundStmt =
76 compoundStmt(forEach(MatchedCallExpr),
81 unless(hasParent(stmtExpr())));
83 ifStmt(eachOf(hasThen(MatchedCallExpr), hasElse(MatchedCallExpr)));
84 auto UnusedInWhileStmt = whileStmt(hasBody(MatchedCallExpr));
85 auto UnusedInDoStmt = doStmt(hasBody(MatchedCallExpr));
86 auto UnusedInForStmt =
87 forStmt(eachOf(hasLoopInit(MatchedCallExpr),
88 hasIncrement(MatchedCallExpr), hasBody(MatchedCallExpr)));
89 auto UnusedInRangeForStmt = cxxForRangeStmt(hasBody(MatchedCallExpr));
90 auto UnusedInCaseStmt = switchCase(forEach(MatchedCallExpr));
92 return stmt(anyOf(UnusedInCompoundStmt, UnusedInIfStmt, UnusedInWhileStmt,
93 UnusedInDoStmt, UnusedInForStmt, UnusedInRangeForStmt,
98 if (!PrintfLikeFunctions.empty())
101 callExpr(argumentCountAtLeast(1),
102 hasArgument(0, stringLiteral(isOrdinary())),
103 callee(functionDecl(unless(cxxMethodDecl()),
105 PrintfLikeFunctions))
110 if (!FprintfLikeFunctions.empty())
113 callExpr(argumentCountAtLeast(2),
114 hasArgument(1, stringLiteral(isOrdinary())),
115 callee(functionDecl(unless(cxxMethodDecl()),
117 FprintfLikeFunctions))
124 unsigned FormatArgOffset = 0;
125 const auto *OldFunction = Result.Nodes.getNodeAs<FunctionDecl>(
"func_decl");
126 const auto *Printf = Result.Nodes.getNodeAs<CallExpr>(
"printf");
128 Printf = Result.Nodes.getNodeAs<CallExpr>(
"fprintf");
133 Result.Context, Printf, FormatArgOffset, StrictMode,
getLangOpts());
134 const Expr *PrintfCall = Printf->getCallee();
136 ? ReplacementPrintlnFunction
137 : ReplacementPrintFunction;
139 diag(PrintfCall->getBeginLoc(),
140 "unable to use '%0' instead of %1 because %2")
141 << ReplacementFunction << OldFunction->getIdentifier()
146 DiagnosticBuilder Diag =
147 diag(PrintfCall->getBeginLoc(),
"use '%0' instead of %1")
148 << ReplacementFunction << OldFunction->getIdentifier();
150 Diag << FixItHint::CreateReplacement(
151 CharSourceRange::getTokenRange(PrintfCall->getBeginLoc(),
152 PrintfCall->getEndLoc()),
153 ReplacementFunction);
154 Converter.
applyFixes(Diag, *Result.SourceManager);
156 if (MaybeHeaderToInclude)
158 Result.Context->getSourceManager().getFileID(PrintfCall->getBeginLoc()),
159 *MaybeHeaderToInclude);
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
UseStdPrintCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
void registerPreprocessor(Preprocessor *PP)
Registers this with the Preprocessor PP, must be called before this class is used.
std::optional< FixItHint > createIncludeInsertion(FileID FileID, llvm::StringRef Header)
Creates a Header inclusion directive fixit in the File FileID.
IncludeSorter::IncludeStyle getStyle() const
AST_MATCHER(Decl, declHasNoReturnAttr)
matches a Decl if it has a "no return" attribute of any kind
inline ::clang::ast_matchers::internal::Matcher< NamedDecl > matchesAnyListedName(llvm::ArrayRef< StringRef > NameList)
static clang::ast_matchers::StatementMatcher unusedReturnValue(clang::ast_matchers::StatementMatcher MatchedCallExpr)
std::string serializeStringList(ArrayRef< StringRef > Strings)
Serialize a sequence of names that can be parsed by parseStringList.
llvm::StringMap< ClangTidyValue > OptionMap