Skip to content
Merged
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
8 changes: 8 additions & 0 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@ impl Suggestions {
Suggestions::Disabled => Vec::new(),
}
}

pub fn len(&self) -> usize {
match self {
Suggestions::Enabled(suggestions) => suggestions.len(),
Suggestions::Sealed(suggestions) => suggestions.len(),
Suggestions::Disabled => 0,
}
}
}

impl Default for Suggestions {
Expand Down
124 changes: 66 additions & 58 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1636,69 +1636,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span_bug!(pat_span, "unexpected resolution for path pattern: {resolved_pat:?}");
};

if let Some(span) = self.tcx.hir_res_span(pat_res) {
let span = match (self.tcx.hir_res_span(pat_res), res.opt_def_id()) {
(Some(span), _) => span,
(None, Some(def_id)) => self.tcx.def_span(def_id),
(None, None) => {
e.emit();
return;
}
};
if let [hir::PathSegment { ident, args: None, .. }] = segments
&& e.suggestions.len() == 0
{
e.span_label(span, format!("{} defined here", res.descr()));
if let [hir::PathSegment { ident, .. }] = segments {
e.span_label(
pat_span,
format!(
"`{}` is interpreted as {} {}, not a new binding",
ident,
res.article(),
res.descr(),
),
);
match self.tcx.parent_hir_node(hir_id) {
hir::Node::PatField(..) => {
e.span_label(
pat_span,
format!(
"`{}` is interpreted as {} {}, not a new binding",
ident,
res.article(),
res.descr(),
),
);
match self.tcx.parent_hir_node(hir_id) {
hir::Node::PatField(..) => {
e.span_suggestion_verbose(
ident.span.shrink_to_hi(),
"bind the struct field to a different name instead",
format!(": other_{}", ident.as_str().to_lowercase()),
Applicability::HasPlaceholders,
);
}
_ => {
let (type_def_id, item_def_id) = match resolved_pat.ty.kind() {
ty::Adt(def, _) => match res {
Res::Def(DefKind::Const { .. }, def_id) => {
(Some(def.did()), Some(def_id))
}
_ => (None, None),
},
_ => (None, None),
};

let is_range = matches!(
type_def_id.and_then(|id| self.tcx.as_lang_item(id)),
Some(
LangItem::Range
| LangItem::RangeFrom
| LangItem::RangeTo
| LangItem::RangeFull
| LangItem::RangeInclusiveStruct
| LangItem::RangeToInclusive,
)
);
if is_range {
if !self.maybe_suggest_range_literal(&mut e, item_def_id, *ident) {
let msg = "constants only support matching by type, \
if you meant to match against a range of values, \
consider using a range pattern like `min ..= max` in the match block";
e.note(msg);
}
} else {
let msg = "introduce a new binding instead";
let sugg = format!("other_{}", ident.as_str().to_lowercase());
e.span_suggestion_verbose(
ident.span.shrink_to_hi(),
"bind the struct field to a different name instead",
format!(": other_{}", ident.as_str().to_lowercase()),
ident.span,
msg,
sugg,
Applicability::HasPlaceholders,
);
}
_ => {
let (type_def_id, item_def_id) = match resolved_pat.ty.kind() {
ty::Adt(def, _) => match res {
Res::Def(DefKind::Const { .. }, def_id) => {
(Some(def.did()), Some(def_id))
}
_ => (None, None),
},
_ => (None, None),
};

let is_range = matches!(
type_def_id.and_then(|id| self.tcx.as_lang_item(id)),
Some(
LangItem::Range
| LangItem::RangeFrom
| LangItem::RangeTo
| LangItem::RangeFull
| LangItem::RangeInclusiveStruct
| LangItem::RangeToInclusive,
)
);
if is_range {
if !self.maybe_suggest_range_literal(&mut e, item_def_id, *ident) {
let msg = "constants only support matching by type, \
if you meant to match against a range of values, \
consider using a range pattern like `min ..= max` in the match block";
e.note(msg);
}
} else {
let msg = "introduce a new binding instead";
let sugg = format!("other_{}", ident.as_str().to_lowercase());
e.span_suggestion(
ident.span,
msg,
sugg,
Applicability::HasPlaceholders,
);
}
}
};
}
}
};
}
e.emit();
}
Expand Down
6 changes: 5 additions & 1 deletion tests/ui/blind/blind-item-block-middle.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ LL | let bar = 5;
| |
| expected integer, found `bar`
| `bar` is interpreted as a unit struct, not a new binding
| help: introduce a new binding instead: `other_bar`
|
help: introduce a new binding instead
|
LL | let other_bar = 5;
| ++++++

error: aborting due to 1 previous error

Expand Down
24 changes: 20 additions & 4 deletions tests/ui/consts/const_in_pattern/arrays-and-slices.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ LL | BSTR_SIZED => {}
| |
| expected `&[u8]`, found `&[u8; 3]`
| `BSTR_SIZED` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_bstr_sized`
|
= note: expected reference `&[u8]`
found reference `&'static [u8; 3]`
help: introduce a new binding instead
|
LL - BSTR_SIZED => {}
LL + other_bstr_sized => {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one feels marginal, and likewise the other ones in this test. I can imagine a user thinking this would match against b"012". But it's sufficiently broken thinking that it's hard to say for sure, so I can live with it.

Copy link
Contributor Author

@estebank estebank Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm currently looking at a completely different case, and maybe the suggestion I'm adding might also be relevant here?

error: constant of non-structural type `S` in a pattern
  --> $DIR/match_ice.rs:11:9
   |
LL | struct S;
   | -------- `S` must be annotated with `#[derive(PartialEq)]` to be usable in patterns
...
LL |     const C: &S = &S;
   |     ----------- constant defined here
LL |     match C {
LL |         C => {}
   |         ^ constant of non-structural type
   |
   = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
help: if `S` manually implemented `PartialEq`, you could check for equality
   |
LL -         C => {}
LL +         binding if binding == C => {}
   |

Note that the binding suggestion is pre-existing, it's just being made more obvious now.

|

error[E0308]: mismatched types
--> $DIR/arrays-and-slices.rs:23:9
Expand All @@ -29,10 +33,14 @@ LL | STRUCT_SIZED => {}
| |
| expected `&SomeStruct<[u8]>`, found `&SomeStruct<[u8; 3]>`
| `STRUCT_SIZED` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_struct_sized`
|
= note: expected reference `&SomeStruct<[u8]>`
found reference `&'static SomeStruct<[u8; 3]>`
help: introduce a new binding instead
|
LL - STRUCT_SIZED => {}
LL + other_struct_sized => {}
|

error[E0308]: mismatched types
--> $DIR/arrays-and-slices.rs:30:9
Expand All @@ -47,10 +55,14 @@ LL | BSTR_SIZED => {}
| |
| expected `&[u8]`, found `&[u8; 3]`
| `BSTR_SIZED` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_bstr_sized`
|
= note: expected reference `&[u8]`
found reference `&'static [u8; 3]`
help: introduce a new binding instead
|
LL - BSTR_SIZED => {}
LL + other_bstr_sized => {}
|

error[E0308]: mismatched types
--> $DIR/arrays-and-slices.rs:37:9
Expand All @@ -65,10 +77,14 @@ LL | STRUCT_SIZED => {}
| |
| expected `&SomeStruct<[u8]>`, found `&SomeStruct<[u8; 3]>`
| `STRUCT_SIZED` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_struct_sized`
|
= note: expected reference `&SomeStruct<[u8]>`
found reference `&'static SomeStruct<[u8; 3]>`
help: introduce a new binding instead
|
LL - STRUCT_SIZED => {}
LL + other_struct_sized => {}
|

error: cannot use unsized non-slice type `SomeStruct<[u8]>` in constant patterns
--> $DIR/arrays-and-slices.rs:47:9
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/did_you_mean/compatible-variants-in-pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ fn b(s: Option<S>) {
S => {
//~^ ERROR mismatched types
//~| HELP try wrapping
//~| HELP introduce a new binding instead
}
_ => {}
}
Expand All @@ -32,7 +31,6 @@ fn c(s: Result<S, S>) {
S => {
//~^ ERROR mismatched types
//~| HELP try wrapping
//~| HELP introduce a new binding instead
}
_ => {}
}
Expand Down
28 changes: 3 additions & 25 deletions tests/ui/did_you_mean/compatible-variants-in-pat.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,25 @@ LL | Foo::Bar(Bar { x }) => {
error[E0308]: mismatched types
--> $DIR/compatible-variants-in-pat.rs:21:9
|
LL | struct S;
| -------- unit struct defined here
...
LL | match s {
| - this expression has type `Option<S>`
LL | S => {
| ^
| |
| expected `Option<S>`, found `S`
| `S` is interpreted as a unit struct, not a new binding
| ^ expected `Option<S>`, found `S`
|
= note: expected enum `Option<S>`
found struct `S`
help: try wrapping the pattern in `Some`
|
LL | Some(S) => {
| +++++ +
help: introduce a new binding instead
|
LL - S => {
LL + other_s => {
|

error[E0308]: mismatched types
--> $DIR/compatible-variants-in-pat.rs:32:9
--> $DIR/compatible-variants-in-pat.rs:31:9
|
LL | struct S;
| -------- unit struct defined here
...
LL | match s {
| - this expression has type `Result<S, S>`
LL | S => {
| ^
| |
| expected `Result<S, S>`, found `S`
| `S` is interpreted as a unit struct, not a new binding
| ^ expected `Result<S, S>`, found `S`
|
= note: expected enum `Result<S, S>`
found struct `S`
Expand All @@ -59,11 +42,6 @@ LL | Ok(S) => {
| +++ +
LL | Err(S) => {
| ++++ +
help: introduce a new binding instead
|
LL - S => {
LL + other_s => {
|

error: aborting due to 3 previous errors

Expand Down
7 changes: 6 additions & 1 deletion tests/ui/issues/issue-33504.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ LL | let Test = 1;
| |
| expected integer, found `Test`
| `Test` is interpreted as a unit struct, not a new binding
| help: introduce a new binding instead: `other_test`
|
help: introduce a new binding instead
|
LL - let Test = 1;
LL + let other_test = 1;
|

error: aborting due to 1 previous error

Expand Down
3 changes: 0 additions & 3 deletions tests/ui/issues/issue-5100.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
error[E0308]: mismatched types
--> $DIR/issue-5100.rs:9:9
|
LL | enum A { B, C }
| - unit variant defined here
...
LL | match (true, false) {
| ------------- this expression has type `(bool, bool)`
LL | A::B => (),
Expand Down
6 changes: 5 additions & 1 deletion tests/ui/match/match-const-tuple-type-mismatch.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ LL | A => (),
| |
| expected integer, found `(isize, isize)`
| `A` is interpreted as a constant, not a new binding
| help: introduce a new binding instead: `other_a`
|
= note: expected type `{integer}`
found tuple `(isize, isize)`
help: introduce a new binding instead
|
LL - A => (),
LL + other_a => (),
|

error: aborting due to 1 previous error

Expand Down
3 changes: 0 additions & 3 deletions tests/ui/match/match-tag-nullary.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
error[E0308]: mismatched types
--> $DIR/match-tag-nullary.rs:4:40
|
LL | enum B { B }
| - unit variant defined here
LL |
LL | fn main() { let x: A = A::A; match x { B::B => { } } }
| - ^^^^ expected `A`, found `B`
| |
Expand Down
3 changes: 0 additions & 3 deletions tests/ui/match/mismatched-types-in-match-7867.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
error[E0308]: mismatched types
--> $DIR/mismatched-types-in-match-7867.rs:10:9
|
LL | enum A { B, C }
| - unit variant defined here
...
LL | match (true, false) {
| ------------- this expression has type `(bool, bool)`
LL | A::B => (),
Expand Down
3 changes: 0 additions & 3 deletions tests/ui/privacy/private-unit-struct-assignment.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ LL | struct C;
error[E0308]: mismatched types
--> $DIR/private-unit-struct-assignment.rs:8:5
|
LL | struct C;
| -------- unit struct defined here
...
LL | A::C = 1;
| ^^^^ - this expression has type `{integer}`
| |
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/resolve/name-clash-nullary.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,19 @@ LL | let None: isize = 42;
| ^^^^ ----- expected due to this
| |
| expected `isize`, found `Option<_>`
| `None` is interpreted as a unit variant, not a new binding
|
--> $SRC_DIR/core/src/option.rs:LL:COL
|
= note: unit variant defined here
|
= note: expected type `isize`
found enum `Option<_>`
help: introduce a new binding instead
|
LL - let None: isize = 42;
LL + let other_none: isize = 42;
|

error: aborting due to 1 previous error

Expand Down
Loading
Loading