diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2024-12-16 20:00:22 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-16 20:00:22 +0100 |
| commit | 86db97e2b3c50729d30b08d77dae67318d3cc2e1 (patch) | |
| tree | 4fdd0c7109aea7129b211b66de1b1d6fdbcb6e21 /compiler/rustc_resolve/src | |
| parent | d9ba4bf6feb9b49f6a7e63097705cc39cef63234 (diff) | |
| parent | 733fd03f0f9d5c8ad595f7b7cde17d3c1f33a19e (diff) | |
Rollup merge of #134284 - estebank:issue-74863, r=lcnr
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:18:30
|
LL | if let Website { url, .. } = website {
| ------------------- this pattern doesn't include `title`, which is available in `Website`
LL | println!("[{}]({})", title, url);
| ^^^^^ not found in this scope
```
Fix #74863.
Diffstat (limited to 'compiler/rustc_resolve/src')
| -rw-r--r-- | compiler/rustc_resolve/src/late.rs | 40 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late/diagnostics.rs | 43 |
2 files changed, 79 insertions, 4 deletions
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index bde11428d40..61be85d2dff 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -19,7 +19,9 @@ use rustc_ast::visit::{ use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::codes::*; -use rustc_errors::{Applicability, DiagArgValue, IntoDiagArg, StashKey, Suggestions}; +use rustc_errors::{ + Applicability, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions, +}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId}; @@ -264,12 +266,17 @@ impl RibKind<'_> { #[derive(Debug)] pub(crate) struct Rib<'ra, R = Res> { pub bindings: IdentMap<R>, + pub patterns_with_skipped_bindings: FxHashMap<DefId, Vec<(Span, Result<(), ErrorGuaranteed>)>>, pub kind: RibKind<'ra>, } impl<'ra, R> Rib<'ra, R> { fn new(kind: RibKind<'ra>) -> Rib<'ra, R> { - Rib { bindings: Default::default(), kind } + Rib { + bindings: Default::default(), + patterns_with_skipped_bindings: Default::default(), + kind, + } } } @@ -3775,6 +3782,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// When a whole or-pattern has been dealt with, the thing happens. /// /// See the implementation and `fresh_binding` for more details. + #[tracing::instrument(skip(self, bindings), level = "debug")] fn resolve_pattern_inner( &mut self, pat: &Pat, @@ -3783,7 +3791,6 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ) { // Visit all direct subpatterns of this pattern. pat.walk(&mut |pat| { - debug!("resolve_pattern pat={:?} node={:?}", pat, pat.kind); match pat.kind { PatKind::Ident(bmode, ident, ref sub) => { // First try to resolve the identifier as some existing entity, @@ -3809,8 +3816,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { PatKind::Path(ref qself, ref path) => { self.smart_resolve_path(pat.id, qself, path, PathSource::Pat); } - PatKind::Struct(ref qself, ref path, ..) => { + PatKind::Struct(ref qself, ref path, ref _fields, ref rest) => { self.smart_resolve_path(pat.id, qself, path, PathSource::Struct); + self.record_patterns_with_skipped_bindings(pat, rest); } PatKind::Or(ref ps) => { // Add a new set of bindings to the stack. `Or` here records that when a @@ -3843,6 +3851,30 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }); } + fn record_patterns_with_skipped_bindings(&mut self, pat: &Pat, rest: &ast::PatFieldsRest) { + match rest { + ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) => { + // Record that the pattern doesn't introduce all the bindings it could. + if let Some(partial_res) = self.r.partial_res_map.get(&pat.id) + && let Some(res) = partial_res.full_res() + && let Some(def_id) = res.opt_def_id() + { + self.ribs[ValueNS] + .last_mut() + .unwrap() + .patterns_with_skipped_bindings + .entry(def_id) + .or_default() + .push((pat.span, match rest { + ast::PatFieldsRest::Recovered(guar) => Err(*guar), + _ => Ok(()), + })); + } + } + ast::PatFieldsRest::None => {} + } + } + fn fresh_binding( &mut self, ident: Ident, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 09f3e848766..85ea6a74d3c 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -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,48 @@ 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 }; + 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(|(_, had_error)| had_error.is_err()) { + // 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 ty = self.r.tcx.item_name(*def_id); + for (span, _) in spans { + err.span_label( + *span, + format!( + "this pattern doesn't include `{field}`, which is \ + available in `{ty}`", + ), + ); + } + } + } + } + } + } + } + fn suggest_at_operator_in_slice_pat_with_range( &mut self, err: &mut Diag<'_>, |
