Skip to content

Commit e897fe7

Browse files
committed
Rollup merge of rust-lang#55301 - estebank:macro-allowed, r=petrochenkov
List allowed tokens after macro fragments Fix rust-lang#34069.
2 parents eb74711 + 2cfd790 commit e897fe7

File tree

5 files changed

+385
-141
lines changed

5 files changed

+385
-141
lines changed

Diff for: src/libsyntax/ext/tt/macro_rules.rs

+94-44
Original file line numberDiff line numberDiff line change
@@ -808,15 +808,15 @@ fn check_matcher_core(sess: &ParseSess,
808808
if let TokenTree::MetaVarDecl(_, ref name, ref frag_spec) = *token {
809809
for next_token in &suffix_first.tokens {
810810
match is_in_follow(next_token, &frag_spec.as_str()) {
811-
Err((msg, help)) => {
811+
IsInFollow::Invalid(msg, help) => {
812812
sess.span_diagnostic.struct_span_err(next_token.span(), &msg)
813813
.help(help).emit();
814814
// don't bother reporting every source of
815815
// conflict for a particular element of `last`.
816816
continue 'each_last;
817817
}
818-
Ok(true) => {}
819-
Ok(false) => {
818+
IsInFollow::Yes => {}
819+
IsInFollow::No(ref possible) => {
820820
let may_be = if last.tokens.len() == 1 &&
821821
suffix_first.tokens.len() == 1
822822
{
@@ -825,15 +825,41 @@ fn check_matcher_core(sess: &ParseSess,
825825
"may be"
826826
};
827827

828-
sess.span_diagnostic.span_err(
829-
next_token.span(),
828+
let sp = next_token.span();
829+
let mut err = sess.span_diagnostic.struct_span_err(
830+
sp,
830831
&format!("`${name}:{frag}` {may_be} followed by `{next}`, which \
831832
is not allowed for `{frag}` fragments",
832833
name=name,
833834
frag=frag_spec,
834835
next=quoted_tt_to_string(next_token),
835-
may_be=may_be)
836+
may_be=may_be),
836837
);
838+
err.span_label(
839+
sp,
840+
format!("not allowed after `{}` fragments", frag_spec),
841+
);
842+
let msg = "allowed there are: ";
843+
match &possible[..] {
844+
&[] => {}
845+
&[t] => {
846+
err.note(&format!(
847+
"only {} is allowed after `{}` fragments",
848+
t,
849+
frag_spec,
850+
));
851+
}
852+
ts => {
853+
err.note(&format!(
854+
"{}{} or {}",
855+
msg,
856+
ts[..ts.len() - 1].iter().map(|s| *s)
857+
.collect::<Vec<_>>().join(", "),
858+
ts[ts.len() - 1],
859+
));
860+
}
861+
}
862+
err.emit();
837863
}
838864
}
839865
}
@@ -876,6 +902,12 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool {
876902
}
877903
}
878904

905+
enum IsInFollow {
906+
Yes,
907+
No(Vec<&'static str>),
908+
Invalid(String, &'static str),
909+
}
910+
879911
/// True if `frag` can legally be followed by the token `tok`. For
880912
/// fragments that can consume an unbounded number of tokens, `tok`
881913
/// must be within a well-defined follow set. This is intended to
@@ -884,81 +916,99 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool {
884916
/// break macros that were relying on that binary operator as a
885917
/// separator.
886918
// when changing this do not forget to update doc/book/macros.md!
887-
fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> Result<bool, (String, &'static str)> {
919+
fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> IsInFollow {
888920
use self::quoted::TokenTree;
889921

890922
if let TokenTree::Token(_, token::CloseDelim(_)) = *tok {
891923
// closing a token tree can never be matched by any fragment;
892924
// iow, we always require that `(` and `)` match, etc.
893-
Ok(true)
925+
IsInFollow::Yes
894926
} else {
895927
match frag {
896928
"item" => {
897929
// since items *must* be followed by either a `;` or a `}`, we can
898930
// accept anything after them
899-
Ok(true)
931+
IsInFollow::Yes
900932
},
901933
"block" => {
902934
// anything can follow block, the braces provide an easy boundary to
903935
// maintain
904-
Ok(true)
936+
IsInFollow::Yes
905937
},
906-
"stmt" | "expr" => match *tok {
907-
TokenTree::Token(_, ref tok) => match *tok {
908-
FatArrow | Comma | Semi => Ok(true),
909-
_ => Ok(false)
910-
},
911-
_ => Ok(false),
938+
"stmt" | "expr" => {
939+
let tokens = vec!["`=>`", "`,`", "`;`"];
940+
match *tok {
941+
TokenTree::Token(_, ref tok) => match *tok {
942+
FatArrow | Comma | Semi => IsInFollow::Yes,
943+
_ => IsInFollow::No(tokens),
944+
},
945+
_ => IsInFollow::No(tokens),
946+
}
912947
},
913-
"pat" => match *tok {
914-
TokenTree::Token(_, ref tok) => match *tok {
915-
FatArrow | Comma | Eq | BinOp(token::Or) => Ok(true),
916-
Ident(i, false) if i.name == "if" || i.name == "in" => Ok(true),
917-
_ => Ok(false)
918-
},
919-
_ => Ok(false),
948+
"pat" => {
949+
let tokens = vec!["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
950+
match *tok {
951+
TokenTree::Token(_, ref tok) => match *tok {
952+
FatArrow | Comma | Eq | BinOp(token::Or) => IsInFollow::Yes,
953+
Ident(i, false) if i.name == "if" || i.name == "in" => IsInFollow::Yes,
954+
_ => IsInFollow::No(tokens),
955+
},
956+
_ => IsInFollow::No(tokens),
957+
}
920958
},
921-
"path" | "ty" => match *tok {
922-
TokenTree::Token(_, ref tok) => match *tok {
923-
OpenDelim(token::DelimToken::Brace) | OpenDelim(token::DelimToken::Bracket) |
924-
Comma | FatArrow | Colon | Eq | Gt | BinOp(token::Shr) | Semi |
925-
BinOp(token::Or) => Ok(true),
926-
Ident(i, false) if i.name == "as" || i.name == "where" => Ok(true),
927-
_ => Ok(false)
928-
},
929-
TokenTree::MetaVarDecl(_, _, frag) if frag.name == "block" => Ok(true),
930-
_ => Ok(false),
959+
"path" | "ty" => {
960+
let tokens = vec![
961+
"`{`", "`[`", "`=>`", "`,`", "`>`","`=`", "`:`", "`;`", "`|`", "`as`",
962+
"`where`",
963+
];
964+
match *tok {
965+
TokenTree::Token(_, ref tok) => match *tok {
966+
OpenDelim(token::DelimToken::Brace) |
967+
OpenDelim(token::DelimToken::Bracket) |
968+
Comma | FatArrow | Colon | Eq | Gt | BinOp(token::Shr) | Semi |
969+
BinOp(token::Or) => IsInFollow::Yes,
970+
Ident(i, false) if i.name == "as" || i.name == "where" => IsInFollow::Yes,
971+
_ => IsInFollow::No(tokens),
972+
},
973+
TokenTree::MetaVarDecl(_, _, frag) if frag.name == "block" => IsInFollow::Yes,
974+
_ => IsInFollow::No(tokens),
975+
}
931976
},
932977
"ident" | "lifetime" => {
933978
// being a single token, idents and lifetimes are harmless
934-
Ok(true)
979+
IsInFollow::Yes
935980
},
936981
"literal" => {
937982
// literals may be of a single token, or two tokens (negative numbers)
938-
Ok(true)
983+
IsInFollow::Yes
939984
},
940985
"meta" | "tt" => {
941986
// being either a single token or a delimited sequence, tt is
942987
// harmless
943-
Ok(true)
988+
IsInFollow::Yes
944989
},
945990
"vis" => {
946991
// Explicitly disallow `priv`, on the off chance it comes back.
992+
let tokens = vec!["`,`", "an ident", "a type"];
947993
match *tok {
948994
TokenTree::Token(_, ref tok) => match *tok {
949-
Comma => Ok(true),
950-
Ident(i, is_raw) if is_raw || i.name != "priv" => Ok(true),
951-
ref tok => Ok(tok.can_begin_type())
995+
Comma => IsInFollow::Yes,
996+
Ident(i, is_raw) if is_raw || i.name != "priv" => IsInFollow::Yes,
997+
ref tok => if tok.can_begin_type() {
998+
IsInFollow::Yes
999+
} else {
1000+
IsInFollow::No(tokens)
1001+
}
9521002
},
9531003
TokenTree::MetaVarDecl(_, _, frag) if frag.name == "ident"
9541004
|| frag.name == "ty"
955-
|| frag.name == "path" => Ok(true),
956-
_ => Ok(false)
1005+
|| frag.name == "path" => IsInFollow::Yes,
1006+
_ => IsInFollow::No(tokens),
9571007
}
9581008
},
959-
"" => Ok(true), // keywords::Invalid
960-
_ => Err((format!("invalid fragment specifier `{}`", frag),
961-
VALID_FRAGMENT_NAMES_MSG))
1009+
"" => IsInFollow::Yes, // keywords::Invalid
1010+
_ => IsInFollow::Invalid(format!("invalid fragment specifier `{}`", frag),
1011+
VALID_FRAGMENT_NAMES_MSG),
9621012
}
9631013
}
9641014
}

0 commit comments

Comments
 (0)