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:
parent
517e38997d
commit
0bdfd0f4c7
11 changed files with 53 additions and 28 deletions
|
@ -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);
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 = ~[];
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))))
|
||||||
|
|
|
@ -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();
|
||||||
|
|
19
src/test/compile-fail/issue-11692.rs
Normal file
19
src/test/compile-fail/issue-11692.rs
Normal 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
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue