From d45cb279012a56faaa39de305c1f8cd2098ecfb0 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Thu, 6 Dec 2012 11:01:58 -0800 Subject: [PATCH] syntax: point quote tokens at the site of quote-using-extension invocation. --- src/libsyntax/ext/base.rs | 7 ++ src/libsyntax/ext/build.rs | 18 ++++ src/libsyntax/ext/expand.rs | 23 +++--- src/libsyntax/ext/quote.rs | 161 ++++++++++++++++++------------------ 4 files changed, 120 insertions(+), 89 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 630ba3b8749..1f5b2b92f97 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -163,6 +163,7 @@ trait ext_ctxt { fn codemap() -> @CodeMap; fn parse_sess() -> parse::parse_sess; fn cfg() -> ast::crate_cfg; + fn call_site() -> span; fn print_backtrace(); fn backtrace() -> Option<@ExpnInfo>; fn mod_push(mod_name: ast::ident); @@ -195,6 +196,12 @@ impl ctxt_repr: ext_ctxt { fn codemap() -> @CodeMap { self.parse_sess.cm } fn parse_sess() -> parse::parse_sess { self.parse_sess } fn cfg() -> ast::crate_cfg { self.cfg } + fn call_site() -> span { + match self.backtrace { + Some(@ExpandedFrom({call_site: cs, _})) => cs, + None => self.bug(~"missing top span") + } + } fn print_backtrace() { } fn backtrace() -> Option<@ExpnInfo> { self.backtrace } fn mod_push(i: ast::ident) { self.mod_path.push(i); } diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 9d74509a02c..04a59d1fe41 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -145,6 +145,24 @@ fn mk_glob_use(cx: ext_ctxt, sp: span, vis: ast::private, span: sp} } +fn mk_local(cx: ext_ctxt, sp: span, mutbl: bool, + ident: ast::ident, ex: @ast::expr) -> @ast::stmt { + + let pat : @ast::pat = @{id: cx.next_id(), + node: ast::pat_ident(ast::bind_by_value, + mk_raw_path(sp, ~[ident]), + None), + span: sp}; + let ty : @ast::Ty = @{ id: cx.next_id(), node: ast::ty_infer, span: sp }; + let local : @ast::local = @{node: {is_mutbl: mutbl, + ty: ty, + pat: pat, + init: Some(ex), + id: cx.next_id()}, + span: sp}; + let decl = {node: ast::decl_local(~[local]), span: sp}; + @{ node: ast::stmt_decl(@decl, cx.next_id()), span: sp } +} fn mk_block(cx: ext_ctxt, sp: span, view_items: ~[@ast::view_item], stmts: ~[@ast::stmt], diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 9bbe9568f26..022f843a0ec 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -50,10 +50,11 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, fmt!("%s can only be used as a decorator", *extname)); } Some(normal({expander: exp, span: exp_sp})) => { - let expanded = exp(cx, (*mac).span, args, body); cx.bt_push(ExpandedFrom({call_site: s, callie: {name: *extname, span: exp_sp}})); + let expanded = exp(cx, (*mac).span, args, body); + //keep going, outside-in let fully_expanded = fld.fold_expr(expanded).node; cx.bt_pop(); @@ -90,6 +91,9 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, fmt!("macro undefined: '%s'", *extname)) } Some(normal_tt({expander: exp, span: exp_sp})) => { + cx.bt_push(ExpandedFrom({call_site: s, + callie: {name: *extname, span: exp_sp}})); + let expanded = match exp(cx, (*mac).span, (*tts)) { mr_expr(e) => e, mr_any(expr_maker,_,_) => expr_maker(), @@ -98,8 +102,6 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, *extname)) }; - cx.bt_push(ExpandedFrom({call_site: s, - callie: {name: *extname, span: exp_sp}})); //keep going, outside-in let fully_expanded = fld.fold_expr(expanded).node; cx.bt_pop(); @@ -107,13 +109,14 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, (fully_expanded, s) } Some(normal({expander: exp, span: exp_sp})) => { + cx.bt_push(ExpandedFrom({call_site: s, + callie: {name: *extname, span: exp_sp}})); + //convert the new-style invoc for the old-style macro let arg = base::tt_args_to_original_flavor(cx, pth.span, (*tts)); let expanded = exp(cx, (*mac).span, arg, None); - cx.bt_push(ExpandedFrom({call_site: s, - callie: {name: *extname, span: exp_sp}})); //keep going, outside-in let fully_expanded = fld.fold_expr(expanded).node; cx.bt_pop(); @@ -296,6 +299,8 @@ fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", *extname)), Some(normal_tt({expander: exp, span: exp_sp})) => { + cx.bt_push(ExpandedFrom( + {call_site: sp, callie: {name: *extname, span: exp_sp}})); let expanded = match exp(cx, mac.span, tts) { mr_expr(e) => @{node: stmt_expr(e, cx.next_id()), span: e.span}, @@ -305,8 +310,6 @@ fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, fmt!("non-stmt macro in stmt pos: %s", *extname)) }; - cx.bt_push(ExpandedFrom( - {call_site: sp, callie: {name: *extname, span: exp_sp}})); //keep going, outside-in let fully_expanded = fld.fold_stmt(expanded).node; cx.bt_pop(); @@ -315,15 +318,15 @@ fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt, } Some(normal({expander: exp, span: exp_sp})) => { + cx.bt_push(ExpandedFrom({call_site: sp, + callie: {name: *extname, + span: exp_sp}})); //convert the new-style invoc for the old-style macro let arg = base::tt_args_to_original_flavor(cx, pth.span, tts); let exp_expr = exp(cx, mac.span, arg, None); let expanded = @{node: stmt_expr(exp_expr, cx.next_id()), span: exp_expr.span}; - cx.bt_push(ExpandedFrom({call_site: sp, - callie: {name: *extname, - span: exp_sp}})); //keep going, outside-in let fully_expanded = fld.fold_stmt(expanded).node; cx.bt_pop(); diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index eb5c90320df..d1761ea5110 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -86,65 +86,6 @@ fn id_ext(cx: ext_ctxt, str: ~str) -> ast::ident { cx.parse_sess().interner.intern(@str) } -fn mk_option_span(cx: ext_ctxt, - qsp: span, - sp: Option) -> @ast::expr { - match sp { - None => build::mk_path(cx, qsp, ids_ext(cx, ~[~"None"])), - Some(sp) => { - build::mk_call(cx, qsp, - ids_ext(cx, ~[~"Some"]), - ~[build::mk_managed(cx, qsp, - mk_span(cx, qsp, sp))]) - } - } -} - -fn mk_span(cx: ext_ctxt, qsp: span, sp: span) -> @ast::expr { - - let e_expn_info = match sp.expn_info { - None => build::mk_path(cx, qsp, ids_ext(cx, ~[~"None"])), - Some(@codemap::ExpandedFrom(ref cr)) => { - let e_callee = - build::mk_rec_e( - cx, qsp, - ~[{ident: id_ext(cx, ~"name"), - ex: build::mk_uniq_str(cx, qsp, - (*cr).callie.name)}, - {ident: id_ext(cx, ~"span"), - ex: mk_option_span(cx, qsp, (*cr).callie.span)}]); - - let e_expn_info_ = - build::mk_call( - cx, qsp, - ids_ext(cx, ~[~"expanded_from"]), - ~[build::mk_rec_e( - cx, qsp, - ~[{ident: id_ext(cx, ~"call_site"), - ex: mk_span(cx, qsp, (*cr).call_site)}, - {ident: id_ext(cx, ~"callie"), - ex: e_callee}])]); - - build::mk_call(cx, qsp, - ids_ext(cx, ~[~"Some"]), - ~[build::mk_managed(cx, qsp, e_expn_info_)]) - } - }; - - let span_path = ids_ext(cx, ~[~"span"]); - - build::mk_struct_e(cx, qsp, - span_path, - ~[{ident: id_ext(cx, ~"lo"), - ex: mk_bytepos(cx, qsp, sp.lo) }, - - {ident: id_ext(cx, ~"hi"), - ex: mk_bytepos(cx, qsp, sp.hi) }, - - {ident: id_ext(cx, ~"expn_info"), - ex: e_expn_info}]) -} - // Lift an ident to the expr that evaluates to that ident. fn mk_ident(cx: ext_ctxt, sp: span, ident: ast::ident) -> @ast::expr { let e_meth = build::mk_access(cx, sp, @@ -321,59 +262,121 @@ fn mk_token(cx: ext_ctxt, sp: span, tok: token::Token) -> @ast::expr { } -fn mk_tt(cx: ext_ctxt, sp: span, tt: &ast::token_tree) -> @ast::expr { +fn mk_tt(cx: ext_ctxt, sp: span, tt: &ast::token_tree) + -> ~[@ast::stmt] { + match *tt { + ast::tt_tok(sp, ref tok) => { + let e_sp = build::mk_path(cx, sp, + ids_ext(cx, ~[~"sp"])); let e_tok = build::mk_call(cx, sp, ids_ext(cx, ~[~"tt_tok"]), - ~[mk_span(cx, sp, sp), - mk_token(cx, sp, (*tok))]); - build::mk_uniq_vec_e(cx, sp, ~[e_tok]) - } + ~[e_sp, mk_token(cx, sp, *tok)]); + let e_push = + build::mk_call_(cx, sp, + build::mk_access(cx, sp, + ids_ext(cx, ~[~"tt"]), + id_ext(cx, ~"push")), + ~[e_tok]); + ~[build::mk_stmt(cx, sp, e_push)] - ast::tt_delim(ref tts) => { - let e_delim = - build::mk_call(cx, sp, - ids_ext(cx, ~[~"tt_delim"]), - ~[mk_tts(cx, sp, (*tts))]); - build::mk_uniq_vec_e(cx, sp, ~[e_delim]) } + ast::tt_delim(ref tts) => mk_tts(cx, sp, *tts), ast::tt_seq(*) => fail ~"tt_seq in quote!", - ast::tt_nonterminal(sp, ident) => - build::mk_copy(cx, sp, build::mk_path(cx, sp, ~[ident])) + ast::tt_nonterminal(sp, ident) => { + let e_push = + build::mk_call_(cx, sp, + build::mk_access + (cx, sp, + ids_ext(cx, ~[~"tt"]), + id_ext(cx, ~"push_all_move")), + ~[build::mk_path(cx, sp, ~[ident])]); + ~[build::mk_stmt(cx, sp, e_push)] + } } } -fn mk_tts(cx: ext_ctxt, sp: span, tts: &[ast::token_tree]) -> @ast::expr { - let e_tts = tts.map(|tt| mk_tt(cx, sp, tt)); - build::mk_call(cx, sp, - ids_ext(cx, ~[~"vec", ~"concat"]), - ~[build::mk_slice_vec_e(cx, sp, e_tts)]) +fn mk_tts(cx: ext_ctxt, sp: span, tts: &[ast::token_tree]) + -> ~[@ast::stmt] { + let mut ss = ~[]; + for tts.each |tt| { + ss.push_all_move(mk_tt(cx, sp, tt)); + } + ss } fn expand_tts(cx: ext_ctxt, sp: span, tts: ~[ast::token_tree]) -> @ast::expr { + // NB: It appears that the main parser loses its mind if we consider // $foo as a tt_nonterminal during the main parse, so we have to re-parse // under quote_depth > 0. This is silly and should go away; the _guess_ is // it has to do with transition away from supporting old-style macros, so // try removing it when enough of them are gone. + let p = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts); p.quote_depth += 1u; let tts = p.parse_all_token_trees(); p.abort_if_errors(); // We want to emit a block expression that does a sequence of 'use's to - // import the runtime module, followed by a tt expression. + // import the runtime module, followed by a tt-building expression. + let uses = ~[ build::mk_glob_use(cx, sp, ids_ext(cx, ~[~"syntax", ~"ext", ~"quote", ~"rt"])) ]; - build::mk_block(cx, sp, uses, ~[], Some(mk_tts(cx, sp, tts))) + + // We also bind a single value, sp, to ext_cx.call_site() + // + // This causes every span in a token-tree quote to be attributed to the + // call site of the extension using the quote. We can't really do much + // better since the source of the quote may well be in a library that + // was not even parsed by this compilation run, that the user has no + // source code for (eg. in libsyntax, which they're just _using_). + // + // The old quasiquoter had an elaborate mechanism for denoting input + // file locations from which quotes originated; unfortunately this + // relied on feeding the source string of the quote back into the + // compiler (which we don't really want to do) and, in any case, only + // pushed the problem a very small step further back: an error + // resulting from a parse of the resulting quote is still attributed to + // the site the string literal occured, which was in a source file + // _other_ than the one the user has control over. For example, an + // error in a quote from the protocol compiler, invoked in user code + // using proto! for example, will be attributed to the pipec.rs file in + // libsyntax, which the user might not even have source to (unless they + // happen to have a compiler on hand). Over all, the phase distinction + // just makes quotes "hard to attribute". Possibly this could be fixed + // by recreating some of the original qq machinery in the tt regime + // (pushing fake FileMaps onto the parser to account for original sites + // of quotes, for example) but at this point it seems not likely to be + // worth the hassle. + + let e_sp = build::mk_call_(cx, sp, + build::mk_access(cx, sp, + ids_ext(cx, ~[~"ext_cx"]), + id_ext(cx, ~"call_site")), + ~[]); + + let stmt_let_sp = build::mk_local(cx, sp, false, + id_ext(cx, ~"sp"), + e_sp); + + let stmt_let_tt = build::mk_local(cx, sp, true, + id_ext(cx, ~"tt"), + build::mk_uniq_vec_e(cx, sp, ~[])); + + build::mk_block(cx, sp, uses, + ~[stmt_let_sp, + stmt_let_tt] + mk_tts(cx, sp, tts), + Some(build::mk_path(cx, sp, + ids_ext(cx, ~[~"tt"])))) } fn expand_parse_call(cx: ext_ctxt, -- GitLab