Skip to content

Commit feeb23d

Browse files
committed
Auto merge of #24003 - rprichard:span-fixes, r=huonw
* In `noop_fold_expr`, call `new_span` in these cases: - `ExprMethodCall`'s identifier - `ExprField`'s identifier - `ExprTupField`'s integer Calling `new_span` for `ExprMethodCall`'s identifier is necessary to print an acceptable diagnostic for `write!(&2, "")`. We see this error: ``` <std macros>:2:20: 2:66 error: type `&mut _` does not implement any method in scope named `write_fmt` <std macros>:2 ( & mut * $ dst ) . write_fmt ( format_args ! ( $ ( $ arg ) * ) ) ) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` With this change, we also see a macro expansion backtrace leading to the `write!(&2, "")` call site. * After fully expanding a macro, we replace the expansion expression's span with the original span. Call `fld.new_span` to add a backtrace to this span. (Note that I'm call `new_span` after `bt.pop()`, so the macro just expanded isn't on the backtrace.) The motivating example for this change is `println!("{}")`. The format string literal is `concat!($fmt, "arg")` and is inside the libstd macro. We need to see the backtrace to find the `println!` call site. * Add a backtrace to the `format_args!` format expression span. r? alexcrichton Addresses #23459
2 parents 03f563a + ddbdf51 commit feeb23d

13 files changed

+172
-23
lines changed

src/libsyntax/codemap.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ impl CodeMap {
871871
F: FnOnce(Option<&ExpnInfo>) -> T,
872872
{
873873
match id {
874-
NO_EXPANSION => f(None),
874+
NO_EXPANSION | COMMAND_LINE_EXPN => f(None),
875875
ExpnId(i) => f(Some(&(*self.expansions.borrow())[i as usize]))
876876
}
877877
}

src/libsyntax/diagnostic.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -465,22 +465,21 @@ fn emit(dst: &mut EmitterWriter, cm: &codemap::CodeMap, rsp: RenderSpan,
465465
match rsp {
466466
FullSpan(_) => {
467467
try!(highlight_lines(dst, cm, sp, lvl, cm.span_to_lines(sp)));
468+
try!(print_macro_backtrace(dst, cm, sp));
468469
}
469470
EndSpan(_) => {
470471
try!(end_highlight_lines(dst, cm, sp, lvl, cm.span_to_lines(sp)));
472+
try!(print_macro_backtrace(dst, cm, sp));
471473
}
472474
Suggestion(_, ref suggestion) => {
473475
try!(highlight_suggestion(dst, cm, sp, suggestion));
476+
try!(print_macro_backtrace(dst, cm, sp));
474477
}
475478
FileLine(..) => {
476479
// no source text in this case!
477480
}
478481
}
479482

480-
if sp != COMMAND_LINE_SP {
481-
try!(print_macro_backtrace(dst, cm, sp));
482-
}
483-
484483
match code {
485484
Some(code) =>
486485
match dst.registry.as_ref().and_then(|registry| registry.find_description(code)) {

src/libsyntax/ext/base.rs

-10
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,6 @@ impl<'a> ExtCtxt<'a> {
605605
None => self.bug("missing top span")
606606
})
607607
}
608-
pub fn print_backtrace(&self) { }
609608
pub fn backtrace(&self) -> ExpnId { self.backtrace }
610609
pub fn original_span(&self) -> Span {
611610
let mut expn_id = self.backtrace;
@@ -700,7 +699,6 @@ impl<'a> ExtCtxt<'a> {
700699
/// substitute; we never hit resolve/type-checking so the dummy
701700
/// value doesn't have to match anything)
702701
pub fn span_fatal(&self, sp: Span, msg: &str) -> ! {
703-
self.print_backtrace();
704702
panic!(self.parse_sess.span_diagnostic.span_fatal(sp, msg));
705703
}
706704

@@ -710,35 +708,27 @@ impl<'a> ExtCtxt<'a> {
710708
/// Compilation will be stopped in the near future (at the end of
711709
/// the macro expansion phase).
712710
pub fn span_err(&self, sp: Span, msg: &str) {
713-
self.print_backtrace();
714711
self.parse_sess.span_diagnostic.span_err(sp, msg);
715712
}
716713
pub fn span_warn(&self, sp: Span, msg: &str) {
717-
self.print_backtrace();
718714
self.parse_sess.span_diagnostic.span_warn(sp, msg);
719715
}
720716
pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! {
721-
self.print_backtrace();
722717
self.parse_sess.span_diagnostic.span_unimpl(sp, msg);
723718
}
724719
pub fn span_bug(&self, sp: Span, msg: &str) -> ! {
725-
self.print_backtrace();
726720
self.parse_sess.span_diagnostic.span_bug(sp, msg);
727721
}
728722
pub fn span_note(&self, sp: Span, msg: &str) {
729-
self.print_backtrace();
730723
self.parse_sess.span_diagnostic.span_note(sp, msg);
731724
}
732725
pub fn span_help(&self, sp: Span, msg: &str) {
733-
self.print_backtrace();
734726
self.parse_sess.span_diagnostic.span_help(sp, msg);
735727
}
736728
pub fn fileline_help(&self, sp: Span, msg: &str) {
737-
self.print_backtrace();
738729
self.parse_sess.span_diagnostic.fileline_help(sp, msg);
739730
}
740731
pub fn bug(&self, msg: &str) -> ! {
741-
self.print_backtrace();
742732
self.parse_sess.span_diagnostic.handler().bug(msg);
743733
}
744734
pub fn trace_macros(&self) -> bool {

src/libsyntax/ext/expand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
5555
fully_expanded.map(|e| ast::Expr {
5656
id: ast::DUMMY_NODE_ID,
5757
node: e.node,
58-
span: span,
58+
span: fld.new_span(span),
5959
})
6060
}
6161

src/libsyntax/ext/format.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use ext::base::*;
1717
use ext::base;
1818
use ext::build::AstBuilder;
1919
use fmt_macros as parse;
20+
use fold::Folder;
2021
use parse::token::special_idents;
2122
use parse::token;
2223
use ptr::P;
@@ -649,6 +650,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
649650
names: HashMap<String, P<ast::Expr>>)
650651
-> P<ast::Expr> {
651652
let arg_types: Vec<_> = (0..args.len()).map(|_| None).collect();
653+
// Expand the format literal so that efmt.span will have a backtrace. This
654+
// is essential for locating a bug when the format literal is generated in
655+
// a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")).
656+
let efmt = ecx.expander().fold_expr(efmt);
652657
let mut cx = Context {
653658
ecx: ecx,
654659
args: args,
@@ -663,9 +668,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
663668
pieces: Vec::new(),
664669
str_pieces: Vec::new(),
665670
all_pieces_simple: true,
666-
fmtsp: sp,
671+
fmtsp: efmt.span,
667672
};
668-
cx.fmtsp = efmt.span;
669673
let fmt = match expr_to_string(cx.ecx,
670674
efmt,
671675
"format argument must be a string literal.") {

src/libsyntax/ext/log_syntax.rs

-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ pub fn expand_syntax_ext<'cx>(cx: &'cx mut base::ExtCtxt,
2626
return base::DummyResult::any(sp);
2727
}
2828

29-
cx.print_backtrace();
30-
3129
println!("{}", print::pprust::tts_to_string(tts));
3230

3331
// any so that `log_syntax` can be invoked as an expression and item.

src/libsyntax/fold.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1176,7 +1176,7 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
11761176
}
11771177
ExprMethodCall(i, tps, args) => {
11781178
ExprMethodCall(
1179-
respan(i.span, folder.fold_ident(i.node)),
1179+
respan(folder.new_span(i.span), folder.fold_ident(i.node)),
11801180
tps.move_map(|x| folder.fold_ty(x)),
11811181
args.move_map(|x| folder.fold_expr(x)))
11821182
}
@@ -1246,11 +1246,13 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
12461246
}
12471247
ExprField(el, ident) => {
12481248
ExprField(folder.fold_expr(el),
1249-
respan(ident.span, folder.fold_ident(ident.node)))
1249+
respan(folder.new_span(ident.span),
1250+
folder.fold_ident(ident.node)))
12501251
}
12511252
ExprTupField(el, ident) => {
12521253
ExprTupField(folder.fold_expr(el),
1253-
respan(ident.span, folder.fold_usize(ident.node)))
1254+
respan(folder.new_span(ident.span),
1255+
folder.fold_usize(ident.node)))
12541256
}
12551257
ExprIndex(el, er) => {
12561258
ExprIndex(folder.fold_expr(el), folder.fold_expr(er))

src/test/auxiliary/internal_unstable.rs

+33
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ pub struct Foo {
2222
pub x: u8
2323
}
2424

25+
impl Foo {
26+
#[unstable(feature = "method")]
27+
pub fn method(&self) {}
28+
}
29+
30+
#[stable(feature = "stable", since = "1.0.0")]
31+
pub struct Bar {
32+
#[unstable(feature = "struct2_field")]
33+
pub x: u8
34+
}
35+
2536
#[allow_internal_unstable]
2637
#[macro_export]
2738
macro_rules! call_unstable_allow {
@@ -36,6 +47,18 @@ macro_rules! construct_unstable_allow {
3647
}
3748
}
3849

50+
#[allow_internal_unstable]
51+
#[macro_export]
52+
macro_rules! call_method_allow {
53+
($e: expr) => { $e.method() }
54+
}
55+
56+
#[allow_internal_unstable]
57+
#[macro_export]
58+
macro_rules! access_field_allow {
59+
($e: expr) => { $e.x }
60+
}
61+
3962
#[allow_internal_unstable]
4063
#[macro_export]
4164
macro_rules! pass_through_allow {
@@ -54,6 +77,16 @@ macro_rules! construct_unstable_noallow {
5477
}
5578
}
5679

80+
#[macro_export]
81+
macro_rules! call_method_noallow {
82+
($e: expr) => { $e.method() }
83+
}
84+
85+
#[macro_export]
86+
macro_rules! access_field_noallow {
87+
($e: expr) => { $e.x }
88+
}
89+
5790
#[macro_export]
5891
macro_rules! pass_through_noallow {
5992
($e: expr) => { $e }

src/test/compile-fail/internal-unstable-noallow.rs

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
// aux-build:internal_unstable.rs
1717
// error-pattern:use of unstable library feature 'function'
1818
// error-pattern:use of unstable library feature 'struct_field'
19+
// error-pattern:use of unstable library feature 'method'
20+
// error-pattern:use of unstable library feature 'struct2_field'
1921

2022
#[macro_use]
2123
extern crate internal_unstable;
@@ -24,4 +26,8 @@ fn main() {
2426
call_unstable_noallow!();
2527

2628
construct_unstable_noallow!(0);
29+
30+
|x: internal_unstable::Foo| { call_method_noallow!(x) };
31+
32+
|x: internal_unstable::Bar| { access_field_noallow!(x) };
2733
}

src/test/compile-fail/internal-unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ fn main() {
3636
// ok, the instability is contained.
3737
call_unstable_allow!();
3838
construct_unstable_allow!(0);
39+
|x: internal_unstable::Foo| { call_method_allow!(x) };
40+
|x: internal_unstable::Bar| { access_field_allow!(x) };
3941

4042
// bad.
4143
pass_through_allow!(internal_unstable::unstable()); //~ ERROR use of unstable
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Macros in statement vs expression position handle backtraces differently.
12+
13+
macro_rules! fake_method_stmt { //~ NOTE in expansion of
14+
() => {
15+
1.fake() //~ ERROR does not implement any method
16+
}
17+
}
18+
19+
macro_rules! fake_field_stmt { //~ NOTE in expansion of
20+
() => {
21+
1.fake //~ ERROR no field with that name
22+
}
23+
}
24+
25+
macro_rules! fake_anon_field_stmt { //~ NOTE in expansion of
26+
() => {
27+
(1).0 //~ ERROR type was not a tuple
28+
}
29+
}
30+
31+
macro_rules! fake_method_expr { //~ NOTE in expansion of
32+
() => {
33+
1.fake() //~ ERROR does not implement any method
34+
}
35+
}
36+
37+
macro_rules! fake_field_expr {
38+
() => {
39+
1.fake
40+
}
41+
}
42+
43+
macro_rules! fake_anon_field_expr {
44+
() => {
45+
(1).0
46+
}
47+
}
48+
49+
fn main() {
50+
fake_method_stmt!(); //~ NOTE expansion site
51+
fake_field_stmt!(); //~ NOTE expansion site
52+
fake_anon_field_stmt!(); //~ NOTE expansion site
53+
54+
let _ = fake_method_expr!(); //~ NOTE expansion site
55+
let _ = fake_field_expr!(); //~ ERROR no field with that name
56+
let _ = fake_anon_field_expr!(); //~ ERROR type was not a tuple
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// In expression position, but not statement position, when we expand a macro,
12+
// we replace the span of the expanded expression with that of the call site.
13+
14+
macro_rules! nested_expr {
15+
() => (fake)
16+
}
17+
18+
macro_rules! call_nested_expr {
19+
() => (nested_expr!())
20+
}
21+
22+
macro_rules! call_nested_expr_sum { //~ NOTE in expansion of
23+
() => { 1 + nested_expr!(); } //~ ERROR unresolved name
24+
}
25+
26+
fn main() {
27+
1 + call_nested_expr!(); //~ ERROR unresolved name
28+
call_nested_expr_sum!(); //~ NOTE expansion site
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// The `format_args!` syntax extension issues errors before code expansion
12+
// has completed, but we still need a backtrace.
13+
14+
// This test includes stripped-down versions of `print!` and `println!`,
15+
// because we can't otherwise verify the lines of the backtrace.
16+
17+
fn print(_args: std::fmt::Arguments) {}
18+
19+
macro_rules! myprint { //~ NOTE in expansion of
20+
($($arg:tt)*) => (print(format_args!($($arg)*)));
21+
}
22+
23+
macro_rules! myprintln { //~ NOTE in expansion of
24+
($fmt:expr) => (myprint!(concat!($fmt, "\n"))); //~ ERROR invalid reference to argument `0`
25+
}
26+
27+
fn main() {
28+
myprintln!("{}"); //~ NOTE expansion site
29+
}

0 commit comments

Comments
 (0)