Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always emit unclosed delimiter diagnostics #58903

Merged
merged 11 commits into from Mar 8, 2019
4 changes: 2 additions & 2 deletions src/librustc_metadata/cstore_impl.rs
Expand Up @@ -439,8 +439,8 @@ impl cstore::CStore {

let source_file = sess.parse_sess.source_map().new_source_file(source_name, def.body);
let local_span = Span::new(source_file.start_pos, source_file.end_pos, NO_EXPANSION);
let (body, errors) = source_file_to_stream(&sess.parse_sess, source_file, None);
emit_unclosed_delims(&errors, &sess.diagnostic());
let (body, mut errors) = source_file_to_stream(&sess.parse_sess, source_file, None);
emit_unclosed_delims(&mut errors, &sess.diagnostic());

// Mark the attrs as used
let attrs = data.get_item_attrs(id.index, sess);
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/ext/tt/macro_parser.rs
Expand Up @@ -761,7 +761,7 @@ pub fn parse(
else if bb_items.is_empty() && next_items.is_empty() {
return Failure(
parser.span,
parser.token,
parser.token.clone(),
"no rules expected this token in macro call",
);
}
Expand Down
11 changes: 9 additions & 2 deletions src/libsyntax/parse/mod.rs
Expand Up @@ -6,6 +6,7 @@ use crate::source_map::{SourceMap, FilePathMapping};
use crate::feature_gate::UnstableFeatures;
use crate::parse::parser::Parser;
use crate::symbol::Symbol;
use crate::syntax::parse::parser::emit_unclosed_delims;
use crate::tokenstream::{TokenStream, TokenTree};
use crate::diagnostics::plugin::ErrorMap;
use crate::print::pprust::token_to_string;
Expand Down Expand Up @@ -141,8 +142,14 @@ pub fn parse_stream_from_source_str(
source: String,
sess: &ParseSess,
override_span: Option<Span>,
) -> (TokenStream, Vec<lexer::UnmatchedBrace>) {
source_file_to_stream(sess, sess.source_map().new_source_file(name, source), override_span)
) -> TokenStream {
let (stream, mut errors) = source_file_to_stream(
sess,
sess.source_map().new_source_file(name, source),
override_span,
);
emit_unclosed_delims(&mut errors, &sess.span_diagnostic);
stream
}

/// Creates a new parser from a source string.
Expand Down
43 changes: 34 additions & 9 deletions src/libsyntax/parse/parser.rs
Expand Up @@ -46,7 +46,7 @@ use crate::ThinVec;
use crate::tokenstream::{self, DelimSpan, TokenTree, TokenStream, TreeAndJoint};
use crate::symbol::{Symbol, keywords};

use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
use errors::{Applicability, DiagnosticBuilder, DiagnosticId, FatalError};
use rustc_target::spec::abi::{self, Abi};
use syntax_pos::{Span, MultiSpan, BytePos, FileName};
use log::{debug, trace};
Expand Down Expand Up @@ -256,8 +256,15 @@ pub struct Parser<'a> {
/// it gets removed from here. Every entry left at the end gets emitted as an independent
/// error.
crate unclosed_delims: Vec<UnmatchedBrace>,
last_unexpected_token_span: Option<Span>,
}

impl<'a> Drop for Parser<'a> {
fn drop(&mut self) {
let diag = self.diagnostic();
emit_unclosed_delims(&mut self.unclosed_delims, diag);
}
}

#[derive(Clone)]
struct TokenCursor {
Expand Down Expand Up @@ -582,6 +589,7 @@ impl<'a> Parser<'a> {
unmatched_angle_bracket_count: 0,
max_angle_bracket_count: 0,
unclosed_delims: Vec::new(),
last_unexpected_token_span: None,
};

let tok = parser.next_tok();
Expand Down Expand Up @@ -775,6 +783,8 @@ impl<'a> Parser<'a> {
} else if inedible.contains(&self.token) {
// leave it in the input
Ok(false)
} else if self.last_unexpected_token_span == Some(self.span) {
FatalError.raise();
} else {
let mut expected = edible.iter()
.map(|x| TokenType::Token(x.clone()))
Expand Down Expand Up @@ -802,6 +812,7 @@ impl<'a> Parser<'a> {
(self.sess.source_map().next_point(self.prev_span),
format!("expected {} here", expect)))
};
self.last_unexpected_token_span = Some(self.span);
let mut err = self.fatal(&msg_exp);
if self.token.is_ident_named("and") {
err.span_suggestion_short(
Expand Down Expand Up @@ -1497,9 +1508,13 @@ impl<'a> Parser<'a> {
pub fn parse_trait_item(&mut self, at_end: &mut bool) -> PResult<'a, TraitItem> {
maybe_whole!(self, NtTraitItem, |x| x);
let attrs = self.parse_outer_attributes()?;
let mut unclosed_delims = vec![];
let (mut item, tokens) = self.collect_tokens(|this| {
this.parse_trait_item_(at_end, attrs)
let item = this.parse_trait_item_(at_end, attrs);
unclosed_delims.append(&mut this.unclosed_delims);
item
})?;
self.unclosed_delims.append(&mut unclosed_delims);
// See `parse_item` for why this clause is here.
if !item.attrs.iter().any(|attr| attr.style == AttrStyle::Inner) {
item.tokens = Some(tokens);
Expand Down Expand Up @@ -6333,7 +6348,10 @@ impl<'a> Parser<'a> {
fn_inputs.append(&mut input);
(fn_inputs, recovered)
} else {
return self.unexpected();
match self.expect_one_of(&[], &[]) {
Err(err) => return Err(err),
Ok(recovered) => (vec![self_arg], recovered),
}
}
} else {
self.parse_seq_to_before_end(&token::CloseDelim(token::Paren), sep, parse_arg_fn)?
Expand Down Expand Up @@ -6459,9 +6477,13 @@ impl<'a> Parser<'a> {
pub fn parse_impl_item(&mut self, at_end: &mut bool) -> PResult<'a, ImplItem> {
maybe_whole!(self, NtImplItem, |x| x);
let attrs = self.parse_outer_attributes()?;
let mut unclosed_delims = vec![];
let (mut item, tokens) = self.collect_tokens(|this| {
this.parse_impl_item_(at_end, attrs)
let item = this.parse_impl_item_(at_end, attrs);
unclosed_delims.append(&mut this.unclosed_delims);
item
})?;
self.unclosed_delims.append(&mut unclosed_delims);

// See `parse_item` for why this clause is here.
if !item.attrs.iter().any(|attr| attr.style == AttrStyle::Inner) {
Expand Down Expand Up @@ -7781,9 +7803,13 @@ impl<'a> Parser<'a> {
macros_allowed: bool,
attributes_allowed: bool,
) -> PResult<'a, Option<P<Item>>> {
let mut unclosed_delims = vec![];
let (ret, tokens) = self.collect_tokens(|this| {
this.parse_item_implementation(attrs, macros_allowed, attributes_allowed)
let item = this.parse_item_implementation(attrs, macros_allowed, attributes_allowed);
unclosed_delims.append(&mut this.unclosed_delims);
item
})?;
self.unclosed_delims.append(&mut unclosed_delims);

// Once we've parsed an item and recorded the tokens we got while
// parsing we may want to store `tokens` into the item we're about to
Expand Down Expand Up @@ -8539,8 +8565,6 @@ impl<'a> Parser<'a> {
module: self.parse_mod_items(&token::Eof, lo)?,
span: lo.to(self.span),
});
emit_unclosed_delims(&self.unclosed_delims, self.diagnostic());
self.unclosed_delims.clear();
krate
}

Expand Down Expand Up @@ -8571,8 +8595,8 @@ impl<'a> Parser<'a> {
}
}

pub fn emit_unclosed_delims(unclosed_delims: &[UnmatchedBrace], handler: &errors::Handler) {
for unmatched in unclosed_delims {
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_to_string(&token::Token::CloseDelim(unmatched.found_delim)),
Expand All @@ -8586,4 +8610,5 @@ pub fn emit_unclosed_delims(unclosed_delims: &[UnmatchedBrace], handler: &errors
}
err.emit();
}
unclosed_delims.clear();
}
21 changes: 3 additions & 18 deletions src/libsyntax/parse/token.rs
Expand Up @@ -10,7 +10,6 @@ use crate::print::pprust;
use crate::ptr::P;
use crate::symbol::keywords;
use crate::syntax::parse::parse_stream_from_source_str;
use crate::syntax::parse::parser::emit_unclosed_delims;
use crate::tokenstream::{self, DelimSpan, TokenStream, TokenTree};

use syntax_pos::symbol::{self, Symbol};
Expand Down Expand Up @@ -675,9 +674,7 @@ impl Nonterminal {
// FIXME(#43081): Avoid this pretty-print + reparse hack
let source = pprust::nonterminal_to_string(self);
let filename = FileName::macro_expansion_source_code(&source);
let (tokens_for_real, errors) =
parse_stream_from_source_str(filename, source, sess, Some(span));
emit_unclosed_delims(&errors, &sess.span_diagnostic);
let tokens_for_real = parse_stream_from_source_str(filename, source, sess, Some(span));

// During early phases of the compiler the AST could get modified
// directly (e.g., attributes added or removed) and the internal cache
Expand Down Expand Up @@ -740,13 +737,7 @@ fn prepend_attrs(sess: &ParseSess,
let source = pprust::attr_to_string(attr);
let macro_filename = FileName::macro_expansion_source_code(&source);
if attr.is_sugared_doc {
let (stream, errors) = parse_stream_from_source_str(
macro_filename,
source,
sess,
Some(span),
);
emit_unclosed_delims(&errors, &sess.span_diagnostic);
let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
builder.push(stream);
continue
}
Expand All @@ -763,13 +754,7 @@ fn prepend_attrs(sess: &ParseSess,
// ... and for more complicated paths, fall back to a reparse hack that
// should eventually be removed.
} else {
let (stream, errors) = parse_stream_from_source_str(
macro_filename,
source,
sess,
Some(span),
);
emit_unclosed_delims(&errors, &sess.span_diagnostic);
let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
brackets.push(stream);
}

Expand Down
7 changes: 2 additions & 5 deletions src/libsyntax_ext/proc_macro_server.rs
Expand Up @@ -12,7 +12,6 @@ use syntax::ast;
use syntax::ext::base::ExtCtxt;
use syntax::parse::lexer::comments;
use syntax::parse::{self, token, ParseSess};
use syntax::parse::parser::emit_unclosed_delims;
use syntax::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint};
use syntax_pos::hygiene::{SyntaxContext, Transparency};
use syntax_pos::symbol::{keywords, Symbol};
Expand Down Expand Up @@ -410,14 +409,12 @@ impl server::TokenStream for Rustc<'_> {
stream.is_empty()
}
fn from_str(&mut self, src: &str) -> Self::TokenStream {
let (tokens, errors) = parse::parse_stream_from_source_str(
parse::parse_stream_from_source_str(
FileName::proc_macro_source_code(src.clone()),
src.to_string(),
self.sess,
Some(self.call_site),
);
emit_unclosed_delims(&errors, &self.sess.span_diagnostic);
tokens
)
}
fn to_string(&mut self, stream: &Self::TokenStream) -> String {
stream.to_string()
Expand Down
6 changes: 6 additions & 0 deletions src/test/ui/issues/issue-58856-1.rs
@@ -0,0 +1,6 @@
impl A {
fn b(self>
//~^ ERROR expected one of `)`, `,`, or `:`, found `>`
}

fn main() {}
11 changes: 11 additions & 0 deletions src/test/ui/issues/issue-58856-1.stderr
@@ -0,0 +1,11 @@
error: expected one of `)`, `,`, or `:`, found `>`
--> $DIR/issue-58856-1.rs:2:14
|
LL | fn b(self>
| - ^
| | |
| | help: `)` may belong here
| unclosed delimiter

error: aborting due to previous error

14 changes: 14 additions & 0 deletions src/test/ui/issues/issue-58856-2.rs
@@ -0,0 +1,14 @@
struct Empty;

trait Howness {}

impl Howness for () {
fn how_are_you(&self -> Empty {
//~^ ERROR expected one of `)` or `,`, found `->`
//~| ERROR method `how_are_you` is not a member of trait `Howness`
Empty
}
}
//~^ ERROR expected one of `async`, `const`, `crate`, `default`, `existential`, `extern`, `fn`,

fn main() {}
30 changes: 30 additions & 0 deletions src/test/ui/issues/issue-58856-2.stderr
@@ -0,0 +1,30 @@
error: expected one of `)` or `,`, found `->`
--> $DIR/issue-58856-2.rs:6:26
|
LL | fn how_are_you(&self -> Empty {
| - -^^
| | |
| | help: `)` may belong here
| unclosed delimiter

error: expected one of `async`, `const`, `crate`, `default`, `existential`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `)`
--> $DIR/issue-58856-2.rs:11:1
|
LL | }
| - expected one of 11 possible tokens here
LL | }
| ^ unexpected token
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally identified what is causing these: when reaching the end of the current TokenTree, self.bump() will advance to a close token corresponding to the opening delimiter. There are two options: 1) attempt to rewrite the TokenTree representation that the Parser has once we suggest a close delim (high potential for bad lurking bugs), or 2) catch this specific case in expect_one_of, but that might be too magical.


error[E0407]: method `how_are_you` is not a member of trait `Howness`
--> $DIR/issue-58856-2.rs:6:5
|
LL | / fn how_are_you(&self -> Empty {
LL | | //~^ ERROR expected one of `)` or `,`, found `->`
LL | | //~| ERROR method `how_are_you` is not a member of trait `Howness`
LL | | Empty
LL | | }
| |_____^ not a member of trait `Howness`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0407`.
3 changes: 0 additions & 3 deletions src/test/ui/parser/recover-enum2.rs
Expand Up @@ -25,9 +25,6 @@ fn main() {
// fail again
enum Test4 {
Nope(i32 {}) //~ ERROR: found `{`
//~^ ERROR: found `{`
}
}
// still recover later
let bad_syntax = _; //~ ERROR: expected expression, found reserved identifier `_`
}
14 changes: 1 addition & 13 deletions src/test/ui/parser/recover-enum2.stderr
Expand Up @@ -10,17 +10,5 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `{`
LL | Nope(i32 {}) //~ ERROR: found `{`
| ^ expected one of 7 possible tokens here

error: expected one of `!`, `&&`, `&`, `(`, `)`, `*`, `+`, `,`, `...`, `::`, `<`, `?`, `[`, `_`, `crate`, `dyn`, `extern`, `fn`, `for`, `impl`, `pub`, `unsafe`, `}`, or lifetime, found `{`
--> $DIR/recover-enum2.rs:27:22
|
LL | Nope(i32 {}) //~ ERROR: found `{`
| ^ expected one of 24 possible tokens here

error: expected expression, found reserved identifier `_`
--> $DIR/recover-enum2.rs:32:22
|
LL | let bad_syntax = _; //~ ERROR: expected expression, found reserved identifier `_`
| ^ expected expression

error: aborting due to 4 previous errors
error: aborting due to 2 previous errors

6 changes: 6 additions & 0 deletions src/test/ui/parser/unclosed-delimiter-in-dep.rs
@@ -0,0 +1,6 @@
mod unclosed_delim_mod;

fn main() {
let _: usize = unclosed_delim_mod::new();
//~^ ERROR mismatched types
}
23 changes: 23 additions & 0 deletions src/test/ui/parser/unclosed-delimiter-in-dep.stderr
@@ -0,0 +1,23 @@
error: incorrect close delimiter: `}`
--> $DIR/unclosed_delim_mod.rs:5:1
|
LL | pub fn new() -> Result<Value, ()> {
| - close delimiter possibly meant for this
LL | Ok(Value {
| - un-closed delimiter
LL | }
LL | }
| ^ incorrect close delimiter

error[E0308]: mismatched types
--> $DIR/unclosed-delimiter-in-dep.rs:4:20
|
LL | let _: usize = unclosed_delim_mod::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected usize, found enum `std::result::Result`
|
= note: expected type `usize`
found type `std::result::Result<unclosed_delim_mod::Value, ()>`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.
6 changes: 6 additions & 0 deletions src/test/ui/parser/unclosed_delim_mod.rs
@@ -0,0 +1,6 @@
pub struct Value {}
pub fn new() -> Result<Value, ()> {
Ok(Value {
}
}
//~^ ERROR incorrect close delimiter