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 {