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) {
74static clang::ast_matchers::StatementMatcher
76 auto UnusedInCompoundStmt =
77 compoundStmt(forEach(MatchedCallExpr),
82 unless(hasParent(stmtExpr())));
84 ifStmt(eachOf(hasThen(MatchedCallExpr), hasElse(MatchedCallExpr)));
85 auto UnusedInWhileStmt = whileStmt(hasBody(MatchedCallExpr));
86 auto UnusedInDoStmt = doStmt(hasBody(MatchedCallExpr));
87 auto UnusedInForStmt =
88 forStmt(eachOf(hasLoopInit(MatchedCallExpr),
89 hasIncrement(MatchedCallExpr), hasBody(MatchedCallExpr)));
90 auto UnusedInRangeForStmt = cxxForRangeStmt(hasBody(MatchedCallExpr));
91 auto UnusedInCaseStmt = switchCase(forEach(MatchedCallExpr));
93 return stmt(anyOf(UnusedInCompoundStmt, UnusedInIfStmt, UnusedInWhileStmt,
94 UnusedInDoStmt, UnusedInForStmt, UnusedInRangeForStmt,
99 if (!PrintfLikeFunctions.empty())
102 callExpr(argumentCountAtLeast(1),
103 hasArgument(0, stringLiteral(isOrdinary())),
105 PrintfLikeFunctions))
110 if (!FprintfLikeFunctions.empty())
113 callExpr(argumentCountAtLeast(2),
114 hasArgument(1, stringLiteral(isOrdinary())),
116 FprintfLikeFunctions))
123 unsigned FormatArgOffset = 0;
124 const auto *OldFunction = Result.Nodes.getNodeAs<FunctionDecl>(
"func_decl");
125 const auto *Printf = Result.Nodes.getNodeAs<CallExpr>(
"printf");
127 Printf = Result.Nodes.getNodeAs<CallExpr>(
"fprintf");
134 assert(PP &&
"Preprocessor should be set by registerPPCallbacks");
136 Result.Context, Printf, FormatArgOffset, ConverterConfig,
getLangOpts(),
137 *Result.SourceManager, *PP);
138 const Expr *PrintfCall = Printf->getCallee();
140 ? ReplacementPrintlnFunction
141 : ReplacementPrintFunction;
143 diag(PrintfCall->getBeginLoc(),
144 "unable to use '%0' instead of %1 because %2")
145 << PrintfCall->getSourceRange() << ReplacementFunction
146 << OldFunction->getIdentifier()
151 DiagnosticBuilder Diag =
152 diag(PrintfCall->getBeginLoc(),
"use '%0' instead of %1")
153 << ReplacementFunction << OldFunction->getIdentifier();
155 Diag << FixItHint::CreateReplacement(
156 CharSourceRange::getTokenRange(PrintfCall->getExprLoc(),
157 PrintfCall->getEndLoc()),
158 ReplacementFunction);
159 Converter.
applyFixes(Diag, *Result.SourceManager);
161 if (MaybeHeaderToInclude)
163 Result.Context->getSourceManager().getFileID(PrintfCall->getBeginLoc()),
164 *MaybeHeaderToInclude);
llvm::SmallString< 256U > Name
::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(Expr, isMacroID)
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