diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 3522c8863cf52..6f40815f8c579 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -354,203 +354,12 @@ fn check_rhs(cx: &mut ExtCtxt, rhs: &TokenTree) -> bool { false } -// Issue 30450: when we are through a warning cycle, we can just error -// on all failure conditions and remove this struct and enum. - -#[derive(Debug)] -struct OnFail { - saw_failure: bool, - action: OnFailAction, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -enum OnFailAction { Warn, Error, DoNothing } - -impl OnFail { - fn warn() -> OnFail { OnFail { saw_failure: false, action: OnFailAction::Warn } } - fn error() -> OnFail { OnFail { saw_failure: false, action: OnFailAction::Error } } - fn do_nothing() -> OnFail { OnFail { saw_failure: false, action: OnFailAction::DoNothing } } - fn react(&mut self, cx: &mut ExtCtxt, sp: Span, msg: &str, help: Option<&str>) { - match self.action { - OnFailAction::DoNothing => {} - OnFailAction::Error => { - let mut err = cx.struct_span_err(sp, msg); - if let Some(msg) = help { err.span_help(sp, msg); } - err.emit(); - } - OnFailAction::Warn => { - let mut warn = cx.struct_span_warn(sp, msg); - if let Some(msg) = help { warn.span_help(sp, msg); } - warn.span_note(sp, "The above warning will be a hard error in the next release.") - .emit(); - } - }; - self.saw_failure = true; - } -} - fn check_matcher(cx: &mut ExtCtxt, matcher: &[TokenTree]) -> bool { - // Issue 30450: when we are through a warning cycle, we can just - // error on all failure conditions (and remove check_matcher_old). - - // First run the old-pass, but *only* to find out if it would have failed. - let mut on_fail = OnFail::do_nothing(); - check_matcher_old(cx, matcher.iter(), &Eof, &mut on_fail); - // Then run the new pass, but merely warn if the old pass accepts and new pass rejects. - // (Note this silently accepts code if new pass accepts.) - let mut on_fail = if on_fail.saw_failure { - OnFail::error() - } else { - OnFail::warn() - }; - check_matcher_new(cx, matcher, &mut on_fail); - // matcher is valid if the new pass didn't see any error, - // or if errors were considered warnings - on_fail.action != OnFailAction::Error || !on_fail.saw_failure -} - -// returns the last token that was checked, for TokenTree::Sequence. -// return value is used by recursive calls. -fn check_matcher_old<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token, on_fail: &mut OnFail) --> Option<(Span, Token)> where I: Iterator { - use print::pprust::token_to_string; - use std::iter::once; - - let mut last = None; - - // 2. For each token T in M: - let mut tokens = matcher.peekable(); - while let Some(token) = tokens.next() { - last = match *token { - TokenTree::Token(sp, MatchNt(ref name, ref frag_spec)) => { - // ii. If T is a simple NT, look ahead to the next token T' in - // M. If T' is in the set FOLLOW(NT), continue. Else; reject. - if can_be_followed_by_any(&frag_spec.name.as_str()) { - continue - } else { - let next_token = match tokens.peek() { - // If T' closes a complex NT, replace T' with F - Some(&&TokenTree::Token(_, CloseDelim(_))) => follow.clone(), - Some(&&TokenTree::Token(_, ref tok)) => tok.clone(), - Some(&&TokenTree::Sequence(sp, _)) => { - // Be conservative around sequences: to be - // more specific, we would need to - // consider FIRST sets, but also the - // possibility that the sequence occurred - // zero times (in which case we need to - // look at the token that follows the - // sequence, which may itself be a sequence, - // and so on). - on_fail.react(cx, sp, - &format!("`${0}:{1}` is followed by a \ - sequence repetition, which is not \ - allowed for `{1}` fragments", - name, frag_spec), - None); - Eof - }, - // die next iteration - Some(&&TokenTree::Delimited(_, ref delim)) => delim.close_token(), - // else, we're at the end of the macro or sequence - None => follow.clone() - }; - - let tok = if let TokenTree::Token(_, ref tok) = *token { - tok - } else { - unreachable!() - }; - - // If T' is in the set FOLLOW(NT), continue. Else, reject. - match (&next_token, is_in_follow(cx, &next_token, &frag_spec.name.as_str())) { - (_, Err((msg, _))) => { - // no need for help message, those messages - // are never emitted anyway... - on_fail.react(cx, sp, &msg, None); - continue - } - (&Eof, _) => return Some((sp, tok.clone())), - (_, Ok(true)) => continue, - (next, Ok(false)) => { - on_fail.react(cx, sp, &format!("`${0}:{1}` is followed by `{2}`, which \ - is not allowed for `{1}` fragments", - name, frag_spec, - token_to_string(next)), None); - continue - }, - } - } - }, - TokenTree::Sequence(sp, ref seq) => { - // iii. Else, T is a complex NT. - match seq.separator { - // If T has the form $(...)U+ or $(...)U* for some token U, - // run the algorithm on the contents with F set to U. If it - // accepts, continue, else, reject. - Some(ref u) => { - let last = check_matcher_old(cx, seq.tts.iter(), u, on_fail); - match last { - // Since the delimiter isn't required after the last - // repetition, make sure that the *next* token is - // sane. This doesn't actually compute the FIRST of - // the rest of the matcher yet, it only considers - // single tokens and simple NTs. This is imprecise, - // but conservatively correct. - Some((span, tok)) => { - let fol = match tokens.peek() { - Some(&&TokenTree::Token(_, ref tok)) => tok.clone(), - Some(&&TokenTree::Delimited(_, ref delim)) => - delim.close_token(), - Some(_) => { - on_fail.react(cx, sp, "sequence repetition followed by \ - another sequence repetition, which is not allowed", - None); - Eof - }, - None => Eof - }; - check_matcher_old(cx, once(&TokenTree::Token(span, tok.clone())), - &fol, on_fail) - }, - None => last, - } - }, - // If T has the form $(...)+ or $(...)*, run the algorithm - // on the contents with F set to the token following the - // sequence. If it accepts, continue, else, reject. - None => { - let fol = match tokens.peek() { - Some(&&TokenTree::Token(_, ref tok)) => tok.clone(), - Some(&&TokenTree::Delimited(_, ref delim)) => delim.close_token(), - Some(_) => { - on_fail.react(cx, sp, "sequence repetition followed by another \ - sequence repetition, which is not allowed", None); - Eof - }, - None => Eof - }; - check_matcher_old(cx, seq.tts.iter(), &fol, on_fail) - } - } - }, - TokenTree::Token(..) => { - // i. If T is not an NT, continue. - continue - }, - TokenTree::Delimited(_, ref tts) => { - // if we don't pass in that close delimiter, we'll incorrectly consider the matcher - // `{ $foo:ty }` as having a follow that isn't `RBrace` - check_matcher_old(cx, tts.tts.iter(), &tts.close_token(), on_fail) - } - } - } - last -} - -fn check_matcher_new(cx: &mut ExtCtxt, matcher: &[TokenTree], on_fail: &mut OnFail) { let first_sets = FirstSets::new(matcher); let empty_suffix = TokenSet::empty(); - check_matcher_core(cx, &first_sets, matcher, &empty_suffix, on_fail); + let err = cx.parse_sess.span_diagnostic.err_count(); + check_matcher_core(cx, &first_sets, matcher, &empty_suffix); + err == cx.parse_sess.span_diagnostic.err_count() } // The FirstSets for a matcher is a mapping from subsequences in the @@ -790,8 +599,7 @@ impl TokenSet { fn check_matcher_core(cx: &mut ExtCtxt, first_sets: &FirstSets, matcher: &[TokenTree], - follow: &TokenSet, - on_fail: &mut OnFail) -> TokenSet { + follow: &TokenSet) -> TokenSet { use print::pprust::token_to_string; let mut last = TokenSet::empty(); @@ -820,11 +628,11 @@ fn check_matcher_core(cx: &mut ExtCtxt, TokenTree::Token(sp, ref tok) => { let can_be_followed_by_any; if let Err(bad_frag) = has_legal_fragment_specifier(tok) { - on_fail.react(cx, sp, - &format!("invalid fragment specifier `{}`", bad_frag), - Some("valid fragment specifiers are `ident`, `block`, \ - `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \ - and `item`")); + cx.struct_span_err(sp, &format!("invalid fragment specifier `{}`", bad_frag)) + .help("valid fragment specifiers are `ident`, `block`, \ + `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt` \ + and `item`") + .emit(); // (This eliminates false positives and duplicates // from error messages.) can_be_followed_by_any = true; @@ -845,7 +653,7 @@ fn check_matcher_core(cx: &mut ExtCtxt, } TokenTree::Delimited(_, ref d) => { let my_suffix = TokenSet::singleton((d.close_span, Token::CloseDelim(d.delim))); - check_matcher_core(cx, first_sets, &d.tts, &my_suffix, on_fail); + check_matcher_core(cx, first_sets, &d.tts, &my_suffix); // don't track non NT tokens last.replace_with_irrelevant(); @@ -877,7 +685,7 @@ fn check_matcher_core(cx: &mut ExtCtxt, // At this point, `suffix_first` is built, and // `my_suffix` is some TokenSet that we can use // for checking the interior of `seq_rep`. - let next = check_matcher_core(cx, first_sets, &seq_rep.tts, my_suffix, on_fail); + let next = check_matcher_core(cx, first_sets, &seq_rep.tts, my_suffix); if next.maybe_empty { last.add_all(&next); } else { @@ -899,7 +707,7 @@ fn check_matcher_core(cx: &mut ExtCtxt, for &(sp, ref next_token) in &suffix_first.tokens { match is_in_follow(cx, next_token, &frag_spec.name.as_str()) { Err((msg, help)) => { - on_fail.react(cx, sp, &msg, Some(help)); + cx.struct_span_err(sp, &msg).help(help).emit(); // don't bother reporting every source of // conflict for a particular element of `last`. continue 'each_last; @@ -914,15 +722,14 @@ fn check_matcher_core(cx: &mut ExtCtxt, "may be" }; - on_fail.react( - cx, sp, + cx.span_err( + sp, &format!("`${name}:{frag}` {may_be} followed by `{next}`, which \ is not allowed for `{frag}` fragments", name=name, frag=frag_spec, next=token_to_string(next_token), - may_be=may_be), - None + may_be=may_be) ); } } @@ -952,33 +759,11 @@ fn token_can_be_followed_by_any(tok: &Token) -> bool { /// ANYTHING without fear of future compatibility hazards). fn frag_can_be_followed_by_any(frag: &str) -> bool { match frag { - "item" | // always terminated by `}` or `;` - "block" | // exactly one token tree - "ident" | // exactly one token tree - "meta" | // exactly one token tree - "tt" => // exactly one token tree - true, - - _ => - false, - } -} - -/// True if a fragment of type `frag` can be followed by any sort of -/// token. We use this (among other things) as a useful approximation -/// for when `frag` can be followed by a repetition like `$(...)*` or -/// `$(...)+`. In general, these can be a bit tricky to reason about, -/// so we adopt a conservative position that says that any fragment -/// specifier which consumes at most one token tree can be followed by -/// a fragment specifier (indeed, these fragments can be followed by -/// ANYTHING without fear of future compatibility hazards). -fn can_be_followed_by_any(frag: &str) -> bool { - match frag { - "item" | // always terminated by `}` or `;` + "item" | // always terminated by `}` or `;` "block" | // exactly one token tree "ident" | // exactly one token tree - "meta" | // exactly one token tree - "tt" => // exactly one token tree + "meta" | // exactly one token tree + "tt" => // exactly one token tree true, _ => diff --git a/src/test/compile-fail/issue-30715.rs b/src/test/compile-fail/issue-30715.rs deleted file mode 100644 index 5cacf8f53c62e..0000000000000 --- a/src/test/compile-fail/issue-30715.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: -Z continue-parse-after-error - -macro_rules! parallel { - ( - // If future has `pred`/`moelarry` fragments (where "pred" is - // "like expr, but with `{` in its FOLLOW set"), then could - // use `pred` instead of future-proof erroring here. See also: - // - // https://github.com/rust-lang/rfcs/pull/1384#issuecomment-160165525 - for $id:ident in $iter:expr { //~ WARN `$iter:expr` is followed by `{` - $( $inner:expr; )* - } - ) => {}; -} - - -fn main() { - parallel! { - for i in 0..n { - x += i; //~ ERROR expected `:`, found `+=` - } //~ ERROR unexpected end of macro invocation - } -} diff --git a/src/test/compile-fail/macro-follow.rs b/src/test/compile-fail/macro-follow.rs index f985340c52410..001bc42274b4d 100644 --- a/src/test/compile-fail/macro-follow.rs +++ b/src/test/compile-fail/macro-follow.rs @@ -12,9 +12,9 @@ // FOLLOW(pat) = {FatArrow, Comma, Eq, Or, Ident(if), Ident(in)} macro_rules! follow_pat { - ($p:pat ()) => {}; //~WARN `$p:pat` is followed by `(` - ($p:pat []) => {}; //~WARN `$p:pat` is followed by `[` - ($p:pat {}) => {}; //~WARN `$p:pat` is followed by `{` + ($p:pat ()) => {}; //~ERROR `$p:pat` is followed by `(` + ($p:pat []) => {}; //~ERROR `$p:pat` is followed by `[` + ($p:pat {}) => {}; //~ERROR `$p:pat` is followed by `{` ($p:pat :) => {}; //~ERROR `$p:pat` is followed by `:` ($p:pat >) => {}; //~ERROR `$p:pat` is followed by `>` ($p:pat +) => {}; //~ERROR `$p:pat` is followed by `+` @@ -32,9 +32,9 @@ macro_rules! follow_pat { } // FOLLOW(expr) = {FatArrow, Comma, Semicolon} macro_rules! follow_expr { - ($e:expr ()) => {}; //~WARN `$e:expr` is followed by `(` - ($e:expr []) => {}; //~WARN `$e:expr` is followed by `[` - ($e:expr {}) => {}; //~WARN `$e:expr` is followed by `{` + ($e:expr ()) => {}; //~ERROR `$e:expr` is followed by `(` + ($e:expr []) => {}; //~ERROR `$e:expr` is followed by `[` + ($e:expr {}) => {}; //~ERROR `$e:expr` is followed by `{` ($e:expr =) => {}; //~ERROR `$e:expr` is followed by `=` ($e:expr |) => {}; //~ERROR `$e:expr` is followed by `|` ($e:expr :) => {}; //~ERROR `$e:expr` is followed by `:` @@ -57,7 +57,7 @@ macro_rules! follow_expr { // FOLLOW(ty) = {OpenDelim(Brace), Comma, FatArrow, Colon, Eq, Gt, Semi, Or, // Ident(as), Ident(where), OpenDelim(Bracket), Nonterminal(Block)} macro_rules! follow_ty { - ($t:ty ()) => {}; //~WARN `$t:ty` is followed by `(` + ($t:ty ()) => {}; //~ERROR `$t:ty` is followed by `(` ($t:ty []) => {}; // ok (RFC 1462) ($t:ty +) => {}; //~ERROR `$t:ty` is followed by `+` ($t:ty ident) => {}; //~ERROR `$t:ty` is followed by `ident` @@ -75,9 +75,9 @@ macro_rules! follow_ty { } // FOLLOW(stmt) = FOLLOW(expr) macro_rules! follow_stmt { - ($s:stmt ()) => {}; //~WARN `$s:stmt` is followed by `(` - ($s:stmt []) => {}; //~WARN `$s:stmt` is followed by `[` - ($s:stmt {}) => {}; //~WARN `$s:stmt` is followed by `{` + ($s:stmt ()) => {}; //~ERROR `$s:stmt` is followed by `(` + ($s:stmt []) => {}; //~ERROR `$s:stmt` is followed by `[` + ($s:stmt {}) => {}; //~ERROR `$s:stmt` is followed by `{` ($s:stmt =) => {}; //~ERROR `$s:stmt` is followed by `=` ($s:stmt |) => {}; //~ERROR `$s:stmt` is followed by `|` ($s:stmt :) => {}; //~ERROR `$s:stmt` is followed by `:` @@ -99,7 +99,7 @@ macro_rules! follow_stmt { } // FOLLOW(path) = FOLLOW(ty) macro_rules! follow_path { - ($p:path ()) => {}; //~WARN `$p:path` is followed by `(` + ($p:path ()) => {}; //~ERROR `$p:path` is followed by `(` ($p:path []) => {}; // ok (RFC 1462) ($p:path +) => {}; //~ERROR `$p:path` is followed by `+` ($p:path ident) => {}; //~ERROR `$p:path` is followed by `ident`