[clang-tidy] Ignore standard tag dispatch types in readability-named-parameter (#208730)

Parameters whose type is a standard tag type (e.g. std::in_place_t,
std::nothrow_t,
iterator tags, lock tags) are used purely for overload resolution and
don't need
a name. This patch skips them.

Added an IgnoredTypes option so users can customize the list. Default
covers 24
standard tag types.

Fixes #208448.

AI Usage: This commit is AI-assisted, reviewed and verified by me.
This commit is contained in:
hehuan 2026-07-23 09:07:18 +08:00 committed by GitHub
parent 8eca60b49a
commit d0ec2ec1f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 171 additions and 1 deletions

View file

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "NamedParameterCheck.h"
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
@ -15,15 +16,47 @@ using namespace clang::ast_matchers;
namespace clang::tidy::readability {
// Types whose parameters do not need a name (tag dispatch types, category
// tags, etc.).
static constexpr StringRef DefaultIgnoredTypes =
"std::adopt_lock_t;"
"std::allocator_arg_t;"
"std::bidirectional_iterator_tag;"
"std::contiguous_iterator_tag;"
"std::default_sentinel_t;"
"std::defer_lock_t;"
"std::destroying_delete_t;"
"std::forward_iterator_tag;"
"std::from_range_t;"
"std::in_place_index_t;"
"std::in_place_t;"
"std::in_place_type_t;"
"std::input_iterator_tag;"
"std::nothrow_t;"
"std::nostopstate_t;"
"std::nullopt_t;"
"std::output_iterator_tag;"
"std::piecewise_construct_t;"
"std::random_access_iterator_tag;"
"std::sorted_equivalent_t;"
"std::sorted_unique_t;"
"std::try_to_lock_t;"
"std::unexpect_t;"
"std::unreachable_sentinel_t";
NamedParameterCheck::NamedParameterCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
InsertPlainNamesInForwardDecls(
Options.get("InsertPlainNamesInForwardDecls", false)) {}
Options.get("InsertPlainNamesInForwardDecls", false)),
IgnoredTypes(utils::options::parseStringList(
Options.get("IgnoredTypes", DefaultIgnoredTypes))) {}
void NamedParameterCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "InsertPlainNamesInForwardDecls",
InsertPlainNamesInForwardDecls);
Options.store(Opts, "IgnoredTypes",
utils::options::serializeStringList(IgnoredTypes));
}
void NamedParameterCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
@ -75,6 +108,15 @@ void NamedParameterCheck::check(const MatchFinder::MatchResult &Result) {
if (Parm->getType().getCanonicalType()->isNullPtrType())
continue;
// Skip the types configured by the IgnoredTypes option (e.g. standard
// tag dispatch types).
if (const auto *Record =
Parm->getType().getCanonicalType()->getAsCXXRecordDecl()) {
const std::string QName = Record->getQualifiedNameAsString();
if (llvm::is_contained(IgnoredTypes, QName))
continue;
}
// Look for comments. We explicitly want to allow idioms like
// void foo(int /*unused*/)
const char *Begin = SM.getCharacterData(Parm->getBeginLoc());

View file

@ -36,6 +36,7 @@ public:
private:
const bool InsertPlainNamesInForwardDecls;
const std::vector<StringRef> IgnoredTypes;
};
} // namespace clang::tidy::readability

View file

@ -103,6 +103,13 @@ New check aliases
Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^
- Improved :doc:`readability-named-parameter
<clang-tidy/checks/readability/named-parameter>` check by ignoring
standard tag types (e.g. ``std::in_place_t``, ``std::allocator_arg_t``,
``std::nothrow_t``, iterator tags, lock tags, etc.) that are used
exclusively for overload resolution. Added the :option:`IgnoredTypes`
option to allow customizing the set of ignored types.
Removed checks
^^^^^^^^^^^^^^

View file

@ -25,6 +25,11 @@ function definition.
Corresponding cpplint.py check name: `readability/function`.
The check ignores parameters whose types are standard tag types (e.g.
``std::in_place_t``, ``std::allocator_arg_t``, ``std::nothrow_t``,
iterator tags, lock tags, etc.). The set of ignored types can be customized
with the :option:`IgnoredTypes` option.
Options
-------
@ -33,3 +38,36 @@ Options
If set to `true`, the check will insert parameter names without comments for
forward declarations only. Otherwise, the check will insert parameter names
as comments (e.g., ``/*param*/``). Default is `false`.
.. option:: IgnoredTypes
A semicolon-separated list of fully-qualified type names whose parameters
do not need to be named (for example, tag dispatch types, iterator tags,
etc.). Defaults to the standard tag types:
.. code-block:: text
std::adopt_lock_t
std::allocator_arg_t
std::bidirectional_iterator_tag
std::contiguous_iterator_tag
std::default_sentinel_t
std::defer_lock_t
std::destroying_delete_t
std::forward_iterator_tag
std::from_range_t
std::in_place_index_t
std::in_place_t
std::in_place_type_t
std::input_iterator_tag
std::nothrow_t
std::nostopstate_t
std::nullopt_t
std::output_iterator_tag
std::piecewise_construct_t
std::random_access_iterator_tag
std::sorted_equivalent_t
std::sorted_unique_t
std::try_to_lock_t
std::unexpect_t
std::unreachable_sentinel_t

View file

@ -0,0 +1,24 @@
// RUN: %check_clang_tidy %s readability-named-parameter %t -- \
// RUN: -config="{CheckOptions: [{key: readability-named-parameter.IgnoredTypes, value: 'MyCustomTag'}]}"
struct MyCustomTag {};
struct OtherTag {};
// MyCustomTag is in the custom IgnoredTypes list, so it should not warn.
void f_custom(MyCustomTag) {}
// OtherTag is NOT in the custom list, so it should warn.
void f_other(OtherTag) {}
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: all parameters should be named in a function
// CHECK-FIXES: void f_other(OtherTag /*unused*/) {}
// int is not in the custom list either, so it should warn.
void f_int(int) {}
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: all parameters should be named in a function
// CHECK-FIXES: void f_int(int /*unused*/) {}
// Verify the default std types are NOT ignored when a custom list replaces them.
namespace std { struct in_place_t {}; }
void f_in_place(std::in_place_t) {}
// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: all parameters should be named in a function
// CHECK-FIXES: void f_in_place(std::in_place_t /*unused*/) {}

View file

@ -161,10 +161,68 @@ void MockFunction(Unused, int q, Unused) {
namespace std {
typedef decltype(nullptr) nullptr_t;
struct adopt_lock_t {};
struct allocator_arg_t {};
struct bidirectional_iterator_tag {};
struct contiguous_iterator_tag {};
struct default_sentinel_t {};
struct defer_lock_t {};
struct destroying_delete_t {};
struct forward_iterator_tag {};
struct from_range_t {};
template<unsigned I> struct in_place_index_t {};
struct in_place_t {};
template<class T> struct in_place_type_t {};
struct input_iterator_tag {};
struct nothrow_t {};
struct nostopstate_t {};
struct nullopt_t {};
struct output_iterator_tag {};
struct piecewise_construct_t {};
struct random_access_iterator_tag {};
struct sorted_equivalent_t {};
struct sorted_unique_t {};
struct try_to_lock_t {};
struct unexpect_t {};
struct unreachable_sentinel_t {};
}
void f(std::nullptr_t) {}
// Standard tag dispatch types should not trigger warnings.
void f_adopt_lock(std::adopt_lock_t) {}
void f_allocator_arg(std::allocator_arg_t) {}
void f_bidir_iter(std::bidirectional_iterator_tag) {}
void f_contig_iter(std::contiguous_iterator_tag) {}
void f_default_sentinel(std::default_sentinel_t) {}
void f_defer_lock(std::defer_lock_t) {}
void f_destroying(std::destroying_delete_t) {}
void f_forward_iter(std::forward_iterator_tag) {}
void f_from_range(std::from_range_t) {}
void f_in_place_index(std::in_place_index_t<0>) {}
void f_in_place(std::in_place_t) {}
void f_in_place_type(std::in_place_type_t<int>) {}
void f_input_iter(std::input_iterator_tag) {}
void f_nothrow(std::nothrow_t) {}
void f_nostopstate(std::nostopstate_t) {}
void f_nullopt(std::nullopt_t) {}
void f_output_iter(std::output_iterator_tag) {}
void f_piecewise(std::piecewise_construct_t) {}
void f_random_iter(std::random_access_iterator_tag) {}
void f_sorted_equivalent(std::sorted_equivalent_t) {}
void f_sorted_unique(std::sorted_unique_t) {}
void f_try_to_lock(std::try_to_lock_t) {}
void f_unexpect(std::unexpect_t) {}
void f_unreachable_sentinel(std::unreachable_sentinel_t) {}
// A type not in the IgnoredTypes list should still trigger a warning.
struct NotATag {};
void f_not_a_tag(NotATag) {}
// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: all parameters should be named in a function
// CHECK-FIXES: void f_not_a_tag(NotATag /*unused*/) {}
// CHECK-MESSAGES-PLAIN-NAMES: :[[@LINE-3]]:25: warning: all parameters should be named in a function
// CHECK-FIXES-PLAIN-NAMES: void f_not_a_tag(NotATag /*unused*/) {}
typedef void (F)(int);
F f;
void f(int x) {}