Require Ident to contain a valid ident
diff --git a/src/ident.rs b/src/ident.rs
index afc8a3a..40dc1a8 100644
--- a/src/ident.rs
+++ b/src/ident.rs
@@ -4,11 +4,27 @@
 use std::hash::{Hash, Hasher};
 
 use proc_macro2::Symbol;
+use unicode_xid::UnicodeXID;
 
 use Span;
 use tokens;
 
-#[derive(Clone, Debug)]
+/// A word of Rust code, such as a keyword or variable name.
+///
+/// An identifier consists of at least one Unicode code point, the first of
+/// which has the XID_Start property and the rest of which have the XID_Continue
+/// property. An underscore may be used as the first character as long as it is
+/// not the only character.
+///
+/// - The empty string is not an identifier. Use `Option<Ident>`.
+/// - An underscore by itself is not an identifier. Use
+///   `syn::tokens::Underscore` instead.
+/// - A lifetime is not an identifier. Use `syn::Lifetime` instead.
+///
+/// An identifier constructed with `Ident::new` is permitted to be a Rust
+/// keyword, though parsing an identifier with `syn!(Ident)` rejects Rust
+/// keywords.
+#[derive(Copy, Clone, Debug)]
 pub struct Ident {
     pub sym: Symbol,
     pub span: Span,
@@ -16,6 +32,42 @@
 
 impl Ident {
     pub fn new(sym: Symbol, span: Span) -> Self {
+        let s = sym.as_str();
+
+        if s.is_empty() {
+            panic!("ident is not allowed to be empty; use Option<Ident>");
+        }
+
+        if s.starts_with('\'') {
+            panic!("ident is not allowed to be a lifetime; use syn::Lifetime");
+        }
+
+        if s == "_" {
+            panic!("`_` is not a valid ident; use syn::tokens::Underscore");
+        }
+
+        fn xid_ok(s: &str) -> bool {
+            let mut chars = s.chars();
+            let first = chars.next().unwrap();
+            if !(UnicodeXID::is_xid_start(first) || first == '_') {
+                return false;
+            }
+            for ch in chars {
+                if !UnicodeXID::is_xid_continue(ch) {
+                    return false;
+                }
+            }
+            true
+        }
+
+        fn integer_ok(s: &str) -> bool {
+            s.bytes().all(|digit| digit >= b'0' && digit <= b'9')
+        }
+
+        if !(xid_ok(s) || integer_ok(s)) {
+            panic!("{:?} is not a valid ident", s);
+        }
+
         Ident {
             sym: sym,
             span: span,
@@ -59,12 +111,6 @@
     }
 }
 
-impl From<usize> for Ident {
-    fn from(u: usize) -> Self {
-        Ident::new(u.to_string()[..].into(), Span::default())
-    }
-}
-
 impl AsRef<str> for Ident {
     fn as_ref(&self) -> &str {
         self.sym.as_str()