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())),
104 PrintfLikeFunctions))
109 if (!FprintfLikeFunctions.empty())
112 callExpr(argumentCountAtLeast(2),
113 hasArgument(1, stringLiteral(isOrdinary())),
115 FprintfLikeFunctions))
122 unsigned FormatArgOffset = 0;
123 const auto *OldFunction = Result.Nodes.getNodeAs<FunctionDecl>(
"func_decl");
124 const auto *Printf = Result.Nodes.getNodeAs<CallExpr>(
"printf");
126 Printf = Result.Nodes.getNodeAs<CallExpr>(
"fprintf");
134 Result.Context, Printf, FormatArgOffset, ConverterConfig,
getLangOpts());
135 const Expr *PrintfCall = Printf->getCallee();
137 ? ReplacementPrintlnFunction
138 : ReplacementPrintFunction;
140 diag(PrintfCall->getBeginLoc(),
141 "unable to use '%0' instead of %1 because %2")
142 << PrintfCall->getSourceRange() << ReplacementFunction
143 << OldFunction->getIdentifier()
148 DiagnosticBuilder Diag =
149 diag(PrintfCall->getBeginLoc(),
"use '%0' instead of %1")
150 << ReplacementFunction << OldFunction->getIdentifier();
152 Diag << FixItHint::CreateReplacement(
153 CharSourceRange::getTokenRange(PrintfCall->getExprLoc(),
154 PrintfCall->getEndLoc()),
155 ReplacementFunction);
156 Converter.
applyFixes(Diag, *Result.SourceManager);
158 if (MaybeHeaderToInclude)
160 Result.Context->getSourceManager().getFileID(PrintfCall->getBeginLoc()),
161 *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(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