about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast/src/ast.rs1
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs5
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/derive.rs1
-rw-r--r--compiler/rustc_expand/src/build.rs1
-rw-r--r--compiler/rustc_expand/src/placeholders.rs1
-rw-r--r--compiler/rustc_hir/src/hir.rs37
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs13
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs13
-rw-r--r--compiler/rustc_typeck/src/check/method/suggest.rs29
-rw-r--r--src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed2
-rw-r--r--src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr4
-rw-r--r--src/test/ui/traits/issue-95898.stderr4
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(&param.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