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 1/3] 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 d2e51c9cb4868..dc0848176d6c8 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1046,6 +1046,7 @@ impl<'feat> ExpansionConfig<'feat> { } // 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 f786b1abb8a1b..c7aab95e1d498 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 74fa85d130b02..6fdcadd1dde1e 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -128,7 +128,7 @@ impl 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 ae3edfcbf325d..553cac80d8222 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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { 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 8eee25405df6b..0014fd5e937e0 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -131,6 +131,15 @@ impl TokenTree { } } + /// 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 @@ impl PartialEq for TokenStream { } 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 @@ impl TokenStream { } } + 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 a7c247689cce8..49684acb4a2d9 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -100,6 +100,7 @@ impl 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 @@ impl Span { 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 @@ impl 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 0000000000000..d779bec4ace53 --- /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 0000000000000..e7336697f48e7 --- /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 + From 09c712dae3e34c34b2d0c9511c8c492b2b1b4b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 20 Jul 2017 21:28:53 -0700 Subject: [PATCH 2/3] Fix test --- src/test/ui/did_you_mean/issue-31424.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/did_you_mean/issue-31424.stderr b/src/test/ui/did_you_mean/issue-31424.stderr index 023f9ef1defb7..47dc7c975724a 100644 --- a/src/test/ui/did_you_mean/issue-31424.stderr +++ b/src/test/ui/did_you_mean/issue-31424.stderr @@ -11,7 +11,7 @@ error[E0596]: cannot borrow immutable argument `self` as mutable --> $DIR/issue-31424.rs:23:15 | 22 | fn bar(self: &mut Self) { - | ---- consider changing this to `mut self` + | --------------- consider changing this to `mut self: &mut Self` 23 | (&mut self).bar(); | ^^^^ cannot borrow mutably From 6772661f37d6a6f88b95808fe957b8c6c15371f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 21 Jul 2017 16:44:23 -0700 Subject: [PATCH 3/3] Review comments --- src/libsyntax/ext/tt/macro_rules.rs | 9 +++++---- src/libsyntax/tokenstream.rs | 2 +- src/libsyntax_pos/lib.rs | 31 ++++++++++------------------- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index c7aab95e1d498..8e746676ecd9e 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -120,15 +120,16 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt, _ => cx.span_bug(sp, "malformed macro rhs"), }; + let rhs_spans = rhs.iter().map(|t| t.span()).collect::>(); // rhs has holes ( `$id` and `$(...)` that need filled) - let mut tts = transcribe(cx, Some(named_matches), rhs.clone()); + let mut tts = transcribe(cx, Some(named_matches), rhs); // 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| { + if rhs_spans.len() == tts.len() { + tts = tts.map_enumerated(|i, tt| { let mut tt = tt.clone(); - let mut sp = rhs[i].span(); + let mut sp = rhs_spans[i]; sp.ctxt = tt.span().ctxt; tt.set_span(sp); tt diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index 0014fd5e937e0..747bc7b438554 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -258,7 +258,7 @@ impl TokenStream { } } - pub fn map_pos TokenTree>(self, mut f: F) -> TokenStream { + pub fn map_enumerated TokenTree>(self, mut f: F) -> TokenStream { let mut trees = self.into_trees(); let mut result = Vec::new(); let mut i = 0; diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 49684acb4a2d9..ad5f3845e2be7 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -185,29 +185,18 @@ impl Span { 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) - let ctxt = if self.ctxt == SyntaxContext::empty() { - end.ctxt - } else { - self.ctxt - }; - Span {lo, hi, ctxt} + Span { + lo: cmp::min(self.lo, end.lo), + hi: cmp::max(self.hi, end.hi), + // FIXME(jseyfried): self.ctxt should always equal end.ctxt here (c.f. issue #23480) + ctxt: if self.ctxt == SyntaxContext::empty() { + end.ctxt + } else { + self.ctxt + }, + } } /// Return a `Span` between the end of `self` to the beginning of `end`.