Skip to content

Commit 0036e72

Browse files
committed
Try to recover from unmatched delimiters in the parser
1 parent 7ee7207 commit 0036e72

15 files changed

+293
-86
lines changed

src/librustc_metadata/cstore_impl.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ impl cstore::CStore {
465465

466466
let source_file = sess.parse_sess.source_map().new_source_file(source_name, def.body);
467467
let local_span = Span::new(source_file.start_pos, source_file.end_pos, NO_EXPANSION);
468-
let body = source_file_to_stream(&sess.parse_sess, source_file, None);
468+
let body = source_file_to_stream(&sess.parse_sess, source_file, None).0;
469469

470470
// Mark the attrs as used
471471
let attrs = data.get_item_attrs(id.index, sess);

src/libsyntax/parse/lexer/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub struct StringReader<'a> {
6666
/// The raw source span which *does not* take `override_span` into account
6767
span_src_raw: Span,
6868
open_braces: Vec<(token::DelimToken, Span)>,
69+
crate unmatched_braces: Vec<(token::DelimToken, Span)>,
6970
/// The type and spans for all braces
7071
///
7172
/// Used only for error recovery when arriving to EOF with mismatched braces.
@@ -221,6 +222,7 @@ impl<'a> StringReader<'a> {
221222
span_src_raw: syntax_pos::DUMMY_SP,
222223
open_braces: Vec::new(),
223224
matching_delim_spans: Vec::new(),
225+
unmatched_braces: Vec::new(),
224226
override_span,
225227
last_unclosed_found_span: None,
226228
}

src/libsyntax/parse/lexer/tokentrees.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ impl<'a> StringReader<'a> {
4949
token::Eof => {
5050
let msg = "this file contains an un-closed delimiter";
5151
let mut err = self.sess.span_diagnostic.struct_span_err(self.span, msg);
52-
for &(_, sp) in &self.open_braces {
52+
for &(tok, sp) in &self.open_braces {
5353
err.span_label(sp, "un-closed delimiter");
54+
self.unmatched_braces.push((tok, sp));
5455
}
5556

5657
if let Some((delim, _)) = self.open_braces.last() {
@@ -134,7 +135,7 @@ impl<'a> StringReader<'a> {
134135
}
135136
err.emit();
136137
}
137-
self.open_braces.pop().unwrap();
138+
self.unmatched_braces.push(self.open_braces.pop().unwrap());
138139

139140
// If the incorrect delimiter matches an earlier opening
140141
// delimiter, then don't consume it (it can be used to

src/libsyntax/parse/mod.rs

+25-17
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,13 @@ crate fn parse_stmt_from_source_str(name: FileName, source: String, sess: &Parse
168168
new_parser_from_source_str(sess, name, source).parse_stmt()
169169
}
170170

171-
pub fn parse_stream_from_source_str(name: FileName, source: String, sess: &ParseSess,
172-
override_span: Option<Span>)
173-
-> TokenStream {
174-
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span)
171+
pub fn parse_stream_from_source_str(
172+
name: FileName,
173+
source: String,
174+
sess: &ParseSess,
175+
override_span: Option<Span>,
176+
) -> TokenStream {
177+
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span).0
175178
}
176179

177180
// Create a new parser from a source string
@@ -191,26 +194,28 @@ pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path) -> Parser<'a>
191194
/// Given a session, a crate config, a path, and a span, add
192195
/// the file at the given path to the source_map, and return a parser.
193196
/// On an error, use the given span as the source of the problem.
194-
crate fn new_sub_parser_from_file<'a>(sess: &'a ParseSess,
195-
path: &Path,
196-
directory_ownership: DirectoryOwnership,
197-
module_name: Option<String>,
198-
sp: Span) -> Parser<'a> {
197+
crate fn new_sub_parser_from_file<'a>(
198+
sess: &'a ParseSess,
199+
path: &Path,
200+
directory_ownership: DirectoryOwnership,
201+
module_name: Option<String>,
202+
sp: Span,
203+
) -> Parser<'a> {
199204
let mut p = source_file_to_parser(sess, file_to_source_file(sess, path, Some(sp)));
200205
p.directory.ownership = directory_ownership;
201206
p.root_module_name = module_name;
202207
p
203208
}
204209

205210
/// Given a source_file and config, return a parser
206-
fn source_file_to_parser(sess: & ParseSess, source_file: Lrc<SourceFile>) -> Parser {
211+
fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser {
207212
let end_pos = source_file.end_pos;
208-
let mut parser = stream_to_parser(sess, source_file_to_stream(sess, source_file, None));
209-
213+
let (tts, open_braces) = source_file_to_stream(sess, source_file, None);
214+
let mut parser = stream_to_parser(sess, tts);
215+
parser.open_braces = open_braces;
210216
if parser.token == token::Eof && parser.span.is_dummy() {
211217
parser.span = Span::new(end_pos, end_pos, parser.span.ctxt());
212218
}
213-
214219
parser
215220
}
216221

@@ -240,12 +245,15 @@ fn file_to_source_file(sess: &ParseSess, path: &Path, spanopt: Option<Span>)
240245
}
241246

242247
/// Given a source_file, produce a sequence of token-trees
243-
pub fn source_file_to_stream(sess: &ParseSess,
244-
source_file: Lrc<SourceFile>,
245-
override_span: Option<Span>) -> TokenStream {
248+
pub fn source_file_to_stream(
249+
sess: &ParseSess,
250+
source_file: Lrc<SourceFile>,
251+
override_span: Option<Span>,
252+
) -> (TokenStream, Vec<(token::DelimToken, Span)>) {
246253
let mut srdr = lexer::StringReader::new(sess, source_file, override_span);
247254
srdr.real_token();
248-
panictry!(srdr.parse_all_token_trees())
255+
let tt = panictry!(srdr.parse_all_token_trees());
256+
(tt, srdr.unmatched_braces)
249257
}
250258

251259
/// Given stream and the `ParseSess`, produce a parser

src/libsyntax/parse/parser.rs

+80-16
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ pub struct Parser<'a> {
250250
desugar_doc_comments: bool,
251251
/// Whether we should configure out of line modules as we parse.
252252
pub cfg_mods: bool,
253+
/// Unmatched open delimiters, used for parse recovery when multiple tokens could be valid.
254+
crate open_braces: Vec<(token::DelimToken, Span)>,
253255
}
254256

255257

@@ -569,6 +571,7 @@ impl<'a> Parser<'a> {
569571
},
570572
desugar_doc_comments,
571573
cfg_mods: true,
574+
open_braces: Vec::new(),
572575
};
573576

574577
let tok = parser.next_tok();
@@ -635,6 +638,55 @@ impl<'a> Parser<'a> {
635638
}
636639
}
637640

641+
fn recover_closing_delimiter(
642+
&mut self,
643+
tokens: &[token::Token],
644+
mut err: DiagnosticBuilder<'a>,
645+
) -> PResult<'a, ()> {
646+
let mut pos = None;
647+
let mut tokens: Vec<token::Token> = tokens.iter().map(|t| (*t).clone()).collect();
648+
tokens.extend(self.expected_tokens.iter().filter_map(|t| match t {
649+
TokenType::Token(t) => Some((*t).clone()),
650+
_ => None,
651+
}));
652+
for (i, (delim, span)) in self.open_braces.iter().enumerate() {
653+
if tokens.contains(&token::CloseDelim(*delim)) && self.span > *span {
654+
pos = Some(i);
655+
// do not break, we want to use the last one that would apply
656+
}
657+
}
658+
match pos {
659+
Some(pos) => {
660+
// Recover and assume that the detected unclosed delimiter was meant for
661+
// this location. Emit the diagnostic and act as if the delimiter was
662+
// present for the parser's sake.
663+
664+
// Don't attempt to recover from this unclosed delimiter more than once.
665+
let (delim, open_sp) = self.open_braces.remove(pos);
666+
let delim = TokenType::Token(token::CloseDelim(delim));
667+
668+
// We want to suggest the inclusion of the closing delimiter where it makes
669+
// the most sense, which is immediately after the last token:
670+
//
671+
// {foo(bar {}}
672+
// - - ^ expected one of `)`, <...>
673+
// | |
674+
// | help: ...missing `)` might belong here
675+
// you might have meant to close this...
676+
err.span_label(open_sp, "if you meant to close this...");
677+
err.span_suggestion_short_with_applicability(
678+
self.sess.source_map().next_point(self.prev_span),
679+
&format!("...the missing {} may belong here", delim.to_string()),
680+
delim.to_string(),
681+
Applicability::MaybeIncorrect,
682+
);
683+
err.emit();
684+
Ok(())
685+
}
686+
_ => Err(err),
687+
}
688+
}
689+
638690
/// Expect and consume the token t. Signal an error if
639691
/// the next token is not t.
640692
pub fn expect(&mut self, t: &token::Token) -> PResult<'a, ()> {
@@ -668,7 +720,7 @@ impl<'a> Parser<'a> {
668720
err.span_label(self.span, "unexpected token");
669721
}
670722
}
671-
Err(err)
723+
self.recover_closing_delimiter(&[t.clone()], err)
672724
}
673725
} else {
674726
self.expect_one_of(slice::from_ref(t), &[])
@@ -761,7 +813,7 @@ impl<'a> Parser<'a> {
761813
err.span_label(self.span, "unexpected token");
762814
}
763815
}
764-
Err(err)
816+
self.recover_closing_delimiter(edible, err)
765817
}
766818
}
767819

@@ -1075,11 +1127,12 @@ impl<'a> Parser<'a> {
10751127
/// Parse a sequence, not including the closing delimiter. The function
10761128
/// f must consume tokens until reaching the next separator or
10771129
/// closing bracket.
1078-
pub fn parse_seq_to_before_end<T, F>(&mut self,
1079-
ket: &token::Token,
1080-
sep: SeqSep,
1081-
f: F)
1082-
-> PResult<'a, Vec<T>>
1130+
pub fn parse_seq_to_before_end<T, F>(
1131+
&mut self,
1132+
ket: &token::Token,
1133+
sep: SeqSep,
1134+
f: F,
1135+
) -> PResult<'a, Vec<T>>
10831136
where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>
10841137
{
10851138
self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f)
@@ -1124,8 +1177,12 @@ impl<'a> Parser<'a> {
11241177
v.push(t);
11251178
continue;
11261179
},
1127-
Err(mut e) => {
1128-
e.cancel();
1180+
Err(mut err) => {
1181+
err.cancel();
1182+
let kets: Vec<token::Token> = kets.iter()
1183+
.map(|t| (*t).clone())
1184+
.collect();
1185+
let _ = self.recover_closing_delimiter(&kets[..], e);
11291186
break;
11301187
}
11311188
}
@@ -1141,7 +1198,13 @@ impl<'a> Parser<'a> {
11411198
break;
11421199
}
11431200

1144-
let t = f(self)?;
1201+
let t = match f(self) {
1202+
Ok(t) => t,
1203+
Err(e) => {
1204+
let kets: Vec<token::Token> = kets.iter().map(|t| (*t).clone()).collect();
1205+
return self.recover_closing_delimiter(&kets[..], e).map(|_| v);
1206+
}
1207+
};
11451208
v.push(t);
11461209
}
11471210

@@ -1151,12 +1214,13 @@ impl<'a> Parser<'a> {
11511214
/// Parse a sequence, including the closing delimiter. The function
11521215
/// f must consume tokens until reaching the next separator or
11531216
/// closing bracket.
1154-
fn parse_unspanned_seq<T, F>(&mut self,
1155-
bra: &token::Token,
1156-
ket: &token::Token,
1157-
sep: SeqSep,
1158-
f: F)
1159-
-> PResult<'a, Vec<T>> where
1217+
fn parse_unspanned_seq<T, F>(
1218+
&mut self,
1219+
bra: &token::Token,
1220+
ket: &token::Token,
1221+
sep: SeqSep,
1222+
f: F,
1223+
) -> PResult<'a, Vec<T>> where
11601224
F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
11611225
{
11621226
self.expect(bra)?;

src/test/ui/parser-recovery-3.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct A;
12+
13+
impl A {
14+
fn banana(&mut self) {
15+
fn peach(this: &Self, foo: usize {
16+
//~^ ERROR expected one of
17+
//~| ERROR expected pattern
18+
}
19+
}
20+
//~^ ERROR expected one of
21+
//~| ERROR incorrect close delimiter
22+
}
23+
24+
fn main() {}

src/test/ui/parser-recovery-3.stderr

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
error: incorrect close delimiter: `}`
2+
--> $DIR/parser-recovery-3.rs:19:5
3+
|
4+
LL | fn banana(&mut self) {
5+
| - close delimiter possibly meant for this
6+
LL | fn peach(this: &Self, foo: usize {
7+
| - un-closed delimiter
8+
...
9+
LL | }
10+
| ^ incorrect close delimiter
11+
12+
error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `{`
13+
--> $DIR/parser-recovery-3.rs:15:42
14+
|
15+
LL | fn peach(this: &Self, foo: usize {
16+
| - -^ expected one of 7 possible tokens here
17+
| | |
18+
| | help: ...the missing `)` may belong here
19+
| if you meant to close this...
20+
21+
error: expected pattern, found `{`
22+
--> $DIR/parser-recovery-3.rs:15:42
23+
|
24+
LL | fn peach(this: &Self, foo: usize {
25+
| ^ expected pattern
26+
27+
error: expected one of `->`, `where`, or `{`, found `}`
28+
--> $DIR/parser-recovery-3.rs:19:5
29+
|
30+
LL | }
31+
| ^ expected one of `->`, `where`, or `{` here
32+
33+
error: aborting due to 4 previous errors
34+

src/test/ui/parser-recovery-4.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn foo(_: usize) {}
12+
fn bar() {}
13+
14+
fn main() {
15+
let x = 1;
16+
foo(x
17+
bar()
18+
//~^ ERROR expected one of
19+
//~^^^ ERROR this function takes 1 parameter but 2 parameters were supplied
20+
}
21+
//~^ ERROR incorrect close delimiter

0 commit comments

Comments
 (0)