Skip to content

Commit 2cfd790

Browse files
committedOct 25, 2018
List allowed tokens after macro fragments
1 parent 8ec22e7 commit 2cfd790

File tree

5 files changed

+385
-141
lines changed

5 files changed

+385
-141
lines changed
 

‎src/libsyntax/ext/tt/macro_rules.rs

+94-44
Original file line numberDiff line numberDiff line change
@@ -792,15 +792,15 @@ fn check_matcher_core(sess: &ParseSess,
792792
if let TokenTree::MetaVarDecl(_, ref name, ref frag_spec) = *token {
793793
for next_token in &suffix_first.tokens {
794794
match is_in_follow(next_token, &frag_spec.as_str()) {
795-
Err((msg, help)) => {
795+
IsInFollow::Invalid(msg, help) => {
796796
sess.span_diagnostic.struct_span_err(next_token.span(), &msg)
797797
.help(help).emit();
798798
// don't bother reporting every source of
799799
// conflict for a particular element of `last`.
800800
continue 'each_last;
801801
}
802-
Ok(true) => {}
803-
Ok(false) => {
802+
IsInFollow::Yes => {}
803+
IsInFollow::No(ref possible) => {
804804
let may_be = if last.tokens.len() == 1 &&
805805
suffix_first.tokens.len() == 1
806806
{
@@ -809,15 +809,41 @@ fn check_matcher_core(sess: &ParseSess,
809809
"may be"
810810
};
811811

812-
sess.span_diagnostic.span_err(
813-
next_token.span(),
812+
let sp = next_token.span();
813+
let mut err = sess.span_diagnostic.struct_span_err(
814+
sp,
814815
&format!("`${name}:{frag}` {may_be} followed by `{next}`, which \
815816
is not allowed for `{frag}` fragments",
816817
name=name,
817818
frag=frag_spec,
818819
next=quoted_tt_to_string(next_token),
819-
may_be=may_be)
820+
may_be=may_be),
820821
);
822+
err.span_label(
823+
sp,
824+
format!("not allowed after `{}` fragments", frag_spec),
825+
);
826+
let msg = "allowed there are: ";
827+
match &possible[..] {
828+
&[] => {}
829+
&[t] => {
830+
err.note(&format!(
831+
"only {} is allowed after `{}` fragments",
832+
t,
833+
frag_spec,
834+
));
835+
}
836+
ts => {
837+
err.note(&format!(
838+
"{}{} or {}",
839+
msg,
840+
ts[..ts.len() - 1].iter().map(|s| *s)
841+
.collect::<Vec<_>>().join(", "),
842+
ts[ts.len() - 1],
843+
));
844+
}
845+
}
846+
err.emit();
821847
}
822848
}
823849
}
@@ -860,6 +886,12 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool {
860886
}
861887
}
862888

889+
enum IsInFollow {
890+
Yes,
891+
No(Vec<&'static str>),
892+
Invalid(String, &'static str),
893+
}
894+
863895
/// True if `frag` can legally be followed by the token `tok`. For
864896
/// fragments that can consume an unbounded number of tokens, `tok`
865897
/// must be within a well-defined follow set. This is intended to
@@ -868,81 +900,99 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool {
868900
/// break macros that were relying on that binary operator as a
869901
/// separator.
870902
// when changing this do not forget to update doc/book/macros.md!
871-
fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> Result<bool, (String, &'static str)> {
903+
fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> IsInFollow {
872904
use self::quoted::TokenTree;
873905

874906
if let TokenTree::Token(_, token::CloseDelim(_)) = *tok {
875907
// closing a token tree can never be matched by any fragment;
876908
// iow, we always require that `(` and `)` match, etc.
877-
Ok(true)
909+
IsInFollow::Yes
878910
} else {
879911
match frag {
880912
"item" => {
881913
// since items *must* be followed by either a `;` or a `}`, we can
882914
// accept anything after them
883-
Ok(true)
915+
IsInFollow::Yes
884916
},
885917
"block" => {
886918
// anything can follow block, the braces provide an easy boundary to
887919
// maintain
888-
Ok(true)
920+
IsInFollow::Yes
889921
},
890-
"stmt" | "expr" => match *tok {
891-
TokenTree::Token(_, ref tok) => match *tok {
892-
FatArrow | Comma | Semi => Ok(true),
893-
_ => Ok(false)
894-
},
895-
_ => Ok(false),
922+
"stmt" | "expr" => {
923+
let tokens = vec!["`=>`", "`,`", "`;`"];
924+
match *tok {
925+
TokenTree::Token(_, ref tok) => match *tok {
926+
FatArrow | Comma | Semi => IsInFollow::Yes,
927+
_ => IsInFollow::No(tokens),
928+
},
929+
_ => IsInFollow::No(tokens),
930+
}
896931
},
897-
"pat" => match *tok {
898-
TokenTree::Token(_, ref tok) => match *tok {
899-
FatArrow | Comma | Eq | BinOp(token::Or) => Ok(true),
900-
Ident(i, false) if i.name == "if" || i.name == "in" => Ok(true),
901-
_ => Ok(false)
902-
},
903-
_ => Ok(false),
932+
"pat" => {
933+
let tokens = vec!["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
934+
match *tok {
935+
TokenTree::Token(_, ref tok) => match *tok {
936+
FatArrow | Comma | Eq | BinOp(token::Or) => IsInFollow::Yes,
937+
Ident(i, false) if i.name == "if" || i.name == "in" => IsInFollow::Yes,
938+
_ => IsInFollow::No(tokens),
939+
},
940+
_ => IsInFollow::No(tokens),
941+
}
904942
},
905-
"path" | "ty" => match *tok {
906-
TokenTree::Token(_, ref tok) => match *tok {
907-
OpenDelim(token::DelimToken::Brace) | OpenDelim(token::DelimToken::Bracket) |
908-
Comma | FatArrow | Colon | Eq | Gt | BinOp(token::Shr) | Semi |
909-
BinOp(token::Or) => Ok(true),
910-
Ident(i, false) if i.name == "as" || i.name == "where" => Ok(true),
911-
_ => Ok(false)
912-
},
913-
TokenTree::MetaVarDecl(_, _, frag) if frag.name == "block" => Ok(true),
914-
_ => Ok(false),
943+
"path" | "ty" => {
944+
let tokens = vec![
945+
"`{`", "`[`", "`=>`", "`,`", "`>`","`=`", "`:`", "`;`", "`|`", "`as`",
946+
"`where`",
947+
];
948+
match *tok {
949+
TokenTree::Token(_, ref tok) => match *tok {
950+
OpenDelim(token::DelimToken::Brace) |
951+
OpenDelim(token::DelimToken::Bracket) |
952+
Comma | FatArrow | Colon | Eq | Gt | BinOp(token::Shr) | Semi |
953+
BinOp(token::Or) => IsInFollow::Yes,
954+
Ident(i, false) if i.name == "as" || i.name == "where" => IsInFollow::Yes,
955+
_ => IsInFollow::No(tokens),
956+
},
957+
TokenTree::MetaVarDecl(_, _, frag) if frag.name == "block" => IsInFollow::Yes,
958+
_ => IsInFollow::No(tokens),
959+
}
915960
},
916961
"ident" | "lifetime" => {
917962
// being a single token, idents and lifetimes are harmless
918-
Ok(true)
963+
IsInFollow::Yes
919964
},
920965
"literal" => {
921966
// literals may be of a single token, or two tokens (negative numbers)
922-
Ok(true)
967+
IsInFollow::Yes
923968
},
924969
"meta" | "tt" => {
925970
// being either a single token or a delimited sequence, tt is
926971
// harmless
927-
Ok(true)
972+
IsInFollow::Yes
928973
},
929974
"vis" => {
930975
// Explicitly disallow `priv`, on the off chance it comes back.
976+
let tokens = vec!["`,`", "an ident", "a type"];
931977
match *tok {
932978
TokenTree::Token(_, ref tok) => match *tok {
933-
Comma => Ok(true),
934-
Ident(i, is_raw) if is_raw || i.name != "priv" => Ok(true),
935-
ref tok => Ok(tok.can_begin_type())
979+
Comma => IsInFollow::Yes,
980+
Ident(i, is_raw) if is_raw || i.name != "priv" => IsInFollow::Yes,
981+
ref tok => if tok.can_begin_type() {
982+
IsInFollow::Yes
983+
} else {
984+
IsInFollow::No(tokens)
985+
}
936986
},
937987
TokenTree::MetaVarDecl(_, _, frag) if frag.name == "ident"
938988
|| frag.name == "ty"
939-
|| frag.name == "path" => Ok(true),
940-
_ => Ok(false)
989+
|| frag.name == "path" => IsInFollow::Yes,
990+
_ => IsInFollow::No(tokens),
941991
}
942992
},
943-
"" => Ok(true), // keywords::Invalid
944-
_ => Err((format!("invalid fragment specifier `{}`", frag),
945-
VALID_FRAGMENT_NAMES_MSG))
993+
"" => IsInFollow::Yes, // keywords::Invalid
994+
_ => IsInFollow::Invalid(format!("invalid fragment specifier `{}`", frag),
995+
VALID_FRAGMENT_NAMES_MSG),
946996
}
947997
}
948998
}

0 commit comments

Comments
 (0)
Please sign in to comment.