1
Fork 0

Store all generic bounds as where predicates.

This commit is contained in:
Camille GILLOT 2022-02-07 22:58:30 +01:00
parent 05b29f9a92
commit 94449e6101
30 changed files with 770 additions and 953 deletions

View file

@ -267,9 +267,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
this.lower_maybe_async_body(span, &decl, asyncness, body.as_deref()); this.lower_maybe_async_body(span, &decl, asyncness, body.as_deref());
let (generics, decl) = let (generics, decl) =
this.add_implicit_generics(generics, id, |this, idty| { this.add_implicit_generics(generics, id, |this, idty, idpb| {
let ret_id = asyncness.opt_return_id(); let ret_id = asyncness.opt_return_id();
this.lower_fn_decl(&decl, Some((id, idty)), FnDeclKind::Fn, ret_id) this.lower_fn_decl(
&decl,
Some((id, idty, idpb)),
FnDeclKind::Fn,
ret_id,
)
}); });
let sig = hir::FnSig { let sig = hir::FnSig {
decl, decl,
@ -384,7 +389,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// lifetime to be added, but rather a reference to a // lifetime to be added, but rather a reference to a
// parent lifetime. // parent lifetime.
let (generics, (trait_ref, lowered_ty)) = let (generics, (trait_ref, lowered_ty)) =
self.add_implicit_generics(ast_generics, id, |this, _| { self.add_implicit_generics(ast_generics, id, |this, _, _| {
let trait_ref = trait_ref.as_ref().map(|trait_ref| { let trait_ref = trait_ref.as_ref().map(|trait_ref| {
this.lower_trait_ref( this.lower_trait_ref(
trait_ref, trait_ref,
@ -649,7 +654,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ForeignItemKind::Fn(box Fn { ref sig, ref generics, .. }) => { ForeignItemKind::Fn(box Fn { ref sig, ref generics, .. }) => {
let fdec = &sig.decl; let fdec = &sig.decl;
let (generics, (fn_dec, fn_args)) = let (generics, (fn_dec, fn_args)) =
self.add_implicit_generics(generics, i.id, |this, _| { self.add_implicit_generics(generics, i.id, |this, _, _| {
( (
// Disallow `impl Trait` in foreign items. // Disallow `impl Trait` in foreign items.
this.lower_fn_decl(fdec, None, FnDeclKind::ExternFn, None), this.lower_fn_decl(fdec, None, FnDeclKind::ExternFn, None),
@ -1228,8 +1233,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
is_async: Option<NodeId>, is_async: Option<NodeId>,
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
let header = self.lower_fn_header(sig.header); let header = self.lower_fn_header(sig.header);
let (generics, decl) = self.add_implicit_generics(generics, id, |this, idty| { let (generics, decl) = self.add_implicit_generics(generics, id, |this, idty, idpb| {
this.lower_fn_decl(&sig.decl, Some((id, idty)), kind, is_async) this.lower_fn_decl(&sig.decl, Some((id, idty, idpb)), kind, is_async)
}); });
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) }) (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
} }
@ -1289,7 +1294,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
pub(super) fn lower_generics_mut( pub(super) fn lower_generics_mut(
&mut self, &mut self,
generics: &Generics, generics: &Generics,
itctx: ImplTraitContext<'_, 'hir>, mut itctx: ImplTraitContext<'_, 'hir>,
) -> GenericsCtor<'hir> { ) -> GenericsCtor<'hir> {
// Error if `?Trait` bounds in where clauses don't refer directly to type parameters. // Error if `?Trait` bounds in where clauses don't refer directly to type parameters.
// Note: we used to clone these bounds directly onto the type parameter (and avoid lowering // Note: we used to clone these bounds directly onto the type parameter (and avoid lowering
@ -1338,7 +1343,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
} }
} }
let predicates = self.arena.alloc_from_iter( let mut predicates = SmallVec::new();
predicates.extend(generics.params.iter().filter_map(|param| {
let bounds = self.lower_param_bounds(&param.bounds, itctx.reborrow());
self.lower_generic_bound_predicate(param.ident, param.id, &param.kind, bounds)
}));
predicates.extend(
generics generics
.where_clause .where_clause
.predicates .predicates
@ -1347,8 +1357,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
); );
GenericsCtor { GenericsCtor {
params: self.lower_generic_params_mut(&generics.params, itctx).collect(), params: self.lower_generic_params_mut(&generics.params).collect(),
predicates, predicates,
has_where_clause: !generics.where_clause.predicates.is_empty(),
where_clause_span: self.lower_span(generics.where_clause.span), where_clause_span: self.lower_span(generics.where_clause.span),
span: self.lower_span(generics.span), span: self.lower_span(generics.span),
} }
@ -1363,6 +1374,72 @@ impl<'hir> LoweringContext<'_, 'hir> {
generics_ctor.into_generics(self.arena) generics_ctor.into_generics(self.arena)
} }
pub(super) fn lower_generic_bound_predicate(
&mut self,
ident: Ident,
id: NodeId,
kind: &GenericParamKind,
bounds: &'hir [hir::GenericBound<'hir>],
) -> Option<hir::WherePredicate<'hir>> {
// Do not create a clause if we do not have anything inside it.
if bounds.is_empty() {
return None;
}
let ident = self.lower_ident(ident);
let param_span = ident.span;
let span = bounds
.iter()
.fold(Some(param_span.shrink_to_hi()), |span: Option<Span>, bound| {
let bound_span = bound.span();
// We include bounds that come from a `#[derive(_)]` but point at the user's code,
// as we use this method to get a span appropriate for suggestions.
if !bound_span.can_be_used_for_suggestions() {
None
} else if let Some(span) = span {
Some(span.to(bound_span))
} else {
Some(bound_span)
}
})
.unwrap_or(param_span.shrink_to_hi());
match kind {
GenericParamKind::Const { .. } => None,
GenericParamKind::Type { .. } => {
let def_id = self.resolver.local_def_id(id).to_def_id();
let ty_path = self.arena.alloc(hir::Path {
span: param_span,
res: Res::Def(DefKind::TyParam, def_id),
segments: self.arena.alloc_from_iter([hir::PathSegment::from_ident(ident)]),
});
let ty_id = self.next_id();
let bounded_ty =
self.ty_path(ty_id, param_span, hir::QPath::Resolved(None, ty_path));
Some(hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
bounded_ty: self.arena.alloc(bounded_ty),
bounds,
span,
bound_generic_params: &[],
in_where_clause: false,
}))
}
GenericParamKind::Lifetime => {
let ident_span = self.lower_span(ident.span);
let ident = self.lower_ident(ident);
let res = self.resolver.get_lifetime_res(id).unwrap_or_else(|| {
panic!("Missing resolution for lifetime {:?} at {:?}", id, ident.span)
});
let lt_id = self.resolver.next_node_id();
let lifetime = self.new_named_lifetime_with_res(lt_id, ident_span, ident, res);
Some(hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
lifetime,
span,
bounds,
in_where_clause: false,
}))
}
}
}
fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> { fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> {
match *pred { match *pred {
WherePredicate::BoundPredicate(WhereBoundPredicate { WherePredicate::BoundPredicate(WhereBoundPredicate {
@ -1371,10 +1448,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ref bounds, ref bounds,
span, span,
}) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { }) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
bound_generic_params: self.lower_generic_params( bound_generic_params: self.lower_generic_params(bound_generic_params),
bound_generic_params,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
),
bounded_ty: self bounded_ty: self
.lower_ty(bounded_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)), .lower_ty(bounded_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)),
bounds: self.arena.alloc_from_iter(bounds.iter().map(|bound| { bounds: self.arena.alloc_from_iter(bounds.iter().map(|bound| {
@ -1384,6 +1458,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
) )
})), })),
span: self.lower_span(span), span: self.lower_span(span),
in_where_clause: true,
}), }),
WherePredicate::RegionPredicate(WhereRegionPredicate { WherePredicate::RegionPredicate(WhereRegionPredicate {
ref lifetime, ref lifetime,
@ -1396,6 +1471,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
bounds, bounds,
ImplTraitContext::Disallowed(ImplTraitPosition::Bound), ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
), ),
in_where_clause: true,
}), }),
WherePredicate::EqPredicate(WhereEqPredicate { id, ref lhs_ty, ref rhs_ty, span }) => { WherePredicate::EqPredicate(WhereEqPredicate { id, ref lhs_ty, ref rhs_ty, span }) => {
hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
@ -1414,7 +1490,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// Helper struct for delayed construction of Generics. /// Helper struct for delayed construction of Generics.
pub(super) struct GenericsCtor<'hir> { pub(super) struct GenericsCtor<'hir> {
pub(super) params: SmallVec<[hir::GenericParam<'hir>; 4]>, pub(super) params: SmallVec<[hir::GenericParam<'hir>; 4]>,
predicates: &'hir [hir::WherePredicate<'hir>], pub(super) predicates: SmallVec<[hir::WherePredicate<'hir>; 4]>,
has_where_clause: bool,
where_clause_span: Span, where_clause_span: Span,
span: Span, span: Span,
} }
@ -1423,7 +1500,8 @@ impl<'hir> GenericsCtor<'hir> {
pub(super) fn into_generics(self, arena: &'hir Arena<'hir>) -> &'hir hir::Generics<'hir> { pub(super) fn into_generics(self, arena: &'hir Arena<'hir>) -> &'hir hir::Generics<'hir> {
arena.alloc(hir::Generics { arena.alloc(hir::Generics {
params: arena.alloc_from_iter(self.params), params: arena.alloc_from_iter(self.params),
predicates: self.predicates, predicates: arena.alloc_from_iter(self.predicates),
has_where_clause: self.has_where_clause,
where_clause_span: self.where_clause_span, where_clause_span: self.where_clause_span,
span: self.span, span: self.span,
}) })

View file

@ -259,7 +259,7 @@ enum ImplTraitContext<'b, 'a> {
/// equivalent to a fresh universal parameter like `fn foo<T: Debug>(x: T)`. /// equivalent to a fresh universal parameter like `fn foo<T: Debug>(x: T)`.
/// ///
/// Newly generated parameters should be inserted into the given `Vec`. /// Newly generated parameters should be inserted into the given `Vec`.
Universal(&'b mut Vec<hir::GenericParam<'a>>, LocalDefId), Universal(&'b mut Vec<hir::GenericParam<'a>>, &'b mut Vec<hir::WherePredicate<'a>>, LocalDefId),
/// Treat `impl Trait` as shorthand for a new opaque type. /// Treat `impl Trait` as shorthand for a new opaque type.
/// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually
@ -303,7 +303,7 @@ impl<'a> ImplTraitContext<'_, 'a> {
fn reborrow<'this>(&'this mut self) -> ImplTraitContext<'this, 'a> { fn reborrow<'this>(&'this mut self) -> ImplTraitContext<'this, 'a> {
use self::ImplTraitContext::*; use self::ImplTraitContext::*;
match self { match self {
Universal(params, parent) => Universal(params, *parent), Universal(params, bounds, parent) => Universal(params, bounds, *parent),
ReturnPositionOpaqueTy { origin } => ReturnPositionOpaqueTy { origin: *origin }, ReturnPositionOpaqueTy { origin } => ReturnPositionOpaqueTy { origin: *origin },
TypeAliasesOpaqueTy => TypeAliasesOpaqueTy, TypeAliasesOpaqueTy => TypeAliasesOpaqueTy,
Disallowed(pos) => Disallowed(*pos), Disallowed(pos) => Disallowed(*pos),
@ -704,7 +704,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
Some(hir::GenericParam { Some(hir::GenericParam {
hir_id, hir_id,
name, name,
bounds: &[],
span: self.lower_span(ident.span), span: self.lower_span(ident.span),
pure_wrt_drop: false, pure_wrt_drop: false,
kind: hir::GenericParamKind::Lifetime { kind }, kind: hir::GenericParamKind::Lifetime { kind },
@ -718,14 +717,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&mut self, &mut self,
generics: &Generics, generics: &Generics,
parent_node_id: NodeId, parent_node_id: NodeId,
f: impl FnOnce(&mut Self, &mut Vec<hir::GenericParam<'hir>>) -> T, f: impl FnOnce(
&mut Self,
&mut Vec<hir::GenericParam<'hir>>,
&mut Vec<hir::WherePredicate<'hir>>,
) -> T,
) -> (&'hir hir::Generics<'hir>, T) { ) -> (&'hir hir::Generics<'hir>, T) {
let mut impl_trait_defs = Vec::new(); let mut impl_trait_defs = Vec::new();
let mut impl_trait_bounds = Vec::new();
let mut lowered_generics = self.lower_generics_mut( let mut lowered_generics = self.lower_generics_mut(
generics, generics,
ImplTraitContext::Universal(&mut impl_trait_defs, self.current_hir_id_owner), ImplTraitContext::Universal(
&mut impl_trait_defs,
&mut impl_trait_bounds,
self.current_hir_id_owner,
),
); );
let res = f(self, &mut impl_trait_defs); let res = f(self, &mut impl_trait_defs, &mut impl_trait_bounds);
let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id); let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id);
lowered_generics.params.extend( lowered_generics.params.extend(
@ -736,6 +744,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}) })
.chain(impl_trait_defs), .chain(impl_trait_defs),
); );
lowered_generics.predicates.extend(impl_trait_bounds);
let lowered_generics = lowered_generics.into_generics(self.arena); let lowered_generics = lowered_generics.into_generics(self.arena);
(lowered_generics, res) (lowered_generics, res)
@ -999,7 +1008,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// so desugar to // so desugar to
// //
// fn foo(x: dyn Iterator<Item = impl Debug>) // fn foo(x: dyn Iterator<Item = impl Debug>)
ImplTraitContext::Universal(_, parent) if self.is_in_dyn_type => { ImplTraitContext::Universal(_, _, parent) if self.is_in_dyn_type => {
parent_def_id = parent; parent_def_id = parent;
(true, itctx) (true, itctx)
} }
@ -1188,10 +1197,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
} }
TyKind::BareFn(ref f) => self.with_lifetime_binder(t.id, |this| { TyKind::BareFn(ref f) => self.with_lifetime_binder(t.id, |this| {
hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy { hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
generic_params: this.lower_generic_params( generic_params: this.lower_generic_params(&f.generic_params),
&f.generic_params,
ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
),
unsafety: this.lower_unsafety(f.unsafety), unsafety: this.lower_unsafety(f.unsafety),
abi: this.lower_extern(f.ext), abi: this.lower_extern(f.ext),
decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None), decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
@ -1274,13 +1280,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|this| this.lower_param_bounds(bounds, nested_itctx), |this| this.lower_param_bounds(bounds, nested_itctx),
) )
} }
ImplTraitContext::Universal(in_band_ty_params, parent_def_id) => { ImplTraitContext::Universal(
in_band_ty_params,
in_band_ty_bounds,
parent_def_id,
) => {
// Add a definition for the in-band `Param`. // Add a definition for the in-band `Param`.
let def_id = self.resolver.local_def_id(def_node_id); let def_id = self.resolver.local_def_id(def_node_id);
let hir_bounds = self.lower_param_bounds( let hir_bounds = self.lower_param_bounds(
bounds, bounds,
ImplTraitContext::Universal(in_band_ty_params, parent_def_id), ImplTraitContext::Universal(
in_band_ty_params,
in_band_ty_bounds,
parent_def_id,
),
); );
// Set the name to `impl Bound1 + Bound2`. // Set the name to `impl Bound1 + Bound2`.
let ident = Ident::from_str_and_span(&pprust::ty_to_string(t), span); let ident = Ident::from_str_and_span(&pprust::ty_to_string(t), span);
@ -1288,10 +1302,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir_id: self.lower_node_id(def_node_id), hir_id: self.lower_node_id(def_node_id),
name: ParamName::Plain(self.lower_ident(ident)), name: ParamName::Plain(self.lower_ident(ident)),
pure_wrt_drop: false, pure_wrt_drop: false,
bounds: hir_bounds,
span: self.lower_span(span), span: self.lower_span(span),
kind: hir::GenericParamKind::Type { default: None, synthetic: true }, kind: hir::GenericParamKind::Type { default: None, synthetic: true },
}); });
if let Some(preds) = self.lower_generic_bound_predicate(
ident,
def_node_id,
&GenericParamKind::Type { default: None },
hir_bounds,
) {
in_band_ty_bounds.push(preds)
}
hir::TyKind::Path(hir::QPath::Resolved( hir::TyKind::Path(hir::QPath::Resolved(
None, None,
@ -1374,7 +1395,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
name: p_name, name: p_name,
span, span,
pure_wrt_drop: false, pure_wrt_drop: false,
bounds: &[],
kind: hir::GenericParamKind::Lifetime { kind }, kind: hir::GenericParamKind::Lifetime { kind },
} }
}, },
@ -1386,6 +1406,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
generics: self.arena.alloc(hir::Generics { generics: self.arena.alloc(hir::Generics {
params: lifetime_defs, params: lifetime_defs,
predicates: &[], predicates: &[],
has_where_clause: false,
where_clause_span: lctx.lower_span(span), where_clause_span: lctx.lower_span(span),
span: lctx.lower_span(span), span: lctx.lower_span(span),
}), }),
@ -1463,7 +1484,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_fn_decl( fn lower_fn_decl(
&mut self, &mut self,
decl: &FnDecl, decl: &FnDecl,
mut in_band_ty_params: Option<(NodeId, &mut Vec<hir::GenericParam<'hir>>)>, mut in_band_ty_params: Option<(
NodeId,
&mut Vec<hir::GenericParam<'hir>>,
&mut Vec<hir::WherePredicate<'hir>>,
)>,
kind: FnDeclKind, kind: FnDeclKind,
make_ret_async: Option<NodeId>, make_ret_async: Option<NodeId>,
) -> &'hir hir::FnDecl<'hir> { ) -> &'hir hir::FnDecl<'hir> {
@ -1486,10 +1511,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
inputs = &inputs[..inputs.len() - 1]; inputs = &inputs[..inputs.len() - 1];
} }
let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| { let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| {
if let Some((_, ibty)) = &mut in_band_ty_params { if let Some((_, ibty, ibpb)) = &mut in_band_ty_params {
self.lower_ty_direct( self.lower_ty_direct(
&param.ty, &param.ty,
ImplTraitContext::Universal(ibty, self.current_hir_id_owner), ImplTraitContext::Universal(ibty, ibpb, self.current_hir_id_owner),
) )
} else { } else {
self.lower_ty_direct( self.lower_ty_direct(
@ -1518,7 +1543,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
match decl.output { match decl.output {
FnRetTy::Ty(ref ty) => { FnRetTy::Ty(ref ty) => {
let context = match in_band_ty_params { let context = match in_band_ty_params {
Some((node_id, _)) if kind.impl_trait_return_allowed() => { Some((node_id, _, _)) if kind.impl_trait_return_allowed() => {
let fn_def_id = self.resolver.local_def_id(node_id); let fn_def_id = self.resolver.local_def_id(node_id);
ImplTraitContext::ReturnPositionOpaqueTy { ImplTraitContext::ReturnPositionOpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
@ -1709,7 +1734,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
name: p_name, name: p_name,
span, span,
pure_wrt_drop: false, pure_wrt_drop: false,
bounds: &[],
kind: hir::GenericParamKind::Lifetime { kind }, kind: hir::GenericParamKind::Lifetime { kind },
} }
})); }));
@ -1719,6 +1743,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
generics: this.arena.alloc(hir::Generics { generics: this.arena.alloc(hir::Generics {
params: generic_params, params: generic_params,
predicates: &[], predicates: &[],
has_where_clause: false,
where_clause_span: this.lower_span(span), where_clause_span: this.lower_span(span),
span: this.lower_span(span), span: this.lower_span(span),
}), }),
@ -1925,26 +1950,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_generic_params_mut<'s>( fn lower_generic_params_mut<'s>(
&'s mut self, &'s mut self,
params: &'s [GenericParam], params: &'s [GenericParam],
mut itctx: ImplTraitContext<'s, 'hir>,
) -> impl Iterator<Item = hir::GenericParam<'hir>> + Captures<'a> + Captures<'s> { ) -> impl Iterator<Item = hir::GenericParam<'hir>> + Captures<'a> + Captures<'s> {
params.iter().map(move |param| self.lower_generic_param(param, itctx.reborrow())) params.iter().map(move |param| self.lower_generic_param(param))
} }
fn lower_generic_params( fn lower_generic_params(&mut self, params: &[GenericParam]) -> &'hir [hir::GenericParam<'hir>] {
&mut self, self.arena.alloc_from_iter(self.lower_generic_params_mut(params))
params: &[GenericParam],
itctx: ImplTraitContext<'_, 'hir>,
) -> &'hir [hir::GenericParam<'hir>] {
self.arena.alloc_from_iter(self.lower_generic_params_mut(params, itctx))
} }
fn lower_generic_param( fn lower_generic_param(&mut self, param: &GenericParam) -> hir::GenericParam<'hir> {
&mut self,
param: &GenericParam,
mut itctx: ImplTraitContext<'_, 'hir>,
) -> hir::GenericParam<'hir> {
let bounds: Vec<_> = self.lower_param_bounds_mut(&param.bounds, itctx.reborrow()).collect();
let (name, kind) = match param.kind { let (name, kind) = match param.kind {
GenericParamKind::Lifetime => { GenericParamKind::Lifetime => {
let param_name = if param.ident.name == kw::StaticLifetime let param_name = if param.ident.name == kw::StaticLifetime
@ -1991,7 +2005,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
name, name,
span: self.lower_span(param.span()), span: self.lower_span(param.span()),
pure_wrt_drop: self.sess.contains_name(&param.attrs, sym::may_dangle), pure_wrt_drop: self.sess.contains_name(&param.attrs, sym::may_dangle),
bounds: self.arena.alloc_from_iter(bounds),
kind, kind,
} }
} }
@ -2014,8 +2027,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
p: &PolyTraitRef, p: &PolyTraitRef,
mut itctx: ImplTraitContext<'_, 'hir>, mut itctx: ImplTraitContext<'_, 'hir>,
) -> hir::PolyTraitRef<'hir> { ) -> hir::PolyTraitRef<'hir> {
let bound_generic_params = let bound_generic_params = self.lower_generic_params(&p.bound_generic_params);
self.lower_generic_params(&p.bound_generic_params, itctx.reborrow());
let trait_ref = self.with_lifetime_binder(p.trait_ref.ref_id, |this| { let trait_ref = self.with_lifetime_binder(p.trait_ref.ref_id, |this| {
this.lower_trait_ref(&p.trait_ref, itctx.reborrow()) this.lower_trait_ref(&p.trait_ref, itctx.reborrow())

View file

@ -496,27 +496,24 @@ pub enum GenericParamKind<'hir> {
pub struct GenericParam<'hir> { pub struct GenericParam<'hir> {
pub hir_id: HirId, pub hir_id: HirId,
pub name: ParamName, pub name: ParamName,
pub bounds: GenericBounds<'hir>,
pub span: Span, pub span: Span,
pub pure_wrt_drop: bool, pub pure_wrt_drop: bool,
pub kind: GenericParamKind<'hir>, pub kind: GenericParamKind<'hir>,
} }
impl<'hir> GenericParam<'hir> { impl<'hir> GenericParam<'hir> {
pub fn bounds_span_for_suggestions(&self) -> Option<Span> { /// Synthetic type-parameters are inserted after normal ones.
self.bounds /// In order for normal parameters to be able to refer to synthetic ones,
.iter() /// scans them first.
.fold(None, |span: Option<Span>, bound| { pub fn is_impl_trait(&self) -> bool {
// We include bounds that come from a `#[derive(_)]` but point at the user's code, matches!(self.kind, GenericParamKind::Type { synthetic: true, .. })
// as we use this method to get a span appropriate for suggestions.
if !bound.span().can_be_used_for_suggestions() {
None
} else {
let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span());
Some(span)
} }
})
.map(|sp| sp.shrink_to_hi()) /// This can happen for `async fn`, e.g. `async fn f<'_>(&'_ self)`.
///
/// See `lifetime_to_generic_param` in `rustc_ast_lowering` for more information.
pub fn is_elided_lifetime(&self) -> bool {
matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided })
} }
/// Returns the span of `:` after a generic parameter. /// Returns the span of `:` after a generic parameter.
@ -568,14 +565,20 @@ pub struct GenericParamCount {
pub struct Generics<'hir> { pub struct Generics<'hir> {
pub params: &'hir [GenericParam<'hir>], pub params: &'hir [GenericParam<'hir>],
pub predicates: &'hir [WherePredicate<'hir>], pub predicates: &'hir [WherePredicate<'hir>],
pub has_where_clause: bool,
pub where_clause_span: Span, pub where_clause_span: Span,
pub span: Span, pub span: Span,
} }
impl<'hir> Generics<'hir> { impl<'hir> Generics<'hir> {
pub const fn empty() -> &'hir Generics<'hir> { pub const fn empty() -> &'hir Generics<'hir> {
const NOPE: Generics<'_> = const NOPE: Generics<'_> = Generics {
Generics { params: &[], predicates: &[], where_clause_span: DUMMY_SP, span: DUMMY_SP }; params: &[],
predicates: &[],
has_where_clause: false,
where_clause_span: DUMMY_SP,
span: DUMMY_SP,
};
&NOPE &NOPE
} }
@ -596,6 +599,19 @@ impl<'hir> Generics<'hir> {
} }
} }
/// If there are generic parameters, return wher to introduce a new one, and false.
/// If there is none, return where to introduce `<>` and true.
pub fn span_for_param_suggestion(&self) -> Option<Span> {
if self.params.iter().any(|p| self.span.contains(p.span)) {
// `fn foo<A>(t: impl Trait)`
// ^ suggest `, T: Trait` here
let span = self.span.with_lo(self.span.hi() - BytePos(1)).shrink_to_lo();
Some(span)
} else {
None
}
}
pub fn where_clause_span(&self) -> Option<Span> { pub fn where_clause_span(&self) -> Option<Span> {
if self.predicates.is_empty() { None } else { Some(self.where_clause_span) } if self.predicates.is_empty() { None } else { Some(self.where_clause_span) }
} }
@ -610,7 +626,95 @@ impl<'hir> Generics<'hir> {
/// in `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas. /// in `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
pub fn tail_span_for_predicate_suggestion(&self) -> Span { pub fn tail_span_for_predicate_suggestion(&self) -> Span {
let end = self.span_for_predicates_or_empty_place().shrink_to_hi(); let end = self.span_for_predicates_or_empty_place().shrink_to_hi();
self.predicates.last().map_or(end, |p| p.span()).shrink_to_hi().to(end) if self.has_where_clause {
self.predicates
.iter()
.filter(|p| p.in_where_clause())
.last()
.map_or(end, |p| p.span())
.shrink_to_hi()
.to(end)
} else {
end
}
}
pub fn bounds_for_param(
&self,
param_def_id: LocalDefId,
) -> impl Iterator<Item = &WhereBoundPredicate<'_>> {
self.predicates.iter().filter_map(move |pred| match pred {
WherePredicate::BoundPredicate(bp) if bp.is_param_bound(param_def_id.to_def_id()) => {
Some(bp)
}
_ => None,
})
}
pub fn bounds_span_for_suggestions(&self, param_def_id: LocalDefId) -> Option<Span> {
self.bounds_for_param(param_def_id).flat_map(|bp| bp.bounds.iter().rev()).find_map(
|bound| {
// We include bounds that come from a `#[derive(_)]` but point at the user's code,
// as we use this method to get a span appropriate for suggestions.
let bs = bound.span();
if bs.can_be_used_for_suggestions() { Some(bs.shrink_to_hi()) } else { None }
},
)
}
pub fn span_for_predicate_removal(&self, pos: usize) -> Span {
let predicate = &self.predicates[pos];
let span = predicate.span();
if !predicate.in_where_clause() {
// <T: ?Sized, U>
// ^^^^^^^^
return span;
}
// We need to find out which comma to remove.
if pos < self.predicates.len() - 1 {
let next_pred = &self.predicates[pos + 1];
if next_pred.in_where_clause() {
// where T: ?Sized, Foo: Bar,
// ^^^^^^^^^^^
return span.until(next_pred.span());
}
}
if pos > 0 {
let prev_pred = &self.predicates[pos - 1];
if prev_pred.in_where_clause() {
// where Foo: Bar, T: ?Sized,
// ^^^^^^^^^^^
return prev_pred.span().shrink_to_hi().to(span);
}
}
// This is the only predicate in the where clause.
// where T: ?Sized
// ^^^^^^^^^^^^^^^
self.where_clause_span
}
pub fn span_for_bound_removal(&self, predicate_pos: usize, bound_pos: usize) -> Span {
let predicate = &self.predicates[predicate_pos];
let bounds = predicate.bounds();
if bounds.len() == 1 {
return self.span_for_predicate_removal(predicate_pos);
}
let span = bounds[bound_pos].span();
if bound_pos == 0 {
// where T: ?Sized + Bar, Foo: Bar,
// ^^^^^^^^^
span.to(bounds[1].span().shrink_to_lo())
} else {
// where T: Bar + ?Sized, Foo: Bar,
// ^^^^^^^^^
bounds[bound_pos - 1].span().shrink_to_hi().to(span)
}
} }
} }
@ -633,12 +737,29 @@ impl<'hir> WherePredicate<'hir> {
WherePredicate::EqPredicate(p) => p.span, WherePredicate::EqPredicate(p) => p.span,
} }
} }
pub fn in_where_clause(&self) -> bool {
match self {
WherePredicate::BoundPredicate(p) => p.in_where_clause,
WherePredicate::RegionPredicate(p) => p.in_where_clause,
WherePredicate::EqPredicate(_) => false,
}
}
pub fn bounds(&self) -> GenericBounds<'hir> {
match self {
WherePredicate::BoundPredicate(p) => p.bounds,
WherePredicate::RegionPredicate(p) => p.bounds,
WherePredicate::EqPredicate(_) => &[],
}
}
} }
/// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`).
#[derive(Debug, HashStable_Generic)] #[derive(Debug, HashStable_Generic)]
pub struct WhereBoundPredicate<'hir> { pub struct WhereBoundPredicate<'hir> {
pub span: Span, pub span: Span,
pub in_where_clause: bool,
/// Any generics from a `for` binding. /// Any generics from a `for` binding.
pub bound_generic_params: &'hir [GenericParam<'hir>], pub bound_generic_params: &'hir [GenericParam<'hir>],
/// The type being bounded. /// The type being bounded.
@ -650,14 +771,7 @@ pub struct WhereBoundPredicate<'hir> {
impl<'hir> WhereBoundPredicate<'hir> { impl<'hir> WhereBoundPredicate<'hir> {
/// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate. /// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate.
pub fn is_param_bound(&self, param_def_id: DefId) -> bool { pub fn is_param_bound(&self, param_def_id: DefId) -> bool {
let TyKind::Path(QPath::Resolved(None, path)) = self.bounded_ty.kind else { self.bounded_ty.as_generic_param().map_or(false, |(def_id, _)| def_id == param_def_id)
return false;
};
match path.res {
Res::Def(DefKind::TyParam, def_id)
| Res::SelfTy { trait_: Some(def_id), alias_to: None } => def_id == param_def_id,
_ => false,
}
} }
} }
@ -665,6 +779,7 @@ impl<'hir> WhereBoundPredicate<'hir> {
#[derive(Debug, HashStable_Generic)] #[derive(Debug, HashStable_Generic)]
pub struct WhereRegionPredicate<'hir> { pub struct WhereRegionPredicate<'hir> {
pub span: Span, pub span: Span,
pub in_where_clause: bool,
pub lifetime: Lifetime, pub lifetime: Lifetime,
pub bounds: GenericBounds<'hir>, pub bounds: GenericBounds<'hir>,
} }
@ -2230,6 +2345,23 @@ pub struct Ty<'hir> {
pub span: Span, pub span: Span,
} }
impl<'hir> Ty<'hir> {
/// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate.
pub fn as_generic_param(&self) -> Option<(DefId, Ident)> {
let TyKind::Path(QPath::Resolved(None, path)) = self.kind else {
return None;
};
let [segment] = &path.segments else {
return None;
};
match path.res {
Res::Def(DefKind::TyParam, def_id)
| Res::SelfTy { trait_: Some(def_id), alias_to: None } => Some((def_id, segment.ident)),
_ => None,
}
}
}
/// Not represented directly in the AST; referred to by name through a `ty_path`. /// Not represented directly in the AST; referred to by name through a `ty_path`.
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)] #[derive(HashStable_Generic)]
@ -3315,7 +3447,7 @@ mod size_asserts {
rustc_data_structures::static_assert_size!(super::QPath<'static>, 24); rustc_data_structures::static_assert_size!(super::QPath<'static>, 24);
rustc_data_structures::static_assert_size!(super::Ty<'static>, 72); rustc_data_structures::static_assert_size!(super::Ty<'static>, 72);
rustc_data_structures::static_assert_size!(super::GenericBound<'_>, 48); rustc_data_structures::static_assert_size!(super::GenericBound<'_>, 48);
rustc_data_structures::static_assert_size!(super::Generics<'static>, 48); rustc_data_structures::static_assert_size!(super::Generics<'static>, 56);
rustc_data_structures::static_assert_size!(super::Impl<'static>, 80); rustc_data_structures::static_assert_size!(super::Impl<'static>, 80);
rustc_data_structures::static_assert_size!(super::Item<'static>, 80); rustc_data_structures::static_assert_size!(super::Item<'static>, 80);

View file

@ -899,7 +899,6 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Generi
} }
} }
} }
walk_list!(visitor, visit_param_bound, param.bounds);
} }
pub fn walk_const_param_default<'v, V: Visitor<'v>>(visitor: &mut V, ct: &'v AnonConst) { pub fn walk_const_param_default<'v, V: Visitor<'v>>(visitor: &mut V, ct: &'v AnonConst) {

View file

@ -2096,21 +2096,8 @@ impl<'a> State<'a> {
self.print_ident(param.name.ident()); self.print_ident(param.name.ident());
match param.kind { match param.kind {
GenericParamKind::Lifetime { .. } => { GenericParamKind::Lifetime { .. } => {}
let mut sep = ":";
for bound in param.bounds {
match bound {
GenericBound::Outlives(ref lt) => {
self.word(sep);
self.print_lifetime(lt);
sep = "+";
}
_ => panic!(),
}
}
}
GenericParamKind::Type { ref default, .. } => { GenericParamKind::Type { ref default, .. } => {
self.print_bounds(":", param.bounds);
if let Some(default) = default { if let Some(default) = default {
self.space(); self.space();
self.word_space("="); self.word_space("=");

View file

@ -2327,6 +2327,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
_ => span, _ => span,
}; };
// type_param_span is (span, has_bounds)
let type_param_span = match (generics, bound_kind) { let type_param_span = match (generics, bound_kind) {
(Some((_, ref generics, _)), GenericKind::Param(ref param)) => { (Some((_, ref generics, _)), GenericKind::Param(ref param)) => {
// Account for the case where `param` corresponds to `Self`, // Account for the case where `param` corresponds to `Self`,
@ -2337,25 +2338,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// Get the `hir::Param` to verify whether it already has any bounds. // Get the `hir::Param` to verify whether it already has any bounds.
// We do this to avoid suggesting code that ends up as `T: 'a'b`, // We do this to avoid suggesting code that ends up as `T: 'a'b`,
// instead we suggest `T: 'a + 'b` in that case. // instead we suggest `T: 'a + 'b` in that case.
let id = hir.local_def_id_to_hir_id(def_id); let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let mut has_bounds = false; let ast_generics = self.tcx.hir().get_generics(hir_id.owner);
if let Node::GenericParam(param) = hir.get(id) { let bounds =
has_bounds = !param.bounds.is_empty(); ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id));
}
let sp = self.tcx.def_span(def_id);
// `sp` only covers `T`, change it so that it covers // `sp` only covers `T`, change it so that it covers
// `T:` when appropriate // `T:` when appropriate
let is_impl_trait = bound_kind.to_string().starts_with("impl "); if let Some(span) = bounds {
let sp = if has_bounds && !is_impl_trait { (span, true)
sp.to(self
.tcx
.sess
.source_map()
.next_point(self.tcx.sess.source_map().next_point(sp)))
} else { } else {
sp let sp = self.tcx.def_span(def_id);
}; (sp.shrink_to_hi(), false)
(sp, has_bounds, is_impl_trait) }
}) })
} else { } else {
None None
@ -2411,52 +2405,37 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
fn binding_suggestion<'tcx, S: fmt::Display>( fn binding_suggestion<'tcx, S: fmt::Display>(
err: &mut Diagnostic, err: &mut Diagnostic,
type_param_span: Option<(Span, bool, bool)>, type_param_span: Option<(Span, bool)>,
bound_kind: GenericKind<'tcx>, bound_kind: GenericKind<'tcx>,
sub: S, sub: S,
) { ) {
let msg = "consider adding an explicit lifetime bound"; let msg = "consider adding an explicit lifetime bound";
if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span { if let Some((sp, has_lifetimes)) = type_param_span {
let suggestion = if is_impl_trait { let suggestion =
format!("{} + {}", bound_kind, sub) if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) };
} else { err.span_suggestion_verbose(
let tail = if has_lifetimes { " + " } else { "" };
format!("{}: {}{}", bound_kind, sub, tail)
};
err.span_suggestion(
sp, sp,
&format!("{}...", msg), &format!("{}...", msg),
suggestion, suggestion,
Applicability::MaybeIncorrect, // Issue #41966 Applicability::MaybeIncorrect, // Issue #41966
); );
} else { } else {
let consider = format!( let consider = format!("{} `{}: {}`...", msg, bound_kind, sub,);
"{} {}...",
msg,
if type_param_span.map_or(false, |(_, _, is_impl_trait)| is_impl_trait) {
format!(" `{}` to `{}`", sub, bound_kind)
} else {
format!("`{}: {}`", bound_kind, sub)
},
);
err.help(&consider); err.help(&consider);
} }
} }
let new_binding_suggestion = let new_binding_suggestion =
|err: &mut Diagnostic, |err: &mut Diagnostic, type_param_span: Option<(Span, bool)>| {
type_param_span: Option<(Span, bool, bool)>,
bound_kind: GenericKind<'tcx>| {
let msg = "consider introducing an explicit lifetime bound"; let msg = "consider introducing an explicit lifetime bound";
if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span { if let Some((sp, has_lifetimes)) = type_param_span {
let suggestion = if is_impl_trait { let suggestion = if has_lifetimes {
(sp.shrink_to_hi(), format!(" + {}", new_lt)) format!(" + {}", new_lt)
} else { } else {
let tail = if has_lifetimes { " +" } else { "" }; format!(": {}", new_lt)
(sp, format!("{}: {}{}", bound_kind, new_lt, tail))
}; };
let mut sugg = let mut sugg =
vec![suggestion, (span.shrink_to_hi(), format!(" + {}", new_lt))]; vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {}", new_lt))];
if let Some(lt) = add_lt_sugg { if let Some(lt) = add_lt_sugg {
sugg.push(lt); sugg.push(lt);
sugg.rotate_right(1); sugg.rotate_right(1);
@ -2615,7 +2594,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// suggest: // suggest:
// fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a // fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
ty::Closure(_, _substs) | ty::Opaque(_, _substs) if return_impl_trait => { ty::Closure(_, _substs) | ty::Opaque(_, _substs) if return_impl_trait => {
new_binding_suggestion(&mut err, type_param_span, bound_kind); new_binding_suggestion(&mut err, type_param_span);
} }
_ => { _ => {
binding_suggestion(&mut err, type_param_span, bound_kind, new_lt); binding_suggestion(&mut err, type_param_span, bound_kind, new_lt);

View file

@ -1517,21 +1517,33 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
// Bounds are respected for `type X = impl Trait` // Bounds are respected for `type X = impl Trait`
return; return;
} }
let mut suggested_changing_assoc_types = false;
// There must not be a where clause // There must not be a where clause
if !type_alias_generics.predicates.is_empty() { if type_alias_generics.predicates.is_empty() {
cx.lint( return;
TYPE_ALIAS_BOUNDS, }
|lint| {
let mut where_spans = Vec::new();
let mut inline_spans = Vec::new();
let mut inline_sugg = Vec::new();
for p in type_alias_generics.predicates {
let span = p.span();
if p.in_where_clause() {
where_spans.push(span);
} else {
for b in p.bounds() {
inline_spans.push(b.span());
}
inline_sugg.push((span, String::new()));
}
}
let mut suggested_changing_assoc_types = false;
if !where_spans.is_empty() {
cx.lint(TYPE_ALIAS_BOUNDS, |lint| {
let mut err = lint.build("where clauses are not enforced in type aliases"); let mut err = lint.build("where clauses are not enforced in type aliases");
let spans: Vec<_> = type_alias_generics err.set_span(where_spans);
.predicates
.iter()
.map(|pred| pred.span())
.collect();
err.set_span(spans);
err.span_suggestion( err.span_suggestion(
type_alias_generics.span_for_predicates_or_empty_place(), type_alias_generics.where_clause_span,
"the clause will not be checked when the type alias is used, and should be removed", "the clause will not be checked when the type alias is used, and should be removed",
String::new(), String::new(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
@ -1541,35 +1553,26 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
suggested_changing_assoc_types = true; suggested_changing_assoc_types = true;
} }
err.emit(); err.emit();
}, });
);
} }
// The parameters must not have bounds
for param in type_alias_generics.params.iter() { if !inline_spans.is_empty() {
let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect(); cx.lint(TYPE_ALIAS_BOUNDS, |lint| {
let suggestion = spans
.iter()
.map(|sp| {
let start = param.span.between(*sp); // Include the `:` in `T: Bound`.
(start.to(*sp), String::new())
})
.collect();
if !spans.is_empty() {
cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans, |lint| {
let mut err = let mut err =
lint.build("bounds on generic parameters are not enforced in type aliases"); lint.build("bounds on generic parameters are not enforced in type aliases");
let msg = "the bound will not be checked when the type alias is used, \ err.set_span(inline_spans);
and should be removed"; err.multipart_suggestion(
err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable); "the bound will not be checked when the type alias is used, and should be removed",
inline_sugg,
Applicability::MachineApplicable,
);
if !suggested_changing_assoc_types { if !suggested_changing_assoc_types {
TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
suggested_changing_assoc_types = true;
} }
err.emit(); err.emit();
}); });
} }
} }
}
} }
declare_lint_pass!( declare_lint_pass!(
@ -2084,27 +2087,6 @@ impl ExplicitOutlivesRequirements {
.collect() .collect()
} }
fn collect_outlived_lifetimes<'tcx>(
&self,
param: &'tcx hir::GenericParam<'tcx>,
tcx: TyCtxt<'tcx>,
inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
ty_generics: &'tcx ty::Generics,
) -> Vec<ty::Region<'tcx>> {
let index =
ty_generics.param_def_id_to_index[&tcx.hir().local_def_id(param.hir_id).to_def_id()];
match param.kind {
hir::GenericParamKind::Lifetime { .. } => {
Self::lifetimes_outliving_lifetime(inferred_outlives, index)
}
hir::GenericParamKind::Type { .. } => {
Self::lifetimes_outliving_type(inferred_outlives, index)
}
hir::GenericParamKind::Const { .. } => Vec::new(),
}
}
fn collect_outlives_bound_spans<'tcx>( fn collect_outlives_bound_spans<'tcx>(
&self, &self,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
@ -2212,41 +2194,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
let mut bound_count = 0; let mut bound_count = 0;
let mut lint_spans = Vec::new(); let mut lint_spans = Vec::new();
for param in hir_generics.params {
let has_lifetime_bounds = param
.bounds
.iter()
.any(|bound| matches!(bound, hir::GenericBound::Outlives(_)));
if !has_lifetime_bounds {
continue;
}
let relevant_lifetimes =
self.collect_outlived_lifetimes(param, cx.tcx, inferred_outlives, ty_generics);
if relevant_lifetimes.is_empty() {
continue;
}
let bound_spans = self.collect_outlives_bound_spans(
cx.tcx,
&param.bounds,
&relevant_lifetimes,
infer_static,
);
bound_count += bound_spans.len();
lint_spans.extend(self.consolidate_outlives_bound_spans(
param.span.shrink_to_hi(),
&param.bounds,
bound_spans,
));
}
let mut where_lint_spans = Vec::new(); let mut where_lint_spans = Vec::new();
let mut dropped_predicate_count = 0; let mut dropped_predicate_count = 0;
let num_predicates = hir_generics.predicates.len(); let num_predicates = hir_generics.predicates.len();
for (i, where_predicate) in hir_generics.predicates.iter().enumerate() { for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
let (relevant_lifetimes, bounds, span) = match where_predicate { let (relevant_lifetimes, bounds, span, in_where_clause) = match where_predicate {
hir::WherePredicate::RegionPredicate(predicate) => { hir::WherePredicate::RegionPredicate(predicate) => {
if let Some(Region::EarlyBound(index, ..)) = if let Some(Region::EarlyBound(index, ..)) =
cx.tcx.named_region(predicate.lifetime.hir_id) cx.tcx.named_region(predicate.lifetime.hir_id)
@ -2255,6 +2207,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
Self::lifetimes_outliving_lifetime(inferred_outlives, index), Self::lifetimes_outliving_lifetime(inferred_outlives, index),
&predicate.bounds, &predicate.bounds,
predicate.span, predicate.span,
predicate.in_where_clause,
) )
} else { } else {
continue; continue;
@ -2273,6 +2226,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
Self::lifetimes_outliving_type(inferred_outlives, index), Self::lifetimes_outliving_type(inferred_outlives, index),
&predicate.bounds, &predicate.bounds,
predicate.span, predicate.span,
predicate.in_where_clause,
) )
} }
_ => { _ => {
@ -2299,9 +2253,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
dropped_predicate_count += 1; dropped_predicate_count += 1;
} }
if drop_predicate && !in_where_clause {
lint_spans.push(span);
} else if drop_predicate && i + 1 < num_predicates {
// If all the bounds on a predicate were inferable and there are // If all the bounds on a predicate were inferable and there are
// further predicates, we want to eat the trailing comma. // further predicates, we want to eat the trailing comma.
if drop_predicate && i + 1 < num_predicates {
let next_predicate_span = hir_generics.predicates[i + 1].span(); let next_predicate_span = hir_generics.predicates[i + 1].span();
where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo())); where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo()));
} else { } else {
@ -2315,7 +2271,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
// If all predicates are inferable, drop the entire clause // If all predicates are inferable, drop the entire clause
// (including the `where`) // (including the `where`)
if num_predicates > 0 && dropped_predicate_count == num_predicates { if hir_generics.has_where_clause && dropped_predicate_count == num_predicates {
let where_span = hir_generics let where_span = hir_generics
.where_clause_span() .where_clause_span()
.expect("span of (nonempty) where clause should exist"); .expect("span of (nonempty) where clause should exist");
@ -2344,7 +2300,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
}, },
lint_spans lint_spans
.into_iter() .into_iter()
.map(|span| (span, "".to_owned())) .map(|span| (span, String::new()))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
) )

View file

@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg}; use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; use rustc_hir::WherePredicate;
use rustc_span::Span; use rustc_span::Span;
impl<'tcx> IntoDiagnosticArg for Ty<'tcx> { impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
@ -156,10 +156,10 @@ pub fn suggest_arbitrary_trait_bound(
_ => {} _ => {}
} }
// Suggest a where clause bound for a non-type parameter. // Suggest a where clause bound for a non-type parameter.
let (action, prefix) = if generics.predicates.is_empty() { let (action, prefix) = if generics.has_where_clause {
("introducing a", " where ")
} else {
("extending the", ", ") ("extending the", ", ")
} else {
("introducing a", " where ")
}; };
err.span_suggestion_verbose( err.span_suggestion_verbose(
generics.tail_span_for_predicate_suggestion(), generics.tail_span_for_predicate_suggestion(),
@ -183,61 +183,32 @@ enum SuggestChangingConstraintsMessage<'a> {
} }
fn suggest_removing_unsized_bound( fn suggest_removing_unsized_bound(
tcx: TyCtxt<'_>,
generics: &hir::Generics<'_>, generics: &hir::Generics<'_>,
suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>, suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
param_name: &str,
param: &hir::GenericParam<'_>, param: &hir::GenericParam<'_>,
def_id: Option<DefId>, def_id: Option<DefId>,
) { ) {
// See if there's a `?Sized` bound that can be removed to suggest that. // See if there's a `?Sized` bound that can be removed to suggest that.
// First look at the `where` clause because we can have `where T: ?Sized`, // First look at the `where` clause because we can have `where T: ?Sized`,
// then look at params. // then look at params.
let param_def_id = tcx.hir().local_def_id(param.hir_id);
for (where_pos, predicate) in generics.predicates.iter().enumerate() { for (where_pos, predicate) in generics.predicates.iter().enumerate() {
match predicate { let WherePredicate::BoundPredicate(predicate) = predicate else {
WherePredicate::BoundPredicate(WhereBoundPredicate { continue;
bounded_ty: };
hir::Ty { if !predicate.is_param_bound(param_def_id.to_def_id()) {
kind: continue;
hir::TyKind::Path(hir::QPath::Resolved(
None,
hir::Path {
segments: [segment],
res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
..
},
)),
..
},
bounds,
span,
..
}) if segment.ident.as_str() == param_name => {
for (pos, bound) in bounds.iter().enumerate() {
match bound {
hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
if poly.trait_ref.trait_def_id() == def_id => {}
_ => continue,
}
let sp = match (bounds.len(), pos, generics.predicates.len(), where_pos) {
// where T: ?Sized
// ^^^^^^^^^^^^^^^
(1, _, 1, _) => generics.where_clause_span,
// where Foo: Bar, T: ?Sized,
// ^^^^^^^^^^^
(1, _, len, pos) if pos == len - 1 => {
generics.predicates[pos - 1].span().shrink_to_hi().to(*span)
}
// where T: ?Sized, Foo: Bar,
// ^^^^^^^^^^^
(1, _, _, pos) => span.until(generics.predicates[pos + 1].span()),
// where T: ?Sized + Bar, Foo: Bar,
// ^^^^^^^^^
(_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
// where T: Bar + ?Sized, Foo: Bar,
// ^^^^^^^^^
(_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
}; };
for (pos, bound) in predicate.bounds.iter().enumerate() {
let hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound else {
continue;
};
if poly.trait_ref.trait_def_id() != def_id {
continue;
}
let sp = generics.span_for_bound_removal(where_pos, pos);
suggestions.push(( suggestions.push((
sp, sp,
String::new(), String::new(),
@ -245,35 +216,6 @@ fn suggest_removing_unsized_bound(
)); ));
} }
} }
_ => {}
}
}
for (pos, bound) in param.bounds.iter().enumerate() {
match bound {
hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
if poly.trait_ref.trait_def_id() == def_id =>
{
let sp = match (param.bounds.len(), pos) {
// T: ?Sized,
// ^^^^^^^^
(1, _) => param.span.shrink_to_hi().to(bound.span()),
// T: ?Sized + Bar,
// ^^^^^^^^^
(_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
// T: Bar + ?Sized,
// ^^^^^^^^^
(_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
};
suggestions.push((
sp,
String::new(),
SuggestChangingConstraintsMessage::RemovingQSized,
));
}
_ => {}
}
}
} }
/// Suggest restricting a type param with a new bound. /// Suggest restricting a type param with a new bound.
@ -322,13 +264,7 @@ pub fn suggest_constraining_type_params<'a>(
param.span, param.span,
&format!("this type parameter needs to be `{}`", constraint), &format!("this type parameter needs to be `{}`", constraint),
); );
suggest_removing_unsized_bound( suggest_removing_unsized_bound(tcx, generics, &mut suggestions, param, def_id);
generics,
&mut suggestions,
param_name,
param,
def_id,
);
} }
} }
@ -349,36 +285,21 @@ pub fn suggest_constraining_type_params<'a>(
)) ))
}; };
if param_name.starts_with("impl ") { // When the type parameter has been provided bounds
// If there's an `impl Trait` used in argument position, suggest
// restricting it:
// //
// fn foo(t: impl Foo) { ... } // Message:
// -------- // fn foo<T>(t: T) where T: Foo { ... }
// ^^^^^^
// | // |
// help: consider further restricting this bound with `+ Bar` // help: consider further restricting this bound with `+ Bar`
// //
// Suggestion for tools in this case is: // Suggestion:
// // fn foo<T>(t: T) where T: Foo { ... }
// fn foo(t: impl Foo) { ... } // ^
// --------
// | // |
// replace with: `impl Foo + Bar` // replace with: ` + Bar`
//
// `impl Trait` must have at least one trait in the list // Or, if user has provided some bounds, suggest restricting them:
let bound_list_non_empty = true;
suggest_restrict(param.span.shrink_to_hi(), bound_list_non_empty);
continue;
}
if generics.predicates.is_empty()
// Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
// `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
&& !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
{
if let Some(span) = param.bounds_span_for_suggestions() {
// If user has provided some bounds, suggest restricting them:
// //
// fn foo<T: Foo>(t: T) { ... } // fn foo<T: Foo>(t: T) { ... }
// --- // ---
@ -391,34 +312,18 @@ pub fn suggest_constraining_type_params<'a>(
// -- // --
// | // |
// replace with: `T: Bar +` // replace with: `T: Bar +`
let param_def_id = tcx.hir().local_def_id(param.hir_id);
// `bounds_span_for_suggestions` returns `None` if the list is empty if let Some(span) = generics.bounds_span_for_suggestions(param_def_id) {
let bound_list_non_empty = true; suggest_restrict(span, true);
continue;
suggest_restrict(span, bound_list_non_empty);
} else {
let (colon, span) = match param.colon_span_for_suggestions(tcx.sess.source_map()) {
// If there is already a colon after generic, do not suggest adding it again
Some(sp) => ("", sp.shrink_to_hi()),
None => (":", param.span.shrink_to_hi()),
};
// If user hasn't provided any bounds, suggest adding a new one:
//
// fn foo<T>(t: T) { ... }
// - help: consider restricting this type parameter with `T: Foo`
suggestions.push((
span,
format!("{colon} {constraint}"),
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
));
} }
} else {
if generics.has_where_clause {
// This part is a bit tricky, because using the `where` clause user can // This part is a bit tricky, because using the `where` clause user can
// provide zero, one or many bounds for the same type parameter, so we // provide zero, one or many bounds for the same type parameter, so we
// have following cases to consider: // have following cases to consider:
// //
// 1) When the type parameter has been provided zero bounds // When the type parameter has been provided zero bounds
// //
// Message: // Message:
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... } // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
@ -427,47 +332,27 @@ pub fn suggest_constraining_type_params<'a>(
// Suggestion: // Suggestion:
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... } // fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
// - insert: `, X: Bar` // - insert: `, X: Bar`
// suggestions.push((
// generics.tail_span_for_predicate_suggestion(),
// 2) When the type parameter has been provided one bound constraints
// .iter()
// Message: .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
// fn foo<T>(t: T) where T: Foo { ... } .collect::<String>(),
// ^^^^^^ SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
// | ));
// help: consider further restricting this bound with `+ Bar` continue;
// }
// Suggestion:
// fn foo<T>(t: T) where T: Foo { ... } // Additionally, there may be no `where` clause but the generic parameter has a default:
// ^^
// |
// replace with: `T: Bar +`
//
//
// 3) When the type parameter has been provided many bounds
//
// Message:
// fn foo<T>(t: T) where T: Foo, T: Bar {... }
// - help: consider further restricting this type parameter with `where T: Zar`
//
// Suggestion:
// fn foo<T>(t: T) where T: Foo, T: Bar {... }
// - insert: `, T: Zar`
//
// Additionally, there may be no `where` clause whatsoever in the case that this was
// reached because the generic parameter has a default:
// //
// Message: // Message:
// trait Foo<T=()> {... } // trait Foo<T=()> {... }
// - help: consider further restricting this type parameter with `where T: Zar` // - help: consider further restricting this type parameter with `where T: Zar`
// //
// Suggestion: // Suggestion:
// trait Foo<T=()> where T: Zar {... } // trait Foo<T=()> {... }
// - insert: `where T: Zar` // - insert: `where T: Zar`
if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) {
if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
&& generics.predicates.len() == 0
{
// Suggest a bound, but there is no existing `where` clause *and* the type param has a // Suggest a bound, but there is no existing `where` clause *and* the type param has a
// default (`<T=Foo>`), so we suggest adding `where T: Bar`. // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
suggestions.push(( suggestions.push((
@ -475,48 +360,19 @@ pub fn suggest_constraining_type_params<'a>(
format!(" where {}: {}", param_name, constraint), format!(" where {}: {}", param_name, constraint),
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
)); ));
} else { continue;
let mut param_spans = Vec::new();
let mut non_empty = false;
for predicate in generics.predicates {
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
span,
bounded_ty,
bounds,
..
}) = predicate
{
if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
if let Some(segment) = path.segments.first() {
if segment.ident.to_string() == param_name {
non_empty = !bounds.is_empty();
param_spans.push(span);
}
}
}
}
} }
match param_spans[..] { // If user hasn't provided any bounds, suggest adding a new one:
[&param_span] => suggest_restrict(param_span.shrink_to_hi(), non_empty), //
_ => { // fn foo<T>(t: T) { ... }
// - help: consider restricting this type parameter with `T: Foo`
suggestions.push(( suggestions.push((
generics.tail_span_for_predicate_suggestion(), param.span.shrink_to_hi(),
constraints format!(": {}", constraint),
.iter() SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
.map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
.collect::<String>(),
SuggestChangingConstraintsMessage::RestrictTypeFurther {
ty: param_name,
},
)); ));
} }
}
}
}
}
if suggestions.len() == 1 { if suggestions.len() == 1 {
let (span, suggestion, msg) = suggestions.pop().unwrap(); let (span, suggestion, msg) = suggestions.pop().unwrap();

View file

@ -602,24 +602,13 @@ impl<T> Trait<T> for X {
} else { } else {
return false; return false;
}; };
let Some(def_id) = def_id.as_local() else {
return false;
};
// First look in the `where` clause, as this might be // First look in the `where` clause, as this might be
// `fn foo<T>(x: T) where T: Trait`. // `fn foo<T>(x: T) where T: Trait`.
for predicate in hir_generics.predicates { for pred in hir_generics.bounds_for_param(def_id) {
if let hir::WherePredicate::BoundPredicate(pred) = predicate {
if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) =
pred.bounded_ty.kind
{
if path.res.opt_def_id() == Some(def_id) {
// This predicate is binding type param `A` in `<A as T>::Foo` to
// something, potentially `T`.
} else {
continue;
}
} else {
continue;
}
if self.constrain_generic_bound_associated_type_structured_suggestion( if self.constrain_generic_bound_associated_type_structured_suggestion(
diag, diag,
&trait_ref, &trait_ref,
@ -634,24 +623,6 @@ impl<T> Trait<T> for X {
} }
} }
} }
for param in hir_generics.params {
if self.hir().opt_local_def_id(param.hir_id).map(|id| id.to_def_id())
== Some(def_id)
{
// This is type param `A` in `<A as T>::Foo`.
return self.constrain_generic_bound_associated_type_structured_suggestion(
diag,
&trait_ref,
param.bounds,
&assoc,
assoc_substs,
ty,
msg,
false,
);
}
}
}
} }
false false
} }

View file

@ -1585,11 +1585,6 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
} }
fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) { fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
for param in generics.params {
for bound in param.bounds {
self.check_generic_bound(bound);
}
}
for predicate in generics.predicates { for predicate in generics.predicates {
match predicate { match predicate {
hir::WherePredicate::BoundPredicate(bound_pred) => { hir::WherePredicate::BoundPredicate(bound_pred) => {

View file

@ -1328,13 +1328,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
match param.kind { match param.kind {
GenericParamKind::Lifetime { .. } => {} GenericParamKind::Lifetime { .. } => {}
GenericParamKind::Type { ref default, .. } => { GenericParamKind::Type { ref default, .. } => {
walk_list!(this, visit_param_bound, param.bounds);
if let Some(ref ty) = default { if let Some(ref ty) = default {
this.visit_ty(&ty); this.visit_ty(&ty);
} }
} }
GenericParamKind::Const { ref ty, default } => { GenericParamKind::Const { ref ty, default } => {
walk_list!(this, visit_param_bound, param.bounds);
this.visit_ty(&ty); this.visit_ty(&ty);
if let Some(default) = default { if let Some(default) = default {
this.visit_body(this.tcx.hir().body(default.body)); this.visit_body(this.tcx.hir().body(default.body));
@ -1393,6 +1391,32 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
}) => { }) => {
this.visit_lifetime(lifetime); this.visit_lifetime(lifetime);
walk_list!(this, visit_param_bound, bounds); walk_list!(this, visit_param_bound, bounds);
if lifetime.name != hir::LifetimeName::Static {
for bound in bounds {
let hir::GenericBound::Outlives(ref lt) = bound else {
continue;
};
if lt.name != hir::LifetimeName::Static {
continue;
}
this.insert_lifetime(lt, Region::Static);
this.tcx
.sess
.struct_span_warn(
lifetime.span,
&format!(
"unnecessary lifetime parameter `{}`",
lifetime.name.ident(),
),
)
.help(&format!(
"you can use the `'static` lifetime directly, in place of `{}`",
lifetime.name.ident(),
))
.emit();
}
}
} }
&hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
ref lhs_ty, ref lhs_ty,
@ -1714,8 +1738,6 @@ fn object_lifetime_defaults_for_item<'tcx>(
GenericParamKind::Type { .. } => { GenericParamKind::Type { .. } => {
let mut set = Set1::Empty; let mut set = Set1::Empty;
add_bounds(&mut set, &param.bounds);
let param_def_id = tcx.hir().local_def_id(param.hir_id); let param_def_id = tcx.hir().local_def_id(param.hir_id);
for predicate in generics.predicates { for predicate in generics.predicates {
// Look for `type: ...` where clauses. // Look for `type: ...` where clauses.
@ -3124,50 +3146,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
// It is a soft error to shadow a lifetime within a parent scope. // It is a soft error to shadow a lifetime within a parent scope.
self.check_lifetime_param_for_shadowing(old_scope, &lifetime_i); self.check_lifetime_param_for_shadowing(old_scope, &lifetime_i);
for bound in lifetime_i.bounds {
match bound {
hir::GenericBound::Outlives(ref lt) => match lt.name {
hir::LifetimeName::Underscore => {
self.tcx.sess.delay_span_bug(
lt.span,
"use of `'_` in illegal place, but not caught by lowering",
);
}
hir::LifetimeName::Static => {
self.insert_lifetime(lt, Region::Static);
self.tcx
.sess
.struct_span_warn(
lifetime_i.span.to(lt.span),
&format!(
"unnecessary lifetime parameter `{}`",
lifetime_i.name.ident(),
),
)
.help(&format!(
"you can use the `'static` lifetime directly, in place of `{}`",
lifetime_i.name.ident(),
))
.emit();
}
hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit => {
self.resolve_lifetime_ref(lt);
}
hir::LifetimeName::ImplicitObjectLifetimeDefault => {
self.tcx.sess.delay_span_bug(
lt.span,
"lowering generated `ImplicitObjectLifetimeDefault` \
outside of an object type",
);
}
hir::LifetimeName::Error => {
// No need to do anything, error already reported.
}
},
_ => bug!(),
}
}
} }
} }
@ -3326,18 +3304,6 @@ fn insert_late_bound_lifetimes(
// ignore binders here and scrape up all names we see. // ignore binders here and scrape up all names we see.
let mut appears_in_where_clause = AllCollector::default(); let mut appears_in_where_clause = AllCollector::default();
appears_in_where_clause.visit_generics(generics); appears_in_where_clause.visit_generics(generics);
for param in generics.params {
if let hir::GenericParamKind::Lifetime { .. } = param.kind {
if !param.bounds.is_empty() {
// `'a: 'b` means both `'a` and `'b` are referenced
appears_in_where_clause
.regions
.insert(hir::LifetimeName::Param(param.name.normalize_to_macros_2_0()));
}
}
}
debug!(?appears_in_where_clause.regions); debug!(?appears_in_where_clause.regions);
// Late bound regions are those that: // Late bound regions are those that:

View file

@ -1267,13 +1267,11 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
match param.kind { match param.kind {
hir::GenericParamKind::Lifetime { .. } => {} hir::GenericParamKind::Lifetime { .. } => {}
hir::GenericParamKind::Type { ref default, .. } => { hir::GenericParamKind::Type { ref default, .. } => {
self.process_bounds(param.bounds);
if let Some(ref ty) = default { if let Some(ref ty) = default {
self.visit_ty(ty); self.visit_ty(ty);
} }
} }
hir::GenericParamKind::Const { ref ty, ref default } => { hir::GenericParamKind::Const { ref ty, ref default } => {
self.process_bounds(param.bounds);
self.visit_ty(ty); self.visit_ty(ty);
if let Some(default) = default { if let Some(default) = default {
self.visit_anon_const(default); self.visit_anon_const(default);

View file

@ -630,31 +630,6 @@ impl<'hir> Sig for hir::Generics<'hir> {
param_text.push_str(&id_to_string(&scx.tcx.hir(), default.hir_id)); param_text.push_str(&id_to_string(&scx.tcx.hir(), default.hir_id));
} }
} }
if !param.bounds.is_empty() {
param_text.push_str(": ");
match param.kind {
hir::GenericParamKind::Lifetime { .. } => {
let bounds = param
.bounds
.iter()
.map(|bound| match bound {
hir::GenericBound::Outlives(lt) => lt.name.ident().to_string(),
_ => panic!(),
})
.collect::<Vec<_>>()
.join(" + ");
param_text.push_str(&bounds);
// FIXME add lifetime bounds refs.
}
hir::GenericParamKind::Type { .. } => {
param_text.push_str(&bounds_to_string(param.bounds));
// FIXME descend properly into bounds.
}
hir::GenericParamKind::Const { .. } => {
// Const generics cannot contain bounds.
}
}
}
text.push_str(&param_text); text.push_str(&param_text);
text.push(','); text.push(',');
} }

View file

@ -2420,25 +2420,14 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
let sized_trait = self.tcx.lang_items().sized_trait(); let sized_trait = self.tcx.lang_items().sized_trait();
debug!("maybe_suggest_unsized_generics: generics.params={:?}", generics.params); debug!("maybe_suggest_unsized_generics: generics.params={:?}", generics.params);
debug!("maybe_suggest_unsized_generics: generics.predicates={:?}", generics.predicates); debug!("maybe_suggest_unsized_generics: generics.predicates={:?}", generics.predicates);
let param = generics.params.iter().filter(|param| param.span == span).find(|param| { let Some(param) = generics.params.iter().find(|param| param.span == span) else {
// Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
// `Sized` bound is there intentionally and we don't need to suggest relaxing it.
param
.bounds
.iter()
.all(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) != sized_trait)
});
let Some(param) = param else {
return; return;
}; };
let param_def_id = self.tcx.hir().local_def_id(param.hir_id).to_def_id(); let param_def_id = self.tcx.hir().local_def_id(param.hir_id);
let preds = generics.predicates.iter(); // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
let explicitly_sized = preds // `Sized` bound is there intentionally and we don't need to suggest relaxing it.
.filter_map(|pred| match pred { let explicitly_sized = generics
hir::WherePredicate::BoundPredicate(bp) => Some(bp), .bounds_for_param(param_def_id)
_ => None,
})
.filter(|bp| bp.is_param_bound(param_def_id))
.flat_map(|bp| bp.bounds) .flat_map(|bp| bp.bounds)
.any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait); .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
if explicitly_sized { if explicitly_sized {
@ -2461,9 +2450,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
_ => {} _ => {}
}; };
// Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`. // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
let (span, separator) = match param.bounds { let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param_def_id)
[] => (span.shrink_to_hi(), ":"), {
[.., bound] => (bound.span().shrink_to_hi(), " +"), (s, " +")
} else {
(span.shrink_to_hi(), ":")
}; };
err.span_suggestion_verbose( err.span_suggestion_verbose(
span, span,

View file

@ -320,7 +320,7 @@ pub trait InferCtxtExt<'tcx> {
fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) { fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
( (
generics.tail_span_for_predicate_suggestion(), generics.tail_span_for_predicate_suggestion(),
format!("{} {}", if !generics.predicates.is_empty() { "," } else { " where" }, pred,), format!("{} {}", if generics.has_where_clause { "," } else { " where" }, pred,),
) )
} }
@ -392,21 +392,10 @@ fn suggest_restriction<'tcx>(
let pred = trait_pred.to_predicate(tcx).to_string(); let pred = trait_pred.to_predicate(tcx).to_string();
let pred = pred.replace(&impl_trait_str, &type_param_name); let pred = pred.replace(&impl_trait_str, &type_param_name);
let mut sugg = vec![ let mut sugg = vec![
// Find the last of the generic parameters contained within the span of if let Some(span) = generics.span_for_param_suggestion() {
// the generics (span, format!(", {}", type_param))
match generics } else {
.params (generics.span, format!("<{}>", type_param))
.iter()
.map(|p| p.bounds_span_for_suggestions().unwrap_or(p.span.shrink_to_hi()))
.filter(|&span| generics.span.contains(span) && span.can_be_used_for_suggestions())
.max_by_key(|span| span.hi())
{
// `fn foo(t: impl Trait)`
// ^ suggest `<T: Trait>` here
None => (generics.span, format!("<{}>", type_param)),
// `fn foo<A>(t: impl Trait)`
// ^^^ suggest `<A, T: Trait>` here
Some(span) => (span, format!(", {}", type_param)),
}, },
// `fn foo(t: impl Trait)` // `fn foo(t: impl Trait)`
// ^ suggest `where <T as Trait>::A: Bound` // ^ suggest `where <T as Trait>::A: Bound`

View file

@ -924,17 +924,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let self_ty_def_id = tcx.hir().local_def_id(self_ty).to_def_id(); let self_ty_def_id = tcx.hir().local_def_id(self_ty).to_def_id();
for clause in where_clause { for clause in where_clause {
if let hir::WherePredicate::BoundPredicate(pred) = clause { if let hir::WherePredicate::BoundPredicate(pred) = clause {
match pred.bounded_ty.kind { if pred.is_param_bound(self_ty_def_id) {
hir::TyKind::Path(hir::QPath::Resolved(_, path)) => match path.res {
Res::Def(DefKind::TyParam, def_id) if def_id == self_ty_def_id => {}
_ => continue,
},
_ => continue,
}
search_bounds(pred.bounds); search_bounds(pred.bounds);
} }
} }
} }
}
let sized_def_id = tcx.lang_items().require(LangItem::Sized); let sized_def_id = tcx.lang_items().require(LangItem::Sized);
match (&sized_def_id, unbound) { match (&sized_def_id, unbound) {
@ -2389,7 +2384,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
bf.unsafety, bf.unsafety,
bf.abi, bf.abi,
bf.decl, bf.decl,
&hir::Generics::empty(),
None, None,
Some(ast_ty), Some(ast_ty),
)) ))
@ -2551,8 +2545,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
unsafety: hir::Unsafety, unsafety: hir::Unsafety,
abi: abi::Abi, abi: abi::Abi,
decl: &hir::FnDecl<'_>, decl: &hir::FnDecl<'_>,
generics: &hir::Generics<'_>, generics: Option<&hir::Generics<'_>>,
ident_span: Option<Span>,
hir_ty: Option<&hir::Ty<'_>>, hir_ty: Option<&hir::Ty<'_>>,
) -> ty::PolyFnSig<'tcx> { ) -> ty::PolyFnSig<'tcx> {
debug!("ty_of_fn"); debug!("ty_of_fn");
@ -2565,7 +2558,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let mut visitor = HirPlaceholderCollector::default(); let mut visitor = HirPlaceholderCollector::default();
let mut infer_replacements = vec![]; let mut infer_replacements = vec![];
if let Some(generics) = generics {
walk_generics(&mut visitor, generics); walk_generics(&mut visitor, generics);
}
let input_tys: Vec<_> = decl let input_tys: Vec<_> = decl
.inputs .inputs
@ -2617,8 +2612,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let mut diag = crate::collect::placeholder_type_error_diag( let mut diag = crate::collect::placeholder_type_error_diag(
tcx, tcx,
ident_span.map(|sp| sp.shrink_to_hi()), generics,
generics.params,
visitor.0, visitor.0,
infer_replacements.iter().map(|(s, _)| *s).collect(), infer_replacements.iter().map(|(s, _)| *s).collect(),
true, true,

View file

@ -804,7 +804,8 @@ fn compare_synthetic_generics<'tcx>(
iter::zip(impl_m_type_params, trait_m_type_params) iter::zip(impl_m_type_params, trait_m_type_params)
{ {
if impl_synthetic != trait_synthetic { if impl_synthetic != trait_synthetic {
let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id.expect_local()); let impl_def_id = impl_def_id.expect_local();
let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id);
let impl_span = tcx.hir().span(impl_hir_id); let impl_span = tcx.hir().span(impl_hir_id);
let trait_span = tcx.def_span(trait_def_id); let trait_span = tcx.def_span(trait_def_id);
let mut err = struct_span_err!( let mut err = struct_span_err!(
@ -868,14 +869,14 @@ fn compare_synthetic_generics<'tcx>(
hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs, hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs,
_ => unreachable!(), _ => unreachable!(),
}; };
struct Visitor(Option<Span>, hir::def_id::DefId); struct Visitor(Option<Span>, hir::def_id::LocalDefId);
impl<'v> intravisit::Visitor<'v> for Visitor { impl<'v> intravisit::Visitor<'v> for Visitor {
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
intravisit::walk_ty(self, ty); intravisit::walk_ty(self, ty);
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) =
ty.kind ty.kind
&& let Res::Def(DefKind::TyParam, def_id) = path.res && let Res::Def(DefKind::TyParam, def_id) = path.res
&& def_id == self.1 && def_id == self.1.to_def_id()
{ {
self.0 = Some(ty.span); self.0 = Some(ty.span);
} }
@ -887,17 +888,7 @@ fn compare_synthetic_generics<'tcx>(
} }
let span = visitor.0?; let span = visitor.0?;
let bounds = let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
impl_m.generics.params.iter().find_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => None,
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
if param.hir_id == impl_hir_id {
Some(&param.bounds)
} else {
None
}
}
})?;
let bounds = bounds.first()?.span().to(bounds.last()?.span()); let bounds = bounds.first()?.span().to(bounds.last()?.span());
let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?; let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?;

View file

@ -605,7 +605,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.. ..
})) = fn_node else { return }; })) = fn_node else { return };
let Some(expected_generic_param) = params.get(expected_ty_as_param.index as usize) else { return }; if params.get(expected_ty_as_param.index as usize).is_none() {
return;
};
// get all where BoundPredicates here, because they are used in to cases below // get all where BoundPredicates here, because they are used in to cases below
let where_predicates = predicates let where_predicates = predicates
@ -639,10 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
where_predicates.iter().flatten().flat_map(|bounds| bounds.iter()); where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
// extract all bounds from the source code using their spans // extract all bounds from the source code using their spans
let all_matching_bounds_strs = expected_generic_param let all_matching_bounds_strs = predicates_from_where
.bounds
.iter()
.chain(predicates_from_where)
.filter_map(|bound| match bound { .filter_map(|bound| match bound {
GenericBound::Trait(_, _) => { GenericBound::Trait(_, _) => {
self.tcx.sess.source_map().span_to_snippet(bound.span()).ok() self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()

View file

@ -1868,37 +1868,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// instead we suggest `T: Foo + Bar` in that case. // instead we suggest `T: Foo + Bar` in that case.
match hir.get(id) { match hir.get(id) {
Node::GenericParam(param) => { Node::GenericParam(param) => {
let mut impl_trait = false; let impl_trait = matches!(
let has_bounds = param.kind,
if let hir::GenericParamKind::Type { synthetic: true, .. } = hir::GenericParamKind::Type { synthetic: true, .. },
&param.kind );
let ast_generics = hir.get_generics(id.owner).unwrap();
let (sp, has_bounds) = if let Some(span) =
ast_generics.bounds_span_for_suggestions(def_id)
{ {
// We've found `fn foo(x: impl Trait)` instead of (span, true)
// `fn foo<T>(x: T)`. We want to suggest the correct
// `fn foo(x: impl Trait + TraitBound)` instead of
// `fn foo<T: TraitBound>(x: T)`. (#63706)
impl_trait = true;
param.bounds.get(1)
} else { } else {
param.bounds.get(0) (hir.span(id).shrink_to_hi(), false)
}; };
let sp = hir.span(id); let trait_def_ids: FxHashSet<DefId> = ast_generics
let sp = if let Some(first_bound) = has_bounds { .bounds_for_param(def_id)
sp.until(first_bound.span()) .flat_map(|bp| bp.bounds.iter())
} else if let Some(colon_sp) =
// If the generic param is declared with a colon but without bounds:
// fn foo<T:>(t: T) { ... }
param.colon_span_for_suggestions(
self.inh.tcx.sess.source_map(),
)
{
sp.to(colon_sp)
} else {
sp
};
let trait_def_ids: FxHashSet<DefId> = param
.bounds
.iter()
.filter_map(|bound| bound.trait_ref()?.trait_def_id()) .filter_map(|bound| bound.trait_ref()?.trait_def_id())
.collect(); .collect();
if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) { if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) {
@ -1910,11 +1894,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)), )),
candidates.iter().map(|t| { candidates.iter().map(|t| {
format!( format!(
"{}{} {}{}", "{} {}",
param.name.ident(), if has_bounds || impl_trait { " +" } else { ":" },
if impl_trait { " +" } else { ":" },
self.tcx.def_path_str(t.def_id), self.tcx.def_path_str(t.def_id),
if has_bounds.is_some() { " + " } else { "" },
) )
}), }),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,

View file

@ -373,16 +373,7 @@ fn typeck_with_fallback<'tcx>(
let (fcx, wf_tys) = if let Some(hir::FnSig { header, decl, .. }) = fn_sig { let (fcx, wf_tys) = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() {
let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
<dyn AstConv<'_>>::ty_of_fn( <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None)
&fcx,
id,
header.unsafety,
header.abi,
decl,
&hir::Generics::empty(),
None,
None,
)
} else { } else {
tcx.fn_sig(def_id) tcx.fn_sig(def_id)
}; };

View file

@ -1755,8 +1755,7 @@ fn check_variances_for_type_defn<'tcx>(
match param.name { match param.name {
hir::ParamName::Error => {} hir::ParamName::Error => {}
_ => { _ => {
let has_explicit_bounds = let has_explicit_bounds = explicitly_bounded_params.contains(&parameter);
!param.bounds.is_empty() || explicitly_bounded_params.contains(&parameter);
report_bivariance(tcx, param, has_explicit_bounds); report_bivariance(tcx, param, has_explicit_bounds);
} }
} }

View file

@ -1,4 +1,3 @@
// ignore-tidy-filelength
//! "Collection" is the process of determining the type and other external //! "Collection" is the process of determining the type and other external
//! details of each item in Rust. Collection is specifically concerned //! details of each item in Rust. Collection is specifically concerned
//! with *inter-procedural* things -- for example, for a function //! with *inter-procedural* things -- for example, for a function
@ -149,8 +148,7 @@ struct CollectItemTypesVisitor<'tcx> {
/// all already existing generic type parameters to avoid suggesting a name that is already in use. /// all already existing generic type parameters to avoid suggesting a name that is already in use.
crate fn placeholder_type_error<'tcx>( crate fn placeholder_type_error<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
span: Option<Span>, generics: Option<&hir::Generics<'_>>,
generics: &[hir::GenericParam<'_>],
placeholder_types: Vec<Span>, placeholder_types: Vec<Span>,
suggest: bool, suggest: bool,
hir_ty: Option<&hir::Ty<'_>>, hir_ty: Option<&hir::Ty<'_>>,
@ -160,23 +158,13 @@ crate fn placeholder_type_error<'tcx>(
return; return;
} }
placeholder_type_error_diag( placeholder_type_error_diag(tcx, generics, placeholder_types, vec![], suggest, hir_ty, kind)
tcx,
span,
generics,
placeholder_types,
vec![],
suggest,
hir_ty,
kind,
)
.emit(); .emit();
} }
crate fn placeholder_type_error_diag<'tcx>( crate fn placeholder_type_error_diag<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
span: Option<Span>, generics: Option<&hir::Generics<'_>>,
generics: &[hir::GenericParam<'_>],
placeholder_types: Vec<Span>, placeholder_types: Vec<Span>,
additional_spans: Vec<Span>, additional_spans: Vec<Span>,
suggest: bool, suggest: bool,
@ -187,26 +175,24 @@ crate fn placeholder_type_error_diag<'tcx>(
return bad_placeholder(tcx, additional_spans, kind); return bad_placeholder(tcx, additional_spans, kind);
} }
let type_name = generics.next_type_param_name(None); let params = generics.map(|g| g.params).unwrap_or_default();
let type_name = params.next_type_param_name(None);
let mut sugg: Vec<_> = let mut sugg: Vec<_> =
placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect(); placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect();
if generics.is_empty() { if let Some(generics) = generics {
if let Some(span) = span { if let Some(arg) = params.iter().find(|arg| {
sugg.push((span, format!("<{}>", type_name))); matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. }))
} }) {
} else if let Some(arg) = generics
.iter()
.find(|arg| matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. })))
{
// Account for `_` already present in cases like `struct S<_>(_);` and suggest // Account for `_` already present in cases like `struct S<_>(_);` and suggest
// `struct S<T>(T);` instead of `struct S<_, T>(T);`. // `struct S<T>(T);` instead of `struct S<_, T>(T);`.
sugg.push((arg.span, (*type_name).to_string())); sugg.push((arg.span, (*type_name).to_string()));
} else { } else if let Some(span) = generics.span_for_param_suggestion() {
let last = generics.iter().last().unwrap();
// Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`. // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
let span = last.bounds_span_for_suggestions().unwrap_or(last.span.shrink_to_hi());
sugg.push((span, format!(", {}", type_name))); sugg.push((span, format!(", {}", type_name)));
} else {
sugg.push((generics.span, format!("<{}>", type_name)));
}
} }
let mut err = let mut err =
@ -270,15 +256,7 @@ fn reject_placeholder_type_signatures_in_item<'tcx>(
let mut visitor = HirPlaceholderCollector::default(); let mut visitor = HirPlaceholderCollector::default();
visitor.visit_item(item); visitor.visit_item(item);
placeholder_type_error( placeholder_type_error(tcx, Some(generics), visitor.0, suggest, None, item.kind.descr());
tcx,
Some(generics.span),
generics.params,
visitor.0,
suggest,
None,
item.kind.descr(),
);
} }
impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
@ -680,26 +658,8 @@ impl<'tcx> ItemCtxt<'tcx> {
only_self_bounds: OnlySelfBounds, only_self_bounds: OnlySelfBounds,
assoc_name: Option<Ident>, assoc_name: Option<Ident>,
) -> Vec<(ty::Predicate<'tcx>, Span)> { ) -> Vec<(ty::Predicate<'tcx>, Span)> {
let from_ty_params = ast_generics
.params
.iter()
.filter_map(|param| match param.kind {
GenericParamKind::Type { .. } | GenericParamKind::Const { .. }
if param.hir_id == param_id =>
{
Some(&param.bounds)
}
_ => None,
})
.flat_map(|bounds| bounds.iter())
.filter(|b| match assoc_name {
Some(assoc_name) => self.bound_defines_assoc_item(b, assoc_name),
None => true,
})
.flat_map(|b| predicates_from_bound(self, ty, b, ty::List::empty()));
let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id(); let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id();
let from_where_clauses = ast_generics ast_generics
.predicates .predicates
.iter() .iter()
.filter_map(|wp| match *wp { .filter_map(|wp| match *wp {
@ -724,9 +684,8 @@ impl<'tcx> ItemCtxt<'tcx> {
}) })
.filter_map(move |b| bt.map(|bt| (bt, b, bvars))) .filter_map(move |b| bt.map(|bt| (bt, b, bvars)))
}) })
.flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars)); .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars))
.collect()
from_ty_params.chain(from_where_clauses).collect()
} }
fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool { fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool {
@ -772,7 +731,6 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
placeholder_type_error( placeholder_type_error(
tcx, tcx,
None, None,
&[],
visitor.0, visitor.0,
false, false,
None, None,
@ -852,15 +810,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
if let hir::TyKind::TraitObject(..) = ty.kind { if let hir::TyKind::TraitObject(..) = ty.kind {
let mut visitor = HirPlaceholderCollector::default(); let mut visitor = HirPlaceholderCollector::default();
visitor.visit_item(it); visitor.visit_item(it);
placeholder_type_error( placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr());
tcx,
None,
&[],
visitor.0,
false,
None,
it.kind.descr(),
);
} }
} }
_ => (), _ => (),
@ -888,7 +838,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
// Account for `const C: _;`. // Account for `const C: _;`.
let mut visitor = HirPlaceholderCollector::default(); let mut visitor = HirPlaceholderCollector::default();
visitor.visit_trait_item(trait_item); visitor.visit_trait_item(trait_item);
placeholder_type_error(tcx, None, &[], visitor.0, false, None, "constant"); placeholder_type_error(tcx, None, visitor.0, false, None, "constant");
} }
hir::TraitItemKind::Type(_, Some(_)) => { hir::TraitItemKind::Type(_, Some(_)) => {
@ -897,7 +847,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
// Account for `type T = _;`. // Account for `type T = _;`.
let mut visitor = HirPlaceholderCollector::default(); let mut visitor = HirPlaceholderCollector::default();
visitor.visit_trait_item(trait_item); visitor.visit_trait_item(trait_item);
placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type"); placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
} }
hir::TraitItemKind::Type(_, None) => { hir::TraitItemKind::Type(_, None) => {
@ -907,7 +857,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
let mut visitor = HirPlaceholderCollector::default(); let mut visitor = HirPlaceholderCollector::default();
visitor.visit_trait_item(trait_item); visitor.visit_trait_item(trait_item);
placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type"); placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
} }
}; };
@ -929,7 +879,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
let mut visitor = HirPlaceholderCollector::default(); let mut visitor = HirPlaceholderCollector::default();
visitor.visit_impl_item(impl_item); visitor.visit_impl_item(impl_item);
placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type"); placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
} }
hir::ImplItemKind::Const(..) => {} hir::ImplItemKind::Const(..) => {}
} }
@ -1892,15 +1842,14 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
match tcx.hir().get(hir_id) { match tcx.hir().get(hir_id) {
TraitItem(hir::TraitItem { TraitItem(hir::TraitItem {
kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)), kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)),
ident,
generics, generics,
.. ..
}) })
| Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), ident, .. }) => { | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => {
infer_return_ty_for_fn_sig(tcx, sig, *ident, generics, def_id, &icx) infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
} }
ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), ident, generics, .. }) => { ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
// Do not try to inference the return type for a impl method coming from a trait // Do not try to inference the return type for a impl method coming from a trait
if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) =
tcx.hir().get(tcx.hir().get_parent_node(hir_id)) tcx.hir().get(tcx.hir().get_parent_node(hir_id))
@ -1912,18 +1861,16 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
sig.header.unsafety, sig.header.unsafety,
sig.header.abi, sig.header.abi,
sig.decl, sig.decl,
generics, Some(generics),
Some(ident.span),
None, None,
) )
} else { } else {
infer_return_ty_for_fn_sig(tcx, sig, *ident, generics, def_id, &icx) infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
} }
} }
TraitItem(hir::TraitItem { TraitItem(hir::TraitItem {
kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _), kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _),
ident,
generics, generics,
.. ..
}) => <dyn AstConv<'_>>::ty_of_fn( }) => <dyn AstConv<'_>>::ty_of_fn(
@ -1932,16 +1879,13 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
header.unsafety, header.unsafety,
header.abi, header.abi,
decl, decl,
generics, Some(generics),
Some(ident.span),
None, None,
), ),
ForeignItem(&hir::ForeignItem { ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => {
kind: ForeignItemKind::Fn(fn_decl, _, _), ident, ..
}) => {
let abi = tcx.hir().get_foreign_abi(hir_id); let abi = tcx.hir().get_foreign_abi(hir_id);
compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi, ident) compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi)
} }
Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => { Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => {
@ -1982,7 +1926,6 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
fn infer_return_ty_for_fn_sig<'tcx>( fn infer_return_ty_for_fn_sig<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
sig: &hir::FnSig<'_>, sig: &hir::FnSig<'_>,
ident: Ident,
generics: &hir::Generics<'_>, generics: &hir::Generics<'_>,
def_id: LocalDefId, def_id: LocalDefId,
icx: &ItemCtxt<'tcx>, icx: &ItemCtxt<'tcx>,
@ -2037,8 +1980,7 @@ fn infer_return_ty_for_fn_sig<'tcx>(
sig.header.unsafety, sig.header.unsafety,
sig.header.abi, sig.header.abi,
sig.decl, sig.decl,
generics, Some(generics),
Some(ident.span),
None, None,
), ),
} }
@ -2301,29 +2243,9 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
// Collect the region predicates that were declared inline as // Collect the region predicates that were declared inline as
// well. In the case of parameters declared on a fn or method, we // well. In the case of parameters declared on a fn or method, we
// have to be careful to only iterate over early-bound regions. // have to be careful to only iterate over early-bound regions.
let mut index = parent_count + has_own_self as u32; let mut index = parent_count
for param in early_bound_lifetimes_from_generics(tcx, hir_id.owner, ast_generics) { + has_own_self as u32
let region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { + early_bound_lifetimes_from_generics(tcx, hir_id.owner, ast_generics).count() as u32;
def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
index,
name: param.name.ident().name,
}));
index += 1;
match param.kind {
GenericParamKind::Lifetime { .. } => {
param.bounds.iter().for_each(|bound| match bound {
hir::GenericBound::Outlives(lt) => {
let bound = <dyn AstConv<'_>>::ast_region_to_region(&icx, lt, None);
let outlives = ty::Binder::dummy(ty::OutlivesPredicate(region, bound));
predicates.insert((outlives.to_predicate(tcx), lt.span));
}
_ => bug!(),
});
}
_ => bug!(),
}
}
// Collect the predicates that were written inline by the user on each // Collect the predicates that were written inline by the user on each
// type parameter (e.g., `<T: Foo>`). // type parameter (e.g., `<T: Foo>`).
@ -2336,12 +2258,12 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
let param_ty = ty::ParamTy::new(index, name).to_ty(tcx); let param_ty = ty::ParamTy::new(index, name).to_ty(tcx);
index += 1; index += 1;
let mut bounds = <dyn AstConv<'_>>::compute_bounds(&icx, param_ty, param.bounds); let mut bounds = Bounds::default();
// Params are implicitly sized unless a `?Sized` bound is found // Params are implicitly sized unless a `?Sized` bound is found
<dyn AstConv<'_>>::add_implicitly_sized( <dyn AstConv<'_>>::add_implicitly_sized(
&icx, &icx,
&mut bounds, &mut bounds,
param.bounds, &[],
Some((param.hir_id, ast_generics.predicates)), Some((param.hir_id, ast_generics.predicates)),
param.span, param.span,
); );
@ -2349,7 +2271,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
} }
GenericParamKind::Const { .. } => { GenericParamKind::Const { .. } => {
// Bounds on const parameters are currently not possible. // Bounds on const parameters are currently not possible.
debug_assert!(param.bounds.is_empty());
index += 1; index += 1;
} }
} }
@ -2614,7 +2535,6 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
def_id: DefId, def_id: DefId,
decl: &'tcx hir::FnDecl<'tcx>, decl: &'tcx hir::FnDecl<'tcx>,
abi: abi::Abi, abi: abi::Abi,
ident: Ident,
) -> ty::PolyFnSig<'tcx> { ) -> ty::PolyFnSig<'tcx> {
let unsafety = if abi == abi::Abi::RustIntrinsic { let unsafety = if abi == abi::Abi::RustIntrinsic {
intrinsic_operation_unsafety(tcx.item_name(def_id)) intrinsic_operation_unsafety(tcx.item_name(def_id))
@ -2628,8 +2548,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
unsafety, unsafety,
abi, abi,
decl, decl,
&hir::Generics::empty(), None,
Some(ident.span),
None, None,
); );

View file

@ -49,8 +49,6 @@ def check_generic_param(param):
ty = param["kind"]["type"] ty = param["kind"]["type"]
if ty["default"]: if ty["default"]:
check_type(ty["default"]) check_type(ty["default"])
for bound in ty["bounds"]:
check_generic_bound(bound)
elif "const" in param["kind"]: elif "const" in param["kind"]:
check_type(param["kind"]["const"]) check_type(param["kind"]["const"])

View file

@ -238,9 +238,12 @@ impl Clean<Option<Lifetime>> for ty::Region<'_> {
} }
} }
impl Clean<WherePredicate> for hir::WherePredicate<'_> { impl Clean<Option<WherePredicate>> for hir::WherePredicate<'_> {
fn clean(&self, cx: &mut DocContext<'_>) -> WherePredicate { fn clean(&self, cx: &mut DocContext<'_>) -> Option<WherePredicate> {
match *self { if !self.in_where_clause() {
return None;
}
Some(match *self {
hir::WherePredicate::BoundPredicate(ref wbp) => { hir::WherePredicate::BoundPredicate(ref wbp) => {
let bound_params = wbp let bound_params = wbp
.bound_generic_params .bound_generic_params
@ -250,11 +253,7 @@ impl Clean<WherePredicate> for hir::WherePredicate<'_> {
// Higher-ranked lifetimes can't have bounds. // Higher-ranked lifetimes can't have bounds.
assert_matches!( assert_matches!(
param, param,
hir::GenericParam { hir::GenericParam { kind: hir::GenericParamKind::Lifetime { .. }, .. }
kind: hir::GenericParamKind::Lifetime { .. },
bounds: [],
..
}
); );
Lifetime(param.name.ident().name) Lifetime(param.name.ident().name)
}) })
@ -275,7 +274,7 @@ impl Clean<WherePredicate> for hir::WherePredicate<'_> {
lhs: wrp.lhs_ty.clean(cx), lhs: wrp.lhs_ty.clean(cx),
rhs: wrp.rhs_ty.clean(cx).into(), rhs: wrp.rhs_ty.clean(cx).into(),
}, },
} })
} }
} }
@ -456,33 +455,65 @@ impl Clean<GenericParamDef> for ty::GenericParamDef {
} }
} }
impl Clean<GenericParamDef> for hir::GenericParam<'_> { fn clean_generic_param(
fn clean(&self, cx: &mut DocContext<'_>) -> GenericParamDef { cx: &mut DocContext<'_>,
let (name, kind) = match self.kind { generics: Option<&hir::Generics<'_>>,
param: &hir::GenericParam<'_>,
) -> GenericParamDef {
let (name, kind) = match param.kind {
hir::GenericParamKind::Lifetime { .. } => { hir::GenericParamKind::Lifetime { .. } => {
let outlives = self let outlives = if let Some(generics) = generics {
.bounds generics
.predicates
.iter() .iter()
.flat_map(|pred| {
match pred {
hir::WherePredicate::RegionPredicate(rp)
if rp.lifetime.name == hir::LifetimeName::Param(param.name)
&& !rp.in_where_clause =>
{
rp.bounds
}
_ => &[],
}
.iter()
})
.map(|bound| match bound { .map(|bound| match bound {
hir::GenericBound::Outlives(lt) => lt.clean(cx), hir::GenericBound::Outlives(lt) => lt.clean(cx),
_ => panic!(), _ => panic!(),
}) })
.collect(); .collect()
(self.name.ident().name, GenericParamDefKind::Lifetime { outlives }) } else {
Vec::new()
};
(param.name.ident().name, GenericParamDefKind::Lifetime { outlives })
} }
hir::GenericParamKind::Type { ref default, synthetic } => ( hir::GenericParamKind::Type { ref default, synthetic } => {
self.name.ident().name, let did = cx.tcx.hir().local_def_id(param.hir_id);
let bounds = if let Some(generics) = generics {
generics
.bounds_for_param(did)
.filter(|bp| !bp.in_where_clause)
.flat_map(|bp| bp.bounds)
.filter_map(|x| x.clean(cx))
.collect()
} else {
Vec::new()
};
(
param.name.ident().name,
GenericParamDefKind::Type { GenericParamDefKind::Type {
did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), did: did.to_def_id(),
bounds: self.bounds.iter().filter_map(|x| x.clean(cx)).collect(), bounds,
default: default.map(|t| t.clean(cx)).map(Box::new), default: default.map(|t| t.clean(cx)).map(Box::new),
synthetic, synthetic,
}, },
), )
}
hir::GenericParamKind::Const { ref ty, default } => ( hir::GenericParamKind::Const { ref ty, default } => (
self.name.ident().name, param.name.ident().name,
GenericParamDefKind::Const { GenericParamDefKind::Const {
did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), did: cx.tcx.hir().local_def_id(param.hir_id).to_def_id(),
ty: Box::new(ty.clean(cx)), ty: Box::new(ty.clean(cx)),
default: default.map(|ct| { default: default.map(|ct| {
let def_id = cx.tcx.hir().local_def_id(ct.hir_id); let def_id = cx.tcx.hir().local_def_id(ct.hir_id);
@ -493,7 +524,6 @@ impl Clean<GenericParamDef> for hir::GenericParam<'_> {
}; };
GenericParamDef { name, kind } GenericParamDef { name, kind }
}
} }
impl Clean<Generics> for hir::Generics<'_> { impl Clean<Generics> for hir::Generics<'_> {
@ -524,7 +554,7 @@ impl Clean<Generics> for hir::Generics<'_> {
.iter() .iter()
.filter(|param| is_impl_trait(param)) .filter(|param| is_impl_trait(param))
.map(|param| { .map(|param| {
let param: GenericParamDef = param.clean(cx); let param = clean_generic_param(cx, Some(self), param);
match param.kind { match param.kind {
GenericParamDefKind::Lifetime { .. } => unreachable!(), GenericParamDefKind::Lifetime { .. } => unreachable!(),
GenericParamDefKind::Type { did, ref bounds, .. } => { GenericParamDefKind::Type { did, ref bounds, .. } => {
@ -538,14 +568,14 @@ impl Clean<Generics> for hir::Generics<'_> {
let mut params = Vec::with_capacity(self.params.len()); let mut params = Vec::with_capacity(self.params.len());
for p in self.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) { for p in self.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
let p = p.clean(cx); let p = clean_generic_param(cx, Some(self), p);
params.push(p); params.push(p);
} }
params.extend(impl_trait_params); params.extend(impl_trait_params);
let mut generics = Generics { let mut generics = Generics {
params, params,
where_predicates: self.predicates.iter().map(|x| x.clean(cx)).collect(), where_predicates: self.predicates.iter().filter_map(|x| x.clean(cx)).collect(),
}; };
// Some duplicates are generated for ?Sized bounds between type params and where // Some duplicates are generated for ?Sized bounds between type params and where
@ -954,7 +984,11 @@ impl Clean<PolyTrait> for hir::PolyTraitRef<'_> {
fn clean(&self, cx: &mut DocContext<'_>) -> PolyTrait { fn clean(&self, cx: &mut DocContext<'_>) -> PolyTrait {
PolyTrait { PolyTrait {
trait_: self.trait_ref.clean(cx), trait_: self.trait_ref.clean(cx),
generic_params: self.bound_generic_params.iter().map(|x| x.clean(cx)).collect(), generic_params: self
.bound_generic_params
.iter()
.map(|x| clean_generic_param(cx, None, x))
.collect(),
} }
} }
} }
@ -1823,7 +1857,8 @@ impl Clean<BareFunctionDecl> for hir::BareFnTy<'_> {
fn clean(&self, cx: &mut DocContext<'_>) -> BareFunctionDecl { fn clean(&self, cx: &mut DocContext<'_>) -> BareFunctionDecl {
let (generic_params, decl) = enter_impl_trait(cx, |cx| { let (generic_params, decl) = enter_impl_trait(cx, |cx| {
// NOTE: generics must be cleaned before args // NOTE: generics must be cleaned before args
let generic_params = self.generic_params.iter().map(|x| x.clean(cx)).collect(); let generic_params =
self.generic_params.iter().map(|x| clean_generic_param(cx, None, x)).collect();
let args = clean_args_from_types_and_names(cx, self.decl.inputs, self.param_names); let args = clean_args_from_types_and_names(cx, self.decl.inputs, self.param_names);
let decl = clean_fn_decl_with_args(cx, self.decl, args); let decl = clean_fn_decl_with_args(cx, self.decl, args);
(generic_params, decl) (generic_params, decl)

View file

@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{ExprKind, GenericParam, GenericParamKind, HirId, Mod, Node}; use rustc_hir::{ExprKind, GenericParam, HirId, Mod, Node};
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use rustc_span::Span; use rustc_span::Span;
@ -100,16 +100,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
self.tcx.hir() self.tcx.hir()
} }
fn visit_generic_param(&mut self, p: &'tcx GenericParam<'tcx>) { fn visit_generic_param(&mut self, _: &'tcx GenericParam<'tcx>) {}
if !matches!(p.kind, GenericParamKind::Type { .. }) {
return;
}
for bound in p.bounds {
if let Some(trait_ref) = bound.trait_ref() {
self.handle_path(trait_ref.path, None);
}
}
}
fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) { fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
self.handle_path(path, None); self.handle_path(path, None);

View file

@ -0,0 +1,10 @@
warning: unnecessary lifetime parameter `'a`
--> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:13:5
|
LL | 'a: 'static,
| ^^
|
= help: you can use the `'static` lifetime directly, in place of `'a`
warning: 1 warning emitted

View file

@ -0,0 +1,26 @@
warning: unnecessary lifetime parameter `'a`
--> $DIR/regions-static-bound-rpass.rs:5:5
|
LL | 'a: 'static,
| ^^
|
= help: you can use the `'static` lifetime directly, in place of `'a`
warning: unnecessary lifetime parameter `'a`
--> $DIR/regions-static-bound-rpass.rs:12:5
|
LL | 'a: 'static,
| ^^
|
= help: you can use the `'static` lifetime directly, in place of `'a`
warning: unnecessary lifetime parameter `'b`
--> $DIR/regions-static-bound-rpass.rs:20:5
|
LL | 'b: 'static,
| ^^
|
= help: you can use the `'static` lifetime directly, in place of `'b`
warning: 3 warnings emitted

View file

@ -139,7 +139,13 @@ fn check_fn_inner<'tcx>(
.iter() .iter()
.filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); .filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
for typ in types { for typ in types {
for bound in typ.bounds { for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) {
if pred.in_where_clause {
// has_where_lifetimes checked that this predicate contains no lifetime.
continue;
}
for bound in pred.bounds {
let mut visitor = RefVisitor::new(cx); let mut visitor = RefVisitor::new(cx);
walk_param_bound(&mut visitor, bound); walk_param_bound(&mut visitor, bound);
if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
@ -167,6 +173,7 @@ fn check_fn_inner<'tcx>(
} }
} }
} }
}
if could_use_elision(cx, decl, body, trait_sig, generics.params) { if could_use_elision(cx, decl, body, trait_sig, generics.params) {
span_lint( span_lint(
cx, cx,
@ -322,11 +329,9 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
let mut allowed_lts = FxHashSet::default(); let mut allowed_lts = FxHashSet::default();
for par in named_generics.iter() { for par in named_generics.iter() {
if let GenericParamKind::Lifetime { .. } = par.kind { if let GenericParamKind::Lifetime { .. } = par.kind {
if par.bounds.is_empty() {
allowed_lts.insert(RefLt::Named(par.name.ident().name)); allowed_lts.insert(RefLt::Named(par.name.ident().name));
} }
} }
}
allowed_lts.insert(RefLt::Unnamed); allowed_lts.insert(RefLt::Unnamed);
allowed_lts.insert(RefLt::Static); allowed_lts.insert(RefLt::Static);
allowed_lts allowed_lts

View file

@ -8,8 +8,7 @@ use rustc_data_structures::unhash::UnhashMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::{ use rustc_hir::{
GenericBound, Generics, Item, ItemKind, Node, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind, GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind, WherePredicate,
WherePredicate,
}; };
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
@ -219,30 +218,19 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
return; return;
} }
let mut map = FxHashMap::default(); let mut map = FxHashMap::<_, Vec<_>>::default();
for param in gen.params {
if let ParamName::Plain(ref ident) = param.name {
let res = param
.bounds
.iter()
.filter_map(get_trait_info_from_bound)
.collect::<Vec<_>>();
map.insert(*ident, res);
}
}
for predicate in gen.predicates { for predicate in gen.predicates {
if_chain! { if_chain! {
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
if !bound_predicate.span.from_expansion(); if !bound_predicate.span.from_expansion();
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
if let Some(segment) = segments.first(); if let Some(segment) = segments.first();
if let Some(trait_resolutions_direct) = map.get(&segment.ident);
then { then {
for (res_where, _, _) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) { for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
if let Some((_, _, span_direct)) = trait_resolutions_direct let trait_resolutions_direct = map.entry(segment.ident).or_default();
if let Some((_, span_direct)) = trait_resolutions_direct
.iter() .iter()
.find(|(res_direct, _, _)| *res_direct == res_where) { .find(|(res_direct, _)| *res_direct == res_where) {
span_lint_and_help( span_lint_and_help(
cx, cx,
TRAIT_DUPLICATION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS,
@ -252,6 +240,9 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
"consider removing this trait bound", "consider removing this trait bound",
); );
} }
else {
trait_resolutions_direct.push((res_where, span_where))
}
} }
} }
} }

View file

@ -104,8 +104,10 @@ fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id:
if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did); if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did);
if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
if synthetic; if synthetic;
if let Some(generics) = cx.tcx.hir().get_generics(id.owner);
if let Some(pred) = generics.bounds_for_param(did.expect_local()).next();
then { then {
Some(generic_param.bounds) Some(pred.bounds)
} else { } else {
None None
} }