From 0bdfd0f4c76fa29a4be774937bc72165390b06d6 Mon Sep 17 00:00:00 2001 From: Douglas Young Date: Tue, 18 Feb 2014 16:14:12 +0000 Subject: [PATCH] Avoid returning original macro if expansion fails. Closes #11692. Instead of returning the original expression, a dummy expression (with identical span) is returned. This prevents infinite loops of failed expansions as well as odd double error messages in certain situations. --- src/libsyntax/ext/asm.rs | 2 +- src/libsyntax/ext/base.rs | 14 ++++++++++---- src/libsyntax/ext/bytes.rs | 2 +- src/libsyntax/ext/concat.rs | 2 +- src/libsyntax/ext/concat_idents.rs | 4 ++-- src/libsyntax/ext/env.rs | 12 ++++++------ src/libsyntax/ext/expand.rs | 8 ++++---- src/libsyntax/ext/format.rs | 4 ++-- src/libsyntax/ext/source_util.rs | 12 ++++++------ src/libsyntax/ext/trace_macros.rs | 2 +- src/test/compile-fail/issue-11692.rs | 19 +++++++++++++++++++ 11 files changed, 53 insertions(+), 28 deletions(-) create mode 100644 src/test/compile-fail/issue-11692.rs diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs index 1a3ebf3ce5d1c..1bf82573c4949 100644 --- a/src/libsyntax/ext/asm.rs +++ b/src/libsyntax/ext/asm.rs @@ -64,7 +64,7 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) "inline assembly must be a string literal.") { Some((s, st)) => (s, st), // let compilation continue - None => return MacResult::dummy_expr(), + None => return MacResult::dummy_expr(sp), }; asm = s; asm_str_style = Some(style); diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 3dac82ae3b77f..e6fffe8b53f48 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -101,6 +101,7 @@ pub trait AnyMacro { fn make_stmt(&self) -> @ast::Stmt; } + pub enum MacResult { MRExpr(@ast::Expr), MRItem(@ast::Item), @@ -112,10 +113,15 @@ impl MacResult { /// type signatures after emitting a non-fatal error (which stop /// compilation well before the validity (or otherwise)) of the /// expression are checked. - pub fn dummy_expr() -> MacResult { - MRExpr(@ast::Expr { - id: ast::DUMMY_NODE_ID, node: ast::ExprLogLevel, span: codemap::DUMMY_SP - }) + pub fn raw_dummy_expr(sp: codemap::Span) -> @ast::Expr { + @ast::Expr { + id: ast::DUMMY_NODE_ID, + node: ast::ExprLogLevel, + span: sp + } + } + pub fn dummy_expr(sp: codemap::Span) -> MacResult { + MRExpr(MacResult::raw_dummy_expr(sp)) } } diff --git a/src/libsyntax/ext/bytes.rs b/src/libsyntax/ext/bytes.rs index 39bb870b969a0..68aa757c524fb 100644 --- a/src/libsyntax/ext/bytes.rs +++ b/src/libsyntax/ext/bytes.rs @@ -21,7 +21,7 @@ use std::char; pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult { // Gather all argument expressions let exprs = match get_exprs_from_tts(cx, sp, tts) { - None => return MacResult::dummy_expr(), + None => return MacResult::dummy_expr(sp), Some(e) => e, }; let mut bytes = ~[]; diff --git a/src/libsyntax/ext/concat.rs b/src/libsyntax/ext/concat.rs index c13f9bf92af02..5316b8f721248 100644 --- a/src/libsyntax/ext/concat.rs +++ b/src/libsyntax/ext/concat.rs @@ -21,7 +21,7 @@ pub fn expand_syntax_ext(cx: &mut base::ExtCtxt, tts: &[ast::TokenTree]) -> base::MacResult { let es = match base::get_exprs_from_tts(cx, sp, tts) { Some(e) => e, - None => return base::MacResult::dummy_expr() + None => return base::MacResult::dummy_expr(sp) }; let mut accumulator = ~""; for e in es.move_iter() { diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs index 15e9d31daa00f..85cfd4f61e414 100644 --- a/src/libsyntax/ext/concat_idents.rs +++ b/src/libsyntax/ext/concat_idents.rs @@ -25,7 +25,7 @@ pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) ast::TTTok(_, token::COMMA) => (), _ => { cx.span_err(sp, "concat_idents! expecting comma."); - return MacResult::dummy_expr(); + return MacResult::dummy_expr(sp); } } } else { @@ -35,7 +35,7 @@ pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) } _ => { cx.span_err(sp, "concat_idents! requires ident args."); - return MacResult::dummy_expr(); + return MacResult::dummy_expr(sp); } } } diff --git a/src/libsyntax/ext/env.rs b/src/libsyntax/ext/env.rs index c23a1ce1e28d5..fec1e70af0735 100644 --- a/src/libsyntax/ext/env.rs +++ b/src/libsyntax/ext/env.rs @@ -26,7 +26,7 @@ use std::os; pub fn expand_option_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult { let var = match get_single_str_from_tts(cx, sp, tts, "option_env!") { - None => return MacResult::dummy_expr(), + None => return MacResult::dummy_expr(sp), Some(v) => v }; @@ -42,14 +42,14 @@ pub fn expand_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) let exprs = match get_exprs_from_tts(cx, sp, tts) { Some([]) => { cx.span_err(sp, "env! takes 1 or 2 arguments"); - return MacResult::dummy_expr(); + return MacResult::dummy_expr(sp); } - None => return MacResult::dummy_expr(), + None => return MacResult::dummy_expr(sp), Some(exprs) => exprs }; let var = match expr_to_str(cx, exprs[0], "expected string literal") { - None => return MacResult::dummy_expr(), + None => return MacResult::dummy_expr(sp), Some((v, _style)) => v }; let msg = match exprs.len() { @@ -60,13 +60,13 @@ pub fn expand_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) } 2 => { match expr_to_str(cx, exprs[1], "expected string literal") { - None => return MacResult::dummy_expr(), + None => return MacResult::dummy_expr(sp), Some((s, _style)) => s } } _ => { cx.span_err(sp, "env! takes 1 or 2 arguments"); - return MacResult::dummy_expr(); + return MacResult::dummy_expr(sp); } }; diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 97766e1a14b48..4b81713f7d0c9 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -51,7 +51,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { format!("expected macro name without module \ separators")); // let compilation continue - return e; + return MacResult::raw_dummy_expr(e.span); } let extname = pth.segments[0].identifier; let extnamestr = token::get_ident(extname); @@ -64,7 +64,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { extnamestr.get())); // let compilation continue - return e; + return MacResult::raw_dummy_expr(e.span); } Some(&NormalTT(ref expandfun, exp_span)) => { fld.cx.bt_push(ExpnInfo { @@ -98,7 +98,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { extnamestr.get() ) ); - return e; + return MacResult::raw_dummy_expr(e.span); } }; @@ -111,7 +111,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { format!("'{}' is not a tt-style macro", extnamestr.get()) ); - return e; + return MacResult::raw_dummy_expr(e.span); } }; diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index 01d348595b88c..13b1afb4c007c 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -811,7 +811,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, expr, "format argument must be a string literal.") { Some((fmt, _)) => fmt, - None => return efmt + None => return MacResult::raw_dummy_expr(sp) }; let mut parser = parse::Parser::new(fmt.get()); @@ -829,7 +829,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, match parser.errors.shift() { Some(error) => { cx.ecx.span_err(efmt.span, "invalid format string: " + error); - return efmt; + return MacResult::raw_dummy_expr(sp); } None => {} } diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 150f054b2afe4..c81ee55c23768 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -83,7 +83,7 @@ pub fn expand_include(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult { let file = match get_single_str_from_tts(cx, sp, tts, "include!") { Some(f) => f, - None => return MacResult::dummy_expr(), + None => return MacResult::dummy_expr(sp), }; // The file will be added to the code map by the parser let mut p = @@ -101,13 +101,13 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult { let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") { Some(f) => f, - None => return MacResult::dummy_expr() + None => return MacResult::dummy_expr(sp) }; let file = res_rel_file(cx, sp, &Path::new(file)); let bytes = match File::open(&file).read_to_end() { Err(e) => { cx.span_err(sp, format!("couldn't read {}: {}", file.display(), e)); - return MacResult::dummy_expr(); + return MacResult::dummy_expr(sp); } Ok(bytes) => bytes, }; @@ -123,7 +123,7 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) } None => { cx.span_err(sp, format!("{} wasn't a utf-8 file", file.display())); - return MacResult::dummy_expr(); + return MacResult::dummy_expr(sp); } } } @@ -133,13 +133,13 @@ pub fn expand_include_bin(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) { let file = match get_single_str_from_tts(cx, sp, tts, "include_bin!") { Some(f) => f, - None => return MacResult::dummy_expr() + None => return MacResult::dummy_expr(sp) }; let file = res_rel_file(cx, sp, &Path::new(file)); match File::open(&file).read_to_end() { Err(e) => { cx.span_err(sp, format!("couldn't read {}: {}", file.display(), e)); - return MacResult::dummy_expr(); + return MacResult::dummy_expr(sp); } Ok(bytes) => { base::MRExpr(cx.expr_lit(sp, ast::LitBinary(Rc::new(bytes)))) diff --git a/src/libsyntax/ext/trace_macros.rs b/src/libsyntax/ext/trace_macros.rs index 4189ea6a967a8..db2c9dcddb6dc 100644 --- a/src/libsyntax/ext/trace_macros.rs +++ b/src/libsyntax/ext/trace_macros.rs @@ -33,7 +33,7 @@ pub fn expand_trace_macros(cx: &mut ExtCtxt, cx.set_trace_macros(false); } else { cx.span_err(sp, "trace_macros! only accepts `true` or `false`"); - return base::MacResult::dummy_expr(); + return base::MacResult::dummy_expr(sp); } rust_parser.bump(); diff --git a/src/test/compile-fail/issue-11692.rs b/src/test/compile-fail/issue-11692.rs new file mode 100644 index 0000000000000..aed2c4d579a3d --- /dev/null +++ b/src/test/compile-fail/issue-11692.rs @@ -0,0 +1,19 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + print!(test!()); + //~^ ERROR: macro undefined: 'test' + //~^^ ERROR: format argument must be a string literal + + concat!(test!()); + //~^ ERROR: macro undefined: 'test' + //~^^ ERROR: expected a literal +}