Skip to content

Commit d63a46a

Browse files
authored
Rollup merge of rust-lang#95970 - WaffleLapkin:nicer_trait_suggestions, r=compiler-errors
Fix suggestions in case of `T:` bounds This PR fixes a corner case in `suggest_constraining_type_params` that was causing incorrect suggestions. For the following functions: ```rust fn a<T:>(t: T) { [t, t]; } fn b<T>(t: T) where T: { [t, t]; } ``` We previously suggested the following: ```text ... help: consider restricting type parameter `T` | 1 | fn a<T: Copy:>(t: T) { [t, t]; } | ++++++ ... help: consider further restricting this bound | 2 | fn b<T>(t: T) where T: + Copy { [t, t]; } | ++++++ ``` Note that neither `T: Copy:` not `where T: + Copy` is a correct bound. With this commit the suggestions are correct: ```text ... help: consider restricting type parameter `T` | 1 | fn a<T: Copy>(t: T) { [t, t]; } | ++++ ... help: consider further restricting this bound | 2 | fn b<T>(t: T) where T: Copy { [t, t]; } | ++++ ``` r? `@compiler-errors` I've tried fixing rust-lang#95898 here too, but got too confused with how `suggest_traits_to_import` works and what it does 😅
2 parents 91813a7 + e4710fe commit d63a46a

File tree

5 files changed

+124
-9
lines changed

5 files changed

+124
-9
lines changed

compiler/rustc_hir/src/hir.rs

+35-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_error_messages::MultiSpan;
1717
use rustc_index::vec::IndexVec;
1818
use rustc_macros::HashStable_Generic;
1919
use rustc_span::hygiene::MacroKind;
20-
use rustc_span::source_map::Spanned;
20+
use rustc_span::source_map::{SourceMap, Spanned};
2121
use rustc_span::symbol::{kw, sym, Ident, Symbol};
2222
use rustc_span::{def_id::LocalDefId, BytePos, Span, DUMMY_SP};
2323
use rustc_target::asm::InlineAsmRegOrRegClass;
@@ -523,6 +523,40 @@ impl<'hir> GenericParam<'hir> {
523523
})
524524
.map(|sp| sp.shrink_to_hi())
525525
}
526+
527+
/// Returns the span of `:` after a generic parameter.
528+
///
529+
/// For example:
530+
///
531+
/// ```text
532+
/// fn a<T:>()
533+
/// ^
534+
/// | here
535+
/// here |
536+
/// v
537+
/// fn b<T :>()
538+
///
539+
/// fn c<T
540+
///
541+
/// :>()
542+
/// ^
543+
/// |
544+
/// here
545+
/// ```
546+
pub fn colon_span_for_suggestions(&self, source_map: &SourceMap) -> Option<Span> {
547+
let sp = source_map
548+
.span_extend_while(self.span.shrink_to_hi(), |c| c.is_whitespace() || c == ':')
549+
.ok()?;
550+
551+
let snippet = source_map.span_to_snippet(sp).ok()?;
552+
let offset = snippet.find(':')?;
553+
554+
let colon_sp = sp
555+
.with_lo(BytePos(sp.lo().0 + offset as u32))
556+
.with_hi(BytePos(sp.lo().0 + (offset + ':'.len_utf8()) as u32));
557+
558+
Some(colon_sp)
559+
}
526560
}
527561

528562
#[derive(Default)]

compiler/rustc_middle/src/ty/diagnostics.rs

+28-7
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,14 @@ pub fn suggest_constraining_type_params<'a>(
336336
}
337337

338338
let constraint = constraints.iter().map(|&(c, _)| c).collect::<Vec<_>>().join(" + ");
339-
let mut suggest_restrict = |span| {
339+
let mut suggest_restrict = |span, bound_list_non_empty| {
340340
suggestions.push((
341341
span,
342-
format!(" + {}", constraint),
342+
if bound_list_non_empty {
343+
format!(" + {}", constraint)
344+
} else {
345+
format!(" {}", constraint)
346+
},
343347
SuggestChangingConstraintsMessage::RestrictBoundFurther,
344348
))
345349
};
@@ -360,7 +364,10 @@ pub fn suggest_constraining_type_params<'a>(
360364
// |
361365
// replace with: `impl Foo + Bar`
362366

363-
suggest_restrict(param.span.shrink_to_hi());
367+
// `impl Trait` must have at least one trait in the list
368+
let bound_list_non_empty = true;
369+
370+
suggest_restrict(param.span.shrink_to_hi(), bound_list_non_empty);
364371
continue;
365372
}
366373

@@ -383,15 +390,25 @@ pub fn suggest_constraining_type_params<'a>(
383390
// --
384391
// |
385392
// replace with: `T: Bar +`
386-
suggest_restrict(span);
393+
394+
// `bounds_span_for_suggestions` returns `None` if the list is empty
395+
let bound_list_non_empty = true;
396+
397+
suggest_restrict(span, bound_list_non_empty);
387398
} else {
399+
let (colon, span) = match param.colon_span_for_suggestions(tcx.sess.source_map()) {
400+
// If there is already a colon after generic, do not suggest adding it again
401+
Some(sp) => ("", sp.shrink_to_hi()),
402+
None => (":", param.span.shrink_to_hi()),
403+
};
404+
388405
// If user hasn't provided any bounds, suggest adding a new one:
389406
//
390407
// fn foo<T>(t: T) { ... }
391408
// - help: consider restricting this type parameter with `T: Foo`
392409
suggestions.push((
393-
param.span.shrink_to_hi(),
394-
format!(": {}", constraint),
410+
span,
411+
format!("{colon} {constraint}"),
395412
SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
396413
));
397414
}
@@ -459,17 +476,21 @@ pub fn suggest_constraining_type_params<'a>(
459476
));
460477
} else {
461478
let mut param_spans = Vec::new();
479+
let mut non_empty = false;
462480

463481
for predicate in generics.where_clause.predicates {
464482
if let WherePredicate::BoundPredicate(WhereBoundPredicate {
465483
span,
466484
bounded_ty,
485+
bounds,
467486
..
468487
}) = predicate
469488
{
470489
if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
471490
if let Some(segment) = path.segments.first() {
472491
if segment.ident.to_string() == param_name {
492+
non_empty = !bounds.is_empty();
493+
473494
param_spans.push(span);
474495
}
475496
}
@@ -478,7 +499,7 @@ pub fn suggest_constraining_type_params<'a>(
478499
}
479500

480501
match param_spans[..] {
481-
[&param_span] => suggest_restrict(param_span.shrink_to_hi()),
502+
[&param_span] => suggest_restrict(param_span.shrink_to_hi(), non_empty),
482503
_ => {
483504
suggestions.push((
484505
generics.where_clause.tail_span_for_suggestion(),

src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed

+14
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,18 @@ where
6969
(t, t) //~ use of moved value: `t`
7070
}
7171

72+
#[rustfmt::skip]
73+
fn existing_colon<T: Copy>(t: T) {
74+
//~^ HELP consider restricting type parameter `T`
75+
[t, t]; //~ use of moved value: `t`
76+
}
77+
78+
fn existing_colon_in_where<T>(t: T)
79+
where
80+
T: Copy,
81+
//~^ HELP consider further restricting this bound
82+
{
83+
[t, t]; //~ use of moved value: `t`
84+
}
85+
7286
fn main() {}

src/test/ui/moves/use_of_moved_value_copy_suggestions.rs

+14
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,18 @@ where
6969
(t, t) //~ use of moved value: `t`
7070
}
7171

72+
#[rustfmt::skip]
73+
fn existing_colon<T:>(t: T) {
74+
//~^ HELP consider restricting type parameter `T`
75+
[t, t]; //~ use of moved value: `t`
76+
}
77+
78+
fn existing_colon_in_where<T>(t: T)
79+
where
80+
T:,
81+
//~^ HELP consider further restricting this bound
82+
{
83+
[t, t]; //~ use of moved value: `t`
84+
}
85+
7286
fn main() {}

src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr

+33-1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,38 @@ help: consider further restricting this bound
142142
LL | T: B + Trait + Copy,
143143
| ++++++++++++++
144144

145-
error: aborting due to 9 previous errors
145+
error[E0382]: use of moved value: `t`
146+
--> $DIR/use_of_moved_value_copy_suggestions.rs:83:9
147+
|
148+
LL | fn existing_colon_in_where<T>(t: T)
149+
| - move occurs because `t` has type `T`, which does not implement the `Copy` trait
150+
...
151+
LL | [t, t];
152+
| - ^ value used here after move
153+
| |
154+
| value moved here
155+
|
156+
help: consider further restricting this bound
157+
|
158+
LL | T: Copy,
159+
| ++++
160+
161+
error[E0382]: use of moved value: `t`
162+
--> $DIR/use_of_moved_value_copy_suggestions.rs:75:9
163+
|
164+
LL | fn existing_colon<T:>(t: T) {
165+
| - move occurs because `t` has type `T`, which does not implement the `Copy` trait
166+
LL |
167+
LL | [t, t];
168+
| - ^ value used here after move
169+
| |
170+
| value moved here
171+
|
172+
help: consider restricting type parameter `T`
173+
|
174+
LL | fn existing_colon<T: Copy>(t: T) {
175+
| ++++
176+
177+
error: aborting due to 11 previous errors
146178

147179
For more information about this error, try `rustc --explain E0382`.

0 commit comments

Comments
 (0)