diff options
| -rw-r--r-- | compiler/rustc_ast/src/ast.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_ast/src/mut_visit.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_ast_lowering/src/lib.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_builtin_macros/src/derive.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/build.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/placeholders.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_hir/src/hir.rs | 37 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/diagnostics.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/generics.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/method/suggest.rs | 29 | ||||
| -rw-r--r-- | src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed | 2 | ||||
| -rw-r--r-- | src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr | 4 | ||||
| -rw-r--r-- | src/test/ui/traits/issue-95898.stderr | 4 |
13 files changed, 63 insertions, 53 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a310828b9fb..cdcd221e811 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -397,6 +397,7 @@ pub struct GenericParam { pub bounds: GenericBounds, pub is_placeholder: bool, pub kind: GenericParamKind, + pub colon_span: Option<Span>, } impl GenericParam { diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index cba49835f69..d7b1bc6a7f5 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -867,9 +867,12 @@ pub fn noop_flat_map_generic_param<T: MutVisitor>( mut param: GenericParam, vis: &mut T, ) -> SmallVec<[GenericParam; 1]> { - let GenericParam { id, ident, attrs, bounds, kind, is_placeholder: _ } = &mut param; + let GenericParam { id, ident, attrs, bounds, kind, colon_span, is_placeholder: _ } = &mut param; vis.visit_id(id); vis.visit_ident(ident); + if let Some(ref mut colon_span) = colon_span { + vis.visit_span(colon_span); + } visit_thin_attrs(attrs, vis); visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis)); match kind { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 6abb3d03e83..d433775f85c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -707,6 +707,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: self.lower_span(ident.span), pure_wrt_drop: false, kind: hir::GenericParamKind::Lifetime { kind }, + colon_span: None, }) } @@ -1304,6 +1305,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { pure_wrt_drop: false, span: self.lower_span(span), kind: hir::GenericParamKind::Type { default: None, synthetic: true }, + colon_span: None, }); if let Some(preds) = self.lower_generic_bound_predicate( ident, @@ -1396,6 +1398,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span, pure_wrt_drop: false, kind: hir::GenericParamKind::Lifetime { kind }, + colon_span: None, } }, )); @@ -1735,6 +1738,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span, pure_wrt_drop: false, kind: hir::GenericParamKind::Lifetime { kind }, + colon_span: None, } })); debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params); @@ -2006,6 +2010,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: self.lower_span(param.span()), pure_wrt_drop: self.sess.contains_name(¶m.attrs, sym::may_dangle), kind, + colon_span: param.colon_span.map(|s| self.lower_span(s)), } } diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 61681ec66a4..391c46d1813 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -95,6 +95,7 @@ fn dummy_annotatable() -> Annotatable { bounds: Default::default(), is_placeholder: false, kind: GenericParamKind::Lifetime, + colon_span: None, }) } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index b8ed75cb6bb..301c67f7026 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -113,6 +113,7 @@ impl<'a> ExtCtxt<'a> { bounds, kind: ast::GenericParamKind::Type { default }, is_placeholder: false, + colon_span: None, } } diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 15af5fdc5f8..0d5d6ee0794 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -149,6 +149,7 @@ pub fn placeholder( ident, is_placeholder: true, kind: ast::GenericParamKind::Lifetime, + colon_span: None, } }]), AstFragmentKind::Params => AstFragment::Params(smallvec![ast::Param { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a5c8088a279..eef9947f867 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -17,7 +17,7 @@ use rustc_error_messages::MultiSpan; use rustc_index::vec::IndexVec; use rustc_macros::HashStable_Generic; use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::{SourceMap, Spanned}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{def_id::LocalDefId, BytePos, Span, DUMMY_SP}; use rustc_target::asm::InlineAsmRegOrRegClass; @@ -499,6 +499,7 @@ pub struct GenericParam<'hir> { pub span: Span, pub pure_wrt_drop: bool, pub kind: GenericParamKind<'hir>, + pub colon_span: Option<Span>, } impl<'hir> GenericParam<'hir> { @@ -515,40 +516,6 @@ impl<'hir> GenericParam<'hir> { pub fn is_elided_lifetime(&self) -> bool { matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided }) } - - /// Returns the span of `:` after a generic parameter. - /// - /// For example: - /// - /// ```text - /// fn a<T:>() - /// ^ - /// | here - /// here | - /// v - /// fn b<T :>() - /// - /// fn c<T - /// - /// :>() - /// ^ - /// | - /// here - /// ``` - pub fn colon_span_for_suggestions(&self, source_map: &SourceMap) -> Option<Span> { - let sp = source_map - .span_extend_while(self.span.shrink_to_hi(), |c| c.is_whitespace() || c == ':') - .ok()?; - - let snippet = source_map.span_to_snippet(sp).ok()?; - let offset = snippet.find(':')?; - - let colon_sp = sp - .with_lo(BytePos(sp.lo().0 + offset as u32)) - .with_hi(BytePos(sp.lo().0 + (offset + ':'.len_utf8()) as u32)); - - Some(colon_sp) - } } #[derive(Default)] diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index aeeab677ae0..f53dc0000ca 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -363,6 +363,19 @@ pub fn suggest_constraining_type_params<'a>( continue; } + // If user has provided a colon, don't suggest adding another: + // + // fn foo<T:>(t: T) { ... } + // - insert: consider restricting this type parameter with `T: Foo` + if let Some(colon_span) = param.colon_span { + suggestions.push(( + colon_span.shrink_to_hi(), + format!(" {}", constraint), + SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, + )); + continue; + } + // If user hasn't provided any bounds, suggest adding a new one: // // fn foo<T>(t: T) { ... } diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 29fe2b76101..8081bac7cfd 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -30,8 +30,10 @@ impl<'a> Parser<'a> { let ident = self.parse_ident()?; // Parse optional colon and param bounds. + let mut colon_span = None; let bounds = if self.eat(&token::Colon) { - self.parse_generic_bounds(Some(self.prev_token.span))? + colon_span = Some(self.prev_token.span); + self.parse_generic_bounds(colon_span)? } else { Vec::new() }; @@ -45,6 +47,7 @@ impl<'a> Parser<'a> { bounds, kind: GenericParamKind::Type { default }, is_placeholder: false, + colon_span, }) } @@ -69,6 +72,7 @@ impl<'a> Parser<'a> { bounds: Vec::new(), kind: GenericParamKind::Const { ty, kw_span: const_span, default }, is_placeholder: false, + colon_span: None, }) } @@ -97,10 +101,10 @@ impl<'a> Parser<'a> { let param = if this.check_lifetime() { let lifetime = this.expect_lifetime(); // Parse lifetime parameter. - let bounds = if this.eat(&token::Colon) { - this.parse_lt_param_bounds() + let (colon_span, bounds) = if this.eat(&token::Colon) { + (Some(this.prev_token.span), this.parse_lt_param_bounds()) } else { - Vec::new() + (None, Vec::new()) }; Some(ast::GenericParam { ident: lifetime.ident, @@ -109,6 +113,7 @@ impl<'a> Parser<'a> { bounds, kind: ast::GenericParamKind::Lifetime, is_placeholder: false, + colon_span, }) } else if this.check_keyword(kw::Const) { // Parse const parameter. diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index e931dadbee7..640dccf66b0 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -1868,18 +1868,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // instead we suggest `T: Foo + Bar` in that case. match hir.get(id) { Node::GenericParam(param) => { - let impl_trait = matches!( - param.kind, - hir::GenericParamKind::Type { synthetic: true, .. }, - ); + enum Introducer { + Plus, + Colon, + Nothing, + } let ast_generics = hir.get_generics(id.owner).unwrap(); - let (sp, has_bounds) = if let Some(span) = + let (sp, mut introducer) = if let Some(span) = ast_generics.bounds_span_for_suggestions(def_id) { - (span, true) + (span, Introducer::Plus) + } else if let Some(colon_span) = param.colon_span { + (colon_span.shrink_to_hi(), Introducer::Nothing) } else { - (hir.span(id).shrink_to_hi(), false) + (param.span.shrink_to_hi(), Introducer::Colon) }; + if matches!( + param.kind, + hir::GenericParamKind::Type { synthetic: true, .. }, + ) { + introducer = Introducer::Plus + } let trait_def_ids: FxHashSet<DefId> = ast_generics .bounds_for_param(def_id) .flat_map(|bp| bp.bounds.iter()) @@ -1895,7 +1904,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { candidates.iter().map(|t| { format!( "{} {}", - if has_bounds || impl_trait { " +" } else { ":" }, + match introducer { + Introducer::Plus => " +", + Introducer::Colon => ":", + Introducer::Nothing => "", + }, self.tcx.def_path_str(t.def_id), ) }), diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed index 10cff3a27e4..ba1b745ba84 100644 --- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed @@ -70,7 +70,7 @@ where } #[rustfmt::skip] -fn existing_colon<T: Copy:>(t: T) { +fn existing_colon<T: Copy>(t: T) { //~^ HELP consider restricting type parameter `T` [t, t]; //~ use of moved value: `t` } diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr index 150fa5d92d6..2353cd079a3 100644 --- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr @@ -171,8 +171,8 @@ LL | [t, t]; | help: consider restricting type parameter `T` | -LL | fn existing_colon<T: Copy:>(t: T) { - | ++++++ +LL | fn existing_colon<T: Copy>(t: T) { + | ++++ error: aborting due to 11 previous errors diff --git a/src/test/ui/traits/issue-95898.stderr b/src/test/ui/traits/issue-95898.stderr index 0b06b9b6e01..0a58ad4b663 100644 --- a/src/test/ui/traits/issue-95898.stderr +++ b/src/test/ui/traits/issue-95898.stderr @@ -7,8 +7,8 @@ LL | t.clone(); = help: items from traits can only be used if the type parameter is bounded by the trait help: the following trait defines an item `clone`, perhaps you need to restrict type parameter `T` with it: | -LL | fn foo<T: Clone:>(t: T) { - | +++++++ +LL | fn foo<T: Clone>(t: T) { + | +++++ error: aborting due to previous error |
