From e13eb37eeb3ccb447f33496fe86d77498558d2d5 Mon Sep 17 00:00:00 2001 From: Gurinder Singh Date: Sun, 14 Jul 2024 17:46:25 +0530 Subject: [PATCH] Fix malformed suggestion for repeated maybe unsized bounds --- compiler/rustc_hir/src/hir.rs | 24 ++- compiler/rustc_middle/src/ty/diagnostics.rs | 69 +++++-- ...tionf-for-repeated-unsized-bound-127441.rs | 39 ++++ ...f-for-repeated-unsized-bound-127441.stderr | 192 ++++++++++++++++++ 4 files changed, 295 insertions(+), 29 deletions(-) create mode 100644 tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.rs create mode 100644 tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.stderr diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index d57fad6ba4c2d..0f1b2bec6c648 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -723,7 +723,7 @@ impl<'hir> Generics<'hir> { ) } - fn span_for_predicate_removal(&self, pos: usize) -> Span { + pub fn span_for_predicate_removal(&self, pos: usize) -> Span { let predicate = &self.predicates[pos]; let span = predicate.span(); @@ -766,15 +766,21 @@ impl<'hir> Generics<'hir> { 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()) + let bound_span = bounds[bound_pos].span(); + if bound_pos < bounds.len() - 1 { + // If there's another bound after the current bound + // include the following '+' e.g.: + // + // `T: Foo + CurrentBound + Bar` + // ^^^^^^^^^^^^^^^ + bound_span.to(bounds[bound_pos + 1].span().shrink_to_lo()) } else { - // where T: Bar + ?Sized, Foo: Bar, - // ^^^^^^^^^ - bounds[bound_pos - 1].span().shrink_to_hi().to(span) + // If the current bound is the last bound + // include the preceding '+' E.g.: + // + // `T: Foo + Bar + CurrentBound` + // ^^^^^^^^^^^^^^^ + bound_span.with_lo(bounds[bound_pos - 1].span().hi()) } } } diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 4bf2233799130..3a5cb22be388f 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -188,31 +188,60 @@ fn suggest_changing_unsized_bound( continue; }; - 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; - } - 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(( + let unsized_bounds = predicate + .bounds + .iter() + .enumerate() + .filter(|(_, bound)| { + if let hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound + && poly.trait_ref.trait_def_id() == def_id + { + true + } else { + false + } + }) + .collect::>(); + + if unsized_bounds.is_empty() { + continue; + } + + let mut push_suggestion = |sp, msg| suggestions.push((sp, String::new(), msg)); + + if predicate.bounds.len() == unsized_bounds.len() { + // All the bounds are unsized bounds, e.g. + // `T: ?Sized + ?Sized` or `_: impl ?Sized + ?Sized`, + // so in this case: + // - if it's an impl trait predicate suggest changing the + // the first bound to sized and removing the rest + // - Otherwise simply suggest removing the entire predicate + if predicate.origin == PredicateOrigin::ImplTrait { + let first_bound = unsized_bounds[0].1; + let first_bound_span = first_bound.span(); + if first_bound_span.can_be_used_for_suggestions() { + let question_span = + first_bound_span.with_hi(first_bound_span.lo() + BytePos(1)); + push_suggestion( question_span, - String::new(), SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized, - )); + ); + + for (pos, _) in unsized_bounds.iter().skip(1) { + let sp = generics.span_for_bound_removal(where_pos, *pos); + push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized); + } } } else { + let sp = generics.span_for_predicate_removal(where_pos); + push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized); + } + } else { + // Some of the bounds are other than unsized. + // So push separate removal suggestion for each unsized bound + for (pos, _) in unsized_bounds { let sp = generics.span_for_bound_removal(where_pos, pos); - suggestions.push(( - sp, - String::new(), - SuggestChangingConstraintsMessage::RemoveMaybeUnsized, - )); + push_suggestion(sp, SuggestChangingConstraintsMessage::RemoveMaybeUnsized); } } } diff --git a/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.rs b/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.rs new file mode 100644 index 0000000000000..e6d7f74880fb2 --- /dev/null +++ b/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.rs @@ -0,0 +1,39 @@ +// Regression test for #127441 + +// Tests that we make the correct suggestion +// in case there are more than one `?Sized` +// bounds on a function parameter + +use std::fmt::Debug; + +fn foo1(a: T) {} +//~^ ERROR he size for values of type `T` cannot be known at compilation time + +fn foo2(a: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR the size for values of type `T` cannot be known at compilation time + +fn foo3(a: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR he size for values of type `T` cannot be known at compilation time + +fn foo4(a: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR the size for values of type `T` cannot be known at compilation time + +fn foo5(_: impl ?Sized) {} +//~^ ERROR the size for values of type `impl ?Sized` cannot be known at compilation time + +fn foo6(_: impl ?Sized + ?Sized) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR the size for values of type `impl ?Sized + ?Sized` cannot be known at compilation tim + +fn foo7(_: impl ?Sized + ?Sized + Debug) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR the size for values of type `impl ?Sized + ?Sized + Debug` cannot be known at compilation time + +fn foo8(_: impl ?Sized + Debug + ?Sized ) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| ERROR the size for values of type `impl ?Sized + Debug + ?Sized` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.stderr b/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.stderr new file mode 100644 index 0000000000000..3e8f45ee9fc26 --- /dev/null +++ b/tests/ui/trait-bounds/bad-suggestionf-for-repeated-unsized-bound-127441.stderr @@ -0,0 +1,192 @@ +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:12:12 + | +LL | fn foo2(a: T) {} + | ^^^^^^ ^^^^^^ + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:16:12 + | +LL | fn foo3(a: T) {} + | ^^^^^^ ^^^^^^ + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:20:12 + | +LL | fn foo4(a: T) {} + | ^^^^^^ ^^^^^^ + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:27:17 + | +LL | fn foo6(_: impl ?Sized + ?Sized) {} + | ^^^^^^ ^^^^^^ + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:31:17 + | +LL | fn foo7(_: impl ?Sized + ?Sized + Debug) {} + | ^^^^^^ ^^^^^^ + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:35:17 + | +LL | fn foo8(_: impl ?Sized + Debug + ?Sized ) {} + | ^^^^^^ ^^^^^^ + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:9:20 + | +LL | fn foo1(a: T) {} + | - ^ doesn't have a size known at compile-time + | | + | this type parameter needs to be `Sized` + | + = help: unsized fn params are gated as an unstable feature +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn foo1(a: T) {} +LL + fn foo1(a: T) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo1(a: &T) {} + | + + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:12:29 + | +LL | fn foo2(a: T) {} + | - ^ doesn't have a size known at compile-time + | | + | this type parameter needs to be `Sized` + | + = help: unsized fn params are gated as an unstable feature +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn foo2(a: T) {} +LL + fn foo2(a: T) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo2(a: &T) {} + | + + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:16:37 + | +LL | fn foo3(a: T) {} + | - ^ doesn't have a size known at compile-time + | | + | this type parameter needs to be `Sized` + | + = help: unsized fn params are gated as an unstable feature +help: consider restricting type parameters + | +LL - fn foo3(a: T) {} +LL + fn foo3(a: T) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo3(a: &T) {} + | + + +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:20:38 + | +LL | fn foo4(a: T) {} + | - ^ doesn't have a size known at compile-time + | | + | this type parameter needs to be `Sized` + | + = help: unsized fn params are gated as an unstable feature +help: consider restricting type parameters + | +LL - fn foo4(a: T) {} +LL + fn foo4(a: T) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo4(a: &T) {} + | + + +error[E0277]: the size for values of type `impl ?Sized` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:24:9 + | +LL | fn foo5(_: impl ?Sized) {} + | ^ ----------- this type parameter needs to be `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 foo5(_: impl ?Sized) {} +LL + fn foo5(_: impl Sized) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo5(_: &impl ?Sized) {} + | + + +error[E0277]: the size for values of type `impl ?Sized + ?Sized` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:27:9 + | +LL | fn foo6(_: impl ?Sized + ?Sized) {} + | ^ -------------------- this type parameter needs to be `Sized` + | | + | doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider restricting type parameters + | +LL - fn foo6(_: impl ?Sized + ?Sized) {} +LL + fn foo6(_: impl Sized) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo6(_: &impl ?Sized + ?Sized) {} + | + + +error[E0277]: the size for values of type `impl ?Sized + ?Sized + Debug` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:31:9 + | +LL | fn foo7(_: impl ?Sized + ?Sized + Debug) {} + | ^ ---------------------------- this type parameter needs to be `Sized` + | | + | doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider restricting type parameters + | +LL - fn foo7(_: impl ?Sized + ?Sized + Debug) {} +LL + fn foo7(_: impl Debug) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo7(_: &impl ?Sized + ?Sized + Debug) {} + | + + +error[E0277]: the size for values of type `impl ?Sized + Debug + ?Sized` cannot be known at compilation time + --> $DIR/bad-suggestionf-for-repeated-unsized-bound-127441.rs:35:9 + | +LL | fn foo8(_: impl ?Sized + Debug + ?Sized ) {} + | ^ ---------------------------- this type parameter needs to be `Sized` + | | + | doesn't have a size known at compile-time + | + = help: unsized fn params are gated as an unstable feature +help: consider restricting type parameters + | +LL - fn foo8(_: impl ?Sized + Debug + ?Sized ) {} +LL + fn foo8(_: impl Debug ) {} + | +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn foo8(_: &impl ?Sized + Debug + ?Sized ) {} + | + + +error: aborting due to 14 previous errors + +Some errors have detailed explanations: E0203, E0277. +For more information about an error, try `rustc --explain E0203`.