Skip to content

Commit d38d576

Browse files
committed
rustdoc: Properly clean fn params in all contexts
1 parent ee6608f commit d38d576

File tree

13 files changed

+108
-57
lines changed

13 files changed

+108
-57
lines changed

src/librustdoc/clean/mod.rs

+35-37
Original file line numberDiff line numberDiff line change
@@ -1065,16 +1065,12 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[hir::Attrib
10651065
for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.lit()).enumerate() {
10661066
match literal.kind {
10671067
ast::LitKind::Int(a, _) => {
1068-
let param = func.generics.params.remove(0);
1069-
if let GenericParamDef {
1070-
name,
1071-
kind: GenericParamDefKind::Const { ty, .. },
1072-
..
1073-
} = param
1074-
{
1075-
func.decl
1076-
.inputs
1077-
.insert(a.get() as _, Parameter { name, type_: *ty, is_const: true });
1068+
let GenericParamDef { name, kind, .. } = func.generics.params.remove(0);
1069+
if let GenericParamDefKind::Const { ty, .. } = kind {
1070+
func.decl.inputs.insert(
1071+
a.get() as _,
1072+
Parameter { name: Some(name), type_: *ty, is_const: true },
1073+
);
10781074
} else {
10791075
panic!("unexpected non const in position {pos}");
10801076
}
@@ -1101,7 +1097,10 @@ fn clean_function<'tcx>(
11011097
let generics = clean_generics(generics, cx);
11021098
let params = match params {
11031099
ParamsSrc::Body(body_id) => clean_params_via_body(cx, sig.decl.inputs, body_id),
1104-
ParamsSrc::Idents(idents) => clean_params(cx, sig.decl.inputs, idents),
1100+
// Let's not perpetuate anon params from Rust 2015; use `_` for them.
1101+
ParamsSrc::Idents(idents) => clean_params(cx, sig.decl.inputs, idents, |ident| {
1102+
Some(ident.map_or(kw::Underscore, |ident| ident.name))
1103+
}),
11051104
};
11061105
let decl = clean_fn_decl_with_params(cx, sig.decl, Some(&sig.header), params);
11071106
(generics, decl)
@@ -1113,30 +1112,13 @@ fn clean_params<'tcx>(
11131112
cx: &mut DocContext<'tcx>,
11141113
types: &[hir::Ty<'tcx>],
11151114
idents: &[Option<Ident>],
1115+
postprocess: impl Fn(Option<Ident>) -> Option<Symbol>,
11161116
) -> Vec<Parameter> {
1117-
fn nonempty_name(ident: &Option<Ident>) -> Option<Symbol> {
1118-
if let Some(ident) = ident
1119-
&& ident.name != kw::Underscore
1120-
{
1121-
Some(ident.name)
1122-
} else {
1123-
None
1124-
}
1125-
}
1126-
1127-
// If at least one argument has a name, use `_` as the name of unnamed
1128-
// arguments. Otherwise omit argument names.
1129-
let default_name = if idents.iter().any(|ident| nonempty_name(ident).is_some()) {
1130-
kw::Underscore
1131-
} else {
1132-
kw::Empty // FIXME: using kw::Empty is a bit of a hack
1133-
};
1134-
11351117
types
11361118
.iter()
11371119
.enumerate()
11381120
.map(|(i, ty)| Parameter {
1139-
name: idents.get(i).and_then(nonempty_name).unwrap_or(default_name),
1121+
name: postprocess(idents[i]),
11401122
type_: clean_ty(ty, cx),
11411123
is_const: false,
11421124
})
@@ -1152,7 +1134,7 @@ fn clean_params_via_body<'tcx>(
11521134
.iter()
11531135
.zip(cx.tcx.hir_body(body_id).params)
11541136
.map(|(ty, param)| Parameter {
1155-
name: name_from_pat(param.pat),
1137+
name: Some(name_from_pat(param.pat)),
11561138
type_: clean_ty(ty, cx),
11571139
is_const: false,
11581140
})
@@ -1182,8 +1164,6 @@ fn clean_poly_fn_sig<'tcx>(
11821164
did: Option<DefId>,
11831165
sig: ty::PolyFnSig<'tcx>,
11841166
) -> FnDecl {
1185-
let mut names = did.map_or(&[] as &[_], |did| cx.tcx.fn_arg_idents(did)).iter();
1186-
11871167
let mut output = clean_middle_ty(sig.output(), cx, None, None);
11881168

11891169
// If the return type isn't an `impl Trait`, we can safely assume that this
@@ -1196,12 +1176,20 @@ fn clean_poly_fn_sig<'tcx>(
11961176
output = output.sugared_async_return_type();
11971177
}
11981178

1179+
let mut idents = did.map(|did| cx.tcx.fn_arg_idents(did)).unwrap_or_default().iter().copied();
1180+
1181+
// If this comes from a fn item, let's not perpetuate anon params from Rust 2015; use `_` for them.
1182+
// If this comes from a fn ptr ty, we just keep params unnamed since it's more conventional stylistically.
1183+
// Since the param name is not part of the semantic type, these params never bear a name unlike
1184+
// in the HIR case, thus we can't peform any fancy fallback logic unlike `clean_bare_fn_ty`.
1185+
let fallback = did.map(|_| kw::Underscore);
1186+
11991187
let params = sig
12001188
.inputs()
12011189
.iter()
1202-
.map(|t| Parameter {
1203-
type_: clean_middle_ty(t.map_bound(|t| *t), cx, None, None),
1204-
name: if let Some(Some(ident)) = names.next() { ident.name } else { kw::Underscore },
1190+
.map(|ty| Parameter {
1191+
name: idents.next().flatten().map(|ident| ident.name).or(fallback),
1192+
type_: clean_middle_ty(ty.map_bound(|ty| *ty), cx, None, None),
12051193
is_const: false,
12061194
})
12071195
.collect();
@@ -2591,7 +2579,17 @@ fn clean_bare_fn_ty<'tcx>(
25912579
.filter(|p| !is_elided_lifetime(p))
25922580
.map(|x| clean_generic_param(cx, None, x))
25932581
.collect();
2594-
let params = clean_params(cx, bare_fn.decl.inputs, bare_fn.param_idents);
2582+
// Since it's more conventional stylistically, elide the name of all params called `_`
2583+
// unless there's at least one interestingly named param in which case don't elide any
2584+
// name since mixing named and unnamed params is less legible.
2585+
let filter = |ident: Option<Ident>| {
2586+
ident.map(|ident| ident.name).filter(|&ident| ident != kw::Underscore)
2587+
};
2588+
let fallback =
2589+
bare_fn.param_idents.iter().copied().find_map(filter).map(|_| kw::Underscore);
2590+
let params = clean_params(cx, bare_fn.decl.inputs, bare_fn.param_idents, |ident| {
2591+
filter(ident).or(fallback)
2592+
});
25952593
let decl = clean_fn_decl_with_params(cx, bare_fn.decl, None, params);
25962594
(generic_params, decl)
25972595
});

src/librustdoc/clean/types.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1421,7 +1421,7 @@ impl FnDecl {
14211421
/// A function parameter.
14221422
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
14231423
pub(crate) struct Parameter {
1424-
pub(crate) name: Symbol,
1424+
pub(crate) name: Option<Symbol>,
14251425
pub(crate) type_: Type,
14261426
/// This field is used to represent "const" arguments from the `rustc_legacy_const_generics`
14271427
/// feature. More information in <https://github.com/rust-lang/rust/issues/83167>.
@@ -1430,7 +1430,7 @@ pub(crate) struct Parameter {
14301430

14311431
impl Parameter {
14321432
pub(crate) fn to_receiver(&self) -> Option<&Type> {
1433-
(self.name == kw::SelfLower).then_some(&self.type_)
1433+
if name == Some(kw::SelfLower) { Some(&self.type_) } else { None }
14341434
}
14351435
}
14361436

src/librustdoc/clean/utils.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -303,13 +303,12 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
303303
debug!("trying to get a name from pattern: {p:?}");
304304

305305
Symbol::intern(&match &p.kind {
306-
// FIXME(never_patterns): does this make sense?
307-
PatKind::Missing => unreachable!(),
308-
PatKind::Wild
309-
| PatKind::Err(_)
306+
PatKind::Err(_)
307+
| PatKind::Missing // Let's not perpetuate anon params from Rust 2015; use `_` for them.
310308
| PatKind::Never
309+
| PatKind::Range(..)
311310
| PatKind::Struct(..)
312-
| PatKind::Range(..) => {
311+
| PatKind::Wild => {
313312
return kw::Underscore;
314313
}
315314
PatKind::Binding(_, _, ident, _) => return ident.name,

src/librustdoc/html/format.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1237,8 +1237,8 @@ pub(crate) fn print_params(params: &[clean::Parameter], cx: &Context<'_>) -> imp
12371237
.iter()
12381238
.map(|param| {
12391239
fmt::from_fn(|f| {
1240-
if !param.name.is_empty() {
1241-
write!(f, "{}: ", param.name)?;
1240+
if let Some(name) = param.name {
1241+
write!(f, "{name}: ")?;
12421242
}
12431243
param.type_.print(cx).fmt(f)
12441244
})
@@ -1359,7 +1359,9 @@ impl clean::FnDecl {
13591359
if param.is_const {
13601360
write!(f, "const ")?;
13611361
}
1362-
write!(f, "{}: ", param.name)?;
1362+
if let Some(name) = param.name {
1363+
write!(f, "{name}: ")?;
1364+
}
13631365
param.type_.print(cx).fmt(f)?;
13641366
}
13651367
match (line_wrapping_indent, last_input_index) {

src/librustdoc/json/conversions.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_hir::def::CtorKind;
1111
use rustc_hir::def_id::DefId;
1212
use rustc_metadata::rendered_const;
1313
use rustc_middle::{bug, ty};
14-
use rustc_span::{Pos, Symbol};
14+
use rustc_span::{Pos, Symbol, kw};
1515
use rustdoc_json_types::*;
1616

1717
use crate::clean::{self, ItemId};
@@ -610,7 +610,10 @@ impl FromClean<clean::FnDecl> for FunctionSignature {
610610
FunctionSignature {
611611
inputs: inputs
612612
.into_iter()
613-
.map(|param| (param.name.to_string(), param.type_.into_json(renderer)))
613+
.map(|param| {
614+
// FIXME: using kw::Empty is a bit of a hack
615+
(param.name.unwrap_or(kw::Empty).to_string(), param.type_.into_json(renderer))
616+
})
614617
.collect(),
615618
output: if output.is_unit() { None } else { Some(output.into_json(renderer)) },
616619
is_c_variadic: c_variadic,

tests/rustdoc/anon-fn-params.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Test that we render the deprecated anonymous trait function parameters from Rust 2015 as
2+
// underscores in order not to perpetuate it and for legibility.
3+
4+
//@ edition: 2015
5+
#![expect(anonymous_parameters)]
6+
7+
// Check the "local case" (HIR cleaning) //
8+
9+
//@ has anon_fn_params/trait.Trait.html
10+
pub trait Trait {
11+
//@ has - '//*[@id="tymethod.required"]' 'fn required(_: Option<i32>, _: impl Fn(&str) -> bool)'
12+
fn required(Option<i32>, impl Fn(&str) -> bool);
13+
//@ has - '//*[@id="method.provided"]' 'fn provided(_: [i32; 2])'
14+
fn provided([i32; 2]) {}
15+
}
16+
17+
// Check the "extern case" (middle cleaning) //
18+
19+
//@ aux-build: ext-anon-fn-params.rs
20+
extern crate ext_anon_fn_params;
21+
22+
//@ has anon_fn_params/trait.ExtTrait.html
23+
//@ has - '//*[@id="tymethod.required"]' 'fn required(_: Option<i32>, _: impl Fn(&str) -> bool)'
24+
//@ has - '//*[@id="method.provided"]' 'fn provided(_: [i32; 2])'
25+
pub use ext_anon_fn_params::Trait as ExtTrait;

tests/rustdoc/assoc-fns.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Basic testing for associated functions (in traits, trait impls & inherent impls).
2+
3+
//@ has assoc_fns/trait.Trait.html
4+
pub trait Trait {
5+
//@ has - '//*[@id="tymethod.required"]' 'fn required(first: i32, second: &str)'
6+
fn required(first: i32, second: &str);
7+
8+
//@ has - '//*[@id="method.provided"]' 'fn provided(only: ())'
9+
fn provided(only: ()) {}
10+
11+
//@ has - '//*[@id="tymethod.params_are_unnamed"]' 'fn params_are_unnamed(_: i32, _: u32)'
12+
fn params_are_unnamed(_: i32, _: u32);
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//@ edition: 2015
2+
#![expect(anonymous_parameters)]
3+
4+
pub trait Trait {
5+
fn required(Option<i32>, impl Fn(&str) -> bool);
6+
fn provided([i32; 2]) {}
7+
}

tests/rustdoc/ffi.rs

+4
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ pub use lib::foreigner;
99
extern "C" {
1010
//@ has ffi/fn.another.html //pre 'pub unsafe extern "C" fn another(cold_as_ice: u32)'
1111
pub fn another(cold_as_ice: u32);
12+
13+
//@ has ffi/fn.params_are_unnamed.html //pre \
14+
// 'pub unsafe extern "C" fn params_are_unnamed(_: i32, _: u32)'
15+
pub fn params_are_unnamed(_: i32, _: u32);
1216
}

tests/rustdoc/inline_cross/default-generic-args.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,17 @@ pub use default_generic_args::R2;
5353

5454
//@ has user/type.H0.html
5555
// Check that we handle higher-ranked regions correctly:
56-
//@ has - '//*[@class="rust item-decl"]//code' "fn(_: for<'a> fn(_: Re<'a>))"
56+
//@ has - '//*[@class="rust item-decl"]//code' "fn(for<'a> fn(Re<'a>))"
5757
pub use default_generic_args::H0;
5858

5959
//@ has user/type.H1.html
6060
// Check that we don't conflate distinct universially quantified regions (#1):
61-
//@ has - '//*[@class="rust item-decl"]//code' "for<'b> fn(_: for<'a> fn(_: Re<'a, &'b ()>))"
61+
//@ has - '//*[@class="rust item-decl"]//code' "for<'b> fn(for<'a> fn(Re<'a, &'b ()>))"
6262
pub use default_generic_args::H1;
6363

6464
//@ has user/type.H2.html
6565
// Check that we don't conflate distinct universially quantified regions (#2):
66-
//@ has - '//*[@class="rust item-decl"]//code' "for<'a> fn(_: for<'b> fn(_: Re<'a, &'b ()>))"
66+
//@ has - '//*[@class="rust item-decl"]//code' "for<'a> fn(for<'b> fn(Re<'a, &'b ()>))"
6767
pub use default_generic_args::H2;
6868

6969
//@ has user/type.P0.html
@@ -86,7 +86,7 @@ pub use default_generic_args::A0;
8686
// Demonstrates that we currently don't elide generic arguments that are alpha-equivalent to their
8787
// respective generic parameter (after instantiation) for perf reasons (it would require us to
8888
// create an inference context).
89-
//@ has - '//*[@class="rust item-decl"]//code' "Alpha<for<'arbitrary> fn(_: &'arbitrary ())>"
89+
//@ has - '//*[@class="rust item-decl"]//code' "Alpha<for<'arbitrary> fn(&'arbitrary ())>"
9090
pub use default_generic_args::A1;
9191

9292
//@ has user/type.M0.html

tests/rustdoc/inline_cross/fn-type.rs renamed to tests/rustdoc/inline_cross/fn-ptr-ty.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
// They should be rendered exactly as the user wrote it, i.e., in source order and with unused
33
// parameters present, not stripped.
44

5-
//@ aux-crate:fn_type=fn-type.rs
5+
//@ aux-crate:fn_ptr_ty=fn-ptr-ty.rs
66
//@ edition: 2021
77
#![crate_name = "user"]
88

99
//@ has user/type.F.html
1010
//@ has - '//*[@class="rust item-decl"]//code' \
11-
// "for<'z, 'a, '_unused> fn(_: &'z for<'b> fn(_: &'b str), _: &'a ()) -> &'a ();"
12-
pub use fn_type::F;
11+
// "for<'z, 'a, '_unused> fn(&'z for<'b> fn(&'b str), &'a ()) -> &'a ();"
12+
pub use fn_ptr_ty::F;

tests/rustdoc/inline_cross/impl_trait.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ pub use impl_trait_aux::func4;
2929
//@ has impl_trait/fn.func5.html
3030
//@ has - '//pre[@class="rust item-decl"]' "func5("
3131
//@ has - '//pre[@class="rust item-decl"]' "_f: impl for<'any> Fn(&'any str, &'any str) -> bool + for<'r> Other<T<'r> = ()>,"
32-
//@ has - '//pre[@class="rust item-decl"]' "_a: impl for<'beta, 'alpha, '_gamma> Auxiliary<'alpha, Item<'beta> = fn(_: &'beta ())>"
32+
//@ has - '//pre[@class="rust item-decl"]' "_a: impl for<'beta, 'alpha, '_gamma> Auxiliary<'alpha, Item<'beta> = fn(&'beta ())>"
3333
//@ !has - '//pre[@class="rust item-decl"]' 'where'
3434
pub use impl_trait_aux::func5;
3535

0 commit comments

Comments
 (0)