Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {

self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg);
self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);
self.suggest_range_struct_destructuring(&mut err, path, source);
self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);

if let Some((span, label)) = base_error.span_label {
Expand Down Expand Up @@ -1383,6 +1384,84 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
}
}

fn suggest_range_struct_destructuring(
&mut self,
err: &mut Diag<'_>,
path: &[Segment],
source: PathSource<'_, '_, '_>,
) {
if !matches!(source, PathSource::Pat | PathSource::TupleStruct(..) | PathSource::Expr(..)) {
return;
}

let Some(pat) = self.diag_metadata.current_pat else { return };
let ast::PatKind::Range(start, end, end_kind) = &pat.kind else { return };

let [segment] = path else { return };
let failing_span = segment.ident.span;

let start_snippet =
start.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok());
let end_snippet =
end.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok());

let in_start = start.as_ref().is_some_and(|e| e.span.contains(failing_span));
let in_end = end.as_ref().is_some_and(|e| e.span.contains(failing_span));

if !in_start && !in_end {
return;
}

let field = |name: &str, val: String| {
if val == name { val } else { format!("{name}: {val}") }
};

let mut resolve_short_name = |short: &str, full: &str| -> String {
let ident = Ident::from_str(short);
let path = Segment::from_path(&Path::from_ident(ident));

match self.resolve_path(&path, Some(TypeNS), None, PathSource::Type) {
PathResult::Module(..) | PathResult::NonModule(..) => short.to_string(),
_ => full.to_string(),
}
};
// FIXME(new_range): Also account for new range types
let (struct_path, fields) = match (start_snippet, end_snippet, &end_kind.node) {
(Some(start), Some(end), ast::RangeEnd::Excluded) => (
resolve_short_name("Range", "std::ops::Range"),
vec![field("start", start), field("end", end)],
),
(Some(start), Some(end), ast::RangeEnd::Included(_)) => (
resolve_short_name("RangeInclusive", "std::ops::RangeInclusive"),
vec![field("start", start), field("end", end)],
),
(Some(start), None, _) => (
resolve_short_name("RangeFrom", "std::ops::RangeFrom"),
vec![field("start", start)],
),
(None, Some(end), ast::RangeEnd::Excluded) => {
(resolve_short_name("RangeTo", "std::ops::RangeTo"), vec![field("end", end)])
}
(None, Some(end), ast::RangeEnd::Included(_)) => (
resolve_short_name("RangeToInclusive", "std::ops::RangeToInclusive"),
vec![field("end", end)],
),
_ => return,
};

err.span_suggestion_verbose(
pat.span,
format!("if you meant to destructure a range use a struct pattern"),
format!("{} {{ {} }}", struct_path, fields.join(", ")),
Applicability::MaybeIncorrect,
);

err.note(
"range patterns match against the start and end of a range; \
to bind the components, use a struct pattern",
);
}

fn suggest_swapping_misplaced_self_ty_and_trait(
&mut self,
err: &mut Diag<'_>,
Expand Down
6 changes: 6 additions & 0 deletions tests/ui/match/issue-92100.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ error[E0425]: cannot find value `a` in this scope
LL | [a.., a] => {}
| ^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to collect the rest of the slice in `a`, use the at operator
|
LL | [a @ .., a] => {}
| +
help: if you meant to destructure a range use a struct pattern
|
LL - [a.., a] => {}
LL + [std::ops::RangeFrom { start: a }, a] => {}
|

error: aborting due to 1 previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@ error[E0425]: cannot find value `rest` in this scope
LL | [1, rest..] => println!("{rest}"),
| ^^^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to collect the rest of the slice in `rest`, use the at operator
|
LL | [1, rest @ ..] => println!("{rest}"),
| +
help: if you meant to destructure a range use a struct pattern
|
LL - [1, rest..] => println!("{rest}"),
LL + [1, std::ops::RangeFrom { start: rest }] => println!("{rest}"),
|

error[E0425]: cannot find value `rest` in this scope
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:35
Expand All @@ -33,11 +39,17 @@ error[E0425]: cannot find value `tail` in this scope
LL | [_, ..tail] => println!("{tail}"),
| ^^^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to collect the rest of the slice in `tail`, use the at operator
|
LL - [_, ..tail] => println!("{tail}"),
LL + [_, tail @ ..] => println!("{tail}"),
|
help: if you meant to destructure a range use a struct pattern
|
LL - [_, ..tail] => println!("{tail}"),
LL + [_, std::ops::RangeTo { end: tail }] => println!("{tail}"),
|

error[E0425]: cannot find value `tail` in this scope
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:11:35
Expand All @@ -51,11 +63,17 @@ error[E0425]: cannot find value `tail` in this scope
LL | [_, ...tail] => println!("{tail}"),
| ^^^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to collect the rest of the slice in `tail`, use the at operator
|
LL - [_, ...tail] => println!("{tail}"),
LL + [_, tail @ ..] => println!("{tail}"),
|
help: if you meant to destructure a range use a struct pattern
|
LL - [_, ...tail] => println!("{tail}"),
LL + [_, std::ops::RangeToInclusive { end: tail }] => println!("{tail}"),
|

error[E0425]: cannot find value `tail` in this scope
--> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:17:36
Expand Down
40 changes: 40 additions & 0 deletions tests/ui/resolve/suggest-range-struct-destructuring.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};

fn test_range(r: Range<u32>) {
let start..end = r;
//~^ ERROR cannot find value `start`
//~| ERROR cannot find value `end`
}

fn test_inclusive(r: RangeInclusive<u32>) {
let start..=end = r;
//~^ ERROR cannot find value `start`
//~| ERROR cannot find value `end`
}

fn test_from(r: RangeFrom<u32>) {
let start.. = r;
//~^ ERROR cannot find value `start`
}

fn test_to(r: RangeTo<u32>) {
let ..end = r;
//~^ ERROR cannot find value `end`
}

fn test_to_inclusive(r: RangeToInclusive<u32>) {
let ..=end = r;
//~^ ERROR cannot find value `end`
}

// Case 6: Complex Path (Keep this! It works!)
mod my {
// We don't define MISSING here to trigger the error
}
fn test_path(r: Range<u32>) {
let my::MISSING..end = r;
//~^ ERROR cannot find value `MISSING`
//~| ERROR cannot find value `end`
}

fn main() {}
127 changes: 127 additions & 0 deletions tests/ui/resolve/suggest-range-struct-destructuring.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
error[E0425]: cannot find value `start` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:4:9
|
LL | let start..end = r;
| ^^^^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to destructure a range use a struct pattern
|
LL - let start..end = r;
LL + let Range { start, end } = r;
|

error[E0425]: cannot find value `end` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:4:16
|
LL | let start..end = r;
| ^^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to destructure a range use a struct pattern
|
LL - let start..end = r;
LL + let Range { start, end } = r;
|

error[E0425]: cannot find value `start` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:10:9
|
LL | let start..=end = r;
| ^^^^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to destructure a range use a struct pattern
|
LL - let start..=end = r;
LL + let RangeInclusive { start, end } = r;
|

error[E0425]: cannot find value `end` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:10:17
|
LL | let start..=end = r;
| ^^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to destructure a range use a struct pattern
|
LL - let start..=end = r;
LL + let RangeInclusive { start, end } = r;
|

error[E0425]: cannot find value `start` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:16:9
|
LL | let start.. = r;
| ^^^^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to collect the rest of the slice in `start`, use the at operator
|
LL | let start @ .. = r;
| +
help: if you meant to destructure a range use a struct pattern
|
LL - let start.. = r;
LL + let RangeFrom { start } = r;
|

error[E0425]: cannot find value `end` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:21:11
|
LL | let ..end = r;
| ^^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to collect the rest of the slice in `end`, use the at operator
|
LL - let ..end = r;
LL + let end @ .. = r;
|
help: if you meant to destructure a range use a struct pattern
|
LL - let ..end = r;
LL + let RangeTo { end } = r;
|

error[E0425]: cannot find value `end` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:26:12
|
LL | let ..=end = r;
| ^^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to collect the rest of the slice in `end`, use the at operator
|
LL - let ..=end = r;
LL + let end @ .. = r;
|
help: if you meant to destructure a range use a struct pattern
|
LL - let ..=end = r;
LL + let RangeToInclusive { end } = r;
|

error[E0425]: cannot find value `MISSING` in module `my`
--> $DIR/suggest-range-struct-destructuring.rs:35:13
|
LL | let my::MISSING..end = r;
| ^^^^^^^ not found in `my`

error[E0425]: cannot find value `end` in this scope
--> $DIR/suggest-range-struct-destructuring.rs:35:22
|
LL | let my::MISSING..end = r;
| ^^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to destructure a range use a struct pattern
|
LL - let my::MISSING..end = r;
LL + let Range { start: my::MISSING, end } = r;
|

error: aborting due to 9 previous errors

For more information about this error, try `rustc --explain E0425`.
6 changes: 6 additions & 0 deletions tests/ui/typeck/issue-105946.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ error[E0425]: cannot find value `_y` in this scope
LL | let [_y..] = [Box::new(1), Box::new(2)];
| ^^ not found in this scope
|
= note: range patterns match against the start and end of a range; to bind the components, use a struct pattern
help: if you meant to collect the rest of the slice in `_y`, use the at operator
|
LL | let [_y @ ..] = [Box::new(1), Box::new(2)];
| +
help: if you meant to destructure a range use a struct pattern
|
LL - let [_y..] = [Box::new(1), Box::new(2)];
LL + let [std::ops::RangeFrom { start: _y }] = [Box::new(1), Box::new(2)];
|

error[E0658]: `X..` patterns in slices are experimental
--> $DIR/issue-105946.rs:7:10
Expand Down
Loading