]> git.lizzy.rs Git - rust.git/commitdiff
Implementation of the `vis` macro matcher.
authorDaniel Keep <daniel.keep@gmail.com>
Sun, 24 Apr 2016 16:04:01 +0000 (02:04 +1000)
committerAlex Burka <alex@alexburka.com>
Sat, 15 Apr 2017 19:06:19 +0000 (19:06 +0000)
src/libsyntax/ext/tt/macro_parser.rs
src/libsyntax/ext/tt/macro_rules.rs
src/libsyntax/fold.rs
src/libsyntax/parse/token.rs
src/libsyntax/print/pprust.rs
src/test/run-pass/macro-pub-matcher.rs [new file with mode: 0644]

index 6cd1fea2e75e2ccf8749c0f0afcef19b9c4ab030..eb0b7c29f8d9ad8d74aa15a283c7b12f5ea1c608 100644 (file)
@@ -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")
index 93348c8f0837677faddbd7b437e17e63328536a4..4e197a85ebd9629192c6165ec5b4a4acf89ace63 100644 (file)
@@ -790,6 +790,19 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> Result<bool, (String, &'
                 // harmless
                 Ok(true)
             },
+            "vis" => {
+                // 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: &quoted::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,
     }
 }
index a6ab8e10d9f91aef1cff7fe1944be51965869e5c..f39399a62e856c366fb62d73361cc8c68b3b6121 100644 (file)
@@ -636,6 +636,7 @@ pub fn noop_fold_interpolated<T: Folder>(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)),
     }
 }
 
index 74aa3984a9a42a1f2917c59d5f1e69b7d6260bed..513aa866043ad1b30d1ebba8dcc6f02ac69358c3 100644 (file)
@@ -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 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
             NtGenerics(..) => f.pad("NtGenerics(..)"),
             NtWhereClause(..) => f.pad("NtWhereClause(..)"),
             NtArg(..) => f.pad("NtArg(..)"),
+            NtVis(..) => f.pad("NtVis(..)"),
         }
     }
 }
index 433ba3d3693f1a396565ec486d2b2d4e53a5d1e5..2494af2c1618692154384cd91152bc083869c66b 100644 (file)
@@ -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 (file)
index 0000000..64fa0be
--- /dev/null
@@ -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() {}