From e99d309c5695a2b5ad1ab44c06fd32ec506cebaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 19 Jul 2017 21:54:01 -0700 Subject: [PATCH] Use the macro structure spans instead of the invocation --- src/libsyntax/ext/expand.rs | 1 + src/libsyntax/ext/tt/macro_rules.rs | 15 ++++++++- src/libsyntax/ext/tt/quoted.rs | 2 +- src/libsyntax/parse/parser.rs | 8 ++--- src/libsyntax/tokenstream.rs | 32 +++++++++++++++++++ src/libsyntax_pos/lib.rs | 28 +++++++++++++--- src/test/ui/span/macro-span-replacement.rs | 19 +++++++++++ .../ui/span/macro-span-replacement.stderr | 11 +++++++ 8 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 src/test/ui/span/macro-span-replacement.rs create mode 100644 src/test/ui/span/macro-span-replacement.stderr diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d2e51c9cb48..dc0848176d6 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1046,6 +1046,7 @@ fn enable_compile_error = compile_error, } // A Marker adds the given mark to the syntax context. +#[derive(Debug)] pub struct Marker(pub Mark); impl Folder for Marker { diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index f786b1abb8a..c7aab95e1d4 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -119,8 +119,21 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt, quoted::TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(), _ => cx.span_bug(sp, "malformed macro rhs"), }; + // rhs has holes ( `$id` and `$(...)` that need filled) - let tts = transcribe(cx, Some(named_matches), rhs); + let mut tts = transcribe(cx, Some(named_matches), rhs.clone()); + + // Replace all the tokens for the corresponding positions in the macro, to maintain + // proper positions in error reporting, while maintaining the macro_backtrace. + if rhs.len() == tts.len() { + tts = tts.map_pos(|i, tt| { + let mut tt = tt.clone(); + let mut sp = rhs[i].span(); + sp.ctxt = tt.span().ctxt; + tt.set_span(sp); + tt + }); + } if cx.trace_macros() { trace_macros_note(cx, sp, format!("to `{}`", tts)); diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index 74fa85d130b..6fdcadd1dde 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -128,7 +128,7 @@ pub fn get_tt(&self, index: usize) -> TokenTree { } } - /// Retrieve the TokenTree's span. + /// Retrieve the `TokenTree`'s span. pub fn span(&self) -> Span { match *self { TokenTree::Token(sp, _) | diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index ae3edfcbf32..553cac80d82 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -150,7 +150,7 @@ fn maybe_append(mut lhs: Vec, rhs: Option>) lhs } -#[derive(Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] enum PrevTokenKind { DocComment, Comma, @@ -6090,8 +6090,7 @@ fn parse_macro_use_or_failure( let (delim, tts) = self.expect_delimited_token_tree()?; if delim != token::Brace { if !self.eat(&token::Semi) { - let prev_span = self.prev_span; - self.span_err(prev_span, + self.span_err(self.prev_span, "macros that expand to items must either \ be surrounded with braces or followed by \ a semicolon"); @@ -6108,8 +6107,7 @@ fn parse_macro_use_or_failure( match visibility { Visibility::Inherited => {} _ => { - let prev_span = self.prev_span; - return Err(self.span_fatal(prev_span, "unmatched visibility `pub`")); + return Err(self.span_fatal(self.prev_span, "unmatched visibility `pub`")); } } diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index 8eee25405df..0014fd5e937 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -131,6 +131,15 @@ pub fn span(&self) -> Span { } } + /// Modify the `TokenTree`'s span inplace. + pub fn set_span(&mut self, span: Span) { + match *self { + TokenTree::Token(ref mut sp, _) | TokenTree::Delimited(ref mut sp, _) => { + *sp = span; + } + } + } + /// Indicates if the stream is a token that is equal to the provided token. pub fn eq_token(&self, t: Token) -> bool { match *self { @@ -190,6 +199,14 @@ fn eq(&self, other: &TokenStream) -> bool { } impl TokenStream { + pub fn len(&self) -> usize { + if let TokenStreamKind::Stream(ref slice) = self.kind { + slice.len() + } else { + 0 + } + } + pub fn empty() -> TokenStream { TokenStream { kind: TokenStreamKind::Empty } } @@ -241,6 +258,21 @@ pub fn as_tree(self) -> (TokenTree, bool /* joint? */) { } } + pub fn map_pos TokenTree>(self, mut f: F) -> TokenStream { + let mut trees = self.into_trees(); + let mut result = Vec::new(); + let mut i = 0; + while let Some(stream) = trees.next_as_stream() { + result.push(match stream.kind { + TokenStreamKind::Tree(tree) => f(i, tree).into(), + TokenStreamKind::JointTree(tree) => f(i, tree).joint(), + _ => unreachable!() + }); + i += 1; + } + TokenStream::concat(result) + } + pub fn map TokenTree>(self, mut f: F) -> TokenStream { let mut trees = self.into_trees(); let mut result = Vec::new(); diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index a7c247689cc..49684acb4a2 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -100,6 +100,7 @@ pub fn substitute_dummy(self, other: Span) -> Span { if self.source_equal(&DUMMY_SP) { other } else { self } } + /// Return true if `self` fully encloses `other`. pub fn contains(self, other: Span) -> bool { self.lo <= other.lo && other.hi <= self.hi } @@ -184,15 +185,32 @@ pub fn macro_backtrace(mut self) -> Vec { result } + pub fn empty_ctxt(&self) -> bool { + self.ctxt == SyntaxContext::empty() + } + + /// Return a `Span` that would enclose both `self` and `end`. pub fn to(self, end: Span) -> Span { + let lo = if self.lo < end.lo { + self.lo + } else { + end.lo + }; + let hi = if self.hi > end.hi { + self.hi + } else { + end.hi + }; // FIXME(jseyfried): self.ctxt should always equal end.ctxt here (c.f. issue #23480) - if self.ctxt == SyntaxContext::empty() { - Span { lo: self.lo, ..end } + let ctxt = if self.ctxt == SyntaxContext::empty() { + end.ctxt } else { - Span { hi: end.hi, ..self } - } + self.ctxt + }; + Span {lo, hi, ctxt} } + /// Return a `Span` between the end of `self` to the beginning of `end`. pub fn between(self, end: Span) -> Span { Span { lo: self.hi, @@ -205,6 +223,7 @@ pub fn between(self, end: Span) -> Span { } } + /// Return a `Span` between the beginning of `self` to the beginning of `end`. pub fn until(self, end: Span) -> Span { Span { lo: self.lo, @@ -852,6 +871,7 @@ pub struct FileLines { thread_local!(pub static SPAN_DEBUG: Cell fmt::Result> = Cell::new(default_span_debug)); +#[derive(Debug)] pub struct MacroBacktrace { /// span where macro was applied to generate this code pub call_site: Span, diff --git a/src/test/ui/span/macro-span-replacement.rs b/src/test/ui/span/macro-span-replacement.rs new file mode 100644 index 00000000000..d779bec4ace --- /dev/null +++ b/src/test/ui/span/macro-span-replacement.rs @@ -0,0 +1,19 @@ +// Copyright 2017 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. + +macro_rules! m { + ($a:tt $b:tt) => { + $b $a; + } +} + +fn main() { + m!(S struct); +} diff --git a/src/test/ui/span/macro-span-replacement.stderr b/src/test/ui/span/macro-span-replacement.stderr new file mode 100644 index 00000000000..e7336697f48 --- /dev/null +++ b/src/test/ui/span/macro-span-replacement.stderr @@ -0,0 +1,11 @@ +warning: struct is never used: `S` + --> $DIR/macro-span-replacement.rs:13:9 + | +13 | $b $a; + | ^^^^^^ +... +18 | m!(S struct); + | ------------- in this macro invocation + | + = note: #[warn(dead_code)] on by default + -- GitLab