From a2489495d909c43cfbefaeb79db6a77b13908257 Mon Sep 17 00:00:00 2001 From: Daniel Keep Date: Mon, 25 Apr 2016 02:04:01 +1000 Subject: [PATCH] Implementation of the `vis` macro matcher. --- src/libsyntax/ext/tt/macro_parser.rs | 1 + src/libsyntax/ext/tt/macro_rules.rs | 15 +++- src/libsyntax/fold.rs | 1 + src/libsyntax/parse/token.rs | 2 + src/libsyntax/print/pprust.rs | 5 ++ src/test/run-pass/macro-pub-matcher.rs | 119 +++++++++++++++++++++++++ 6 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/macro-pub-matcher.rs diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 6cd1fea2e75e..eb0b7c29f8d9 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -529,6 +529,7 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal { token::NtPath(panictry!(p.parse_path(PathStyle::Type))) }, "meta" => token::NtMeta(panictry!(p.parse_meta_item())), + "vis" => token::NtVis(panictry!(p.parse_visibility(true))), // this is not supposed to happen, since it has been checked // when compiling the macro. _ => p.span_bug(sp, "invalid fragment specifier") diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 93348c8f0837..4e197a85ebd9 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -790,6 +790,19 @@ fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> Result { + // Explicitly disallow `priv`, on the off chance it comes back. + match *tok { + Comma => Ok(true), + ModSep => Ok(true), + MatchNt(_, ref frag, _, _) => { + let name = frag.name.as_str(); + Ok(name == "ident" || name == "ty") + }, + Ident(i, _) if i.name.as_str() != "priv" => Ok(true), + _ => Ok(false) + } + }, "" => Ok(true), // keywords::Invalid _ => Err((format!("invalid fragment specifier `{}`", frag), "valid fragment specifiers are `ident`, `block`, \ @@ -813,7 +826,7 @@ fn has_legal_fragment_specifier(tok: "ed::TokenTree) -> Result<(), String> { fn is_legal_fragment_specifier(frag: &str) -> bool { match frag { "item" | "block" | "stmt" | "expr" | "pat" | - "path" | "ty" | "ident" | "meta" | "tt" | "" => true, + "path" | "ty" | "ident" | "meta" | "tt" | "vis" | "" => true, _ => false, } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index a6ab8e10d9f9..f39399a62e85 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -636,6 +636,7 @@ pub fn noop_fold_interpolated(nt: token::Nonterminal, fld: &mut T) token::NtWhereClause(where_clause) => token::NtWhereClause(fld.fold_where_clause(where_clause)), token::NtArg(arg) => token::NtArg(fld.fold_arg(arg)), + token::NtVis(vis) => token::NtVis(fld.fold_vis(vis)), } } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 74aa3984a9a4..513aa866043a 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -371,6 +371,7 @@ pub enum Nonterminal { NtGenerics(ast::Generics), NtWhereClause(ast::WhereClause), NtArg(ast::Arg), + NtVis(ast::Visibility), } impl fmt::Debug for Nonterminal { @@ -392,6 +393,7 @@ impl fmt::Debug for Nonterminal { NtGenerics(..) => f.pad("NtGenerics(..)"), NtWhereClause(..) => f.pad("NtWhereClause(..)"), NtArg(..) => f.pad("NtArg(..)"), + NtVis(..) => f.pad("NtVis(..)"), } } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 433ba3d3693f..2494af2c1618 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -293,6 +293,7 @@ pub fn token_to_string(tok: &Token) -> String { token::NtGenerics(ref e) => generics_to_string(&e), token::NtWhereClause(ref e) => where_clause_to_string(&e), token::NtArg(ref e) => arg_to_string(&e), + token::NtVis(ref e) => vis_to_string(&e), } } } @@ -373,6 +374,10 @@ pub fn ident_to_string(id: ast::Ident) -> String { to_string(|s| s.print_ident(id)) } +pub fn vis_to_string(v: &ast::Visibility) -> String { + to_string(|s| s.print_visibility(v)) +} + pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, constness: ast::Constness, diff --git a/src/test/run-pass/macro-pub-matcher.rs b/src/test/run-pass/macro-pub-matcher.rs new file mode 100644 index 000000000000..64fa0bef464c --- /dev/null +++ b/src/test/run-pass/macro-pub-matcher.rs @@ -0,0 +1,119 @@ +#![allow(dead_code, unused_imports)] +#![feature(pub_restricted)] + +/** +Ensure that `:vis` matches can be captured in existing positions, and passed +through without the need for reparse tricks. +*/ +macro_rules! vis_passthru { + ($vis:vis const $name:ident: $ty:ty = $e:expr;) => { $vis const $name: $ty = $e; }; + ($vis:vis enum $name:ident {}) => { $vis struct $name {} }; + ($vis:vis extern "C" fn $name:ident() {}) => { $vis extern "C" fn $name() {} }; + ($vis:vis fn $name:ident() {}) => { $vis fn $name() {} }; + ($vis:vis mod $name:ident {}) => { $vis mod $name {} }; + ($vis:vis static $name:ident: $ty:ty = $e:expr;) => { $vis static $name: $ty = $e; }; + ($vis:vis struct $name:ident;) => { $vis struct $name; }; + ($vis:vis trait $name:ident {}) => { $vis trait $name {} }; + ($vis:vis type $name:ident = $ty:ty;) => { $vis type $name = $ty; }; + ($vis:vis use $path:ident as $name:ident;) => { $vis use self::$path as $name; }; +} + +mod with_pub { + vis_passthru! { pub const A: i32 = 0; } + vis_passthru! { pub enum B {} } + vis_passthru! { pub extern "C" fn c() {} } + vis_passthru! { pub mod d {} } + vis_passthru! { pub static E: i32 = 0; } + vis_passthru! { pub struct F; } + vis_passthru! { pub trait G {} } + vis_passthru! { pub type H = i32; } + vis_passthru! { pub use A as I; } +} + +mod without_pub { + vis_passthru! { const A: i32 = 0; } + vis_passthru! { enum B {} } + vis_passthru! { extern "C" fn c() {} } + vis_passthru! { mod d {} } + vis_passthru! { static E: i32 = 0; } + vis_passthru! { struct F; } + vis_passthru! { trait G {} } + vis_passthru! { type H = i32; } + vis_passthru! { use A as I; } +} + +mod with_pub_restricted { + vis_passthru! { pub(crate) const A: i32 = 0; } + vis_passthru! { pub(crate) enum B {} } + vis_passthru! { pub(crate) extern "C" fn c() {} } + vis_passthru! { pub(crate) mod d {} } + vis_passthru! { pub(crate) static E: i32 = 0; } + vis_passthru! { pub(crate) struct F; } + vis_passthru! { pub(crate) trait G {} } + vis_passthru! { pub(crate) type H = i32; } + vis_passthru! { pub(crate) use A as I; } +} + +mod garden { + mod with_pub_restricted_path { + vis_passthru! { pub(::garden) const A: i32 = 0; } + vis_passthru! { pub(::garden) enum B {} } + vis_passthru! { pub(::garden) extern "C" fn c() {} } + vis_passthru! { pub(::garden) mod d {} } + vis_passthru! { pub(::garden) static E: i32 = 0; } + vis_passthru! { pub(::garden) struct F; } + vis_passthru! { pub(::garden) trait G {} } + vis_passthru! { pub(::garden) type H = i32; } + vis_passthru! { pub(::garden) use A as I; } + } +} + +/* +Ensure that the `:vis` matcher works in a more complex situation: parsing a +struct definition. +*/ +macro_rules! vis_parse_struct { + /* + The rule duplication is currently unavoidable due to the leading attribute + matching. + */ + ($(#[$($attrs:tt)*])* pub($($vis:tt)*) struct $name:ident {$($body:tt)*}) => { + vis_parse_struct! { @parse_fields $(#[$($attrs)*])*, pub($($vis)*), $name, $($body)* } + }; + ($(#[$($attrs:tt)*])* pub struct $name:ident {$($body:tt)*}) => { + vis_parse_struct! { @parse_fields $(#[$($attrs)*])*, pub, $name, $($body)* } + }; + ($(#[$($attrs:tt)*])* struct $name:ident {$($body:tt)*}) => { + vis_parse_struct! { @parse_fields $(#[$($attrs)*])*, , $name, $($body)* } + }; + + ($(#[$($attrs:tt)*])* pub($($vis:tt)*) struct $name:ident ($($body:tt)*);) => { + vis_parse_struct! { @parse_tuple $(#[$($attrs)*])*, pub($($vis)*), $name, $($body)* } + }; + ($(#[$($attrs:tt)*])* pub struct $name:ident ($($body:tt)*);) => { + vis_parse_struct! { @parse_tuple $(#[$($attrs)*])*, pub, $name, $($body)* } + }; + ($(#[$($attrs:tt)*])* struct $name:ident ($($body:tt)*);) => { + vis_parse_struct! { @parse_tuple $(#[$($attrs)*])*, , $name, $($body)* } + }; + + (@parse_fields $(#[$attrs:meta])*, $vis:vis, $name:ident, $($fvis:vis $fname:ident: $fty:ty),* $(,)*) => { + $(#[$attrs])* $vis struct $name { $($fvis $fname: $fty,)* } + }; + + (@parse_tuple $(#[$attrs:meta])*, $vis:vis, $name:ident, $($fvis:vis $fty:ty),* $(,)*) => { + $(#[$attrs])* $vis struct $name ( $($fvis $fty,)* ); + }; +} + +mod test_struct { + vis_parse_struct! { pub(crate) struct A { pub a: i32, b: i32, pub(crate) c: i32 } } + vis_parse_struct! { pub struct B { a: i32, pub(crate) b: i32, pub c: i32 } } + vis_parse_struct! { struct C { pub(crate) a: i32, pub b: i32, c: i32 } } + + vis_parse_struct! { pub(crate) struct D (pub i32, i32, pub(crate) i32); } + vis_parse_struct! { pub struct E (i32, pub(crate) i32, pub i32); } + vis_parse_struct! { struct F (pub(crate) i32, pub i32, i32); } +} + +fn main() {}