Auto merge of #33982 - LeoTestard:remove-check-matcher-old, r=pnkfelix
Remove the old FOLLOW checking (aka `check_matcher_old`). It was supposed to be removed at the next release cycle but is still in the tree since like 6 months. Potential breaking change, since some cases (such as #25658) will change from a warning to an error. But the warning stating that it will be a hard error in the next release has been there for 6 months now. I think it's safe to break this code. ^_^
This commit is contained in:
commit
371bf0eda2
@ -349,203 +349,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<Item=&'a TokenTree> {
|
||||
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
|
||||
@ -785,8 +594,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();
|
||||
@ -815,11 +623,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;
|
||||
@ -840,7 +648,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();
|
||||
|
||||
@ -872,7 +680,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 {
|
||||
@ -894,7 +702,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;
|
||||
@ -909,15 +717,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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -947,33 +754,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 `;`
|
||||
"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 `;`
|
||||
"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,
|
||||
|
||||
_ =>
|
||||
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
|
||||
}
|
||||
}
|
@ -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`
|
||||
|
Loading…
x
Reference in New Issue
Block a user