about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2020-06-16 17:24:16 -0700
committerEsteban Küber <esteban@kuber.com.ar>2020-06-16 17:24:16 -0700
commitaa84b0fa376cd739e082786717cf31faaadeef66 (patch)
tree587457973c5a182e1b4ed9afe46e10f9002b9ee6
parentd2b8e6090cb694898d4481f4a55d5489fa4b279e (diff)
downloadrust-aa84b0fa376cd739e082786717cf31faaadeef66.tar.gz
rust-aa84b0fa376cd739e082786717cf31faaadeef66.zip
Provide `help` when `T: ?Sized` can't be suggested
-rw-r--r--src/librustc_trait_selection/traits/error_reporting/mod.rs54
-rw-r--r--src/test/ui/issues/issue-18919.stderr7
-rw-r--r--src/test/ui/issues/issue-23281.stderr7
-rw-r--r--src/test/ui/suggestions/adt-param-with-implicit-sized-bound.rs11
-rw-r--r--src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr50
-rw-r--r--src/test/ui/unsized/unsized-enum.stderr7
-rw-r--r--src/test/ui/unsized/unsized-inherent-impl-self-type.stderr7
-rw-r--r--src/test/ui/unsized/unsized-struct.stderr7
-rw-r--r--src/test/ui/unsized/unsized-trait-impl-self-type.stderr7
-rw-r--r--src/test/ui/wf/wf-fn-where-clause.stderr7
10 files changed, 154 insertions, 10 deletions
diff --git a/src/librustc_trait_selection/traits/error_reporting/mod.rs b/src/librustc_trait_selection/traits/error_reporting/mod.rs
index a99c14f763f..33da020da8a 100644
--- a/src/librustc_trait_selection/traits/error_reporting/mod.rs
+++ b/src/librustc_trait_selection/traits/error_reporting/mod.rs
@@ -26,7 +26,7 @@ use rustc_middle::ty::{
     TypeFoldable, WithConstness,
 };
 use rustc_session::DiagnosticMessageId;
-use rustc_span::{ExpnKind, Span, DUMMY_SP};
+use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP};
 use std::fmt;
 
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -1740,10 +1740,36 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
                     // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
                     // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
                     // is not.
-                    let mut visitor = FindTypeParam { param: param.name.ident().name, valid: true };
+                    let mut visitor = FindTypeParam {
+                        param: param.name.ident().name,
+                        invalid_spans: vec![],
+                        nested: false,
+                    };
                     visitor.visit_item(item);
-                    if !visitor.valid {
-                        continue;
+                    if !visitor.invalid_spans.is_empty() {
+                        let mut multispan: MultiSpan = param.span.into();
+                        multispan.push_span_label(
+                            param.span,
+                            format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
+                        );
+                        for sp in visitor.invalid_spans {
+                            multispan.push_span_label(
+                                sp,
+                                format!(
+                                    "...if indirection was used here: `Box<{}>`",
+                                    param.name.ident(),
+                                ),
+                            );
+                        }
+                        err.span_help(
+                            multispan,
+                            &format!(
+                                "you could relax the implicit `Sized` bound on `{T}` if it were \
+                                 used through indirection like `&{T}` or `Box<{T}>`",
+                                T = param.name.ident(),
+                            ),
+                        );
+                        return;
                     }
                 }
                 _ => {}
@@ -1782,7 +1808,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
 /// `param: ?Sized` would be a valid constraint.
 struct FindTypeParam {
     param: rustc_span::Symbol,
-    valid: bool,
+    invalid_spans: Vec<Span>,
+    nested: bool,
 }
 
 impl<'v> Visitor<'v> for FindTypeParam {
@@ -1794,15 +1821,24 @@ impl<'v> Visitor<'v> for FindTypeParam {
 
     fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
         match ty.kind {
-            hir::TyKind::Ptr(_) | hir::TyKind::Rptr(..) | hir::TyKind::TraitObject(..) => return,
+            hir::TyKind::Ptr(_) | hir::TyKind::Rptr(..) | hir::TyKind::TraitObject(..) => {}
             hir::TyKind::Path(hir::QPath::Resolved(None, path))
                 if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
             {
-                self.valid = false;
+                if !self.nested {
+                    self.invalid_spans.push(ty.span);
+                }
+            }
+            hir::TyKind::Path(_) => {
+                let prev = self.nested;
+                self.nested = true;
+                hir::intravisit::walk_ty(self, ty);
+                self.nested = prev;
+            }
+            _ => {
+                hir::intravisit::walk_ty(self, ty);
             }
-            _ => {}
         }
-        hir::intravisit::walk_ty(self, ty);
     }
 }
 
diff --git a/src/test/ui/issues/issue-18919.stderr b/src/test/ui/issues/issue-18919.stderr
index 1ea40d0728e..383cdd4979a 100644
--- a/src/test/ui/issues/issue-18919.stderr
+++ b/src/test/ui/issues/issue-18919.stderr
@@ -9,6 +9,13 @@ LL | enum Option<T> {
    |
    = help: the trait `std::marker::Sized` is not implemented for `dyn for<'r> std::ops::Fn(&'r isize) -> isize`
    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>`
+  --> $DIR/issue-18919.rs:7:13
+   |
+LL | enum Option<T> {
+   |             ^ this could be changed to `T: ?Sized`...
+LL |     Some(T),
+   |          - ...if indirection was used here: `Box<T>`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-23281.stderr b/src/test/ui/issues/issue-23281.stderr
index 3b4b8997a70..cffa5236169 100644
--- a/src/test/ui/issues/issue-23281.stderr
+++ b/src/test/ui/issues/issue-23281.stderr
@@ -9,6 +9,13 @@ LL | struct Vec<T> {
    |
    = help: the trait `std::marker::Sized` is not implemented for `(dyn std::ops::Fn() + 'static)`
    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>`
+  --> $DIR/issue-23281.rs:8:12
+   |
+LL | struct Vec<T> {
+   |            ^ this could be changed to `T: ?Sized`...
+LL |     t: T,
+   |        - ...if indirection was used here: `Box<T>`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.rs b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.rs
index a5aae8cc4d8..ef64d799b65 100644
--- a/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.rs
+++ b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.rs
@@ -2,6 +2,7 @@ trait Trait {
     fn func1() -> Struct1<Self>; //~ ERROR E0277
     fn func2<'a>() -> Struct2<'a, Self>; //~ ERROR E0277
     fn func3() -> Struct3<Self>; //~ ERROR E0277
+    fn func4() -> Struct4<Self>; //~ ERROR E0277
 }
 
 struct Struct1<T>{
@@ -14,4 +15,14 @@ struct Struct3<T>{
     _t: T,
 }
 
+struct X<T>(T);
+
+struct Struct4<T>{
+    _t: X<T>,
+}
+
+struct Struct5<T: ?Sized>{
+    _t: X<T>, //~ ERROR E0277
+}
+
 fn main() {}
diff --git a/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr
index a8a3386c66f..ee08f51f802 100644
--- a/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr
+++ b/src/test/ui/suggestions/adt-param-with-implicit-sized-bound.stderr
@@ -1,3 +1,24 @@
+error[E0277]: the size for values of type `T` cannot be known at compilation time
+  --> $DIR/adt-param-with-implicit-sized-bound.rs:25:5
+   |
+LL | struct X<T>(T);
+   |          - required by this bound in `X`
+...
+LL | struct Struct5<T: ?Sized>{
+   |                - this type parameter needs to be `std::marker::Sized`
+LL |     _t: X<T>,
+   |     ^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `std::marker::Sized` is not implemented for `T`
+   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>`
+  --> $DIR/adt-param-with-implicit-sized-bound.rs:18:10
+   |
+LL | struct X<T>(T);
+   |          ^  - ...if indirection was used here: `Box<T>`
+   |          |
+   |          this could be changed to `T: ?Sized`...
+
 error[E0277]: the size for values of type `Self` cannot be known at compilation time
   --> $DIR/adt-param-with-implicit-sized-bound.rs:2:19
    |
@@ -49,11 +70,38 @@ LL | struct Struct3<T>{
    |
    = help: the trait `std::marker::Sized` is not implemented for `Self`
    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>`
+  --> $DIR/adt-param-with-implicit-sized-bound.rs:14:16
+   |
+LL | struct Struct3<T>{
+   |                ^ this could be changed to `T: ?Sized`...
+LL |     _t: T,
+   |         - ...if indirection was used here: `Box<T>`
 help: consider further restricting `Self`
    |
 LL |     fn func3() -> Struct3<Self> where Self: std::marker::Sized;
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error[E0277]: the size for values of type `Self` cannot be known at compilation time
+  --> $DIR/adt-param-with-implicit-sized-bound.rs:5:19
+   |
+LL |     fn func4() -> Struct4<Self>;
+   |                   ^^^^^^^^^^^^^ doesn't have a size known at compile-time
+...
+LL | struct Struct4<T>{
+   |                - required by this bound in `Struct4`
+   |
+   = help: the trait `std::marker::Sized` is not implemented for `Self`
+   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+help: consider further restricting `Self`
+   |
+LL |     fn func4() -> Struct4<Self> where Self: std::marker::Sized;
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL | struct Struct4<T: ?Sized>{
+   |                 ^^^^^^^^
+
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unsized/unsized-enum.stderr b/src/test/ui/unsized/unsized-enum.stderr
index f43d00f9739..1908aee25ea 100644
--- a/src/test/ui/unsized/unsized-enum.stderr
+++ b/src/test/ui/unsized/unsized-enum.stderr
@@ -11,6 +11,13 @@ LL | fn foo2<T: ?Sized>() { not_sized::<Foo<T>>() }
    |
    = help: the trait `std::marker::Sized` is not implemented for `T`
    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+help: you could relax the implicit `Sized` bound on `U` if it were used through indirection like `&U` or `Box<U>`
+  --> $DIR/unsized-enum.rs:4:10
+   |
+LL | enum Foo<U> { FooSome(U), FooNone }
+   |          ^            - ...if indirection was used here: `Box<U>`
+   |          |
+   |          this could be changed to `U: ?Sized`...
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr b/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr
index 808c9c583d4..e0f077d66f9 100644
--- a/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr
+++ b/src/test/ui/unsized/unsized-inherent-impl-self-type.stderr
@@ -11,6 +11,13 @@ LL | impl<X: ?Sized> S5<X> {
    |
    = help: the trait `std::marker::Sized` is not implemented for `X`
    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+help: you could relax the implicit `Sized` bound on `Y` if it were used through indirection like `&Y` or `Box<Y>`
+  --> $DIR/unsized-inherent-impl-self-type.rs:5:11
+   |
+LL | struct S5<Y>(Y);
+   |           ^  - ...if indirection was used here: `Box<Y>`
+   |           |
+   |           this could be changed to `Y: ?Sized`...
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/unsized/unsized-struct.stderr b/src/test/ui/unsized/unsized-struct.stderr
index 42fc5569ece..d92d1d9113e 100644
--- a/src/test/ui/unsized/unsized-struct.stderr
+++ b/src/test/ui/unsized/unsized-struct.stderr
@@ -11,6 +11,13 @@ LL | fn foo2<T: ?Sized>() { not_sized::<Foo<T>>() }
    |
    = help: the trait `std::marker::Sized` is not implemented for `T`
    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>`
+  --> $DIR/unsized-struct.rs:4:12
+   |
+LL | struct Foo<T> { data: T }
+   |            ^          - ...if indirection was used here: `Box<T>`
+   |            |
+   |            this could be changed to `T: ?Sized`...
 
 error[E0277]: the size for values of type `T` cannot be known at compilation time
   --> $DIR/unsized-struct.rs:13:24
diff --git a/src/test/ui/unsized/unsized-trait-impl-self-type.stderr b/src/test/ui/unsized/unsized-trait-impl-self-type.stderr
index c2b2fe40ce6..73c5439da53 100644
--- a/src/test/ui/unsized/unsized-trait-impl-self-type.stderr
+++ b/src/test/ui/unsized/unsized-trait-impl-self-type.stderr
@@ -11,6 +11,13 @@ LL | impl<X: ?Sized> T3<X> for S5<X> {
    |
    = help: the trait `std::marker::Sized` is not implemented for `X`
    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+help: you could relax the implicit `Sized` bound on `Y` if it were used through indirection like `&Y` or `Box<Y>`
+  --> $DIR/unsized-trait-impl-self-type.rs:8:11
+   |
+LL | struct S5<Y>(Y);
+   |           ^  - ...if indirection was used here: `Box<Y>`
+   |           |
+   |           this could be changed to `Y: ?Sized`...
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/wf/wf-fn-where-clause.stderr b/src/test/ui/wf/wf-fn-where-clause.stderr
index 59a4f9bad9d..731d31ac34f 100644
--- a/src/test/ui/wf/wf-fn-where-clause.stderr
+++ b/src/test/ui/wf/wf-fn-where-clause.stderr
@@ -23,6 +23,13 @@ LL | struct Vec<T> {
    |
    = help: the trait `std::marker::Sized` is not implemented for `(dyn std::marker::Copy + 'static)`
    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box<T>`
+  --> $DIR/wf-fn-where-clause.rs:16:12
+   |
+LL | struct Vec<T> {
+   |            ^ this could be changed to `T: ?Sized`...
+LL |     t: T,
+   |        - ...if indirection was used here: `Box<T>`
 
 error[E0038]: the trait `std::marker::Copy` cannot be made into an object
   --> $DIR/wf-fn-where-clause.rs:12:16