Round trip testing
diff --git a/src/attr.rs b/src/attr.rs
index 8a16982..068e0ee 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -1,12 +1,24 @@
use super::*;
+use std::iter;
+
/// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Attribute {
+ pub style: AttrStyle,
pub value: MetaItem,
pub is_sugared_doc: bool,
}
+/// Distinguishes between Attributes that decorate items and Attributes that
+/// are contained as statements within items. These two cases need to be
+/// distinguished for pretty-printing.
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum AttrStyle {
+ Outer,
+ Inner,
+}
+
/// A compile-time attribute item.
///
/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
@@ -36,6 +48,31 @@
}
}
+pub trait FilterAttrs<'a> {
+ type Ret: Iterator<Item = &'a Attribute>;
+
+ fn outer(self) -> Self::Ret;
+ fn inner(self) -> Self::Ret;
+}
+
+impl<'a, T> FilterAttrs<'a> for T where T: IntoIterator<Item = &'a Attribute> {
+ type Ret = iter::Filter<T::IntoIter, fn(&&Attribute) -> bool>;
+
+ fn outer(self) -> Self::Ret {
+ fn is_outer(attr: &&Attribute) -> bool {
+ attr.style == AttrStyle::Outer
+ }
+ self.into_iter().filter(is_outer)
+ }
+
+ fn inner(self) -> Self::Ret {
+ fn is_inner(attr: &&Attribute) -> bool {
+ attr.style == AttrStyle::Inner
+ }
+ self.into_iter().filter(is_inner)
+ }
+}
+
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
@@ -43,13 +80,27 @@
use lit::{Lit, StrStyle};
use lit::parsing::lit;
- named!(pub attribute -> Attribute, alt!(
+ named!(pub inner_attr -> Attribute, do_parse!(
+ punct!("#") >>
+ punct!("!") >>
+ punct!("[") >>
+ meta_item: meta_item >>
+ punct!("]") >>
+ (Attribute {
+ style: AttrStyle::Inner,
+ value: meta_item,
+ is_sugared_doc: false,
+ })
+ ));
+
+ named!(pub outer_attr -> Attribute, alt!(
do_parse!(
punct!("#") >>
punct!("[") >>
meta_item: meta_item >>
punct!("]") >>
(Attribute {
+ style: AttrStyle::Outer,
value: meta_item,
is_sugared_doc: false,
})
@@ -60,6 +111,7 @@
not!(peek!(tag!("/"))) >>
content: take_until!("\n") >>
(Attribute {
+ style: AttrStyle::Outer,
value: MetaItem::NameValue(
"doc".into(),
Lit::Str(
@@ -102,6 +154,7 @@
fn to_tokens(&self, tokens: &mut Tokens) {
match *self {
Attribute {
+ style: AttrStyle::Outer,
value: MetaItem::NameValue(
ref name,
Lit::Str(ref value, StrStyle::Cooked),
@@ -112,6 +165,9 @@
}
_ => {
tokens.append("#");
+ if let AttrStyle::Inner = self.style {
+ tokens.append("!");
+ }
tokens.append("[");
self.value.to_tokens(tokens);
tokens.append("]");
diff --git a/src/data.rs b/src/data.rs
index 30edc8b..7082363 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -49,7 +49,7 @@
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
- use attr::parsing::attribute;
+ use attr::parsing::outer_attr;
use ident::parsing::ident;
use lit::parsing::int;
use ty::parsing::ty;
@@ -71,7 +71,7 @@
));
named!(variant -> Variant, do_parse!(
- attrs: many0!(attribute) >>
+ attrs: many0!(outer_attr) >>
id: ident >>
data: alt!(
struct_like_body => { VariantData::Struct }
@@ -106,7 +106,7 @@
));
named!(struct_field -> Field, do_parse!(
- attrs: many0!(attribute) >>
+ attrs: many0!(outer_attr) >>
vis: visibility >>
id: ident >>
punct!(":") >>
@@ -120,7 +120,7 @@
));
named!(tuple_field -> Field, do_parse!(
- attrs: many0!(attribute) >>
+ attrs: many0!(outer_attr) >>
vis: visibility >>
ty: ty >>
(Field {
diff --git a/src/item.rs b/src/item.rs
index d8488c8..86c7df9 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -209,7 +209,7 @@
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
- use attr::parsing::attribute;
+ use attr::parsing::outer_attr;
use data::parsing::visibility;
use ident::parsing::ident;
use macro_input::{Body, MacroInput};
@@ -234,7 +234,7 @@
));
named!(item_extern_crate -> Item, do_parse!(
- attrs: many0!(attribute) >>
+ attrs: many0!(outer_attr) >>
vis: visibility >>
keyword!("extern") >>
keyword!("crate") >>
@@ -275,3 +275,73 @@
}
));
}
+
+#[cfg(feature = "printing")]
+mod printing {
+ use super::*;
+ use attr::FilterAttrs;
+ use data::{VariantData, Visibility};
+ use quote::{Tokens, ToTokens};
+
+ impl ToTokens for Item {
+ fn to_tokens(&self, tokens: &mut Tokens) {
+ for attr in self.attrs.outer() {
+ attr.to_tokens(tokens);
+ }
+ match self.node {
+ ItemKind::ExternCrate(ref original) => {
+ tokens.append("extern");
+ tokens.append("crate");
+ if let Some(ref original) = *original {
+ original.to_tokens(tokens);
+ tokens.append("as");
+ }
+ self.ident.to_tokens(tokens);
+ tokens.append(";");
+ }
+ ItemKind::Use(ref _view_path) => unimplemented!(),
+ ItemKind::Static(ref _ty, ref _mutability, ref _expr) => unimplemented!(),
+ ItemKind::Const(ref _ty, ref _expr) => unimplemented!(),
+ ItemKind::Fn(ref _decl, _unsafety, _constness, ref _abi, ref _generics, ref _block) => unimplemented!(),
+ ItemKind::Mod(ref _items) => unimplemented!(),
+ ItemKind::ForeignMod(ref _foreign_mod) => unimplemented!(),
+ ItemKind::Ty(ref _ty, ref _generics) => unimplemented!(),
+ ItemKind::Enum(ref variants, ref generics) => {
+ if let Visibility::Public = self.vis {
+ tokens.append("pub");
+ }
+ tokens.append("enum");
+ self.ident.to_tokens(tokens);
+ generics.to_tokens(tokens);
+ generics.where_clause.to_tokens(tokens);
+ tokens.append("{");
+ for variant in variants {
+ variant.to_tokens(tokens);
+ tokens.append(",");
+ }
+ tokens.append("}");
+ }
+ ItemKind::Struct(ref variant_data, ref generics) => {
+ if let Visibility::Public = self.vis {
+ tokens.append("pub");
+ }
+ tokens.append("struct");
+ self.ident.to_tokens(tokens);
+ generics.to_tokens(tokens);
+ generics.where_clause.to_tokens(tokens);
+ variant_data.to_tokens(tokens);
+ match *variant_data {
+ VariantData::Struct(_) => { /* no semicolon */ }
+ VariantData::Tuple(_) |
+ VariantData::Unit => tokens.append(";"),
+ }
+ }
+ ItemKind::Union(ref _variant_data, ref _generics) => unimplemented!(),
+ ItemKind::Trait(_unsafety, ref _generics, ref _bound, ref _item) => unimplemented!(),
+ ItemKind::DefaultImpl(_unsafety, ref _path) => unimplemented!(),
+ ItemKind::Impl(_unsafety, _polarity, ref _generics, ref _path, ref _ty, ref _item) => unimplemented!(),
+ ItemKind::Mac(ref _mac) => unimplemented!(),
+ }
+ }
+ }
+}
diff --git a/src/krate.rs b/src/krate.rs
new file mode 100644
index 0000000..c5b2126
--- /dev/null
+++ b/src/krate.rs
@@ -0,0 +1,43 @@
+use super::*;
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Crate {
+ attrs: Vec<Attribute>,
+ items: Vec<Item>,
+}
+
+#[cfg(feature = "parsing")]
+pub mod parsing {
+ use super::*;
+ use attr::parsing::inner_attr;
+ use item::parsing::item;
+ use nom::multispace;
+
+ named!(pub krate -> Crate, do_parse!(
+ attrs: many0!(inner_attr) >>
+ items: many0!(item) >>
+ option!(multispace) >>
+ (Crate {
+ attrs: attrs,
+ items: items,
+ })
+ ));
+}
+
+#[cfg(feature = "printing")]
+mod printing {
+ use super::*;
+ use attr::FilterAttrs;
+ use quote::{Tokens, ToTokens};
+
+ impl ToTokens for Crate {
+ fn to_tokens(&self, tokens: &mut Tokens) {
+ for attr in self.attrs.inner() {
+ attr.to_tokens(tokens);
+ }
+ for item in &self.items {
+ item.to_tokens(tokens);
+ }
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index df62d75..50a754c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,7 @@
mod attr;
pub use attr::{
Attribute,
+ AttrStyle,
MetaItem,
};
@@ -65,6 +66,11 @@
WhereRegionPredicate,
};
+#[cfg(feature = "full")]
+mod krate;
+#[cfg(feature = "full")]
+pub use krate::Crate;
+
mod ident;
pub use ident::{
Ident,
@@ -155,13 +161,18 @@
use nom;
#[cfg(feature = "full")]
- use {expr, item};
+ use {expr, item, krate};
pub fn parse_macro_input(input: &str) -> Result<MacroInput, String> {
unwrap("macro input", macro_input::parsing::macro_input, input)
}
#[cfg(feature = "full")]
+ pub fn parse_crate(input: &str) -> Result<Crate, String> {
+ unwrap("crate", krate::parsing::krate, input)
+ }
+
+ #[cfg(feature = "full")]
pub fn parse_item(input: &str) -> Result<Item, String> {
unwrap("item", item::parsing::item, input)
}
diff --git a/src/macro_input.rs b/src/macro_input.rs
index 848e609..c2b5900 100644
--- a/src/macro_input.rs
+++ b/src/macro_input.rs
@@ -18,14 +18,14 @@
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
- use attr::parsing::attribute;
+ use attr::parsing::outer_attr;
use data::parsing::{visibility, struct_body, enum_body};
use generics::parsing::generics;
use ident::parsing::ident;
use nom::multispace;
named!(pub macro_input -> MacroInput, do_parse!(
- attrs: many0!(attribute) >>
+ attrs: many0!(outer_attr) >>
vis: visibility >>
which: alt!(keyword!("struct") | keyword!("enum")) >>
id: ident >>
@@ -55,12 +55,13 @@
#[cfg(feature = "printing")]
mod printing {
use super::*;
+ use attr::FilterAttrs;
use data::{Visibility, VariantData};
use quote::{Tokens, ToTokens};
impl ToTokens for MacroInput {
fn to_tokens(&self, tokens: &mut Tokens) {
- for attr in &self.attrs {
+ for attr in self.attrs.outer() {
attr.to_tokens(tokens);
}
if let Visibility::Public = self.vis {