From b7ec2488ff2f29681fe28691d20fd2c260a9e454 Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Sat, 4 Feb 2012 18:37:24 -0700 Subject: [PATCH] Fix macro backtraces. In addition add information about the macro doing the expansion, and move the printing of the expansion backtrace from codemap::span_to_str to the diagnostic code. The backtrace is now more verbose and includes information on the macro doing the expansion, in addition to the expansion site. --- src/comp/driver/diagnostic.rs | 13 +++++++++ src/comp/front/core_inject.rs | 2 +- src/comp/syntax/ast_util.rs | 2 +- src/comp/syntax/codemap.rs | 38 ++++++------------------- src/comp/syntax/ext/base.rs | 50 ++++++++++++++++++++------------- src/comp/syntax/ext/expand.rs | 18 ++++++++---- src/comp/syntax/ext/simplext.rs | 8 +++--- 7 files changed, 72 insertions(+), 59 deletions(-) diff --git a/src/comp/driver/diagnostic.rs b/src/comp/driver/diagnostic.rs index 5c0661fe51e..427bac77197 100644 --- a/src/comp/driver/diagnostic.rs +++ b/src/comp/driver/diagnostic.rs @@ -179,6 +179,7 @@ fn emit(cmsp: option<(codemap::codemap, span)>, let lines = codemap::span_to_lines(sp, cm); print_diagnostic(ss, lvl, msg); highlight_lines(cm, sp, lines); + print_macro_backtrace(cm, sp); } none { print_diagnostic("", lvl, msg); @@ -241,3 +242,15 @@ fn highlight_lines(cm: codemap::codemap, sp: span, io::stderr().write_str(s + "\n"); } } + +fn print_macro_backtrace(cm: codemap::codemap, sp: span) { + option::may (sp.expn_info) {|ei| + let ss = option::maybe("", ei.callie.span, + bind codemap::span_to_str(_, cm)); + print_diagnostic(ss, note, + #fmt("in expansion of #%s", ei.callie.name)); + let ss = codemap::span_to_str(ei.call_site, cm); + print_diagnostic(ss, note, "expansion site"); + print_macro_backtrace(cm, ei.call_site); + } +} diff --git a/src/comp/front/core_inject.rs b/src/comp/front/core_inject.rs index f285d4bf67b..07bb48b8b3e 100644 --- a/src/comp/front/core_inject.rs +++ b/src/comp/front/core_inject.rs @@ -24,7 +24,7 @@ fn inject_libcore_ref(sess: session, fn spanned(x: T) -> @ast::spanned { ret @{node: x, span: {lo: 0u, hi: 0u, - expanded_from: codemap::os_none}}; + expn_info: option::none}}; } let n1 = sess.next_node_id(); diff --git a/src/comp/syntax/ast_util.rs b/src/comp/syntax/ast_util.rs index bdfaee5d4c2..767a2431bd3 100644 --- a/src/comp/syntax/ast_util.rs +++ b/src/comp/syntax/ast_util.rs @@ -7,7 +7,7 @@ fn respan(sp: span, t: T) -> spanned { /* assuming that we're not in macro expansion */ fn mk_sp(lo: uint, hi: uint) -> span { - ret {lo: lo, hi: hi, expanded_from: codemap::os_none}; + ret {lo: lo, hi: hi, expn_info: none}; } // make this a const, once the compiler supports it diff --git a/src/comp/syntax/codemap.rs b/src/comp/syntax/codemap.rs index 74b9bb091b4..7af9efe5984 100644 --- a/src/comp/syntax/codemap.rs +++ b/src/comp/syntax/codemap.rs @@ -92,38 +92,18 @@ fn lookup_byte_pos(map: codemap, pos: uint) -> loc { ret lookup_pos(map, pos, lookup); } -enum opt_span { - - //hack (as opposed to option), to make `span` compile - os_none, - os_some(@span), +enum expn_info_ { + expanded_from({call_site: span, + callie: {name: str, span: option}}) } -type span = {lo: uint, hi: uint, expanded_from: opt_span}; +type expn_info = option<@expn_info_>; +type span = {lo: uint, hi: uint, expn_info: expn_info}; fn span_to_str(sp: span, cm: codemap) -> str { - let cur = sp; - let res = ""; - // FIXME: Should probably be doing pointer comparison on filemap - let prev_file = none; - while true { - let lo = lookup_char_pos(cm, cur.lo); - let hi = lookup_char_pos(cm, cur.hi); - res += - #fmt["%s:%u:%u: %u:%u", - if some(lo.file.name) == prev_file { - "-" - } else { lo.file.name }, lo.line, lo.col, hi.line, hi.col]; - alt cur.expanded_from { - os_none { break; } - os_some(new_sp) { - cur = *new_sp; - prev_file = some(lo.file.name); - res += "<<"; - } - } - } - - ret res; + let lo = lookup_char_pos(cm, sp.lo); + let hi = lookup_char_pos(cm, sp.hi); + ret #fmt("%s:%u:%u: %u:%u", lo.file.name, + lo.line, lo.col, hi.line, hi.col) } type file_lines = {file: filemap, lines: [uint]}; diff --git a/src/comp/syntax/ext/base.rs b/src/comp/syntax/ext/base.rs index 42ec73077f6..6ab1c8446cf 100644 --- a/src/comp/syntax/ext/base.rs +++ b/src/comp/syntax/ext/base.rs @@ -1,12 +1,15 @@ import core::{vec, option}; import std::map::hashmap; import driver::session::session; -import codemap::span; +import codemap::{span, expn_info, expanded_from}; import std::map::new_str_hash; import codemap; -type syntax_expander = +type syntax_expander_ = fn@(ext_ctxt, span, ast::mac_arg, ast::mac_body) -> @ast::expr; +type syntax_expander = { + expander: syntax_expander_, + span: option}; type macro_def = {ident: str, ext: syntax_extension}; type macro_definer = fn@(ext_ctxt, span, ast::mac_arg, ast::mac_body) -> macro_def; @@ -19,27 +22,29 @@ enum syntax_extension { // A temporary hard-coded map of methods for expanding syntax extension // AST nodes into full ASTs fn syntax_expander_table() -> hashmap { + fn builtin(f: syntax_expander_) -> syntax_extension + {normal({expander: f, span: none})} let syntax_expanders = new_str_hash::(); - syntax_expanders.insert("fmt", normal(ext::fmt::expand_syntax_ext)); - syntax_expanders.insert("env", normal(ext::env::expand_syntax_ext)); + syntax_expanders.insert("fmt", builtin(ext::fmt::expand_syntax_ext)); + syntax_expanders.insert("env", builtin(ext::env::expand_syntax_ext)); syntax_expanders.insert("macro", macro_defining(ext::simplext::add_new_extension)); syntax_expanders.insert("concat_idents", - normal(ext::concat_idents::expand_syntax_ext)); + builtin(ext::concat_idents::expand_syntax_ext)); syntax_expanders.insert("ident_to_str", - normal(ext::ident_to_str::expand_syntax_ext)); + builtin(ext::ident_to_str::expand_syntax_ext)); syntax_expanders.insert("log_syntax", - normal(ext::log_syntax::expand_syntax_ext)); + builtin(ext::log_syntax::expand_syntax_ext)); syntax_expanders.insert("ast", - normal(ext::qquote::expand_ast)); + builtin(ext::qquote::expand_ast)); ret syntax_expanders; } iface ext_ctxt { fn session() -> session; fn print_backtrace(); - fn backtrace() -> codemap::opt_span; - fn bt_push(sp: span); + fn backtrace() -> expn_info; + fn bt_push(ei: codemap::expn_info_); fn bt_pop(); fn span_fatal(sp: span, msg: str) -> !; fn span_err(sp: span, msg: str); @@ -51,20 +56,26 @@ iface ext_ctxt { fn mk_ctxt(sess: session) -> ext_ctxt { type ctxt_repr = {sess: session, - mutable backtrace: codemap::opt_span}; + mutable backtrace: expn_info}; impl of ext_ctxt for ctxt_repr { fn session() -> session { self.sess } fn print_backtrace() { } - fn backtrace() -> codemap::opt_span { self.backtrace } - fn bt_push(sp: span) { - self.backtrace = codemap::os_some( - @{lo: sp.lo, hi: sp.hi, expanded_from: self.backtrace}); + fn backtrace() -> expn_info { self.backtrace } + fn bt_push(ei: codemap::expn_info_) { + alt ei { + expanded_from({call_site: cs, callie: callie}) { + self.backtrace = + some(@expanded_from({ + call_site: {lo: cs.lo, hi: cs.hi, + expn_info: self.backtrace}, + callie: callie})); + } + } } fn bt_pop() { alt self.backtrace { - codemap::os_some(@{expanded_from: pre, _}) { - let tmp = pre; - self.backtrace = tmp; + some(@expanded_from({call_site: {expn_info: prev, _}, _})) { + self.backtrace = prev } _ { self.bug("tried to pop without a push"); } } @@ -88,7 +99,8 @@ fn mk_ctxt(sess: session) -> ext_ctxt { fn bug(msg: str) -> ! { self.print_backtrace(); self.sess.bug(msg); } fn next_id() -> ast::node_id { ret self.sess.next_node_id(); } } - {sess: sess, mutable backtrace: codemap::os_none} as ext_ctxt + let imp : ctxt_repr = {sess: sess, mutable backtrace: none}; + ret imp as ext_ctxt } fn expr_to_str(cx: ext_ctxt, expr: @ast::expr, error: str) -> str { diff --git a/src/comp/syntax/ext/expand.rs b/src/comp/syntax/ext/expand.rs index 201e44232bb..b91b794571f 100644 --- a/src/comp/syntax/ext/expand.rs +++ b/src/comp/syntax/ext/expand.rs @@ -11,7 +11,7 @@ import syntax::ext::base::*; import syntax::ext::qquote::{expand_qquote,qq_helper}; import syntax::parse::parser::parse_expr_from_source_str; -import codemap::span; +import codemap::{span, expanded_from}; fn expand_expr(exts: hashmap, cx: ext_ctxt, e: expr_, s: span, fld: ast_fold, @@ -29,10 +29,12 @@ fn expand_expr(exts: hashmap, cx: ext_ctxt, cx.span_fatal(pth.span, #fmt["macro undefined: '%s'", extname]) } - some(normal(ext)) { - let expanded = ext(cx, pth.span, args, body); + some(normal({expander: exp, span: exp_sp})) { + let expanded = exp(cx, pth.span, args, body); - cx.bt_push(mac.span); + let info = {call_site: s, + callie: {name: extname, span: exp_sp}}; + cx.bt_push(expanded_from(info)); //keep going, outside-in let fully_expanded = fld.fold_expr(expanded).node; cx.bt_pop(); @@ -53,6 +55,11 @@ fn expand_expr(exts: hashmap, cx: ext_ctxt, }; } +fn new_span(cx: ext_ctxt, sp: span) -> span { + /* this discards information in the case of macro-defining macros */ + ret {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()}; +} + // FIXME: this is a terrible kludge to inject some macros into the default // compilation environment. When the macro-definition system is substantially // more mature, these should move from here, into a compiled part of libcore @@ -73,7 +80,8 @@ fn expand_crate(sess: session::session, c: @crate) -> @crate { let afp = default_ast_fold(); let cx: ext_ctxt = mk_ctxt(sess); let f_pre = - {fold_expr: bind expand_expr(exts, cx, _, _, _, afp.fold_expr) + {fold_expr: bind expand_expr(exts, cx, _, _, _, afp.fold_expr), + new_span: bind new_span(cx, _) with *afp}; let f = make_fold(f_pre); let cm = parse_expr_from_source_str("", diff --git a/src/comp/syntax/ext/simplext.rs b/src/comp/syntax/ext/simplext.rs index 3473ae1dbe1..6304df29e26 100644 --- a/src/comp/syntax/ext/simplext.rs +++ b/src/comp/syntax/ext/simplext.rs @@ -190,7 +190,7 @@ fn transcribe(cx: ext_ctxt, b: bindings, body: @expr) -> @expr { fn new_id(_old: node_id, cx: ext_ctxt) -> node_id { ret cx.next_id(); } fn new_span(cx: ext_ctxt, sp: span) -> span { /* this discards information in the case of macro-defining macros */ - ret {lo: sp.lo, hi: sp.hi, expanded_from: cx.backtrace()}; + ret {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()}; } let afp = default_ast_fold(); let f_pre = @@ -202,8 +202,8 @@ fn transcribe(cx: ext_ctxt, b: bindings, body: @expr) -> @expr { fold_block: bind transcribe_block(cx, b, idx_path, _, _, _, afp.fold_block), map_exprs: bind transcribe_exprs(cx, b, idx_path, _, _), - new_id: bind new_id(_, cx), - new_span: bind new_span(cx, _) with *afp}; + new_id: bind new_id(_, cx) + with *afp}; let f = make_fold(f_pre); let result = f.fold_expr(body); ret result; @@ -753,7 +753,7 @@ fn add_new_extension(cx: ext_ctxt, sp: span, arg: ast::mac_arg, "at least one clause") } }, - ext: normal(ext)}; + ext: normal({expander: ext, span: some(arg.span)})}; fn generic_extension(cx: ext_ctxt, sp: span, arg: ast::mac_arg, _body: ast::mac_body, clauses: [@clause]) -> @expr {