From ee7e717322f83d4b055e15b9948c07dd59118a5c Mon Sep 17 00:00:00 2001
From: Lukas Markeffsky <@>
Date: Wed, 14 Jun 2023 08:03:35 +0000
Subject: [PATCH] tweak suggestion for argument-position `impl ?Sized`

---
 compiler/rustc_middle/src/ty/diagnostics.rs | 41 +++++++++++++++------
 tests/ui/trait-bounds/apit-unsized.rs       |  4 ++
 tests/ui/trait-bounds/apit-unsized.stderr   | 41 +++++++++++++++++++++
 3 files changed, 74 insertions(+), 12 deletions(-)
 create mode 100644 tests/ui/trait-bounds/apit-unsized.rs
 create mode 100644 tests/ui/trait-bounds/apit-unsized.stderr

diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 9c948dba1e4..1c80692c306 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -14,8 +14,8 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnostic
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
-use rustc_hir::WherePredicate;
-use rustc_span::Span;
+use rustc_hir::{PredicateOrigin, WherePredicate};
+use rustc_span::{BytePos, Span};
 use rustc_type_ir::sty::TyKind::*;
 
 impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
@@ -156,10 +156,11 @@ enum SuggestChangingConstraintsMessage<'a> {
     RestrictBoundFurther,
     RestrictType { ty: &'a str },
     RestrictTypeFurther { ty: &'a str },
-    RemovingQSized,
+    RemoveMaybeUnsized,
+    ReplaceMaybeUnsizedWithSized,
 }
 
-fn suggest_removing_unsized_bound(
+fn suggest_changing_unsized_bound(
     generics: &hir::Generics<'_>,
     suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
     param: &hir::GenericParam<'_>,
@@ -183,12 +184,25 @@ fn suggest_removing_unsized_bound(
             if poly.trait_ref.trait_def_id() != def_id {
                 continue;
             }
-            let sp = generics.span_for_bound_removal(where_pos, pos);
-            suggestions.push((
-                sp,
-                String::new(),
-                SuggestChangingConstraintsMessage::RemovingQSized,
-            ));
+            if predicate.origin == PredicateOrigin::ImplTrait && predicate.bounds.len() == 1 {
+                // For `impl ?Sized` with no other bounds, suggest `impl Sized` instead.
+                let bound_span = bound.span();
+                if bound_span.can_be_used_for_suggestions() {
+                    let question_span = bound_span.with_hi(bound_span.lo() + BytePos(1));
+                    suggestions.push((
+                        question_span,
+                        String::new(),
+                        SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized,
+                    ));
+                }
+            } else {
+                let sp = generics.span_for_bound_removal(where_pos, pos);
+                suggestions.push((
+                    sp,
+                    String::new(),
+                    SuggestChangingConstraintsMessage::RemoveMaybeUnsized,
+                ));
+            }
         }
     }
 }
@@ -245,7 +259,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, def_id);
+                suggest_changing_unsized_bound(generics, &mut suggestions, param, def_id);
             }
         }
 
@@ -395,9 +409,12 @@ pub fn suggest_constraining_type_params<'a>(
             SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
                 Cow::from(format!("consider further restricting type parameter `{}`", ty))
             }
-            SuggestChangingConstraintsMessage::RemovingQSized => {
+            SuggestChangingConstraintsMessage::RemoveMaybeUnsized => {
                 Cow::from("consider removing the `?Sized` bound to make the type parameter `Sized`")
             }
+            SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized => {
+                Cow::from("consider replacing `?Sized` with `Sized`")
+            }
         };
 
         err.span_suggestion_verbose(span, msg, suggestion, applicability);
diff --git a/tests/ui/trait-bounds/apit-unsized.rs b/tests/ui/trait-bounds/apit-unsized.rs
new file mode 100644
index 00000000000..469d6a6345e
--- /dev/null
+++ b/tests/ui/trait-bounds/apit-unsized.rs
@@ -0,0 +1,4 @@
+fn foo(_: impl Iterator<Item = i32> + ?Sized) {} //~ ERROR [E0277]
+fn bar(_: impl ?Sized) {} //~ ERROR [E0277]
+
+fn main() {}
diff --git a/tests/ui/trait-bounds/apit-unsized.stderr b/tests/ui/trait-bounds/apit-unsized.stderr
new file mode 100644
index 00000000000..7a4a2274d44
--- /dev/null
+++ b/tests/ui/trait-bounds/apit-unsized.stderr
@@ -0,0 +1,41 @@
+error[E0277]: the size for values of type `impl Iterator<Item = i32> + ?Sized` cannot be known at compilation time
+  --> $DIR/apit-unsized.rs:1:8
+   |
+LL | fn foo(_: impl Iterator<Item = i32> + ?Sized) {}
+   |        ^  ---------------------------------- this type parameter needs to be `std::marker::Sized`
+   |        |
+   |        doesn't have a size known at compile-time
+   |
+   = help: unsized fn params are gated as an unstable feature
+help: consider removing the `?Sized` bound to make the type parameter `Sized`
+   |
+LL - fn foo(_: impl Iterator<Item = i32> + ?Sized) {}
+LL + fn foo(_: impl Iterator<Item = i32>) {}
+   |
+help: function arguments must have a statically known size, borrowed types always have a known size
+   |
+LL | fn foo(_: &impl Iterator<Item = i32> + ?Sized) {}
+   |           +
+
+error[E0277]: the size for values of type `impl ?Sized` cannot be known at compilation time
+  --> $DIR/apit-unsized.rs:2:8
+   |
+LL | fn bar(_: impl ?Sized) {}
+   |        ^  ----------- this type parameter needs to be `std::marker::Sized`
+   |        |
+   |        doesn't have a size known at compile-time
+   |
+   = help: unsized fn params are gated as an unstable feature
+help: consider replacing `?Sized` with `Sized`
+   |
+LL - fn bar(_: impl ?Sized) {}
+LL + fn bar(_: impl Sized) {}
+   |
+help: function arguments must have a statically known size, borrowed types always have a known size
+   |
+LL | fn bar(_: &impl ?Sized) {}
+   |           +
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.