-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang-tidy] Add modernize-use-std-format check #90397
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
0d6ede5
[clang-tidy] Add modernize-use-std-format check
mikecrowe 00ca5cf
fixup! [clang-tidy] Add modernize-use-std-format check
mikecrowe 366308b
fixup! [clang-tidy] Add modernize-use-std-format check
mikecrowe 9705848
fixup! [clang-tidy] Add modernize-use-std-format check
mikecrowe 5c9fc69
fixup! [clang-tidy] Add modernize-use-std-format check
mikecrowe 56313da
fixup! [clang-tidy] Add modernize-use-std-format check
mikecrowe f31f47e
fixup! [clang-tidy] Add modernize-use-std-format check
mikecrowe 0f4b068
fixup! [clang-tidy] Add modernize-use-std-format check
mikecrowe e39d8cf
Update clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-for…
mikecrowe ed085a9
Update clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-for…
mikecrowe 84a8134
Update clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-for…
mikecrowe 54c325d
fixup! [clang-tidy] Add modernize-use-std-format check
mikecrowe 6410bd2
Remove unnecessary variable as suggested by @5chmidti
mikecrowe d95e9b5
Fix mistake in docs pointed out by @5chmidti
mikecrowe 3a09ca6
use-std-format: Use simpler CheckOptions syntax in lit tests
mikecrowe c97267b
use-std-format: Ensure that function name gets underlined in diagnostic
mikecrowe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
107 changes: 107 additions & 0 deletions
107
clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
//===--- UseStdFormatCheck.cpp - clang-tidy -------------------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "UseStdFormatCheck.h" | ||
#include "../utils/FormatStringConverter.h" | ||
#include "../utils/Matchers.h" | ||
#include "../utils/OptionsUtils.h" | ||
#include "clang/ASTMatchers/ASTMatchFinder.h" | ||
#include "clang/Lex/Lexer.h" | ||
#include "clang/Tooling/FixIt.h" | ||
|
||
using namespace clang::ast_matchers; | ||
|
||
namespace clang::tidy::modernize { | ||
|
||
namespace { | ||
AST_MATCHER(StringLiteral, isOrdinary) { return Node.isOrdinary(); } | ||
} // namespace | ||
|
||
UseStdFormatCheck::UseStdFormatCheck(StringRef Name, ClangTidyContext *Context) | ||
: ClangTidyCheck(Name, Context), | ||
StrictMode(Options.getLocalOrGlobal("StrictMode", false)), | ||
StrFormatLikeFunctions(utils::options::parseStringList( | ||
Options.get("StrFormatLikeFunctions", ""))), | ||
ReplacementFormatFunction( | ||
Options.get("ReplacementFormatFunction", "std::format")), | ||
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", | ||
utils::IncludeSorter::IS_LLVM), | ||
areDiagsSelfContained()), | ||
MaybeHeaderToInclude(Options.get("FormatHeader")) { | ||
if (StrFormatLikeFunctions.empty()) | ||
StrFormatLikeFunctions.push_back("absl::StrFormat"); | ||
|
||
if (!MaybeHeaderToInclude && ReplacementFormatFunction == "std::format") | ||
MaybeHeaderToInclude = "<format>"; | ||
} | ||
|
||
void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM, | ||
Preprocessor *PP, | ||
Preprocessor *ModuleExpanderPP) { | ||
IncludeInserter.registerPreprocessor(PP); | ||
} | ||
|
||
void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) { | ||
Finder->addMatcher( | ||
callExpr(argumentCountAtLeast(1), | ||
hasArgument(0, stringLiteral(isOrdinary())), | ||
callee(functionDecl(unless(cxxMethodDecl()), | ||
matchers::matchesAnyListedName( | ||
StrFormatLikeFunctions)) | ||
.bind("func_decl"))) | ||
.bind("strformat"), | ||
this); | ||
} | ||
|
||
void UseStdFormatCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { | ||
using utils::options::serializeStringList; | ||
Options.store(Opts, "StrictMode", StrictMode); | ||
Options.store(Opts, "StrFormatLikeFunctions", | ||
serializeStringList(StrFormatLikeFunctions)); | ||
Options.store(Opts, "ReplacementFormatFunction", ReplacementFormatFunction); | ||
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); | ||
if (MaybeHeaderToInclude) | ||
Options.store(Opts, "FormatHeader", *MaybeHeaderToInclude); | ||
} | ||
|
||
void UseStdFormatCheck::check(const MatchFinder::MatchResult &Result) { | ||
const unsigned FormatArgOffset = 0; | ||
const auto *OldFunction = Result.Nodes.getNodeAs<FunctionDecl>("func_decl"); | ||
const auto *StrFormat = Result.Nodes.getNodeAs<CallExpr>("strformat"); | ||
|
||
utils::FormatStringConverter::Configuration ConverterConfig; | ||
ConverterConfig.StrictMode = StrictMode; | ||
utils::FormatStringConverter Converter(Result.Context, StrFormat, | ||
FormatArgOffset, ConverterConfig, | ||
getLangOpts()); | ||
const Expr *StrFormatCall = StrFormat->getCallee(); | ||
if (!Converter.canApply()) { | ||
diag(StrFormat->getBeginLoc(), | ||
"unable to use '%0' instead of %1 because %2") | ||
<< StrFormatCall->getSourceRange() << ReplacementFormatFunction | ||
<< OldFunction->getIdentifier() | ||
<< Converter.conversionNotPossibleReason(); | ||
return; | ||
} | ||
|
||
DiagnosticBuilder Diag = | ||
diag(StrFormatCall->getBeginLoc(), "use '%0' instead of %1") | ||
<< ReplacementFormatFunction << OldFunction->getIdentifier(); | ||
PiotrZSL marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Diag << FixItHint::CreateReplacement( | ||
CharSourceRange::getTokenRange(StrFormatCall->getSourceRange()), | ||
ReplacementFormatFunction); | ||
Converter.applyFixes(Diag, *Result.SourceManager); | ||
|
||
if (MaybeHeaderToInclude) | ||
Diag << IncludeInserter.createIncludeInsertion( | ||
Result.Context->getSourceManager().getFileID( | ||
StrFormatCall->getBeginLoc()), | ||
*MaybeHeaderToInclude); | ||
} | ||
|
||
} // namespace clang::tidy::modernize |
51 changes: 51 additions & 0 deletions
51
clang-tools-extra/clang-tidy/modernize/UseStdFormatCheck.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
//===--- UseStdFormatCheck.h - clang-tidy -----------------------*- C++ -*-===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H | ||
|
||
#include "../ClangTidyCheck.h" | ||
mikecrowe marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#include "../utils/IncludeInserter.h" | ||
|
||
namespace clang::tidy::modernize { | ||
|
||
/// Converts calls to absl::StrFormat, or other functions via configuration | ||
/// options, to C++20's std::format, or another function via a configuration | ||
/// option, modifying the format string appropriately and removing | ||
/// now-unnecessary calls to std::string::c_str() and std::string::data(). | ||
/// | ||
/// For the user-facing documentation see: | ||
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-format.html | ||
class UseStdFormatCheck : public ClangTidyCheck { | ||
public: | ||
UseStdFormatCheck(StringRef Name, ClangTidyContext *Context); | ||
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { | ||
if (ReplacementFormatFunction == "std::format") | ||
return LangOpts.CPlusPlus20; | ||
return LangOpts.CPlusPlus; | ||
} | ||
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, | ||
Preprocessor *ModuleExpanderPP) override; | ||
void storeOptions(ClangTidyOptions::OptionMap &Opts) override; | ||
void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
std::optional<TraversalKind> getCheckTraversalKind() const override { | ||
return TK_IgnoreUnlessSpelledInSource; | ||
} | ||
|
||
private: | ||
bool StrictMode; | ||
std::vector<StringRef> StrFormatLikeFunctions; | ||
StringRef ReplacementFormatFunction; | ||
utils::IncludeInserter IncludeInserter; | ||
std::optional<StringRef> MaybeHeaderToInclude; | ||
}; | ||
|
||
} // namespace clang::tidy::modernize | ||
|
||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESTDFORMATCHECK_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
clang-tools-extra/docs/clang-tidy/checks/modernize/use-std-format.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
.. title:: clang-tidy - modernize-use-std-format | ||
|
||
modernize-use-std-format | ||
======================== | ||
|
||
Converts calls to ``absl::StrFormat``, or other functions via | ||
configuration options, to C++20's ``std::format``, or another function | ||
via a configuration option, modifying the format string appropriately and | ||
removing now-unnecessary calls to ``std::string::c_str()`` and | ||
``std::string::data()``. | ||
|
||
For example, it turns lines like | ||
|
||
.. code-block:: c++ | ||
|
||
return absl::StrFormat("The %s is %3d", description.c_str(), value); | ||
|
||
into: | ||
|
||
.. code-block:: c++ | ||
|
||
return std::format("The {} is {:3}", description, value); | ||
|
||
The check uses the same format-string-conversion algorithm as | ||
`modernize-use-std-print <../modernize/use-std-print.html>`_ and its | ||
shortcomings are described in the documentation for that check. | ||
|
||
Options | ||
------- | ||
|
||
.. option:: StrictMode | ||
|
||
When `true`, the check will add casts when converting from variadic | ||
functions and printing signed or unsigned integer types (including | ||
fixed-width integer types from ``<cstdint>``, ``ptrdiff_t``, ``size_t`` | ||
and ``ssize_t``) as the opposite signedness to ensure that the output | ||
would matches that of a simple wrapper for ``std::sprintf`` that | ||
accepted a C-style variable argument list. For example, with | ||
`StrictMode` enabled, | ||
|
||
.. code-block:: c++ | ||
|
||
extern std::string strprintf(const char *format, ...); | ||
int i = -42; | ||
unsigned int u = 0xffffffff; | ||
return strprintf("%d %u\n", i, u); | ||
|
||
would be converted to | ||
|
||
.. code-block:: c++ | ||
|
||
return std::format("{} {}\n", static_cast<unsigned int>(i), static_cast<int>(u)); | ||
|
||
to ensure that the output will continue to be the unsigned representation | ||
of -42 and the signed representation of 0xffffffff (often 4294967254 | ||
and -1 respectively). When `false` (which is the default), these casts | ||
will not be added which may cause a change in the output. Note that this | ||
option makes no difference for the default value of | ||
`StrFormatLikeFunctions` since ``absl::StrFormat`` takes a function | ||
parameter pack and is not a variadic function. | ||
|
||
.. option:: StrFormatLikeFunctions | ||
|
||
A semicolon-separated list of (fully qualified) function names to | ||
replace, with the requirement that the first parameter contains the | ||
printf-style format string and the arguments to be formatted follow | ||
immediately afterwards. The default value for this option is | ||
`absl::StrFormat`. | ||
|
||
.. option:: ReplacementFormatFunction | ||
|
||
The function that will be used to replace the function set by the | ||
`StrFormatLikeFunctions` option rather than the default | ||
`std::format`. It is expected that the function provides an interface | ||
that is compatible with ``std::format``. A suitable candidate would be | ||
`fmt::format`. | ||
|
||
.. option:: FormatHeader | ||
|
||
The header that must be included for the declaration of | ||
`ReplacementFormatFunction` so that a ``#include`` directive can be added if | ||
required. If `ReplacementFormatFunction` is `std::format` then this option will | ||
default to ``<format>``, otherwise this option will default to nothing | ||
and no ``#include`` directive will be added. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.