Pare down the Synom trait

I would like to make it clearer that parsing a string is second-class
functionality compared to parsing tokens.
diff --git a/src/file.rs b/src/file.rs
new file mode 100644
index 0000000..0cc1428
--- /dev/null
+++ b/src/file.rs
@@ -0,0 +1,46 @@
+use super::*;
+
+ast_struct! {
+    pub struct File {
+        pub shebang: Option<String>,
+        pub attrs: Vec<Attribute>,
+        pub items: Vec<Item>,
+    }
+}
+
+#[cfg(feature = "parsing")]
+pub mod parsing {
+    use super::*;
+
+    use synom::Synom;
+
+    impl Synom for File {
+        named!(parse -> Self, do_parse!(
+            attrs: many0!(call!(Attribute::parse_inner)) >>
+            items: many0!(syn!(Item)) >>
+            (File {
+                shebang: None,
+                attrs: attrs,
+                items: items,
+            })
+        ));
+
+        fn description() -> Option<&'static str> {
+            Some("crate")
+        }
+    }
+}
+
+#[cfg(feature = "printing")]
+mod printing {
+    use super::*;
+    use attr::FilterAttrs;
+    use quote::{Tokens, ToTokens};
+
+    impl ToTokens for File {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append_all(self.attrs.inner());
+            tokens.append_all(&self.items);
+        }
+    }
+}
diff --git a/src/fold.rs b/src/fold.rs
index b39557a..99b66cb 100644
--- a/src/fold.rs
+++ b/src/fold.rs
@@ -13,7 +13,7 @@
 /// method's default implementation recursively visits the substructure of the
 /// input via the `noop_fold` methods, which perform an "identity fold", that
 /// is, they return the same structure that they are given (for example the
-/// `fold_crate` method by default calls `fold::noop_fold_crate`).
+/// `fold_file` method by default calls `fold::noop_fold_file`).
 ///
 /// If you want to ensure that your code handles every variant explicitly, you
 /// need to override each method and monitor future changes to `Folder` in case
@@ -90,8 +90,8 @@
     }
 
     #[cfg(feature = "full")]
-    fn fold_crate(&mut self, _crate: Crate) -> Crate {
-        noop_fold_crate(self, _crate)
+    fn fold_file(&mut self, file: File) -> File {
+        noop_fold_file(self, file)
     }
     #[cfg(feature = "full")]
     fn fold_item(&mut self, item: Item) -> Item {
@@ -564,15 +564,14 @@
 }
 
 #[cfg(feature = "full")]
-pub fn noop_fold_crate<F: ?Sized + Folder>(folder: &mut F,
-                                           krate: Crate)
-                                           -> Crate {
-    Crate {
-        attrs: krate.attrs.lift(|a| folder.fold_attribute(a)),
-        items: krate.items.lift(|i| folder.fold_item(i)),
-        ..krate
+pub fn noop_fold_file<F: ?Sized + Folder>(folder: &mut F,
+                                          file: File)
+                                          -> File {
+    File {
+        attrs: file.attrs.lift(|a| folder.fold_attribute(a)),
+        items: file.items.lift(|i| folder.fold_item(i)),
+        ..file
     }
-
 }
 
 #[cfg(feature = "full")]
diff --git a/src/krate.rs b/src/krate.rs
deleted file mode 100644
index 7239bc3..0000000
--- a/src/krate.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-use super::*;
-
-ast_struct! {
-    pub struct Crate {
-        pub shebang: Option<String>,
-        pub attrs: Vec<Attribute>,
-        pub items: Vec<Item>,
-    }
-}
-
-#[cfg(feature = "parsing")]
-pub mod parsing {
-    use super::*;
-
-    use synom::{Synom, ParseError};
-
-    impl Synom for Crate {
-        named!(parse -> Self, do_parse!(
-            attrs: many0!(call!(Attribute::parse_inner)) >>
-            items: many0!(syn!(Item)) >>
-            (Crate {
-                shebang: None,
-                attrs: attrs,
-                items: items,
-            })
-        ));
-
-        fn description() -> Option<&'static str> {
-            Some("crate")
-        }
-
-        fn parse_str_all(mut input: &str) -> Result<Self, ParseError> {
-            // Strip the BOM if it is present
-            const BOM: &'static str = "\u{feff}";
-            if input.starts_with(BOM) {
-                input = &input[BOM.len()..];
-            }
-
-            let mut shebang = None;
-            if input.starts_with("#!") && !input.starts_with("#![") {
-                if let Some(idx) = input.find('\n') {
-                    shebang = Some(input[..idx].to_string());
-                    input = &input[idx..];
-                } else {
-                    shebang = Some(input.to_string());
-                    input = "";
-                }
-            }
-
-            let mut krate: Crate = Self::parse_all(input.parse()?)?;
-            krate.shebang = shebang;
-            Ok(krate)
-        }
-    }
-}
-
-#[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) {
-            tokens.append_all(self.attrs.inner());
-            tokens.append_all(&self.items);
-        }
-    }
-}
diff --git a/src/lib.rs b/src/lib.rs
index a19581d..92cb9b2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,6 +2,7 @@
 
 #![cfg_attr(feature = "cargo-clippy", allow(large_enum_variant))]
 
+extern crate proc_macro;
 extern crate proc_macro2;
 extern crate unicode_xid;
 
@@ -62,9 +63,9 @@
                ArgSelf, ArgCaptured};
 
 #[cfg(feature = "full")]
-mod krate;
+mod file;
 #[cfg(feature = "full")]
-pub use krate::Crate;
+pub use file::File;
 
 mod lifetime;
 pub use lifetime::Lifetime;
@@ -100,45 +101,177 @@
 #[cfg(feature = "fold")]
 pub mod fold;
 
+////////////////////////////////////////////////////////////////////////////////
+
 #[cfg(feature = "parsing")]
-mod parsing {
-    use std::str::FromStr;
+pub use synom::ParseError;
 
-    use super::*;
-    use synom::{Synom, ParseError};
-    use proc_macro2::TokenStream;
+#[cfg(feature = "parsing")]
+use synom::{Synom, SynomBuffer};
 
-    macro_rules! traits {
-        ($($ty:ident,)*) => ($(
-            impl From<TokenStream> for $ty {
-                fn from(stream: TokenStream) -> $ty {
-                    $ty::parse_all_unwrap(stream)
-                }
+/// Parse tokens of source code into the chosen syn data type.
+///
+/// This is preferred over parsing a string because tokens are able to preserve
+/// information about where in the user's code they were originally written (the
+/// "span" of the token), possibly allowing the compiler to produce better error
+/// messages.
+///
+/// # Examples
+///
+/// ```rust,ignore
+/// extern crate proc_macro;
+/// use proc_macro::TokenStream;
+///
+/// extern crate syn;
+///
+/// #[macro_use]
+/// extern crate quote;
+///
+/// use syn::DeriveInput;
+///
+/// #[proc_macro_derive(MyMacro)]
+/// pub fn my_macro(input: TokenStream) -> TokenStream {
+///     // Parse the tokens into a syntax tree
+///     let ast: DeriveInput = syn::parse(input).unwrap();
+///
+///     // Build the output, possibly using quasi-quotation
+///     let expanded = quote! {
+///         /* ... */
+///     };
+///
+///     // Parse back to a token stream and return it
+///     expanded.parse().unwrap()
+/// }
+/// ```
+#[cfg(feature = "parsing")]
+pub fn parse<T>(tokens: proc_macro::TokenStream) -> Result<T, ParseError>
+    where T: Synom,
+{
+    _parse(tokens.into())
+}
+
+#[cfg(feature = "parsing")]
+fn _parse<T>(tokens: proc_macro2::TokenStream) -> Result<T, ParseError>
+    where T: Synom,
+{
+    let buf = SynomBuffer::new(tokens);
+    let result = T::parse(buf.begin());
+    let err = match result {
+        Ok((rest, t)) => {
+            if rest.eof() {
+                return Ok(t);
+            } else if rest == buf.begin() {
+                // parsed nothing
+                ParseError::new("failed to parse anything")
+            } else {
+                ParseError::new("failed to parse all tokens")
             }
+        }
+        Err(err) => err,
+    };
+    match T::description() {
+        Some(s) => Err(ParseError::new(format!("parsing {}: {}", s, err))),
+        None => Err(err),
+    }
+}
 
-            impl FromStr for $ty {
-                type Err = ParseError;
+/// Parse a string of Rust code into the chosen syn data type.
+///
+/// # Examples
+///
+/// ```rust
+/// extern crate syn;
+/// #
+/// # #[macro_use]
+/// # extern crate error_chain;
+///
+/// use syn::Expr;
+/// #
+/// # error_chain! {
+/// #     foreign_links {
+/// #         Syn(syn::ParseError);
+/// #     }
+/// # }
+///
+/// fn run() -> Result<()> {
+///     let code = "assert_eq!(u8::max_value(), 255)";
+///     let expr = syn::parse_str::<Expr>(code)?;
+///     println!("{:#?}", expr);
+///     Ok(())
+/// }
+/// #
+/// # fn main() { run().unwrap() }
+/// ```
+#[cfg(feature = "parsing")]
+pub fn parse_str<T: Synom>(s: &str) -> Result<T, ParseError> {
+    _parse(s.parse()?)
+}
 
-                fn from_str(s: &str) -> Result<Self, Self::Err> {
-                    $ty::parse_str_all(s)
-                }
-            }
-        )*)
+// FIXME the name parse_file makes it sound like you might pass in a path to a
+// file, rather than the content.
+/// Parse the content of a file of Rust code.
+///
+/// This is different from `syn::parse_str::<File>(content)` in two ways:
+///
+/// - It discards a leading byte order mark `\u{FEFF}` if the file has one.
+/// - It preserves the shebang line of the file, such as `#!/usr/bin/env rustx`.
+///
+/// If present, either of these would be an error using `from_str`.
+///
+/// # Examples
+///
+/// ```rust,no_run
+/// extern crate syn;
+/// #
+/// # #[macro_use]
+/// # extern crate error_chain;
+///
+/// use std::fs::File;
+/// use std::io::Read;
+/// #
+/// # error_chain! {
+/// #     foreign_links {
+/// #         Io(std::io::Error);
+/// #         Syn(syn::ParseError);
+/// #     }
+/// # }
+///
+/// fn run() -> Result<()> {
+///     let mut file = File::open("path/to/code.rs")?;
+///     let mut content = String::new();
+///     file.read_to_string(&mut content)?;
+///
+///     let ast = syn::parse_file(&content)?;
+///     if let Some(shebang) = ast.shebang {
+///         println!("{}", shebang);
+///     }
+///     println!("{} items", ast.items.len());
+///
+///     Ok(())
+/// }
+/// #
+/// # fn main() { run().unwrap() }
+/// ```
+#[cfg(all(feature = "parsing", feature = "full"))]
+pub fn parse_file(mut content: &str) -> Result<File, ParseError> {
+    // Strip the BOM if it is present
+    const BOM: &'static str = "\u{feff}";
+    if content.starts_with(BOM) {
+        content = &content[BOM.len()..];
     }
 
-    traits! {
-        DeriveInput,
-        TyParamBound,
-        Ident,
-        WhereClause,
-        Ty,
-        Lit,
+    let mut shebang = None;
+    if content.starts_with("#!") && !content.starts_with("#![") {
+        if let Some(idx) = content.find('\n') {
+            shebang = Some(content[..idx].to_string());
+            content = &content[idx..];
+        } else {
+            shebang = Some(content.to_string());
+            content = "";
+        }
     }
 
-    #[cfg(feature = "full")]
-    traits! {
-        Expr,
-        Item,
-        Crate,
-    }
+    let mut file: File = parse_str(content)?;
+    file.shebang = shebang;
+    Ok(file)
 }
diff --git a/src/visit.rs b/src/visit.rs
index 5542c45..550afb7 100644
--- a/src/visit.rs
+++ b/src/visit.rs
@@ -82,8 +82,8 @@
     }
 
     #[cfg(feature = "full")]
-    fn visit_crate(&mut self, _crate: &Crate) {
-        walk_crate(self, _crate);
+    fn visit_file(&mut self, file: &File) {
+        walk_file(self, file);
     }
     #[cfg(feature = "full")]
     fn visit_item(&mut self, item: &Item) {
@@ -330,9 +330,9 @@
 }
 
 #[cfg(feature = "full")]
-pub fn walk_crate<V: Visitor>(visitor: &mut V, _crate: &Crate) {
-    walk_list!(visitor, visit_attribute, &_crate.attrs);
-    walk_list!(visitor, visit_item, &_crate.items);
+pub fn walk_file<V: Visitor>(visitor: &mut V, file: &File) {
+    walk_list!(visitor, visit_attribute, &file.attrs);
+    walk_list!(visitor, visit_item, &file.items);
 }
 
 #[cfg(feature = "full")]