Skip to content

Commit d45cb27

Browse files
committed
syntax: point quote tokens at the site of quote-using-extension invocation.
1 parent 9f27bf7 commit d45cb27

File tree

4 files changed

+120
-89
lines changed

4 files changed

+120
-89
lines changed

src/libsyntax/ext/base.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ trait ext_ctxt {
163163
fn codemap() -> @CodeMap;
164164
fn parse_sess() -> parse::parse_sess;
165165
fn cfg() -> ast::crate_cfg;
166+
fn call_site() -> span;
166167
fn print_backtrace();
167168
fn backtrace() -> Option<@ExpnInfo>;
168169
fn mod_push(mod_name: ast::ident);
@@ -195,6 +196,12 @@ fn mk_ctxt(parse_sess: parse::parse_sess,
195196
fn codemap() -> @CodeMap { self.parse_sess.cm }
196197
fn parse_sess() -> parse::parse_sess { self.parse_sess }
197198
fn cfg() -> ast::crate_cfg { self.cfg }
199+
fn call_site() -> span {
200+
match self.backtrace {
201+
Some(@ExpandedFrom({call_site: cs, _})) => cs,
202+
None => self.bug(~"missing top span")
203+
}
204+
}
198205
fn print_backtrace() { }
199206
fn backtrace() -> Option<@ExpnInfo> { self.backtrace }
200207
fn mod_push(i: ast::ident) { self.mod_path.push(i); }

src/libsyntax/ext/build.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,24 @@ fn mk_glob_use(cx: ext_ctxt, sp: span,
145145
vis: ast::private,
146146
span: sp}
147147
}
148+
fn mk_local(cx: ext_ctxt, sp: span, mutbl: bool,
149+
ident: ast::ident, ex: @ast::expr) -> @ast::stmt {
150+
151+
let pat : @ast::pat = @{id: cx.next_id(),
152+
node: ast::pat_ident(ast::bind_by_value,
153+
mk_raw_path(sp, ~[ident]),
154+
None),
155+
span: sp};
156+
let ty : @ast::Ty = @{ id: cx.next_id(), node: ast::ty_infer, span: sp };
157+
let local : @ast::local = @{node: {is_mutbl: mutbl,
158+
ty: ty,
159+
pat: pat,
160+
init: Some(ex),
161+
id: cx.next_id()},
162+
span: sp};
163+
let decl = {node: ast::decl_local(~[local]), span: sp};
164+
@{ node: ast::stmt_decl(@decl, cx.next_id()), span: sp }
165+
}
148166
fn mk_block(cx: ext_ctxt, sp: span,
149167
view_items: ~[@ast::view_item],
150168
stmts: ~[@ast::stmt],

src/libsyntax/ext/expand.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
5050
fmt!("%s can only be used as a decorator", *extname));
5151
}
5252
Some(normal({expander: exp, span: exp_sp})) => {
53-
let expanded = exp(cx, (*mac).span, args, body);
5453

5554
cx.bt_push(ExpandedFrom({call_site: s,
5655
callie: {name: *extname, span: exp_sp}}));
56+
let expanded = exp(cx, (*mac).span, args, body);
57+
5758
//keep going, outside-in
5859
let fully_expanded = fld.fold_expr(expanded).node;
5960
cx.bt_pop();
@@ -90,6 +91,9 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
9091
fmt!("macro undefined: '%s'", *extname))
9192
}
9293
Some(normal_tt({expander: exp, span: exp_sp})) => {
94+
cx.bt_push(ExpandedFrom({call_site: s,
95+
callie: {name: *extname, span: exp_sp}}));
96+
9397
let expanded = match exp(cx, (*mac).span, (*tts)) {
9498
mr_expr(e) => e,
9599
mr_any(expr_maker,_,_) => expr_maker(),
@@ -98,22 +102,21 @@ fn expand_expr(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
98102
*extname))
99103
};
100104

101-
cx.bt_push(ExpandedFrom({call_site: s,
102-
callie: {name: *extname, span: exp_sp}}));
103105
//keep going, outside-in
104106
let fully_expanded = fld.fold_expr(expanded).node;
105107
cx.bt_pop();
106108

107109
(fully_expanded, s)
108110
}
109111
Some(normal({expander: exp, span: exp_sp})) => {
112+
cx.bt_push(ExpandedFrom({call_site: s,
113+
callie: {name: *extname, span: exp_sp}}));
114+
110115
//convert the new-style invoc for the old-style macro
111116
let arg = base::tt_args_to_original_flavor(cx, pth.span,
112117
(*tts));
113118
let expanded = exp(cx, (*mac).span, arg, None);
114119

115-
cx.bt_push(ExpandedFrom({call_site: s,
116-
callie: {name: *extname, span: exp_sp}}));
117120
//keep going, outside-in
118121
let fully_expanded = fld.fold_expr(expanded).node;
119122
cx.bt_pop();
@@ -296,6 +299,8 @@ fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
296299
cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", *extname)),
297300
298301
Some(normal_tt({expander: exp, span: exp_sp})) => {
302+
cx.bt_push(ExpandedFrom(
303+
{call_site: sp, callie: {name: *extname, span: exp_sp}}));
299304
let expanded = match exp(cx, mac.span, tts) {
300305
mr_expr(e) =>
301306
@{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,
305310
fmt!("non-stmt macro in stmt pos: %s", *extname))
306311
};
307312
308-
cx.bt_push(ExpandedFrom(
309-
{call_site: sp, callie: {name: *extname, span: exp_sp}}));
310313
//keep going, outside-in
311314
let fully_expanded = fld.fold_stmt(expanded).node;
312315
cx.bt_pop();
@@ -315,15 +318,15 @@ fn expand_stmt(exts: HashMap<~str, syntax_extension>, cx: ext_ctxt,
315318
}
316319
317320
Some(normal({expander: exp, span: exp_sp})) => {
321+
cx.bt_push(ExpandedFrom({call_site: sp,
322+
callie: {name: *extname,
323+
span: exp_sp}}));
318324
//convert the new-style invoc for the old-style macro
319325
let arg = base::tt_args_to_original_flavor(cx, pth.span, tts);
320326
let exp_expr = exp(cx, mac.span, arg, None);
321327
let expanded = @{node: stmt_expr(exp_expr, cx.next_id()),
322328
span: exp_expr.span};
323329
324-
cx.bt_push(ExpandedFrom({call_site: sp,
325-
callie: {name: *extname,
326-
span: exp_sp}}));
327330
//keep going, outside-in
328331
let fully_expanded = fld.fold_stmt(expanded).node;
329332
cx.bt_pop();

src/libsyntax/ext/quote.rs

Lines changed: 82 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -86,65 +86,6 @@ fn id_ext(cx: ext_ctxt, str: ~str) -> ast::ident {
8686
cx.parse_sess().interner.intern(@str)
8787
}
8888

89-
fn mk_option_span(cx: ext_ctxt,
90-
qsp: span,
91-
sp: Option<span>) -> @ast::expr {
92-
match sp {
93-
None => build::mk_path(cx, qsp, ids_ext(cx, ~[~"None"])),
94-
Some(sp) => {
95-
build::mk_call(cx, qsp,
96-
ids_ext(cx, ~[~"Some"]),
97-
~[build::mk_managed(cx, qsp,
98-
mk_span(cx, qsp, sp))])
99-
}
100-
}
101-
}
102-
103-
fn mk_span(cx: ext_ctxt, qsp: span, sp: span) -> @ast::expr {
104-
105-
let e_expn_info = match sp.expn_info {
106-
None => build::mk_path(cx, qsp, ids_ext(cx, ~[~"None"])),
107-
Some(@codemap::ExpandedFrom(ref cr)) => {
108-
let e_callee =
109-
build::mk_rec_e(
110-
cx, qsp,
111-
~[{ident: id_ext(cx, ~"name"),
112-
ex: build::mk_uniq_str(cx, qsp,
113-
(*cr).callie.name)},
114-
{ident: id_ext(cx, ~"span"),
115-
ex: mk_option_span(cx, qsp, (*cr).callie.span)}]);
116-
117-
let e_expn_info_ =
118-
build::mk_call(
119-
cx, qsp,
120-
ids_ext(cx, ~[~"expanded_from"]),
121-
~[build::mk_rec_e(
122-
cx, qsp,
123-
~[{ident: id_ext(cx, ~"call_site"),
124-
ex: mk_span(cx, qsp, (*cr).call_site)},
125-
{ident: id_ext(cx, ~"callie"),
126-
ex: e_callee}])]);
127-
128-
build::mk_call(cx, qsp,
129-
ids_ext(cx, ~[~"Some"]),
130-
~[build::mk_managed(cx, qsp, e_expn_info_)])
131-
}
132-
};
133-
134-
let span_path = ids_ext(cx, ~[~"span"]);
135-
136-
build::mk_struct_e(cx, qsp,
137-
span_path,
138-
~[{ident: id_ext(cx, ~"lo"),
139-
ex: mk_bytepos(cx, qsp, sp.lo) },
140-
141-
{ident: id_ext(cx, ~"hi"),
142-
ex: mk_bytepos(cx, qsp, sp.hi) },
143-
144-
{ident: id_ext(cx, ~"expn_info"),
145-
ex: e_expn_info}])
146-
}
147-
14889
// Lift an ident to the expr that evaluates to that ident.
14990
fn mk_ident(cx: ext_ctxt, sp: span, ident: ast::ident) -> @ast::expr {
15091
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 {
321262
}
322263

323264

324-
fn mk_tt(cx: ext_ctxt, sp: span, tt: &ast::token_tree) -> @ast::expr {
265+
fn mk_tt(cx: ext_ctxt, sp: span, tt: &ast::token_tree)
266+
-> ~[@ast::stmt] {
267+
325268
match *tt {
269+
326270
ast::tt_tok(sp, ref tok) => {
271+
let e_sp = build::mk_path(cx, sp,
272+
ids_ext(cx, ~[~"sp"]));
327273
let e_tok =
328274
build::mk_call(cx, sp,
329275
ids_ext(cx, ~[~"tt_tok"]),
330-
~[mk_span(cx, sp, sp),
331-
mk_token(cx, sp, (*tok))]);
332-
build::mk_uniq_vec_e(cx, sp, ~[e_tok])
333-
}
276+
~[e_sp, mk_token(cx, sp, *tok)]);
277+
let e_push =
278+
build::mk_call_(cx, sp,
279+
build::mk_access(cx, sp,
280+
ids_ext(cx, ~[~"tt"]),
281+
id_ext(cx, ~"push")),
282+
~[e_tok]);
283+
~[build::mk_stmt(cx, sp, e_push)]
334284

335-
ast::tt_delim(ref tts) => {
336-
let e_delim =
337-
build::mk_call(cx, sp,
338-
ids_ext(cx, ~[~"tt_delim"]),
339-
~[mk_tts(cx, sp, (*tts))]);
340-
build::mk_uniq_vec_e(cx, sp, ~[e_delim])
341285
}
342286

287+
ast::tt_delim(ref tts) => mk_tts(cx, sp, *tts),
343288
ast::tt_seq(*) => fail ~"tt_seq in quote!",
344289
345-
ast::tt_nonterminal(sp, ident) =>
346-
build::mk_copy(cx, sp, build::mk_path(cx, sp, ~[ident]))
290+
ast::tt_nonterminal(sp, ident) => {
291+
let e_push =
292+
build::mk_call_(cx, sp,
293+
build::mk_access
294+
(cx, sp,
295+
ids_ext(cx, ~[~"tt"]),
296+
id_ext(cx, ~"push_all_move")),
297+
~[build::mk_path(cx, sp, ~[ident])]);
298+
~[build::mk_stmt(cx, sp, e_push)]
299+
}
347300
}
348301
}
349302

350-
fn mk_tts(cx: ext_ctxt, sp: span, tts: &[ast::token_tree]) -> @ast::expr {
351-
let e_tts = tts.map(|tt| mk_tt(cx, sp, tt));
352-
build::mk_call(cx, sp,
353-
ids_ext(cx, ~[~"vec", ~"concat"]),
354-
~[build::mk_slice_vec_e(cx, sp, e_tts)])
303+
fn mk_tts(cx: ext_ctxt, sp: span, tts: &[ast::token_tree])
304+
-> ~[@ast::stmt] {
305+
let mut ss = ~[];
306+
for tts.each |tt| {
307+
ss.push_all_move(mk_tt(cx, sp, tt));
308+
}
309+
ss
355310
}
356311

357312
fn expand_tts(cx: ext_ctxt,
358313
sp: span,
359314
tts: ~[ast::token_tree]) -> @ast::expr {
315+
360316
// NB: It appears that the main parser loses its mind if we consider
361317
// $foo as a tt_nonterminal during the main parse, so we have to re-parse
362318
// under quote_depth > 0. This is silly and should go away; the _guess_ is
363319
// it has to do with transition away from supporting old-style macros, so
364320
// try removing it when enough of them are gone.
321+
365322
let p = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts);
366323
p.quote_depth += 1u;
367324
let tts = p.parse_all_token_trees();
368325
p.abort_if_errors();
369326

370327
// We want to emit a block expression that does a sequence of 'use's to
371-
// import the runtime module, followed by a tt expression.
328+
// import the runtime module, followed by a tt-building expression.
329+
372330
let uses = ~[ build::mk_glob_use(cx, sp, ids_ext(cx, ~[~"syntax",
373331
~"ext",
374332
~"quote",
375333
~"rt"])) ];
376-
build::mk_block(cx, sp, uses, ~[], Some(mk_tts(cx, sp, tts)))
334+
335+
// We also bind a single value, sp, to ext_cx.call_site()
336+
//
337+
// This causes every span in a token-tree quote to be attributed to the
338+
// call site of the extension using the quote. We can't really do much
339+
// better since the source of the quote may well be in a library that
340+
// was not even parsed by this compilation run, that the user has no
341+
// source code for (eg. in libsyntax, which they're just _using_).
342+
//
343+
// The old quasiquoter had an elaborate mechanism for denoting input
344+
// file locations from which quotes originated; unfortunately this
345+
// relied on feeding the source string of the quote back into the
346+
// compiler (which we don't really want to do) and, in any case, only
347+
// pushed the problem a very small step further back: an error
348+
// resulting from a parse of the resulting quote is still attributed to
349+
// the site the string literal occured, which was in a source file
350+
// _other_ than the one the user has control over. For example, an
351+
// error in a quote from the protocol compiler, invoked in user code
352+
// using proto! for example, will be attributed to the pipec.rs file in
353+
// libsyntax, which the user might not even have source to (unless they
354+
// happen to have a compiler on hand). Over all, the phase distinction
355+
// just makes quotes "hard to attribute". Possibly this could be fixed
356+
// by recreating some of the original qq machinery in the tt regime
357+
// (pushing fake FileMaps onto the parser to account for original sites
358+
// of quotes, for example) but at this point it seems not likely to be
359+
// worth the hassle.
360+
361+
let e_sp = build::mk_call_(cx, sp,
362+
build::mk_access(cx, sp,
363+
ids_ext(cx, ~[~"ext_cx"]),
364+
id_ext(cx, ~"call_site")),
365+
~[]);
366+
367+
let stmt_let_sp = build::mk_local(cx, sp, false,
368+
id_ext(cx, ~"sp"),
369+
e_sp);
370+
371+
let stmt_let_tt = build::mk_local(cx, sp, true,
372+
id_ext(cx, ~"tt"),
373+
build::mk_uniq_vec_e(cx, sp, ~[]));
374+
375+
build::mk_block(cx, sp, uses,
376+
~[stmt_let_sp,
377+
stmt_let_tt] + mk_tts(cx, sp, tts),
378+
Some(build::mk_path(cx, sp,
379+
ids_ext(cx, ~[~"tt"]))))
377380
}
378381

379382
fn expand_parse_call(cx: ext_ctxt,

0 commit comments

Comments
 (0)