diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 41ccd88b4a8..c8f1708a38c 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -221,6 +221,21 @@ impl Span { } } + /// The `Span` for the tokens in the previous macro expansion from which + /// `self` was generated from, if any. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn parent(&self) -> Option { + self.0.ctxt().outer().expn_info().map(|i| Span(i.call_site)) + } + + /// The span for the origin source code that `self` was generated from. If + /// this `Span` wasn't generated from other macro expansions then the return + /// value is the same as `*self`. + #[unstable(feature = "proc_macro", issue = "38356")] + pub fn source(&self) -> Span { + Span(self.0.source_callsite()) + } + /// Get the starting line/column in the source file for this span. #[unstable(feature = "proc_macro", issue = "38356")] pub fn start(&self) -> LineColumn { diff --git a/src/test/ui-fulldeps/proc-macro/auxiliary/parent-source-spans.rs b/src/test/ui-fulldeps/proc-macro/auxiliary/parent-source-spans.rs new file mode 100644 index 00000000000..3eb96c2ab96 --- /dev/null +++ b/src/test/ui-fulldeps/proc-macro/auxiliary/parent-source-spans.rs @@ -0,0 +1,51 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// no-prefer-dynamic +#![feature(proc_macro)] +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree, TokenNode, Span}; + +fn lit_span(tt: TokenTree) -> (Span, String) { + use TokenNode::*; + match tt.kind { + Literal(..) | Group(..) => (tt.span, tt.to_string().trim().into()), + _ => panic!("expected a literal in token tree, got: {:?}", tt) + } +} + +#[proc_macro] +pub fn parent_source_spans(input: TokenStream) -> TokenStream { + let mut tokens = input.into_iter(); + let (sp1, str1) = lit_span(tokens.next().expect("first string")); + let _ = tokens.next(); + let (sp2, str2) = lit_span(tokens.next().expect("second string")); + + sp1.error(format!("first final: {}", str1)).emit(); + sp2.error(format!("second final: {}", str2)).emit(); + + if let (Some(p1), Some(p2)) = (sp1.parent(), sp2.parent()) { + p1.error(format!("first parent: {}", str1)).emit(); + p2.error(format!("second parent: {}", str2)).emit(); + + if let (Some(gp1), Some(gp2)) = (p1.parent(), p2.parent()) { + gp1.error(format!("first grandparent: {}", str1)).emit(); + gp2.error(format!("second grandparent: {}", str2)).emit(); + } + } + + sp1.source().error(format!("first source: {}", str1)).emit(); + sp2.source().error(format!("second source: {}", str2)).emit(); + + "ok".parse().unwrap() +} diff --git a/src/test/ui-fulldeps/proc-macro/parent-source-spans.rs b/src/test/ui-fulldeps/proc-macro/parent-source-spans.rs new file mode 100644 index 00000000000..4c71afbac4d --- /dev/null +++ b/src/test/ui-fulldeps/proc-macro/parent-source-spans.rs @@ -0,0 +1,61 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:parent-source-spans.rs +// ignore-stage1 + +#![feature(proc_macro, decl_macro)] + +extern crate parent_source_spans; + +use parent_source_spans::parent_source_spans; + +macro one($a:expr, $b:expr) { + two!($a, $b); + //~^ ERROR first parent: "hello" + //~| ERROR second parent: "world" +} + +macro two($a:expr, $b:expr) { + three!($a, $b); + //~^ ERROR first final: "hello" + //~| ERROR second final: "world" + //~| ERROR first final: "yay" + //~| ERROR second final: "rust" +} + +// forwarding tokens directly doesn't create a new source chain +macro three($($tokens:tt)*) { + four!($($tokens)*); +} + +macro four($($tokens:tt)*) { + parent_source_spans!($($tokens)*); +} + +fn main() { + one!("hello", "world"); + //~^ ERROR first grandparent: "hello" + //~| ERROR second grandparent: "world" + //~| ERROR first source: "hello" + //~| ERROR second source: "world" + + two!("yay", "rust"); + //~^ ERROR first parent: "yay" + //~| ERROR second parent: "rust" + //~| ERROR first source: "yay" + //~| ERROR second source: "rust" + + three!("hip", "hop"); + //~^ ERROR first final: "hip" + //~| ERROR second final: "hop" + //~| ERROR first source: "hip" + //~| ERROR second source: "hop" +} diff --git a/src/test/ui-fulldeps/proc-macro/parent-source-spans.stderr b/src/test/ui-fulldeps/proc-macro/parent-source-spans.stderr new file mode 100644 index 00000000000..7194b05b18e --- /dev/null +++ b/src/test/ui-fulldeps/proc-macro/parent-source-spans.stderr @@ -0,0 +1,128 @@ +error: first final: "hello" + --> $DIR/parent-source-spans.rs:27:12 + | +27 | three!($a, $b); + | ^^ +... +44 | one!("hello", "world"); + | ----------------------- in this macro invocation + +error: second final: "world" + --> $DIR/parent-source-spans.rs:27:16 + | +27 | three!($a, $b); + | ^^ +... +44 | one!("hello", "world"); + | ----------------------- in this macro invocation + +error: first parent: "hello" + --> $DIR/parent-source-spans.rs:21:5 + | +21 | two!($a, $b); + | ^^^^^^^^^^^^^ +... +44 | one!("hello", "world"); + | ----------------------- in this macro invocation + +error: second parent: "world" + --> $DIR/parent-source-spans.rs:21:5 + | +21 | two!($a, $b); + | ^^^^^^^^^^^^^ +... +44 | one!("hello", "world"); + | ----------------------- in this macro invocation + +error: first grandparent: "hello" + --> $DIR/parent-source-spans.rs:44:5 + | +44 | one!("hello", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: second grandparent: "world" + --> $DIR/parent-source-spans.rs:44:5 + | +44 | one!("hello", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: first source: "hello" + --> $DIR/parent-source-spans.rs:44:5 + | +44 | one!("hello", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: second source: "world" + --> $DIR/parent-source-spans.rs:44:5 + | +44 | one!("hello", "world"); + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: first final: "yay" + --> $DIR/parent-source-spans.rs:27:12 + | +27 | three!($a, $b); + | ^^ +... +50 | two!("yay", "rust"); + | -------------------- in this macro invocation + +error: second final: "rust" + --> $DIR/parent-source-spans.rs:27:16 + | +27 | three!($a, $b); + | ^^ +... +50 | two!("yay", "rust"); + | -------------------- in this macro invocation + +error: first parent: "yay" + --> $DIR/parent-source-spans.rs:50:5 + | +50 | two!("yay", "rust"); + | ^^^^^^^^^^^^^^^^^^^^ + +error: second parent: "rust" + --> $DIR/parent-source-spans.rs:50:5 + | +50 | two!("yay", "rust"); + | ^^^^^^^^^^^^^^^^^^^^ + +error: first source: "yay" + --> $DIR/parent-source-spans.rs:50:5 + | +50 | two!("yay", "rust"); + | ^^^^^^^^^^^^^^^^^^^^ + +error: second source: "rust" + --> $DIR/parent-source-spans.rs:50:5 + | +50 | two!("yay", "rust"); + | ^^^^^^^^^^^^^^^^^^^^ + +error: first final: "hip" + --> $DIR/parent-source-spans.rs:56:12 + | +56 | three!("hip", "hop"); + | ^^^^^ + +error: second final: "hop" + --> $DIR/parent-source-spans.rs:56:19 + | +56 | three!("hip", "hop"); + | ^^^^^ + +error: first source: "hip" + --> $DIR/parent-source-spans.rs:56:12 + | +56 | three!("hip", "hop"); + | ^^^^^ + +error: second source: "hop" + --> $DIR/parent-source-spans.rs:56:19 + | +56 | three!("hip", "hop"); + | ^^^^^ + +error: aborting due to 18 previous errors +