about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAndy Russell <arussell123@gmail.com>2018-12-04 14:10:32 -0500
committerAndy Russell <arussell123@gmail.com>2019-01-02 11:02:30 -0500
commit0a6fb8473872b2a6dd7fe66697f90dceac667ec4 (patch)
treeb7249971dd35682476f30aa214312a60105ae87d
parentcae164753f557f668cb75610abda4f790981e5e6 (diff)
downloadrust-0a6fb8473872b2a6dd7fe66697f90dceac667ec4.tar.gz
rust-0a6fb8473872b2a6dd7fe66697f90dceac667ec4.zip
make `panictry!` private to libsyntax
This commit completely removes usage of the `panictry!` macro from
outside libsyntax. The macro causes parse errors to be fatal, so using
it in libsyntax_ext caused parse failures *within* a syntax extension to
be fatal, which is probably not intended.

Furthermore, this commit adds spans to diagnostics emitted by empty
extensions if they were missing, à la #56491.
-rw-r--r--src/librustdoc/core.rs10
-rw-r--r--src/librustdoc/lib.rs2
-rw-r--r--src/librustdoc/test.rs13
-rw-r--r--src/libsyntax/lib.rs2
-rw-r--r--src/libsyntax_ext/asm.rs116
-rw-r--r--src/libsyntax_ext/assert.rs68
-rw-r--r--src/libsyntax_ext/cfg.rs35
-rw-r--r--src/libsyntax_ext/format.rs48
-rw-r--r--src/libsyntax_ext/global_asm.rs57
-rw-r--r--src/test/rustdoc-ui/failed-doctest-output.stdout4
-rw-r--r--src/test/ui/asm/asm-parse-errors.rs15
-rw-r--r--src/test/ui/asm/asm-parse-errors.stderr68
-rw-r--r--src/test/ui/issues/issue-22644.rs4
-rw-r--r--src/test/ui/issues/issue-22644.stderr18
-rw-r--r--src/test/ui/macros/assert.rs2
-rw-r--r--src/test/ui/macros/assert.stderr16
-rw-r--r--src/test/ui/macros/cfg.rs5
-rw-r--r--src/test/ui/macros/cfg.stderr21
-rw-r--r--src/test/ui/macros/format-parse-errors.rs10
-rw-r--r--src/test/ui/macros/format-parse-errors.stderr44
-rw-r--r--src/test/ui/macros/global-asm.rs7
-rw-r--r--src/test/ui/macros/global-asm.stderr20
22 files changed, 451 insertions, 134 deletions
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 3fa2d085ece..032b0fc9748 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -23,7 +23,7 @@ use syntax::json::JsonEmitter;
 use syntax::ptr::P;
 use syntax::symbol::keywords;
 use syntax_pos::DUMMY_SP;
-use errors;
+use errors::{self, FatalError};
 use errors::emitter::{Emitter, EmitterWriter};
 use parking_lot::ReentrantMutex;
 
@@ -429,7 +429,13 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
 
         let control = &driver::CompileController::basic();
 
-        let krate = panictry!(driver::phase_1_parse_input(control, &sess, &input));
+        let krate = match driver::phase_1_parse_input(control, &sess, &input) {
+            Ok(krate) => krate,
+            Err(mut e) => {
+                e.emit();
+                FatalError.raise();
+            }
+        };
 
         let name = match crate_name {
             Some(ref crate_name) => crate_name.clone(),
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 67d512ed71d..1b6d7e87192 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -32,7 +32,7 @@ extern crate rustc_metadata;
 extern crate rustc_target;
 extern crate rustc_typeck;
 extern crate serialize;
-#[macro_use] extern crate syntax;
+extern crate syntax;
 extern crate syntax_pos;
 extern crate test as testing;
 #[macro_use] extern crate log;
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index 24bb00f4112..dd9fbbb2546 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -1,4 +1,4 @@
-use errors;
+use errors::{self, FatalError};
 use errors::emitter::ColorConfig;
 use rustc_data_structures::sync::Lrc;
 use rustc_lint;
@@ -84,9 +84,14 @@ pub fn run(mut options: Options) -> isize {
         target_features::add_configuration(&mut cfg, &sess, &*codegen_backend);
         sess.parse_sess.config = cfg;
 
-        let krate = panictry!(driver::phase_1_parse_input(&driver::CompileController::basic(),
-                                                        &sess,
-                                                        &input));
+        let krate =
+            match driver::phase_1_parse_input(&driver::CompileController::basic(), &sess, &input) {
+                Ok(krate) => krate,
+                Err(mut e) => {
+                    e.emit();
+                    FatalError.raise();
+                }
+            };
         let driver::ExpansionResult { defs, mut hir_forest, .. } = {
             phase_2_configure_and_expand(
                 &sess,
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index f1345d0e6f1..ea943e16a46 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -44,8 +44,6 @@ use ast::AttrId;
 // way towards a non-panic!-prone parser. It should be used for fatal parsing
 // errors; eventually we plan to convert all code using panictry to just use
 // normal try.
-// Exported for syntax_ext, not meant for general use.
-#[macro_export]
 macro_rules! panictry {
     ($e:expr) => ({
         use std::result::Result::{Ok, Err};
diff --git a/src/libsyntax_ext/asm.rs b/src/libsyntax_ext/asm.rs
index a8f3c40db60..41ee6e91b3d 100644
--- a/src/libsyntax_ext/asm.rs
+++ b/src/libsyntax_ext/asm.rs
@@ -4,6 +4,7 @@ use self::State::*;
 
 use rustc_data_structures::thin_vec::ThinVec;
 
+use errors::DiagnosticBuilder;
 use syntax::ast;
 use syntax::ext::base;
 use syntax::ext::base::*;
@@ -51,6 +52,34 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
                                        feature_gate::EXPLAIN_ASM);
     }
 
+    let mut inline_asm = match parse_inline_asm(cx, sp, tts) {
+        Ok(Some(inline_asm)) => inline_asm,
+        Ok(None) => return DummyResult::expr(sp),
+        Err(mut err) => {
+            err.emit();
+            return DummyResult::expr(sp);
+        }
+    };
+
+    // If there are no outputs, the inline assembly is executed just for its side effects,
+    // so ensure that it is volatile
+    if inline_asm.outputs.is_empty() {
+        inline_asm.volatile = true;
+    }
+
+    MacEager::expr(P(ast::Expr {
+        id: ast::DUMMY_NODE_ID,
+        node: ast::ExprKind::InlineAsm(P(inline_asm)),
+        span: sp,
+        attrs: ThinVec::new(),
+    }))
+}
+
+fn parse_inline_asm<'a>(
+    cx: &mut ExtCtxt<'a>,
+    sp: Span,
+    tts: &[tokenstream::TokenTree],
+) -> Result<Option<ast::InlineAsm>, DiagnosticBuilder<'a>> {
     // Split the tts before the first colon, to avoid `asm!("x": y)`  being
     // parsed as `asm!(z)` with `z = "x": y` which is type ascription.
     let first_colon = tts.iter()
@@ -80,22 +109,33 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
                 if asm_str_style.is_some() {
                     // If we already have a string with instructions,
                     // ending up in Asm state again is an error.
-                    span_err!(cx, sp, E0660, "malformed inline assembly");
-                    return DummyResult::expr(sp);
+                    return Err(struct_span_err!(
+                        cx.parse_sess.span_diagnostic,
+                        sp,
+                        E0660,
+                        "malformed inline assembly"
+                    ));
                 }
                 // Nested parser, stop before the first colon (see above).
                 let mut p2 = cx.new_parser_from_tts(&tts[..first_colon]);
-                let (s, style) = match expr_to_string(cx,
-                                                      panictry!(p2.parse_expr()),
-                                                      "inline assembly must be a string literal") {
-                    Some((s, st)) => (s, st),
-                    // let compilation continue
-                    None => return DummyResult::expr(sp),
-                };
+
+                if p2.token == token::Eof {
+                    let mut err =
+                        cx.struct_span_err(sp, "macro requires a string literal as an argument");
+                    err.span_label(sp, "string literal required");
+                    return Err(err);
+                }
+
+                let expr = p2.parse_expr()?;
+                let (s, style) =
+                    match expr_to_string(cx, expr, "inline assembly must be a string literal") {
+                        Some((s, st)) => (s, st),
+                        None => return Ok(None),
+                    };
 
                 // This is most likely malformed.
                 if p2.token != token::Eof {
-                    let mut extra_tts = panictry!(p2.parse_all_token_trees());
+                    let mut extra_tts = p2.parse_all_token_trees()?;
                     extra_tts.extend(tts[first_colon..].iter().cloned());
                     p = parse::stream_to_parser(cx.parse_sess, extra_tts.into_iter().collect());
                 }
@@ -105,18 +145,17 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
             }
             Outputs => {
                 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
-
                     if !outputs.is_empty() {
                         p.eat(&token::Comma);
                     }
 
-                    let (constraint, _str_style) = panictry!(p.parse_str());
+                    let (constraint, _) = p.parse_str()?;
 
                     let span = p.prev_span;
 
-                    panictry!(p.expect(&token::OpenDelim(token::Paren)));
-                    let out = panictry!(p.parse_expr());
-                    panictry!(p.expect(&token::CloseDelim(token::Paren)));
+                    p.expect(&token::OpenDelim(token::Paren))?;
+                    let expr = p.parse_expr()?;
+                    p.expect(&token::CloseDelim(token::Paren))?;
 
                     // Expands a read+write operand into two operands.
                     //
@@ -143,7 +182,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
                     let is_indirect = constraint_str.contains("*");
                     outputs.push(ast::InlineAsmOutput {
                         constraint: output.unwrap_or(constraint),
-                        expr: out,
+                        expr,
                         is_rw,
                         is_indirect,
                     });
@@ -151,12 +190,11 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
             }
             Inputs => {
                 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
-
                     if !inputs.is_empty() {
                         p.eat(&token::Comma);
                     }
 
-                    let (constraint, _str_style) = panictry!(p.parse_str());
+                    let (constraint, _) = p.parse_str()?;
 
                     if constraint.as_str().starts_with("=") {
                         span_err!(cx, p.prev_span, E0662,
@@ -166,21 +204,20 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
                                                 "input operand constraint contains '+'");
                     }
 
-                    panictry!(p.expect(&token::OpenDelim(token::Paren)));
-                    let input = panictry!(p.parse_expr());
-                    panictry!(p.expect(&token::CloseDelim(token::Paren)));
+                    p.expect(&token::OpenDelim(token::Paren))?;
+                    let input = p.parse_expr()?;
+                    p.expect(&token::CloseDelim(token::Paren))?;
 
                     inputs.push((constraint, input));
                 }
             }
             Clobbers => {
                 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
-
                     if !clobs.is_empty() {
                         p.eat(&token::Comma);
                     }
 
-                    let (s, _str_style) = panictry!(p.parse_str());
+                    let (s, _) = p.parse_str()?;
 
                     if OPTIONS.iter().any(|&opt| s == opt) {
                         cx.span_warn(p.prev_span, "expected a clobber, found an option");
@@ -193,7 +230,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
                 }
             }
             Options => {
-                let (option, _str_style) = panictry!(p.parse_str());
+                let (option, _) = p.parse_str()?;
 
                 if option == "volatile" {
                     // Indicates that the inline assembly has side effects
@@ -234,26 +271,15 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
         }
     }
 
-    // If there are no outputs, the inline assembly is executed just for its side effects,
-    // so ensure that it is volatile
-    if outputs.is_empty() {
-        volatile = true;
-    }
-
-    MacEager::expr(P(ast::Expr {
-        id: ast::DUMMY_NODE_ID,
-        node: ast::ExprKind::InlineAsm(P(ast::InlineAsm {
-            asm,
-            asm_str_style: asm_str_style.unwrap(),
-            outputs,
-            inputs,
-            clobbers: clobs,
-            volatile,
-            alignstack,
-            dialect,
-            ctxt: cx.backtrace(),
-        })),
-        span: sp,
-        attrs: ThinVec::new(),
+    Ok(Some(ast::InlineAsm {
+        asm,
+        asm_str_style: asm_str_style.unwrap(),
+        outputs,
+        inputs,
+        clobbers: clobs,
+        volatile,
+        alignstack,
+        dialect,
+        ctxt: cx.backtrace(),
     }))
 }
diff --git a/src/libsyntax_ext/assert.rs b/src/libsyntax_ext/assert.rs
index 2f5743e5e9b..b27f495322a 100644
--- a/src/libsyntax_ext/assert.rs
+++ b/src/libsyntax_ext/assert.rs
@@ -1,9 +1,11 @@
-use syntax::ast::*;
+use errors::DiagnosticBuilder;
+use syntax::ast::{self, *};
 use syntax::source_map::Spanned;
 use syntax::ext::base::*;
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token;
 use syntax::print::pprust;
+use syntax::ptr::P;
 use syntax::symbol::Symbol;
 use syntax::tokenstream::{TokenStream, TokenTree};
 use syntax_pos::{Span, DUMMY_SP};
@@ -13,33 +15,18 @@ pub fn expand_assert<'cx>(
     sp: Span,
     tts: &[TokenTree],
 ) -> Box<dyn MacResult + 'cx> {
-    let mut parser = cx.new_parser_from_tts(tts);
-
-    if parser.token == token::Eof {
-        cx.struct_span_err(sp, "macro requires a boolean expression as an argument")
-            .span_label(sp, "boolean expression required")
-            .emit();
-        return DummyResult::expr(sp);
-    }
-
-    let cond_expr = panictry!(parser.parse_expr());
-    let custom_msg_args = if parser.eat(&token::Comma) {
-        let ts = parser.parse_tokens();
-        if !ts.is_empty() {
-            Some(ts)
-        } else {
-            None
+    let Assert { cond_expr, custom_message } = match parse_assert(cx, sp, tts) {
+        Ok(assert) => assert,
+        Err(mut err) => {
+            err.emit();
+            return DummyResult::expr(sp);
         }
-    } else {
-        None
     };
 
     let sp = sp.apply_mark(cx.current_expansion.mark);
     let panic_call = Mac_ {
         path: Path::from_ident(Ident::new(Symbol::intern("panic"), sp)),
-        tts: if let Some(ts) = custom_msg_args {
-            ts.into()
-        } else {
+        tts: custom_message.unwrap_or_else(|| {
             TokenStream::from(TokenTree::Token(
                 DUMMY_SP,
                 token::Literal(
@@ -49,8 +36,8 @@ pub fn expand_assert<'cx>(
                     ))),
                     None,
                 ),
-            )).into()
-        },
+            ))
+        }).into(),
         delim: MacDelimiter::Parenthesis,
     };
     let if_expr = cx.expr_if(
@@ -67,3 +54,36 @@ pub fn expand_assert<'cx>(
     );
     MacEager::expr(if_expr)
 }
+
+struct Assert {
+    cond_expr: P<ast::Expr>,
+    custom_message: Option<TokenStream>,
+}
+
+fn parse_assert<'a>(
+    cx: &mut ExtCtxt<'a>,
+    sp: Span,
+    tts: &[TokenTree]
+) -> Result<Assert, DiagnosticBuilder<'a>> {
+    let mut parser = cx.new_parser_from_tts(tts);
+
+    if parser.token == token::Eof {
+        let mut err = cx.struct_span_err(sp, "macro requires a boolean expression as an argument");
+        err.span_label(sp, "boolean expression required");
+        return Err(err);
+    }
+
+    Ok(Assert {
+        cond_expr: parser.parse_expr()?,
+        custom_message: if parser.eat(&token::Comma) {
+            let ts = parser.parse_tokens();
+            if !ts.is_empty() {
+                Some(ts)
+            } else {
+                None
+            }
+        } else {
+            None
+        },
+    })
+}
diff --git a/src/libsyntax_ext/cfg.rs b/src/libsyntax_ext/cfg.rs
index 7e3c1bbddf5..3b47b03cbe8 100644
--- a/src/libsyntax_ext/cfg.rs
+++ b/src/libsyntax_ext/cfg.rs
@@ -2,6 +2,8 @@
 /// a literal `true` or `false` based on whether the given cfg matches the
 /// current compilation environment.
 
+use errors::DiagnosticBuilder;
+use syntax::ast;
 use syntax::ext::base::*;
 use syntax::ext::base;
 use syntax::ext::build::AstBuilder;
@@ -15,16 +17,39 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
                        tts: &[tokenstream::TokenTree])
                        -> Box<dyn base::MacResult + 'static> {
     let sp = sp.apply_mark(cx.current_expansion.mark);
+
+    match parse_cfg(cx, sp, tts) {
+        Ok(cfg) => {
+            let matches_cfg = attr::cfg_matches(&cfg, cx.parse_sess, cx.ecfg.features);
+            MacEager::expr(cx.expr_bool(sp, matches_cfg))
+        }
+        Err(mut err) => {
+            err.emit();
+            DummyResult::expr(sp)
+        }
+    }
+}
+
+fn parse_cfg<'a>(
+    cx: &mut ExtCtxt<'a>,
+    sp: Span,
+    tts: &[tokenstream::TokenTree],
+) -> Result<ast::MetaItem, DiagnosticBuilder<'a>> {
     let mut p = cx.new_parser_from_tts(tts);
-    let cfg = panictry!(p.parse_meta_item());
+
+    if p.token == token::Eof {
+        let mut err = cx.struct_span_err(sp, "macro requires a cfg-pattern as an argument");
+        err.span_label(sp, "cfg-pattern required");
+        return Err(err);
+    }
+
+    let cfg = p.parse_meta_item()?;
 
     let _ = p.eat(&token::Comma);
 
     if !p.eat(&token::Eof) {
-        cx.span_err(sp, "expected 1 cfg-pattern");
-        return DummyResult::expr(sp);
+        return Err(cx.struct_span_err(sp, "expected 1 cfg-pattern"));
     }
 
-    let matches_cfg = attr::cfg_matches(&cfg, cx.parse_sess, cx.ecfg.features);
-    MacEager::expr(cx.expr_bool(sp, matches_cfg))
+    Ok(cfg)
 }
diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs
index c11f27f3ed5..61722ba5516 100644
--- a/src/libsyntax_ext/format.rs
+++ b/src/libsyntax_ext/format.rs
@@ -3,6 +3,7 @@ use self::Position::*;
 
 use fmt_macros as parse;
 
+use errors::DiagnosticBuilder;
 use syntax::ast;
 use syntax::ext::base::{self, *};
 use syntax::ext::build::AstBuilder;
@@ -112,7 +113,7 @@ struct Context<'a, 'b: 'a> {
     is_literal: bool,
 }
 
-/// Parses the arguments from the given list of tokens, returning None
+/// Parses the arguments from the given list of tokens, returning the diagnostic
 /// if there's a parse error so we can continue parsing other format!
 /// expressions.
 ///
@@ -121,27 +122,26 @@ struct Context<'a, 'b: 'a> {
 /// ```text
 /// Some((fmtstr, parsed arguments, index map for named arguments))
 /// ```
-fn parse_args(ecx: &mut ExtCtxt,
-              sp: Span,
-              tts: &[tokenstream::TokenTree])
-              -> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<String, usize>)> {
+fn parse_args<'a>(
+    ecx: &mut ExtCtxt<'a>,
+    sp: Span,
+    tts: &[tokenstream::TokenTree]
+) -> Result<(P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<String, usize>), DiagnosticBuilder<'a>> {
     let mut args = Vec::<P<ast::Expr>>::new();
     let mut names = FxHashMap::<String, usize>::default();
 
     let mut p = ecx.new_parser_from_tts(tts);
 
     if p.token == token::Eof {
-        ecx.span_err(sp, "requires at least a format string argument");
-        return None;
+        return Err(ecx.struct_span_err(sp, "requires at least a format string argument"));
     }
 
-    let fmtstr = panictry!(p.parse_expr());
+    let fmtstr = p.parse_expr()?;
     let mut named = false;
 
     while p.token != token::Eof {
         if !p.eat(&token::Comma) {
-            ecx.span_err(p.span, "expected token: `,`");
-            return None;
+            return Err(ecx.struct_span_err(p.span, "expected token: `,`"));
         }
         if p.token == token::Eof {
             break;
@@ -152,16 +152,15 @@ fn parse_args(ecx: &mut ExtCtxt,
                 p.bump();
                 i
             } else {
-                ecx.span_err(
+                return Err(ecx.struct_span_err(
                     p.span,
                     "expected ident, positional arguments cannot follow named arguments",
-                );
-                return None;
+                ));
             };
             let name: &str = &ident.as_str();
 
-            panictry!(p.expect(&token::Eq));
-            let e = panictry!(p.parse_expr());
+            p.expect(&token::Eq).unwrap();
+            let e = p.parse_expr()?;
             if let Some(prev) = names.get(name) {
                 ecx.struct_span_err(e.span, &format!("duplicate argument named `{}`", name))
                     .span_note(args[*prev].span, "previously here")
@@ -177,10 +176,11 @@ fn parse_args(ecx: &mut ExtCtxt,
             names.insert(name.to_string(), slot);
             args.push(e);
         } else {
-            args.push(panictry!(p.parse_expr()));
+            let e = p.parse_expr()?;
+            args.push(e);
         }
     }
-    Some((fmtstr, args, names))
+    Ok((fmtstr, args, names))
 }
 
 impl<'a, 'b> Context<'a, 'b> {
@@ -689,10 +689,13 @@ pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt,
                                -> Box<dyn base::MacResult + 'cx> {
     sp = sp.apply_mark(ecx.current_expansion.mark);
     match parse_args(ecx, sp, tts) {
-        Some((efmt, args, names)) => {
+        Ok((efmt, args, names)) => {
             MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt, args, names, false))
         }
-        None => DummyResult::expr(sp),
+        Err(mut err) => {
+            err.emit();
+            DummyResult::expr(sp)
+        }
     }
 }
 
@@ -716,10 +719,13 @@ pub fn expand_format_args_nl<'cx>(
     }
     sp = sp.apply_mark(ecx.current_expansion.mark);
     match parse_args(ecx, sp, tts) {
-        Some((efmt, args, names)) => {
+        Ok((efmt, args, names)) => {
             MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt, args, names, true))
         }
-        None => DummyResult::expr(sp),
+        Err(mut err) => {
+            err.emit();
+            DummyResult::expr(sp)
+        }
     }
 }
 
diff --git a/src/libsyntax_ext/global_asm.rs b/src/libsyntax_ext/global_asm.rs
index a58c267ab4f..0a12e27c4fc 100644
--- a/src/libsyntax_ext/global_asm.rs
+++ b/src/libsyntax_ext/global_asm.rs
@@ -8,11 +8,13 @@
 /// LLVM's `module asm "some assembly here"`. All of LLVM's caveats
 /// therefore apply.
 
+use errors::DiagnosticBuilder;
 use syntax::ast;
 use syntax::source_map::respan;
 use syntax::ext::base;
 use syntax::ext::base::*;
 use syntax::feature_gate;
+use syntax::parse::token;
 use syntax::ptr::P;
 use syntax::symbol::Symbol;
 use syntax_pos::Span;
@@ -31,24 +33,47 @@ pub fn expand_global_asm<'cx>(cx: &'cx mut ExtCtxt,
                                        feature_gate::EXPLAIN_GLOBAL_ASM);
     }
 
+    match parse_global_asm(cx, sp, tts) {
+        Ok(Some(global_asm)) => {
+            MacEager::items(smallvec![P(ast::Item {
+                ident: ast::Ident::with_empty_ctxt(Symbol::intern("")),
+                attrs: Vec::new(),
+                id: ast::DUMMY_NODE_ID,
+                node: ast::ItemKind::GlobalAsm(P(global_asm)),
+                vis: respan(sp.shrink_to_lo(), ast::VisibilityKind::Inherited),
+                span: sp,
+                tokens: None,
+            })])
+        }
+        Ok(None) => DummyResult::any(sp),
+        Err(mut err) => {
+            err.emit();
+            DummyResult::any(sp)
+        }
+    }
+}
+
+fn parse_global_asm<'a>(
+    cx: &mut ExtCtxt<'a>,
+    sp: Span,
+    tts: &[tokenstream::TokenTree]
+) -> Result<Option<ast::GlobalAsm>, DiagnosticBuilder<'a>> {
     let mut p = cx.new_parser_from_tts(tts);
-    let (asm, _) = match expr_to_string(cx,
-                                        panictry!(p.parse_expr()),
-                                        "inline assembly must be a string literal") {
+
+    if p.token == token::Eof {
+        let mut err = cx.struct_span_err(sp, "macro requires a string literal as an argument");
+        err.span_label(sp, "string literal required");
+        return Err(err);
+    }
+
+    let expr = p.parse_expr()?;
+    let (asm, _) = match expr_to_string(cx, expr, "inline assembly must be a string literal") {
         Some((s, st)) => (s, st),
-        None => return DummyResult::any(sp),
+        None => return Ok(None),
     };
 
-    MacEager::items(smallvec![P(ast::Item {
-        ident: ast::Ident::with_empty_ctxt(Symbol::intern("")),
-        attrs: Vec::new(),
-        id: ast::DUMMY_NODE_ID,
-        node: ast::ItemKind::GlobalAsm(P(ast::GlobalAsm {
-            asm,
-            ctxt: cx.backtrace(),
-        })),
-        vis: respan(sp.shrink_to_lo(), ast::VisibilityKind::Inherited),
-        span: sp,
-        tokens: None,
-    })])
+    Ok(Some(ast::GlobalAsm {
+        asm,
+        ctxt: cx.backtrace(),
+    }))
 }
diff --git a/src/test/rustdoc-ui/failed-doctest-output.stdout b/src/test/rustdoc-ui/failed-doctest-output.stdout
index 54830dceb8f..9fd46e94f03 100644
--- a/src/test/rustdoc-ui/failed-doctest-output.stdout
+++ b/src/test/rustdoc-ui/failed-doctest-output.stdout
@@ -12,7 +12,7 @@ error[E0425]: cannot find value `no` in this scope
 3 | no
   | ^^ not found in this scope
 
-thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:316:13
+thread '$DIR/failed-doctest-output.rs - OtherStruct (line 17)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:321:13
 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
 
 ---- $DIR/failed-doctest-output.rs - SomeStruct (line 11) stdout ----
@@ -21,7 +21,7 @@ thread '$DIR/failed-doctest-output.rs - SomeStruct (line 11)' panicked at 'test
 thread 'main' panicked at 'oh no', $DIR/failed-doctest-output.rs:3:1
 note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
 
-', src/librustdoc/test.rs:351:17
+', src/librustdoc/test.rs:356:17
 
 
 failures:
diff --git a/src/test/ui/asm/asm-parse-errors.rs b/src/test/ui/asm/asm-parse-errors.rs
new file mode 100644
index 00000000000..e712ac5826e
--- /dev/null
+++ b/src/test/ui/asm/asm-parse-errors.rs
@@ -0,0 +1,15 @@
+#![feature(asm)]
+
+fn main() {
+    asm!(); //~ ERROR requires a string literal as an argument
+    asm!("nop" : struct); //~ ERROR expected string literal
+    asm!("mov %eax, $$0x2" : struct); //~ ERROR expected string literal
+    asm!("mov %eax, $$0x2" : "={eax}" struct); //~ ERROR expected `(`
+    asm!("mov %eax, $$0x2" : "={eax}"(struct)); //~ ERROR expected expression
+    asm!("in %dx, %al" : "={al}"(result) : struct); //~ ERROR expected string literal
+    asm!("in %dx, %al" : "={al}"(result) : "{dx}" struct); //~ ERROR expected `(`
+    asm!("in %dx, %al" : "={al}"(result) : "{dx}"(struct)); //~ ERROR expected expression
+    asm!("mov $$0x200, %eax" : : : struct); //~ ERROR expected string literal
+    asm!("mov eax, 2" : "={eax}"(foo) : : : struct); //~ ERROR expected string literal
+    asm!(123); //~ ERROR inline assembly must be a string literal
+}
diff --git a/src/test/ui/asm/asm-parse-errors.stderr b/src/test/ui/asm/asm-parse-errors.stderr
new file mode 100644
index 00000000000..37b61b80b2b
--- /dev/null
+++ b/src/test/ui/asm/asm-parse-errors.stderr
@@ -0,0 +1,68 @@
+error: macro requires a string literal as an argument
+  --> $DIR/asm-parse-errors.rs:4:5
+   |
+LL |     asm!(); //~ ERROR requires a string literal as an argument
+   |     ^^^^^^^ string literal required
+
+error: expected string literal
+  --> $DIR/asm-parse-errors.rs:5:18
+   |
+LL |     asm!("nop" : struct); //~ ERROR expected string literal
+   |                  ^^^^^^ expected string literal
+
+error: expected string literal
+  --> $DIR/asm-parse-errors.rs:6:30
+   |
+LL |     asm!("mov %eax, $$0x2" : struct); //~ ERROR expected string literal
+   |                              ^^^^^^ expected string literal
+
+error: expected `(`, found keyword `struct`
+  --> $DIR/asm-parse-errors.rs:7:39
+   |
+LL |     asm!("mov %eax, $$0x2" : "={eax}" struct); //~ ERROR expected `(`
+   |                                       ^^^^^^ expected `(`
+
+error: expected expression, found keyword `struct`
+  --> $DIR/asm-parse-errors.rs:8:39
+   |
+LL |     asm!("mov %eax, $$0x2" : "={eax}"(struct)); //~ ERROR expected expression
+   |                                       ^^^^^^ expected expression
+
+error: expected string literal
+  --> $DIR/asm-parse-errors.rs:9:44
+   |
+LL |     asm!("in %dx, %al" : "={al}"(result) : struct); //~ ERROR expected string literal
+   |                                            ^^^^^^ expected string literal
+
+error: expected `(`, found keyword `struct`
+  --> $DIR/asm-parse-errors.rs:10:51
+   |
+LL |     asm!("in %dx, %al" : "={al}"(result) : "{dx}" struct); //~ ERROR expected `(`
+   |                                                   ^^^^^^ expected `(`
+
+error: expected expression, found keyword `struct`
+  --> $DIR/asm-parse-errors.rs:11:51
+   |
+LL |     asm!("in %dx, %al" : "={al}"(result) : "{dx}"(struct)); //~ ERROR expected expression
+   |                                                   ^^^^^^ expected expression
+
+error: expected string literal
+  --> $DIR/asm-parse-errors.rs:12:36
+   |
+LL |     asm!("mov $$0x200, %eax" : : : struct); //~ ERROR expected string literal
+   |                                    ^^^^^^ expected string literal
+
+error: expected string literal
+  --> $DIR/asm-parse-errors.rs:13:45
+   |
+LL |     asm!("mov eax, 2" : "={eax}"(foo) : : : struct); //~ ERROR expected string literal
+   |                                             ^^^^^^ expected string literal
+
+error: inline assembly must be a string literal
+  --> $DIR/asm-parse-errors.rs:14:10
+   |
+LL |     asm!(123); //~ ERROR inline assembly must be a string literal
+   |          ^^^
+
+error: aborting due to 11 previous errors
+
diff --git a/src/test/ui/issues/issue-22644.rs b/src/test/ui/issues/issue-22644.rs
index c87a927e550..9244ff5931d 100644
--- a/src/test/ui/issues/issue-22644.rs
+++ b/src/test/ui/issues/issue-22644.rs
@@ -1,5 +1,7 @@
+#![feature(type_ascription)]
+
 fn main() {
-    let a : u32 = 0;
+    let a : usize = 0;
     let long_name : usize = 0;
 
     println!("{}", a as usize > long_name);
diff --git a/src/test/ui/issues/issue-22644.stderr b/src/test/ui/issues/issue-22644.stderr
index ea5c9c5ad23..de97b2271b1 100644
--- a/src/test/ui/issues/issue-22644.stderr
+++ b/src/test/ui/issues/issue-22644.stderr
@@ -1,5 +1,5 @@
 error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
-  --> $DIR/issue-22644.rs:6:31
+  --> $DIR/issue-22644.rs:8:31
    |
 LL |     println!("{}", a as usize < long_name); //~ ERROR `<` is interpreted as a start of generic
    |                    ---------- ^ --------- interpreted as generic arguments
@@ -8,7 +8,7 @@ LL |     println!("{}", a as usize < long_name); //~ ERROR `<` is interpreted as
    |                    help: try comparing the cast value: `(a as usize)`
 
 error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
-  --> $DIR/issue-22644.rs:7:33
+  --> $DIR/issue-22644.rs:9:33
    |
 LL |     println!("{}{}", a as usize < long_name, long_name);
    |                      ---------- ^ -------------------- interpreted as generic arguments
@@ -17,7 +17,7 @@ LL |     println!("{}{}", a as usize < long_name, long_name);
    |                      help: try comparing the cast value: `(a as usize)`
 
 error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
-  --> $DIR/issue-22644.rs:9:31
+  --> $DIR/issue-22644.rs:11:31
    |
 LL |     println!("{}", a as usize < 4); //~ ERROR `<` is interpreted as a start of generic
    |                    ---------- ^ - interpreted as generic arguments
@@ -26,7 +26,7 @@ LL |     println!("{}", a as usize < 4); //~ ERROR `<` is interpreted as a start
    |                    help: try comparing the cast value: `(a as usize)`
 
 error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
-  --> $DIR/issue-22644.rs:11:31
+  --> $DIR/issue-22644.rs:13:31
    |
 LL |     println!("{}{}", a: usize < long_name, long_name);
    |                      -------- ^ -------------------- interpreted as generic arguments
@@ -35,7 +35,7 @@ LL |     println!("{}{}", a: usize < long_name, long_name);
    |                      help: try comparing the cast value: `(a: usize)`
 
 error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
-  --> $DIR/issue-22644.rs:13:29
+  --> $DIR/issue-22644.rs:15:29
    |
 LL |     println!("{}", a: usize < 4); //~ ERROR `<` is interpreted as a start of generic
    |                    -------- ^ - interpreted as generic arguments
@@ -44,7 +44,7 @@ LL |     println!("{}", a: usize < 4); //~ ERROR `<` is interpreted as a start o
    |                    help: try comparing the cast value: `(a: usize)`
 
 error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
-  --> $DIR/issue-22644.rs:18:20
+  --> $DIR/issue-22644.rs:20:20
    |
 LL |                    < //~ ERROR `<` is interpreted as a start of generic
    |                    ^ not interpreted as comparison
@@ -58,7 +58,7 @@ LL |                    usize)
    |
 
 error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
-  --> $DIR/issue-22644.rs:27:20
+  --> $DIR/issue-22644.rs:29:20
    |
 LL |                    < //~ ERROR `<` is interpreted as a start of generic
    |                    ^ not interpreted as comparison
@@ -75,7 +75,7 @@ LL |
  ...
 
 error: `<` is interpreted as a start of generic arguments for `usize`, not a shift
-  --> $DIR/issue-22644.rs:30:31
+  --> $DIR/issue-22644.rs:32:31
    |
 LL |     println!("{}", a as usize << long_name); //~ ERROR `<` is interpreted as a start of generic
    |                    ---------- ^^ --------- interpreted as generic arguments
@@ -84,7 +84,7 @@ LL |     println!("{}", a as usize << long_name); //~ ERROR `<` is interpreted a
    |                    help: try shifting the cast value: `(a as usize)`
 
 error: expected type, found `4`
-  --> $DIR/issue-22644.rs:32:28
+  --> $DIR/issue-22644.rs:34:28
    |
 LL |     println!("{}", a: &mut 4); //~ ERROR expected type, found `4`
    |                            ^ expecting a type here because of type ascription
diff --git a/src/test/ui/macros/assert.rs b/src/test/ui/macros/assert.rs
index 8732cb58d74..71b0dbb19e2 100644
--- a/src/test/ui/macros/assert.rs
+++ b/src/test/ui/macros/assert.rs
@@ -1,4 +1,6 @@
 fn main() {
     assert!();  //~ ERROR requires a boolean expression
+    assert!(struct); //~ ERROR expected expression
     debug_assert!(); //~ ERROR requires a boolean expression
+    debug_assert!(struct); //~ ERROR expected expression
 }
diff --git a/src/test/ui/macros/assert.stderr b/src/test/ui/macros/assert.stderr
index 89faba0cf63..2cfcebabcb9 100644
--- a/src/test/ui/macros/assert.stderr
+++ b/src/test/ui/macros/assert.stderr
@@ -4,13 +4,25 @@ error: macro requires a boolean expression as an argument
 LL |     assert!();  //~ ERROR requires a boolean expression
    |     ^^^^^^^^^^ boolean expression required
 
+error: expected expression, found keyword `struct`
+  --> $DIR/assert.rs:3:13
+   |
+LL |     assert!(struct); //~ ERROR expected expression
+   |             ^^^^^^ expected expression
+
 error: macro requires a boolean expression as an argument
-  --> $DIR/assert.rs:3:5
+  --> $DIR/assert.rs:4:5
    |
 LL |     debug_assert!(); //~ ERROR requires a boolean expression
    |     ^^^^^^^^^^^^^^^^ boolean expression required
    |
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
 
-error: aborting due to 2 previous errors
+error: expected expression, found keyword `struct`
+  --> $DIR/assert.rs:5:19
+   |
+LL |     debug_assert!(struct); //~ ERROR expected expression
+   |                   ^^^^^^ expected expression
+
+error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/macros/cfg.rs b/src/test/ui/macros/cfg.rs
new file mode 100644
index 00000000000..222161a8183
--- /dev/null
+++ b/src/test/ui/macros/cfg.rs
@@ -0,0 +1,5 @@
+fn main() {
+    cfg!(); //~ ERROR macro requires a cfg-pattern
+    cfg!(123); //~ ERROR expected identifier
+    cfg!(foo = 123); //~ ERROR literal in `cfg` predicate value must be a string
+}
diff --git a/src/test/ui/macros/cfg.stderr b/src/test/ui/macros/cfg.stderr
new file mode 100644
index 00000000000..a7aca88f3e5
--- /dev/null
+++ b/src/test/ui/macros/cfg.stderr
@@ -0,0 +1,21 @@
+error: macro requires a cfg-pattern as an argument
+  --> $DIR/cfg.rs:2:5
+   |
+LL |     cfg!(); //~ ERROR macro requires a cfg-pattern
+   |     ^^^^^^^ cfg-pattern required
+
+error: expected identifier, found `123`
+  --> $DIR/cfg.rs:3:10
+   |
+LL |     cfg!(123); //~ ERROR expected identifier
+   |          ^^^ expected identifier
+
+error[E0565]: literal in `cfg` predicate value must be a string
+  --> $DIR/cfg.rs:4:16
+   |
+LL |     cfg!(foo = 123); //~ ERROR literal in `cfg` predicate value must be a string
+   |                ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0565`.
diff --git a/src/test/ui/macros/format-parse-errors.rs b/src/test/ui/macros/format-parse-errors.rs
new file mode 100644
index 00000000000..ba1e441fe33
--- /dev/null
+++ b/src/test/ui/macros/format-parse-errors.rs
@@ -0,0 +1,10 @@
+fn main() {
+    format!(); //~ ERROR requires at least a format string argument
+    format!(struct); //~ ERROR expected expression
+    format!("s", name =); //~ ERROR expected expression
+    format!("s", foo = struct); //~ ERROR expected expression
+    format!("s", struct); //~ ERROR expected expression
+
+    // This error should come after parsing errors to ensure they are non-fatal.
+    format!(123); //~ ERROR format argument must be a string literal
+}
diff --git a/src/test/ui/macros/format-parse-errors.stderr b/src/test/ui/macros/format-parse-errors.stderr
new file mode 100644
index 00000000000..0463c548901
--- /dev/null
+++ b/src/test/ui/macros/format-parse-errors.stderr
@@ -0,0 +1,44 @@
+error: requires at least a format string argument
+  --> $DIR/format-parse-errors.rs:2:5
+   |
+LL |     format!(); //~ ERROR requires at least a format string argument
+   |     ^^^^^^^^^^
+   |
+   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+error: expected expression, found keyword `struct`
+  --> $DIR/format-parse-errors.rs:3:13
+   |
+LL |     format!(struct); //~ ERROR expected expression
+   |             ^^^^^^ expected expression
+
+error: expected expression, found `<eof>`
+  --> $DIR/format-parse-errors.rs:4:23
+   |
+LL |     format!("s", name =); //~ ERROR expected expression
+   |                       ^ expected expression
+
+error: expected expression, found keyword `struct`
+  --> $DIR/format-parse-errors.rs:5:24
+   |
+LL |     format!("s", foo = struct); //~ ERROR expected expression
+   |                        ^^^^^^ expected expression
+
+error: expected expression, found keyword `struct`
+  --> $DIR/format-parse-errors.rs:6:18
+   |
+LL |     format!("s", struct); //~ ERROR expected expression
+   |                  ^^^^^^ expected expression
+
+error: format argument must be a string literal
+  --> $DIR/format-parse-errors.rs:9:13
+   |
+LL |     format!(123); //~ ERROR format argument must be a string literal
+   |             ^^^
+help: you might be missing a string literal to format with
+   |
+LL |     format!("{}", 123); //~ ERROR format argument must be a string literal
+   |             ^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/test/ui/macros/global-asm.rs b/src/test/ui/macros/global-asm.rs
new file mode 100644
index 00000000000..8402afa5085
--- /dev/null
+++ b/src/test/ui/macros/global-asm.rs
@@ -0,0 +1,7 @@
+#![feature(global_asm)]
+
+fn main() {
+    global_asm!();  //~ ERROR requires a string literal as an argument
+    global_asm!(struct); //~ ERROR expected expression
+    global_asm!(123); //~ ERROR inline assembly must be a string literal
+}
diff --git a/src/test/ui/macros/global-asm.stderr b/src/test/ui/macros/global-asm.stderr
new file mode 100644
index 00000000000..94664c96db3
--- /dev/null
+++ b/src/test/ui/macros/global-asm.stderr
@@ -0,0 +1,20 @@
+error: macro requires a string literal as an argument
+  --> $DIR/global-asm.rs:4:5
+   |
+LL |     global_asm!();  //~ ERROR requires a string literal as an argument
+   |     ^^^^^^^^^^^^^^ string literal required
+
+error: expected expression, found keyword `struct`
+  --> $DIR/global-asm.rs:5:17
+   |
+LL |     global_asm!(struct); //~ ERROR expected expression
+   |                 ^^^^^^ expected expression
+
+error: inline assembly must be a string literal
+  --> $DIR/global-asm.rs:6:17
+   |
+LL |     global_asm!(123); //~ ERROR inline assembly must be a string literal
+   |                 ^^^
+
+error: aborting due to 3 previous errors
+