use std::{borrow::Cow, collections::BTreeMap, num::NonZeroUsize};
use rustdoc_types::{
AssocItemConstraint, AssocItemConstraintKind, GenericArg, GenericArgs, GenericBound,
GenericParamDef, GenericParamDefKind, Generics, Path, Term, Type, WherePredicate,
};
use crate::PackageIndex;
use super::{
names::{Names, is_synthetic_type_param, parameter_impl_trait_placeholder},
sort_key,
};
#[derive(Clone, Copy, Debug)]
struct SyntheticTypeParam {
index: usize,
}
#[derive(Clone, Debug)]
pub(super) struct FnParameterImplTraits {
by_parameter: Vec<Vec<Cow<'static, str>>>,
}
#[derive(Clone, Copy)]
pub(super) struct ParameterImplTraitCursor {
parameter_index: usize,
next: usize,
}
impl FnParameterImplTraits {
pub(super) fn cursor_for_parameter(&self, position: NonZeroUsize) -> ParameterImplTraitCursor {
let parameter_index = position.get() - 1;
self.by_parameter
.get(parameter_index)
.expect("function parameter position was out of bounds");
ParameterImplTraitCursor {
parameter_index,
next: 0,
}
}
}
impl ParameterImplTraitCursor {
pub(super) fn next<'a>(&mut self, impl_traits: &'a FnParameterImplTraits) -> &'a str {
let names = impl_traits
.by_parameter
.get(self.parameter_index)
.expect("parameter impl Trait cursor had an invalid parameter index");
let name = names.get(self.next).unwrap_or_else(|| {
unreachable!(
"parameter-position impl Trait had no matching synthetic generic parameter: \
next={}, names={:?}",
self.next, names,
)
});
self.next += 1;
name.as_ref()
}
pub(super) fn next_index(&self) -> usize {
self.next
}
pub(super) fn assert_finished(&self, impl_traits: &FnParameterImplTraits) {
let names = impl_traits
.by_parameter
.get(self.parameter_index)
.expect("parameter impl Trait cursor had an invalid parameter index");
assert_eq!(
self.next,
names.len(),
"not all parameter-position impl Trait synthetic generic parameters were used",
);
}
}
fn collect_type_impl_trait_params<'a>(
crate_: &PackageIndex<'_>,
names: &Names<'a>,
type_: &'a Type,
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
output: &mut Vec<SyntheticTypeParam>,
) {
match type_ {
Type::ResolvedPath(path) => {
collect_path_impl_trait_params(crate_, names, path, synthetic_params, output);
}
Type::DynTrait(dyn_trait) => {
let mut traits = Vec::new();
for trait_ in &dyn_trait.traits {
assert_supported_hrtb_generic_params(&trait_.generic_params);
let mut scoped_names = names.clone();
for param in &trait_.generic_params {
scoped_names.add_higher_ranked_param(param);
}
let mut params = Vec::new();
collect_path_impl_trait_params(
crate_,
&scoped_names,
&trait_.trait_,
synthetic_params,
&mut params,
);
traits.push((
sort_key::parameter_poly_trait(crate_, names, trait_),
params,
));
}
traits.sort_unstable_by(|a, b| a.0.cmp(&b.0));
for (_, params) in traits {
output.extend(params);
}
}
Type::Generic(_) | Type::Primitive(_) | Type::Infer | Type::Pat { .. } => {
}
Type::FunctionPointer(pointer) => {
assert_supported_hrtb_generic_params(&pointer.generic_params);
}
Type::Tuple(types) => {
for type_ in types {
collect_type_impl_trait_params(crate_, names, type_, synthetic_params, output);
}
}
Type::Slice(type_) | Type::Array { type_, .. } => {
collect_type_impl_trait_params(crate_, names, type_, synthetic_params, output);
}
Type::ImplTrait(bounds) => {
collect_bounds_impl_trait_params(crate_, names, bounds, synthetic_params, output);
output.push(synthetic_params.next().expect(
"parameter-position impl Trait had no matching synthetic generic parameter",
));
}
Type::RawPointer { type_, .. } | Type::BorrowedRef { type_, .. } => {
collect_type_impl_trait_params(crate_, names, type_, synthetic_params, output);
}
Type::QualifiedPath {
args,
self_type,
trait_,
..
} => {
collect_type_impl_trait_params(crate_, names, self_type, synthetic_params, output);
if let Some(trait_) = trait_ {
collect_path_impl_trait_params(crate_, names, trait_, synthetic_params, output);
}
if let Some(args) = args.as_deref() {
collect_generic_args_impl_trait_params(
crate_,
names,
args,
synthetic_params,
output,
);
}
}
}
}
fn assert_supported_hrtb_generic_params(params: &[GenericParamDef]) {
for param in params {
match ¶m.kind {
GenericParamDefKind::Lifetime { .. } => {}
GenericParamDefKind::Type { .. } => {
unreachable!("found type generic param definition in HRTB position: {param:?}");
}
GenericParamDefKind::Const { .. } => {
unreachable!("found const generic param definition in HRTB position: {param:?}");
}
}
}
}
fn collect_path_impl_trait_params<'a>(
crate_: &PackageIndex<'_>,
names: &Names<'a>,
path: &'a Path,
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
output: &mut Vec<SyntheticTypeParam>,
) {
if let Some(args) = path.args.as_deref() {
collect_generic_args_impl_trait_params(crate_, names, args, synthetic_params, output);
}
}
fn collect_generic_args_impl_trait_params<'a>(
crate_: &PackageIndex<'_>,
names: &Names<'a>,
args: &'a GenericArgs,
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
output: &mut Vec<SyntheticTypeParam>,
) {
match args {
GenericArgs::AngleBracketed { args, constraints } => {
for arg in args {
match arg {
GenericArg::Type(type_) => {
collect_type_impl_trait_params(
crate_,
names,
type_,
synthetic_params,
output,
);
}
GenericArg::Lifetime(_) | GenericArg::Const(_) | GenericArg::Infer => {}
}
}
let mut constraint_params = Vec::new();
for constraint in constraints {
let mut params = Vec::new();
collect_assoc_item_constraint_impl_trait_params_raw(
crate_,
names,
constraint,
synthetic_params,
&mut params,
);
constraint_params.push((
sort_key::parameter_assoc_item_constraint(crate_, names, constraint),
params,
));
}
constraint_params.sort_unstable_by(|a, b| a.0.cmp(&b.0));
for (_, params) in constraint_params {
output.extend(params);
}
}
GenericArgs::Parenthesized { .. } => {
}
GenericArgs::ReturnTypeNotation => {}
}
}
fn collect_assoc_item_constraint_impl_trait_params_raw<'a>(
crate_: &PackageIndex<'_>,
names: &Names<'a>,
constraint: &'a AssocItemConstraint,
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
output: &mut Vec<SyntheticTypeParam>,
) {
if let Some(args) = constraint.args.as_deref() {
collect_generic_args_impl_trait_params(crate_, names, args, synthetic_params, output);
}
match &constraint.binding {
AssocItemConstraintKind::Constraint(bounds) => {
collect_bounds_impl_trait_params(crate_, names, bounds, synthetic_params, output);
}
AssocItemConstraintKind::Equality(term) => {
collect_term_impl_trait_params(crate_, names, term, synthetic_params, output);
}
}
}
fn collect_bounds_impl_trait_params<'a>(
crate_: &PackageIndex<'_>,
names: &Names<'a>,
bounds: &'a [GenericBound],
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
output: &mut Vec<SyntheticTypeParam>,
) {
let mut bound_params = Vec::new();
for bound in bounds {
let mut params = Vec::new();
match bound {
GenericBound::TraitBound {
trait_,
generic_params,
..
} => {
assert_supported_hrtb_generic_params(generic_params);
let mut scoped_names = names.clone();
for param in generic_params {
scoped_names.add_higher_ranked_param(param);
}
collect_path_impl_trait_params(
crate_,
&scoped_names,
trait_,
synthetic_params,
&mut params,
);
}
GenericBound::Outlives(_) | GenericBound::Use(_) => {}
}
bound_params.push((
sort_key::parameter_generic_bound(crate_, names, bound),
params,
));
}
bound_params.sort_unstable_by(|a, b| a.0.cmp(&b.0));
for (_, params) in bound_params {
output.extend(params);
}
}
fn collect_term_impl_trait_params<'a>(
crate_: &PackageIndex<'_>,
names: &Names<'a>,
term: &'a Term,
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
output: &mut Vec<SyntheticTypeParam>,
) {
match term {
Term::Type(type_) => {
collect_type_impl_trait_params(crate_, names, type_, synthetic_params, output);
}
Term::Constant(constant) => {
unreachable!("found associated const equality constraint term: {constant:?}");
}
}
}
pub(super) fn compute_for_function<'a>(
crate_: &'a PackageIndex<'a>,
names: &Names<'a>,
function: &'a rustdoc_types::Function,
) -> FnParameterImplTraits {
let synthetic_params = function
.generics
.params
.iter()
.enumerate()
.filter(|(_, param)| is_synthetic_type_param(param))
.map(|(index, _)| SyntheticTypeParam { index })
.collect::<Vec<_>>();
let mut synthetic_params = synthetic_params.iter().copied();
let mut sink = Vec::new();
collect_generics_impl_trait_names(
&function.generics,
&mut synthetic_params,
None,
&mut sink,
false,
);
let mut output = Vec::with_capacity(function.sig.inputs.len());
for (index, (_, type_)) in function.sig.inputs.iter().enumerate() {
let parameter_position = index + 1;
let mut canonical_synthetic_params = synthetic_params.clone();
let mut canonical_params = Vec::new();
collect_type_impl_trait_params(
crate_,
names,
type_,
&mut canonical_synthetic_params,
&mut canonical_params,
);
let mut synthetic_names_by_index = BTreeMap::new();
for (local_index, param) in canonical_params.iter().enumerate() {
let existing = synthetic_names_by_index.insert(
param.index,
parameter_impl_trait_placeholder(parameter_position, local_index + 1),
);
assert!(
existing.is_none(),
"duplicate synthetic type parameter index `{}` in parameter {parameter_position}",
param.index,
);
}
let mut parameter_names = Vec::new();
collect_type_impl_trait_names(
type_,
&mut synthetic_params,
Some(&synthetic_names_by_index),
&mut parameter_names,
true,
);
output.push(parameter_names);
}
collect_where_predicates_impl_trait_names(
&function.generics.where_predicates,
&mut synthetic_params,
None,
&mut sink,
false,
);
assert!(
synthetic_params.next().is_none(),
"rustdoc synthetic generics outnumbered parameter-position impl Trait occurrences",
);
FnParameterImplTraits {
by_parameter: output,
}
}
fn collect_generics_impl_trait_names(
generics: &Generics,
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
synthetic_names_by_index: Option<&BTreeMap<usize, Cow<'static, str>>>,
output: &mut Vec<Cow<'static, str>>,
emit: bool,
) {
for param in &generics.params {
match ¶m.kind {
GenericParamDefKind::Type {
bounds,
default,
is_synthetic: false,
} => {
collect_bounds_impl_trait_names(
bounds,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
if let Some(default) = default {
collect_type_impl_trait_names(
default,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
}
GenericParamDefKind::Type {
is_synthetic: true, ..
}
| GenericParamDefKind::Lifetime { .. } => {}
GenericParamDefKind::Const { type_, .. } => {
collect_type_impl_trait_names(
type_,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
}
}
}
fn collect_type_impl_trait_names(
type_: &Type,
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
synthetic_names_by_index: Option<&BTreeMap<usize, Cow<'static, str>>>,
output: &mut Vec<Cow<'static, str>>,
emit: bool,
) {
match type_ {
Type::ResolvedPath(path) => {
collect_path_impl_trait_names(
path,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
Type::DynTrait(dyn_trait) => {
for trait_ in &dyn_trait.traits {
assert_supported_hrtb_generic_params(&trait_.generic_params);
collect_path_impl_trait_names(
&trait_.trait_,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
}
Type::Generic(_) | Type::Primitive(_) | Type::Infer | Type::Pat { .. } => {}
Type::FunctionPointer(pointer) => {
assert_supported_hrtb_generic_params(&pointer.generic_params);
}
Type::Tuple(types) => {
for type_ in types {
collect_type_impl_trait_names(
type_,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
}
Type::Slice(type_) | Type::Array { type_, .. } => {
collect_type_impl_trait_names(
type_,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
Type::ImplTrait(bounds) => {
collect_bounds_impl_trait_names(
bounds,
synthetic_params,
synthetic_names_by_index,
output,
false,
);
let param = synthetic_params.next().expect(
"parameter-position impl Trait had no matching synthetic generic parameter",
);
if emit {
let names = synthetic_names_by_index.expect(
"parameter-position impl Trait name emission requires precomputed names",
);
output.push(names.get(¶m.index).cloned().unwrap_or_else(|| {
unreachable!(
"missing normalized name for synthetic type parameter index `{}`",
param.index,
)
}));
}
}
Type::RawPointer { type_, .. } | Type::BorrowedRef { type_, .. } => {
collect_type_impl_trait_names(
type_,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
Type::QualifiedPath {
args,
self_type,
trait_,
..
} => {
collect_type_impl_trait_names(
self_type,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
if let Some(trait_) = trait_ {
collect_path_impl_trait_names(
trait_,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
if let Some(args) = args.as_deref() {
collect_generic_args_impl_trait_names(
args,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
}
}
}
fn collect_path_impl_trait_names(
path: &Path,
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
synthetic_names_by_index: Option<&BTreeMap<usize, Cow<'static, str>>>,
output: &mut Vec<Cow<'static, str>>,
emit: bool,
) {
if let Some(args) = path.args.as_deref() {
collect_generic_args_impl_trait_names(
args,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
}
fn collect_generic_args_impl_trait_names(
args: &GenericArgs,
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
synthetic_names_by_index: Option<&BTreeMap<usize, Cow<'static, str>>>,
output: &mut Vec<Cow<'static, str>>,
emit: bool,
) {
match args {
GenericArgs::AngleBracketed { args, constraints } => {
for arg in args {
match arg {
GenericArg::Type(type_) => {
collect_type_impl_trait_names(
type_,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
GenericArg::Lifetime(_) | GenericArg::Const(_) | GenericArg::Infer => {}
}
}
for constraint in constraints {
collect_assoc_item_constraint_impl_trait_names(
constraint,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
}
GenericArgs::Parenthesized { .. } => {
}
GenericArgs::ReturnTypeNotation => {}
}
}
fn collect_assoc_item_constraint_impl_trait_names(
constraint: &AssocItemConstraint,
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
synthetic_names_by_index: Option<&BTreeMap<usize, Cow<'static, str>>>,
output: &mut Vec<Cow<'static, str>>,
emit: bool,
) {
if let Some(args) = constraint.args.as_deref() {
collect_generic_args_impl_trait_names(
args,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
match &constraint.binding {
AssocItemConstraintKind::Constraint(bounds) => {
collect_bounds_impl_trait_names(
bounds,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
AssocItemConstraintKind::Equality(term) => {
collect_term_impl_trait_names(
term,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
}
}
fn collect_bounds_impl_trait_names(
bounds: &[GenericBound],
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
synthetic_names_by_index: Option<&BTreeMap<usize, Cow<'static, str>>>,
output: &mut Vec<Cow<'static, str>>,
emit: bool,
) {
for bound in bounds {
match bound {
GenericBound::TraitBound {
trait_,
generic_params,
..
} => {
assert_supported_hrtb_generic_params(generic_params);
collect_path_impl_trait_names(
trait_,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
GenericBound::Outlives(_) | GenericBound::Use(_) => {}
}
}
}
fn collect_term_impl_trait_names(
term: &Term,
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
synthetic_names_by_index: Option<&BTreeMap<usize, Cow<'static, str>>>,
output: &mut Vec<Cow<'static, str>>,
emit: bool,
) {
match term {
Term::Type(type_) => {
collect_type_impl_trait_names(
type_,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
Term::Constant(constant) => {
unreachable!("found associated const equality constraint term: {constant:?}");
}
}
}
fn collect_where_predicates_impl_trait_names(
predicates: &[WherePredicate],
synthetic_params: &mut impl Iterator<Item = SyntheticTypeParam>,
synthetic_names_by_index: Option<&BTreeMap<usize, Cow<'static, str>>>,
output: &mut Vec<Cow<'static, str>>,
emit: bool,
) {
for predicate in predicates {
match predicate {
WherePredicate::BoundPredicate {
type_,
bounds,
generic_params,
} => {
assert_supported_hrtb_generic_params(generic_params);
collect_type_impl_trait_names(
type_,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
collect_bounds_impl_trait_names(
bounds,
synthetic_params,
synthetic_names_by_index,
output,
emit,
);
}
WherePredicate::LifetimePredicate { .. } => {}
WherePredicate::EqPredicate { .. } => {
unreachable!(
"found general equality predicate in function generics: {predicate:?}"
);
}
}
}
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use crate::adapter::normalize::names::parameter_impl_trait_placeholder;
#[test]
fn first_impl_trait_names_use_requested_static_prefix() {
assert!(matches!(
parameter_impl_trait_placeholder(1, 1),
Cow::Borrowed("IT1_1")
));
assert!(matches!(
parameter_impl_trait_placeholder(8, 1),
Cow::Borrowed("IT8_1")
));
assert!(
matches!(parameter_impl_trait_placeholder(9, 1), Cow::Owned(value) if value == "IT9_1")
);
assert!(
matches!(parameter_impl_trait_placeholder(1, 2), Cow::Owned(value) if value == "IT1_2")
);
}
}