1
Fork 0

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.
This commit is contained in:
Douglas Young 2014-02-18 16:14:12 +00:00
parent 517e38997d
commit 0bdfd0f4c7
11 changed files with 53 additions and 28 deletions

View file

@ -64,7 +64,7 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
"inline assembly must be a string literal.") { "inline assembly must be a string literal.") {
Some((s, st)) => (s, st), Some((s, st)) => (s, st),
// let compilation continue // let compilation continue
None => return MacResult::dummy_expr(), None => return MacResult::dummy_expr(sp),
}; };
asm = s; asm = s;
asm_str_style = Some(style); asm_str_style = Some(style);

View file

@ -101,6 +101,7 @@ pub trait AnyMacro {
fn make_stmt(&self) -> @ast::Stmt; fn make_stmt(&self) -> @ast::Stmt;
} }
pub enum MacResult { pub enum MacResult {
MRExpr(@ast::Expr), MRExpr(@ast::Expr),
MRItem(@ast::Item), MRItem(@ast::Item),
@ -112,10 +113,15 @@ impl MacResult {
/// type signatures after emitting a non-fatal error (which stop /// type signatures after emitting a non-fatal error (which stop
/// compilation well before the validity (or otherwise)) of the /// compilation well before the validity (or otherwise)) of the
/// expression are checked. /// expression are checked.
pub fn dummy_expr() -> MacResult { pub fn raw_dummy_expr(sp: codemap::Span) -> @ast::Expr {
MRExpr(@ast::Expr { @ast::Expr {
id: ast::DUMMY_NODE_ID, node: ast::ExprLogLevel, span: codemap::DUMMY_SP id: ast::DUMMY_NODE_ID,
}) node: ast::ExprLogLevel,
span: sp
}
}
pub fn dummy_expr(sp: codemap::Span) -> MacResult {
MRExpr(MacResult::raw_dummy_expr(sp))
} }
} }

View file

@ -21,7 +21,7 @@ use std::char;
pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult { pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult {
// Gather all argument expressions // Gather all argument expressions
let exprs = match get_exprs_from_tts(cx, sp, tts) { let exprs = match get_exprs_from_tts(cx, sp, tts) {
None => return MacResult::dummy_expr(), None => return MacResult::dummy_expr(sp),
Some(e) => e, Some(e) => e,
}; };
let mut bytes = ~[]; let mut bytes = ~[];

View file

@ -21,7 +21,7 @@ pub fn expand_syntax_ext(cx: &mut base::ExtCtxt,
tts: &[ast::TokenTree]) -> base::MacResult { tts: &[ast::TokenTree]) -> base::MacResult {
let es = match base::get_exprs_from_tts(cx, sp, tts) { let es = match base::get_exprs_from_tts(cx, sp, tts) {
Some(e) => e, Some(e) => e,
None => return base::MacResult::dummy_expr() None => return base::MacResult::dummy_expr(sp)
}; };
let mut accumulator = ~""; let mut accumulator = ~"";
for e in es.move_iter() { for e in es.move_iter() {

View file

@ -25,7 +25,7 @@ pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
ast::TTTok(_, token::COMMA) => (), ast::TTTok(_, token::COMMA) => (),
_ => { _ => {
cx.span_err(sp, "concat_idents! expecting comma."); cx.span_err(sp, "concat_idents! expecting comma.");
return MacResult::dummy_expr(); return MacResult::dummy_expr(sp);
} }
} }
} else { } 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."); cx.span_err(sp, "concat_idents! requires ident args.");
return MacResult::dummy_expr(); return MacResult::dummy_expr(sp);
} }
} }
} }

View file

@ -26,7 +26,7 @@ use std::os;
pub fn expand_option_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) pub fn expand_option_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> base::MacResult { -> base::MacResult {
let var = match get_single_str_from_tts(cx, sp, tts, "option_env!") { 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 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) { let exprs = match get_exprs_from_tts(cx, sp, tts) {
Some([]) => { Some([]) => {
cx.span_err(sp, "env! takes 1 or 2 arguments"); 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 Some(exprs) => exprs
}; };
let var = match expr_to_str(cx, exprs[0], "expected string literal") { 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 Some((v, _style)) => v
}; };
let msg = match exprs.len() { let msg = match exprs.len() {
@ -60,13 +60,13 @@ pub fn expand_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
} }
2 => { 2 => {
match expr_to_str(cx, exprs[1], "expected string literal") { 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 Some((s, _style)) => s
} }
} }
_ => { _ => {
cx.span_err(sp, "env! takes 1 or 2 arguments"); cx.span_err(sp, "env! takes 1 or 2 arguments");
return MacResult::dummy_expr(); return MacResult::dummy_expr(sp);
} }
}; };

View file

@ -51,7 +51,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
format!("expected macro name without module \ format!("expected macro name without module \
separators")); separators"));
// let compilation continue // let compilation continue
return e; return MacResult::raw_dummy_expr(e.span);
} }
let extname = pth.segments[0].identifier; let extname = pth.segments[0].identifier;
let extnamestr = token::get_ident(extname); let extnamestr = token::get_ident(extname);
@ -64,7 +64,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
extnamestr.get())); extnamestr.get()));
// let compilation continue // let compilation continue
return e; return MacResult::raw_dummy_expr(e.span);
} }
Some(&NormalTT(ref expandfun, exp_span)) => { Some(&NormalTT(ref expandfun, exp_span)) => {
fld.cx.bt_push(ExpnInfo { fld.cx.bt_push(ExpnInfo {
@ -98,7 +98,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
extnamestr.get() 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", format!("'{}' is not a tt-style macro",
extnamestr.get()) extnamestr.get())
); );
return e; return MacResult::raw_dummy_expr(e.span);
} }
}; };

View file

@ -811,7 +811,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
expr, expr,
"format argument must be a string literal.") { "format argument must be a string literal.") {
Some((fmt, _)) => fmt, Some((fmt, _)) => fmt,
None => return efmt None => return MacResult::raw_dummy_expr(sp)
}; };
let mut parser = parse::Parser::new(fmt.get()); 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() { match parser.errors.shift() {
Some(error) => { Some(error) => {
cx.ecx.span_err(efmt.span, "invalid format string: " + error); cx.ecx.span_err(efmt.span, "invalid format string: " + error);
return efmt; return MacResult::raw_dummy_expr(sp);
} }
None => {} None => {}
} }

View file

@ -83,7 +83,7 @@ pub fn expand_include(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> base::MacResult { -> base::MacResult {
let file = match get_single_str_from_tts(cx, sp, tts, "include!") { let file = match get_single_str_from_tts(cx, sp, tts, "include!") {
Some(f) => f, 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 // The file will be added to the code map by the parser
let mut p = let mut p =
@ -101,13 +101,13 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> base::MacResult { -> base::MacResult {
let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") { let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") {
Some(f) => f, 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 file = res_rel_file(cx, sp, &Path::new(file));
let bytes = match File::open(&file).read_to_end() { let bytes = match File::open(&file).read_to_end() {
Err(e) => { Err(e) => {
cx.span_err(sp, format!("couldn't read {}: {}", file.display(), e)); cx.span_err(sp, format!("couldn't read {}: {}", file.display(), e));
return MacResult::dummy_expr(); return MacResult::dummy_expr(sp);
} }
Ok(bytes) => bytes, Ok(bytes) => bytes,
}; };
@ -123,7 +123,7 @@ pub fn expand_include_str(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
} }
None => { None => {
cx.span_err(sp, format!("{} wasn't a utf-8 file", file.display())); 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!") { let file = match get_single_str_from_tts(cx, sp, tts, "include_bin!") {
Some(f) => f, 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 file = res_rel_file(cx, sp, &Path::new(file));
match File::open(&file).read_to_end() { match File::open(&file).read_to_end() {
Err(e) => { Err(e) => {
cx.span_err(sp, format!("couldn't read {}: {}", file.display(), e)); cx.span_err(sp, format!("couldn't read {}: {}", file.display(), e));
return MacResult::dummy_expr(); return MacResult::dummy_expr(sp);
} }
Ok(bytes) => { Ok(bytes) => {
base::MRExpr(cx.expr_lit(sp, ast::LitBinary(Rc::new(bytes)))) base::MRExpr(cx.expr_lit(sp, ast::LitBinary(Rc::new(bytes))))

View file

@ -33,7 +33,7 @@ pub fn expand_trace_macros(cx: &mut ExtCtxt,
cx.set_trace_macros(false); cx.set_trace_macros(false);
} else { } else {
cx.span_err(sp, "trace_macros! only accepts `true` or `false`"); 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(); rust_parser.bump();

View file

@ -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 <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.
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
}