diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs
index 675482fd644..5b8307eb6c6 100644
--- a/src/libsyntax/ext/tt/macro_parser.rs
+++ b/src/libsyntax/ext/tt/macro_parser.rs
@@ -200,18 +200,19 @@ pub enum NamedMatch {
 }
 
 pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>])
-            -> HashMap<Name, Rc<NamedMatch>> {
+            -> ParseResult<HashMap<Name, Rc<NamedMatch>>> {
     fn n_rec(p_s: &ParseSess, m: &TokenTree, res: &[Rc<NamedMatch>],
-             ret_val: &mut HashMap<Name, Rc<NamedMatch>>, idx: &mut usize) {
+             ret_val: &mut HashMap<Name, Rc<NamedMatch>>, idx: &mut usize)
+             -> Result<(), (codemap::Span, String)> {
         match *m {
             TokenTree::Sequence(_, ref seq) => {
                 for next_m in &seq.tts {
-                    n_rec(p_s, next_m, res, ret_val, idx)
+                    try!(n_rec(p_s, next_m, res, ret_val, idx))
                 }
             }
             TokenTree::Delimited(_, ref delim) => {
                 for next_m in &delim.tts {
-                    n_rec(p_s, next_m, res, ret_val, idx)
+                    try!(n_rec(p_s, next_m, res, ret_val, idx));
                 }
             }
             TokenTree::Token(sp, MatchNt(bind_name, _, _, _)) => {
@@ -221,26 +222,36 @@ pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc<NamedMatch>])
                         *idx += 1;
                     }
                     Occupied(..) => {
-                        panic!(p_s.span_diagnostic
-                           .span_fatal(sp,
-                                       &format!("duplicated bind name: {}",
-                                               bind_name)))
+                        return Err((sp, format!("duplicated bind name: {}", bind_name)))
                     }
                 }
             }
-            TokenTree::Token(_, SubstNt(..)) => panic!("Cannot fill in a NT"),
+            TokenTree::Token(sp, SubstNt(..)) => {
+                return Err((sp, "missing fragment specifier".to_string()))
+            }
             TokenTree::Token(_, _) => (),
         }
+
+        Ok(())
     }
+
     let mut ret_val = HashMap::new();
     let mut idx = 0;
-    for m in ms { n_rec(p_s, m, res, &mut ret_val, &mut idx) }
-    ret_val
+    for m in ms {
+        match n_rec(p_s, m, res, &mut ret_val, &mut idx) {
+            Ok(_) => {},
+            Err((sp, msg)) => return Error(sp, msg),
+        }
+    }
+
+    Success(ret_val)
 }
 
 pub enum ParseResult<T> {
     Success(T),
+    /// Arm failed to match
     Failure(codemap::Span, String),
+    /// Fatal error (malformed macro?). Abort compilation.
     Error(codemap::Span, String)
 }
 
@@ -429,7 +440,7 @@ pub fn parse(sess: &ParseSess,
                 for dv in &mut (&mut eof_eis[0]).matches {
                     v.push(dv.pop().unwrap());
                 }
-                return Success(nameize(sess, ms, &v[..]));
+                return nameize(sess, ms, &v[..]);
             } else if eof_eis.len() > 1 {
                 return Error(sp, "ambiguity: multiple successful parses".to_string());
             } else {
diff --git a/src/test/compile-fail/macro-missing-fragment.rs b/src/test/compile-fail/macro-missing-fragment.rs
new file mode 100644
index 00000000000..66f4ce55be8
--- /dev/null
+++ b/src/test/compile-fail/macro-missing-fragment.rs
@@ -0,0 +1,17 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+macro_rules! m {
+    ( $( any_token $field_rust_type )* ) => {}; //~ ERROR missing fragment
+}
+
+fn main() {
+    m!();
+}