From b7f7756566c9e10983ee51bc97afe9852838299a Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Thu, 8 Aug 2019 12:31:24 -0700 Subject: Recover parser from `foo(_, _)` --- src/libsyntax/parse/parser.rs | 70 ++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 20 deletions(-) (limited to 'src/libsyntax') diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index d85c2df16a3..30e16592113 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2052,9 +2052,23 @@ impl<'a> Parser<'a> { while self.token != token::CloseDelim(token::Paren) { es.push(match self.parse_expr() { Ok(es) => es, - Err(err) => { + Err(mut err) => { // recover from parse error in tuple list - return Ok(self.recover_seq_parse_error(token::Paren, lo, Err(err))); + match self.token.kind { + token::Ident(name, false) + if name == kw::Underscore && self.look_ahead(1, |t| { + t == &token::Comma + }) => { + // Special-case handling of `Foo<(_, _, _)>` + err.emit(); + let sp = self.token.span; + self.bump(); + self.mk_expr(sp, ExprKind::Err, ThinVec::new()) + } + _ => return Ok( + self.recover_seq_parse_error(token::Paren, lo, Err(err)), + ), + } } }); recovered = self.expect_one_of( @@ -2456,9 +2470,10 @@ impl<'a> Parser<'a> { } /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. - fn parse_dot_or_call_expr(&mut self, - already_parsed_attrs: Option>) - -> PResult<'a, P> { + fn parse_dot_or_call_expr( + &mut self, + already_parsed_attrs: Option>, + ) -> PResult<'a, P> { let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; let b = self.parse_bottom_expr(); @@ -2466,16 +2481,16 @@ impl<'a> Parser<'a> { self.parse_dot_or_call_expr_with(b, span, attrs) } - fn parse_dot_or_call_expr_with(&mut self, - e0: P, - lo: Span, - mut attrs: ThinVec) - -> PResult<'a, P> { + fn parse_dot_or_call_expr_with( + &mut self, + e0: P, + lo: Span, + mut attrs: ThinVec, + ) -> PResult<'a, P> { // Stitch the list of outer attributes onto the return value. // A little bit ugly, but the best way given the current code // structure - self.parse_dot_or_call_expr_with_(e0, lo) - .map(|expr| + self.parse_dot_or_call_expr_with_(e0, lo).map(|expr| expr.map(|mut expr| { attrs.extend::>(expr.attrs.into()); expr.attrs = attrs; @@ -2483,10 +2498,7 @@ impl<'a> Parser<'a> { ExprKind::If(..) if !expr.attrs.is_empty() => { // Just point to the first attribute in there... let span = expr.attrs[0].span; - - self.span_err(span, - "attributes are not yet allowed on `if` \ - expressions"); + self.span_err(span, "attributes are not yet allowed on `if` expressions"); } _ => {} } @@ -2624,7 +2636,24 @@ impl<'a> Parser<'a> { } fn parse_paren_expr_seq(&mut self) -> PResult<'a, Vec>> { - self.parse_paren_comma_seq(|p| p.parse_expr()).map(|(r, _)| r) + self.parse_paren_comma_seq(|p| { + match p.parse_expr() { + Ok(expr) => Ok(expr), + Err(mut err) => match p.token.kind { + token::Ident(name, false) + if name == kw::Underscore && p.look_ahead(1, |t| { + t == &token::Comma + }) => { + // Special-case handling of `foo(_, _, _)` + err.emit(); + let sp = p.token.span; + p.bump(); + Ok(p.mk_expr(sp, ExprKind::Err, ThinVec::new())) + } + _ => Err(err), + }, + } + }).map(|(r, _)| r) } crate fn process_potential_macro_variable(&mut self) { @@ -2806,9 +2835,10 @@ impl<'a> Parser<'a> { /// This parses an expression accounting for associativity and precedence of the operators in /// the expression. #[inline] - fn parse_assoc_expr(&mut self, - already_parsed_attrs: Option>) - -> PResult<'a, P> { + fn parse_assoc_expr( + &mut self, + already_parsed_attrs: Option>, + ) -> PResult<'a, P> { self.parse_assoc_expr_with(0, already_parsed_attrs.into()) } -- cgit 1.4.1-3-g733a5 From 7c96d90c2081f3ca84b3786a125cf2c415335e40 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Thu, 8 Aug 2019 18:24:00 -0700 Subject: More explicit diagnostic when using a `vec![]` in a pattern ``` error: unexpected `(` after qualified path --> $DIR/vec-macro-in-pattern.rs:3:14 | LL | Some(vec![x]) => (), | ^^^^^^^ | | | unexpected `(` after qualified path | in this macro invocation | use a slice pattern here instead | = help: for more information, see https://doc.rust-lang.org/edition-guide/rust-2018/slice-patterns.html = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) ``` --- src/libsyntax/ext/expand.rs | 28 +++++++++++++++++----- src/libsyntax/ext/tt/macro_rules.rs | 23 ++++++++++++++++++ src/test/ui/proc-macro/lifetimes.stderr | 2 +- src/test/ui/suggestions/vec-macro-in-pattern.rs | 6 +++++ .../ui/suggestions/vec-macro-in-pattern.stderr | 15 ++++++++++++ src/test/ui/type/ascription/issue-47666.stderr | 1 + 6 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/suggestions/vec-macro-in-pattern.rs create mode 100644 src/test/ui/suggestions/vec-macro-in-pattern.stderr (limited to 'src/libsyntax') diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 964c81dd466..36f059531d3 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -686,12 +686,13 @@ impl<'a, 'b> MacroExpander<'a, 'b> { ); } - fn parse_ast_fragment(&mut self, - toks: TokenStream, - kind: AstFragmentKind, - path: &Path, - span: Span) - -> AstFragment { + fn parse_ast_fragment( + &mut self, + toks: TokenStream, + kind: AstFragmentKind, + path: &Path, + span: Span, + ) -> AstFragment { let mut parser = self.cx.new_parser_from_tts(&toks.into_trees().collect::>()); match parser.parse_ast_fragment(kind, false) { Ok(fragment) => { @@ -700,6 +701,21 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } Err(mut err) => { err.set_span(span); + match kind { + AstFragmentKind::Ty => { + err.span_label( + span, + "this macro call doesn't expand to a type", + ); + } + AstFragmentKind::Pat => { + err.span_label( + span, + "this macro call doesn't expand to a pattern", + ); + } + _ => {} + }; err.emit(); self.cx.trace_macros_diag(); kind.dummy(span) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 7401f256412..a9d0b739d6a 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -70,6 +70,29 @@ impl<'a> ParserAnyMacro<'a> { } else if !parser.sess.source_map().span_to_filename(parser.token.span).is_real() { e.span_label(site_span, "in this macro invocation"); } + match kind { + AstFragmentKind::Ty => { + e.span_label( + site_span, + "this macro call doesn't expand to a type", + ); + } + AstFragmentKind::Pat if macro_ident.name == sym::vec => { + e.span_label( + site_span, + "use a slice pattern here instead", + ); + e.help("for more information, see https://doc.rust-lang.org/edition-guide/\ + rust-2018/slice-patterns.html"); + } + AstFragmentKind::Pat => { + e.span_label( + site_span, + "this macro call doesn't expand to a pattern", + ); + } + _ => {} + }; e })); diff --git a/src/test/ui/proc-macro/lifetimes.stderr b/src/test/ui/proc-macro/lifetimes.stderr index 2356a119530..6e91201405c 100644 --- a/src/test/ui/proc-macro/lifetimes.stderr +++ b/src/test/ui/proc-macro/lifetimes.stderr @@ -2,7 +2,7 @@ error: expected type, found `'` --> $DIR/lifetimes.rs:9:10 | LL | type A = single_quote_alone!(); - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ this macro call doesn't expand to a type error: aborting due to previous error diff --git a/src/test/ui/suggestions/vec-macro-in-pattern.rs b/src/test/ui/suggestions/vec-macro-in-pattern.rs new file mode 100644 index 00000000000..5c42a6bdbd4 --- /dev/null +++ b/src/test/ui/suggestions/vec-macro-in-pattern.rs @@ -0,0 +1,6 @@ +fn main() { + match Some(vec![3]) { + Some(vec![x]) => (), //~ ERROR unexpected `(` after qualified path + _ => (), + } +} diff --git a/src/test/ui/suggestions/vec-macro-in-pattern.stderr b/src/test/ui/suggestions/vec-macro-in-pattern.stderr new file mode 100644 index 00000000000..f94cb93a520 --- /dev/null +++ b/src/test/ui/suggestions/vec-macro-in-pattern.stderr @@ -0,0 +1,15 @@ +error: unexpected `(` after qualified path + --> $DIR/vec-macro-in-pattern.rs:3:14 + | +LL | Some(vec![x]) => (), + | ^^^^^^^ + | | + | unexpected `(` after qualified path + | in this macro invocation + | use a slice pattern here instead + | + = help: for more information, see https://doc.rust-lang.org/edition-guide/rust-2018/slice-patterns.html + = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error: aborting due to previous error + diff --git a/src/test/ui/type/ascription/issue-47666.stderr b/src/test/ui/type/ascription/issue-47666.stderr index 965bbe5ea41..2f052341fae 100644 --- a/src/test/ui/type/ascription/issue-47666.stderr +++ b/src/test/ui/type/ascription/issue-47666.stderr @@ -6,6 +6,7 @@ LL | let _ = Option:Some(vec![0, 1]); | | | | | expected type | | in this macro invocation + | | this macro call doesn't expand to a type | help: maybe write a path separator here: `::` | = note: `#![feature(type_ascription)]` lets you annotate an expression with a type: `: ` -- cgit 1.4.1-3-g733a5 From 75c5ad2e827a077c3738dee11d9e0dc99962f384 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Fri, 9 Aug 2019 09:39:30 -0700 Subject: review comments: use structured suggestion --- src/libsyntax/ext/expand.rs | 17 +------ src/libsyntax/ext/tt/macro_rules.rs | 53 ++++++++++++++-------- src/test/ui/suggestions/vec-macro-in-pattern.fixed | 8 ++++ src/test/ui/suggestions/vec-macro-in-pattern.rs | 6 ++- .../ui/suggestions/vec-macro-in-pattern.stderr | 8 ++-- 5 files changed, 52 insertions(+), 40 deletions(-) create mode 100644 src/test/ui/suggestions/vec-macro-in-pattern.fixed (limited to 'src/libsyntax') diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 36f059531d3..9a3195b1165 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -6,6 +6,7 @@ use crate::config::StripUnconfigured; use crate::ext::base::*; use crate::ext::proc_macro::collect_derives; use crate::ext::hygiene::{ExpnId, SyntaxContext, ExpnInfo, ExpnKind}; +use crate::ext::tt::macro_rules::annotate_err_with_kind; use crate::ext::placeholders::{placeholder, PlaceholderExpander}; use crate::feature_gate::{self, Features, GateIssue, is_builtin_attr, emit_feature_err}; use crate::mut_visit::*; @@ -701,21 +702,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } Err(mut err) => { err.set_span(span); - match kind { - AstFragmentKind::Ty => { - err.span_label( - span, - "this macro call doesn't expand to a type", - ); - } - AstFragmentKind::Pat => { - err.span_label( - span, - "this macro call doesn't expand to a pattern", - ); - } - _ => {} - }; + annotate_err_with_kind(&mut err, kind, span); err.emit(); self.cx.trace_macros_diag(); kind.dummy(span) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index a9d0b739d6a..b057a9ad44d 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -17,7 +17,7 @@ use crate::symbol::{kw, sym, Symbol}; use crate::tokenstream::{DelimSpan, TokenStream, TokenTree}; use crate::{ast, attr, attr::TransparencyError}; -use errors::FatalError; +use errors::{DiagnosticBuilder, FatalError}; use log::debug; use syntax_pos::Span; @@ -43,6 +43,18 @@ pub struct ParserAnyMacro<'a> { arm_span: Span, } +pub fn annotate_err_with_kind(err: &mut DiagnosticBuilder<'_>, kind: AstFragmentKind, span: Span) { + match kind { + AstFragmentKind::Ty => { + err.span_label(span, "this macro call doesn't expand to a type"); + } + AstFragmentKind::Pat => { + err.span_label(span, "this macro call doesn't expand to a pattern"); + } + _ => {} + }; +} + impl<'a> ParserAnyMacro<'a> { pub fn make(mut self: Box>, kind: AstFragmentKind) -> AstFragment { let ParserAnyMacro { site_span, macro_ident, ref mut parser, arm_span } = *self; @@ -71,27 +83,30 @@ impl<'a> ParserAnyMacro<'a> { e.span_label(site_span, "in this macro invocation"); } match kind { - AstFragmentKind::Ty => { - e.span_label( - site_span, - "this macro call doesn't expand to a type", - ); - } AstFragmentKind::Pat if macro_ident.name == sym::vec => { - e.span_label( - site_span, - "use a slice pattern here instead", - ); + let mut suggestion = None; + if let Ok(code) = parser.sess.source_map().span_to_snippet(site_span) { + if let Some(bang) = code.find('!') { + suggestion = Some(code[bang + 1..].to_string()); + } + } + if let Some(suggestion) = suggestion { + e.span_suggestion( + site_span, + "use a slice pattern here instead", + suggestion, + Applicability::MachineApplicable, + ); + } else { + e.span_label( + site_span, + "use a slice pattern here instead", + ); + } e.help("for more information, see https://doc.rust-lang.org/edition-guide/\ - rust-2018/slice-patterns.html"); - } - AstFragmentKind::Pat => { - e.span_label( - site_span, - "this macro call doesn't expand to a pattern", - ); + rust-2018/slice-patterns.html"); } - _ => {} + _ => annotate_err_with_kind(&mut e, kind, site_span), }; e })); diff --git a/src/test/ui/suggestions/vec-macro-in-pattern.fixed b/src/test/ui/suggestions/vec-macro-in-pattern.fixed new file mode 100644 index 00000000000..e1695d6820a --- /dev/null +++ b/src/test/ui/suggestions/vec-macro-in-pattern.fixed @@ -0,0 +1,8 @@ +// run-rustfix +fn main() { + // everything after `.as_ref` should be suggested + match Some(vec![3]).as_ref().map(|v| v.as_slice()) { + Some([_x]) => (), //~ ERROR unexpected `(` after qualified path + _ => (), + } +} diff --git a/src/test/ui/suggestions/vec-macro-in-pattern.rs b/src/test/ui/suggestions/vec-macro-in-pattern.rs index 5c42a6bdbd4..4843629fbcf 100644 --- a/src/test/ui/suggestions/vec-macro-in-pattern.rs +++ b/src/test/ui/suggestions/vec-macro-in-pattern.rs @@ -1,6 +1,8 @@ +// run-rustfix fn main() { - match Some(vec![3]) { - Some(vec![x]) => (), //~ ERROR unexpected `(` after qualified path + // everything after `.as_ref` should be suggested + match Some(vec![3]).as_ref().map(|v| v.as_slice()) { + Some(vec![_x]) => (), //~ ERROR unexpected `(` after qualified path _ => (), } } diff --git a/src/test/ui/suggestions/vec-macro-in-pattern.stderr b/src/test/ui/suggestions/vec-macro-in-pattern.stderr index f94cb93a520..59ca8ebbf63 100644 --- a/src/test/ui/suggestions/vec-macro-in-pattern.stderr +++ b/src/test/ui/suggestions/vec-macro-in-pattern.stderr @@ -1,12 +1,12 @@ error: unexpected `(` after qualified path - --> $DIR/vec-macro-in-pattern.rs:3:14 + --> $DIR/vec-macro-in-pattern.rs:5:14 | -LL | Some(vec![x]) => (), - | ^^^^^^^ +LL | Some(vec![_x]) => (), + | ^^^^^^^^ | | | unexpected `(` after qualified path | in this macro invocation - | use a slice pattern here instead + | help: use a slice pattern here instead: `[_x]` | = help: for more information, see https://doc.rust-lang.org/edition-guide/rust-2018/slice-patterns.html = note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) -- cgit 1.4.1-3-g733a5