From 94449e610163b4cbe762089351f5517ad90a3841 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 7 Feb 2022 22:58:30 +0100 Subject: [PATCH] Store all generic bounds as where predicates. --- compiler/rustc_ast_lowering/src/item.rs | 108 +++++- compiler/rustc_ast_lowering/src/lib.rs | 88 +++-- compiler/rustc_hir/src/hir.rs | 186 ++++++++-- compiler/rustc_hir/src/intravisit.rs | 1 - compiler/rustc_hir_pretty/src/lib.rs | 15 +- .../src/infer/error_reporting/mod.rs | 67 ++-- compiler/rustc_lint/src/builtin.rs | 166 ++++----- compiler/rustc_middle/src/ty/diagnostics.rs | 336 +++++------------- compiler/rustc_middle/src/ty/error.rs | 59 +-- compiler/rustc_privacy/src/lib.rs | 5 - compiler/rustc_resolve/src/late/lifetimes.rs | 86 ++--- .../rustc_save_analysis/src/dump_visitor.rs | 2 - compiler/rustc_save_analysis/src/sig.rs | 25 -- .../src/traits/error_reporting/mod.rs | 31 +- .../src/traits/error_reporting/suggestions.rs | 21 +- compiler/rustc_typeck/src/astconv/mod.rs | 20 +- .../rustc_typeck/src/check/compare_method.rs | 19 +- .../src/check/fn_ctxt/suggestions.rs | 9 +- .../rustc_typeck/src/check/method/suggest.rs | 46 +-- compiler/rustc_typeck/src/check/mod.rs | 11 +- compiler/rustc_typeck/src/check/wfcheck.rs | 3 +- compiler/rustc_typeck/src/collect.rs | 165 +++------ src/etc/check_missing_items.py | 2 - src/librustdoc/clean/mod.rs | 119 ++++--- src/librustdoc/html/render/span_map.rs | 13 +- ...utlives-static-outlives-free-region.stderr | 10 + .../regions/regions-static-bound-rpass.stderr | 26 ++ .../clippy/clippy_lints/src/lifetimes.rs | 53 +-- .../clippy/clippy_lints/src/trait_bounds.rs | 27 +- .../clippy_lints/src/types/borrowed_box.rs | 4 +- 30 files changed, 770 insertions(+), 953 deletions(-) create mode 100644 src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr create mode 100644 src/test/ui/regions/regions-static-bound-rpass.stderr diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 764eb4eb8ba..125acdcc27d 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -267,9 +267,14 @@ impl<'hir> LoweringContext<'_, 'hir> { this.lower_maybe_async_body(span, &decl, asyncness, body.as_deref()); 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(); - 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 { decl, @@ -384,7 +389,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // lifetime to be added, but rather a reference to a // parent lifetime. 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| { this.lower_trait_ref( trait_ref, @@ -649,7 +654,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ForeignItemKind::Fn(box Fn { ref sig, ref generics, .. }) => { let fdec = &sig.decl; 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. this.lower_fn_decl(fdec, None, FnDeclKind::ExternFn, None), @@ -1228,8 +1233,8 @@ impl<'hir> LoweringContext<'_, 'hir> { is_async: Option, ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) { let header = self.lower_fn_header(sig.header); - let (generics, decl) = self.add_implicit_generics(generics, id, |this, idty| { - this.lower_fn_decl(&sig.decl, Some((id, idty)), kind, is_async) + let (generics, decl) = self.add_implicit_generics(generics, id, |this, idty, idpb| { + this.lower_fn_decl(&sig.decl, Some((id, idty, idpb)), kind, is_async) }); (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( &mut self, generics: &Generics, - itctx: ImplTraitContext<'_, 'hir>, + mut itctx: ImplTraitContext<'_, 'hir>, ) -> GenericsCtor<'hir> { // 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 @@ -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(¶m.bounds, itctx.reborrow()); + self.lower_generic_bound_predicate(param.ident, param.id, ¶m.kind, bounds) + })); + predicates.extend( generics .where_clause .predicates @@ -1347,8 +1357,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ); GenericsCtor { - params: self.lower_generic_params_mut(&generics.params, itctx).collect(), + params: self.lower_generic_params_mut(&generics.params).collect(), predicates, + has_where_clause: !generics.where_clause.predicates.is_empty(), where_clause_span: self.lower_span(generics.where_clause.span), span: self.lower_span(generics.span), } @@ -1363,6 +1374,72 @@ impl<'hir> LoweringContext<'_, 'hir> { 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> { + // 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, 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> { match *pred { WherePredicate::BoundPredicate(WhereBoundPredicate { @@ -1371,10 +1448,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ref bounds, span, }) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - bound_generic_params: self.lower_generic_params( - bound_generic_params, - ImplTraitContext::Disallowed(ImplTraitPosition::Generic), - ), + bound_generic_params: self.lower_generic_params(bound_generic_params), bounded_ty: self .lower_ty(bounded_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)), bounds: self.arena.alloc_from_iter(bounds.iter().map(|bound| { @@ -1384,6 +1458,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) })), span: self.lower_span(span), + in_where_clause: true, }), WherePredicate::RegionPredicate(WhereRegionPredicate { ref lifetime, @@ -1396,6 +1471,7 @@ impl<'hir> LoweringContext<'_, 'hir> { bounds, ImplTraitContext::Disallowed(ImplTraitPosition::Bound), ), + in_where_clause: true, }), WherePredicate::EqPredicate(WhereEqPredicate { id, ref lhs_ty, ref rhs_ty, span }) => { hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { @@ -1414,7 +1490,8 @@ impl<'hir> LoweringContext<'_, 'hir> { /// Helper struct for delayed construction of Generics. pub(super) struct GenericsCtor<'hir> { 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, span: Span, } @@ -1423,7 +1500,8 @@ impl<'hir> GenericsCtor<'hir> { pub(super) fn into_generics(self, arena: &'hir Arena<'hir>) -> &'hir hir::Generics<'hir> { arena.alloc(hir::Generics { 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, span: self.span, }) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 6acd8fcc5ce..6abb3d03e83 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -259,7 +259,7 @@ enum ImplTraitContext<'b, 'a> { /// equivalent to a fresh universal parameter like `fn foo(x: T)`. /// /// Newly generated parameters should be inserted into the given `Vec`. - Universal(&'b mut Vec>, LocalDefId), + Universal(&'b mut Vec>, &'b mut Vec>, LocalDefId), /// Treat `impl Trait` as shorthand for a new opaque type. /// 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> { use self::ImplTraitContext::*; match self { - Universal(params, parent) => Universal(params, *parent), + Universal(params, bounds, parent) => Universal(params, bounds, *parent), ReturnPositionOpaqueTy { origin } => ReturnPositionOpaqueTy { origin: *origin }, TypeAliasesOpaqueTy => TypeAliasesOpaqueTy, Disallowed(pos) => Disallowed(*pos), @@ -704,7 +704,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { Some(hir::GenericParam { hir_id, name, - bounds: &[], span: self.lower_span(ident.span), pure_wrt_drop: false, kind: hir::GenericParamKind::Lifetime { kind }, @@ -718,14 +717,23 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, generics: &Generics, parent_node_id: NodeId, - f: impl FnOnce(&mut Self, &mut Vec>) -> T, + f: impl FnOnce( + &mut Self, + &mut Vec>, + &mut Vec>, + ) -> T, ) -> (&'hir hir::Generics<'hir>, T) { let mut impl_trait_defs = Vec::new(); + let mut impl_trait_bounds = Vec::new(); let mut lowered_generics = self.lower_generics_mut( 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); lowered_generics.params.extend( @@ -736,6 +744,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }) .chain(impl_trait_defs), ); + lowered_generics.predicates.extend(impl_trait_bounds); let lowered_generics = lowered_generics.into_generics(self.arena); (lowered_generics, res) @@ -999,7 +1008,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // so desugar to // // fn foo(x: dyn Iterator) - ImplTraitContext::Universal(_, parent) if self.is_in_dyn_type => { + ImplTraitContext::Universal(_, _, parent) if self.is_in_dyn_type => { parent_def_id = parent; (true, itctx) } @@ -1188,10 +1197,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } TyKind::BareFn(ref f) => self.with_lifetime_binder(t.id, |this| { hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy { - generic_params: this.lower_generic_params( - &f.generic_params, - ImplTraitContext::Disallowed(ImplTraitPosition::Generic), - ), + generic_params: this.lower_generic_params(&f.generic_params), unsafety: this.lower_unsafety(f.unsafety), abi: this.lower_extern(f.ext), 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), ) } - 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`. let def_id = self.resolver.local_def_id(def_node_id); let hir_bounds = self.lower_param_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`. 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), name: ParamName::Plain(self.lower_ident(ident)), pure_wrt_drop: false, - bounds: hir_bounds, span: self.lower_span(span), 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( None, @@ -1374,7 +1395,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { name: p_name, span, pure_wrt_drop: false, - bounds: &[], kind: hir::GenericParamKind::Lifetime { kind }, } }, @@ -1386,6 +1406,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { generics: self.arena.alloc(hir::Generics { params: lifetime_defs, predicates: &[], + has_where_clause: false, where_clause_span: lctx.lower_span(span), span: lctx.lower_span(span), }), @@ -1463,7 +1484,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_fn_decl( &mut self, decl: &FnDecl, - mut in_band_ty_params: Option<(NodeId, &mut Vec>)>, + mut in_band_ty_params: Option<( + NodeId, + &mut Vec>, + &mut Vec>, + )>, kind: FnDeclKind, make_ret_async: Option, ) -> &'hir hir::FnDecl<'hir> { @@ -1486,10 +1511,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { inputs = &inputs[..inputs.len() - 1]; } 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( ¶m.ty, - ImplTraitContext::Universal(ibty, self.current_hir_id_owner), + ImplTraitContext::Universal(ibty, ibpb, self.current_hir_id_owner), ) } else { self.lower_ty_direct( @@ -1518,7 +1543,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match decl.output { FnRetTy::Ty(ref ty) => { 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); ImplTraitContext::ReturnPositionOpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id), @@ -1709,7 +1734,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { name: p_name, span, pure_wrt_drop: false, - bounds: &[], kind: hir::GenericParamKind::Lifetime { kind }, } })); @@ -1719,6 +1743,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { generics: this.arena.alloc(hir::Generics { params: generic_params, predicates: &[], + has_where_clause: false, where_clause_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>( &'s mut self, params: &'s [GenericParam], - mut itctx: ImplTraitContext<'s, 'hir>, ) -> impl Iterator> + 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( - &mut self, - params: &[GenericParam], - itctx: ImplTraitContext<'_, 'hir>, - ) -> &'hir [hir::GenericParam<'hir>] { - self.arena.alloc_from_iter(self.lower_generic_params_mut(params, itctx)) + fn lower_generic_params(&mut self, params: &[GenericParam]) -> &'hir [hir::GenericParam<'hir>] { + self.arena.alloc_from_iter(self.lower_generic_params_mut(params)) } - fn lower_generic_param( - &mut self, - param: &GenericParam, - mut itctx: ImplTraitContext<'_, 'hir>, - ) -> hir::GenericParam<'hir> { - let bounds: Vec<_> = self.lower_param_bounds_mut(¶m.bounds, itctx.reborrow()).collect(); - + fn lower_generic_param(&mut self, param: &GenericParam) -> hir::GenericParam<'hir> { let (name, kind) = match param.kind { GenericParamKind::Lifetime => { let param_name = if param.ident.name == kw::StaticLifetime @@ -1991,7 +2005,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { name, span: self.lower_span(param.span()), pure_wrt_drop: self.sess.contains_name(¶m.attrs, sym::may_dangle), - bounds: self.arena.alloc_from_iter(bounds), kind, } } @@ -2014,8 +2027,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { p: &PolyTraitRef, mut itctx: ImplTraitContext<'_, 'hir>, ) -> hir::PolyTraitRef<'hir> { - let bound_generic_params = - self.lower_generic_params(&p.bound_generic_params, itctx.reborrow()); + let bound_generic_params = self.lower_generic_params(&p.bound_generic_params); let trait_ref = self.with_lifetime_binder(p.trait_ref.ref_id, |this| { this.lower_trait_ref(&p.trait_ref, itctx.reborrow()) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e34f9e8ca6f..a5c8088a279 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -496,27 +496,24 @@ pub enum GenericParamKind<'hir> { pub struct GenericParam<'hir> { pub hir_id: HirId, pub name: ParamName, - pub bounds: GenericBounds<'hir>, pub span: Span, pub pure_wrt_drop: bool, pub kind: GenericParamKind<'hir>, } impl<'hir> GenericParam<'hir> { - pub fn bounds_span_for_suggestions(&self) -> Option { - self.bounds - .iter() - .fold(None, |span: Option, 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. - 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()) + /// Synthetic type-parameters are inserted after normal ones. + /// In order for normal parameters to be able to refer to synthetic ones, + /// scans them first. + pub fn is_impl_trait(&self) -> bool { + matches!(self.kind, GenericParamKind::Type { synthetic: true, .. }) + } + + /// 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. @@ -568,14 +565,20 @@ pub struct GenericParamCount { pub struct Generics<'hir> { pub params: &'hir [GenericParam<'hir>], pub predicates: &'hir [WherePredicate<'hir>], + pub has_where_clause: bool, pub where_clause_span: Span, pub span: Span, } impl<'hir> Generics<'hir> { pub const fn empty() -> &'hir Generics<'hir> { - const NOPE: Generics<'_> = - Generics { params: &[], predicates: &[], where_clause_span: DUMMY_SP, span: DUMMY_SP }; + const NOPE: Generics<'_> = Generics { + params: &[], + predicates: &[], + has_where_clause: false, + where_clause_span: DUMMY_SP, + span: DUMMY_SP, + }; &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 { + if self.params.iter().any(|p| self.span.contains(p.span)) { + // `fn foo(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 { 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) where T: Foo,` so we don't suggest two trailing commas. pub fn tail_span_for_predicate_suggestion(&self) -> Span { 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> { + 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 { + 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() { + // + // ^^^^^^^^ + 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, } } + + 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`). #[derive(Debug, HashStable_Generic)] pub struct WhereBoundPredicate<'hir> { pub span: Span, + pub in_where_clause: bool, /// Any generics from a `for` binding. pub bound_generic_params: &'hir [GenericParam<'hir>], /// The type being bounded. @@ -650,14 +771,7 @@ pub struct WhereBoundPredicate<'hir> { impl<'hir> WhereBoundPredicate<'hir> { /// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate. pub fn is_param_bound(&self, param_def_id: DefId) -> bool { - let TyKind::Path(QPath::Resolved(None, path)) = self.bounded_ty.kind else { - 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, - } + self.bounded_ty.as_generic_param().map_or(false, |(def_id, _)| def_id == param_def_id) } } @@ -665,6 +779,7 @@ impl<'hir> WhereBoundPredicate<'hir> { #[derive(Debug, HashStable_Generic)] pub struct WhereRegionPredicate<'hir> { pub span: Span, + pub in_where_clause: bool, pub lifetime: Lifetime, pub bounds: GenericBounds<'hir>, } @@ -2230,6 +2345,23 @@ pub struct Ty<'hir> { 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`. #[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)] #[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::Ty<'static>, 72); 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::Item<'static>, 80); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 85ef54f8890..d41b85fd20f 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -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) { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 375a96b1519..7af9622b2cf 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -2096,21 +2096,8 @@ impl<'a> State<'a> { self.print_ident(param.name.ident()); match param.kind { - 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::Lifetime { .. } => {} GenericParamKind::Type { ref default, .. } => { - self.print_bounds(":", param.bounds); if let Some(default) = default { self.space(); self.word_space("="); diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 8f363cbe0de..2e50dbff510 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2327,6 +2327,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => span, }; + // type_param_span is (span, has_bounds) let type_param_span = match (generics, bound_kind) { (Some((_, ref generics, _)), GenericKind::Param(ref param)) => { // 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. // We do this to avoid suggesting code that ends up as `T: 'a'b`, // instead we suggest `T: 'a + 'b` in that case. - let id = hir.local_def_id_to_hir_id(def_id); - let mut has_bounds = false; - if let Node::GenericParam(param) = hir.get(id) { - has_bounds = !param.bounds.is_empty(); - } - let sp = self.tcx.def_span(def_id); + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); + let ast_generics = self.tcx.hir().get_generics(hir_id.owner); + let bounds = + ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id)); // `sp` only covers `T`, change it so that it covers // `T:` when appropriate - let is_impl_trait = bound_kind.to_string().starts_with("impl "); - let sp = if has_bounds && !is_impl_trait { - sp.to(self - .tcx - .sess - .source_map() - .next_point(self.tcx.sess.source_map().next_point(sp))) + if let Some(span) = bounds { + (span, true) } else { - sp - }; - (sp, has_bounds, is_impl_trait) + let sp = self.tcx.def_span(def_id); + (sp.shrink_to_hi(), false) + } }) } else { None @@ -2411,52 +2405,37 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { fn binding_suggestion<'tcx, S: fmt::Display>( err: &mut Diagnostic, - type_param_span: Option<(Span, bool, bool)>, + type_param_span: Option<(Span, bool)>, bound_kind: GenericKind<'tcx>, sub: S, ) { let msg = "consider adding an explicit lifetime bound"; - if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span { - let suggestion = if is_impl_trait { - format!("{} + {}", bound_kind, sub) - } else { - let tail = if has_lifetimes { " + " } else { "" }; - format!("{}: {}{}", bound_kind, sub, tail) - }; - err.span_suggestion( + if let Some((sp, has_lifetimes)) = type_param_span { + let suggestion = + if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) }; + err.span_suggestion_verbose( sp, &format!("{}...", msg), suggestion, Applicability::MaybeIncorrect, // Issue #41966 ); } else { - let consider = format!( - "{} {}...", - msg, - if type_param_span.map_or(false, |(_, _, is_impl_trait)| is_impl_trait) { - format!(" `{}` to `{}`", sub, bound_kind) - } else { - format!("`{}: {}`", bound_kind, sub) - }, - ); + let consider = format!("{} `{}: {}`...", msg, bound_kind, sub,); err.help(&consider); } } let new_binding_suggestion = - |err: &mut Diagnostic, - type_param_span: Option<(Span, bool, bool)>, - bound_kind: GenericKind<'tcx>| { + |err: &mut Diagnostic, type_param_span: Option<(Span, bool)>| { let msg = "consider introducing an explicit lifetime bound"; - if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span { - let suggestion = if is_impl_trait { - (sp.shrink_to_hi(), format!(" + {}", new_lt)) + if let Some((sp, has_lifetimes)) = type_param_span { + let suggestion = if has_lifetimes { + format!(" + {}", new_lt) } else { - let tail = if has_lifetimes { " +" } else { "" }; - (sp, format!("{}: {}{}", bound_kind, new_lt, tail)) + format!(": {}", new_lt) }; 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 { sugg.push(lt); sugg.rotate_right(1); @@ -2615,7 +2594,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // suggest: // 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 => { - 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); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 4c26a515bf6..3564f15e210 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1517,58 +1517,61 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { // Bounds are respected for `type X = impl Trait` return; } - let mut suggested_changing_assoc_types = false; // There must not be a where clause - if !type_alias_generics.predicates.is_empty() { - cx.lint( - TYPE_ALIAS_BOUNDS, - |lint| { - let mut err = lint.build("where clauses are not enforced in type aliases"); - let spans: Vec<_> = type_alias_generics - .predicates - .iter() - .map(|pred| pred.span()) - .collect(); - err.set_span(spans); - err.span_suggestion( - type_alias_generics.span_for_predicates_or_empty_place(), - "the clause will not be checked when the type alias is used, and should be removed", - String::new(), - Applicability::MachineApplicable, - ); - if !suggested_changing_assoc_types { - TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); - suggested_changing_assoc_types = true; - } - err.emit(); - }, - ); + if type_alias_generics.predicates.is_empty() { + return; } - // The parameters must not have bounds - for param in type_alias_generics.params.iter() { - let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect(); - 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 = - 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, \ - and should be removed"; - err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable); - if !suggested_changing_assoc_types { - TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); - suggested_changing_assoc_types = true; - } - err.emit(); - }); + + 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"); + err.set_span(where_spans); + err.span_suggestion( + type_alias_generics.where_clause_span, + "the clause will not be checked when the type alias is used, and should be removed", + String::new(), + Applicability::MachineApplicable, + ); + if !suggested_changing_assoc_types { + TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); + suggested_changing_assoc_types = true; + } + err.emit(); + }); + } + + if !inline_spans.is_empty() { + cx.lint(TYPE_ALIAS_BOUNDS, |lint| { + let mut err = + lint.build("bounds on generic parameters are not enforced in type aliases"); + err.set_span(inline_spans); + err.multipart_suggestion( + "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 { + TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err); + } + err.emit(); + }); + } } } @@ -2084,27 +2087,6 @@ impl ExplicitOutlivesRequirements { .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> { - 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>( &self, tcx: TyCtxt<'tcx>, @@ -2212,41 +2194,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { let mut bound_count = 0; 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, - ¶m.bounds, - &relevant_lifetimes, - infer_static, - ); - bound_count += bound_spans.len(); - lint_spans.extend(self.consolidate_outlives_bound_spans( - param.span.shrink_to_hi(), - ¶m.bounds, - bound_spans, - )); - } - let mut where_lint_spans = Vec::new(); let mut dropped_predicate_count = 0; let num_predicates = hir_generics.predicates.len(); 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) => { if let Some(Region::EarlyBound(index, ..)) = 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), &predicate.bounds, predicate.span, + predicate.in_where_clause, ) } else { continue; @@ -2273,6 +2226,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { Self::lifetimes_outliving_type(inferred_outlives, index), &predicate.bounds, predicate.span, + predicate.in_where_clause, ) } _ => { @@ -2299,9 +2253,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { dropped_predicate_count += 1; } - // If all the bounds on a predicate were inferable and there are - // further predicates, we want to eat the trailing comma. - if drop_predicate && i + 1 < num_predicates { + 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 + // further predicates, we want to eat the trailing comma. let next_predicate_span = hir_generics.predicates[i + 1].span(); where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo())); } else { @@ -2315,7 +2271,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { // If all predicates are inferable, drop the entire clause // (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 .where_clause_span() .expect("span of (nonempty) where clause should exist"); @@ -2344,7 +2300,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { }, lint_spans .into_iter() - .map(|span| (span, "".to_owned())) + .map(|span| (span, String::new())) .collect::>(), Applicability::MachineApplicable, ) diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 5203fb51882..aeeab677ae0 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; +use rustc_hir::WherePredicate; use rustc_span::Span; 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. - let (action, prefix) = if generics.predicates.is_empty() { - ("introducing a", " where ") - } else { + let (action, prefix) = if generics.has_where_clause { ("extending the", ", ") + } else { + ("introducing a", " where ") }; err.span_suggestion_verbose( generics.tail_span_for_predicate_suggestion(), @@ -183,95 +183,37 @@ enum SuggestChangingConstraintsMessage<'a> { } fn suggest_removing_unsized_bound( + tcx: TyCtxt<'_>, generics: &hir::Generics<'_>, suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>, - param_name: &str, param: &hir::GenericParam<'_>, def_id: Option, ) { // 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`, // 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() { - match predicate { - WherePredicate::BoundPredicate(WhereBoundPredicate { - bounded_ty: - hir::Ty { - kind: - 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()), - }; + let WherePredicate::BoundPredicate(predicate) = predicate else { + continue; + }; + if !predicate.is_param_bound(param_def_id.to_def_id()) { + continue; + }; - suggestions.push(( - sp, - String::new(), - SuggestChangingConstraintsMessage::RemovingQSized, - )); - } + 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; } - _ => {} - } - } - 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, - )); - } - _ => {} + let sp = generics.span_for_bound_removal(where_pos, pos); + suggestions.push(( + sp, + String::new(), + SuggestChangingConstraintsMessage::RemovingQSized, + )); } } } @@ -322,13 +264,7 @@ pub fn suggest_constraining_type_params<'a>( param.span, &format!("this type parameter needs to be `{}`", constraint), ); - suggest_removing_unsized_bound( - generics, - &mut suggestions, - param_name, - param, - def_id, - ); + suggest_removing_unsized_bound(tcx, generics, &mut suggestions, param, def_id); } } @@ -349,76 +285,45 @@ pub fn suggest_constraining_type_params<'a>( )) }; - if param_name.starts_with("impl ") { - // If there's an `impl Trait` used in argument position, suggest - // restricting it: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // replace with: `impl Foo + Bar` - - // `impl Trait` must have at least one trait in the list - let bound_list_non_empty = true; - - suggest_restrict(param.span.shrink_to_hi(), bound_list_non_empty); + // When the type parameter has been provided bounds + // + // Message: + // fn foo(t: T) where T: Foo { ... } + // ^^^^^^ + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion: + // fn foo(t: T) where T: Foo { ... } + // ^ + // | + // replace with: ` + Bar` + // + // Or, if user has provided some bounds, suggest restricting them: + // + // fn foo(t: T) { ... } + // --- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: T) { ... } + // -- + // | + // replace with: `T: Bar +` + let param_def_id = tcx.hir().local_def_id(param.hir_id); + if let Some(span) = generics.bounds_span_for_suggestions(param_def_id) { + suggest_restrict(span, true); continue; } - if generics.predicates.is_empty() - // Given `trait Base: Super` where `T: Copy`, suggest restricting in the - // `where` clause instead of `trait Base: Super`. - && !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: T) { ... } - // --- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: T) { ... } - // -- - // | - // replace with: `T: Bar +` - - // `bounds_span_for_suggestions` returns `None` if the list is empty - let bound_list_non_empty = true; - - 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) { ... } - // - 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 // provide zero, one or many bounds for the same type parameter, so we // 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: // fn foo(x: X, y: Y) where Y: Foo { ... } @@ -427,95 +332,46 @@ pub fn suggest_constraining_type_params<'a>( // Suggestion: // fn foo(x: X, y: Y) where Y: Foo { ... } // - insert: `, X: Bar` - // - // - // 2) When the type parameter has been provided one bound - // - // Message: - // fn foo(t: T) where T: Foo { ... } - // ^^^^^^ - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion: - // fn foo(t: T) where T: Foo { ... } - // ^^ - // | - // replace with: `T: Bar +` - // - // - // 3) When the type parameter has been provided many bounds - // - // Message: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // fn foo(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: - // trait Foo {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // trait Foo where T: Zar {... } - // - insert: `where T: Zar` - - 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 - // default (``), so we suggest adding `where T: Bar`. - suggestions.push(( - generics.tail_span_for_predicate_suggestion(), - format!(" where {}: {}", param_name, constraint), - SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, - )); - } else { - 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[..] { - [¶m_span] => suggest_restrict(param_span.shrink_to_hi(), non_empty), - _ => { - suggestions.push(( - generics.tail_span_for_predicate_suggestion(), - constraints - .iter() - .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint)) - .collect::(), - SuggestChangingConstraintsMessage::RestrictTypeFurther { - ty: param_name, - }, - )); - } - } - } + suggestions.push(( + generics.tail_span_for_predicate_suggestion(), + constraints + .iter() + .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint)) + .collect::(), + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, + )); + continue; } + + // Additionally, there may be no `where` clause but the generic parameter has a default: + // + // Message: + // trait Foo {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // trait Foo {... } + // - insert: `where T: Zar` + if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) { + // Suggest a bound, but there is no existing `where` clause *and* the type param has a + // default (``), so we suggest adding `where T: Bar`. + suggestions.push(( + generics.tail_span_for_predicate_suggestion(), + format!(" where {}: {}", param_name, constraint), + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, + )); + continue; + } + + // If user hasn't provided any bounds, suggest adding a new one: + // + // fn foo(t: T) { ... } + // - help: consider restricting this type parameter with `T: Foo` + suggestions.push(( + param.span.shrink_to_hi(), + format!(": {}", constraint), + SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, + )); } if suggestions.len() == 1 { diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 5c5d0a46534..da0934b67c5 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -602,53 +602,24 @@ impl Trait for X { } else { return false; }; + let Some(def_id) = def_id.as_local() else { + return false; + }; // First look in the `where` clause, as this might be // `fn foo(x: T) where T: Trait`. - for predicate in hir_generics.predicates { - 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 `::Foo` to - // something, potentially `T`. - } else { - continue; - } - } else { - continue; - } - - if self.constrain_generic_bound_associated_type_structured_suggestion( - diag, - &trait_ref, - pred.bounds, - &assoc, - assoc_substs, - ty, - msg, - false, - ) { - return true; - } - } - } - 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 `::Foo`. - return self.constrain_generic_bound_associated_type_structured_suggestion( - diag, - &trait_ref, - param.bounds, - &assoc, - assoc_substs, - ty, - msg, - false, - ); + for pred in hir_generics.bounds_for_param(def_id) { + if self.constrain_generic_bound_associated_type_structured_suggestion( + diag, + &trait_ref, + pred.bounds, + &assoc, + assoc_substs, + ty, + msg, + false, + ) { + return true; } } } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 5c396c2b7ea..619e0d0341f 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1585,11 +1585,6 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, '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 { match predicate { hir::WherePredicate::BoundPredicate(bound_pred) => { diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index f7e94dd10f7..787536d2a38 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -1328,13 +1328,11 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { match param.kind { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { ref default, .. } => { - walk_list!(this, visit_param_bound, param.bounds); if let Some(ref ty) = default { this.visit_ty(&ty); } } GenericParamKind::Const { ref ty, default } => { - walk_list!(this, visit_param_bound, param.bounds); this.visit_ty(&ty); if let Some(default) = default { 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); 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 { ref lhs_ty, @@ -1714,8 +1738,6 @@ fn object_lifetime_defaults_for_item<'tcx>( GenericParamKind::Type { .. } => { let mut set = Set1::Empty; - add_bounds(&mut set, ¶m.bounds); - let param_def_id = tcx.hir().local_def_id(param.hir_id); for predicate in generics.predicates { // 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. 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. let mut appears_in_where_clause = AllCollector::default(); 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); // Late bound regions are those that: diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index a27ac75d584..b4230a144f8 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -1267,13 +1267,11 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { match param.kind { hir::GenericParamKind::Lifetime { .. } => {} hir::GenericParamKind::Type { ref default, .. } => { - self.process_bounds(param.bounds); if let Some(ref ty) = default { self.visit_ty(ty); } } hir::GenericParamKind::Const { ref ty, ref default } => { - self.process_bounds(param.bounds); self.visit_ty(ty); if let Some(default) = default { self.visit_anon_const(default); diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index 8f50f445719..d1286c9b8b0 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -630,31 +630,6 @@ impl<'hir> Sig for hir::Generics<'hir> { 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::>() - .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(¶m_text); text.push(','); } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index a3e8be44c7a..7a3579eb1cc 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2420,25 +2420,14 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { let sized_trait = self.tcx.lang_items().sized_trait(); debug!("maybe_suggest_unsized_generics: generics.params={:?}", generics.params); debug!("maybe_suggest_unsized_generics: generics.predicates={:?}", generics.predicates); - let param = generics.params.iter().filter(|param| param.span == span).find(|param| { - // 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 { + let Some(param) = generics.params.iter().find(|param| param.span == span) else { return; }; - let param_def_id = self.tcx.hir().local_def_id(param.hir_id).to_def_id(); - let preds = generics.predicates.iter(); - let explicitly_sized = preds - .filter_map(|pred| match pred { - hir::WherePredicate::BoundPredicate(bp) => Some(bp), - _ => None, - }) - .filter(|bp| bp.is_param_bound(param_def_id)) + let param_def_id = self.tcx.hir().local_def_id(param.hir_id); + // 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. + let explicitly_sized = generics + .bounds_for_param(param_def_id) .flat_map(|bp| bp.bounds) .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait); 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`. - let (span, separator) = match param.bounds { - [] => (span.shrink_to_hi(), ":"), - [.., bound] => (bound.span().shrink_to_hi(), " +"), + let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param_def_id) + { + (s, " +") + } else { + (span.shrink_to_hi(), ":") }; err.span_suggestion_verbose( span, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 9900cdd3eba..446b14a17ae 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -320,7 +320,7 @@ pub trait InferCtxtExt<'tcx> { fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) { ( 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 = pred.replace(&impl_trait_str, &type_param_name); let mut sugg = vec![ - // Find the last of the generic parameters contained within the span of - // the generics - match generics - .params - .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 `` here - None => (generics.span, format!("<{}>", type_param)), - // `fn foo(t: impl Trait)` - // ^^^ suggest `` here - Some(span) => (span, format!(", {}", type_param)), + if let Some(span) = generics.span_for_param_suggestion() { + (span, format!(", {}", type_param)) + } else { + (generics.span, format!("<{}>", type_param)) }, // `fn foo(t: impl Trait)` // ^ suggest `where ::A: Bound` diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 3e36ffa7fe0..b8422ce3208 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -924,14 +924,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let self_ty_def_id = tcx.hir().local_def_id(self_ty).to_def_id(); for clause in where_clause { if let hir::WherePredicate::BoundPredicate(pred) = clause { - match pred.bounded_ty.kind { - 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, + if pred.is_param_bound(self_ty_def_id) { + search_bounds(pred.bounds); } - search_bounds(pred.bounds); } } } @@ -2389,7 +2384,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { bf.unsafety, bf.abi, bf.decl, - &hir::Generics::empty(), None, Some(ast_ty), )) @@ -2551,8 +2545,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { unsafety: hir::Unsafety, abi: abi::Abi, decl: &hir::FnDecl<'_>, - generics: &hir::Generics<'_>, - ident_span: Option, + generics: Option<&hir::Generics<'_>>, hir_ty: Option<&hir::Ty<'_>>, ) -> ty::PolyFnSig<'tcx> { debug!("ty_of_fn"); @@ -2565,7 +2558,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut visitor = HirPlaceholderCollector::default(); let mut infer_replacements = vec![]; - walk_generics(&mut visitor, generics); + if let Some(generics) = generics { + walk_generics(&mut visitor, generics); + } let input_tys: Vec<_> = decl .inputs @@ -2617,8 +2612,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut diag = crate::collect::placeholder_type_error_diag( tcx, - ident_span.map(|sp| sp.shrink_to_hi()), - generics.params, + generics, visitor.0, infer_replacements.iter().map(|(s, _)| *s).collect(), true, diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 4ab6f2cdafb..6d78a863d54 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -804,7 +804,8 @@ fn compare_synthetic_generics<'tcx>( iter::zip(impl_m_type_params, trait_m_type_params) { 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 trait_span = tcx.def_span(trait_def_id); let mut err = struct_span_err!( @@ -868,14 +869,14 @@ fn compare_synthetic_generics<'tcx>( hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs, _ => unreachable!(), }; - struct Visitor(Option, hir::def_id::DefId); + struct Visitor(Option, hir::def_id::LocalDefId); impl<'v> intravisit::Visitor<'v> for Visitor { fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { intravisit::walk_ty(self, ty); if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = ty.kind && 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); } @@ -887,17 +888,7 @@ fn compare_synthetic_generics<'tcx>( } let span = visitor.0?; - let 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(¶m.bounds) - } else { - None - } - } - })?; + let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds; let bounds = bounds.first()?.span().to(bounds.last()?.span()); let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?; diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index c42db45854f..681d1e37f86 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -605,7 +605,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .. })) = 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 let where_predicates = predicates @@ -639,10 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { where_predicates.iter().flatten().flat_map(|bounds| bounds.iter()); // extract all bounds from the source code using their spans - let all_matching_bounds_strs = expected_generic_param - .bounds - .iter() - .chain(predicates_from_where) + let all_matching_bounds_strs = predicates_from_where .filter_map(|bound| match bound { GenericBound::Trait(_, _) => { self.tcx.sess.source_map().span_to_snippet(bound.span()).ok() diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 727352c395b..e931dadbee7 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -1868,37 +1868,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // instead we suggest `T: Foo + Bar` in that case. match hir.get(id) { Node::GenericParam(param) => { - let mut impl_trait = false; - let has_bounds = - if let hir::GenericParamKind::Type { synthetic: true, .. } = - ¶m.kind - { - // We've found `fn foo(x: impl Trait)` instead of - // `fn foo(x: T)`. We want to suggest the correct - // `fn foo(x: impl Trait + TraitBound)` instead of - // `fn foo(x: T)`. (#63706) - impl_trait = true; - param.bounds.get(1) - } else { - param.bounds.get(0) - }; - let sp = hir.span(id); - let sp = if let Some(first_bound) = has_bounds { - sp.until(first_bound.span()) - } else if let Some(colon_sp) = - // If the generic param is declared with a colon but without bounds: - // fn foo(t: T) { ... } - param.colon_span_for_suggestions( - self.inh.tcx.sess.source_map(), - ) + let impl_trait = matches!( + param.kind, + hir::GenericParamKind::Type { synthetic: true, .. }, + ); + 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) { - sp.to(colon_sp) + (span, true) } else { - sp + (hir.span(id).shrink_to_hi(), false) }; - let trait_def_ids: FxHashSet = param - .bounds - .iter() + let trait_def_ids: FxHashSet = ast_generics + .bounds_for_param(def_id) + .flat_map(|bp| bp.bounds.iter()) .filter_map(|bound| bound.trait_ref()?.trait_def_id()) .collect(); 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| { format!( - "{}{} {}{}", - param.name.ident(), - if impl_trait { " +" } else { ":" }, + "{} {}", + if has_bounds || impl_trait { " +" } else { ":" }, self.tcx.def_path_str(t.def_id), - if has_bounds.is_some() { " + " } else { "" }, ) }), Applicability::MaybeIncorrect, diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index f7bb30cd13e..76c955d6f69 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -373,16 +373,7 @@ fn typeck_with_fallback<'tcx>( 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 fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - >::ty_of_fn( - &fcx, - id, - header.unsafety, - header.abi, - decl, - &hir::Generics::empty(), - None, - None, - ) + >::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None) } else { tcx.fn_sig(def_id) }; diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index ce70650d4ac..ec2b7c13ff3 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -1755,8 +1755,7 @@ fn check_variances_for_type_defn<'tcx>( match param.name { hir::ParamName::Error => {} _ => { - let has_explicit_bounds = - !param.bounds.is_empty() || explicitly_bounded_params.contains(¶meter); + let has_explicit_bounds = explicitly_bounded_params.contains(¶meter); report_bivariance(tcx, param, has_explicit_bounds); } } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index fd63fd1eb7d..f85735ec57b 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1,4 +1,3 @@ -// ignore-tidy-filelength //! "Collection" is the process of determining the type and other external //! details of each item in Rust. Collection is specifically concerned //! 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. crate fn placeholder_type_error<'tcx>( tcx: TyCtxt<'tcx>, - span: Option, - generics: &[hir::GenericParam<'_>], + generics: Option<&hir::Generics<'_>>, placeholder_types: Vec, suggest: bool, hir_ty: Option<&hir::Ty<'_>>, @@ -160,23 +158,13 @@ crate fn placeholder_type_error<'tcx>( return; } - placeholder_type_error_diag( - tcx, - span, - generics, - placeholder_types, - vec![], - suggest, - hir_ty, - kind, - ) - .emit(); + placeholder_type_error_diag(tcx, generics, placeholder_types, vec![], suggest, hir_ty, kind) + .emit(); } crate fn placeholder_type_error_diag<'tcx>( tcx: TyCtxt<'tcx>, - span: Option, - generics: &[hir::GenericParam<'_>], + generics: Option<&hir::Generics<'_>>, placeholder_types: Vec, additional_spans: Vec, suggest: bool, @@ -187,26 +175,24 @@ crate fn placeholder_type_error_diag<'tcx>( 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<_> = placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect(); - if generics.is_empty() { - if let Some(span) = span { - sugg.push((span, format!("<{}>", type_name))); + if let Some(generics) = generics { + if let Some(arg) = params.iter().find(|arg| { + matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. })) + }) { + // Account for `_` already present in cases like `struct S<_>(_);` and suggest + // `struct S(T);` instead of `struct S<_, T>(T);`. + sugg.push((arg.span, (*type_name).to_string())); + } else if let Some(span) = generics.span_for_param_suggestion() { + // Account for bounds, we want `fn foo(_: K)` not `fn foo(_: K)`. + sugg.push((span, format!(", {}", type_name))); + } else { + sugg.push((generics.span, format!("<{}>", type_name))); } - } 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 - // `struct S(T);` instead of `struct S<_, T>(T);`. - sugg.push((arg.span, (*type_name).to_string())); - } else { - let last = generics.iter().last().unwrap(); - // Account for bounds, we want `fn foo(_: K)` not `fn foo(_: K)`. - let span = last.bounds_span_for_suggestions().unwrap_or(last.span.shrink_to_hi()); - sugg.push((span, format!(", {}", type_name))); } let mut err = @@ -270,15 +256,7 @@ fn reject_placeholder_type_signatures_in_item<'tcx>( let mut visitor = HirPlaceholderCollector::default(); visitor.visit_item(item); - placeholder_type_error( - tcx, - Some(generics.span), - generics.params, - visitor.0, - suggest, - None, - item.kind.descr(), - ); + placeholder_type_error(tcx, Some(generics), visitor.0, suggest, None, item.kind.descr()); } impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { @@ -680,26 +658,8 @@ impl<'tcx> ItemCtxt<'tcx> { only_self_bounds: OnlySelfBounds, assoc_name: Option, ) -> 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(¶m.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 from_where_clauses = ast_generics + ast_generics .predicates .iter() .filter_map(|wp| match *wp { @@ -724,9 +684,8 @@ impl<'tcx> ItemCtxt<'tcx> { }) .filter_map(move |b| bt.map(|bt| (bt, b, bvars))) }) - .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars)); - - from_ty_params.chain(from_where_clauses).collect() + .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars)) + .collect() } 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( tcx, None, - &[], visitor.0, false, None, @@ -852,15 +810,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { if let hir::TyKind::TraitObject(..) = ty.kind { let mut visitor = HirPlaceholderCollector::default(); visitor.visit_item(it); - placeholder_type_error( - tcx, - None, - &[], - visitor.0, - false, - None, - it.kind.descr(), - ); + placeholder_type_error(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: _;`. let mut visitor = HirPlaceholderCollector::default(); 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(_)) => { @@ -897,7 +847,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { // Account for `type T = _;`. let mut visitor = HirPlaceholderCollector::default(); 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) => { @@ -907,7 +857,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { let mut visitor = HirPlaceholderCollector::default(); 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(); 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(..) => {} } @@ -1892,15 +1842,14 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { match tcx.hir().get(hir_id) { TraitItem(hir::TraitItem { kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)), - ident, generics, .. }) - | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), ident, .. }) => { - infer_return_ty_for_fn_sig(tcx, sig, *ident, generics, def_id, &icx) + | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => { + 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 if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = 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.abi, sig.decl, - generics, - Some(ident.span), + Some(generics), None, ) } 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 { kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _), - ident, generics, .. }) => >::ty_of_fn( @@ -1932,16 +1879,13 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { header.unsafety, header.abi, decl, - generics, - Some(ident.span), + Some(generics), None, ), - ForeignItem(&hir::ForeignItem { - kind: ForeignItemKind::Fn(fn_decl, _, _), ident, .. - }) => { + ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { 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() => { @@ -1982,7 +1926,6 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { fn infer_return_ty_for_fn_sig<'tcx>( tcx: TyCtxt<'tcx>, sig: &hir::FnSig<'_>, - ident: Ident, generics: &hir::Generics<'_>, def_id: LocalDefId, icx: &ItemCtxt<'tcx>, @@ -2037,8 +1980,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( sig.header.unsafety, sig.header.abi, sig.decl, - generics, - Some(ident.span), + Some(generics), 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 // well. In the case of parameters declared on a fn or method, we // have to be careful to only iterate over early-bound regions. - let mut index = parent_count + has_own_self as u32; - for param in early_bound_lifetimes_from_generics(tcx, hir_id.owner, ast_generics) { - let region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { - 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 = >::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!(), - } - } + let mut index = parent_count + + has_own_self as u32 + + early_bound_lifetimes_from_generics(tcx, hir_id.owner, ast_generics).count() as u32; // Collect the predicates that were written inline by the user on each // type parameter (e.g., ``). @@ -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); index += 1; - let mut bounds = >::compute_bounds(&icx, param_ty, param.bounds); + let mut bounds = Bounds::default(); // Params are implicitly sized unless a `?Sized` bound is found >::add_implicitly_sized( &icx, &mut bounds, - param.bounds, + &[], Some((param.hir_id, ast_generics.predicates)), param.span, ); @@ -2349,7 +2271,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } GenericParamKind::Const { .. } => { // Bounds on const parameters are currently not possible. - debug_assert!(param.bounds.is_empty()); index += 1; } } @@ -2614,7 +2535,6 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( def_id: DefId, decl: &'tcx hir::FnDecl<'tcx>, abi: abi::Abi, - ident: Ident, ) -> ty::PolyFnSig<'tcx> { let unsafety = if abi == abi::Abi::RustIntrinsic { intrinsic_operation_unsafety(tcx.item_name(def_id)) @@ -2628,8 +2548,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( unsafety, abi, decl, - &hir::Generics::empty(), - Some(ident.span), + None, None, ); diff --git a/src/etc/check_missing_items.py b/src/etc/check_missing_items.py index 89696f39262..343dd0387f4 100644 --- a/src/etc/check_missing_items.py +++ b/src/etc/check_missing_items.py @@ -49,8 +49,6 @@ def check_generic_param(param): ty = param["kind"]["type"] if ty["default"]: check_type(ty["default"]) - for bound in ty["bounds"]: - check_generic_bound(bound) elif "const" in param["kind"]: check_type(param["kind"]["const"]) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 258880ac928..d458deddae3 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -238,9 +238,12 @@ impl Clean> for ty::Region<'_> { } } -impl Clean for hir::WherePredicate<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> WherePredicate { - match *self { +impl Clean> for hir::WherePredicate<'_> { + fn clean(&self, cx: &mut DocContext<'_>) -> Option { + if !self.in_where_clause() { + return None; + } + Some(match *self { hir::WherePredicate::BoundPredicate(ref wbp) => { let bound_params = wbp .bound_generic_params @@ -250,11 +253,7 @@ impl Clean for hir::WherePredicate<'_> { // Higher-ranked lifetimes can't have bounds. assert_matches!( param, - hir::GenericParam { - kind: hir::GenericParamKind::Lifetime { .. }, - bounds: [], - .. - } + hir::GenericParam { kind: hir::GenericParamKind::Lifetime { .. }, .. } ); Lifetime(param.name.ident().name) }) @@ -275,7 +274,7 @@ impl Clean for hir::WherePredicate<'_> { lhs: wrp.lhs_ty.clean(cx), rhs: wrp.rhs_ty.clean(cx).into(), }, - } + }) } } @@ -456,44 +455,75 @@ impl Clean for ty::GenericParamDef { } } -impl Clean for hir::GenericParam<'_> { - fn clean(&self, cx: &mut DocContext<'_>) -> GenericParamDef { - let (name, kind) = match self.kind { - hir::GenericParamKind::Lifetime { .. } => { - let outlives = self - .bounds +fn clean_generic_param( + cx: &mut DocContext<'_>, + generics: Option<&hir::Generics<'_>>, + param: &hir::GenericParam<'_>, +) -> GenericParamDef { + let (name, kind) = match param.kind { + hir::GenericParamKind::Lifetime { .. } => { + let outlives = if let Some(generics) = generics { + generics + .predicates .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 { hir::GenericBound::Outlives(lt) => lt.clean(cx), _ => panic!(), }) - .collect(); - (self.name.ident().name, GenericParamDefKind::Lifetime { outlives }) - } - hir::GenericParamKind::Type { ref default, synthetic } => ( - self.name.ident().name, + .collect() + } else { + Vec::new() + }; + (param.name.ident().name, GenericParamDefKind::Lifetime { outlives }) + } + hir::GenericParamKind::Type { ref default, synthetic } => { + 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 { - did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), - bounds: self.bounds.iter().filter_map(|x| x.clean(cx)).collect(), + did: did.to_def_id(), + bounds, default: default.map(|t| t.clean(cx)).map(Box::new), synthetic, }, - ), - hir::GenericParamKind::Const { ref ty, default } => ( - self.name.ident().name, - GenericParamDefKind::Const { - did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), - ty: Box::new(ty.clean(cx)), - default: default.map(|ct| { - let def_id = cx.tcx.hir().local_def_id(ct.hir_id); - Box::new(ty::Const::from_anon_const(cx.tcx, def_id).to_string()) - }), - }, - ), - }; + ) + } + hir::GenericParamKind::Const { ref ty, default } => ( + param.name.ident().name, + GenericParamDefKind::Const { + did: cx.tcx.hir().local_def_id(param.hir_id).to_def_id(), + ty: Box::new(ty.clean(cx)), + default: default.map(|ct| { + let def_id = cx.tcx.hir().local_def_id(ct.hir_id); + Box::new(ty::Const::from_anon_const(cx.tcx, def_id).to_string()) + }), + }, + ), + }; - GenericParamDef { name, kind } - } + GenericParamDef { name, kind } } impl Clean for hir::Generics<'_> { @@ -524,7 +554,7 @@ impl Clean for hir::Generics<'_> { .iter() .filter(|param| is_impl_trait(param)) .map(|param| { - let param: GenericParamDef = param.clean(cx); + let param = clean_generic_param(cx, Some(self), param); match param.kind { GenericParamDefKind::Lifetime { .. } => unreachable!(), GenericParamDefKind::Type { did, ref bounds, .. } => { @@ -538,14 +568,14 @@ impl Clean for hir::Generics<'_> { 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)) { - let p = p.clean(cx); + let p = clean_generic_param(cx, Some(self), p); params.push(p); } params.extend(impl_trait_params); let mut generics = Generics { 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 @@ -954,7 +984,11 @@ impl Clean for hir::PolyTraitRef<'_> { fn clean(&self, cx: &mut DocContext<'_>) -> PolyTrait { PolyTrait { 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 for hir::BareFnTy<'_> { fn clean(&self, cx: &mut DocContext<'_>) -> BareFunctionDecl { let (generic_params, decl) = enter_impl_trait(cx, |cx| { // 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 decl = clean_fn_decl_with_args(cx, self.decl, args); (generic_params, decl) diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 06c63ec97d7..b5502309560 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; 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::ty::TyCtxt; use rustc_span::Span; @@ -100,16 +100,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { self.tcx.hir() } - fn visit_generic_param(&mut self, p: &'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_generic_param(&mut self, _: &'tcx GenericParam<'tcx>) {} fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) { self.handle_path(path, None); diff --git a/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr b/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr new file mode 100644 index 00000000000..29ed654f238 --- /dev/null +++ b/src/test/ui/regions/regions-free-region-outlives-static-outlives-free-region.stderr @@ -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 + diff --git a/src/test/ui/regions/regions-static-bound-rpass.stderr b/src/test/ui/regions/regions-static-bound-rpass.stderr new file mode 100644 index 00000000000..202c56a5456 --- /dev/null +++ b/src/test/ui/regions/regions-static-bound-rpass.stderr @@ -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 + diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 4ec7c2362f0..662a561f171 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -139,28 +139,35 @@ fn check_fn_inner<'tcx>( .iter() .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); for typ in types { - for bound in typ.bounds { - let mut visitor = RefVisitor::new(cx); - walk_param_bound(&mut visitor, bound); - if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { - return; + 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; } - if let GenericBound::Trait(ref trait_ref, _) = *bound { - let params = &trait_ref - .trait_ref - .path - .segments - .last() - .expect("a path must have at least one segment") - .args; - if let Some(params) = *params { - let lifetimes = params.args.iter().filter_map(|arg| match arg { - GenericArg::Lifetime(lt) => Some(lt), - _ => None, - }); - for bound in lifetimes { - if bound.name != LifetimeName::Static && !bound.is_elided() { - return; + + for bound in pred.bounds { + let mut visitor = RefVisitor::new(cx); + walk_param_bound(&mut visitor, bound); + if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { + return; + } + if let GenericBound::Trait(ref trait_ref, _) = *bound { + let params = &trait_ref + .trait_ref + .path + .segments + .last() + .expect("a path must have at least one segment") + .args; + if let Some(params) = *params { + let lifetimes = params.args.iter().filter_map(|arg| match arg { + GenericArg::Lifetime(lt) => Some(lt), + _ => None, + }); + for bound in lifetimes { + if bound.name != LifetimeName::Static && !bound.is_elided() { + return; + } } } } @@ -322,9 +329,7 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { let mut allowed_lts = FxHashSet::default(); for par in named_generics.iter() { 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); diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index c388d2854cc..3d1b2ee925b 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -8,8 +8,7 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{ - GenericBound, Generics, Item, ItemKind, Node, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind, - WherePredicate, + GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -219,30 +218,19 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { return; } - let mut map = FxHashMap::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::>(); - map.insert(*ident, res); - } - } - + let mut map = FxHashMap::<_, Vec<_>>::default(); for predicate in gen.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if !bound_predicate.span.from_expansion(); if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; if let Some(segment) = segments.first(); - if let Some(trait_resolutions_direct) = map.get(&segment.ident); then { - for (res_where, _, _) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) { - if let Some((_, _, span_direct)) = trait_resolutions_direct + for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) { + let trait_resolutions_direct = map.entry(segment.ident).or_default(); + if let Some((_, span_direct)) = trait_resolutions_direct .iter() - .find(|(res_direct, _, _)| *res_direct == res_where) { + .find(|(res_direct, _)| *res_direct == res_where) { span_lint_and_help( cx, TRAIT_DUPLICATION_IN_BOUNDS, @@ -252,6 +240,9 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { "consider removing this trait bound", ); } + else { + trait_resolutions_direct.push((res_where, span_where)) + } } } } diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs index 7c06906293b..f35f44eda56 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -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 GenericParamKind::Type { synthetic, .. } = generic_param.kind; 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 { - Some(generic_param.bounds) + Some(pred.bounds) } else { None }