Skip to content

Commit 1615554

Browse files
authored
Unrolled build for rust-lang#134284
Rollup merge of rust-lang#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 rust-lang#74863.
2 parents 83ab648 + 733fd03 commit 1615554

File tree

8 files changed

+135
-8
lines changed

8 files changed

+135
-8
lines changed

Diff for: compiler/rustc_ast/src/ast.rs

+2
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,8 @@ pub enum PatKind {
859859
pub enum PatFieldsRest {
860860
/// `module::StructName { field, ..}`
861861
Rest,
862+
/// `module::StructName { field, syntax error }`
863+
Recovered(ErrorGuaranteed),
862864
/// `module::StructName { field }`
863865
None,
864866
}

Diff for: compiler/rustc_ast_lowering/src/pat.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
9292
span: self.lower_span(f.span),
9393
}
9494
}));
95-
break hir::PatKind::Struct(qpath, fs, *etc == ast::PatFieldsRest::Rest);
95+
break hir::PatKind::Struct(
96+
qpath,
97+
fs,
98+
matches!(
99+
etc,
100+
ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_)
101+
),
102+
);
96103
}
97104
PatKind::Tuple(pats) => {
98105
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");

Diff for: compiler/rustc_ast_pretty/src/pprust/state.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1654,11 +1654,14 @@ impl<'a> State<'a> {
16541654
},
16551655
|f| f.pat.span,
16561656
);
1657-
if *etc == ast::PatFieldsRest::Rest {
1657+
if let ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) = etc {
16581658
if !fields.is_empty() {
16591659
self.word_space(",");
16601660
}
16611661
self.word("..");
1662+
if let ast::PatFieldsRest::Recovered(_) = etc {
1663+
self.word("/* recovered parse error */");
1664+
}
16621665
}
16631666
if !empty {
16641667
self.space();

Diff for: compiler/rustc_parse/src/parser/pat.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1371,10 +1371,10 @@ impl<'a> Parser<'a> {
13711371
self.bump();
13721372
let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| {
13731373
e.span_label(path.span, "while parsing the fields for this pattern");
1374-
e.emit();
1374+
let guar = e.emit();
13751375
self.recover_stmt();
13761376
// When recovering, pretend we had `Foo { .. }`, to avoid cascading errors.
1377-
(ThinVec::new(), PatFieldsRest::Rest)
1377+
(ThinVec::new(), PatFieldsRest::Recovered(guar))
13781378
});
13791379
self.bump();
13801380
Ok(PatKind::Struct(qself, path, fields, etc))

Diff for: compiler/rustc_resolve/src/late.rs

+36-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ use rustc_ast::visit::{
1919
use rustc_ast::*;
2020
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
2121
use rustc_errors::codes::*;
22-
use rustc_errors::{Applicability, DiagArgValue, IntoDiagArg, StashKey, Suggestions};
22+
use rustc_errors::{
23+
Applicability, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
24+
};
2325
use rustc_hir::def::Namespace::{self, *};
2426
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
2527
use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId};
@@ -264,12 +266,17 @@ impl RibKind<'_> {
264266
#[derive(Debug)]
265267
pub(crate) struct Rib<'ra, R = Res> {
266268
pub bindings: IdentMap<R>,
269+
pub patterns_with_skipped_bindings: FxHashMap<DefId, Vec<(Span, Result<(), ErrorGuaranteed>)>>,
267270
pub kind: RibKind<'ra>,
268271
}
269272

270273
impl<'ra, R> Rib<'ra, R> {
271274
fn new(kind: RibKind<'ra>) -> Rib<'ra, R> {
272-
Rib { bindings: Default::default(), kind }
275+
Rib {
276+
bindings: Default::default(),
277+
patterns_with_skipped_bindings: Default::default(),
278+
kind,
279+
}
273280
}
274281
}
275282

@@ -3775,6 +3782,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
37753782
/// When a whole or-pattern has been dealt with, the thing happens.
37763783
///
37773784
/// See the implementation and `fresh_binding` for more details.
3785+
#[tracing::instrument(skip(self, bindings), level = "debug")]
37783786
fn resolve_pattern_inner(
37793787
&mut self,
37803788
pat: &Pat,
@@ -3783,7 +3791,6 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
37833791
) {
37843792
// Visit all direct subpatterns of this pattern.
37853793
pat.walk(&mut |pat| {
3786-
debug!("resolve_pattern pat={:?} node={:?}", pat, pat.kind);
37873794
match pat.kind {
37883795
PatKind::Ident(bmode, ident, ref sub) => {
37893796
// 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> {
38093816
PatKind::Path(ref qself, ref path) => {
38103817
self.smart_resolve_path(pat.id, qself, path, PathSource::Pat);
38113818
}
3812-
PatKind::Struct(ref qself, ref path, ..) => {
3819+
PatKind::Struct(ref qself, ref path, ref _fields, ref rest) => {
38133820
self.smart_resolve_path(pat.id, qself, path, PathSource::Struct);
3821+
self.record_patterns_with_skipped_bindings(pat, rest);
38143822
}
38153823
PatKind::Or(ref ps) => {
38163824
// 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> {
38433851
});
38443852
}
38453853

3854+
fn record_patterns_with_skipped_bindings(&mut self, pat: &Pat, rest: &ast::PatFieldsRest) {
3855+
match rest {
3856+
ast::PatFieldsRest::Rest | ast::PatFieldsRest::Recovered(_) => {
3857+
// Record that the pattern doesn't introduce all the bindings it could.
3858+
if let Some(partial_res) = self.r.partial_res_map.get(&pat.id)
3859+
&& let Some(res) = partial_res.full_res()
3860+
&& let Some(def_id) = res.opt_def_id()
3861+
{
3862+
self.ribs[ValueNS]
3863+
.last_mut()
3864+
.unwrap()
3865+
.patterns_with_skipped_bindings
3866+
.entry(def_id)
3867+
.or_default()
3868+
.push((pat.span, match rest {
3869+
ast::PatFieldsRest::Recovered(guar) => Err(*guar),
3870+
_ => Ok(()),
3871+
}));
3872+
}
3873+
}
3874+
ast::PatFieldsRest::None => {}
3875+
}
3876+
}
3877+
38463878
fn fresh_binding(
38473879
&mut self,
38483880
ident: Ident,

Diff for: compiler/rustc_resolve/src/late/diagnostics.rs

+43
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
430430
let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone());
431431
err.code(code);
432432

433+
self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg);
433434
self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);
434435
self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);
435436

@@ -1120,6 +1121,48 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
11201121
true
11211122
}
11221123

1124+
fn detect_missing_binding_available_from_pattern(
1125+
&mut self,
1126+
err: &mut Diag<'_>,
1127+
path: &[Segment],
1128+
following_seg: Option<&Segment>,
1129+
) {
1130+
let [segment] = path else { return };
1131+
let None = following_seg else { return };
1132+
for rib in self.ribs[ValueNS].iter().rev() {
1133+
for (def_id, spans) in &rib.patterns_with_skipped_bindings {
1134+
if let Some(fields) = self.r.field_idents(*def_id) {
1135+
for field in fields {
1136+
if field.name == segment.ident.name {
1137+
if spans.iter().all(|(_, had_error)| had_error.is_err()) {
1138+
// This resolution error will likely be fixed by fixing a
1139+
// syntax error in a pattern, so it is irrelevant to the user.
1140+
let multispan: MultiSpan =
1141+
spans.iter().map(|(s, _)| *s).collect::<Vec<_>>().into();
1142+
err.span_note(
1143+
multispan,
1144+
"this pattern had a recovered parse error which likely lost \
1145+
the expected fields",
1146+
);
1147+
err.downgrade_to_delayed_bug();
1148+
}
1149+
let ty = self.r.tcx.item_name(*def_id);
1150+
for (span, _) in spans {
1151+
err.span_label(
1152+
*span,
1153+
format!(
1154+
"this pattern doesn't include `{field}`, which is \
1155+
available in `{ty}`",
1156+
),
1157+
);
1158+
}
1159+
}
1160+
}
1161+
}
1162+
}
1163+
}
1164+
}
1165+
11231166
fn suggest_at_operator_in_slice_pat_with_range(
11241167
&mut self,
11251168
err: &mut Diag<'_>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
struct Website {
2+
url: String,
3+
title: Option<String>,
4+
}
5+
6+
fn main() {
7+
let website = Website {
8+
url: "http://www.example.com".into(),
9+
title: Some("Example Domain".into()),
10+
};
11+
12+
if let Website { url, Some(title) } = website { //~ ERROR expected `,`
13+
//~^ NOTE while parsing the fields for this pattern
14+
println!("[{}]({})", title, url); // we hide the errors for `title` and `url`
15+
}
16+
17+
if let Website { url, .. } = website { //~ NOTE this pattern
18+
println!("[{}]({})", title, url); //~ ERROR cannot find value `title` in this scope
19+
//~^ NOTE not found in this scope
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: expected `,`
2+
--> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:12:31
3+
|
4+
LL | if let Website { url, Some(title) } = website {
5+
| ------- ^
6+
| |
7+
| while parsing the fields for this pattern
8+
9+
error[E0425]: cannot find value `title` in this scope
10+
--> $DIR/struct-pattern-with-missing-fields-resolve-error.rs:18:30
11+
|
12+
LL | if let Website { url, .. } = website {
13+
| ------------------- this pattern doesn't include `title`, which is available in `Website`
14+
LL | println!("[{}]({})", title, url);
15+
| ^^^^^ not found in this scope
16+
17+
error: aborting due to 2 previous errors
18+
19+
For more information about this error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)