about summary refs log tree commit diff
path: root/compiler/rustc_middle
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-07-24 22:22:16 +0200
committerGitHub <noreply@github.com>2024-07-24 22:22:16 +0200
commit2ff33bb1df6ef62a8f325887324cecf4b00f5087 (patch)
tree4bfcdc377a72009800454a4ea58f58813b1d37c2 /compiler/rustc_middle
parented5dfed535b4be3a7ab0f4c0d73f274a7fe6165a (diff)
parente13eb37eeb3ccb447f33496fe86d77498558d2d5 (diff)
downloadrust-2ff33bb1df6ef62a8f325887324cecf4b00f5087.tar.gz
rust-2ff33bb1df6ef62a8f325887324cecf4b00f5087.zip
Rollup merge of #127717 - gurry:127441-stray-impl-sugg, r=compiler-errors
Fix malformed suggestion for repeated maybe unsized bounds

Fixes #127441

Now when we encounter something like `foo(a : impl ?Sized + ?Sized)`, instead of suggesting removal of both bounds and leaving `foo(a: impl )` behind, we suggest changing the first bound to `Sized` and removing the second bound, resulting in `foo(a: impl Sized)`.

Although the issue was reported for impl trait types, it also occurred with regular param bounds. So if we encounter `foo<T: ?Sized + ?Sized>(a: T)` we now detect that all the bounds are `?Sized` and therefore emit the suggestion to remove the entire predicate `: ?Sized + ?Sized` resulting in `foo<T>(a: T)`.

Lastly, if we encounter a situation where some of the bounds are something other than `?Sized`, then we emit separate removal suggestions for each `?Sized` bound. E.g. if we see `foo(a: impl ?Sized + Bar + ?Sized)` or `foo<T: ?Sized + Bar + ?Sized>(a: T)` we emit suggestions such that the user will be left with `foo(a : impl Bar)` or `foo<T: Bar>(a: T)` respectively.
Diffstat (limited to 'compiler/rustc_middle')
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs69
1 files changed, 49 insertions, 20 deletions
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index f479b18c7c4..f2261f4a43b 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::<Vec<_>>();
+
+        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);
             }
         }
     }