about summary refs log tree commit diff
path: root/src/libsyntax/parse
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsyntax/parse')
-rw-r--r--src/libsyntax/parse/lexer/mod.rs2
-rw-r--r--src/libsyntax/parse/lexer/tokentrees.rs9
-rw-r--r--src/libsyntax/parse/mod.rs17
-rw-r--r--src/libsyntax/parse/parser.rs46
-rw-r--r--src/libsyntax/parse/parser/diagnostics.rs50
-rw-r--r--src/libsyntax/parse/parser/item.rs17
6 files changed, 93 insertions, 48 deletions
diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs
index e6dc9a4c134..48cf117f65a 100644
--- a/src/libsyntax/parse/lexer/mod.rs
+++ b/src/libsyntax/parse/lexer/mod.rs
@@ -23,7 +23,7 @@ mod unicode_chars;
 #[derive(Clone, Debug)]
 pub struct UnmatchedBrace {
     pub expected_delim: token::DelimToken,
-    pub found_delim: token::DelimToken,
+    pub found_delim: Option<token::DelimToken>,
     pub found_span: Span,
     pub unclosed_span: Option<Span>,
     pub candidate_span: Option<Span>,
diff --git a/src/libsyntax/parse/lexer/tokentrees.rs b/src/libsyntax/parse/lexer/tokentrees.rs
index 853723de14f..de8ac2c71e8 100644
--- a/src/libsyntax/parse/lexer/tokentrees.rs
+++ b/src/libsyntax/parse/lexer/tokentrees.rs
@@ -80,6 +80,13 @@ impl<'a> TokenTreesReader<'a> {
                     .struct_span_err(self.token.span, msg);
                 for &(_, sp) in &self.open_braces {
                     err.span_label(sp, "un-closed delimiter");
+                    self.unmatched_braces.push(UnmatchedBrace {
+                        expected_delim: token::DelimToken::Brace,
+                        found_delim: None,
+                        found_span: self.token.span,
+                        unclosed_span: Some(sp),
+                        candidate_span: None,
+                    });
                 }
 
                 if let Some((delim, _)) = self.open_braces.last() {
@@ -170,7 +177,7 @@ impl<'a> TokenTreesReader<'a> {
                             let (tok, _) = self.open_braces.pop().unwrap();
                             self.unmatched_braces.push(UnmatchedBrace {
                                 expected_delim: tok,
-                                found_delim: other,
+                                found_delim: Some(other),
                                 found_span: self.token.span,
                                 unclosed_span: unclosed_delimiter,
                                 candidate_span: candidate,
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index f5e416b722b..6d8ecdf805b 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -1,7 +1,7 @@
 //! The main parser interface.
 
 use crate::ast;
-use crate::parse::parser::{Parser, emit_unclosed_delims};
+use crate::parse::parser::{Parser, emit_unclosed_delims, make_unclosed_delims_error};
 use crate::parse::token::Nonterminal;
 use crate::tokenstream::{self, TokenStream, TokenTree};
 use crate::print::pprust;
@@ -108,7 +108,7 @@ pub fn parse_stream_from_source_str(
         sess.source_map().new_source_file(name, source),
         override_span,
     );
-    emit_unclosed_delims(&mut errors, &sess.span_diagnostic);
+    emit_unclosed_delims(&mut errors, &sess);
     stream
 }
 
@@ -242,18 +242,9 @@ pub fn maybe_file_to_stream(
             err.buffer(&mut buffer);
             // Not using `emit_unclosed_delims` to use `db.buffer`
             for unmatched in unmatched_braces {
-                let mut db = sess.span_diagnostic.struct_span_err(unmatched.found_span, &format!(
-                    "incorrect close delimiter: `{}`",
-                    pprust::token_kind_to_string(&token::CloseDelim(unmatched.found_delim)),
-                ));
-                db.span_label(unmatched.found_span, "incorrect close delimiter");
-                if let Some(sp) = unmatched.candidate_span {
-                    db.span_label(sp, "close delimiter possibly meant for this");
+                if let Some(err) = make_unclosed_delims_error(unmatched, &sess) {
+                    err.buffer(&mut buffer);
                 }
-                if let Some(sp) = unmatched.unclosed_span {
-                    db.span_label(sp, "un-closed delimiter");
-                }
-                db.buffer(&mut buffer);
             }
             Err(buffer)
         }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 6ead1ce811d..e81d4573b73 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -27,7 +27,7 @@ use crate::symbol::{kw, sym, Symbol};
 use crate::tokenstream::{self, DelimSpan, TokenTree, TokenStream, TreeAndJoint};
 use crate::ThinVec;
 
-use errors::{Applicability, DiagnosticId, FatalError};
+use errors::{Applicability, DiagnosticBuilder, DiagnosticId, FatalError};
 use rustc_target::spec::abi::{self, Abi};
 use syntax_pos::{Span, BytePos, DUMMY_SP, FileName};
 use log::debug;
@@ -148,8 +148,7 @@ pub struct Parser<'a> {
 
 impl<'a> Drop for Parser<'a> {
     fn drop(&mut self) {
-        let diag = self.diagnostic();
-        emit_unclosed_delims(&mut self.unclosed_delims, diag);
+        emit_unclosed_delims(&mut self.unclosed_delims, &self.sess);
     }
 }
 
@@ -1370,20 +1369,31 @@ impl<'a> Parser<'a> {
     }
 }
 
-pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, handler: &errors::Handler) {
-    for unmatched in unclosed_delims.iter() {
-        let mut err = handler.struct_span_err(unmatched.found_span, &format!(
-            "incorrect close delimiter: `{}`",
-            pprust::token_kind_to_string(&token::CloseDelim(unmatched.found_delim)),
-        ));
-        err.span_label(unmatched.found_span, "incorrect close delimiter");
-        if let Some(sp) = unmatched.candidate_span {
-            err.span_label(sp, "close delimiter possibly meant for this");
-        }
-        if let Some(sp) = unmatched.unclosed_span {
-            err.span_label(sp, "un-closed delimiter");
-        }
-        err.emit();
+crate fn make_unclosed_delims_error(
+    unmatched: UnmatchedBrace,
+    sess: &ParseSess,
+) -> Option<DiagnosticBuilder<'_>> {
+    // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
+    // `unmatched_braces` only for error recovery in the `Parser`.
+    let found_delim = unmatched.found_delim?;
+    let mut err = sess.span_diagnostic.struct_span_err(unmatched.found_span, &format!(
+        "incorrect close delimiter: `{}`",
+        pprust::token_kind_to_string(&token::CloseDelim(found_delim)),
+    ));
+    err.span_label(unmatched.found_span, "incorrect close delimiter");
+    if let Some(sp) = unmatched.candidate_span {
+        err.span_label(sp, "close delimiter possibly meant for this");
+    }
+    if let Some(sp) = unmatched.unclosed_span {
+        err.span_label(sp, "un-closed delimiter");
+    }
+    Some(err)
+}
+
+pub fn emit_unclosed_delims(unclosed_delims: &mut Vec<UnmatchedBrace>, sess: &ParseSess) {
+    *sess.reached_eof.borrow_mut() |= unclosed_delims.iter()
+        .any(|unmatched_delim| unmatched_delim.found_delim.is_none());
+    for unmatched in unclosed_delims.drain(..) {
+        make_unclosed_delims_error(unmatched, sess).map(|mut e| e.emit());
     }
-    unclosed_delims.clear();
 }
diff --git a/src/libsyntax/parse/parser/diagnostics.rs b/src/libsyntax/parse/parser/diagnostics.rs
index ab2b4519cb7..fcf3b4c0aa8 100644
--- a/src/libsyntax/parse/parser/diagnostics.rs
+++ b/src/libsyntax/parse/parser/diagnostics.rs
@@ -171,6 +171,12 @@ impl RecoverQPath for Expr {
     }
 }
 
+/// Control whether the closing delimiter should be consumed when calling `Parser::consume_block`.
+crate enum ConsumeClosingDelim {
+    Yes,
+    No,
+}
+
 impl<'a> Parser<'a> {
     pub fn fatal(&self, m: &str) -> DiagnosticBuilder<'a> {
         self.span_fatal(self.token.span, m)
@@ -1105,8 +1111,8 @@ impl<'a> Parser<'a> {
             Ok(x) => x,
             Err(mut err) => {
                 err.emit();
-                // Recover from parse error.
-                self.consume_block(delim);
+                // Recover from parse error, callers expect the closing delim to be consumed.
+                self.consume_block(delim, ConsumeClosingDelim::Yes);
                 self.mk_expr(lo.to(self.prev_span), ExprKind::Err, ThinVec::new())
             }
         }
@@ -1135,6 +1141,11 @@ impl<'a> Parser<'a> {
                  // Don't attempt to recover from this unclosed delimiter more than once.
                 let unmatched = self.unclosed_delims.remove(pos);
                 let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim));
+                if unmatched.found_delim.is_none() {
+                    // We encountered `Eof`, set this fact here to avoid complaining about missing
+                    // `fn main()` when we found place to suggest the closing brace.
+                    *self.sess.reached_eof.borrow_mut() = true;
+                }
 
                 // We want to suggest the inclusion of the closing delimiter where it makes
                 // the most sense, which is immediately after the last token:
@@ -1154,9 +1165,16 @@ impl<'a> Parser<'a> {
                     delim.to_string(),
                     Applicability::MaybeIncorrect,
                 );
-                err.emit();
-                self.expected_tokens.clear();  // reduce errors
-                Ok(true)
+                if unmatched.found_delim.is_none() {
+                    // Encountered `Eof` when lexing blocks. Do not recover here to avoid knockdown
+                    // errors which would be emitted elsewhere in the parser and let other error
+                    // recovery consume the rest of the file.
+                    Err(err)
+                } else {
+                    err.emit();
+                    self.expected_tokens.clear();  // Reduce the number of errors.
+                    Ok(true)
+                }
             }
             _ => Err(err),
         }
@@ -1164,7 +1182,12 @@ impl<'a> Parser<'a> {
 
     /// Recovers from `pub` keyword in places where it seems _reasonable_ but isn't valid.
     pub(super) fn eat_bad_pub(&mut self) {
-        if self.token.is_keyword(kw::Pub) {
+        // When `unclosed_delims` is populated, it means that the code being parsed is already
+        // quite malformed, which might mean that, for example, a pub struct definition could be
+        // parsed as being a trait item, which is invalid and this error would trigger
+        // unconditionally, resulting in misleading diagnostics. Because of this, we only attempt
+        // this nice to have recovery for code that is otherwise well formed.
+        if self.token.is_keyword(kw::Pub) && self.unclosed_delims.is_empty() {
             match self.parse_visibility(false) {
                 Ok(vis) => {
                     self.diagnostic()
@@ -1422,15 +1445,26 @@ impl<'a> Parser<'a> {
         Ok(param)
     }
 
-    pub(super) fn consume_block(&mut self, delim: token::DelimToken) {
+    pub(super) fn consume_block(
+        &mut self,
+        delim: token::DelimToken,
+        consume_close: ConsumeClosingDelim,
+    ) {
         let mut brace_depth = 0;
         loop {
             if self.eat(&token::OpenDelim(delim)) {
                 brace_depth += 1;
-            } else if self.eat(&token::CloseDelim(delim)) {
+            } else if self.check(&token::CloseDelim(delim)) {
                 if brace_depth == 0 {
+                    if let ConsumeClosingDelim::Yes = consume_close {
+                        // Some of the callers of this method expect to be able to parse the
+                        // closing delimiter themselves, so we leave it alone. Otherwise we advance
+                        // the parser.
+                        self.bump();
+                    }
                     return;
                 } else {
+                    self.bump();
                     brace_depth -= 1;
                     continue;
                 }
diff --git a/src/libsyntax/parse/parser/item.rs b/src/libsyntax/parse/parser/item.rs
index fe125336190..5b60e7e6dba 100644
--- a/src/libsyntax/parse/parser/item.rs
+++ b/src/libsyntax/parse/parser/item.rs
@@ -1,5 +1,5 @@
-use super::{Parser, PResult, PathStyle, SemiColonMode, BlockMode};
-use super::diagnostics::{Error, dummy_arg};
+use super::{Parser, PResult, PathStyle};
+use super::diagnostics::{Error, dummy_arg, ConsumeClosingDelim};
 
 use crate::maybe_whole;
 use crate::ptr::P;
@@ -339,7 +339,7 @@ impl<'a> Parser<'a> {
                 let ident = self.parse_ident().unwrap();
                 self.bump();  // `(`
                 let kw_name = self.recover_first_param();
-                self.consume_block(token::Paren);
+                self.consume_block(token::Paren, ConsumeClosingDelim::Yes);
                 let (kw, kw_name, ambiguous) = if self.check(&token::RArrow) {
                     self.eat_to_tokens(&[&token::OpenDelim(token::Brace)]);
                     self.bump();  // `{`
@@ -357,7 +357,7 @@ impl<'a> Parser<'a> {
                 let msg = format!("missing `{}` for {} definition", kw, kw_name);
                 let mut err = self.diagnostic().struct_span_err(sp, &msg);
                 if !ambiguous {
-                    self.consume_block(token::Brace);
+                    self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
                     let suggestion = format!("add `{}` here to parse `{}` as a public {}",
                                              kw,
                                              ident,
@@ -672,7 +672,8 @@ impl<'a> Parser<'a> {
                 Err(mut err) => {
                     err.emit();
                     if !at_end {
-                        self.recover_stmt_(SemiColonMode::Break, BlockMode::Break);
+                        self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+                        break;
                     }
                 }
             }
@@ -861,7 +862,8 @@ impl<'a> Parser<'a> {
                     Err(mut e) => {
                         e.emit();
                         if !at_end {
-                            self.recover_stmt_(SemiColonMode::Break, BlockMode::Break);
+                            self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+                            break;
                         }
                     }
                 }
@@ -1520,7 +1522,7 @@ impl<'a> Parser<'a> {
         if self.eat(&token::OpenDelim(token::Brace)) {
             while self.token != token::CloseDelim(token::Brace) {
                 let field = self.parse_struct_decl_field().map_err(|e| {
-                    self.recover_stmt();
+                    self.consume_block(token::Brace, ConsumeClosingDelim::No);
                     recovered = true;
                     e
                 });
@@ -1528,6 +1530,7 @@ impl<'a> Parser<'a> {
                     Ok(field) => fields.push(field),
                     Err(mut err) => {
                         err.emit();
+                        break;
                     }
                 }
             }