Keep track of patterns that could have introduced a binding, but didn't
When we recover from a pattern parse error, or a pattern uses `..`, we keep track of that and affect resolution error for missing bindings that could have been provided by that pattern. We differentiate between `..` and parse recovery. We silence resolution errors likely caused by the pattern parse error.
```
error[E0425]: cannot find value `title` in this scope
--> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:19:30
|
LL | println!("[{}]({})", title, url);
| ^^^^^ not found in this scope
|
note: `Website` has a field `title` which could have been included in this pattern, but it wasn't
--> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:17:12
|
LL | / struct Website {
LL | | url: String,
LL | | title: Option<String> ,
| | ----- defined here
LL | | }
| |_-
...
LL | if let Website { url, .. } = website {
| ^^^^^^^^^^^^^^^^^^^ this pattern doesn't include `title`, which is available in `Website`
```
Fix #74863.
This commit is contained in:
@@ -430,6 +430,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
||||
let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone());
|
||||
err.code(code);
|
||||
|
||||
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_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);
|
||||
|
||||
@@ -1120,6 +1121,65 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
||||
true
|
||||
}
|
||||
|
||||
fn detect_missing_binding_available_from_pattern(
|
||||
&mut self,
|
||||
err: &mut Diag<'_>,
|
||||
path: &[Segment],
|
||||
following_seg: Option<&Segment>,
|
||||
) {
|
||||
let [segment] = path else { return };
|
||||
let None = following_seg else { return };
|
||||
'outer: for rib in self.ribs[ValueNS].iter().rev() {
|
||||
for (def_id, spans) in &rib.patterns_with_skipped_bindings {
|
||||
if let Some(fields) = self.r.field_idents(*def_id) {
|
||||
for field in fields {
|
||||
if field.name == segment.ident.name {
|
||||
if spans.iter().all(|(_, was_recovered)| *was_recovered) {
|
||||
// This resolution error will likely be fixed by fixing a
|
||||
// syntax error in a pattern, so it is irrelevant to the user.
|
||||
let multispan: MultiSpan =
|
||||
spans.iter().map(|(s, _)| *s).collect::<Vec<_>>().into();
|
||||
err.span_note(
|
||||
multispan,
|
||||
"this pattern had a recovered parse error which likely \
|
||||
lost the expected fields",
|
||||
);
|
||||
err.downgrade_to_delayed_bug();
|
||||
}
|
||||
let mut multispan: MultiSpan = spans
|
||||
.iter()
|
||||
.filter(|(_, was_recovered)| !was_recovered)
|
||||
.map(|(sp, _)| *sp)
|
||||
.collect::<Vec<_>>()
|
||||
.into();
|
||||
let def_span = self.r.def_span(*def_id);
|
||||
let ty = self.r.tcx.item_name(*def_id);
|
||||
multispan.push_span_label(def_span, String::new());
|
||||
multispan.push_span_label(field.span, "defined here".to_string());
|
||||
for (span, _) in spans.iter().filter(|(_, r)| !r) {
|
||||
multispan.push_span_label(
|
||||
*span,
|
||||
format!(
|
||||
"this pattern doesn't include `{field}`, which is \
|
||||
available in `{ty}`",
|
||||
),
|
||||
);
|
||||
}
|
||||
err.span_note(
|
||||
multispan,
|
||||
format!(
|
||||
"`{ty}` has a field `{field}` which could have been included \
|
||||
in this pattern, but it wasn't",
|
||||
),
|
||||
);
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_at_operator_in_slice_pat_with_range(
|
||||
&mut self,
|
||||
err: &mut Diag<'_>,
|
||||
|
||||
Reference in New Issue
Block a user