Skip to content

Commit d615130

Browse files
committed
use fields in autotupling parse recovery
We introduce a new `PartialPResult`, which is like `PResult`, except instead of the `Err` value being just a `DiagnosticBuilder`, it's a tuple whose first element is of the `Ok` type and whose second element is the diagnostic-builder: this provides a way to return to callers any useful parsing work we did before hitting the error (at the cost of having to sprinkle some awkward `.map_err`s around to convert between `PResult` and `PartialPResult`). `PartialPResult` is then used for more thorough recovery when a user omits the parens from what we presume ought to have been a tuple pattern: after emitting an error for the erroneous pattern, we can continue working with the destructured fields as if the user had correctly written a tuple, potentially catching more errors in subsequent code without the user needing to recompile.
1 parent ed6679c commit d615130

File tree

4 files changed

+64
-47
lines changed

4 files changed

+64
-47
lines changed

src/libsyntax/parse/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ use std::str;
3030

3131
pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
3232

33+
/// Like `PResult`, but the `Err` value is a tuple whose first value is a
34+
/// "partial result" (of some useful work we did before hitting the error), and
35+
/// whose second value is the error.
36+
pub type PartialPResult<'a, T> = Result<T, (T, DiagnosticBuilder<'a>)>;
37+
3338
#[macro_use]
3439
pub mod parser;
3540

src/libsyntax/parse/parser.rs

+38-17
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use parse::{new_sub_parser_from_file, ParseSess, Directory, DirectoryOwnership};
5353
use util::parser::{AssocOp, Fixity};
5454
use print::pprust;
5555
use ptr::P;
56-
use parse::PResult;
56+
use parse::{PResult, PartialPResult};
5757
use tokenstream::{self, Delimited, ThinTokenStream, TokenTree, TokenStream};
5858
use symbol::{Symbol, keywords};
5959
use util::ThinVec;
@@ -3485,7 +3485,7 @@ impl<'a> Parser<'a> {
34853485
}
34863486

34873487
fn parse_pat_tuple_elements(&mut self, unary_needs_comma: bool)
3488-
-> PResult<'a, (Vec<P<Pat>>, Option<usize>)> {
3488+
-> PartialPResult<'a, (Vec<P<Pat>>, Option<usize>)> {
34893489
let mut fields = vec![];
34903490
let mut ddpos = None;
34913491

@@ -3494,19 +3494,24 @@ impl<'a> Parser<'a> {
34943494
ddpos = Some(fields.len());
34953495
if self.eat(&token::Comma) {
34963496
// `..` needs to be followed by `)` or `, pat`, `..,)` is disallowed.
3497-
fields.push(self.parse_pat()?);
3497+
let field = self.parse_pat()
3498+
.map_err(|err| ((fields.clone(), ddpos), err))?;
3499+
fields.push(field);
34983500
}
34993501
} else if ddpos.is_some() && self.eat(&token::DotDot) {
35003502
// Emit a friendly error, ignore `..` and continue parsing
35013503
self.span_err(self.prev_span, "`..` can only be used once per \
35023504
tuple or tuple struct pattern");
35033505
} else {
3504-
fields.push(self.parse_pat()?);
3506+
let field = self.parse_pat()
3507+
.map_err(|err| ((fields.clone(), ddpos), err))?;
3508+
fields.push(field);
35053509
}
35063510

35073511
if !self.check(&token::CloseDelim(token::Paren)) ||
35083512
(unary_needs_comma && fields.len() == 1 && ddpos.is_none()) {
3509-
self.expect(&token::Comma)?;
3513+
self.expect(&token::Comma)
3514+
.map_err(|err| ((fields.clone(), ddpos), err))?;
35103515
}
35113516
}
35123517

@@ -3690,11 +3695,11 @@ impl<'a> Parser<'a> {
36903695
}))
36913696
}
36923697

3693-
/// A wrapper around `parse_pat` with some special error handling for the
3694-
/// "top-level" patterns in a match arm, `for` loop, `let`, &c. (in contast
3695-
/// to subpatterns within such).
3698+
/// A wrapper around `parse_pat` with some special error handling and
3699+
/// recovery for the "top-level" patterns in a match arm, `for` loop,
3700+
/// `let`, &c. (in contast to subpatterns within such).
36963701
pub fn parse_top_level_pat(&mut self) -> PResult<'a, P<Pat>> {
3697-
let pat = self.parse_pat()?;
3702+
let mut pat = self.parse_pat()?;
36983703
if self.token == token::Comma {
36993704
// An unexpected comma after a top-level pattern is a clue that the
37003705
// user (perhaps more accustomed to some other language) forgot the
@@ -3703,12 +3708,21 @@ impl<'a> Parser<'a> {
37033708
// later.
37043709
let comma_span = self.span;
37053710
self.bump();
3706-
if let Err(mut err) = self.parse_pat_tuple_elements(false) {
3707-
// We didn't expect this to work anyway; we just wanted
3708-
// to advance to the end of the comma-sequence so we know
3709-
// the span to suggest parenthesizing
3710-
err.cancel();
3711-
}
3711+
let mut fields = vec![pat.clone()];
3712+
let (rest_fields, ddpos) = match self.parse_pat_tuple_elements(false) {
3713+
Ok(fields_ddpos) => fields_ddpos,
3714+
Err((fields_ddpos, mut err)) => {
3715+
// We didn't really expect this to work anyway; we want the
3716+
// partial result (so that post-recovery code knows about
3717+
// any bindings) and to advance to the end of the
3718+
// comma-sequence (so we know the span to suggest
3719+
// parenthesizing), but we'll emit our own error in just a
3720+
// moment
3721+
err.cancel();
3722+
fields_ddpos
3723+
}
3724+
};
3725+
fields.extend(rest_fields);
37123726
let seq_span = pat.span.to(self.prev_span);
37133727
let mut err = self.struct_span_err(comma_span,
37143728
"unexpected `,` in pattern");
@@ -3717,6 +3731,11 @@ impl<'a> Parser<'a> {
37173731
format!("({})", seq_snippet));
37183732
}
37193733
err.emit();
3734+
// Now that we've emitted our own error, the rest of the parser
3735+
// can pretend this was actually a tuple
3736+
pat = P(Pat { node: PatKind::Tuple(fields, ddpos),
3737+
span: seq_span,
3738+
id: ast::DUMMY_NODE_ID });
37203739
}
37213740
Ok(pat)
37223741
}
@@ -3746,7 +3765,8 @@ impl<'a> Parser<'a> {
37463765
token::OpenDelim(token::Paren) => {
37473766
// Parse (pat,pat,pat,...) as tuple pattern
37483767
self.bump();
3749-
let (fields, ddpos) = self.parse_pat_tuple_elements(true)?;
3768+
let (fields, ddpos) = self.parse_pat_tuple_elements(true)
3769+
.map_err(|(_partial, err)| err)?;
37503770
self.expect(&token::CloseDelim(token::Paren))?;
37513771
pat = PatKind::Tuple(fields, ddpos);
37523772
}
@@ -3839,7 +3859,8 @@ impl<'a> Parser<'a> {
38393859
}
38403860
// Parse tuple struct or enum pattern
38413861
self.bump();
3842-
let (fields, ddpos) = self.parse_pat_tuple_elements(false)?;
3862+
let (fields, ddpos) = self.parse_pat_tuple_elements(false)
3863+
.map_err(|(_partial, err)| err)?;
38433864
self.expect(&token::CloseDelim(token::Paren))?;
38443865
pat = PatKind::TupleStruct(path, fields, ddpos)
38453866
}

src/test/ui/did_you_mean/issue-48492-tuple-destructure-missing-parens.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,15 @@ struct Genome {
4242
allosomes: (Allosome, Allosome)
4343
}
4444

45+
fn three_slice_to_tuple<T>(slice: &[T]) -> (T, T, T) {
46+
(slice[0], slice[1], slice[2])
47+
}
48+
4549
fn find_start_codon(strand: &[Nucleotide]) -> Option<usize> {
4650
let mut reading_frame = strand.windows(3);
47-
while let b1, b2, b3 = reading_frame.next().expect("there should be a start codon") {
48-
//~^ ERROR unexpected `,` in pattern
51+
while let b1, b2, b3 = three_slice_to_tuple(reading_frame.next()
52+
//~^ ERROR unexpected `,` in pattern
53+
.expect("there should be a start codon")) {
4954
// ...
5055
}
5156
None
@@ -54,11 +59,9 @@ fn find_start_codon(strand: &[Nucleotide]) -> Option<usize> {
5459
fn find_thr(strand: &[Nucleotide]) -> Option<usize> {
5560
let mut reading_frame = strand.windows(3);
5661
let mut i = 0;
57-
if let b1, b2, b3 = reading_frame.next().unwrap() {
62+
if let b1, b2, b3 = three_slice_to_tuple(reading_frame.next().unwrap()) {
5863
//~^ ERROR unexpected `,` in pattern
5964
match (b1, b2, b3) {
60-
//~^ ERROR cannot find value `b2` in this scope
61-
//~| ERROR cannot find value `b3` in this scope
6265
Nucleotide::Adenine, Nucleotide::Cytosine, _ => {
6366
//~^ ERROR unexpected `,` in pattern
6467
return Some(i);
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,38 @@
11
error: unexpected `,` in pattern
2-
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:47:17
2+
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:51:17
33
|
4-
47 | while let b1, b2, b3 = reading_frame.next().expect("there should be a start codon") {
4+
51 | while let b1, b2, b3 = three_slice_to_tuple(reading_frame.next()
55
| --^------- help: try adding parentheses: `(b1, b2, b3)`
66

77
error: unexpected `,` in pattern
8-
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:57:14
8+
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:62:14
99
|
10-
57 | if let b1, b2, b3 = reading_frame.next().unwrap() {
10+
62 | if let b1, b2, b3 = three_slice_to_tuple(reading_frame.next().unwrap()) {
1111
| --^------- help: try adding parentheses: `(b1, b2, b3)`
1212

1313
error: unexpected `,` in pattern
14-
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:62:32
14+
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:65:32
1515
|
16-
62 | Nucleotide::Adenine, Nucleotide::Cytosine, _ => {
16+
65 | Nucleotide::Adenine, Nucleotide::Cytosine, _ => {
1717
| -------------------^------------------------ help: try adding parentheses: `(Nucleotide::Adenine, Nucleotide::Cytosine, _)`
1818

1919
error: unexpected `,` in pattern
20-
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:74:10
20+
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:77:10
2121
|
22-
74 | for x, _barr_body in women.iter().map(|woman| woman.allosomes.clone()) {
22+
77 | for x, _barr_body in women.iter().map(|woman| woman.allosomes.clone()) {
2323
| -^----------- help: try adding parentheses: `(x, _barr_body)`
2424

2525
error: unexpected `,` in pattern
26-
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:78:10
26+
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:81:10
2727
|
28-
78 | for x, y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) {
28+
81 | for x, y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) {
2929
| -^------------------- help: try adding parentheses: `(x, y @ Allosome::Y(_))`
3030

3131
error: unexpected `,` in pattern
32-
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:86:14
32+
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:89:14
3333
|
34-
86 | let women, men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned()
34+
89 | let women, men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned()
3535
| -----^---- help: try adding parentheses: `(women, men)`
3636

37-
error[E0425]: cannot find value `b2` in this scope
38-
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:59:20
39-
|
40-
59 | match (b1, b2, b3) {
41-
| ^^ did you mean `b1`?
42-
43-
error[E0425]: cannot find value `b3` in this scope
44-
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:59:24
45-
|
46-
59 | match (b1, b2, b3) {
47-
| ^^ did you mean `b1`?
48-
49-
error: aborting due to 8 previous errors
37+
error: aborting due to 6 previous errors
5038

0 commit comments

Comments
 (0)