Skip to content

Commit 7c33df0

Browse files
committed
auto merge of #11644 : huonw/rust/less-fatality, r=cmr
This means that compilation continues for longer, and so we can see more errors per compile. This is mildly more user-friendly because it stops users having to run rustc n times to see n macro errors: just run it once to see all of them.
2 parents f7cc8a6 + 4be3262 commit 7c33df0

15 files changed

+245
-84
lines changed

src/libsyntax/ext/asm.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,12 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
5959
while continue_ {
6060
match state {
6161
Asm => {
62-
let (s, style) =
63-
expr_to_str(cx, p.parse_expr(),
64-
"inline assembly must be a string literal.");
62+
let (s, style) = match expr_to_str(cx, p.parse_expr(),
63+
"inline assembly must be a string literal.") {
64+
Some((s, st)) => (s, st),
65+
// let compilation continue
66+
None => return MacResult::dummy_expr(),
67+
};
6568
asm = s;
6669
asm_str_style = Some(style);
6770
}

src/libsyntax/ext/base.rs

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,17 @@ pub enum MacResult {
136136
MRAny(@AnyMacro),
137137
MRDef(MacroDef),
138138
}
139+
impl MacResult {
140+
/// Create an empty expression MacResult; useful for satisfying
141+
/// type signatures after emitting a non-fatal error (which stop
142+
/// compilation well before the validity (or otherwise)) of the
143+
/// expression are checked.
144+
pub fn dummy_expr() -> MacResult {
145+
MRExpr(@ast::Expr {
146+
id: ast::DUMMY_NODE_ID, node: ast::ExprLogLevel, span: codemap::DUMMY_SP
147+
})
148+
}
149+
}
139150

140151
pub enum SyntaxExtension {
141152
// #[deriving] and such
@@ -364,10 +375,27 @@ impl<'a> ExtCtxt<'a> {
364375
_ => self.bug("tried to pop without a push")
365376
}
366377
}
378+
/// Emit `msg` attached to `sp`, and stop compilation immediately.
379+
///
380+
/// `span_err` should be strongly prefered where-ever possible:
381+
/// this should *only* be used when
382+
/// - continuing has a high risk of flow-on errors (e.g. errors in
383+
/// declaring a macro would cause all uses of that macro to
384+
/// complain about "undefined macro"), or
385+
/// - there is literally nothing else that can be done (however,
386+
/// in most cases one can construct a dummy expression/item to
387+
/// substitute; we never hit resolve/type-checking so the dummy
388+
/// value doesn't have to match anything)
367389
pub fn span_fatal(&self, sp: Span, msg: &str) -> ! {
368390
self.print_backtrace();
369391
self.parse_sess.span_diagnostic.span_fatal(sp, msg);
370392
}
393+
394+
/// Emit `msg` attached to `sp`, without immediately stopping
395+
/// compilation.
396+
///
397+
/// Compilation will be stopped in the near future (at the end of
398+
/// the macro expansion phase).
371399
pub fn span_err(&self, sp: Span, msg: &str) {
372400
self.print_backtrace();
373401
self.parse_sess.span_diagnostic.span_err(sp, msg);
@@ -402,53 +430,69 @@ impl<'a> ExtCtxt<'a> {
402430
}
403431
}
404432

405-
pub fn expr_to_str(cx: &ExtCtxt, expr: @ast::Expr, err_msg: &str) -> (@str, ast::StrStyle) {
433+
/// Extract a string literal from `expr`, emitting `err_msg` if `expr`
434+
/// is not a string literal. This does not stop compilation on error,
435+
/// merely emits a non-fatal error and returns None.
436+
pub fn expr_to_str(cx: &ExtCtxt, expr: @ast::Expr,
437+
err_msg: &str) -> Option<(@str, ast::StrStyle)> {
406438
match expr.node {
407439
ast::ExprLit(l) => match l.node {
408-
ast::LitStr(s, style) => (s, style),
409-
_ => cx.span_fatal(l.span, err_msg)
440+
ast::LitStr(s, style) => return Some((s, style)),
441+
_ => cx.span_err(l.span, err_msg)
410442
},
411-
_ => cx.span_fatal(expr.span, err_msg)
443+
_ => cx.span_err(expr.span, err_msg)
412444
}
445+
None
413446
}
414447

448+
/// Non-fatally assert that `tts` is empty. Note that this function
449+
/// returns even when `tts` is non-empty, macros that *need* to stop
450+
/// compilation should call
451+
/// `cx.parse_sess.span_diagnostic.abort_if_errors()` (this should be
452+
/// done as rarely as possible).
415453
pub fn check_zero_tts(cx: &ExtCtxt, sp: Span, tts: &[ast::TokenTree],
416454
name: &str) {
417455
if tts.len() != 0 {
418-
cx.span_fatal(sp, format!("{} takes no arguments", name));
456+
cx.span_err(sp, format!("{} takes no arguments", name));
419457
}
420458
}
421459

460+
/// Extract the string literal from the first token of `tts`. If this
461+
/// is not a string literal, emit an error and return None.
422462
pub fn get_single_str_from_tts(cx: &ExtCtxt,
423463
sp: Span,
424464
tts: &[ast::TokenTree],
425465
name: &str)
426-
-> @str {
466+
-> Option<@str> {
427467
if tts.len() != 1 {
428-
cx.span_fatal(sp, format!("{} takes 1 argument.", name));
429-
}
430-
431-
match tts[0] {
432-
ast::TTTok(_, token::LIT_STR(ident))
433-
| ast::TTTok(_, token::LIT_STR_RAW(ident, _)) => cx.str_of(ident),
434-
_ => cx.span_fatal(sp, format!("{} requires a string.", name)),
468+
cx.span_err(sp, format!("{} takes 1 argument.", name));
469+
} else {
470+
match tts[0] {
471+
ast::TTTok(_, token::LIT_STR(ident))
472+
| ast::TTTok(_, token::LIT_STR_RAW(ident, _)) => return Some(cx.str_of(ident)),
473+
_ => cx.span_err(sp, format!("{} requires a string.", name)),
474+
}
435475
}
476+
None
436477
}
437478

479+
/// Extract comma-separated expressions from `tts`. If there is a
480+
/// parsing error, emit a non-fatal error and return None.
438481
pub fn get_exprs_from_tts(cx: &ExtCtxt,
439482
sp: Span,
440-
tts: &[ast::TokenTree]) -> ~[@ast::Expr] {
483+
tts: &[ast::TokenTree]) -> Option<~[@ast::Expr]> {
441484
let mut p = parse::new_parser_from_tts(cx.parse_sess(),
442485
cx.cfg(),
443486
tts.to_owned());
444487
let mut es = ~[];
445488
while p.token != token::EOF {
446489
if es.len() != 0 && !p.eat(&token::COMMA) {
447-
cx.span_fatal(sp, "expected token: `,`");
490+
cx.span_err(sp, "expected token: `,`");
491+
return None;
448492
}
449493
es.push(p.parse_expr());
450494
}
451-
es
495+
Some(es)
452496
}
453497

454498
// in order to have some notion of scoping for macros,

src/libsyntax/ext/bytes.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ use std::char;
2020

2121
pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult {
2222
// Gather all argument expressions
23-
let exprs = get_exprs_from_tts(cx, sp, tts);
23+
let exprs = match get_exprs_from_tts(cx, sp, tts) {
24+
None => return MacResult::dummy_expr(),
25+
Some(e) => e,
26+
};
2427
let mut bytes = ~[];
2528

2629
for expr in exprs.iter() {

src/libsyntax/ext/concat.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ use ext::build::AstBuilder;
1818
pub fn expand_syntax_ext(cx: &mut base::ExtCtxt,
1919
sp: codemap::Span,
2020
tts: &[ast::TokenTree]) -> base::MacResult {
21-
let es = base::get_exprs_from_tts(cx, sp, tts);
21+
let es = match base::get_exprs_from_tts(cx, sp, tts) {
22+
Some(e) => e,
23+
None => return base::MacResult::dummy_expr()
24+
};
2225
let mut accumulator = ~"";
2326
for e in es.move_iter() {
2427
let e = cx.expand_expr(e);

src/libsyntax/ext/concat_idents.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,18 @@ pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
2323
if i & 1 == 1 {
2424
match *e {
2525
ast::TTTok(_, token::COMMA) => (),
26-
_ => cx.span_fatal(sp, "concat_idents! expecting comma.")
26+
_ => {
27+
cx.span_err(sp, "concat_idents! expecting comma.");
28+
return MacResult::dummy_expr();
29+
}
2730
}
2831
} else {
2932
match *e {
3033
ast::TTTok(_, token::IDENT(ident,_)) => res_str.push_str(cx.str_of(ident)),
31-
_ => cx.span_fatal(sp, "concat_idents! requires ident args.")
34+
_ => {
35+
cx.span_err(sp, "concat_idents! requires ident args.");
36+
return MacResult::dummy_expr();
37+
}
3238
}
3339
}
3440
}

src/libsyntax/ext/deriving/default.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,9 @@ fn default_substructure(cx: &ExtCtxt, span: Span, substr: &Substructure) -> @Exp
7070
}
7171
}
7272
StaticEnum(..) => {
73-
cx.span_fatal(span, "`Default` cannot be derived for enums, \
74-
only structs")
73+
cx.span_err(span, "`Default` cannot be derived for enums, only structs");
74+
// let compilation continue
75+
cx.expr_uint(span, 0)
7576
}
7677
_ => cx.bug("Non-static method in `deriving(Default)`")
7778
};

src/libsyntax/ext/deriving/rand.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ fn rand_substructure(cx: &ExtCtxt, span: Span, substr: &Substructure) -> @Expr {
7373
}
7474
StaticEnum(_, ref variants) => {
7575
if variants.is_empty() {
76-
cx.span_fatal(span, "`Rand` cannot be derived for enums with no variants");
76+
cx.span_err(span, "`Rand` cannot be derived for enums with no variants");
77+
// let compilation continue
78+
return cx.expr_uint(span, 0);
7779
}
7880

7981
let variant_count = cx.expr_uint(span, variants.len());

src/libsyntax/ext/deriving/zero.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,9 @@ fn zero_substructure(cx: &ExtCtxt, span: Span, substr: &Substructure) -> @Expr {
8686
}
8787
}
8888
StaticEnum(..) => {
89-
cx.span_fatal(span, "`Zero` cannot be derived for enums, \
90-
only structs")
89+
cx.span_err(span, "`Zero` cannot be derived for enums, only structs");
90+
// let compilation continue
91+
cx.expr_uint(span, 0)
9192
}
9293
_ => cx.bug("Non-static method in `deriving(Zero)`")
9394
};

src/libsyntax/ext/env.rs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ use std::os;
2424

2525
pub fn expand_option_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
2626
-> base::MacResult {
27-
let var = get_single_str_from_tts(cx, sp, tts, "option_env!");
27+
let var = match get_single_str_from_tts(cx, sp, tts, "option_env!") {
28+
None => return MacResult::dummy_expr(),
29+
Some(v) => v
30+
};
2831

2932
let e = match os::getenv(var) {
3033
None => quote_expr!(cx, ::std::option::None::<&'static str>),
@@ -35,24 +38,38 @@ pub fn expand_option_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
3538

3639
pub fn expand_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
3740
-> base::MacResult {
38-
let exprs = get_exprs_from_tts(cx, sp, tts);
39-
40-
if exprs.len() == 0 {
41-
cx.span_fatal(sp, "env! takes 1 or 2 arguments");
42-
}
41+
let exprs = match get_exprs_from_tts(cx, sp, tts) {
42+
Some([]) => {
43+
cx.span_err(sp, "env! takes 1 or 2 arguments");
44+
return MacResult::dummy_expr();
45+
}
46+
None => return MacResult::dummy_expr(),
47+
Some(exprs) => exprs
48+
};
4349

44-
let (var, _var_str_style) = expr_to_str(cx, exprs[0], "expected string literal");
50+
let var = match expr_to_str(cx, exprs[0], "expected string literal") {
51+
None => return MacResult::dummy_expr(),
52+
Some((v, _style)) => v
53+
};
4554
let msg = match exprs.len() {
4655
1 => format!("environment variable `{}` not defined", var).to_managed(),
4756
2 => {
48-
let (s, _style) = expr_to_str(cx, exprs[1], "expected string literal");
49-
s
57+
match expr_to_str(cx, exprs[1], "expected string literal") {
58+
None => return MacResult::dummy_expr(),
59+
Some((s, _style)) => s
60+
}
61+
}
62+
_ => {
63+
cx.span_err(sp, "env! takes 1 or 2 arguments");
64+
return MacResult::dummy_expr();
5065
}
51-
_ => cx.span_fatal(sp, "env! takes 1 or 2 arguments")
5266
};
5367

5468
let e = match os::getenv(var) {
55-
None => cx.span_fatal(sp, msg),
69+
None => {
70+
cx.span_err(sp, msg);
71+
cx.expr_uint(sp, 0)
72+
}
5673
Some(s) => cx.expr_str(sp, s.to_managed())
5774
};
5875
MRExpr(e)

0 commit comments

Comments
 (0)