From b95bde44913852685b350d468d000453ece9696f Mon Sep 17 00:00:00 2001 From: "@amit.chandra" <@amit.chandra> Date: Fri, 31 May 2019 15:09:17 +0530 Subject: squash of all commits for nth_back on ChunksMut wip nth_back for chunks_mut working chunksmut fixed nth_back for chunksmut Signed-off-by: wizAmit --- src/libcore/slice/mod.rs | 19 +++++++++++++++++++ src/libcore/tests/slice.rs | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index c6d44324ef5..e7d175c44c9 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -4330,6 +4330,25 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { Some(tail) } } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let len = self.len(); + if n >= len { + self.v = &mut []; + None + } else { + let start = (len - 1 - n) * self.chunk_size; + let end = match start.checked_add(self.chunk_size) { + Some(res) => cmp::min(res, self.v.len()), + None => self.v.len(), + }; + let (temp, _tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end); + let (head, nth_back) = temp.split_at_mut(start); + self.v = head; + Some(nth_back) + } + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 03e65d2fe0b..0519e579c98 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -222,6 +222,28 @@ fn test_chunks_mut_nth() { assert_eq!(c2.next(), None); } +#[test] +fn test_chunks_mut_nth_back() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.chunks_mut(2); + assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[0, 1]); + + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c1 = v1.chunks_mut(3); + assert_eq!(c1.nth_back(1).unwrap(), &[0, 1, 2]); + assert_eq!(c1.next(), None); + + let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let mut c3 = v3.chunks_mut(10); + assert_eq!(c3.nth_back(0).unwrap(), &[0, 1, 2, 3, 4]); + assert_eq!(c3.next(), None); + + let v4: &mut [i32] = &mut [0, 1, 2]; + let mut c4 = v4.chunks_mut(10); + assert_eq!(c4.nth_back(1_000_000_000usize), None); +} + #[test] fn test_chunks_mut_last() { let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; -- cgit 1.4.1-3-g733a5 From 424220676e29fb67222e9aecf4d5a906948029cc Mon Sep 17 00:00:00 2001 From: Mateusz MikuĊ‚a Date: Fri, 19 Jul 2019 22:03:20 +0200 Subject: Re-enable assertions in PPC dist builder --- src/ci/docker/dist-powerpc-linux/Dockerfile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ci/docker/dist-powerpc-linux/Dockerfile b/src/ci/docker/dist-powerpc-linux/Dockerfile index f03aff060c1..8c052db1b0d 100644 --- a/src/ci/docker/dist-powerpc-linux/Dockerfile +++ b/src/ci/docker/dist-powerpc-linux/Dockerfile @@ -36,7 +36,3 @@ ENV HOSTS=powerpc-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS - -# FIXME(#36150) this will fail the bootstrap. Probably means something bad is -# happening! -ENV NO_LLVM_ASSERTIONS 1 -- cgit 1.4.1-3-g733a5 From 3334802c83070d63b85b5b2e5508c15bfaf6e254 Mon Sep 17 00:00:00 2001 From: Stargateur Date: Tue, 23 Jul 2019 00:55:18 +0200 Subject: Refactoring use commun code between option, result and accum --- src/libcore/iter/adapters/mod.rs | 135 ++++++++++++++++++++++++++++++++++++++ src/libcore/iter/mod.rs | 2 +- src/libcore/iter/traits/accum.rs | 136 +-------------------------------------- src/libcore/option.rs | 42 +----------- src/libcore/result.rs | 36 +---------- 5 files changed, 141 insertions(+), 210 deletions(-) diff --git a/src/libcore/iter/adapters/mod.rs b/src/libcore/iter/adapters/mod.rs index c2edcd22f95..8eae52ffd32 100644 --- a/src/libcore/iter/adapters/mod.rs +++ b/src/libcore/iter/adapters/mod.rs @@ -2062,3 +2062,138 @@ impl ExactSizeIterator for Inspect #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Inspect where F: FnMut(&I::Item) {} + +/// An iterator adapter that produces output as long as the underlying +/// iterator produces `Option::Some` values. +pub(crate) struct OptionShunt { + iter: I, + exited_early: bool, +} + +impl OptionShunt +where + I: Iterator>, +{ + /// Process the given iterator as if it yielded a `T` instead of a + /// `Option`. Any `None` value will stop the inner iterator and + /// the overall result will be a `None`. + pub fn process(iter: I, mut f: F) -> Option + where + F: FnMut(&mut Self) -> U, + { + let mut shunt = OptionShunt::new(iter); + let value = f(shunt.by_ref()); + shunt.reconstruct(value) + } + + fn new(iter: I) -> Self { + OptionShunt { + iter, + exited_early: false, + } + } + + /// Consume the adapter and rebuild a `Option` value. + fn reconstruct(self, val: U) -> Option { + if self.exited_early { + None + } else { + Some(val) + } + } +} + +impl Iterator for OptionShunt +where + I: Iterator>, +{ + type Item = T; + + fn next(&mut self) -> Option { + match self.iter.next() { + Some(Some(v)) => Some(v), + Some(None) => { + self.exited_early = true; + None + } + None => None, + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.exited_early { + (0, Some(0)) + } else { + let (_, upper) = self.iter.size_hint(); + (0, upper) + } + } +} + +/// An iterator adapter that produces output as long as the underlying +/// iterator produces `Result::Ok` values. +/// +/// If an error is encountered, the iterator stops and the error is +/// stored. The error may be recovered later via `reconstruct`. +pub(crate) struct ResultShunt { + iter: I, + error: Option, +} + +impl ResultShunt + where I: Iterator> +{ + /// Process the given iterator as if it yielded a `T` instead of a + /// `Result`. Any errors will stop the inner iterator and + /// the overall result will be an error. + pub fn process(iter: I, mut f: F) -> Result + where F: FnMut(&mut Self) -> U + { + let mut shunt = ResultShunt::new(iter); + let value = f(shunt.by_ref()); + shunt.reconstruct(value) + } + + fn new(iter: I) -> Self { + ResultShunt { + iter, + error: None, + } + } + + /// Consume the adapter and rebuild a `Result` value. This should + /// *always* be called, otherwise any potential error would be + /// lost. + fn reconstruct(self, val: U) -> Result { + match self.error { + None => Ok(val), + Some(e) => Err(e), + } + } +} + +impl Iterator for ResultShunt + where I: Iterator> +{ + type Item = T; + + fn next(&mut self) -> Option { + match self.iter.next() { + Some(Ok(v)) => Some(v), + Some(Err(e)) => { + self.error = Some(e); + None + } + None => None, + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.error.is_some() { + (0, Some(0)) + } else { + let (_, upper) = self.iter.size_hint(); + (0, upper) + } + } +} diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 6eccb9d1ea8..4a7d7f96b9b 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -360,7 +360,7 @@ pub use self::adapters::Flatten; #[stable(feature = "iter_copied", since = "1.36.0")] pub use self::adapters::Copied; -pub(crate) use self::adapters::TrustedRandomAccess; +pub(crate) use self::adapters::{TrustedRandomAccess, OptionShunt, ResultShunt}; mod range; mod sources; diff --git a/src/libcore/iter/traits/accum.rs b/src/libcore/iter/traits/accum.rs index adfb639bae3..01b64fb08ac 100644 --- a/src/libcore/iter/traits/accum.rs +++ b/src/libcore/iter/traits/accum.rs @@ -1,5 +1,6 @@ use crate::ops::{Mul, Add}; use crate::num::Wrapping; +use crate::iter::adapters::{OptionShunt, ResultShunt}; /// Trait to represent types that can be created by summing up an iterator. /// @@ -114,74 +115,6 @@ macro_rules! float_sum_product { integer_sum_product! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } float_sum_product! { f32 f64 } -/// An iterator adapter that produces output as long as the underlying -/// iterator produces `Result::Ok` values. -/// -/// If an error is encountered, the iterator stops and the error is -/// stored. The error may be recovered later via `reconstruct`. -struct ResultShunt { - iter: I, - error: Option, -} - -impl ResultShunt - where I: Iterator> -{ - /// Process the given iterator as if it yielded a `T` instead of a - /// `Result`. Any errors will stop the inner iterator and - /// the overall result will be an error. - pub fn process(iter: I, mut f: F) -> Result - where F: FnMut(&mut Self) -> U - { - let mut shunt = ResultShunt::new(iter); - let value = f(shunt.by_ref()); - shunt.reconstruct(value) - } - - fn new(iter: I) -> Self { - ResultShunt { - iter, - error: None, - } - } - - /// Consume the adapter and rebuild a `Result` value. This should - /// *always* be called, otherwise any potential error would be - /// lost. - fn reconstruct(self, val: U) -> Result { - match self.error { - None => Ok(val), - Some(e) => Err(e), - } - } -} - -impl Iterator for ResultShunt - where I: Iterator> -{ - type Item = T; - - fn next(&mut self) -> Option { - match self.iter.next() { - Some(Ok(v)) => Some(v), - Some(Err(e)) => { - self.error = Some(e); - None - } - None => None, - } - } - - fn size_hint(&self) -> (usize, Option) { - if self.error.is_some() { - (0, Some(0)) - } else { - let (_, upper) = self.iter.size_hint(); - (0, upper) - } - } -} - #[stable(feature = "iter_arith_traits_result", since="1.16.0")] impl Sum> for Result where T: Sum, @@ -224,73 +157,6 @@ impl Product> for Result } } -/// An iterator adapter that produces output as long as the underlying -/// iterator produces `Option::Some` values. -struct OptionShunt { - iter: I, - exited_early: bool, -} - -impl OptionShunt -where - I: Iterator>, -{ - /// Process the given iterator as if it yielded a `T` instead of a - /// `Option`. Any `None` value will stop the inner iterator and - /// the overall result will be a `None`. - pub fn process(iter: I, mut f: F) -> Option - where - F: FnMut(&mut Self) -> U, - { - let mut shunt = OptionShunt::new(iter); - let value = f(shunt.by_ref()); - shunt.reconstruct(value) - } - - fn new(iter: I) -> Self { - OptionShunt { - iter, - exited_early: false, - } - } - - /// Consume the adapter and rebuild a `Option` value. - fn reconstruct(self, val: U) -> Option { - if self.exited_early { - None - } else { - Some(val) - } - } -} - -impl Iterator for OptionShunt -where - I: Iterator>, -{ - type Item = T; - - fn next(&mut self) -> Option { - match self.iter.next() { - Some(Some(v)) => Some(v), - Some(None) => { - self.exited_early = true; - None - } - None => None, - } - } - - fn size_hint(&self) -> (usize, Option) { - if self.exited_early { - (0, Some(0)) - } else { - let (_, upper) = self.iter.size_hint(); - (0, upper) - } - } -} - #[stable(feature = "iter_arith_traits_option", since = "1.37.0")] impl Sum> for Option where diff --git a/src/libcore/option.rs b/src/libcore/option.rs index abc8883d398..70a87cfe5a7 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -135,7 +135,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::iter::{FromIterator, FusedIterator, TrustedLen}; +use crate::iter::{FromIterator, FusedIterator, TrustedLen, OptionShunt}; use crate::{convert, fmt, hint, mem, ops::{self, Deref, DerefMut}}; use crate::pin::Pin; @@ -1499,45 +1499,7 @@ impl> FromIterator> for Option { // FIXME(#11084): This could be replaced with Iterator::scan when this // performance bug is closed. - struct Adapter { - iter: Iter, - found_none: bool, - } - - impl>> Iterator for Adapter { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - match self.iter.next() { - Some(Some(value)) => Some(value), - Some(None) => { - self.found_none = true; - None - } - None => None, - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - if self.found_none { - (0, Some(0)) - } else { - let (_, upper) = self.iter.size_hint(); - (0, upper) - } - } - } - - let mut adapter = Adapter { iter: iter.into_iter(), found_none: false }; - let v: V = FromIterator::from_iter(adapter.by_ref()); - - if adapter.found_none { - None - } else { - Some(v) - } + OptionShunt::process(iter.into_iter(), |i| i.collect()) } } diff --git a/src/libcore/result.rs b/src/libcore/result.rs index cb6bc058730..559877ddd5a 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -231,7 +231,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::fmt; -use crate::iter::{FromIterator, FusedIterator, TrustedLen}; +use crate::iter::{FromIterator, FusedIterator, TrustedLen, ResultShunt}; use crate::ops::{self, Deref, DerefMut}; /// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]). @@ -1343,39 +1343,7 @@ impl> FromIterator> for Result { // FIXME(#11084): This could be replaced with Iterator::scan when this // performance bug is closed. - struct Adapter { - iter: Iter, - err: Option, - } - - impl>> Iterator for Adapter { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - match self.iter.next() { - Some(Ok(value)) => Some(value), - Some(Err(err)) => { - self.err = Some(err); - None - } - None => None, - } - } - - fn size_hint(&self) -> (usize, Option) { - let (_min, max) = self.iter.size_hint(); - (0, max) - } - } - - let mut adapter = Adapter { iter: iter.into_iter(), err: None }; - let v: V = FromIterator::from_iter(adapter.by_ref()); - - match adapter.err { - Some(err) => Err(err), - None => Ok(v), - } + ResultShunt::process(iter.into_iter(), |i| i.collect()) } } -- cgit 1.4.1-3-g733a5 From e2eb957be0f6c31585c31815e6c423e7c1d75856 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 27 May 2019 01:43:12 +0900 Subject: Allow lifetime elision in `Pin<&(mut) Self>` --- src/librustc/middle/resolve_lifetime.rs | 29 ++++++++++- .../ui/self/arbitrary_self_types_pin_lifetime.rs | 60 ++++++++++++++++++++++ ...arbitrary_self_types_pin_lifetime_impl_trait.rs | 13 +++++ ...trary_self_types_pin_lifetime_impl_trait.stderr | 20 ++++++++ .../arbitrary_self_types_pin_lifetime_mismatch.rs | 13 +++++ ...bitrary_self_types_pin_lifetime_mismatch.stderr | 18 +++++++ src/test/ui/self/self_lifetime.rs | 13 +++++ 7 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime.rs create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr create mode 100644 src/test/ui/self/self_lifetime.rs diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index abb896a7c9b..0e99190c20c 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -2174,7 +2174,34 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { false }; - if let hir::TyKind::Rptr(lifetime_ref, ref mt) = inputs[0].node { + let mut self_arg = &inputs[0].node; + + // Apply `self: &(mut) Self` elision rules even if nested in `Pin`. + loop { + if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = *self_arg { + if let Res::Def(DefKind::Struct, def_id) = path.res { + if self.tcx.lang_items().pin_type() == Some(def_id) { + if let Some(args) = path + .segments + .last() + .and_then(|segment| segment.args.as_ref()) + { + if args.args.len() == 1 { + if let GenericArg::Type(ty) = &args.args[0] { + self_arg = &ty.node; + // Keep dereferencing `self_arg` until we get to non-`Pin` + // types. + continue; + } + } + } + } + } + } + break; + } + + if let hir::TyKind::Rptr(lifetime_ref, ref mt) = *self_arg { if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node { if is_self_ty(path.res) { if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs new file mode 100644 index 00000000000..668aaf7166a --- /dev/null +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs @@ -0,0 +1,60 @@ +// compile-pass + +use std::pin::Pin; +use std::task::{Context, Poll}; + +struct Foo; + +impl Foo { + fn pin_ref(self: Pin<&Self>) -> Pin<&Self> { self } + + fn pin_mut(self: Pin<&mut Self>) -> Pin<&mut Self> { self } + + fn pin_pin_pin_ref(self: Pin>>) -> Pin>> { self } + + fn pin_ref_impl_trait(self: Pin<&Self>) -> impl Clone + '_ { self } + + fn b(self: Pin<&Foo>, f: &Foo) -> Pin<&Foo> { self } +} + +type Alias = Pin; +impl Foo { + fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } +} + +struct Bar { + field1: T, + field2: U, +} + +impl Bar { + fn fields(self: Pin<&mut Self>) -> (Pin<&mut T>, Pin<&mut U>) { + let this = self.get_mut(); + (Pin::new(&mut this.field1), Pin::new(&mut this.field2)) + } +} + +trait AsyncBufRead { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) + -> Poll>; +} + +struct Baz(Vec); + +impl AsyncBufRead for Baz { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) + -> Poll> + { + Poll::Ready(Ok(&self.get_mut().0)) + } +} + +fn main() { + let mut foo = Foo; + { Pin::new(&foo).pin_ref() }; + { Pin::new(&mut foo).pin_mut() }; + { Pin::new(Pin::new(Pin::new(&foo))).pin_pin_pin_ref() }; + { Pin::new(&foo).pin_ref_impl_trait() }; + let mut bar = Bar { field1: 0u8, field2: 1u8 }; + { Pin::new(&mut bar).fields() }; +} diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs new file mode 100644 index 00000000000..ad8959727cb --- /dev/null +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.rs @@ -0,0 +1,13 @@ +// compile-fail + +use std::pin::Pin; + +struct Foo; + +impl Foo { + fn f(self: Pin<&Self>) -> impl Clone { self } //~ ERROR cannot infer an appropriate lifetime +} + +fn main() { + { Pin::new(&Foo).f() }; +} diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr new file mode 100644 index 00000000000..5118280e7ec --- /dev/null +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.stderr @@ -0,0 +1,20 @@ +error: cannot infer an appropriate lifetime + --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:8:44 + | +LL | fn f(self: Pin<&Self>) -> impl Clone { self } + | ---------- ^^^^ ...but this borrow... + | | + | this return type evaluates to the `'static` lifetime... + | +note: ...can't outlive the anonymous lifetime #1 defined on the method body at 8:5 + --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:8:5 + | +LL | fn f(self: Pin<&Self>) -> impl Clone { self } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: you can add a constraint to the return type to make it last less than `'static` and match the anonymous lifetime #1 defined on the method body at 8:5 + | +LL | fn f(self: Pin<&Self>) -> impl Clone + '_ { self } + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs new file mode 100644 index 00000000000..3ed5e6bdd72 --- /dev/null +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs @@ -0,0 +1,13 @@ +// compile-fail + +use std::pin::Pin; + +struct Foo; + +impl Foo { + fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f } //~ ERROR E0623 + + fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } //~ ERROR E0623 +} + +fn main() {} diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr new file mode 100644 index 00000000000..6e345b03056 --- /dev/null +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr @@ -0,0 +1,18 @@ +error[E0623]: lifetime mismatch + --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:8:46 + | +LL | fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f } + | ---- ---- ^ ...but data from `f` is returned here + | | + | this parameter and the return type are declared with different lifetimes... + +error[E0623]: lifetime mismatch + --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:10:76 + | +LL | fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } + | ---- ----------------- ^ ...but data from `f` is returned here + | | + | this parameter and the return type are declared with different lifetimes... + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/self/self_lifetime.rs b/src/test/ui/self/self_lifetime.rs new file mode 100644 index 00000000000..a3163ade040 --- /dev/null +++ b/src/test/ui/self/self_lifetime.rs @@ -0,0 +1,13 @@ +// compile-pass + +struct Foo<'a>(&'a ()); +impl<'a> Foo<'a> { + fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } +} + +type Alias = Foo<'static>; +impl Alias { + fn bar<'a>(self: &Alias, arg: &'a ()) -> &() { arg } +} + +fn main() {} -- cgit 1.4.1-3-g733a5 From a1fd4fa848a11698b9a21bc937f5846dff87920e Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 27 May 2019 22:06:08 +0900 Subject: Remove query for `.pin_type()` --- src/librustc/middle/resolve_lifetime.rs | 66 +++++++++++----------- .../ui/self/arbitrary_self_types_pin_lifetime.rs | 2 +- .../arbitrary_self_types_pin_lifetime_mismatch.rs | 5 ++ ...bitrary_self_types_pin_lifetime_mismatch.stderr | 10 +++- 4 files changed, 47 insertions(+), 36 deletions(-) diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 0e99190c20c..d4b46e2003f 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -2174,46 +2174,44 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { false }; - let mut self_arg = &inputs[0].node; - - // Apply `self: &(mut) Self` elision rules even if nested in `Pin`. - loop { - if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = *self_arg { - if let Res::Def(DefKind::Struct, def_id) = path.res { - if self.tcx.lang_items().pin_type() == Some(def_id) { - if let Some(args) = path - .segments - .last() - .and_then(|segment| segment.args.as_ref()) - { - if args.args.len() == 1 { - if let GenericArg::Type(ty) = &args.args[0] { - self_arg = &ty.node; - // Keep dereferencing `self_arg` until we get to non-`Pin` - // types. - continue; - } - } + struct SelfVisitor<'a, F: FnMut(Res) -> bool> { + is_self_ty: F, + map: &'a NamedRegionMap, + lifetime: Option, + } + + impl<'a, F: FnMut(Res) -> bool> Visitor<'a> for SelfVisitor<'a, F> { + fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> { + NestedVisitorMap::None + } + + fn visit_ty(&mut self, ty: &'a hir::Ty) { + if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.node { + if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node + { + if (self.is_self_ty)(path.res) { + self.lifetime = self.map.defs.get(&lifetime_ref.hir_id).copied(); + return; } } } + intravisit::walk_ty(self, ty) } - break; } - if let hir::TyKind::Rptr(lifetime_ref, ref mt) = *self_arg { - if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node { - if is_self_ty(path.res) { - if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { - let scope = Scope::Elision { - elide: Elide::Exact(lifetime), - s: self.scope, - }; - self.with(scope, |_, this| this.visit_ty(output)); - return; - } - } - } + let mut visitor = SelfVisitor { + is_self_ty, + map: self.map, + lifetime: None, + }; + visitor.visit_ty(&inputs[0]); + if let Some(lifetime) = visitor.lifetime { + let scope = Scope::Elision { + elide: Elide::Exact(lifetime), + s: self.scope, + }; + self.with(scope, |_, this| this.visit_ty(output)); + return; } } diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs index 668aaf7166a..ba574eeb460 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs @@ -19,7 +19,7 @@ impl Foo { type Alias = Pin; impl Foo { - fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } + fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> Alias<&Self> { self } } struct Bar { diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs index 3ed5e6bdd72..fc5f94201b8 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.rs @@ -10,4 +10,9 @@ impl Foo { fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } //~ ERROR E0623 } +type Alias = Pin; +impl Foo { + fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } //~ ERROR E0623 +} + fn main() {} diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr index 6e345b03056..3296e14f806 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.stderr @@ -14,5 +14,13 @@ LL | fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, | | | this parameter and the return type are declared with different lifetimes... -error: aborting due to 2 previous errors +error[E0623]: lifetime mismatch + --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:15:58 + | +LL | fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } + | ------ --- ^^^ ...but data from `arg` is returned here + | | + | this parameter and the return type are declared with different lifetimes... + +error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From 7b9a65ea6a3d699c2b43b7c7896307c0475a1ada Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 29 May 2019 17:37:28 +0900 Subject: Make is_self_ty a method on SelfVisitor --- src/librustc/middle/resolve_lifetime.rs | 67 +++++++++++++++++---------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index d4b46e2003f..d7dd0656cb8 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -2146,41 +2146,44 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // First (determined here), if `self` is by-reference, then the // implied output region is the region of the self parameter. if has_self { - // Look for `self: &'a Self` - also desugared from `&'a self`, - // and if that matches, use it for elision and return early. - let is_self_ty = |res: Res| { - if let Res::SelfTy(..) = res { - return true; - } - - // Can't always rely on literal (or implied) `Self` due - // to the way elision rules were originally specified. - let impl_self = impl_self.map(|ty| &ty.node); - if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) = impl_self { - match path.res { - // Whitelist the types that unambiguously always - // result in the same type constructor being used - // (it can't differ between `Self` and `self`). - Res::Def(DefKind::Struct, _) - | Res::Def(DefKind::Union, _) - | Res::Def(DefKind::Enum, _) - | Res::PrimTy(_) => { - return res == path.res - } - _ => {} + struct SelfVisitor<'a> { + map: &'a NamedRegionMap, + impl_self: Option<&'a hir::TyKind>, + lifetime: Option, + } + + impl SelfVisitor<'_> { + // Look for `self: &'a Self` - also desugared from `&'a self`, + // and if that matches, use it for elision and return early. + fn is_self_ty(&self, res: Res) -> bool { + if let Res::SelfTy(..) = res { + return true; } - } - false - }; + // Can't always rely on literal (or implied) `Self` due + // to the way elision rules were originally specified. + if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) = + self.impl_self + { + match path.res { + // Whitelist the types that unambiguously always + // result in the same type constructor being used + // (it can't differ between `Self` and `self`). + Res::Def(DefKind::Struct, _) + | Res::Def(DefKind::Union, _) + | Res::Def(DefKind::Enum, _) + | Res::PrimTy(_) => { + return res == path.res + } + _ => {} + } + } - struct SelfVisitor<'a, F: FnMut(Res) -> bool> { - is_self_ty: F, - map: &'a NamedRegionMap, - lifetime: Option, + false + } } - impl<'a, F: FnMut(Res) -> bool> Visitor<'a> for SelfVisitor<'a, F> { + impl<'a> Visitor<'a> for SelfVisitor<'a> { fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'a> { NestedVisitorMap::None } @@ -2189,7 +2192,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.node { if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node { - if (self.is_self_ty)(path.res) { + if self.is_self_ty(path.res) { self.lifetime = self.map.defs.get(&lifetime_ref.hir_id).copied(); return; } @@ -2200,8 +2203,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } let mut visitor = SelfVisitor { - is_self_ty, map: self.map, + impl_self: impl_self.map(|ty| &ty.node), lifetime: None, }; visitor.visit_ty(&inputs[0]); -- cgit 1.4.1-3-g733a5 From 2f64404ba34c525d7fdaa1a5dfdf784cdc2806a1 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Wed, 29 May 2019 18:50:24 +0900 Subject: Use Set1 instead of Option --- src/librustc/middle/resolve_lifetime.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index d7dd0656cb8..e2b1b54cef3 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -2149,7 +2149,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { struct SelfVisitor<'a> { map: &'a NamedRegionMap, impl_self: Option<&'a hir::TyKind>, - lifetime: Option, + lifetime: Set1, } impl SelfVisitor<'_> { @@ -2193,8 +2193,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.node { if self.is_self_ty(path.res) { - self.lifetime = self.map.defs.get(&lifetime_ref.hir_id).copied(); - return; + if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) { + self.lifetime.insert(*lifetime); + } } } } @@ -2205,10 +2206,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let mut visitor = SelfVisitor { map: self.map, impl_self: impl_self.map(|ty| &ty.node), - lifetime: None, + lifetime: Set1::Empty, }; visitor.visit_ty(&inputs[0]); - if let Some(lifetime) = visitor.lifetime { + if let Set1::One(lifetime) = visitor.lifetime { let scope = Scope::Elision { elide: Elide::Exact(lifetime), s: self.scope, -- cgit 1.4.1-3-g733a5 From 30965689045d71d9335b83c1efb429f1a18a9fa9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 28 Jun 2019 14:46:45 -0400 Subject: add a bevy of new test cases --- src/test/ui/self/elision/README.md | 45 +++++++++++++++++++ src/test/ui/self/elision/alias.rs | 32 +++++++++++++ src/test/ui/self/elision/alias.stderr | 7 +++ src/test/ui/self/elision/lt-alias.rs | 38 ++++++++++++++++ src/test/ui/self/elision/lt-ref-self.rs | 38 ++++++++++++++++ src/test/ui/self/elision/lt-ref-self.stderr | 62 ++++++++++++++++++++++++++ src/test/ui/self/elision/lt-self.rs | 49 ++++++++++++++++++++ src/test/ui/self/elision/lt-struct.rs | 36 +++++++++++++++ src/test/ui/self/elision/ref-alias.rs | 39 ++++++++++++++++ src/test/ui/self/elision/ref-mut-alias.rs | 32 +++++++++++++ src/test/ui/self/elision/ref-mut-alias.stderr | 7 +++ src/test/ui/self/elision/ref-mut-self.rs | 40 +++++++++++++++++ src/test/ui/self/elision/ref-mut-self.stderr | 62 ++++++++++++++++++++++++++ src/test/ui/self/elision/ref-mut-struct.rs | 34 ++++++++++++++ src/test/ui/self/elision/ref-mut-struct.stderr | 52 +++++++++++++++++++++ src/test/ui/self/elision/ref-self.rs | 40 +++++++++++++++++ src/test/ui/self/elision/ref-self.stderr | 62 ++++++++++++++++++++++++++ src/test/ui/self/elision/ref-struct.rs | 34 ++++++++++++++ src/test/ui/self/elision/ref-struct.stderr | 52 +++++++++++++++++++++ src/test/ui/self/elision/self.rs | 36 +++++++++++++++ src/test/ui/self/elision/struct.rs | 34 ++++++++++++++ 21 files changed, 831 insertions(+) create mode 100644 src/test/ui/self/elision/README.md create mode 100644 src/test/ui/self/elision/alias.rs create mode 100644 src/test/ui/self/elision/alias.stderr create mode 100644 src/test/ui/self/elision/lt-alias.rs create mode 100644 src/test/ui/self/elision/lt-ref-self.rs create mode 100644 src/test/ui/self/elision/lt-ref-self.stderr create mode 100644 src/test/ui/self/elision/lt-self.rs create mode 100644 src/test/ui/self/elision/lt-struct.rs create mode 100644 src/test/ui/self/elision/ref-alias.rs create mode 100644 src/test/ui/self/elision/ref-mut-alias.rs create mode 100644 src/test/ui/self/elision/ref-mut-alias.stderr create mode 100644 src/test/ui/self/elision/ref-mut-self.rs create mode 100644 src/test/ui/self/elision/ref-mut-self.stderr create mode 100644 src/test/ui/self/elision/ref-mut-struct.rs create mode 100644 src/test/ui/self/elision/ref-mut-struct.stderr create mode 100644 src/test/ui/self/elision/ref-self.rs create mode 100644 src/test/ui/self/elision/ref-self.stderr create mode 100644 src/test/ui/self/elision/ref-struct.rs create mode 100644 src/test/ui/self/elision/ref-struct.stderr create mode 100644 src/test/ui/self/elision/self.rs create mode 100644 src/test/ui/self/elision/struct.rs diff --git a/src/test/ui/self/elision/README.md b/src/test/ui/self/elision/README.md new file mode 100644 index 00000000000..de29a9d5e53 --- /dev/null +++ b/src/test/ui/self/elision/README.md @@ -0,0 +1,45 @@ +Test cases intended to to document behavior and tryto exhaustively +explore the combinations. + +## Confidence + +These tests are not yet considered 100% normative, in that some +aspects of the current behavior are not desirable. This is expressed +in the "confidence" field in the following table. Values: + +| Confidence | Interpretation | +| --- | --- | +| 100% | this will remain recommended behavior | +| 75% | unclear whether we will continue to accept this | +| 50% | this will likely be deprecated but remain valid | +| 25% | this could change in the future | +| 0% | this is definitely bogus and will likely change in the future in *some* way | + +## Tests + +| Test file | `Self` type | Pattern | Current elision behavior | Confidence | +| --- | --- | --- | --- | --- | +| `self.rs` | `Struct` | `Self` | ignore `self` parameter | 100% | +| `struct.rs` | `Struct` | `Struct` | ignore `self` parameter | 100% | +| `alias.rs` | `Struct` | `Alias` | ignore `self` parameter | 100% | +| `ref-self.rs` | `Struct` | `&Self` | take lifetime from `&Self` | 100% | +| `ref-mut-self.rs` | `Struct` | `&mut Self` | take lifetime from `&Self` | 100% | +| `ref-struct.rs` | `Struct` | `&Struct` | take lifetime from `&Self` | 50% | +| `ref-mut-struct.rs` | `Struct` | `&Struct` | take lifetime from `&Self` | 50% | +| `ref-alias.rs` | `Struct` | `&Alias` | ignore `Alias` | 0% | +| `ref-mut-alias.rs` | `Struct` | `&Alias` | ignore `Alias` | 0% | +| `lt-self.rs` | `Struct<'a>` | `Self` | ignore `Self` (and hence `'a`) | 25% | +| `lt-struct.rs` | `Struct<'a>` | `Self` | ignore `Self` (and hence `'a`) | 0% | +| `lt-alias.rs` | `Alias<'a>` | `Self` | ignore `Self` (and hence `'a`) | 0% | +| `lt-ref-self.rs` | `Struct<'a>` | `&Self` | take lifetime from `&Self` | 75% | + +In each case, we test the following patterns: + +- `self: XXX` +- `self: Box` +- `self: Pin` +- `self: Box>` +- `self: Box>` + +In the non-reference cases, `Pin` causes errors so we substitute `Rc`. + diff --git a/src/test/ui/self/elision/alias.rs b/src/test/ui/self/elision/alias.rs new file mode 100644 index 00000000000..6f113ec1a4b --- /dev/null +++ b/src/test/ui/self/elision/alias.rs @@ -0,0 +1,32 @@ +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::rc::Rc; + +struct Struct { } + +type Alias = Struct; + +impl Struct { + // Test using an alias for `Struct`: + + fn alias(self: Alias, f: &u32) -> &u32 { + f + } + + fn box_Alias(self: Box, f: &u32) -> &u32 { + f + } + + fn rc_Alias(self: Rc, f: &u32) -> &u32 { + f + } + + fn box_box_Alias(self: Box>, f: &u32) -> &u32 { + f + } + + fn box_rc_Alias(self: Box>, f: &u32) -> &u32 { + f + } +} diff --git a/src/test/ui/self/elision/alias.stderr b/src/test/ui/self/elision/alias.stderr new file mode 100644 index 00000000000..a8f2a125b5e --- /dev/null +++ b/src/test/ui/self/elision/alias.stderr @@ -0,0 +1,7 @@ +error[E0601]: `main` function not found in crate `alias` + | + = note: consider adding a `main` function to `$DIR/alias.rs` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0601`. diff --git a/src/test/ui/self/elision/lt-alias.rs b/src/test/ui/self/elision/lt-alias.rs new file mode 100644 index 00000000000..04468278900 --- /dev/null +++ b/src/test/ui/self/elision/lt-alias.rs @@ -0,0 +1,38 @@ +// run-pass + +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::rc::Rc; + +struct Struct<'a> { x: &'a u32 } + +type Alias<'a> = Struct<'a>; + +impl<'a> Alias<'a> { + fn take_self(self, f: &u32) -> &u32 { + f + } + + fn take_Alias(self: Alias<'a>, f: &u32) -> &u32 { + f + } + + fn take_Box_Alias(self: Box>, f: &u32) -> &u32 { + f + } + + fn take_Box_Box_Alias(self: Box>>, f: &u32) -> &u32 { + f + } + + fn take_Rc_Alias(self: Rc>, f: &u32) -> &u32 { + f + } + + fn take_Box_Rc_Alias(self: Box>>, f: &u32) -> &u32 { + f + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/lt-ref-self.rs b/src/test/ui/self/elision/lt-ref-self.rs new file mode 100644 index 00000000000..8abf2876a5c --- /dev/null +++ b/src/test/ui/self/elision/lt-ref-self.rs @@ -0,0 +1,38 @@ +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::pin::Pin; + +struct Struct<'a> { data: &'a u32 } + +impl<'a> Struct<'a> { + // Test using `&self` sugar: + + fn ref_self(&self, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + // Test using `&Self` explicitly: + + fn ref_Self(self: &Self, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_pin_Self(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/lt-ref-self.stderr b/src/test/ui/self/elision/lt-ref-self.stderr new file mode 100644 index 00000000000..f73b3eddd38 --- /dev/null +++ b/src/test/ui/self/elision/lt-ref-self.stderr @@ -0,0 +1,62 @@ +error[E0623]: lifetime mismatch + --> $DIR/lt-ref-self.rs:12:9 + | +LL | fn ref_self(&self, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/lt-ref-self.rs:18:9 + | +LL | fn ref_Self(self: &Self, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/lt-ref-self.rs:22:9 + | +LL | fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/lt-ref-self.rs:26:9 + | +LL | fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/lt-ref-self.rs:30:9 + | +LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/lt-ref-self.rs:34:9 + | +LL | fn box_pin_Self(self: Box>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/self/elision/lt-self.rs b/src/test/ui/self/elision/lt-self.rs new file mode 100644 index 00000000000..c33df08e0ee --- /dev/null +++ b/src/test/ui/self/elision/lt-self.rs @@ -0,0 +1,49 @@ +// run-pass + +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::pin::Pin; +use std::rc::Rc; + +struct Struct<'a> { + x: &'a u32 +} + +impl<'a> Struct<'a> { + fn take_self(self, f: &u32) -> &u32 { + f + } + + fn take_Self(self: Self, f: &u32) -> &u32 { + f + } + + fn take_Box_Self(self: Box, f: &u32) -> &u32 { + f + } + + fn take_Box_Box_Self(self: Box>, f: &u32) -> &u32 { + f + } + + fn take_Rc_Self(self: Rc, f: &u32) -> &u32 { + f + } + + fn take_Box_Rc_Self(self: Box>, f: &u32) -> &u32 { + f + } + + // N/A + //fn take_Pin_Self(self: Pin, f: &u32) -> &u32 { + // f + //} + + // N/A + //fn take_Box_Pin_Self(self: Box>, f: &u32) -> &u32 { + // f + //} +} + +fn main() { } diff --git a/src/test/ui/self/elision/lt-struct.rs b/src/test/ui/self/elision/lt-struct.rs new file mode 100644 index 00000000000..79ffc8fd6f4 --- /dev/null +++ b/src/test/ui/self/elision/lt-struct.rs @@ -0,0 +1,36 @@ +// run-pass + +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::rc::Rc; + +struct Struct<'a> { x: &'a u32 } + +impl<'a> Struct<'a> { + fn take_self(self, f: &u32) -> &u32 { + f + } + + fn take_Struct(self: Struct<'a>, f: &u32) -> &u32 { + f + } + + fn take_Box_Struct(self: Box>, f: &u32) -> &u32 { + f + } + + fn take_Box_Box_Struct(self: Box>>, f: &u32) -> &u32 { + f + } + + fn take_Rc_Struct(self: Rc>, f: &u32) -> &u32 { + f + } + + fn take_Box_Rc_Struct(self: Box>>, f: &u32) -> &u32 { + f + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/ref-alias.rs b/src/test/ui/self/elision/ref-alias.rs new file mode 100644 index 00000000000..23bfe8fb029 --- /dev/null +++ b/src/test/ui/self/elision/ref-alias.rs @@ -0,0 +1,39 @@ +// run-pass + +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::pin::Pin; + +struct Struct { } + +type Alias = Struct; + +impl Struct { + // Test using an alias for `Struct`: + // + // FIXME. We currently fail to recognize this as the self type, which + // feels like a bug. + + fn ref_Alias(self: &Alias, f: &u32) -> &u32 { + f + } + + fn box_ref_Alias(self: Box<&Alias>, f: &u32) -> &u32 { + f + } + + fn pin_ref_Alias(self: Pin<&Alias>, f: &u32) -> &u32 { + f + } + + fn box_box_ref_Alias(self: Box>, f: &u32) -> &u32 { + f + } + + fn box_pin_ref_Alias(self: Box>, f: &u32) -> &u32 { + f + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/ref-mut-alias.rs b/src/test/ui/self/elision/ref-mut-alias.rs new file mode 100644 index 00000000000..abb41296576 --- /dev/null +++ b/src/test/ui/self/elision/ref-mut-alias.rs @@ -0,0 +1,32 @@ +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::pin::Pin; + +struct Struct { } + +type Alias = Struct; + +impl Struct { + // Test using an alias for `Struct`: + + fn ref_Alias(self: &mut Alias, f: &u32) -> &u32 { + f + } + + fn box_ref_Alias(self: Box<&mut Alias>, f: &u32) -> &u32 { + f + } + + fn pin_ref_Alias(self: Pin<&mut Alias>, f: &u32) -> &u32 { + f + } + + fn box_box_ref_Alias(self: Box>, f: &u32) -> &u32 { + f + } + + fn box_pin_ref_Alias(self: Box>, f: &u32) -> &u32 { + f + } +} diff --git a/src/test/ui/self/elision/ref-mut-alias.stderr b/src/test/ui/self/elision/ref-mut-alias.stderr new file mode 100644 index 00000000000..cf202ccaa5d --- /dev/null +++ b/src/test/ui/self/elision/ref-mut-alias.stderr @@ -0,0 +1,7 @@ +error[E0601]: `main` function not found in crate `ref_mut_alias` + | + = note: consider adding a `main` function to `$DIR/ref-mut-alias.rs` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0601`. diff --git a/src/test/ui/self/elision/ref-mut-self.rs b/src/test/ui/self/elision/ref-mut-self.rs new file mode 100644 index 00000000000..6705ca9e305 --- /dev/null +++ b/src/test/ui/self/elision/ref-mut-self.rs @@ -0,0 +1,40 @@ +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::pin::Pin; + +struct Struct { } + +type Alias = Struct; + +impl Struct { + // Test using `&mut self` sugar: + + fn ref_self(&mut self, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + // Test using `&mut Self` explicitly: + + fn ref_Self(self: &mut Self, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_ref_Self(self: Box<&mut Self>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn pin_ref_Self(self: Pin<&mut Self>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/ref-mut-self.stderr b/src/test/ui/self/elision/ref-mut-self.stderr new file mode 100644 index 00000000000..05dc5b774c8 --- /dev/null +++ b/src/test/ui/self/elision/ref-mut-self.stderr @@ -0,0 +1,62 @@ +error[E0623]: lifetime mismatch + --> $DIR/ref-mut-self.rs:14:9 + | +LL | fn ref_self(&mut self, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-mut-self.rs:20:9 + | +LL | fn ref_Self(self: &mut Self, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-mut-self.rs:24:9 + | +LL | fn box_ref_Self(self: Box<&mut Self>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-mut-self.rs:28:9 + | +LL | fn pin_ref_Self(self: Pin<&mut Self>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-mut-self.rs:32:9 + | +LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-mut-self.rs:36:9 + | +LL | fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/self/elision/ref-mut-struct.rs b/src/test/ui/self/elision/ref-mut-struct.rs new file mode 100644 index 00000000000..f063728e247 --- /dev/null +++ b/src/test/ui/self/elision/ref-mut-struct.rs @@ -0,0 +1,34 @@ +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::pin::Pin; + +struct Struct { } + +type Alias = Struct; + +impl Struct { + // Test using `&mut Struct` explicitly: + + fn ref_Struct(self: &mut Struct, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_ref_Struct(self: Box<&mut Struct>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn pin_ref_Struct(self: Pin<&mut Struct>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_pin_ref_Struct(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/ref-mut-struct.stderr b/src/test/ui/self/elision/ref-mut-struct.stderr new file mode 100644 index 00000000000..88dfb17892a --- /dev/null +++ b/src/test/ui/self/elision/ref-mut-struct.stderr @@ -0,0 +1,52 @@ +error[E0623]: lifetime mismatch + --> $DIR/ref-mut-struct.rs:14:9 + | +LL | fn ref_Struct(self: &mut Struct, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-mut-struct.rs:18:9 + | +LL | fn box_ref_Struct(self: Box<&mut Struct>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-mut-struct.rs:22:9 + | +LL | fn pin_ref_Struct(self: Pin<&mut Struct>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-mut-struct.rs:26:9 + | +LL | fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-mut-struct.rs:30:9 + | +LL | fn box_pin_ref_Struct(self: Box>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/self/elision/ref-self.rs b/src/test/ui/self/elision/ref-self.rs new file mode 100644 index 00000000000..af10e10d311 --- /dev/null +++ b/src/test/ui/self/elision/ref-self.rs @@ -0,0 +1,40 @@ +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::pin::Pin; + +struct Struct { } + +type Alias = Struct; + +impl Struct { + // Test using `&self` sugar: + + fn ref_self(&self, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + // Test using `&Self` explicitly: + + fn ref_Self(self: &Self, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/ref-self.stderr b/src/test/ui/self/elision/ref-self.stderr new file mode 100644 index 00000000000..10131cc5935 --- /dev/null +++ b/src/test/ui/self/elision/ref-self.stderr @@ -0,0 +1,62 @@ +error[E0623]: lifetime mismatch + --> $DIR/ref-self.rs:14:9 + | +LL | fn ref_self(&self, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-self.rs:20:9 + | +LL | fn ref_Self(self: &Self, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-self.rs:24:9 + | +LL | fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-self.rs:28:9 + | +LL | fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-self.rs:32:9 + | +LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-self.rs:36:9 + | +LL | fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/self/elision/ref-struct.rs b/src/test/ui/self/elision/ref-struct.rs new file mode 100644 index 00000000000..28afe17c234 --- /dev/null +++ b/src/test/ui/self/elision/ref-struct.rs @@ -0,0 +1,34 @@ +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::pin::Pin; + +struct Struct { } + +type Alias = Struct; + +impl Struct { + // Test using `&Struct` explicitly: + + fn ref_Struct(self: &Struct, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_ref_Struct(self: Box<&Struct>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn pin_ref_Struct(self: Pin<&Struct>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_pin_Struct(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/ref-struct.stderr b/src/test/ui/self/elision/ref-struct.stderr new file mode 100644 index 00000000000..8a17ab13d57 --- /dev/null +++ b/src/test/ui/self/elision/ref-struct.stderr @@ -0,0 +1,52 @@ +error[E0623]: lifetime mismatch + --> $DIR/ref-struct.rs:14:9 + | +LL | fn ref_Struct(self: &Struct, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-struct.rs:18:9 + | +LL | fn box_ref_Struct(self: Box<&Struct>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-struct.rs:22:9 + | +LL | fn pin_ref_Struct(self: Pin<&Struct>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-struct.rs:26:9 + | +LL | fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error[E0623]: lifetime mismatch + --> $DIR/ref-struct.rs:30:9 + | +LL | fn box_pin_Struct(self: Box>, f: &u32) -> &u32 { + | ---- ---- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/self/elision/self.rs b/src/test/ui/self/elision/self.rs new file mode 100644 index 00000000000..cfd1e79e975 --- /dev/null +++ b/src/test/ui/self/elision/self.rs @@ -0,0 +1,36 @@ +// run-pass + +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::rc::Rc; + +struct Struct { } + +impl Struct { + fn take_self(self, f: &u32) -> &u32 { + f + } + + fn take_Self(self: Self, f: &u32) -> &u32 { + f + } + + fn take_Box_Self(self: Box, f: &u32) -> &u32 { + f + } + + fn take_Box_Box_Self(self: Box>, f: &u32) -> &u32 { + f + } + + fn take_Rc_Self(self: Rc, f: &u32) -> &u32 { + f + } + + fn take_Box_Rc_Self(self: Box>, f: &u32) -> &u32 { + f + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/struct.rs b/src/test/ui/self/elision/struct.rs new file mode 100644 index 00000000000..efdeb121fed --- /dev/null +++ b/src/test/ui/self/elision/struct.rs @@ -0,0 +1,34 @@ +// run-pass + +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::rc::Rc; + +struct Struct { } + +impl Struct { + // Test using `&mut Struct` explicitly: + + fn ref_Struct(self: Struct, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_Struct(self: Box, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn rc_Struct(self: Rc, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_box_Struct(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } + + fn box_rc_Struct(self: Box>, f: &u32) -> &u32 { + f //~ ERROR lifetime mismatch + } +} + +fn main() { } -- cgit 1.4.1-3-g733a5 From 258498af5839d1f06043156dc74a9ad727d761db Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 15 Jul 2019 17:59:22 +0900 Subject: Update src/test/ui/self/elision/README.md Co-Authored-By: Mazdak Farrokhzad --- src/test/ui/self/elision/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/self/elision/README.md b/src/test/ui/self/elision/README.md index de29a9d5e53..9e874e2753e 100644 --- a/src/test/ui/self/elision/README.md +++ b/src/test/ui/self/elision/README.md @@ -1,4 +1,4 @@ -Test cases intended to to document behavior and tryto exhaustively +Test cases intended to to document behavior and try to exhaustively explore the combinations. ## Confidence -- cgit 1.4.1-3-g733a5 From c1f22c018e6b2fd64f7f3f94fe48b66c719e243a Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Mon, 15 Jul 2019 18:25:09 +0900 Subject: Add main functions and check-pass annotations --- src/test/ui/self/arbitrary_self_types_pin_lifetime.rs | 2 +- src/test/ui/self/elision/alias.rs | 4 ++++ src/test/ui/self/elision/alias.stderr | 7 ------- src/test/ui/self/elision/lt-alias.rs | 2 +- src/test/ui/self/elision/lt-self.rs | 2 +- src/test/ui/self/elision/lt-struct.rs | 2 +- src/test/ui/self/elision/ref-alias.rs | 2 +- src/test/ui/self/elision/ref-mut-alias.rs | 4 ++++ src/test/ui/self/elision/ref-mut-alias.stderr | 7 ------- src/test/ui/self/elision/self.rs | 2 +- src/test/ui/self/elision/struct.rs | 2 +- src/test/ui/self/self_lifetime.rs | 2 +- 12 files changed, 16 insertions(+), 22 deletions(-) delete mode 100644 src/test/ui/self/elision/alias.stderr delete mode 100644 src/test/ui/self/elision/ref-mut-alias.stderr diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs b/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs index ba574eeb460..30020138812 100644 --- a/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime.rs @@ -1,4 +1,4 @@ -// compile-pass +// check-pass use std::pin::Pin; use std::task::{Context, Poll}; diff --git a/src/test/ui/self/elision/alias.rs b/src/test/ui/self/elision/alias.rs index 6f113ec1a4b..b5aacfaeec4 100644 --- a/src/test/ui/self/elision/alias.rs +++ b/src/test/ui/self/elision/alias.rs @@ -1,3 +1,5 @@ +// check-pass + #![feature(arbitrary_self_types)] #![allow(non_snake_case)] @@ -30,3 +32,5 @@ impl Struct { f } } + +fn main() { } diff --git a/src/test/ui/self/elision/alias.stderr b/src/test/ui/self/elision/alias.stderr deleted file mode 100644 index a8f2a125b5e..00000000000 --- a/src/test/ui/self/elision/alias.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error[E0601]: `main` function not found in crate `alias` - | - = note: consider adding a `main` function to `$DIR/alias.rs` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0601`. diff --git a/src/test/ui/self/elision/lt-alias.rs b/src/test/ui/self/elision/lt-alias.rs index 04468278900..df2300deda2 100644 --- a/src/test/ui/self/elision/lt-alias.rs +++ b/src/test/ui/self/elision/lt-alias.rs @@ -1,4 +1,4 @@ -// run-pass +// check-pass #![feature(arbitrary_self_types)] #![allow(non_snake_case)] diff --git a/src/test/ui/self/elision/lt-self.rs b/src/test/ui/self/elision/lt-self.rs index c33df08e0ee..9b0ee5e42a5 100644 --- a/src/test/ui/self/elision/lt-self.rs +++ b/src/test/ui/self/elision/lt-self.rs @@ -1,4 +1,4 @@ -// run-pass +// check-pass #![feature(arbitrary_self_types)] #![allow(non_snake_case)] diff --git a/src/test/ui/self/elision/lt-struct.rs b/src/test/ui/self/elision/lt-struct.rs index 79ffc8fd6f4..e41dfbbe0bf 100644 --- a/src/test/ui/self/elision/lt-struct.rs +++ b/src/test/ui/self/elision/lt-struct.rs @@ -1,4 +1,4 @@ -// run-pass +// check-pass #![feature(arbitrary_self_types)] #![allow(non_snake_case)] diff --git a/src/test/ui/self/elision/ref-alias.rs b/src/test/ui/self/elision/ref-alias.rs index 23bfe8fb029..d83ac612235 100644 --- a/src/test/ui/self/elision/ref-alias.rs +++ b/src/test/ui/self/elision/ref-alias.rs @@ -1,4 +1,4 @@ -// run-pass +// check-pass #![feature(arbitrary_self_types)] #![allow(non_snake_case)] diff --git a/src/test/ui/self/elision/ref-mut-alias.rs b/src/test/ui/self/elision/ref-mut-alias.rs index abb41296576..395816f8f5d 100644 --- a/src/test/ui/self/elision/ref-mut-alias.rs +++ b/src/test/ui/self/elision/ref-mut-alias.rs @@ -1,3 +1,5 @@ +// check-pass + #![feature(arbitrary_self_types)] #![allow(non_snake_case)] @@ -30,3 +32,5 @@ impl Struct { f } } + +fn main() { } diff --git a/src/test/ui/self/elision/ref-mut-alias.stderr b/src/test/ui/self/elision/ref-mut-alias.stderr deleted file mode 100644 index cf202ccaa5d..00000000000 --- a/src/test/ui/self/elision/ref-mut-alias.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error[E0601]: `main` function not found in crate `ref_mut_alias` - | - = note: consider adding a `main` function to `$DIR/ref-mut-alias.rs` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0601`. diff --git a/src/test/ui/self/elision/self.rs b/src/test/ui/self/elision/self.rs index cfd1e79e975..dbcef71ba14 100644 --- a/src/test/ui/self/elision/self.rs +++ b/src/test/ui/self/elision/self.rs @@ -1,4 +1,4 @@ -// run-pass +// check-pass #![feature(arbitrary_self_types)] #![allow(non_snake_case)] diff --git a/src/test/ui/self/elision/struct.rs b/src/test/ui/self/elision/struct.rs index efdeb121fed..f6e7c0fb129 100644 --- a/src/test/ui/self/elision/struct.rs +++ b/src/test/ui/self/elision/struct.rs @@ -1,4 +1,4 @@ -// run-pass +// check-pass #![feature(arbitrary_self_types)] #![allow(non_snake_case)] diff --git a/src/test/ui/self/self_lifetime.rs b/src/test/ui/self/self_lifetime.rs index a3163ade040..edb47fd4d32 100644 --- a/src/test/ui/self/self_lifetime.rs +++ b/src/test/ui/self/self_lifetime.rs @@ -1,4 +1,4 @@ -// compile-pass +// check-pass struct Foo<'a>(&'a ()); impl<'a> Foo<'a> { -- cgit 1.4.1-3-g733a5 From aab9edc68ab034f94bc956e145d155eb6d26fb3d Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 16 Jul 2019 00:48:28 +0900 Subject: Minor clean up --- src/test/ui/self/elision/README.md | 9 ++++----- src/test/ui/self/elision/ref-mut-self.rs | 2 -- src/test/ui/self/elision/ref-mut-self.stderr | 12 ++++++------ src/test/ui/self/elision/ref-mut-struct.rs | 2 -- src/test/ui/self/elision/ref-mut-struct.stderr | 10 +++++----- src/test/ui/self/elision/ref-self.rs | 2 -- src/test/ui/self/elision/ref-struct.rs | 2 -- src/test/ui/self/elision/ref-struct.stderr | 10 +++++----- src/test/ui/self/elision/struct.rs | 12 +++++------- src/test/ui/self/self_lifetime.rs | 2 ++ 10 files changed, 27 insertions(+), 36 deletions(-) diff --git a/src/test/ui/self/elision/README.md b/src/test/ui/self/elision/README.md index 9e874e2753e..793eb16b037 100644 --- a/src/test/ui/self/elision/README.md +++ b/src/test/ui/self/elision/README.md @@ -1,5 +1,5 @@ Test cases intended to to document behavior and try to exhaustively -explore the combinations. +explore the combinations. ## Confidence @@ -23,11 +23,11 @@ in the "confidence" field in the following table. Values: | `struct.rs` | `Struct` | `Struct` | ignore `self` parameter | 100% | | `alias.rs` | `Struct` | `Alias` | ignore `self` parameter | 100% | | `ref-self.rs` | `Struct` | `&Self` | take lifetime from `&Self` | 100% | -| `ref-mut-self.rs` | `Struct` | `&mut Self` | take lifetime from `&Self` | 100% | +| `ref-mut-self.rs` | `Struct` | `&mut Self` | take lifetime from `&mut Self` | 100% | | `ref-struct.rs` | `Struct` | `&Struct` | take lifetime from `&Self` | 50% | -| `ref-mut-struct.rs` | `Struct` | `&Struct` | take lifetime from `&Self` | 50% | +| `ref-mut-struct.rs` | `Struct` | `&mut Struct` | take lifetime from `&mut Self` | 50% | | `ref-alias.rs` | `Struct` | `&Alias` | ignore `Alias` | 0% | -| `ref-mut-alias.rs` | `Struct` | `&Alias` | ignore `Alias` | 0% | +| `ref-mut-alias.rs` | `Struct` | `&mut Alias` | ignore `Alias` | 0% | | `lt-self.rs` | `Struct<'a>` | `Self` | ignore `Self` (and hence `'a`) | 25% | | `lt-struct.rs` | `Struct<'a>` | `Self` | ignore `Self` (and hence `'a`) | 0% | | `lt-alias.rs` | `Alias<'a>` | `Self` | ignore `Self` (and hence `'a`) | 0% | @@ -42,4 +42,3 @@ In each case, we test the following patterns: - `self: Box>` In the non-reference cases, `Pin` causes errors so we substitute `Rc`. - diff --git a/src/test/ui/self/elision/ref-mut-self.rs b/src/test/ui/self/elision/ref-mut-self.rs index 6705ca9e305..a7ea47bb7f6 100644 --- a/src/test/ui/self/elision/ref-mut-self.rs +++ b/src/test/ui/self/elision/ref-mut-self.rs @@ -5,8 +5,6 @@ use std::pin::Pin; struct Struct { } -type Alias = Struct; - impl Struct { // Test using `&mut self` sugar: diff --git a/src/test/ui/self/elision/ref-mut-self.stderr b/src/test/ui/self/elision/ref-mut-self.stderr index 05dc5b774c8..37984cd72fb 100644 --- a/src/test/ui/self/elision/ref-mut-self.stderr +++ b/src/test/ui/self/elision/ref-mut-self.stderr @@ -1,5 +1,5 @@ error[E0623]: lifetime mismatch - --> $DIR/ref-mut-self.rs:14:9 + --> $DIR/ref-mut-self.rs:12:9 | LL | fn ref_self(&mut self, f: &u32) -> &u32 { | ---- ---- @@ -9,7 +9,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-mut-self.rs:20:9 + --> $DIR/ref-mut-self.rs:18:9 | LL | fn ref_Self(self: &mut Self, f: &u32) -> &u32 { | ---- ---- @@ -19,7 +19,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-mut-self.rs:24:9 + --> $DIR/ref-mut-self.rs:22:9 | LL | fn box_ref_Self(self: Box<&mut Self>, f: &u32) -> &u32 { | ---- ---- @@ -29,7 +29,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-mut-self.rs:28:9 + --> $DIR/ref-mut-self.rs:26:9 | LL | fn pin_ref_Self(self: Pin<&mut Self>, f: &u32) -> &u32 { | ---- ---- @@ -39,7 +39,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-mut-self.rs:32:9 + --> $DIR/ref-mut-self.rs:30:9 | LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | ---- ---- @@ -49,7 +49,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-mut-self.rs:36:9 + --> $DIR/ref-mut-self.rs:34:9 | LL | fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { | ---- ---- diff --git a/src/test/ui/self/elision/ref-mut-struct.rs b/src/test/ui/self/elision/ref-mut-struct.rs index f063728e247..795ddf8ac13 100644 --- a/src/test/ui/self/elision/ref-mut-struct.rs +++ b/src/test/ui/self/elision/ref-mut-struct.rs @@ -5,8 +5,6 @@ use std::pin::Pin; struct Struct { } -type Alias = Struct; - impl Struct { // Test using `&mut Struct` explicitly: diff --git a/src/test/ui/self/elision/ref-mut-struct.stderr b/src/test/ui/self/elision/ref-mut-struct.stderr index 88dfb17892a..2a4826905b9 100644 --- a/src/test/ui/self/elision/ref-mut-struct.stderr +++ b/src/test/ui/self/elision/ref-mut-struct.stderr @@ -1,5 +1,5 @@ error[E0623]: lifetime mismatch - --> $DIR/ref-mut-struct.rs:14:9 + --> $DIR/ref-mut-struct.rs:12:9 | LL | fn ref_Struct(self: &mut Struct, f: &u32) -> &u32 { | ---- ---- @@ -9,7 +9,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-mut-struct.rs:18:9 + --> $DIR/ref-mut-struct.rs:16:9 | LL | fn box_ref_Struct(self: Box<&mut Struct>, f: &u32) -> &u32 { | ---- ---- @@ -19,7 +19,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-mut-struct.rs:22:9 + --> $DIR/ref-mut-struct.rs:20:9 | LL | fn pin_ref_Struct(self: Pin<&mut Struct>, f: &u32) -> &u32 { | ---- ---- @@ -29,7 +29,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-mut-struct.rs:26:9 + --> $DIR/ref-mut-struct.rs:24:9 | LL | fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { | ---- ---- @@ -39,7 +39,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-mut-struct.rs:30:9 + --> $DIR/ref-mut-struct.rs:28:9 | LL | fn box_pin_ref_Struct(self: Box>, f: &u32) -> &u32 { | ---- ---- diff --git a/src/test/ui/self/elision/ref-self.rs b/src/test/ui/self/elision/ref-self.rs index af10e10d311..9655c11f45e 100644 --- a/src/test/ui/self/elision/ref-self.rs +++ b/src/test/ui/self/elision/ref-self.rs @@ -5,8 +5,6 @@ use std::pin::Pin; struct Struct { } -type Alias = Struct; - impl Struct { // Test using `&self` sugar: diff --git a/src/test/ui/self/elision/ref-struct.rs b/src/test/ui/self/elision/ref-struct.rs index 28afe17c234..342d6d2b363 100644 --- a/src/test/ui/self/elision/ref-struct.rs +++ b/src/test/ui/self/elision/ref-struct.rs @@ -5,8 +5,6 @@ use std::pin::Pin; struct Struct { } -type Alias = Struct; - impl Struct { // Test using `&Struct` explicitly: diff --git a/src/test/ui/self/elision/ref-struct.stderr b/src/test/ui/self/elision/ref-struct.stderr index 8a17ab13d57..186e651c143 100644 --- a/src/test/ui/self/elision/ref-struct.stderr +++ b/src/test/ui/self/elision/ref-struct.stderr @@ -1,5 +1,5 @@ error[E0623]: lifetime mismatch - --> $DIR/ref-struct.rs:14:9 + --> $DIR/ref-struct.rs:12:9 | LL | fn ref_Struct(self: &Struct, f: &u32) -> &u32 { | ---- ---- @@ -9,7 +9,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-struct.rs:18:9 + --> $DIR/ref-struct.rs:16:9 | LL | fn box_ref_Struct(self: Box<&Struct>, f: &u32) -> &u32 { | ---- ---- @@ -19,7 +19,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-struct.rs:22:9 + --> $DIR/ref-struct.rs:20:9 | LL | fn pin_ref_Struct(self: Pin<&Struct>, f: &u32) -> &u32 { | ---- ---- @@ -29,7 +29,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-struct.rs:26:9 + --> $DIR/ref-struct.rs:24:9 | LL | fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { | ---- ---- @@ -39,7 +39,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-struct.rs:30:9 + --> $DIR/ref-struct.rs:28:9 | LL | fn box_pin_Struct(self: Box>, f: &u32) -> &u32 { | ---- ---- diff --git a/src/test/ui/self/elision/struct.rs b/src/test/ui/self/elision/struct.rs index f6e7c0fb129..227e993bd3c 100644 --- a/src/test/ui/self/elision/struct.rs +++ b/src/test/ui/self/elision/struct.rs @@ -8,26 +8,24 @@ use std::rc::Rc; struct Struct { } impl Struct { - // Test using `&mut Struct` explicitly: - fn ref_Struct(self: Struct, f: &u32) -> &u32 { - f //~ ERROR lifetime mismatch + f } fn box_Struct(self: Box, f: &u32) -> &u32 { - f //~ ERROR lifetime mismatch + f } fn rc_Struct(self: Rc, f: &u32) -> &u32 { - f //~ ERROR lifetime mismatch + f } fn box_box_Struct(self: Box>, f: &u32) -> &u32 { - f //~ ERROR lifetime mismatch + f } fn box_rc_Struct(self: Box>, f: &u32) -> &u32 { - f //~ ERROR lifetime mismatch + f } } diff --git a/src/test/ui/self/self_lifetime.rs b/src/test/ui/self/self_lifetime.rs index edb47fd4d32..f04bd83ab6e 100644 --- a/src/test/ui/self/self_lifetime.rs +++ b/src/test/ui/self/self_lifetime.rs @@ -1,5 +1,7 @@ // check-pass +// https://github.com/rust-lang/rust/pull/60944#issuecomment-495346120 + struct Foo<'a>(&'a ()); impl<'a> Foo<'a> { fn foo<'b>(self: &'b Foo<'a>) -> &() { self.0 } -- cgit 1.4.1-3-g733a5 From 8507b8e42fbe8b763bb8a7375133d95445813946 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 16 Jul 2019 01:09:25 +0900 Subject: Add test for multiple ref-self --- src/test/ui/self/elision/multiple-ref-self.rs | 43 +++++++++++++++++++++++++++ src/test/ui/self/elision/ref-self.rs | 13 ++++++++ src/test/ui/self/elision/ref-self.stderr | 24 ++++++++++----- 3 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/self/elision/multiple-ref-self.rs diff --git a/src/test/ui/self/elision/multiple-ref-self.rs b/src/test/ui/self/elision/multiple-ref-self.rs new file mode 100644 index 00000000000..f39613d0c90 --- /dev/null +++ b/src/test/ui/self/elision/multiple-ref-self.rs @@ -0,0 +1,43 @@ +// check-pass + +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::marker::PhantomData; +use std::ops::Deref; +use std::pin::Pin; + +struct Struct { } + +struct Wrap(T, PhantomData

); + +impl Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { &self.0 } +} + +impl Struct { + // Test using multiple `&Self`: + + fn wrap_ref_Self_ref_Self(self: Wrap<&Self, &Self>, f: &u8) -> &u8 { + f + } + + fn box_wrap_ref_Self_ref_Self(self: Box>, f: &u32) -> &u32 { + f + } + + fn pin_wrap_ref_Self_ref_Self(self: Pin>, f: &u32) -> &u32 { + f + } + + fn box_box_wrap_ref_Self_ref_Self(self: Box>>, f: &u32) -> &u32 { + f + } + + fn box_pin_wrap_ref_Self_ref_Self(self: Box>>, f: &u32) -> &u32 { + f + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/ref-self.rs b/src/test/ui/self/elision/ref-self.rs index 9655c11f45e..e389d8518ad 100644 --- a/src/test/ui/self/elision/ref-self.rs +++ b/src/test/ui/self/elision/ref-self.rs @@ -1,10 +1,19 @@ #![feature(arbitrary_self_types)] #![allow(non_snake_case)] +use std::marker::PhantomData; +use std::ops::Deref; use std::pin::Pin; struct Struct { } +struct Wrap(T, PhantomData

); + +impl Deref for Wrap { + type Target = T; + fn deref(&self) -> &T { &self.0 } +} + impl Struct { // Test using `&self` sugar: @@ -33,6 +42,10 @@ impl Struct { fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { f //~ ERROR lifetime mismatch } + + fn wrap_ref_Self_Self(self: Wrap<&Self, Self>, f: &u8) -> &u8 { + f //~ ERROR lifetime mismatch + } } fn main() { } diff --git a/src/test/ui/self/elision/ref-self.stderr b/src/test/ui/self/elision/ref-self.stderr index 10131cc5935..611498f18da 100644 --- a/src/test/ui/self/elision/ref-self.stderr +++ b/src/test/ui/self/elision/ref-self.stderr @@ -1,5 +1,5 @@ error[E0623]: lifetime mismatch - --> $DIR/ref-self.rs:14:9 + --> $DIR/ref-self.rs:21:9 | LL | fn ref_self(&self, f: &u32) -> &u32 { | ---- ---- @@ -9,7 +9,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-self.rs:20:9 + --> $DIR/ref-self.rs:27:9 | LL | fn ref_Self(self: &Self, f: &u32) -> &u32 { | ---- ---- @@ -19,7 +19,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-self.rs:24:9 + --> $DIR/ref-self.rs:31:9 | LL | fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { | ---- ---- @@ -29,7 +29,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-self.rs:28:9 + --> $DIR/ref-self.rs:35:9 | LL | fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { | ---- ---- @@ -39,7 +39,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-self.rs:32:9 + --> $DIR/ref-self.rs:39:9 | LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { | ---- ---- @@ -49,7 +49,7 @@ LL | f | ^ ...but data from `f` is returned here error[E0623]: lifetime mismatch - --> $DIR/ref-self.rs:36:9 + --> $DIR/ref-self.rs:43:9 | LL | fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { | ---- ---- @@ -58,5 +58,15 @@ LL | fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { LL | f | ^ ...but data from `f` is returned here -error: aborting due to 6 previous errors +error[E0623]: lifetime mismatch + --> $DIR/ref-self.rs:47:9 + | +LL | fn wrap_ref_Self_Self(self: Wrap<&Self, Self>, f: &u8) -> &u8 { + | --- --- + | | + | this parameter and the return type are declared with different lifetimes... +LL | f + | ^ ...but data from `f` is returned here + +error: aborting due to 7 previous errors -- cgit 1.4.1-3-g733a5 From 1e2905269bf6c6f230204e08f7d400664b630faf Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 26 Jul 2019 15:20:04 +0900 Subject: Add tests for `self: (&)AssocType` --- src/test/ui/self/elision/assoc.rs | 40 +++++++++++++++++++++++++++++++ src/test/ui/self/elision/lt-assoc.rs | 44 +++++++++++++++++++++++++++++++++++ src/test/ui/self/elision/ref-assoc.rs | 40 +++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 src/test/ui/self/elision/assoc.rs create mode 100644 src/test/ui/self/elision/lt-assoc.rs create mode 100644 src/test/ui/self/elision/ref-assoc.rs diff --git a/src/test/ui/self/elision/assoc.rs b/src/test/ui/self/elision/assoc.rs new file mode 100644 index 00000000000..163eb49383a --- /dev/null +++ b/src/test/ui/self/elision/assoc.rs @@ -0,0 +1,40 @@ +// check-pass + +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::rc::Rc; + +trait Trait { + type AssocType; +} + +struct Struct { } + +impl Trait for Struct { + type AssocType = Self; +} + +impl Struct { + fn assoc(self: ::AssocType, f: &u32) -> &u32 { + f + } + + fn box_AssocType(self: Box<::AssocType>, f: &u32) -> &u32 { + f + } + + fn rc_AssocType(self: Rc<::AssocType>, f: &u32) -> &u32 { + f + } + + fn box_box_AssocType(self: Box::AssocType>>, f: &u32) -> &u32 { + f + } + + fn box_rc_AssocType(self: Box::AssocType>>, f: &u32) -> &u32 { + f + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/lt-assoc.rs b/src/test/ui/self/elision/lt-assoc.rs new file mode 100644 index 00000000000..70573598fcb --- /dev/null +++ b/src/test/ui/self/elision/lt-assoc.rs @@ -0,0 +1,44 @@ +// check-pass + +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::rc::Rc; + +trait Trait { + type AssocType; +} + +struct Struct<'a> { x: &'a u32 } + +impl<'a> Trait for Struct<'a> { + type AssocType = Self; +} + +impl<'a> Struct<'a> { + fn take_self(self, f: &u32) -> &u32 { + f + } + + fn take_AssocType(self: as Trait>::AssocType, f: &u32) -> &u32 { + f + } + + fn take_Box_AssocType(self: Box< as Trait>::AssocType>, f: &u32) -> &u32 { + f + } + + fn take_Box_Box_AssocType(self: Box as Trait>::AssocType>>, f: &u32) -> &u32 { + f + } + + fn take_Rc_AssocType(self: Rc< as Trait>::AssocType>, f: &u32) -> &u32 { + f + } + + fn take_Box_Rc_AssocType(self: Box as Trait>::AssocType>>, f: &u32) -> &u32 { + f + } +} + +fn main() { } diff --git a/src/test/ui/self/elision/ref-assoc.rs b/src/test/ui/self/elision/ref-assoc.rs new file mode 100644 index 00000000000..f9354bc8847 --- /dev/null +++ b/src/test/ui/self/elision/ref-assoc.rs @@ -0,0 +1,40 @@ +// check-pass + +#![feature(arbitrary_self_types)] +#![allow(non_snake_case)] + +use std::pin::Pin; + +trait Trait { + type AssocType; +} + +struct Struct { } + +impl Trait for Struct { + type AssocType = Self; +} + +impl Struct { + fn ref_AssocType(self: &::AssocType, f: &u32) -> &u32 { + f + } + + fn box_ref_AssocType(self: Box<&::AssocType>, f: &u32) -> &u32 { + f + } + + fn pin_ref_AssocType(self: Pin<&::AssocType>, f: &u32) -> &u32 { + f + } + + fn box_box_ref_AssocType(self: Box::AssocType>>, f: &u32) -> &u32 { + f + } + + fn box_pin_ref_AssocType(self: Box::AssocType>>, f: &u32) -> &u32 { + f + } +} + +fn main() { } -- cgit 1.4.1-3-g733a5 From 34f59eb9c5b3ced90e77fff9983a1711e06b731a Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 26 Jul 2019 15:41:00 +0900 Subject: Fix typo --- src/test/ui/self/elision/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/self/elision/README.md b/src/test/ui/self/elision/README.md index 793eb16b037..7ace2e0c890 100644 --- a/src/test/ui/self/elision/README.md +++ b/src/test/ui/self/elision/README.md @@ -1,4 +1,4 @@ -Test cases intended to to document behavior and try to exhaustively +Test cases intended to document behavior and try to exhaustively explore the combinations. ## Confidence -- cgit 1.4.1-3-g733a5 From 05f67a297a25dc33f2d739dbccc42f44bc6d7ab9 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 27 Jul 2019 12:35:05 +0900 Subject: arbitrary_self_types lifetime elision: --bless --compare-mode=nll --- ...y_self_types_pin_lifetime_impl_trait.nll.stderr | 14 +++++ ...ary_self_types_pin_lifetime_mismatch.nll.stderr | 28 +++++++++ src/test/ui/self/elision/lt-ref-self.nll.stderr | 62 +++++++++++++++++++ src/test/ui/self/elision/ref-mut-self.nll.stderr | 62 +++++++++++++++++++ src/test/ui/self/elision/ref-mut-struct.nll.stderr | 52 ++++++++++++++++ src/test/ui/self/elision/ref-self.nll.stderr | 72 ++++++++++++++++++++++ src/test/ui/self/elision/ref-struct.nll.stderr | 52 ++++++++++++++++ 7 files changed, 342 insertions(+) create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.nll.stderr create mode 100644 src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr create mode 100644 src/test/ui/self/elision/lt-ref-self.nll.stderr create mode 100644 src/test/ui/self/elision/ref-mut-self.nll.stderr create mode 100644 src/test/ui/self/elision/ref-mut-struct.nll.stderr create mode 100644 src/test/ui/self/elision/ref-self.nll.stderr create mode 100644 src/test/ui/self/elision/ref-struct.nll.stderr diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.nll.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.nll.stderr new file mode 100644 index 00000000000..dcfc9ba511d --- /dev/null +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_impl_trait.nll.stderr @@ -0,0 +1,14 @@ +error: lifetime may not live long enough + --> $DIR/arbitrary_self_types_pin_lifetime_impl_trait.rs:8:31 + | +LL | fn f(self: Pin<&Self>) -> impl Clone { self } + | - ^^^^^^^^^^ opaque type requires that `'1` must outlive `'static` + | | + | let's call the lifetime of this reference `'1` +help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as a constraint + | +LL | fn f(self: Pin<&Self>) -> impl Clone + '_ { self } + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr new file mode 100644 index 00000000000..8a0f1a804ad --- /dev/null +++ b/src/test/ui/self/arbitrary_self_types_pin_lifetime_mismatch.nll.stderr @@ -0,0 +1,28 @@ +error: lifetime may not live long enough + --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:8:46 + | +LL | fn a(self: Pin<&Foo>, f: &Foo) -> &Foo { f } + | - - ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | | | + | | let's call the lifetime of this reference `'1` + | let's call the lifetime of this reference `'2` + +error: lifetime may not live long enough + --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:10:69 + | +LL | fn c(self: Pin<&Self>, f: &Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) } + | - - ^^^^^^^^^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + | | | + | | let's call the lifetime of this reference `'1` + | let's call the lifetime of this reference `'2` + +error: lifetime may not live long enough + --> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:15:58 + | +LL | fn bar<'a>(self: Alias<&Self>, arg: &'a ()) -> &() { arg } + | -- ---- has type `std::pin::Pin<&'1 Foo>` ^^^ function was supposed to return data with lifetime `'1` but it is returning data with lifetime `'a` + | | + | lifetime `'a` defined here + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/self/elision/lt-ref-self.nll.stderr b/src/test/ui/self/elision/lt-ref-self.nll.stderr new file mode 100644 index 00000000000..e97a01e746d --- /dev/null +++ b/src/test/ui/self/elision/lt-ref-self.nll.stderr @@ -0,0 +1,62 @@ +error: lifetime may not live long enough + --> $DIR/lt-ref-self.rs:12:9 + | +LL | fn ref_self(&self, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/lt-ref-self.rs:18:9 + | +LL | fn ref_Self(self: &Self, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/lt-ref-self.rs:22:9 + | +LL | fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/lt-ref-self.rs:26:9 + | +LL | fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/lt-ref-self.rs:30:9 + | +LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/lt-ref-self.rs:34:9 + | +LL | fn box_pin_Self(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/self/elision/ref-mut-self.nll.stderr b/src/test/ui/self/elision/ref-mut-self.nll.stderr new file mode 100644 index 00000000000..3a8ae3fdcba --- /dev/null +++ b/src/test/ui/self/elision/ref-mut-self.nll.stderr @@ -0,0 +1,62 @@ +error: lifetime may not live long enough + --> $DIR/ref-mut-self.rs:12:9 + | +LL | fn ref_self(&mut self, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-mut-self.rs:18:9 + | +LL | fn ref_Self(self: &mut Self, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-mut-self.rs:22:9 + | +LL | fn box_ref_Self(self: Box<&mut Self>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-mut-self.rs:26:9 + | +LL | fn pin_ref_Self(self: Pin<&mut Self>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-mut-self.rs:30:9 + | +LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-mut-self.rs:34:9 + | +LL | fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/self/elision/ref-mut-struct.nll.stderr b/src/test/ui/self/elision/ref-mut-struct.nll.stderr new file mode 100644 index 00000000000..66152ba40a5 --- /dev/null +++ b/src/test/ui/self/elision/ref-mut-struct.nll.stderr @@ -0,0 +1,52 @@ +error: lifetime may not live long enough + --> $DIR/ref-mut-struct.rs:12:9 + | +LL | fn ref_Struct(self: &mut Struct, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-mut-struct.rs:16:9 + | +LL | fn box_ref_Struct(self: Box<&mut Struct>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-mut-struct.rs:20:9 + | +LL | fn pin_ref_Struct(self: Pin<&mut Struct>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-mut-struct.rs:24:9 + | +LL | fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-mut-struct.rs:28:9 + | +LL | fn box_pin_ref_Struct(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/self/elision/ref-self.nll.stderr b/src/test/ui/self/elision/ref-self.nll.stderr new file mode 100644 index 00000000000..20045be0527 --- /dev/null +++ b/src/test/ui/self/elision/ref-self.nll.stderr @@ -0,0 +1,72 @@ +error: lifetime may not live long enough + --> $DIR/ref-self.rs:21:9 + | +LL | fn ref_self(&self, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self.rs:27:9 + | +LL | fn ref_Self(self: &Self, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self.rs:31:9 + | +LL | fn box_ref_Self(self: Box<&Self>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self.rs:35:9 + | +LL | fn pin_ref_Self(self: Pin<&Self>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self.rs:39:9 + | +LL | fn box_box_ref_Self(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self.rs:43:9 + | +LL | fn box_pin_ref_Self(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-self.rs:47:9 + | +LL | fn wrap_ref_Self_Self(self: Wrap<&Self, Self>, f: &u8) -> &u8 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: aborting due to 7 previous errors + diff --git a/src/test/ui/self/elision/ref-struct.nll.stderr b/src/test/ui/self/elision/ref-struct.nll.stderr new file mode 100644 index 00000000000..a258bc9f743 --- /dev/null +++ b/src/test/ui/self/elision/ref-struct.nll.stderr @@ -0,0 +1,52 @@ +error: lifetime may not live long enough + --> $DIR/ref-struct.rs:12:9 + | +LL | fn ref_Struct(self: &Struct, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-struct.rs:16:9 + | +LL | fn box_ref_Struct(self: Box<&Struct>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-struct.rs:20:9 + | +LL | fn pin_ref_Struct(self: Pin<&Struct>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-struct.rs:24:9 + | +LL | fn box_box_ref_Struct(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: lifetime may not live long enough + --> $DIR/ref-struct.rs:28:9 + | +LL | fn box_pin_Struct(self: Box>, f: &u32) -> &u32 { + | - - let's call the lifetime of this reference `'1` + | | + | let's call the lifetime of this reference `'2` +LL | f + | ^ function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1` + +error: aborting due to 5 previous errors + -- cgit 1.4.1-3-g733a5 From 8b3f28cf0f0cd30ddba884b10c395391ff22beb0 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sat, 27 Jul 2019 16:45:45 +0700 Subject: Make more informative error on outer attr after inner --- src/libsyntax/parse/attr.rs | 47 ++++++++++++++++++++++++------------ src/test/ui/parser/inner-attr.stderr | 7 ++++-- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 1758d0b0bb9..810af9b9246 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -4,6 +4,7 @@ use crate::parse::{SeqSep, PResult}; use crate::parse::token::{self, Nonterminal, DelimToken}; use crate::parse::parser::{Parser, TokenType, PathStyle}; use crate::tokenstream::{TokenStream, TokenTree}; +use crate::source_map::Span; use log::debug; use smallvec::smallvec; @@ -11,7 +12,7 @@ use smallvec::smallvec; #[derive(Debug)] enum InnerAttributeParsePolicy<'a> { Permitted, - NotPermitted { reason: &'a str }, + NotPermitted { reason: &'a str, prev_attr_sp: Option }, } const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \ @@ -42,7 +43,8 @@ impl<'a> Parser<'a> { DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG }; let inner_parse_policy = - InnerAttributeParsePolicy::NotPermitted { reason: inner_error_reason }; + InnerAttributeParsePolicy::NotPermitted { reason: inner_error_reason, + prev_attr_sp: attrs.last().and_then(|a| Some(a.span)) }; let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?; attrs.push(attr); just_parsed_doc_comment = false; @@ -77,7 +79,7 @@ impl<'a> Parser<'a> { InnerAttributeParsePolicy::Permitted } else { InnerAttributeParsePolicy::NotPermitted - { reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG } + { reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG, prev_attr_sp: None } }; self.parse_attribute_with_inner_parse_policy(inner_parse_policy) } @@ -98,19 +100,9 @@ impl<'a> Parser<'a> { if let InnerAttributeParsePolicy::Permitted = inner_parse_policy { self.expected_tokens.push(TokenType::Token(token::Not)); } + let style = if self.token == token::Not { self.bump(); - if let InnerAttributeParsePolicy::NotPermitted { reason } = inner_parse_policy - { - let span = self.token.span; - self.diagnostic() - .struct_span_err(span, reason) - .note("inner attributes, like `#![no_std]`, annotate the item \ - enclosing them, and are usually found at the beginning of \ - source files. Outer attributes, like `#[test]`, annotate the \ - item following them.") - .emit() - } ast::AttrStyle::Inner } else { ast::AttrStyle::Outer @@ -121,7 +113,32 @@ impl<'a> Parser<'a> { self.expect(&token::CloseDelim(token::Bracket))?; let hi = self.prev_span; - (lo.to(hi), path, tokens, style) + let attr_sp = lo.to(hi); + + // Emit error if inner attribute is encountered and not permitted + if style == ast::AttrStyle::Inner { + if let InnerAttributeParsePolicy::NotPermitted { reason, prev_attr_sp } + = inner_parse_policy { + let mut diagnostic = self + .diagnostic() + .struct_span_err(attr_sp, reason); + + if let Some(prev_attr_sp) = prev_attr_sp { + diagnostic + .span_label(attr_sp, "not permitted following an outer attibute") + .span_label(prev_attr_sp, "previous outer attribute"); + } + + diagnostic + .note("inner attributes, like `#![no_std]`, annotate the item \ + enclosing them, and are usually found at the beginning of \ + source files. Outer attributes, like `#[test]`, annotate the \ + item following them.") + .emit() + } + } + + (attr_sp, path, tokens, style) } _ => { let token_str = self.this_token_to_string(); diff --git a/src/test/ui/parser/inner-attr.stderr b/src/test/ui/parser/inner-attr.stderr index 11a37bc139b..070d9f47d96 100644 --- a/src/test/ui/parser/inner-attr.stderr +++ b/src/test/ui/parser/inner-attr.stderr @@ -1,8 +1,11 @@ error: an inner attribute is not permitted following an outer attribute - --> $DIR/inner-attr.rs:3:3 + --> $DIR/inner-attr.rs:3:1 | +LL | #[feature(lang_items)] + | ---------------------- previous outer attribute +LL | LL | #![recursion_limit="100"] - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not permitted following an outer attibute | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. -- cgit 1.4.1-3-g733a5 From 3eeec1c5d2a6342b6b38720ae67091e1546ac7d1 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 18 Jul 2019 01:49:10 +0300 Subject: Break dependencies between `syntax_ext` and some other crates Move `source_uitil` macros into `syntax_ext` Cleanup dependencies of `rustc_driver` --- Cargo.lock | 12 --- src/librustc_driver/Cargo.toml | 11 --- src/librustc_passes/Cargo.toml | 1 - src/librustc_passes/ast_validation.rs | 2 +- src/libsyntax/ext/base.rs | 2 +- src/libsyntax/ext/proc_macro.rs | 7 ++ src/libsyntax/ext/source_util.rs | 165 ---------------------------------- src/libsyntax/lib.rs | 3 +- src/libsyntax_ext/lib.rs | 1 + src/libsyntax_ext/proc_macro_decls.rs | 12 +-- src/libsyntax_ext/source_util.rs | 165 ++++++++++++++++++++++++++++++++++ 11 files changed, 178 insertions(+), 203 deletions(-) create mode 100644 src/libsyntax/ext/proc_macro.rs delete mode 100644 src/libsyntax/ext/source_util.rs create mode 100644 src/libsyntax_ext/source_util.rs diff --git a/Cargo.lock b/Cargo.lock index 6384560b672..a8246eed853 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2863,31 +2863,20 @@ dependencies = [ name = "rustc_driver" version = "0.0.0" dependencies = [ - "arena 0.0.0", "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_ast_borrowck 0.0.0", "rustc_codegen_utils 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", - "rustc_incremental 0.0.0", "rustc_interface 0.0.0", - "rustc_lint 0.0.0", "rustc_metadata 0.0.0", "rustc_mir 0.0.0", - "rustc_passes 0.0.0", - "rustc_plugin 0.0.0", - "rustc_privacy 0.0.0", - "rustc_resolve 0.0.0", "rustc_save_analysis 0.0.0", "rustc_target 0.0.0", - "rustc_traits 0.0.0", - "rustc_typeck 0.0.0", "serialize 0.0.0", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "syntax 0.0.0", "syntax_pos 0.0.0", ] @@ -3066,7 +3055,6 @@ dependencies = [ "rustc_errors 0.0.0", "rustc_mir 0.0.0", "syntax 0.0.0", - "syntax_ext 0.0.0", "syntax_pos 0.0.0", ] diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index e8c9828a202..42aa8203cba 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -10,30 +10,19 @@ path = "lib.rs" crate-type = ["dylib"] [dependencies] -arena = { path = "../libarena" } graphviz = { path = "../libgraphviz" } log = "0.4" env_logger = { version = "0.5", default-features = false } -rayon = { version = "0.2.0", package = "rustc-rayon" } rustc = { path = "../librustc" } rustc_target = { path = "../librustc_target" } rustc_ast_borrowck = { path = "../librustc_ast_borrowck" } rustc_data_structures = { path = "../librustc_data_structures" } errors = { path = "../librustc_errors", package = "rustc_errors" } -rustc_incremental = { path = "../librustc_incremental" } -rustc_lint = { path = "../librustc_lint" } rustc_metadata = { path = "../librustc_metadata" } rustc_mir = { path = "../librustc_mir" } -rustc_passes = { path = "../librustc_passes" } -rustc_plugin = { path = "../librustc_plugin" } -rustc_privacy = { path = "../librustc_privacy" } -rustc_resolve = { path = "../librustc_resolve" } rustc_save_analysis = { path = "../librustc_save_analysis" } -rustc_traits = { path = "../librustc_traits" } rustc_codegen_utils = { path = "../librustc_codegen_utils" } -rustc_typeck = { path = "../librustc_typeck" } rustc_interface = { path = "../librustc_interface" } rustc_serialize = { path = "../libserialize", package = "serialize" } syntax = { path = "../libsyntax" } -smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_passes/Cargo.toml b/src/librustc_passes/Cargo.toml index de2476775b0..5f378dacd25 100644 --- a/src/librustc_passes/Cargo.toml +++ b/src/librustc_passes/Cargo.toml @@ -14,6 +14,5 @@ rustc = { path = "../librustc" } rustc_mir = { path = "../librustc_mir"} rustc_data_structures = { path = "../librustc_data_structures" } syntax = { path = "../libsyntax" } -syntax_ext = { path = "../libsyntax_ext" } syntax_pos = { path = "../libsyntax_pos" } errors = { path = "../librustc_errors", package = "rustc_errors" } diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index b550029d978..562865ef55f 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -14,12 +14,12 @@ use rustc::session::Session; use rustc_data_structures::fx::FxHashMap; use syntax::ast::*; use syntax::attr; +use syntax::ext::proc_macro::is_proc_macro_attr; use syntax::feature_gate::is_builtin_attr; use syntax::source_map::Spanned; use syntax::symbol::{kw, sym}; use syntax::visit::{self, Visitor}; use syntax::{span_err, struct_span_err, walk_list}; -use syntax_ext::proc_macro_decls::is_proc_macro_attr; use syntax_pos::{Span, MultiSpan}; use errors::{Applicability, FatalError}; diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 0ab14bee160..e53757dce3d 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -14,7 +14,7 @@ use crate::tokenstream::{self, TokenStream, TokenTree}; use errors::{DiagnosticBuilder, DiagnosticId}; use smallvec::{smallvec, SmallVec}; -use syntax_pos::{Span, MultiSpan, DUMMY_SP}; +use syntax_pos::{FileName, Span, MultiSpan, DUMMY_SP}; use syntax_pos::hygiene::{ExpnInfo, ExpnKind}; use rustc_data_structures::fx::FxHashMap; diff --git a/src/libsyntax/ext/proc_macro.rs b/src/libsyntax/ext/proc_macro.rs new file mode 100644 index 00000000000..3aac79e241a --- /dev/null +++ b/src/libsyntax/ext/proc_macro.rs @@ -0,0 +1,7 @@ +use crate::ast::Attribute; +use crate::symbol::sym; + +pub fn is_proc_macro_attr(attr: &Attribute) -> bool { + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] + .iter().any(|kind| attr.check_name(*kind)) +} diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs deleted file mode 100644 index ae080c05eec..00000000000 --- a/src/libsyntax/ext/source_util.rs +++ /dev/null @@ -1,165 +0,0 @@ -use crate::ast; -use crate::ext::base::{self, *}; -use crate::ext::build::AstBuilder; -use crate::parse::{self, token, DirectoryOwnership}; -use crate::print::pprust; -use crate::ptr::P; -use crate::symbol::Symbol; -use crate::tokenstream; - -use smallvec::SmallVec; -use syntax_pos::{self, Pos, Span}; - -use std::fs; -use std::io::ErrorKind; -use rustc_data_structures::sync::Lrc; - -// These macros all relate to the file system; they either return -// the column/row/filename of the expression, or they include -// a given file into the current one. - -/// line!(): expands to the current line number -pub fn expand_line(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) - -> Box { - base::check_zero_tts(cx, sp, tts, "line!"); - - let topmost = cx.expansion_cause().unwrap_or(sp); - let loc = cx.source_map().lookup_char_pos(topmost.lo()); - - base::MacEager::expr(cx.expr_u32(topmost, loc.line as u32)) -} - -/* column!(): expands to the current column number */ -pub fn expand_column(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) - -> Box { - base::check_zero_tts(cx, sp, tts, "column!"); - - let topmost = cx.expansion_cause().unwrap_or(sp); - let loc = cx.source_map().lookup_char_pos(topmost.lo()); - - base::MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32 + 1)) -} - -/// file!(): expands to the current filename */ -/// The source_file (`loc.file`) contains a bunch more information we could spit -/// out if we wanted. -pub fn expand_file(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) - -> Box { - base::check_zero_tts(cx, sp, tts, "file!"); - - let topmost = cx.expansion_cause().unwrap_or(sp); - let loc = cx.source_map().lookup_char_pos(topmost.lo()); - base::MacEager::expr(cx.expr_str(topmost, Symbol::intern(&loc.file.name.to_string()))) -} - -pub fn expand_stringify(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) - -> Box { - let s = pprust::tts_to_string(tts); - base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&s))) -} - -pub fn expand_mod(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) - -> Box { - base::check_zero_tts(cx, sp, tts, "module_path!"); - let mod_path = &cx.current_expansion.module.mod_path; - let string = mod_path.iter().map(|x| x.to_string()).collect::>().join("::"); - - base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&string))) -} - -/// include! : parse the given file as an expr -/// This is generally a bad idea because it's going to behave -/// unhygienically. -pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) - -> Box { - let file = match get_single_str_from_tts(cx, sp, tts, "include!") { - Some(f) => f, - None => return DummyResult::any(sp), - }; - // The file will be added to the code map by the parser - let file = cx.resolve_path(file, sp); - let directory_ownership = DirectoryOwnership::Owned { relative: None }; - let p = parse::new_sub_parser_from_file(cx.parse_sess(), &file, directory_ownership, None, sp); - - struct ExpandResult<'a> { - p: parse::parser::Parser<'a>, - } - impl<'a> base::MacResult for ExpandResult<'a> { - fn make_expr(mut self: Box>) -> Option> { - Some(panictry!(self.p.parse_expr())) - } - - fn make_items(mut self: Box>) -> Option; 1]>> { - let mut ret = SmallVec::new(); - while self.p.token != token::Eof { - match panictry!(self.p.parse_item()) { - Some(item) => ret.push(item), - None => self.p.diagnostic().span_fatal(self.p.token.span, - &format!("expected item, found `{}`", - self.p.this_token_to_string())) - .raise() - } - } - Some(ret) - } - } - - Box::new(ExpandResult { p }) -} - -// include_str! : read the given file, insert it as a literal string expr -pub fn expand_include_str(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) - -> Box { - let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") { - Some(f) => f, - None => return DummyResult::expr(sp) - }; - let file = cx.resolve_path(file, sp); - match fs::read_to_string(&file) { - Ok(src) => { - let interned_src = Symbol::intern(&src); - - // Add this input file to the code map to make it available as - // dependency information - cx.source_map().new_source_file(file.into(), src); - - base::MacEager::expr(cx.expr_str(sp, interned_src)) - }, - Err(ref e) if e.kind() == ErrorKind::InvalidData => { - cx.span_err(sp, &format!("{} wasn't a utf-8 file", file.display())); - DummyResult::expr(sp) - } - Err(e) => { - cx.span_err(sp, &format!("couldn't read {}: {}", file.display(), e)); - DummyResult::expr(sp) - } - } -} - -pub fn expand_include_bytes(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) - -> Box { - let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") { - Some(f) => f, - None => return DummyResult::expr(sp) - }; - let file = cx.resolve_path(file, sp); - match fs::read(&file) { - Ok(bytes) => { - // Add the contents to the source map if it contains UTF-8. - let (contents, bytes) = match String::from_utf8(bytes) { - Ok(s) => { - let bytes = s.as_bytes().to_owned(); - (s, bytes) - }, - Err(e) => (String::new(), e.into_bytes()), - }; - cx.source_map().new_source_file(file.into(), contents); - - base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::new(bytes)))) - }, - Err(e) => { - cx.span_err(sp, &format!("couldn't read {}: {}", file.display(), e)); - DummyResult::expr(sp) - } - } -} diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 75b4e89ec01..7471fb45325 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -37,6 +37,7 @@ const MACRO_ARGUMENTS: Option<&'static str> = Some("macro arguments"); // way towards a non-panic!-prone parser. It should be used for fatal parsing // errors; eventually we plan to convert all code using panictry to just use // normal try. +#[macro_export] macro_rules! panictry { ($e:expr) => ({ use std::result::Result::{Ok, Err}; @@ -168,7 +169,7 @@ pub mod ext { pub mod derive; pub mod expand; pub mod placeholders; - pub mod source_util; + pub mod proc_macro; pub mod tt { pub mod transcribe; diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 7940abed245..2c5a9ab0512 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -39,6 +39,7 @@ mod global_allocator; mod global_asm; mod log_syntax; mod proc_macro_server; +mod source_util; mod test; mod test_case; mod trace_macros; diff --git a/src/libsyntax_ext/proc_macro_decls.rs b/src/libsyntax_ext/proc_macro_decls.rs index 303d5f00deb..357da6ba3c3 100644 --- a/src/libsyntax_ext/proc_macro_decls.rs +++ b/src/libsyntax_ext/proc_macro_decls.rs @@ -7,21 +7,15 @@ use syntax::ext::base::{ExtCtxt, MacroKind}; use syntax::ext::build::AstBuilder; use syntax::ext::expand::ExpansionConfig; use syntax::ext::hygiene::ExpnId; +use syntax::ext::proc_macro::is_proc_macro_attr; use syntax::mut_visit::MutVisitor; use syntax::parse::ParseSess; use syntax::ptr::P; -use syntax::symbol::Symbol; use syntax::symbol::{kw, sym}; use syntax::visit::{self, Visitor}; use syntax_pos::{Span, DUMMY_SP}; -const PROC_MACRO_KINDS: [Symbol; 3] = [ - sym::proc_macro_derive, - sym::proc_macro_attribute, - sym::proc_macro -]; - struct ProcMacroDerive { trait_name: ast::Name, function_name: Ident, @@ -88,10 +82,6 @@ pub fn modify(sess: &ParseSess, krate } -pub fn is_proc_macro_attr(attr: &ast::Attribute) -> bool { - PROC_MACRO_KINDS.iter().any(|kind| attr.check_name(*kind)) -} - impl<'a> CollectProcMacros<'a> { fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { if self.is_proc_macro_crate && self.in_root && vis.node.is_pub() { diff --git a/src/libsyntax_ext/source_util.rs b/src/libsyntax_ext/source_util.rs new file mode 100644 index 00000000000..8ecfd4ddda7 --- /dev/null +++ b/src/libsyntax_ext/source_util.rs @@ -0,0 +1,165 @@ +use syntax::{ast, panictry}; +use syntax::ext::base::{self, *}; +use syntax::ext::build::AstBuilder; +use syntax::parse::{self, token, DirectoryOwnership}; +use syntax::print::pprust; +use syntax::ptr::P; +use syntax::symbol::Symbol; +use syntax::tokenstream; + +use smallvec::SmallVec; +use syntax_pos::{self, Pos, Span}; + +use std::fs; +use std::io::ErrorKind; +use rustc_data_structures::sync::Lrc; + +// These macros all relate to the file system; they either return +// the column/row/filename of the expression, or they include +// a given file into the current one. + +/// line!(): expands to the current line number +pub fn expand_line(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) + -> Box { + base::check_zero_tts(cx, sp, tts, "line!"); + + let topmost = cx.expansion_cause().unwrap_or(sp); + let loc = cx.source_map().lookup_char_pos(topmost.lo()); + + base::MacEager::expr(cx.expr_u32(topmost, loc.line as u32)) +} + +/* column!(): expands to the current column number */ +pub fn expand_column(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) + -> Box { + base::check_zero_tts(cx, sp, tts, "column!"); + + let topmost = cx.expansion_cause().unwrap_or(sp); + let loc = cx.source_map().lookup_char_pos(topmost.lo()); + + base::MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32 + 1)) +} + +/// file!(): expands to the current filename */ +/// The source_file (`loc.file`) contains a bunch more information we could spit +/// out if we wanted. +pub fn expand_file(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) + -> Box { + base::check_zero_tts(cx, sp, tts, "file!"); + + let topmost = cx.expansion_cause().unwrap_or(sp); + let loc = cx.source_map().lookup_char_pos(topmost.lo()); + base::MacEager::expr(cx.expr_str(topmost, Symbol::intern(&loc.file.name.to_string()))) +} + +pub fn expand_stringify(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) + -> Box { + let s = pprust::tts_to_string(tts); + base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&s))) +} + +pub fn expand_mod(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) + -> Box { + base::check_zero_tts(cx, sp, tts, "module_path!"); + let mod_path = &cx.current_expansion.module.mod_path; + let string = mod_path.iter().map(|x| x.to_string()).collect::>().join("::"); + + base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&string))) +} + +/// include! : parse the given file as an expr +/// This is generally a bad idea because it's going to behave +/// unhygienically. +pub fn expand_include<'cx>(cx: &'cx mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) + -> Box { + let file = match get_single_str_from_tts(cx, sp, tts, "include!") { + Some(f) => f, + None => return DummyResult::any(sp), + }; + // The file will be added to the code map by the parser + let file = cx.resolve_path(file, sp); + let directory_ownership = DirectoryOwnership::Owned { relative: None }; + let p = parse::new_sub_parser_from_file(cx.parse_sess(), &file, directory_ownership, None, sp); + + struct ExpandResult<'a> { + p: parse::parser::Parser<'a>, + } + impl<'a> base::MacResult for ExpandResult<'a> { + fn make_expr(mut self: Box>) -> Option> { + Some(panictry!(self.p.parse_expr())) + } + + fn make_items(mut self: Box>) -> Option; 1]>> { + let mut ret = SmallVec::new(); + while self.p.token != token::Eof { + match panictry!(self.p.parse_item()) { + Some(item) => ret.push(item), + None => self.p.sess.span_diagnostic.span_fatal(self.p.token.span, + &format!("expected item, found `{}`", + self.p.this_token_to_string())) + .raise() + } + } + Some(ret) + } + } + + Box::new(ExpandResult { p }) +} + +// include_str! : read the given file, insert it as a literal string expr +pub fn expand_include_str(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) + -> Box { + let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") { + Some(f) => f, + None => return DummyResult::expr(sp) + }; + let file = cx.resolve_path(file, sp); + match fs::read_to_string(&file) { + Ok(src) => { + let interned_src = Symbol::intern(&src); + + // Add this input file to the code map to make it available as + // dependency information + cx.source_map().new_source_file(file.into(), src); + + base::MacEager::expr(cx.expr_str(sp, interned_src)) + }, + Err(ref e) if e.kind() == ErrorKind::InvalidData => { + cx.span_err(sp, &format!("{} wasn't a utf-8 file", file.display())); + DummyResult::expr(sp) + } + Err(e) => { + cx.span_err(sp, &format!("couldn't read {}: {}", file.display(), e)); + DummyResult::expr(sp) + } + } +} + +pub fn expand_include_bytes(cx: &mut ExtCtxt<'_>, sp: Span, tts: &[tokenstream::TokenTree]) + -> Box { + let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") { + Some(f) => f, + None => return DummyResult::expr(sp) + }; + let file = cx.resolve_path(file, sp); + match fs::read(&file) { + Ok(bytes) => { + // Add the contents to the source map if it contains UTF-8. + let (contents, bytes) = match String::from_utf8(bytes) { + Ok(s) => { + let bytes = s.as_bytes().to_owned(); + (s, bytes) + }, + Err(e) => (String::new(), e.into_bytes()), + }; + cx.source_map().new_source_file(file.into(), contents); + + base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::new(bytes)))) + }, + Err(e) => { + cx.span_err(sp, &format!("couldn't read {}: {}", file.display(), e)); + DummyResult::expr(sp) + } + } +} -- cgit 1.4.1-3-g733a5 From 4ad0daa220fd5e00ce425c9208869ea0d6f4b981 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 18 Jul 2019 21:02:34 +0300 Subject: Move proc macro server into libsyntax --- Cargo.lock | 1 - src/librustc_metadata/Cargo.toml | 1 - src/librustc_metadata/creader.rs | 3 +- src/librustc_metadata/cstore_impl.rs | 2 +- src/libsyntax/ext/derive.rs | 72 --- src/libsyntax/ext/expand.rs | 2 +- src/libsyntax/ext/proc_macro.rs | 246 +++++++++- src/libsyntax/ext/proc_macro_server.rs | 715 +++++++++++++++++++++++++++++ src/libsyntax/lib.rs | 10 +- src/libsyntax_ext/deriving/custom.rs | 119 ----- src/libsyntax_ext/deriving/generic/mod.rs | 44 -- src/libsyntax_ext/deriving/generic/ty.rs | 8 +- src/libsyntax_ext/deriving/mod.rs | 1 - src/libsyntax_ext/lib.rs | 7 +- src/libsyntax_ext/proc_macro_impl.rs | 68 --- src/libsyntax_ext/proc_macro_server.rs | 717 ------------------------------ 16 files changed, 972 insertions(+), 1044 deletions(-) delete mode 100644 src/libsyntax/ext/derive.rs create mode 100644 src/libsyntax/ext/proc_macro_server.rs delete mode 100644 src/libsyntax_ext/deriving/custom.rs delete mode 100644 src/libsyntax_ext/proc_macro_impl.rs delete mode 100644 src/libsyntax_ext/proc_macro_server.rs diff --git a/Cargo.lock b/Cargo.lock index a8246eed853..d1afe5944ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3008,7 +3008,6 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "stable_deref_trait 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntax 0.0.0", - "syntax_ext 0.0.0", "syntax_pos 0.0.0", ] diff --git a/src/librustc_metadata/Cargo.toml b/src/librustc_metadata/Cargo.toml index a9f054d5e8b..5ff60a9267b 100644 --- a/src/librustc_metadata/Cargo.toml +++ b/src/librustc_metadata/Cargo.toml @@ -21,5 +21,4 @@ rustc_target = { path = "../librustc_target" } rustc_serialize = { path = "../libserialize", package = "serialize" } stable_deref_trait = "1.0.0" syntax = { path = "../libsyntax" } -syntax_ext = { path = "../libsyntax_ext" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 3404ec5e173..d5f1e715186 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -586,8 +586,7 @@ impl<'a> CrateLoader<'a> { use std::{env, mem}; use crate::dynamic_lib::DynamicLibrary; use proc_macro::bridge::client::ProcMacro; - use syntax_ext::deriving::custom::ProcMacroDerive; - use syntax_ext::proc_macro_impl::{AttrProcMacro, BangProcMacro}; + use syntax::ext::proc_macro::{BangProcMacro, AttrProcMacro, ProcMacroDerive}; let path = match dylib { Some(dylib) => dylib, diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 914084d7e9e..ee1175e798d 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -31,10 +31,10 @@ use syntax::attr; use syntax::source_map; use syntax::edition::Edition; use syntax::ext::base::{SyntaxExtension, SyntaxExtensionKind}; +use syntax::ext::proc_macro::BangProcMacro; use syntax::parse::source_file_to_stream; use syntax::parse::parser::emit_unclosed_delims; use syntax::symbol::{Symbol, sym}; -use syntax_ext::proc_macro_impl::BangProcMacro; use syntax_pos::{Span, NO_EXPANSION, FileName}; use rustc_data_structures::bit_set::BitSet; diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs deleted file mode 100644 index ff9ad46deec..00000000000 --- a/src/libsyntax/ext/derive.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::attr::HasAttrs; -use crate::ast; -use crate::source_map::{ExpnInfo, ExpnKind}; -use crate::ext::base::{ExtCtxt, MacroKind}; -use crate::ext::build::AstBuilder; -use crate::parse::parser::PathStyle; -use crate::symbol::{Symbol, sym}; -use crate::errors::Applicability; - -use syntax_pos::Span; -use rustc_data_structures::fx::FxHashSet; - -pub fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec) -> Vec { - let mut result = Vec::new(); - attrs.retain(|attr| { - if attr.path != sym::derive { - return true; - } - if !attr.is_meta_item_list() { - cx.struct_span_err(attr.span, "malformed `derive` attribute input") - .span_suggestion( - attr.span, - "missing traits to be derived", - "#[derive(Trait1, Trait2, ...)]".to_owned(), - Applicability::HasPlaceholders, - ).emit(); - return false; - } - - match attr.parse_list(cx.parse_sess, - |parser| parser.parse_path_allowing_meta(PathStyle::Mod)) { - Ok(traits) => { - result.extend(traits); - true - } - Err(mut e) => { - e.emit(); - false - } - } - }); - result -} - -pub fn add_derived_markers(cx: &mut ExtCtxt<'_>, span: Span, traits: &[ast::Path], item: &mut T) - where T: HasAttrs, -{ - let (mut names, mut pretty_name) = (FxHashSet::default(), String::new()); - for (i, path) in traits.iter().enumerate() { - if i > 0 { - pretty_name.push_str(", "); - } - pretty_name.push_str(&path.to_string()); - names.insert(unwrap_or!(path.segments.get(0), continue).ident.name); - } - - let span = span.fresh_expansion(cx.current_expansion.id, ExpnInfo::allow_unstable( - ExpnKind::Macro(MacroKind::Derive, Symbol::intern(&pretty_name)), span, - cx.parse_sess.edition, cx.allow_derive_markers.clone(), - )); - - item.visit_attrs(|attrs| { - if names.contains(&sym::Eq) && names.contains(&sym::PartialEq) { - let meta = cx.meta_word(span, sym::structural_match); - attrs.push(cx.attribute(span, meta)); - } - if names.contains(&sym::Copy) { - let meta = cx.meta_word(span, sym::rustc_copy_clone_marker); - attrs.push(cx.attribute(span, meta)); - } - }); -} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 640daaccc3a..cd602d08c5b 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -4,7 +4,7 @@ use crate::attr::{self, HasAttrs}; use crate::source_map::{dummy_spanned, respan}; use crate::config::StripUnconfigured; use crate::ext::base::*; -use crate::ext::derive::{add_derived_markers, collect_derives}; +use crate::ext::proc_macro::{add_derived_markers, collect_derives}; use crate::ext::hygiene::{ExpnId, SyntaxContext, ExpnInfo, ExpnKind}; use crate::ext::placeholders::{placeholder, PlaceholderExpander}; use crate::feature_gate::{self, Features, GateIssue, is_builtin_attr, emit_feature_err}; diff --git a/src/libsyntax/ext/proc_macro.rs b/src/libsyntax/ext/proc_macro.rs index 3aac79e241a..4b4ac57207e 100644 --- a/src/libsyntax/ext/proc_macro.rs +++ b/src/libsyntax/ext/proc_macro.rs @@ -1,7 +1,249 @@ -use crate::ast::Attribute; -use crate::symbol::sym; +use crate::ast::{self, ItemKind, Attribute, Mac}; +use crate::attr::{mark_used, mark_known, HasAttrs}; +use crate::errors::{Applicability, FatalError}; +use crate::ext::base::{self, *}; +use crate::ext::build::AstBuilder; +use crate::ext::proc_macro_server; +use crate::parse::{self, token}; +use crate::parse::parser::PathStyle; +use crate::symbol::{sym, Symbol}; +use crate::tokenstream::{self, TokenStream}; +use crate::visit::Visitor; + +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sync::Lrc; +use syntax_pos::hygiene::{ExpnInfo, ExpnKind}; +use syntax_pos::{Span, DUMMY_SP}; + +const EXEC_STRATEGY: proc_macro::bridge::server::SameThread = + proc_macro::bridge::server::SameThread; + +pub struct BangProcMacro { + pub client: proc_macro::bridge::client::Client< + fn(proc_macro::TokenStream) -> proc_macro::TokenStream, + >, +} + +impl base::ProcMacro for BangProcMacro { + fn expand<'cx>(&self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + input: TokenStream) + -> TokenStream { + let server = proc_macro_server::Rustc::new(ecx); + match self.client.run(&EXEC_STRATEGY, server, input) { + Ok(stream) => stream, + Err(e) => { + let msg = "proc macro panicked"; + let mut err = ecx.struct_span_fatal(span, msg); + if let Some(s) = e.as_str() { + err.help(&format!("message: {}", s)); + } + + err.emit(); + FatalError.raise(); + } + } + } +} + +pub struct AttrProcMacro { + pub client: proc_macro::bridge::client::Client< + fn(proc_macro::TokenStream, proc_macro::TokenStream) -> proc_macro::TokenStream, + >, +} + +impl base::AttrProcMacro for AttrProcMacro { + fn expand<'cx>(&self, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, + annotation: TokenStream, + annotated: TokenStream) + -> TokenStream { + let server = proc_macro_server::Rustc::new(ecx); + match self.client.run(&EXEC_STRATEGY, server, annotation, annotated) { + Ok(stream) => stream, + Err(e) => { + let msg = "custom attribute panicked"; + let mut err = ecx.struct_span_fatal(span, msg); + if let Some(s) = e.as_str() { + err.help(&format!("message: {}", s)); + } + + err.emit(); + FatalError.raise(); + } + } + } +} + +pub struct ProcMacroDerive { + pub client: proc_macro::bridge::client::Client< + fn(proc_macro::TokenStream) -> proc_macro::TokenStream, + >, + pub attrs: Vec, +} + +impl MultiItemModifier for ProcMacroDerive { + fn expand(&self, + ecx: &mut ExtCtxt<'_>, + span: Span, + _meta_item: &ast::MetaItem, + item: Annotatable) + -> Vec { + let item = match item { + Annotatable::Item(item) => item, + Annotatable::ImplItem(_) | + Annotatable::TraitItem(_) | + Annotatable::ForeignItem(_) | + Annotatable::Stmt(_) | + Annotatable::Expr(_) => { + ecx.span_err(span, "proc-macro derives may only be \ + applied to a struct, enum, or union"); + return Vec::new() + } + }; + match item.node { + ItemKind::Struct(..) | + ItemKind::Enum(..) | + ItemKind::Union(..) => {}, + _ => { + ecx.span_err(span, "proc-macro derives may only be \ + applied to a struct, enum, or union"); + return Vec::new() + } + } + + // Mark attributes as known, and used. + MarkAttrs(&self.attrs).visit_item(&item); + + let token = token::Interpolated(Lrc::new(token::NtItem(item))); + let input = tokenstream::TokenTree::token(token, DUMMY_SP).into(); + + let server = proc_macro_server::Rustc::new(ecx); + let stream = match self.client.run(&EXEC_STRATEGY, server, input) { + Ok(stream) => stream, + Err(e) => { + let msg = "proc-macro derive panicked"; + let mut err = ecx.struct_span_fatal(span, msg); + if let Some(s) = e.as_str() { + err.help(&format!("message: {}", s)); + } + + err.emit(); + FatalError.raise(); + } + }; + + let error_count_before = ecx.parse_sess.span_diagnostic.err_count(); + let msg = "proc-macro derive produced unparseable tokens"; + + let mut parser = parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive")); + let mut items = vec![]; + + loop { + match parser.parse_item() { + Ok(None) => break, + Ok(Some(item)) => { + items.push(Annotatable::Item(item)) + } + Err(mut err) => { + // FIXME: handle this better + err.cancel(); + ecx.struct_span_fatal(span, msg).emit(); + FatalError.raise(); + } + } + } + + + // fail if there have been errors emitted + if ecx.parse_sess.span_diagnostic.err_count() > error_count_before { + ecx.struct_span_fatal(span, msg).emit(); + FatalError.raise(); + } + + items + } +} + +struct MarkAttrs<'a>(&'a [ast::Name]); + +impl<'a> Visitor<'a> for MarkAttrs<'a> { + fn visit_attribute(&mut self, attr: &Attribute) { + if let Some(ident) = attr.ident() { + if self.0.contains(&ident.name) { + mark_used(attr); + mark_known(attr); + } + } + } + + fn visit_mac(&mut self, _mac: &Mac) {} +} pub fn is_proc_macro_attr(attr: &Attribute) -> bool { [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] .iter().any(|kind| attr.check_name(*kind)) } + +crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec) -> Vec { + let mut result = Vec::new(); + attrs.retain(|attr| { + if attr.path != sym::derive { + return true; + } + if !attr.is_meta_item_list() { + cx.struct_span_err(attr.span, "malformed `derive` attribute input") + .span_suggestion( + attr.span, + "missing traits to be derived", + "#[derive(Trait1, Trait2, ...)]".to_owned(), + Applicability::HasPlaceholders, + ).emit(); + return false; + } + + match attr.parse_list(cx.parse_sess, + |parser| parser.parse_path_allowing_meta(PathStyle::Mod)) { + Ok(traits) => { + result.extend(traits); + true + } + Err(mut e) => { + e.emit(); + false + } + } + }); + result +} + +crate fn add_derived_markers( + cx: &mut ExtCtxt<'_>, span: Span, traits: &[ast::Path], item: &mut T +) { + let (mut names, mut pretty_name) = (FxHashSet::default(), String::new()); + for (i, path) in traits.iter().enumerate() { + if i > 0 { + pretty_name.push_str(", "); + } + pretty_name.push_str(&path.to_string()); + names.insert(unwrap_or!(path.segments.get(0), continue).ident.name); + } + + let span = span.fresh_expansion(cx.current_expansion.mark, ExpnInfo::allow_unstable( + ExpnKind::Macro(MacroKind::Derive, Symbol::intern(&pretty_name)), span, + cx.parse_sess.edition, cx.allow_derive_markers.clone(), + )); + + item.visit_attrs(|attrs| { + if names.contains(&sym::Eq) && names.contains(&sym::PartialEq) { + let meta = cx.meta_word(span, sym::structural_match); + attrs.push(cx.attribute(span, meta)); + } + if names.contains(&sym::Copy) { + let meta = cx.meta_word(span, sym::rustc_copy_clone_marker); + attrs.push(cx.attribute(span, meta)); + } + }); +} diff --git a/src/libsyntax/ext/proc_macro_server.rs b/src/libsyntax/ext/proc_macro_server.rs new file mode 100644 index 00000000000..8d0023c9ab1 --- /dev/null +++ b/src/libsyntax/ext/proc_macro_server.rs @@ -0,0 +1,715 @@ +use crate::ast; +use crate::ext::base::ExtCtxt; +use crate::parse::{self, token, ParseSess}; +use crate::parse::lexer::comments; +use crate::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint}; + +use errors::{Diagnostic, DiagnosticBuilder}; +use rustc_data_structures::sync::Lrc; +use syntax_pos::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span}; +use syntax_pos::hygiene::{SyntaxContext, Transparency}; +use syntax_pos::symbol::{kw, sym, Symbol}; + +use proc_macro::{Delimiter, Level, LineColumn, Spacing}; +use proc_macro::bridge::{server, TokenTree}; +use std::{ascii, panic}; +use std::ops::Bound; + +trait FromInternal { + fn from_internal(x: T) -> Self; +} + +trait ToInternal { + fn to_internal(self) -> T; +} + +impl FromInternal for Delimiter { + fn from_internal(delim: token::DelimToken) -> Delimiter { + match delim { + token::Paren => Delimiter::Parenthesis, + token::Brace => Delimiter::Brace, + token::Bracket => Delimiter::Bracket, + token::NoDelim => Delimiter::None, + } + } +} + +impl ToInternal for Delimiter { + fn to_internal(self) -> token::DelimToken { + match self { + Delimiter::Parenthesis => token::Paren, + Delimiter::Brace => token::Brace, + Delimiter::Bracket => token::Bracket, + Delimiter::None => token::NoDelim, + } + } +} + +impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec)> + for TokenTree +{ + fn from_internal(((tree, is_joint), sess, stack): (TreeAndJoint, &ParseSess, &mut Vec)) + -> Self { + use crate::parse::token::*; + + let joint = is_joint == Joint; + let Token { kind, span } = match tree { + tokenstream::TokenTree::Delimited(span, delim, tts) => { + let delimiter = Delimiter::from_internal(delim); + return TokenTree::Group(Group { + delimiter, + stream: tts.into(), + span, + }); + } + tokenstream::TokenTree::Token(token) => token, + }; + + macro_rules! tt { + ($ty:ident { $($field:ident $(: $value:expr)*),+ $(,)? }) => ( + TokenTree::$ty(self::$ty { + $($field $(: $value)*,)+ + span, + }) + ); + ($ty:ident::$method:ident($($value:expr),*)) => ( + TokenTree::$ty(self::$ty::$method($($value,)* span)) + ); + } + macro_rules! op { + ($a:expr) => { + tt!(Punct::new($a, joint)) + }; + ($a:expr, $b:expr) => {{ + stack.push(tt!(Punct::new($b, joint))); + tt!(Punct::new($a, true)) + }}; + ($a:expr, $b:expr, $c:expr) => {{ + stack.push(tt!(Punct::new($c, joint))); + stack.push(tt!(Punct::new($b, true))); + tt!(Punct::new($a, true)) + }}; + } + + match kind { + Eq => op!('='), + Lt => op!('<'), + Le => op!('<', '='), + EqEq => op!('=', '='), + Ne => op!('!', '='), + Ge => op!('>', '='), + Gt => op!('>'), + AndAnd => op!('&', '&'), + OrOr => op!('|', '|'), + Not => op!('!'), + Tilde => op!('~'), + BinOp(Plus) => op!('+'), + BinOp(Minus) => op!('-'), + BinOp(Star) => op!('*'), + BinOp(Slash) => op!('/'), + BinOp(Percent) => op!('%'), + BinOp(Caret) => op!('^'), + BinOp(And) => op!('&'), + BinOp(Or) => op!('|'), + BinOp(Shl) => op!('<', '<'), + BinOp(Shr) => op!('>', '>'), + BinOpEq(Plus) => op!('+', '='), + BinOpEq(Minus) => op!('-', '='), + BinOpEq(Star) => op!('*', '='), + BinOpEq(Slash) => op!('/', '='), + BinOpEq(Percent) => op!('%', '='), + BinOpEq(Caret) => op!('^', '='), + BinOpEq(And) => op!('&', '='), + BinOpEq(Or) => op!('|', '='), + BinOpEq(Shl) => op!('<', '<', '='), + BinOpEq(Shr) => op!('>', '>', '='), + At => op!('@'), + Dot => op!('.'), + DotDot => op!('.', '.'), + DotDotDot => op!('.', '.', '.'), + DotDotEq => op!('.', '.', '='), + Comma => op!(','), + Semi => op!(';'), + Colon => op!(':'), + ModSep => op!(':', ':'), + RArrow => op!('-', '>'), + LArrow => op!('<', '-'), + FatArrow => op!('=', '>'), + Pound => op!('#'), + Dollar => op!('$'), + Question => op!('?'), + SingleQuote => op!('\''), + + Ident(name, false) if name == kw::DollarCrate => tt!(Ident::dollar_crate()), + Ident(name, is_raw) => tt!(Ident::new(name, is_raw)), + Lifetime(name) => { + let ident = ast::Ident::new(name, span).without_first_quote(); + stack.push(tt!(Ident::new(ident.name, false))); + tt!(Punct::new('\'', true)) + } + Literal(lit) => tt!(Literal { lit }), + DocComment(c) => { + let style = comments::doc_comment_style(&c.as_str()); + let stripped = comments::strip_doc_comment_decoration(&c.as_str()); + let mut escaped = String::new(); + for ch in stripped.chars() { + escaped.extend(ch.escape_debug()); + } + let stream = vec![ + Ident(sym::doc, false), + Eq, + TokenKind::lit(token::Str, Symbol::intern(&escaped), None), + ] + .into_iter() + .map(|kind| tokenstream::TokenTree::token(kind, span)) + .collect(); + stack.push(TokenTree::Group(Group { + delimiter: Delimiter::Bracket, + stream, + span: DelimSpan::from_single(span), + })); + if style == ast::AttrStyle::Inner { + stack.push(tt!(Punct::new('!', false))); + } + tt!(Punct::new('#', false)) + } + + Interpolated(nt) => { + let stream = nt.to_tokenstream(sess, span); + TokenTree::Group(Group { + delimiter: Delimiter::None, + stream, + span: DelimSpan::from_single(span), + }) + } + + OpenDelim(..) | CloseDelim(..) => unreachable!(), + Whitespace | Comment | Shebang(..) | Eof => unreachable!(), + } + } +} + +impl ToInternal for TokenTree { + fn to_internal(self) -> TokenStream { + use crate::parse::token::*; + + let (ch, joint, span) = match self { + TokenTree::Punct(Punct { ch, joint, span }) => (ch, joint, span), + TokenTree::Group(Group { + delimiter, + stream, + span, + }) => { + return tokenstream::TokenTree::Delimited( + span, + delimiter.to_internal(), + stream.into(), + ) + .into(); + } + TokenTree::Ident(self::Ident { sym, is_raw, span }) => { + return tokenstream::TokenTree::token(Ident(sym, is_raw), span).into(); + } + TokenTree::Literal(self::Literal { + lit: token::Lit { kind: token::Integer, symbol, suffix }, + span, + }) if symbol.as_str().starts_with("-") => { + let minus = BinOp(BinOpToken::Minus); + let symbol = Symbol::intern(&symbol.as_str()[1..]); + let integer = TokenKind::lit(token::Integer, symbol, suffix); + let a = tokenstream::TokenTree::token(minus, span); + let b = tokenstream::TokenTree::token(integer, span); + return vec![a, b].into_iter().collect(); + } + TokenTree::Literal(self::Literal { + lit: token::Lit { kind: token::Float, symbol, suffix }, + span, + }) if symbol.as_str().starts_with("-") => { + let minus = BinOp(BinOpToken::Minus); + let symbol = Symbol::intern(&symbol.as_str()[1..]); + let float = TokenKind::lit(token::Float, symbol, suffix); + let a = tokenstream::TokenTree::token(minus, span); + let b = tokenstream::TokenTree::token(float, span); + return vec![a, b].into_iter().collect(); + } + TokenTree::Literal(self::Literal { lit, span }) => { + return tokenstream::TokenTree::token(Literal(lit), span).into() + } + }; + + let kind = match ch { + '=' => Eq, + '<' => Lt, + '>' => Gt, + '!' => Not, + '~' => Tilde, + '+' => BinOp(Plus), + '-' => BinOp(Minus), + '*' => BinOp(Star), + '/' => BinOp(Slash), + '%' => BinOp(Percent), + '^' => BinOp(Caret), + '&' => BinOp(And), + '|' => BinOp(Or), + '@' => At, + '.' => Dot, + ',' => Comma, + ';' => Semi, + ':' => Colon, + '#' => Pound, + '$' => Dollar, + '?' => Question, + '\'' => SingleQuote, + _ => unreachable!(), + }; + + let tree = tokenstream::TokenTree::token(kind, span); + TokenStream::new(vec![(tree, if joint { Joint } else { NonJoint })]) + } +} + +impl ToInternal for Level { + fn to_internal(self) -> errors::Level { + match self { + Level::Error => errors::Level::Error, + Level::Warning => errors::Level::Warning, + Level::Note => errors::Level::Note, + Level::Help => errors::Level::Help, + _ => unreachable!("unknown proc_macro::Level variant: {:?}", self), + } + } +} + +#[derive(Clone)] +pub struct TokenStreamIter { + cursor: tokenstream::Cursor, + stack: Vec>, +} + +#[derive(Clone)] +pub struct Group { + delimiter: Delimiter, + stream: TokenStream, + span: DelimSpan, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Punct { + ch: char, + // NB. not using `Spacing` here because it doesn't implement `Hash`. + joint: bool, + span: Span, +} + +impl Punct { + fn new(ch: char, joint: bool, span: Span) -> Punct { + const LEGAL_CHARS: &[char] = &['=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^', + '&', '|', '@', '.', ',', ';', ':', '#', '$', '?', '\'']; + if !LEGAL_CHARS.contains(&ch) { + panic!("unsupported character `{:?}`", ch) + } + Punct { ch, joint, span } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct Ident { + sym: Symbol, + is_raw: bool, + span: Span, +} + +impl Ident { + fn is_valid(string: &str) -> bool { + let mut chars = string.chars(); + if let Some(start) = chars.next() { + (start == '_' || start.is_xid_start()) + && chars.all(|cont| cont == '_' || cont.is_xid_continue()) + } else { + false + } + } + fn new(sym: Symbol, is_raw: bool, span: Span) -> Ident { + let string = sym.as_str(); + if !Self::is_valid(&string) { + panic!("`{:?}` is not a valid identifier", string) + } + // Get rid of gensyms to conservatively check rawness on the string contents only. + if is_raw && !sym.as_interned_str().as_symbol().can_be_raw() { + panic!("`{}` cannot be a raw identifier", string); + } + Ident { sym, is_raw, span } + } + fn dollar_crate(span: Span) -> Ident { + // `$crate` is accepted as an ident only if it comes from the compiler. + Ident { sym: kw::DollarCrate, is_raw: false, span } + } +} + +// FIXME(eddyb) `Literal` should not expose internal `Debug` impls. +#[derive(Clone, Debug)] +pub struct Literal { + lit: token::Lit, + span: Span, +} + +pub(crate) struct Rustc<'a> { + sess: &'a ParseSess, + def_site: Span, + call_site: Span, +} + +impl<'a> Rustc<'a> { + pub fn new(cx: &'a ExtCtxt<'_>) -> Self { + // No way to determine def location for a proc macro right now, so use call location. + let location = cx.current_expansion.id.expn_info().unwrap().call_site; + let to_span = |transparency| { + location.with_ctxt( + SyntaxContext::empty() + .apply_mark_with_transparency(cx.current_expansion.id, transparency), + ) + }; + Rustc { + sess: cx.parse_sess, + def_site: to_span(Transparency::Opaque), + call_site: to_span(Transparency::Transparent), + } + } + + fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option) -> Literal { + Literal { + lit: token::Lit::new(kind, symbol, suffix), + span: server::Span::call_site(self), + } + } +} + +impl server::Types for Rustc<'_> { + type TokenStream = TokenStream; + type TokenStreamBuilder = tokenstream::TokenStreamBuilder; + type TokenStreamIter = TokenStreamIter; + type Group = Group; + type Punct = Punct; + type Ident = Ident; + type Literal = Literal; + type SourceFile = Lrc; + type MultiSpan = Vec; + type Diagnostic = Diagnostic; + type Span = Span; +} + +impl server::TokenStream for Rustc<'_> { + fn new(&mut self) -> Self::TokenStream { + TokenStream::empty() + } + fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { + stream.is_empty() + } + fn from_str(&mut self, src: &str) -> Self::TokenStream { + parse::parse_stream_from_source_str( + FileName::proc_macro_source_code(src), + src.to_string(), + self.sess, + Some(self.call_site), + ) + } + fn to_string(&mut self, stream: &Self::TokenStream) -> String { + stream.to_string() + } + fn from_token_tree( + &mut self, + tree: TokenTree, + ) -> Self::TokenStream { + tree.to_internal() + } + fn into_iter(&mut self, stream: Self::TokenStream) -> Self::TokenStreamIter { + TokenStreamIter { + cursor: stream.trees(), + stack: vec![], + } + } +} + +impl server::TokenStreamBuilder for Rustc<'_> { + fn new(&mut self) -> Self::TokenStreamBuilder { + tokenstream::TokenStreamBuilder::new() + } + fn push(&mut self, builder: &mut Self::TokenStreamBuilder, stream: Self::TokenStream) { + builder.push(stream); + } + fn build(&mut self, builder: Self::TokenStreamBuilder) -> Self::TokenStream { + builder.build() + } +} + +impl server::TokenStreamIter for Rustc<'_> { + fn next( + &mut self, + iter: &mut Self::TokenStreamIter, + ) -> Option> { + loop { + let tree = iter.stack.pop().or_else(|| { + let next = iter.cursor.next_with_joint()?; + Some(TokenTree::from_internal((next, self.sess, &mut iter.stack))) + })?; + // HACK: The condition "dummy span + group with empty delimiter" represents an AST + // fragment approximately converted into a token stream. This may happen, for + // example, with inputs to proc macro attributes, including derives. Such "groups" + // need to flattened during iteration over stream's token trees. + // Eventually this needs to be removed in favor of keeping original token trees + // and not doing the roundtrip through AST. + if let TokenTree::Group(ref group) = tree { + if group.delimiter == Delimiter::None && group.span.entire().is_dummy() { + iter.cursor.append(group.stream.clone()); + continue; + } + } + return Some(tree); + } + } +} + +impl server::Group for Rustc<'_> { + fn new(&mut self, delimiter: Delimiter, stream: Self::TokenStream) -> Self::Group { + Group { + delimiter, + stream, + span: DelimSpan::from_single(server::Span::call_site(self)), + } + } + fn delimiter(&mut self, group: &Self::Group) -> Delimiter { + group.delimiter + } + fn stream(&mut self, group: &Self::Group) -> Self::TokenStream { + group.stream.clone() + } + fn span(&mut self, group: &Self::Group) -> Self::Span { + group.span.entire() + } + fn span_open(&mut self, group: &Self::Group) -> Self::Span { + group.span.open + } + fn span_close(&mut self, group: &Self::Group) -> Self::Span { + group.span.close + } + fn set_span(&mut self, group: &mut Self::Group, span: Self::Span) { + group.span = DelimSpan::from_single(span); + } +} + +impl server::Punct for Rustc<'_> { + fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct { + Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self)) + } + fn as_char(&mut self, punct: Self::Punct) -> char { + punct.ch + } + fn spacing(&mut self, punct: Self::Punct) -> Spacing { + if punct.joint { + Spacing::Joint + } else { + Spacing::Alone + } + } + fn span(&mut self, punct: Self::Punct) -> Self::Span { + punct.span + } + fn with_span(&mut self, punct: Self::Punct, span: Self::Span) -> Self::Punct { + Punct { span, ..punct } + } +} + +impl server::Ident for Rustc<'_> { + fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident { + Ident::new(Symbol::intern(string), is_raw, span) + } + fn span(&mut self, ident: Self::Ident) -> Self::Span { + ident.span + } + fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident { + Ident { span, ..ident } + } +} + +impl server::Literal for Rustc<'_> { + // FIXME(eddyb) `Literal` should not expose internal `Debug` impls. + fn debug(&mut self, literal: &Self::Literal) -> String { + format!("{:?}", literal) + } + fn integer(&mut self, n: &str) -> Self::Literal { + self.lit(token::Integer, Symbol::intern(n), None) + } + fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { + self.lit(token::Integer, Symbol::intern(n), Some(Symbol::intern(kind))) + } + fn float(&mut self, n: &str) -> Self::Literal { + self.lit(token::Float, Symbol::intern(n), None) + } + fn f32(&mut self, n: &str) -> Self::Literal { + self.lit(token::Float, Symbol::intern(n), Some(sym::f32)) + } + fn f64(&mut self, n: &str) -> Self::Literal { + self.lit(token::Float, Symbol::intern(n), Some(sym::f64)) + } + fn string(&mut self, string: &str) -> Self::Literal { + let mut escaped = String::new(); + for ch in string.chars() { + escaped.extend(ch.escape_debug()); + } + self.lit(token::Str, Symbol::intern(&escaped), None) + } + fn character(&mut self, ch: char) -> Self::Literal { + let mut escaped = String::new(); + escaped.extend(ch.escape_unicode()); + self.lit(token::Char, Symbol::intern(&escaped), None) + } + fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal { + let string = bytes + .iter() + .cloned() + .flat_map(ascii::escape_default) + .map(Into::::into) + .collect::(); + self.lit(token::ByteStr, Symbol::intern(&string), None) + } + fn span(&mut self, literal: &Self::Literal) -> Self::Span { + literal.span + } + fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) { + literal.span = span; + } + fn subspan( + &mut self, + literal: &Self::Literal, + start: Bound, + end: Bound, + ) -> Option { + let span = literal.span; + let length = span.hi().to_usize() - span.lo().to_usize(); + + let start = match start { + Bound::Included(lo) => lo, + Bound::Excluded(lo) => lo + 1, + Bound::Unbounded => 0, + }; + + let end = match end { + Bound::Included(hi) => hi + 1, + Bound::Excluded(hi) => hi, + Bound::Unbounded => length, + }; + + // Bounds check the values, preventing addition overflow and OOB spans. + if start > u32::max_value() as usize + || end > u32::max_value() as usize + || (u32::max_value() - start as u32) < span.lo().to_u32() + || (u32::max_value() - end as u32) < span.lo().to_u32() + || start >= end + || end > length + { + return None; + } + + let new_lo = span.lo() + BytePos::from_usize(start); + let new_hi = span.lo() + BytePos::from_usize(end); + Some(span.with_lo(new_lo).with_hi(new_hi)) + } +} + +impl server::SourceFile for Rustc<'_> { + fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { + Lrc::ptr_eq(file1, file2) + } + fn path(&mut self, file: &Self::SourceFile) -> String { + match file.name { + FileName::Real(ref path) => path + .to_str() + .expect("non-UTF8 file path in `proc_macro::SourceFile::path`") + .to_string(), + _ => file.name.to_string(), + } + } + fn is_real(&mut self, file: &Self::SourceFile) -> bool { + file.is_real_file() + } +} + +impl server::MultiSpan for Rustc<'_> { + fn new(&mut self) -> Self::MultiSpan { + vec![] + } + fn push(&mut self, spans: &mut Self::MultiSpan, span: Self::Span) { + spans.push(span) + } +} + +impl server::Diagnostic for Rustc<'_> { + fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic { + let mut diag = Diagnostic::new(level.to_internal(), msg); + diag.set_span(MultiSpan::from_spans(spans)); + diag + } + fn sub( + &mut self, + diag: &mut Self::Diagnostic, + level: Level, + msg: &str, + spans: Self::MultiSpan, + ) { + diag.sub(level.to_internal(), msg, MultiSpan::from_spans(spans), None); + } + fn emit(&mut self, diag: Self::Diagnostic) { + DiagnosticBuilder::new_diagnostic(&self.sess.span_diagnostic, diag).emit() + } +} + +impl server::Span for Rustc<'_> { + fn debug(&mut self, span: Self::Span) -> String { + format!("{:?} bytes({}..{})", span.ctxt(), span.lo().0, span.hi().0) + } + fn def_site(&mut self) -> Self::Span { + self.def_site + } + fn call_site(&mut self) -> Self::Span { + self.call_site + } + fn source_file(&mut self, span: Self::Span) -> Self::SourceFile { + self.sess.source_map().lookup_char_pos(span.lo()).file + } + fn parent(&mut self, span: Self::Span) -> Option { + span.ctxt().outer_expn_info().map(|i| i.call_site) + } + fn source(&mut self, span: Self::Span) -> Self::Span { + span.source_callsite() + } + fn start(&mut self, span: Self::Span) -> LineColumn { + let loc = self.sess.source_map().lookup_char_pos(span.lo()); + LineColumn { + line: loc.line, + column: loc.col.to_usize(), + } + } + fn end(&mut self, span: Self::Span) -> LineColumn { + let loc = self.sess.source_map().lookup_char_pos(span.hi()); + LineColumn { + line: loc.line, + column: loc.col.to_usize(), + } + } + fn join(&mut self, first: Self::Span, second: Self::Span) -> Option { + let self_loc = self.sess.source_map().lookup_char_pos(first.lo()); + let other_loc = self.sess.source_map().lookup_char_pos(second.lo()); + + if self_loc.file.name != other_loc.file.name { + return None; + } + + Some(first.to(second)) + } + fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span { + span.with_ctxt(at.ctxt()) + } + fn source_text(&mut self, span: Self::Span) -> Option { + self.sess.source_map().span_to_snippet(span).ok() + } +} diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 7471fb45325..a4102ce45aa 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -18,12 +18,17 @@ #![feature(label_break_value)] #![feature(mem_take)] #![feature(nll)] +#![feature(proc_macro_diagnostic)] +#![feature(proc_macro_internals)] +#![feature(proc_macro_span)] #![feature(rustc_diagnostic_macros)] #![feature(try_trait)] #![feature(unicode_internals)] #![recursion_limit="256"] +extern crate proc_macro; + pub use errors; use rustc_data_structures::sync::Lock; use rustc_data_structures::bit_set::GrowableBitSet; @@ -162,13 +167,14 @@ pub mod print { } pub mod ext { + mod placeholders; + mod proc_macro_server; + pub use syntax_pos::hygiene; pub mod allocator; pub mod base; pub mod build; - pub mod derive; pub mod expand; - pub mod placeholders; pub mod proc_macro; pub mod tt { diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs deleted file mode 100644 index 98465d75e46..00000000000 --- a/src/libsyntax_ext/deriving/custom.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::proc_macro_impl::EXEC_STRATEGY; -use crate::proc_macro_server; - -use errors::FatalError; -use rustc_data_structures::sync::Lrc; -use syntax::ast::{self, ItemKind, Attribute, Mac}; -use syntax::attr::{mark_used, mark_known}; -use syntax::source_map::Span; -use syntax::ext::base::*; -use syntax::parse; -use syntax::parse::token; -use syntax::tokenstream; -use syntax::visit::Visitor; -use syntax_pos::DUMMY_SP; - -struct MarkAttrs<'a>(&'a [ast::Name]); - -impl<'a> Visitor<'a> for MarkAttrs<'a> { - fn visit_attribute(&mut self, attr: &Attribute) { - if let Some(ident) = attr.ident() { - if self.0.contains(&ident.name) { - mark_used(attr); - mark_known(attr); - } - } - } - - fn visit_mac(&mut self, _mac: &Mac) {} -} - -pub struct ProcMacroDerive { - pub client: proc_macro::bridge::client::Client< - fn(proc_macro::TokenStream) -> proc_macro::TokenStream, - >, - pub attrs: Vec, -} - -impl MultiItemModifier for ProcMacroDerive { - fn expand(&self, - ecx: &mut ExtCtxt<'_>, - span: Span, - _meta_item: &ast::MetaItem, - item: Annotatable) - -> Vec { - let item = match item { - Annotatable::Item(item) => item, - Annotatable::ImplItem(_) | - Annotatable::TraitItem(_) | - Annotatable::ForeignItem(_) | - Annotatable::Stmt(_) | - Annotatable::Expr(_) => { - ecx.span_err(span, "proc-macro derives may only be \ - applied to a struct, enum, or union"); - return Vec::new() - } - }; - match item.node { - ItemKind::Struct(..) | - ItemKind::Enum(..) | - ItemKind::Union(..) => {}, - _ => { - ecx.span_err(span, "proc-macro derives may only be \ - applied to a struct, enum, or union"); - return Vec::new() - } - } - - // Mark attributes as known, and used. - MarkAttrs(&self.attrs).visit_item(&item); - - let token = token::Interpolated(Lrc::new(token::NtItem(item))); - let input = tokenstream::TokenTree::token(token, DUMMY_SP).into(); - - let server = proc_macro_server::Rustc::new(ecx); - let stream = match self.client.run(&EXEC_STRATEGY, server, input) { - Ok(stream) => stream, - Err(e) => { - let msg = "proc-macro derive panicked"; - let mut err = ecx.struct_span_fatal(span, msg); - if let Some(s) = e.as_str() { - err.help(&format!("message: {}", s)); - } - - err.emit(); - FatalError.raise(); - } - }; - - let error_count_before = ecx.parse_sess.span_diagnostic.err_count(); - let msg = "proc-macro derive produced unparseable tokens"; - - let mut parser = parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive")); - let mut items = vec![]; - - loop { - match parser.parse_item() { - Ok(None) => break, - Ok(Some(item)) => { - items.push(Annotatable::Item(item)) - } - Err(mut err) => { - // FIXME: handle this better - err.cancel(); - ecx.struct_span_fatal(span, msg).emit(); - FatalError.raise(); - } - } - } - - - // fail if there have been errors emitted - if ecx.parse_sess.span_diagnostic.err_count() > error_count_before { - ecx.struct_span_fatal(span, msg).emit(); - FatalError.raise(); - } - - items - } -} diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 12482f7248e..7f27769f236 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -1770,50 +1770,6 @@ pub fn cs_fold1(use_foldl: bool, } } -/// Call the method that is being derived on all the fields, and then -/// process the collected results. i.e. -/// -/// ```ignore (only-for-syntax-highlight) -/// f(cx, span, vec![self_1.method(__arg_1_1, __arg_2_1), -/// self_2.method(__arg_1_2, __arg_2_2)]) -/// ``` -#[inline] -pub fn cs_same_method(f: F, - mut enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>, - cx: &mut ExtCtxt<'_>, - trait_span: Span, - substructure: &Substructure<'_>) - -> P - where F: FnOnce(&mut ExtCtxt<'_>, Span, Vec>) -> P -{ - match *substructure.fields { - EnumMatching(.., ref all_fields) | - Struct(_, ref all_fields) => { - // call self_n.method(other_1_n, other_2_n, ...) - let called = all_fields.iter() - .map(|field| { - cx.expr_method_call(field.span, - field.self_.clone(), - substructure.method_ident, - field.other - .iter() - .map(|e| cx.expr_addr_of(field.span, e.clone())) - .collect()) - }) - .collect(); - - f(cx, trait_span, called) - } - EnumNonMatchingCollapsed(ref all_self_args, _, tuple) => { - enum_nonmatch_f(cx, - trait_span, - (&all_self_args[..], tuple), - substructure.nonself_args) - } - StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"), - } -} - /// Returns `true` if the type has no value fields /// (for an enum, no variant has any fields) pub fn is_type_without_fields(item: &Annotatable) -> bool { diff --git a/src/libsyntax_ext/deriving/generic/ty.rs b/src/libsyntax_ext/deriving/generic/ty.rs index fef1b4eebcf..394beb14171 100644 --- a/src/libsyntax_ext/deriving/generic/ty.rs +++ b/src/libsyntax_ext/deriving/generic/ty.rs @@ -18,6 +18,7 @@ pub enum PtrTy<'a> { /// &'lifetime mut Borrowed(Option<&'a str>, ast::Mutability), /// *mut + #[allow(dead_code)] Raw(ast::Mutability), } @@ -107,13 +108,6 @@ pub enum Ty<'a> { Tuple(Vec>), } -/// A const expression. Supports literals and blocks. -#[derive(Clone, Eq, PartialEq)] -pub enum Const { - Literal, - Block, -} - pub fn borrowed_ptrty<'r>() -> PtrTy<'r> { Borrowed(None, ast::Mutability::Immutable) } diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 1a865099288..cad79917af2 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -26,7 +26,6 @@ pub mod decodable; pub mod hash; pub mod debug; pub mod default; -pub mod custom; #[path="cmp/partial_eq.rs"] pub mod partial_eq; diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 2c5a9ab0512..d208d34eea1 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -8,9 +8,6 @@ #![feature(crate_visibility_modifier)] #![feature(decl_macro)] #![feature(nll)] -#![feature(proc_macro_diagnostic)] -#![feature(proc_macro_internals)] -#![feature(proc_macro_span)] #![feature(rustc_diagnostic_macros)] #![feature(unicode_internals)] @@ -32,22 +29,20 @@ mod cfg; mod compile_error; mod concat; mod concat_idents; +mod deriving; mod env; mod format; mod format_foreign; mod global_allocator; mod global_asm; mod log_syntax; -mod proc_macro_server; mod source_util; mod test; mod test_case; mod trace_macros; -pub mod deriving; pub mod plugin_macro_defs; pub mod proc_macro_decls; -pub mod proc_macro_impl; pub fn register_builtin_macros(resolver: &mut dyn syntax::ext::base::Resolver, edition: Edition) { let mut register = |name, kind| resolver.register_builtin_macro( diff --git a/src/libsyntax_ext/proc_macro_impl.rs b/src/libsyntax_ext/proc_macro_impl.rs deleted file mode 100644 index f0fc6392cd7..00000000000 --- a/src/libsyntax_ext/proc_macro_impl.rs +++ /dev/null @@ -1,68 +0,0 @@ -use crate::proc_macro_server; - -use errors::FatalError; -use syntax::source_map::Span; -use syntax::ext::base::{self, *}; -use syntax::tokenstream::TokenStream; - -pub const EXEC_STRATEGY: proc_macro::bridge::server::SameThread = - proc_macro::bridge::server::SameThread; - -pub struct AttrProcMacro { - pub client: proc_macro::bridge::client::Client< - fn(proc_macro::TokenStream, proc_macro::TokenStream) -> proc_macro::TokenStream, - >, -} - -impl base::AttrProcMacro for AttrProcMacro { - fn expand<'cx>(&self, - ecx: &'cx mut ExtCtxt<'_>, - span: Span, - annotation: TokenStream, - annotated: TokenStream) - -> TokenStream { - let server = proc_macro_server::Rustc::new(ecx); - match self.client.run(&EXEC_STRATEGY, server, annotation, annotated) { - Ok(stream) => stream, - Err(e) => { - let msg = "custom attribute panicked"; - let mut err = ecx.struct_span_fatal(span, msg); - if let Some(s) = e.as_str() { - err.help(&format!("message: {}", s)); - } - - err.emit(); - FatalError.raise(); - } - } - } -} - -pub struct BangProcMacro { - pub client: proc_macro::bridge::client::Client< - fn(proc_macro::TokenStream) -> proc_macro::TokenStream, - >, -} - -impl base::ProcMacro for BangProcMacro { - fn expand<'cx>(&self, - ecx: &'cx mut ExtCtxt<'_>, - span: Span, - input: TokenStream) - -> TokenStream { - let server = proc_macro_server::Rustc::new(ecx); - match self.client.run(&EXEC_STRATEGY, server, input) { - Ok(stream) => stream, - Err(e) => { - let msg = "proc macro panicked"; - let mut err = ecx.struct_span_fatal(span, msg); - if let Some(s) = e.as_str() { - err.help(&format!("message: {}", s)); - } - - err.emit(); - FatalError.raise(); - } - } - } -} diff --git a/src/libsyntax_ext/proc_macro_server.rs b/src/libsyntax_ext/proc_macro_server.rs deleted file mode 100644 index e94d79a140d..00000000000 --- a/src/libsyntax_ext/proc_macro_server.rs +++ /dev/null @@ -1,717 +0,0 @@ -use errors::{Diagnostic, DiagnosticBuilder}; - -use std::panic; - -use proc_macro::bridge::{server, TokenTree}; -use proc_macro::{Delimiter, Level, LineColumn, Spacing}; - -use rustc_data_structures::sync::Lrc; -use std::ascii; -use std::ops::Bound; -use syntax::ast; -use syntax::ext::base::ExtCtxt; -use syntax::parse::lexer::comments; -use syntax::parse::{self, token, ParseSess}; -use syntax::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint}; -use syntax_pos::hygiene::{SyntaxContext, Transparency}; -use syntax_pos::symbol::{kw, sym, Symbol}; -use syntax_pos::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span}; - -trait FromInternal { - fn from_internal(x: T) -> Self; -} - -trait ToInternal { - fn to_internal(self) -> T; -} - -impl FromInternal for Delimiter { - fn from_internal(delim: token::DelimToken) -> Delimiter { - match delim { - token::Paren => Delimiter::Parenthesis, - token::Brace => Delimiter::Brace, - token::Bracket => Delimiter::Bracket, - token::NoDelim => Delimiter::None, - } - } -} - -impl ToInternal for Delimiter { - fn to_internal(self) -> token::DelimToken { - match self { - Delimiter::Parenthesis => token::Paren, - Delimiter::Brace => token::Brace, - Delimiter::Bracket => token::Bracket, - Delimiter::None => token::NoDelim, - } - } -} - -impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec)> - for TokenTree -{ - fn from_internal(((tree, is_joint), sess, stack): (TreeAndJoint, &ParseSess, &mut Vec)) - -> Self { - use syntax::parse::token::*; - - let joint = is_joint == Joint; - let Token { kind, span } = match tree { - tokenstream::TokenTree::Delimited(span, delim, tts) => { - let delimiter = Delimiter::from_internal(delim); - return TokenTree::Group(Group { - delimiter, - stream: tts.into(), - span, - }); - } - tokenstream::TokenTree::Token(token) => token, - }; - - macro_rules! tt { - ($ty:ident { $($field:ident $(: $value:expr)*),+ $(,)? }) => ( - TokenTree::$ty(self::$ty { - $($field $(: $value)*,)+ - span, - }) - ); - ($ty:ident::$method:ident($($value:expr),*)) => ( - TokenTree::$ty(self::$ty::$method($($value,)* span)) - ); - } - macro_rules! op { - ($a:expr) => { - tt!(Punct::new($a, joint)) - }; - ($a:expr, $b:expr) => {{ - stack.push(tt!(Punct::new($b, joint))); - tt!(Punct::new($a, true)) - }}; - ($a:expr, $b:expr, $c:expr) => {{ - stack.push(tt!(Punct::new($c, joint))); - stack.push(tt!(Punct::new($b, true))); - tt!(Punct::new($a, true)) - }}; - } - - match kind { - Eq => op!('='), - Lt => op!('<'), - Le => op!('<', '='), - EqEq => op!('=', '='), - Ne => op!('!', '='), - Ge => op!('>', '='), - Gt => op!('>'), - AndAnd => op!('&', '&'), - OrOr => op!('|', '|'), - Not => op!('!'), - Tilde => op!('~'), - BinOp(Plus) => op!('+'), - BinOp(Minus) => op!('-'), - BinOp(Star) => op!('*'), - BinOp(Slash) => op!('/'), - BinOp(Percent) => op!('%'), - BinOp(Caret) => op!('^'), - BinOp(And) => op!('&'), - BinOp(Or) => op!('|'), - BinOp(Shl) => op!('<', '<'), - BinOp(Shr) => op!('>', '>'), - BinOpEq(Plus) => op!('+', '='), - BinOpEq(Minus) => op!('-', '='), - BinOpEq(Star) => op!('*', '='), - BinOpEq(Slash) => op!('/', '='), - BinOpEq(Percent) => op!('%', '='), - BinOpEq(Caret) => op!('^', '='), - BinOpEq(And) => op!('&', '='), - BinOpEq(Or) => op!('|', '='), - BinOpEq(Shl) => op!('<', '<', '='), - BinOpEq(Shr) => op!('>', '>', '='), - At => op!('@'), - Dot => op!('.'), - DotDot => op!('.', '.'), - DotDotDot => op!('.', '.', '.'), - DotDotEq => op!('.', '.', '='), - Comma => op!(','), - Semi => op!(';'), - Colon => op!(':'), - ModSep => op!(':', ':'), - RArrow => op!('-', '>'), - LArrow => op!('<', '-'), - FatArrow => op!('=', '>'), - Pound => op!('#'), - Dollar => op!('$'), - Question => op!('?'), - SingleQuote => op!('\''), - - Ident(name, false) if name == kw::DollarCrate => tt!(Ident::dollar_crate()), - Ident(name, is_raw) => tt!(Ident::new(name, is_raw)), - Lifetime(name) => { - let ident = ast::Ident::new(name, span).without_first_quote(); - stack.push(tt!(Ident::new(ident.name, false))); - tt!(Punct::new('\'', true)) - } - Literal(lit) => tt!(Literal { lit }), - DocComment(c) => { - let style = comments::doc_comment_style(&c.as_str()); - let stripped = comments::strip_doc_comment_decoration(&c.as_str()); - let mut escaped = String::new(); - for ch in stripped.chars() { - escaped.extend(ch.escape_debug()); - } - let stream = vec![ - Ident(sym::doc, false), - Eq, - TokenKind::lit(token::Str, Symbol::intern(&escaped), None), - ] - .into_iter() - .map(|kind| tokenstream::TokenTree::token(kind, span)) - .collect(); - stack.push(TokenTree::Group(Group { - delimiter: Delimiter::Bracket, - stream, - span: DelimSpan::from_single(span), - })); - if style == ast::AttrStyle::Inner { - stack.push(tt!(Punct::new('!', false))); - } - tt!(Punct::new('#', false)) - } - - Interpolated(nt) => { - let stream = nt.to_tokenstream(sess, span); - TokenTree::Group(Group { - delimiter: Delimiter::None, - stream, - span: DelimSpan::from_single(span), - }) - } - - OpenDelim(..) | CloseDelim(..) => unreachable!(), - Whitespace | Comment | Shebang(..) | Eof => unreachable!(), - } - } -} - -impl ToInternal for TokenTree { - fn to_internal(self) -> TokenStream { - use syntax::parse::token::*; - - let (ch, joint, span) = match self { - TokenTree::Punct(Punct { ch, joint, span }) => (ch, joint, span), - TokenTree::Group(Group { - delimiter, - stream, - span, - }) => { - return tokenstream::TokenTree::Delimited( - span, - delimiter.to_internal(), - stream.into(), - ) - .into(); - } - TokenTree::Ident(self::Ident { sym, is_raw, span }) => { - return tokenstream::TokenTree::token(Ident(sym, is_raw), span).into(); - } - TokenTree::Literal(self::Literal { - lit: token::Lit { kind: token::Integer, symbol, suffix }, - span, - }) if symbol.as_str().starts_with("-") => { - let minus = BinOp(BinOpToken::Minus); - let symbol = Symbol::intern(&symbol.as_str()[1..]); - let integer = TokenKind::lit(token::Integer, symbol, suffix); - let a = tokenstream::TokenTree::token(minus, span); - let b = tokenstream::TokenTree::token(integer, span); - return vec![a, b].into_iter().collect(); - } - TokenTree::Literal(self::Literal { - lit: token::Lit { kind: token::Float, symbol, suffix }, - span, - }) if symbol.as_str().starts_with("-") => { - let minus = BinOp(BinOpToken::Minus); - let symbol = Symbol::intern(&symbol.as_str()[1..]); - let float = TokenKind::lit(token::Float, symbol, suffix); - let a = tokenstream::TokenTree::token(minus, span); - let b = tokenstream::TokenTree::token(float, span); - return vec![a, b].into_iter().collect(); - } - TokenTree::Literal(self::Literal { lit, span }) => { - return tokenstream::TokenTree::token(Literal(lit), span).into() - } - }; - - let kind = match ch { - '=' => Eq, - '<' => Lt, - '>' => Gt, - '!' => Not, - '~' => Tilde, - '+' => BinOp(Plus), - '-' => BinOp(Minus), - '*' => BinOp(Star), - '/' => BinOp(Slash), - '%' => BinOp(Percent), - '^' => BinOp(Caret), - '&' => BinOp(And), - '|' => BinOp(Or), - '@' => At, - '.' => Dot, - ',' => Comma, - ';' => Semi, - ':' => Colon, - '#' => Pound, - '$' => Dollar, - '?' => Question, - '\'' => SingleQuote, - _ => unreachable!(), - }; - - let tree = tokenstream::TokenTree::token(kind, span); - TokenStream::new(vec![(tree, if joint { Joint } else { NonJoint })]) - } -} - -impl ToInternal for Level { - fn to_internal(self) -> errors::Level { - match self { - Level::Error => errors::Level::Error, - Level::Warning => errors::Level::Warning, - Level::Note => errors::Level::Note, - Level::Help => errors::Level::Help, - _ => unreachable!("unknown proc_macro::Level variant: {:?}", self), - } - } -} - -#[derive(Clone)] -pub struct TokenStreamIter { - cursor: tokenstream::Cursor, - stack: Vec>, -} - -#[derive(Clone)] -pub struct Group { - delimiter: Delimiter, - stream: TokenStream, - span: DelimSpan, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct Punct { - ch: char, - // NB. not using `Spacing` here because it doesn't implement `Hash`. - joint: bool, - span: Span, -} - -impl Punct { - fn new(ch: char, joint: bool, span: Span) -> Punct { - const LEGAL_CHARS: &[char] = &['=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^', - '&', '|', '@', '.', ',', ';', ':', '#', '$', '?', '\'']; - if !LEGAL_CHARS.contains(&ch) { - panic!("unsupported character `{:?}`", ch) - } - Punct { ch, joint, span } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct Ident { - sym: Symbol, - is_raw: bool, - span: Span, -} - -impl Ident { - fn is_valid(string: &str) -> bool { - let mut chars = string.chars(); - if let Some(start) = chars.next() { - (start == '_' || start.is_xid_start()) - && chars.all(|cont| cont == '_' || cont.is_xid_continue()) - } else { - false - } - } - fn new(sym: Symbol, is_raw: bool, span: Span) -> Ident { - let string = sym.as_str(); - if !Self::is_valid(&string) { - panic!("`{:?}` is not a valid identifier", string) - } - // Get rid of gensyms to conservatively check rawness on the string contents only. - if is_raw && !sym.as_interned_str().as_symbol().can_be_raw() { - panic!("`{}` cannot be a raw identifier", string); - } - Ident { sym, is_raw, span } - } - fn dollar_crate(span: Span) -> Ident { - // `$crate` is accepted as an ident only if it comes from the compiler. - Ident { sym: kw::DollarCrate, is_raw: false, span } - } -} - -// FIXME(eddyb) `Literal` should not expose internal `Debug` impls. -#[derive(Clone, Debug)] -pub struct Literal { - lit: token::Lit, - span: Span, -} - -pub(crate) struct Rustc<'a> { - sess: &'a ParseSess, - def_site: Span, - call_site: Span, -} - -impl<'a> Rustc<'a> { - pub fn new(cx: &'a ExtCtxt<'_>) -> Self { - // No way to determine def location for a proc macro right now, so use call location. - let location = cx.current_expansion.id.expn_info().unwrap().call_site; - let to_span = |transparency| { - location.with_ctxt( - SyntaxContext::empty() - .apply_mark_with_transparency(cx.current_expansion.id, transparency), - ) - }; - Rustc { - sess: cx.parse_sess, - def_site: to_span(Transparency::Opaque), - call_site: to_span(Transparency::Transparent), - } - } - - fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option) -> Literal { - Literal { - lit: token::Lit::new(kind, symbol, suffix), - span: server::Span::call_site(self), - } - } -} - -impl server::Types for Rustc<'_> { - type TokenStream = TokenStream; - type TokenStreamBuilder = tokenstream::TokenStreamBuilder; - type TokenStreamIter = TokenStreamIter; - type Group = Group; - type Punct = Punct; - type Ident = Ident; - type Literal = Literal; - type SourceFile = Lrc; - type MultiSpan = Vec; - type Diagnostic = Diagnostic; - type Span = Span; -} - -impl server::TokenStream for Rustc<'_> { - fn new(&mut self) -> Self::TokenStream { - TokenStream::empty() - } - fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { - stream.is_empty() - } - fn from_str(&mut self, src: &str) -> Self::TokenStream { - parse::parse_stream_from_source_str( - FileName::proc_macro_source_code(src), - src.to_string(), - self.sess, - Some(self.call_site), - ) - } - fn to_string(&mut self, stream: &Self::TokenStream) -> String { - stream.to_string() - } - fn from_token_tree( - &mut self, - tree: TokenTree, - ) -> Self::TokenStream { - tree.to_internal() - } - fn into_iter(&mut self, stream: Self::TokenStream) -> Self::TokenStreamIter { - TokenStreamIter { - cursor: stream.trees(), - stack: vec![], - } - } -} - -impl server::TokenStreamBuilder for Rustc<'_> { - fn new(&mut self) -> Self::TokenStreamBuilder { - tokenstream::TokenStreamBuilder::new() - } - fn push(&mut self, builder: &mut Self::TokenStreamBuilder, stream: Self::TokenStream) { - builder.push(stream); - } - fn build(&mut self, builder: Self::TokenStreamBuilder) -> Self::TokenStream { - builder.build() - } -} - -impl server::TokenStreamIter for Rustc<'_> { - fn next( - &mut self, - iter: &mut Self::TokenStreamIter, - ) -> Option> { - loop { - let tree = iter.stack.pop().or_else(|| { - let next = iter.cursor.next_with_joint()?; - Some(TokenTree::from_internal((next, self.sess, &mut iter.stack))) - })?; - // HACK: The condition "dummy span + group with empty delimiter" represents an AST - // fragment approximately converted into a token stream. This may happen, for - // example, with inputs to proc macro attributes, including derives. Such "groups" - // need to flattened during iteration over stream's token trees. - // Eventually this needs to be removed in favor of keeping original token trees - // and not doing the roundtrip through AST. - if let TokenTree::Group(ref group) = tree { - if group.delimiter == Delimiter::None && group.span.entire().is_dummy() { - iter.cursor.append(group.stream.clone()); - continue; - } - } - return Some(tree); - } - } -} - -impl server::Group for Rustc<'_> { - fn new(&mut self, delimiter: Delimiter, stream: Self::TokenStream) -> Self::Group { - Group { - delimiter, - stream, - span: DelimSpan::from_single(server::Span::call_site(self)), - } - } - fn delimiter(&mut self, group: &Self::Group) -> Delimiter { - group.delimiter - } - fn stream(&mut self, group: &Self::Group) -> Self::TokenStream { - group.stream.clone() - } - fn span(&mut self, group: &Self::Group) -> Self::Span { - group.span.entire() - } - fn span_open(&mut self, group: &Self::Group) -> Self::Span { - group.span.open - } - fn span_close(&mut self, group: &Self::Group) -> Self::Span { - group.span.close - } - fn set_span(&mut self, group: &mut Self::Group, span: Self::Span) { - group.span = DelimSpan::from_single(span); - } -} - -impl server::Punct for Rustc<'_> { - fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct { - Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self)) - } - fn as_char(&mut self, punct: Self::Punct) -> char { - punct.ch - } - fn spacing(&mut self, punct: Self::Punct) -> Spacing { - if punct.joint { - Spacing::Joint - } else { - Spacing::Alone - } - } - fn span(&mut self, punct: Self::Punct) -> Self::Span { - punct.span - } - fn with_span(&mut self, punct: Self::Punct, span: Self::Span) -> Self::Punct { - Punct { span, ..punct } - } -} - -impl server::Ident for Rustc<'_> { - fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident { - Ident::new(Symbol::intern(string), is_raw, span) - } - fn span(&mut self, ident: Self::Ident) -> Self::Span { - ident.span - } - fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident { - Ident { span, ..ident } - } -} - -impl server::Literal for Rustc<'_> { - // FIXME(eddyb) `Literal` should not expose internal `Debug` impls. - fn debug(&mut self, literal: &Self::Literal) -> String { - format!("{:?}", literal) - } - fn integer(&mut self, n: &str) -> Self::Literal { - self.lit(token::Integer, Symbol::intern(n), None) - } - fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { - self.lit(token::Integer, Symbol::intern(n), Some(Symbol::intern(kind))) - } - fn float(&mut self, n: &str) -> Self::Literal { - self.lit(token::Float, Symbol::intern(n), None) - } - fn f32(&mut self, n: &str) -> Self::Literal { - self.lit(token::Float, Symbol::intern(n), Some(sym::f32)) - } - fn f64(&mut self, n: &str) -> Self::Literal { - self.lit(token::Float, Symbol::intern(n), Some(sym::f64)) - } - fn string(&mut self, string: &str) -> Self::Literal { - let mut escaped = String::new(); - for ch in string.chars() { - escaped.extend(ch.escape_debug()); - } - self.lit(token::Str, Symbol::intern(&escaped), None) - } - fn character(&mut self, ch: char) -> Self::Literal { - let mut escaped = String::new(); - escaped.extend(ch.escape_unicode()); - self.lit(token::Char, Symbol::intern(&escaped), None) - } - fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal { - let string = bytes - .iter() - .cloned() - .flat_map(ascii::escape_default) - .map(Into::::into) - .collect::(); - self.lit(token::ByteStr, Symbol::intern(&string), None) - } - fn span(&mut self, literal: &Self::Literal) -> Self::Span { - literal.span - } - fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) { - literal.span = span; - } - fn subspan( - &mut self, - literal: &Self::Literal, - start: Bound, - end: Bound, - ) -> Option { - let span = literal.span; - let length = span.hi().to_usize() - span.lo().to_usize(); - - let start = match start { - Bound::Included(lo) => lo, - Bound::Excluded(lo) => lo + 1, - Bound::Unbounded => 0, - }; - - let end = match end { - Bound::Included(hi) => hi + 1, - Bound::Excluded(hi) => hi, - Bound::Unbounded => length, - }; - - // Bounds check the values, preventing addition overflow and OOB spans. - if start > u32::max_value() as usize - || end > u32::max_value() as usize - || (u32::max_value() - start as u32) < span.lo().to_u32() - || (u32::max_value() - end as u32) < span.lo().to_u32() - || start >= end - || end > length - { - return None; - } - - let new_lo = span.lo() + BytePos::from_usize(start); - let new_hi = span.lo() + BytePos::from_usize(end); - Some(span.with_lo(new_lo).with_hi(new_hi)) - } -} - -impl server::SourceFile for Rustc<'_> { - fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { - Lrc::ptr_eq(file1, file2) - } - fn path(&mut self, file: &Self::SourceFile) -> String { - match file.name { - FileName::Real(ref path) => path - .to_str() - .expect("non-UTF8 file path in `proc_macro::SourceFile::path`") - .to_string(), - _ => file.name.to_string(), - } - } - fn is_real(&mut self, file: &Self::SourceFile) -> bool { - file.is_real_file() - } -} - -impl server::MultiSpan for Rustc<'_> { - fn new(&mut self) -> Self::MultiSpan { - vec![] - } - fn push(&mut self, spans: &mut Self::MultiSpan, span: Self::Span) { - spans.push(span) - } -} - -impl server::Diagnostic for Rustc<'_> { - fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic { - let mut diag = Diagnostic::new(level.to_internal(), msg); - diag.set_span(MultiSpan::from_spans(spans)); - diag - } - fn sub( - &mut self, - diag: &mut Self::Diagnostic, - level: Level, - msg: &str, - spans: Self::MultiSpan, - ) { - diag.sub(level.to_internal(), msg, MultiSpan::from_spans(spans), None); - } - fn emit(&mut self, diag: Self::Diagnostic) { - DiagnosticBuilder::new_diagnostic(&self.sess.span_diagnostic, diag).emit() - } -} - -impl server::Span for Rustc<'_> { - fn debug(&mut self, span: Self::Span) -> String { - format!("{:?} bytes({}..{})", span.ctxt(), span.lo().0, span.hi().0) - } - fn def_site(&mut self) -> Self::Span { - self.def_site - } - fn call_site(&mut self) -> Self::Span { - self.call_site - } - fn source_file(&mut self, span: Self::Span) -> Self::SourceFile { - self.sess.source_map().lookup_char_pos(span.lo()).file - } - fn parent(&mut self, span: Self::Span) -> Option { - span.ctxt().outer_expn_info().map(|i| i.call_site) - } - fn source(&mut self, span: Self::Span) -> Self::Span { - span.source_callsite() - } - fn start(&mut self, span: Self::Span) -> LineColumn { - let loc = self.sess.source_map().lookup_char_pos(span.lo()); - LineColumn { - line: loc.line, - column: loc.col.to_usize(), - } - } - fn end(&mut self, span: Self::Span) -> LineColumn { - let loc = self.sess.source_map().lookup_char_pos(span.hi()); - LineColumn { - line: loc.line, - column: loc.col.to_usize(), - } - } - fn join(&mut self, first: Self::Span, second: Self::Span) -> Option { - let self_loc = self.sess.source_map().lookup_char_pos(first.lo()); - let other_loc = self.sess.source_map().lookup_char_pos(second.lo()); - - if self_loc.file.name != other_loc.file.name { - return None; - } - - Some(first.to(second)) - } - fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span { - span.with_ctxt(at.ctxt()) - } - fn source_text(&mut self, span: Self::Span) -> Option { - self.sess.source_map().span_to_snippet(span).ok() - } -} -- cgit 1.4.1-3-g733a5 From f6eda9937956667c01bc567ab4a9bd6a19f71635 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 18 Jul 2019 21:29:15 +0300 Subject: Move test harness generation into libsyntax_ext --- src/librustc_interface/passes.rs | 2 +- src/libsyntax/lib.rs | 1 - src/libsyntax/test.rs | 424 -------------------------------------- src/libsyntax_ext/Cargo.toml | 8 +- src/libsyntax_ext/lib.rs | 5 +- src/libsyntax_ext/test.rs | 33 +++ src/libsyntax_ext/test_case.rs | 44 ---- src/libsyntax_ext/test_harness.rs | 392 +++++++++++++++++++++++++++++++++++ 8 files changed, 433 insertions(+), 476 deletions(-) delete mode 100644 src/libsyntax/test.rs delete mode 100644 src/libsyntax_ext/test_case.rs create mode 100644 src/libsyntax_ext/test_harness.rs diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 2754c55ea06..58edb898c25 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -456,7 +456,7 @@ fn configure_and_expand_inner<'a>( sess.profiler(|p| p.end_activity("macro expansion")); time(sess, "maybe building test harness", || { - syntax::test::modify_for_testing( + syntax_ext::test_harness::modify_for_testing( &sess.parse_sess, &mut resolver, sess.opts.test, diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index a4102ce45aa..17f379f31b2 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -156,7 +156,6 @@ pub mod show_span; pub mod std_inject; pub use syntax_pos::edition; pub use syntax_pos::symbol; -pub mod test; pub mod tokenstream; pub mod visit; diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs deleted file mode 100644 index 2ada7104843..00000000000 --- a/src/libsyntax/test.rs +++ /dev/null @@ -1,424 +0,0 @@ -// Code that generates a test runner to run all the tests in a crate - -#![allow(dead_code)] -#![allow(unused_imports)] - -use HasTestSignature::*; - -use std::iter; -use std::slice; -use std::mem; -use std::vec; - -use log::debug; -use smallvec::{smallvec, SmallVec}; -use syntax_pos::{DUMMY_SP, NO_EXPANSION, Span, SourceFile, BytePos}; - -use crate::attr::{self, HasAttrs}; -use crate::source_map::{self, SourceMap, ExpnInfo, ExpnKind, dummy_spanned, respan}; -use crate::config; -use crate::entry::{self, EntryPointType}; -use crate::ext::base::{ExtCtxt, Resolver}; -use crate::ext::build::AstBuilder; -use crate::ext::expand::ExpansionConfig; -use crate::ext::hygiene::{self, ExpnId, SyntaxContext, MacroKind}; -use crate::mut_visit::{*, ExpectOne}; -use crate::feature_gate::Features; -use crate::util::map_in_place::MapInPlace; -use crate::parse::{token, ParseSess}; -use crate::ast::{self, Ident}; -use crate::ptr::P; -use crate::symbol::{self, Symbol, kw, sym}; -use crate::ThinVec; - -struct Test { - span: Span, - path: Vec, -} - -struct TestCtxt<'a> { - span_diagnostic: &'a errors::Handler, - path: Vec, - ext_cx: ExtCtxt<'a>, - test_cases: Vec, - reexport_test_harness_main: Option, - is_libtest: bool, - features: &'a Features, - test_runner: Option, - - // top-level re-export submodule, filled out after folding is finished - toplevel_reexport: Option, -} - -// Traverse the crate, collecting all the test functions, eliding any -// existing main functions, and synthesizing a main test harness -pub fn modify_for_testing(sess: &ParseSess, - resolver: &mut dyn Resolver, - should_test: bool, - krate: &mut ast::Crate, - span_diagnostic: &errors::Handler, - features: &Features) { - // Check for #[reexport_test_harness_main = "some_name"] which - // creates a `use __test::main as some_name;`. This needs to be - // unconditional, so that the attribute is still marked as used in - // non-test builds. - let reexport_test_harness_main = - attr::first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main); - - // Do this here so that the test_runner crate attribute gets marked as used - // even in non-test builds - let test_runner = get_test_runner(span_diagnostic, &krate); - - if should_test { - generate_test_harness(sess, resolver, reexport_test_harness_main, - krate, span_diagnostic, features, test_runner) - } -} - -struct TestHarnessGenerator<'a> { - cx: TestCtxt<'a>, - tests: Vec, - - // submodule name, gensym'd identifier for re-exports - tested_submods: Vec<(Ident, Ident)>, -} - -impl<'a> MutVisitor for TestHarnessGenerator<'a> { - fn visit_crate(&mut self, c: &mut ast::Crate) { - noop_visit_crate(c, self); - - // Create a main function to run our tests - let test_main = { - let unresolved = mk_main(&mut self.cx); - self.cx.ext_cx.monotonic_expander().flat_map_item(unresolved).pop().unwrap() - }; - - c.module.items.push(test_main); - } - - fn flat_map_item(&mut self, i: P) -> SmallVec<[P; 1]> { - let ident = i.ident; - if ident.name != kw::Invalid { - self.cx.path.push(ident); - } - debug!("current path: {}", path_name_i(&self.cx.path)); - - let mut item = i.into_inner(); - if is_test_case(&item) { - debug!("this is a test item"); - - let test = Test { - span: item.span, - path: self.cx.path.clone(), - }; - self.cx.test_cases.push(test); - self.tests.push(item.ident); - } - - // We don't want to recurse into anything other than mods, since - // mods or tests inside of functions will break things - if let ast::ItemKind::Mod(mut module) = item.node { - let tests = mem::take(&mut self.tests); - let tested_submods = mem::take(&mut self.tested_submods); - noop_visit_mod(&mut module, self); - let tests = mem::replace(&mut self.tests, tests); - let tested_submods = mem::replace(&mut self.tested_submods, tested_submods); - - if !tests.is_empty() || !tested_submods.is_empty() { - let (it, sym) = mk_reexport_mod(&mut self.cx, item.id, tests, tested_submods); - module.items.push(it); - - if !self.cx.path.is_empty() { - self.tested_submods.push((self.cx.path[self.cx.path.len()-1], sym)); - } else { - debug!("pushing nothing, sym: {:?}", sym); - self.cx.toplevel_reexport = Some(sym); - } - } - item.node = ast::ItemKind::Mod(module); - } - if ident.name != kw::Invalid { - self.cx.path.pop(); - } - smallvec![P(item)] - } - - fn visit_mac(&mut self, _mac: &mut ast::Mac) { - // Do nothing. - } -} - -/// A folder used to remove any entry points (like fn main) because the harness -/// generator will provide its own -struct EntryPointCleaner { - // Current depth in the ast - depth: usize, -} - -impl MutVisitor for EntryPointCleaner { - fn flat_map_item(&mut self, i: P) -> SmallVec<[P; 1]> { - self.depth += 1; - let item = noop_flat_map_item(i, self).expect_one("noop did something"); - self.depth -= 1; - - // Remove any #[main] or #[start] from the AST so it doesn't - // clash with the one we're going to add, but mark it as - // #[allow(dead_code)] to avoid printing warnings. - let item = match entry::entry_point_type(&item, self.depth) { - EntryPointType::MainNamed | - EntryPointType::MainAttr | - EntryPointType::Start => - item.map(|ast::Item {id, ident, attrs, node, vis, span, tokens}| { - let allow_ident = Ident::with_empty_ctxt(sym::allow); - let dc_nested = attr::mk_nested_word_item(Ident::from_str("dead_code")); - let allow_dead_code_item = attr::mk_list_item(DUMMY_SP, allow_ident, - vec![dc_nested]); - let allow_dead_code = attr::mk_attr_outer(DUMMY_SP, - attr::mk_attr_id(), - allow_dead_code_item); - - ast::Item { - id, - ident, - attrs: attrs.into_iter() - .filter(|attr| { - !attr.check_name(sym::main) && !attr.check_name(sym::start) - }) - .chain(iter::once(allow_dead_code)) - .collect(), - node, - vis, - span, - tokens, - } - }), - EntryPointType::None | - EntryPointType::OtherMain => item, - }; - - smallvec![item] - } - - fn visit_mac(&mut self, _mac: &mut ast::Mac) { - // Do nothing. - } -} - -/// Creates an item (specifically a module) that "pub use"s the tests passed in. -/// Each tested submodule will contain a similar reexport module that we will export -/// under the name of the original module. That is, `submod::__test_reexports` is -/// reexported like so `pub use submod::__test_reexports as submod`. -fn mk_reexport_mod(cx: &mut TestCtxt<'_>, - parent: ast::NodeId, - tests: Vec, - tested_submods: Vec<(Ident, Ident)>) - -> (P, Ident) { - let super_ = Ident::with_empty_ctxt(kw::Super); - - let items = tests.into_iter().map(|r| { - cx.ext_cx.item_use_simple(DUMMY_SP, dummy_spanned(ast::VisibilityKind::Public), - cx.ext_cx.path(DUMMY_SP, vec![super_, r])) - }).chain(tested_submods.into_iter().map(|(r, sym)| { - let path = cx.ext_cx.path(DUMMY_SP, vec![super_, r, sym]); - cx.ext_cx.item_use_simple_(DUMMY_SP, dummy_spanned(ast::VisibilityKind::Public), - Some(r), path) - })).collect(); - - let reexport_mod = ast::Mod { - inline: true, - inner: DUMMY_SP, - items, - }; - - let name = Ident::from_str("__test_reexports").gensym(); - let parent = if parent == ast::DUMMY_NODE_ID { ast::CRATE_NODE_ID } else { parent }; - cx.ext_cx.current_expansion.id = cx.ext_cx.resolver.get_module_scope(parent); - let it = cx.ext_cx.monotonic_expander().flat_map_item(P(ast::Item { - ident: name, - attrs: Vec::new(), - id: ast::DUMMY_NODE_ID, - node: ast::ItemKind::Mod(reexport_mod), - vis: dummy_spanned(ast::VisibilityKind::Public), - span: DUMMY_SP, - tokens: None, - })).pop().unwrap(); - - (it, name) -} - -/// Crawl over the crate, inserting test reexports and the test main function -fn generate_test_harness(sess: &ParseSess, - resolver: &mut dyn Resolver, - reexport_test_harness_main: Option, - krate: &mut ast::Crate, - sd: &errors::Handler, - features: &Features, - test_runner: Option) { - // Remove the entry points - let mut cleaner = EntryPointCleaner { depth: 0 }; - cleaner.visit_crate(krate); - - let mut econfig = ExpansionConfig::default("test".to_string()); - econfig.features = Some(features); - - let cx = TestCtxt { - span_diagnostic: sd, - ext_cx: ExtCtxt::new(sess, econfig, resolver), - path: Vec::new(), - test_cases: Vec::new(), - reexport_test_harness_main, - // N.B., doesn't consider the value of `--crate-name` passed on the command line. - is_libtest: attr::find_crate_name(&krate.attrs) - .map(|s| s == sym::test).unwrap_or(false), - toplevel_reexport: None, - features, - test_runner - }; - - TestHarnessGenerator { - cx, - tests: Vec::new(), - tested_submods: Vec::new(), - }.visit_crate(krate); -} - -enum HasTestSignature { - Yes, - No(BadTestSignature), -} - -#[derive(PartialEq)] -enum BadTestSignature { - NotEvenAFunction, - WrongTypeSignature, - NoArgumentsAllowed, - ShouldPanicOnlyWithNoArgs, -} - -/// Creates a function item for use as the main function of a test build. -/// This function will call the `test_runner` as specified by the crate attribute -fn mk_main(cx: &mut TestCtxt<'_>) -> P { - // Writing this out by hand: - // pub fn main() { - // #![main] - // test::test_main_static(&[..tests]); - // } - let sp = DUMMY_SP.fresh_expansion(ExpnId::root(), ExpnInfo::allow_unstable( - ExpnKind::Macro(MacroKind::Attr, sym::test_case), DUMMY_SP, cx.ext_cx.parse_sess.edition, - [sym::main, sym::test, sym::rustc_attrs][..].into(), - )); - let ecx = &cx.ext_cx; - let test_id = Ident::with_empty_ctxt(sym::test); - - // test::test_main_static(...) - let mut test_runner = cx.test_runner.clone().unwrap_or( - ecx.path(sp, vec![ - test_id, ecx.ident_of("test_main_static") - ])); - - test_runner.span = sp; - - let test_main_path_expr = ecx.expr_path(test_runner); - let call_test_main = ecx.expr_call(sp, test_main_path_expr, - vec![mk_tests_slice(cx)]); - let call_test_main = ecx.stmt_expr(call_test_main); - - // #![main] - let main_meta = ecx.meta_word(sp, sym::main); - let main_attr = ecx.attribute(sp, main_meta); - - // extern crate test as test_gensym - let test_extern_stmt = ecx.stmt_item(sp, ecx.item(sp, - test_id, - vec![], - ast::ItemKind::ExternCrate(None) - )); - - // pub fn main() { ... } - let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![])); - - // If no test runner is provided we need to import the test crate - let main_body = if cx.test_runner.is_none() { - ecx.block(sp, vec![test_extern_stmt, call_test_main]) - } else { - ecx.block(sp, vec![call_test_main]) - }; - - let main = ast::ItemKind::Fn(ecx.fn_decl(vec![], ast::FunctionRetTy::Ty(main_ret_ty)), - ast::FnHeader::default(), - ast::Generics::default(), - main_body); - - // Honor the reexport_test_harness_main attribute - let main_id = match cx.reexport_test_harness_main { - Some(sym) => Ident::new(sym, sp), - None => Ident::from_str_and_span("main", sp).gensym(), - }; - - P(ast::Item { - ident: main_id, - attrs: vec![main_attr], - id: ast::DUMMY_NODE_ID, - node: main, - vis: dummy_spanned(ast::VisibilityKind::Public), - span: sp, - tokens: None, - }) - -} - -fn path_name_i(idents: &[Ident]) -> String { - let mut path_name = "".to_string(); - let mut idents_iter = idents.iter().peekable(); - while let Some(ident) = idents_iter.next() { - path_name.push_str(&ident.as_str()); - if idents_iter.peek().is_some() { - path_name.push_str("::") - } - } - path_name -} - -/// Creates a slice containing every test like so: -/// &[path::to::test1, path::to::test2] -fn mk_tests_slice(cx: &TestCtxt<'_>) -> P { - debug!("building test vector from {} tests", cx.test_cases.len()); - let ref ecx = cx.ext_cx; - - ecx.expr_vec_slice(DUMMY_SP, - cx.test_cases.iter().map(|test| { - ecx.expr_addr_of(test.span, - ecx.expr_path(ecx.path(test.span, visible_path(cx, &test.path)))) - }).collect()) -} - -/// Creates a path from the top-level __test module to the test via __test_reexports -fn visible_path(cx: &TestCtxt<'_>, path: &[Ident]) -> Vec{ - let mut visible_path = vec![]; - match cx.toplevel_reexport { - Some(id) => visible_path.push(id), - None => { - cx.span_diagnostic.bug("expected to find top-level re-export name, but found None"); - } - } - visible_path.extend_from_slice(path); - visible_path -} - -fn is_test_case(i: &ast::Item) -> bool { - attr::contains_name(&i.attrs, sym::rustc_test_marker) -} - -fn get_test_runner(sd: &errors::Handler, krate: &ast::Crate) -> Option { - let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?; - test_attr.meta_item_list().map(|meta_list| { - if meta_list.len() != 1 { - sd.span_fatal(test_attr.span, - "`#![test_runner(..)]` accepts exactly 1 argument").raise() - } - match meta_list[0].meta_item() { - Some(meta_item) if meta_item.is_word() => meta_item.path.clone(), - _ => sd.span_fatal(test_attr.span, "`test_runner` argument must be a path").raise() - } - }) -} diff --git a/src/libsyntax_ext/Cargo.toml b/src/libsyntax_ext/Cargo.toml index eafbe6371a3..73310df305b 100644 --- a/src/libsyntax_ext/Cargo.toml +++ b/src/libsyntax_ext/Cargo.toml @@ -10,11 +10,11 @@ path = "lib.rs" doctest = false [dependencies] -fmt_macros = { path = "../libfmt_macros" } errors = { path = "../librustc_errors", package = "rustc_errors" } -syntax = { path = "../libsyntax" } -syntax_pos = { path = "../libsyntax_pos" } +fmt_macros = { path = "../libfmt_macros" } +log = "0.4" rustc_data_structures = { path = "../librustc_data_structures" } rustc_target = { path = "../librustc_target" } smallvec = { version = "0.6.7", features = ["union", "may_dangle"] } -log = "0.4" +syntax = { path = "../libsyntax" } +syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index d208d34eea1..cd0d0886239 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -7,6 +7,7 @@ #![feature(crate_visibility_modifier)] #![feature(decl_macro)] +#![feature(mem_take)] #![feature(nll)] #![feature(rustc_diagnostic_macros)] #![feature(unicode_internals)] @@ -38,11 +39,11 @@ mod global_asm; mod log_syntax; mod source_util; mod test; -mod test_case; mod trace_macros; pub mod plugin_macro_defs; pub mod proc_macro_decls; +pub mod test_harness; pub fn register_builtin_macros(resolver: &mut dyn syntax::ext::base::Resolver, edition: Edition) { let mut register = |name, kind| resolver.register_builtin_macro( @@ -89,7 +90,7 @@ pub fn register_builtin_macros(resolver: &mut dyn syntax::ext::base::Resolver, e bench: test::expand_bench, global_allocator: global_allocator::expand, test: test::expand_test, - test_case: test_case::expand, + test_case: test::expand_test_case, } register_derive! { diff --git a/src/libsyntax_ext/test.rs b/src/libsyntax_ext/test.rs index d381c42f9ce..36aeb3065ff 100644 --- a/src/libsyntax_ext/test.rs +++ b/src/libsyntax_ext/test.rs @@ -7,11 +7,44 @@ use syntax::ext::base::*; use syntax::ext::build::AstBuilder; use syntax::ext::hygiene::SyntaxContext; use syntax::print::pprust; +use syntax::source_map::respan; use syntax::symbol::{Symbol, sym}; use syntax_pos::Span; use std::iter; +// #[test_case] is used by custom test authors to mark tests +// When building for test, it needs to make the item public and gensym the name +// Otherwise, we'll omit the item. This behavior means that any item annotated +// with #[test_case] is never addressable. +// +// We mark item with an inert attribute "rustc_test_marker" which the test generation +// logic will pick up on. +pub fn expand_test_case( + ecx: &mut ExtCtxt<'_>, + attr_sp: Span, + meta_item: &ast::MetaItem, + anno_item: Annotatable +) -> Vec { + check_builtin_macro_attribute(ecx, meta_item, sym::test_case); + + if !ecx.ecfg.should_test { return vec![]; } + + let sp = attr_sp.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.mark)); + let mut item = anno_item.expect_item(); + item = item.map(|mut item| { + item.vis = respan(item.vis.span, ast::VisibilityKind::Public); + item.ident = item.ident.gensym(); + item.attrs.push( + ecx.attribute(sp, + ecx.meta_word(sp, sym::rustc_test_marker)) + ); + item + }); + + return vec![Annotatable::Item(item)] +} + pub fn expand_test( cx: &mut ExtCtxt<'_>, attr_sp: Span, diff --git a/src/libsyntax_ext/test_case.rs b/src/libsyntax_ext/test_case.rs deleted file mode 100644 index ea4a8d541ab..00000000000 --- a/src/libsyntax_ext/test_case.rs +++ /dev/null @@ -1,44 +0,0 @@ -// http://rust-lang.org/COPYRIGHT. -// - -// #[test_case] is used by custom test authors to mark tests -// When building for test, it needs to make the item public and gensym the name -// Otherwise, we'll omit the item. This behavior means that any item annotated -// with #[test_case] is never addressable. -// -// We mark item with an inert attribute "rustc_test_marker" which the test generation -// logic will pick up on. - -use syntax::ast; -use syntax::attr::check_builtin_macro_attribute; -use syntax::ext::base::*; -use syntax::ext::build::AstBuilder; -use syntax::ext::hygiene::SyntaxContext; -use syntax::source_map::respan; -use syntax::symbol::sym; -use syntax_pos::Span; - -pub fn expand( - ecx: &mut ExtCtxt<'_>, - attr_sp: Span, - meta_item: &ast::MetaItem, - anno_item: Annotatable -) -> Vec { - check_builtin_macro_attribute(ecx, meta_item, sym::test_case); - - if !ecx.ecfg.should_test { return vec![]; } - - let sp = attr_sp.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.id)); - let mut item = anno_item.expect_item(); - item = item.map(|mut item| { - item.vis = respan(item.vis.span, ast::VisibilityKind::Public); - item.ident = item.ident.gensym(); - item.attrs.push( - ecx.attribute(sp, - ecx.meta_word(sp, sym::rustc_test_marker)) - ); - item - }); - - return vec![Annotatable::Item(item)] -} diff --git a/src/libsyntax_ext/test_harness.rs b/src/libsyntax_ext/test_harness.rs new file mode 100644 index 00000000000..061f5c3408b --- /dev/null +++ b/src/libsyntax_ext/test_harness.rs @@ -0,0 +1,392 @@ +// Code that generates a test runner to run all the tests in a crate + +use log::debug; +use smallvec::{smallvec, SmallVec}; +use syntax::ast::{self, Ident}; +use syntax::attr; +use syntax::entry::{self, EntryPointType}; +use syntax::ext::base::{ExtCtxt, Resolver}; +use syntax::ext::build::AstBuilder; +use syntax::ext::expand::ExpansionConfig; +use syntax::ext::hygiene::{ExpnId, MacroKind}; +use syntax::feature_gate::Features; +use syntax::mut_visit::{*, ExpectOne}; +use syntax::parse::ParseSess; +use syntax::ptr::P; +use syntax::source_map::{ExpnInfo, ExpnKind, dummy_spanned}; +use syntax::symbol::{kw, sym, Symbol}; +use syntax_pos::{Span, DUMMY_SP}; + +use std::{iter, mem}; + +struct Test { + span: Span, + path: Vec, +} + +struct TestCtxt<'a> { + span_diagnostic: &'a errors::Handler, + path: Vec, + ext_cx: ExtCtxt<'a>, + test_cases: Vec, + reexport_test_harness_main: Option, + test_runner: Option, + // top-level re-export submodule, filled out after folding is finished + toplevel_reexport: Option, +} + +// Traverse the crate, collecting all the test functions, eliding any +// existing main functions, and synthesizing a main test harness +pub fn modify_for_testing(sess: &ParseSess, + resolver: &mut dyn Resolver, + should_test: bool, + krate: &mut ast::Crate, + span_diagnostic: &errors::Handler, + features: &Features) { + // Check for #[reexport_test_harness_main = "some_name"] which + // creates a `use __test::main as some_name;`. This needs to be + // unconditional, so that the attribute is still marked as used in + // non-test builds. + let reexport_test_harness_main = + attr::first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main); + + // Do this here so that the test_runner crate attribute gets marked as used + // even in non-test builds + let test_runner = get_test_runner(span_diagnostic, &krate); + + if should_test { + generate_test_harness(sess, resolver, reexport_test_harness_main, + krate, span_diagnostic, features, test_runner) + } +} + +struct TestHarnessGenerator<'a> { + cx: TestCtxt<'a>, + tests: Vec, + + // submodule name, gensym'd identifier for re-exports + tested_submods: Vec<(Ident, Ident)>, +} + +impl<'a> MutVisitor for TestHarnessGenerator<'a> { + fn visit_crate(&mut self, c: &mut ast::Crate) { + noop_visit_crate(c, self); + + // Create a main function to run our tests + let test_main = { + let unresolved = mk_main(&mut self.cx); + self.cx.ext_cx.monotonic_expander().flat_map_item(unresolved).pop().unwrap() + }; + + c.module.items.push(test_main); + } + + fn flat_map_item(&mut self, i: P) -> SmallVec<[P; 1]> { + let ident = i.ident; + if ident.name != kw::Invalid { + self.cx.path.push(ident); + } + debug!("current path: {}", path_name_i(&self.cx.path)); + + let mut item = i.into_inner(); + if is_test_case(&item) { + debug!("this is a test item"); + + let test = Test { + span: item.span, + path: self.cx.path.clone(), + }; + self.cx.test_cases.push(test); + self.tests.push(item.ident); + } + + // We don't want to recurse into anything other than mods, since + // mods or tests inside of functions will break things + if let ast::ItemKind::Mod(mut module) = item.node { + let tests = mem::take(&mut self.tests); + let tested_submods = mem::take(&mut self.tested_submods); + noop_visit_mod(&mut module, self); + let tests = mem::replace(&mut self.tests, tests); + let tested_submods = mem::replace(&mut self.tested_submods, tested_submods); + + if !tests.is_empty() || !tested_submods.is_empty() { + let (it, sym) = mk_reexport_mod(&mut self.cx, item.id, tests, tested_submods); + module.items.push(it); + + if !self.cx.path.is_empty() { + self.tested_submods.push((self.cx.path[self.cx.path.len()-1], sym)); + } else { + debug!("pushing nothing, sym: {:?}", sym); + self.cx.toplevel_reexport = Some(sym); + } + } + item.node = ast::ItemKind::Mod(module); + } + if ident.name != kw::Invalid { + self.cx.path.pop(); + } + smallvec![P(item)] + } + + fn visit_mac(&mut self, _mac: &mut ast::Mac) { + // Do nothing. + } +} + +/// A folder used to remove any entry points (like fn main) because the harness +/// generator will provide its own +struct EntryPointCleaner { + // Current depth in the ast + depth: usize, +} + +impl MutVisitor for EntryPointCleaner { + fn flat_map_item(&mut self, i: P) -> SmallVec<[P; 1]> { + self.depth += 1; + let item = noop_flat_map_item(i, self).expect_one("noop did something"); + self.depth -= 1; + + // Remove any #[main] or #[start] from the AST so it doesn't + // clash with the one we're going to add, but mark it as + // #[allow(dead_code)] to avoid printing warnings. + let item = match entry::entry_point_type(&item, self.depth) { + EntryPointType::MainNamed | + EntryPointType::MainAttr | + EntryPointType::Start => + item.map(|ast::Item {id, ident, attrs, node, vis, span, tokens}| { + let allow_ident = Ident::with_empty_ctxt(sym::allow); + let dc_nested = attr::mk_nested_word_item(Ident::from_str("dead_code")); + let allow_dead_code_item = attr::mk_list_item(DUMMY_SP, allow_ident, + vec![dc_nested]); + let allow_dead_code = attr::mk_attr_outer(DUMMY_SP, + attr::mk_attr_id(), + allow_dead_code_item); + + ast::Item { + id, + ident, + attrs: attrs.into_iter() + .filter(|attr| { + !attr.check_name(sym::main) && !attr.check_name(sym::start) + }) + .chain(iter::once(allow_dead_code)) + .collect(), + node, + vis, + span, + tokens, + } + }), + EntryPointType::None | + EntryPointType::OtherMain => item, + }; + + smallvec![item] + } + + fn visit_mac(&mut self, _mac: &mut ast::Mac) { + // Do nothing. + } +} + +/// Creates an item (specifically a module) that "pub use"s the tests passed in. +/// Each tested submodule will contain a similar reexport module that we will export +/// under the name of the original module. That is, `submod::__test_reexports` is +/// reexported like so `pub use submod::__test_reexports as submod`. +fn mk_reexport_mod(cx: &mut TestCtxt<'_>, + parent: ast::NodeId, + tests: Vec, + tested_submods: Vec<(Ident, Ident)>) + -> (P, Ident) { + let super_ = Ident::with_empty_ctxt(kw::Super); + + let items = tests.into_iter().map(|r| { + cx.ext_cx.item_use_simple(DUMMY_SP, dummy_spanned(ast::VisibilityKind::Public), + cx.ext_cx.path(DUMMY_SP, vec![super_, r])) + }).chain(tested_submods.into_iter().map(|(r, sym)| { + let path = cx.ext_cx.path(DUMMY_SP, vec![super_, r, sym]); + cx.ext_cx.item_use_simple_(DUMMY_SP, dummy_spanned(ast::VisibilityKind::Public), + Some(r), path) + })).collect(); + + let reexport_mod = ast::Mod { + inline: true, + inner: DUMMY_SP, + items, + }; + + let name = Ident::from_str("__test_reexports").gensym(); + let parent = if parent == ast::DUMMY_NODE_ID { ast::CRATE_NODE_ID } else { parent }; + cx.ext_cx.current_expansion.id = cx.ext_cx.resolver.get_module_scope(parent); + let it = cx.ext_cx.monotonic_expander().flat_map_item(P(ast::Item { + ident: name, + attrs: Vec::new(), + id: ast::DUMMY_NODE_ID, + node: ast::ItemKind::Mod(reexport_mod), + vis: dummy_spanned(ast::VisibilityKind::Public), + span: DUMMY_SP, + tokens: None, + })).pop().unwrap(); + + (it, name) +} + +/// Crawl over the crate, inserting test reexports and the test main function +fn generate_test_harness(sess: &ParseSess, + resolver: &mut dyn Resolver, + reexport_test_harness_main: Option, + krate: &mut ast::Crate, + sd: &errors::Handler, + features: &Features, + test_runner: Option) { + // Remove the entry points + let mut cleaner = EntryPointCleaner { depth: 0 }; + cleaner.visit_crate(krate); + + let mut econfig = ExpansionConfig::default("test".to_string()); + econfig.features = Some(features); + + let cx = TestCtxt { + span_diagnostic: sd, + ext_cx: ExtCtxt::new(sess, econfig, resolver), + path: Vec::new(), + test_cases: Vec::new(), + reexport_test_harness_main, + toplevel_reexport: None, + test_runner + }; + + TestHarnessGenerator { + cx, + tests: Vec::new(), + tested_submods: Vec::new(), + }.visit_crate(krate); +} + +/// Creates a function item for use as the main function of a test build. +/// This function will call the `test_runner` as specified by the crate attribute +fn mk_main(cx: &mut TestCtxt<'_>) -> P { + // Writing this out by hand: + // pub fn main() { + // #![main] + // test::test_main_static(&[..tests]); + // } + let sp = DUMMY_SP.fresh_expansion(ExpnId::root(), ExpnInfo::allow_unstable( + ExpnKind::Macro(MacroKind::Attr, sym::test_case), DUMMY_SP, cx.ext_cx.parse_sess.edition, + [sym::main, sym::test, sym::rustc_attrs][..].into(), + )); + let ecx = &cx.ext_cx; + let test_id = Ident::with_empty_ctxt(sym::test); + + // test::test_main_static(...) + let mut test_runner = cx.test_runner.clone().unwrap_or( + ecx.path(sp, vec![ + test_id, ecx.ident_of("test_main_static") + ])); + + test_runner.span = sp; + + let test_main_path_expr = ecx.expr_path(test_runner); + let call_test_main = ecx.expr_call(sp, test_main_path_expr, + vec![mk_tests_slice(cx)]); + let call_test_main = ecx.stmt_expr(call_test_main); + + // #![main] + let main_meta = ecx.meta_word(sp, sym::main); + let main_attr = ecx.attribute(sp, main_meta); + + // extern crate test as test_gensym + let test_extern_stmt = ecx.stmt_item(sp, ecx.item(sp, + test_id, + vec![], + ast::ItemKind::ExternCrate(None) + )); + + // pub fn main() { ... } + let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![])); + + // If no test runner is provided we need to import the test crate + let main_body = if cx.test_runner.is_none() { + ecx.block(sp, vec![test_extern_stmt, call_test_main]) + } else { + ecx.block(sp, vec![call_test_main]) + }; + + let main = ast::ItemKind::Fn(ecx.fn_decl(vec![], ast::FunctionRetTy::Ty(main_ret_ty)), + ast::FnHeader::default(), + ast::Generics::default(), + main_body); + + // Honor the reexport_test_harness_main attribute + let main_id = match cx.reexport_test_harness_main { + Some(sym) => Ident::new(sym, sp), + None => Ident::from_str_and_span("main", sp).gensym(), + }; + + P(ast::Item { + ident: main_id, + attrs: vec![main_attr], + id: ast::DUMMY_NODE_ID, + node: main, + vis: dummy_spanned(ast::VisibilityKind::Public), + span: sp, + tokens: None, + }) + +} + +fn path_name_i(idents: &[Ident]) -> String { + let mut path_name = "".to_string(); + let mut idents_iter = idents.iter().peekable(); + while let Some(ident) = idents_iter.next() { + path_name.push_str(&ident.as_str()); + if idents_iter.peek().is_some() { + path_name.push_str("::") + } + } + path_name +} + +/// Creates a slice containing every test like so: +/// &[path::to::test1, path::to::test2] +fn mk_tests_slice(cx: &TestCtxt<'_>) -> P { + debug!("building test vector from {} tests", cx.test_cases.len()); + let ref ecx = cx.ext_cx; + + ecx.expr_vec_slice(DUMMY_SP, + cx.test_cases.iter().map(|test| { + ecx.expr_addr_of(test.span, + ecx.expr_path(ecx.path(test.span, visible_path(cx, &test.path)))) + }).collect()) +} + +/// Creates a path from the top-level __test module to the test via __test_reexports +fn visible_path(cx: &TestCtxt<'_>, path: &[Ident]) -> Vec{ + let mut visible_path = vec![]; + match cx.toplevel_reexport { + Some(id) => visible_path.push(id), + None => { + cx.span_diagnostic.bug("expected to find top-level re-export name, but found None"); + } + } + visible_path.extend_from_slice(path); + visible_path +} + +fn is_test_case(i: &ast::Item) -> bool { + attr::contains_name(&i.attrs, sym::rustc_test_marker) +} + +fn get_test_runner(sd: &errors::Handler, krate: &ast::Crate) -> Option { + let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?; + test_attr.meta_item_list().map(|meta_list| { + if meta_list.len() != 1 { + sd.span_fatal(test_attr.span, + "`#![test_runner(..)]` accepts exactly 1 argument").raise() + } + match meta_list[0].meta_item() { + Some(meta_item) if meta_item.is_word() => meta_item.path.clone(), + _ => sd.span_fatal(test_attr.span, "`test_runner` argument must be a path").raise() + } + }) +} -- cgit 1.4.1-3-g733a5 From 4d535bdf59136f69b55107caaa0f5492b5e84d2d Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 18 Jul 2019 22:29:07 +0300 Subject: Move standard library injection into libsyntax_ext --- src/librustc/hir/lowering.rs | 3 +- src/librustc_interface/passes.rs | 7 +- src/librustc_resolve/build_reduced_graph.rs | 7 +- src/libsyntax/lib.rs | 1 - src/libsyntax/parse/mod.rs | 5 +- src/libsyntax/print/pprust.rs | 3 +- src/libsyntax/std_inject.rs | 112 -------------------------- src/libsyntax_ext/lib.rs | 1 + src/libsyntax_ext/standard_library_imports.rs | 95 ++++++++++++++++++++++ 9 files changed, 112 insertions(+), 122 deletions(-) delete mode 100644 src/libsyntax/std_inject.rs create mode 100644 src/libsyntax_ext/standard_library_imports.rs diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 2d1835514d4..0d431b010d6 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -63,7 +63,6 @@ use syntax::errors; use syntax::ext::hygiene::ExpnId; use syntax::print::pprust; use syntax::source_map::{respan, ExpnInfo, ExpnKind, DesugaringKind, Spanned}; -use syntax::std_inject; use syntax::symbol::{kw, sym, Symbol}; use syntax::tokenstream::{TokenStream, TokenTree}; use syntax::parse::token::{self, Token}; @@ -241,7 +240,7 @@ pub fn lower_crate( dep_graph.assert_ignored(); LoweringContext { - crate_root: std_inject::injected_crate_name().map(Symbol::intern), + crate_root: sess.parse_sess.injected_crate_name.try_get().copied(), sess, cstore, resolver, diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 58edb898c25..c7be6276f4a 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -278,7 +278,12 @@ pub fn register_plugins<'a>( krate = time(sess, "crate injection", || { let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| &**s); - syntax::std_inject::maybe_inject_crates_ref(krate, alt_std_name, sess.edition()) + let (krate, name) = + syntax_ext::standard_library_imports::inject(krate, alt_std_name, sess.edition()); + if let Some(name) = name { + sess.parse_sess.injected_crate_name.set(name); + } + krate }); let registrars = time(sess, "plugin loading", || { diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index f52f7d9cfb3..41349cf72a1 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -34,7 +34,6 @@ use syntax::ext::hygiene::ExpnId; use syntax::feature_gate::is_builtin_attr; use syntax::parse::token::{self, Token}; use syntax::span_err; -use syntax::std_inject::injected_crate_name; use syntax::symbol::{kw, sym}; use syntax::visit::{self, Visitor}; @@ -367,8 +366,10 @@ impl<'a> Resolver<'a> { }; self.populate_module_if_necessary(module); - if injected_crate_name().map_or(false, |name| ident.name.as_str() == name) { - self.injected_crate = Some(module); + if let Some(name) = self.session.parse_sess.injected_crate_name.try_get() { + if name.as_str() == ident.name.as_str() { + self.injected_crate = Some(module); + } } let used = self.process_legacy_macro_imports(item, module, &parent_scope); diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 17f379f31b2..bb6a8dfb141 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -153,7 +153,6 @@ pub mod mut_visit; pub mod parse; pub mod ptr; pub mod show_span; -pub mod std_inject; pub use syntax_pos::edition; pub use syntax_pos::symbol; pub mod tokenstream; diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 225065c1cf1..1aac8bbb7aa 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -10,9 +10,10 @@ use crate::parse::token::TokenKind; use crate::tokenstream::{TokenStream, TokenTree}; use crate::diagnostics::plugin::ErrorMap; use crate::print::pprust; +use crate::symbol::Symbol; use errors::{Applicability, FatalError, Level, Handler, ColorConfig, Diagnostic, DiagnosticBuilder}; -use rustc_data_structures::sync::{Lrc, Lock}; +use rustc_data_structures::sync::{Lrc, Lock, Once}; use syntax_pos::{Span, SourceFile, FileName, MultiSpan}; use syntax_pos::edition::Edition; @@ -58,6 +59,7 @@ pub struct ParseSess { pub let_chains_spans: Lock>, // Places where `async || ..` exprs were used and should be feature gated. pub async_closure_spans: Lock>, + pub injected_crate_name: Once, } impl ParseSess { @@ -86,6 +88,7 @@ impl ParseSess { param_attr_spans: Lock::new(Vec::new()), let_chains_spans: Lock::new(Vec::new()), async_closure_spans: Lock::new(Vec::new()), + injected_crate_name: Once::new(), } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 16e0bace925..c4623576395 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -10,7 +10,6 @@ use crate::parse::{self, ParseSess}; use crate::print::pp::{self, Breaks}; use crate::print::pp::Breaks::{Consistent, Inconsistent}; use crate::ptr::P; -use crate::std_inject; use crate::symbol::{kw, sym}; use crate::tokenstream::{self, TokenStream, TokenTree}; @@ -114,7 +113,7 @@ pub fn print_crate<'a>(cm: &'a SourceMap, is_expanded, }; - if is_expanded && std_inject::injected_crate_name().is_some() { + if is_expanded && sess.injected_crate_name.try_get().is_some() { // We need to print `#![no_std]` (and its feature gate) so that // compiling pretty-printed source won't inject libstd again. // However we don't want these attributes in the AST because diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs deleted file mode 100644 index 3fba81c0b69..00000000000 --- a/src/libsyntax/std_inject.rs +++ /dev/null @@ -1,112 +0,0 @@ -use crate::ast; -use crate::attr; -use crate::edition::Edition; -use crate::ext::hygiene::{ExpnId, MacroKind}; -use crate::symbol::{Ident, Symbol, kw, sym}; -use crate::source_map::{ExpnInfo, ExpnKind, dummy_spanned, respan}; -use crate::ptr::P; -use crate::tokenstream::TokenStream; - -use std::cell::Cell; -use std::iter; -use syntax_pos::DUMMY_SP; - -pub fn injected_crate_name() -> Option<&'static str> { - INJECTED_CRATE_NAME.with(|name| name.get()) -} - -thread_local! { - // A `Symbol` might make more sense here, but it doesn't work, probably for - // reasons relating to the use of thread-local storage for the Symbol - // interner. - static INJECTED_CRATE_NAME: Cell> = Cell::new(None); -} - -pub fn maybe_inject_crates_ref( - mut krate: ast::Crate, - alt_std_name: Option<&str>, - edition: Edition, -) -> ast::Crate { - let rust_2018 = edition >= Edition::Edition2018; - - // the first name in this list is the crate name of the crate with the prelude - let names: &[&str] = if attr::contains_name(&krate.attrs, sym::no_core) { - return krate; - } else if attr::contains_name(&krate.attrs, sym::no_std) { - if attr::contains_name(&krate.attrs, sym::compiler_builtins) { - &["core"] - } else { - &["core", "compiler_builtins"] - } - } else { - &["std"] - }; - - // .rev() to preserve ordering above in combination with insert(0, ...) - let alt_std_name = alt_std_name.map(Symbol::intern); - for orig_name_str in names.iter().rev() { - // HACK(eddyb) gensym the injected crates on the Rust 2018 edition, - // so they don't accidentally interfere with the new import paths. - let orig_name_sym = Symbol::intern(orig_name_str); - let orig_name_ident = Ident::with_empty_ctxt(orig_name_sym); - let (rename, orig_name) = if rust_2018 { - (orig_name_ident.gensym(), Some(orig_name_sym)) - } else { - (orig_name_ident, None) - }; - krate.module.items.insert(0, P(ast::Item { - attrs: vec![attr::mk_attr_outer( - DUMMY_SP, - attr::mk_attr_id(), - attr::mk_word_item(ast::Ident::with_empty_ctxt(sym::macro_use)) - )], - vis: dummy_spanned(ast::VisibilityKind::Inherited), - node: ast::ItemKind::ExternCrate(alt_std_name.or(orig_name)), - ident: rename, - id: ast::DUMMY_NODE_ID, - span: DUMMY_SP, - tokens: None, - })); - } - - // the crates have been injected, the assumption is that the first one is the one with - // the prelude. - let name = names[0]; - - INJECTED_CRATE_NAME.with(|opt_name| opt_name.set(Some(name))); - - let span = DUMMY_SP.fresh_expansion(ExpnId::root(), ExpnInfo::allow_unstable( - ExpnKind::Macro(MacroKind::Attr, sym::std_inject), DUMMY_SP, edition, - [sym::prelude_import][..].into(), - )); - - krate.module.items.insert(0, P(ast::Item { - attrs: vec![ast::Attribute { - style: ast::AttrStyle::Outer, - path: ast::Path::from_ident(ast::Ident::new(sym::prelude_import, span)), - tokens: TokenStream::empty(), - id: attr::mk_attr_id(), - is_sugared_doc: false, - span, - }], - vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), - node: ast::ItemKind::Use(P(ast::UseTree { - prefix: ast::Path { - segments: iter::once(ast::Ident::with_empty_ctxt(kw::PathRoot)) - .chain( - [name, "prelude", "v1"].iter().cloned() - .map(ast::Ident::from_str) - ).map(ast::PathSegment::from_ident).collect(), - span, - }, - kind: ast::UseTreeKind::Glob, - span, - })), - id: ast::DUMMY_NODE_ID, - ident: ast::Ident::invalid(), - span, - tokens: None, - })); - - krate -} diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index cd0d0886239..f49c75d7424 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -43,6 +43,7 @@ mod trace_macros; pub mod plugin_macro_defs; pub mod proc_macro_decls; +pub mod standard_library_imports; pub mod test_harness; pub fn register_builtin_macros(resolver: &mut dyn syntax::ext::base::Resolver, edition: Edition) { diff --git a/src/libsyntax_ext/standard_library_imports.rs b/src/libsyntax_ext/standard_library_imports.rs new file mode 100644 index 00000000000..81bb32d79a2 --- /dev/null +++ b/src/libsyntax_ext/standard_library_imports.rs @@ -0,0 +1,95 @@ +use syntax::{ast, attr}; +use syntax::edition::Edition; +use syntax::ext::hygiene::{ExpnId, MacroKind}; +use syntax::ptr::P; +use syntax::source_map::{ExpnInfo, ExpnKind, dummy_spanned, respan}; +use syntax::symbol::{Ident, Symbol, kw, sym}; +use syntax::tokenstream::TokenStream; +use syntax_pos::DUMMY_SP; + +use std::iter; + +pub fn inject( + mut krate: ast::Crate, alt_std_name: Option<&str>, edition: Edition +) -> (ast::Crate, Option) { + let rust_2018 = edition >= Edition::Edition2018; + + // the first name in this list is the crate name of the crate with the prelude + let names: &[&str] = if attr::contains_name(&krate.attrs, sym::no_core) { + return (krate, None); + } else if attr::contains_name(&krate.attrs, sym::no_std) { + if attr::contains_name(&krate.attrs, sym::compiler_builtins) { + &["core"] + } else { + &["core", "compiler_builtins"] + } + } else { + &["std"] + }; + + // .rev() to preserve ordering above in combination with insert(0, ...) + let alt_std_name = alt_std_name.map(Symbol::intern); + for orig_name_str in names.iter().rev() { + // HACK(eddyb) gensym the injected crates on the Rust 2018 edition, + // so they don't accidentally interfere with the new import paths. + let orig_name_sym = Symbol::intern(orig_name_str); + let orig_name_ident = Ident::with_empty_ctxt(orig_name_sym); + let (rename, orig_name) = if rust_2018 { + (orig_name_ident.gensym(), Some(orig_name_sym)) + } else { + (orig_name_ident, None) + }; + krate.module.items.insert(0, P(ast::Item { + attrs: vec![attr::mk_attr_outer( + DUMMY_SP, + attr::mk_attr_id(), + attr::mk_word_item(ast::Ident::with_empty_ctxt(sym::macro_use)) + )], + vis: dummy_spanned(ast::VisibilityKind::Inherited), + node: ast::ItemKind::ExternCrate(alt_std_name.or(orig_name)), + ident: rename, + id: ast::DUMMY_NODE_ID, + span: DUMMY_SP, + tokens: None, + })); + } + + // the crates have been injected, the assumption is that the first one is the one with + // the prelude. + let name = names[0]; + + let span = DUMMY_SP.fresh_expansion(ExpnId::root(), ExpnInfo::allow_unstable( + ExpnKind::Macro(MacroKind::Attr, sym::std_inject), DUMMY_SP, edition, + [sym::prelude_import][..].into(), + )); + + krate.module.items.insert(0, P(ast::Item { + attrs: vec![ast::Attribute { + style: ast::AttrStyle::Outer, + path: ast::Path::from_ident(ast::Ident::new(sym::prelude_import, span)), + tokens: TokenStream::empty(), + id: attr::mk_attr_id(), + is_sugared_doc: false, + span, + }], + vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), + node: ast::ItemKind::Use(P(ast::UseTree { + prefix: ast::Path { + segments: iter::once(ast::Ident::with_empty_ctxt(kw::PathRoot)) + .chain( + [name, "prelude", "v1"].iter().cloned() + .map(ast::Ident::from_str) + ).map(ast::PathSegment::from_ident).collect(), + span, + }, + kind: ast::UseTreeKind::Glob, + span, + })), + id: ast::DUMMY_NODE_ID, + ident: ast::Ident::invalid(), + span, + tokens: None, + })); + + (krate, Some(Symbol::intern(name))) +} -- cgit 1.4.1-3-g733a5 From b5a0e6ea807bcdc71f145038dd1129c22dcf17fd Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 18 Jul 2019 23:29:57 +0300 Subject: syntax_ext: `proc_macro_decls` -> `proc_macro_harness` Few other minor renamings for consistency. Remove one unused dependency from `rustc_passes`. Fix libsyntax tests. Fix rebase. --- Cargo.lock | 1 - src/librustc_interface/passes.rs | 4 +- src/librustc_passes/Cargo.toml | 1 - src/libsyntax/ext/base.rs | 2 +- src/libsyntax/ext/proc_macro.rs | 2 +- src/libsyntax/parse/lexer/mod.rs | 3 +- src/libsyntax_ext/lib.rs | 9 +- src/libsyntax_ext/proc_macro_decls.rs | 414 -------------------------------- src/libsyntax_ext/proc_macro_harness.rs | 414 ++++++++++++++++++++++++++++++++ src/libsyntax_ext/test.rs | 2 +- src/libsyntax_ext/test_harness.rs | 14 +- 11 files changed, 432 insertions(+), 434 deletions(-) delete mode 100644 src/libsyntax_ext/proc_macro_decls.rs create mode 100644 src/libsyntax_ext/proc_macro_harness.rs diff --git a/Cargo.lock b/Cargo.lock index d1afe5944ee..46d8b3de806 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3052,7 +3052,6 @@ dependencies = [ "rustc 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", - "rustc_mir 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", ] diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index c7be6276f4a..3c7d854b36b 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -461,7 +461,7 @@ fn configure_and_expand_inner<'a>( sess.profiler(|p| p.end_activity("macro expansion")); time(sess, "maybe building test harness", || { - syntax_ext::test_harness::modify_for_testing( + syntax_ext::test_harness::inject( &sess.parse_sess, &mut resolver, sess.opts.test, @@ -490,7 +490,7 @@ fn configure_and_expand_inner<'a>( let num_crate_types = crate_types.len(); let is_proc_macro_crate = crate_types.contains(&config::CrateType::ProcMacro); let is_test_crate = sess.opts.test; - syntax_ext::proc_macro_decls::modify( + syntax_ext::proc_macro_harness::inject( &sess.parse_sess, &mut resolver, krate, diff --git a/src/librustc_passes/Cargo.toml b/src/librustc_passes/Cargo.toml index 5f378dacd25..596ec6c19bc 100644 --- a/src/librustc_passes/Cargo.toml +++ b/src/librustc_passes/Cargo.toml @@ -11,7 +11,6 @@ path = "lib.rs" [dependencies] log = "0.4" rustc = { path = "../librustc" } -rustc_mir = { path = "../librustc_mir"} rustc_data_structures = { path = "../librustc_data_structures" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index e53757dce3d..bb7834a133f 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -1,6 +1,6 @@ use crate::ast::{self, Attribute, Name, PatKind}; use crate::attr::{HasAttrs, Stability, Deprecation}; -use crate::source_map::{SourceMap, Spanned, FileName, respan}; +use crate::source_map::{SourceMap, Spanned, respan}; use crate::edition::Edition; use crate::ext::expand::{self, AstFragment, Invocation}; use crate::ext::hygiene::{ExpnId, SyntaxContext, Transparency}; diff --git a/src/libsyntax/ext/proc_macro.rs b/src/libsyntax/ext/proc_macro.rs index 4b4ac57207e..425b9813f59 100644 --- a/src/libsyntax/ext/proc_macro.rs +++ b/src/libsyntax/ext/proc_macro.rs @@ -231,7 +231,7 @@ crate fn add_derived_markers( names.insert(unwrap_or!(path.segments.get(0), continue).ident.name); } - let span = span.fresh_expansion(cx.current_expansion.mark, ExpnInfo::allow_unstable( + let span = span.fresh_expansion(cx.current_expansion.id, ExpnInfo::allow_unstable( ExpnKind::Macro(MacroKind::Derive, Symbol::intern(&pretty_name)), span, cx.parse_sess.edition, cx.allow_derive_markers.clone(), )); diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 52f65e1b474..3cd5464f357 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -794,7 +794,7 @@ mod tests { use std::path::PathBuf; use syntax_pos::{BytePos, Span, NO_EXPANSION, edition::Edition}; use rustc_data_structures::fx::{FxHashSet, FxHashMap}; - use rustc_data_structures::sync::Lock; + use rustc_data_structures::sync::{Lock, Once}; fn mk_sess(sm: Lrc) -> ParseSess { let emitter = errors::emitter::EmitterWriter::new(Box::new(io::sink()), @@ -817,6 +817,7 @@ mod tests { param_attr_spans: Lock::new(Vec::new()), let_chains_spans: Lock::new(Vec::new()), async_closure_spans: Lock::new(Vec::new()), + injected_crate_name: Once::new(), } } diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index f49c75d7424..fae884860ed 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -1,4 +1,5 @@ -//! Syntax extensions in the Rust compiler. +//! This crate contains implementations of built-in macros and other code generating facilities +//! injecting code into the crate before it is lowered to HIR. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] @@ -10,16 +11,12 @@ #![feature(mem_take)] #![feature(nll)] #![feature(rustc_diagnostic_macros)] -#![feature(unicode_internals)] - -extern crate proc_macro; use crate::deriving::*; use syntax::ast::Ident; use syntax::edition::Edition; use syntax::ext::base::{SyntaxExtension, SyntaxExtensionKind, MacroExpanderFn}; -use syntax::ext::source_util; use syntax::symbol::sym; mod error_codes; @@ -42,7 +39,7 @@ mod test; mod trace_macros; pub mod plugin_macro_defs; -pub mod proc_macro_decls; +pub mod proc_macro_harness; pub mod standard_library_imports; pub mod test_harness; diff --git a/src/libsyntax_ext/proc_macro_decls.rs b/src/libsyntax_ext/proc_macro_decls.rs deleted file mode 100644 index 357da6ba3c3..00000000000 --- a/src/libsyntax_ext/proc_macro_decls.rs +++ /dev/null @@ -1,414 +0,0 @@ -use std::mem; - -use syntax::ast::{self, Ident}; -use syntax::attr; -use syntax::source_map::{ExpnInfo, ExpnKind, respan}; -use syntax::ext::base::{ExtCtxt, MacroKind}; -use syntax::ext::build::AstBuilder; -use syntax::ext::expand::ExpansionConfig; -use syntax::ext::hygiene::ExpnId; -use syntax::ext::proc_macro::is_proc_macro_attr; -use syntax::mut_visit::MutVisitor; -use syntax::parse::ParseSess; -use syntax::ptr::P; -use syntax::symbol::{kw, sym}; -use syntax::visit::{self, Visitor}; - -use syntax_pos::{Span, DUMMY_SP}; - -struct ProcMacroDerive { - trait_name: ast::Name, - function_name: Ident, - span: Span, - attrs: Vec, -} - -struct ProcMacroDef { - function_name: Ident, - span: Span, -} - -struct CollectProcMacros<'a> { - derives: Vec, - attr_macros: Vec, - bang_macros: Vec, - in_root: bool, - handler: &'a errors::Handler, - is_proc_macro_crate: bool, - is_test_crate: bool, -} - -pub fn modify(sess: &ParseSess, - resolver: &mut dyn (::syntax::ext::base::Resolver), - mut krate: ast::Crate, - is_proc_macro_crate: bool, - has_proc_macro_decls: bool, - is_test_crate: bool, - num_crate_types: usize, - handler: &errors::Handler) -> ast::Crate { - let ecfg = ExpansionConfig::default("proc_macro".to_string()); - let mut cx = ExtCtxt::new(sess, ecfg, resolver); - - let (derives, attr_macros, bang_macros) = { - let mut collect = CollectProcMacros { - derives: Vec::new(), - attr_macros: Vec::new(), - bang_macros: Vec::new(), - in_root: true, - handler, - is_proc_macro_crate, - is_test_crate, - }; - if has_proc_macro_decls || is_proc_macro_crate { - visit::walk_crate(&mut collect, &krate); - } - (collect.derives, collect.attr_macros, collect.bang_macros) - }; - - if !is_proc_macro_crate { - return krate - } - - if num_crate_types > 1 { - handler.err("cannot mix `proc-macro` crate type with others"); - } - - if is_test_crate { - return krate; - } - - krate.module.items.push(mk_decls(&mut cx, &derives, &attr_macros, &bang_macros)); - - krate -} - -impl<'a> CollectProcMacros<'a> { - fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { - if self.is_proc_macro_crate && self.in_root && vis.node.is_pub() { - self.handler.span_err(sp, - "`proc-macro` crate types cannot \ - export any items other than functions \ - tagged with `#[proc_macro_derive]` currently"); - } - } - - fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { - // Once we've located the `#[proc_macro_derive]` attribute, verify - // that it's of the form `#[proc_macro_derive(Foo)]` or - // `#[proc_macro_derive(Foo, attributes(A, ..))]` - let list = match attr.meta_item_list() { - Some(list) => list, - None => return, - }; - if list.len() != 1 && list.len() != 2 { - self.handler.span_err(attr.span, - "attribute must have either one or two arguments"); - return - } - let trait_attr = match list[0].meta_item() { - Some(meta_item) => meta_item, - _ => { - self.handler.span_err(list[0].span(), "not a meta item"); - return - } - }; - let trait_ident = match trait_attr.ident() { - Some(trait_ident) if trait_attr.is_word() => trait_ident, - _ => { - self.handler.span_err(trait_attr.span, "must only be one word"); - return - } - }; - - if !trait_ident.name.can_be_raw() { - self.handler.span_err(trait_attr.span, - &format!("`{}` cannot be a name of derive macro", trait_ident)); - } - - let attributes_attr = list.get(1); - let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr { - if !attr.check_name(sym::attributes) { - self.handler.span_err(attr.span(), "second argument must be `attributes`") - } - attr.meta_item_list().unwrap_or_else(|| { - self.handler.span_err(attr.span(), - "attribute must be of form: `attributes(foo, bar)`"); - &[] - }).into_iter().filter_map(|attr| { - let attr = match attr.meta_item() { - Some(meta_item) => meta_item, - _ => { - self.handler.span_err(attr.span(), "not a meta item"); - return None; - } - }; - - let ident = match attr.ident() { - Some(ident) if attr.is_word() => ident, - _ => { - self.handler.span_err(attr.span, "must only be one word"); - return None; - } - }; - if !ident.name.can_be_raw() { - self.handler.span_err( - attr.span, - &format!("`{}` cannot be a name of derive helper attribute", ident), - ); - } - - Some(ident.name) - }).collect() - } else { - Vec::new() - }; - - if self.in_root && item.vis.node.is_pub() { - self.derives.push(ProcMacroDerive { - span: item.span, - trait_name: trait_ident.name, - function_name: item.ident, - attrs: proc_attrs, - }); - } else { - let msg = if !self.in_root { - "functions tagged with `#[proc_macro_derive]` must \ - currently reside in the root of the crate" - } else { - "functions tagged with `#[proc_macro_derive]` must be `pub`" - }; - self.handler.span_err(item.span, msg); - } - } - - fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) { - if self.in_root && item.vis.node.is_pub() { - self.attr_macros.push(ProcMacroDef { - span: item.span, - function_name: item.ident, - }); - } else { - let msg = if !self.in_root { - "functions tagged with `#[proc_macro_attribute]` must \ - currently reside in the root of the crate" - } else { - "functions tagged with `#[proc_macro_attribute]` must be `pub`" - }; - self.handler.span_err(item.span, msg); - } - } - - fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) { - if self.in_root && item.vis.node.is_pub() { - self.bang_macros.push(ProcMacroDef { - span: item.span, - function_name: item.ident, - }); - } else { - let msg = if !self.in_root { - "functions tagged with `#[proc_macro]` must \ - currently reside in the root of the crate" - } else { - "functions tagged with `#[proc_macro]` must be `pub`" - }; - self.handler.span_err(item.span, msg); - } - } -} - -impl<'a> Visitor<'a> for CollectProcMacros<'a> { - fn visit_item(&mut self, item: &'a ast::Item) { - if let ast::ItemKind::MacroDef(..) = item.node { - if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) { - let msg = - "cannot export macro_rules! macros from a `proc-macro` crate type currently"; - self.handler.span_err(item.span, msg); - } - } - - // First up, make sure we're checking a bare function. If we're not then - // we're just not interested in this item. - // - // If we find one, try to locate a `#[proc_macro_derive]` attribute on it. - let is_fn = match item.node { - ast::ItemKind::Fn(..) => true, - _ => false, - }; - - let mut found_attr: Option<&'a ast::Attribute> = None; - - for attr in &item.attrs { - if is_proc_macro_attr(&attr) { - if let Some(prev_attr) = found_attr { - let msg = if attr.path.segments[0].ident.name == - prev_attr.path.segments[0].ident.name { - format!("only one `#[{}]` attribute is allowed on any given function", - attr.path) - } else { - format!("`#[{}]` and `#[{}]` attributes cannot both be applied \ - to the same function", attr.path, prev_attr.path) - }; - - self.handler.struct_span_err(attr.span, &msg) - .span_note(prev_attr.span, "previous attribute here") - .emit(); - - return; - } - - found_attr = Some(attr); - } - } - - let attr = match found_attr { - None => { - self.check_not_pub_in_root(&item.vis, item.span); - let prev_in_root = mem::replace(&mut self.in_root, false); - visit::walk_item(self, item); - self.in_root = prev_in_root; - return; - }, - Some(attr) => attr, - }; - - if !is_fn { - let msg = format!("the `#[{}]` attribute may only be used on bare functions", - attr.path); - - self.handler.span_err(attr.span, &msg); - return; - } - - if self.is_test_crate { - return; - } - - if !self.is_proc_macro_crate { - let msg = format!("the `#[{}]` attribute is only usable with crates of the \ - `proc-macro` crate type", attr.path); - - self.handler.span_err(attr.span, &msg); - return; - } - - if attr.check_name(sym::proc_macro_derive) { - self.collect_custom_derive(item, attr); - } else if attr.check_name(sym::proc_macro_attribute) { - self.collect_attr_proc_macro(item); - } else if attr.check_name(sym::proc_macro) { - self.collect_bang_proc_macro(item); - }; - - let prev_in_root = mem::replace(&mut self.in_root, false); - visit::walk_item(self, item); - self.in_root = prev_in_root; - } - - fn visit_mac(&mut self, mac: &'a ast::Mac) { - visit::walk_mac(self, mac) - } -} - -// Creates a new module which looks like: -// -// #[doc(hidden)] -// mod $gensym { -// extern crate proc_macro; -// -// use proc_macro::bridge::client::ProcMacro; -// -// #[rustc_proc_macro_decls] -// static DECLS: &[ProcMacro] = &[ -// ProcMacro::custom_derive($name_trait1, &[], ::$name1); -// ProcMacro::custom_derive($name_trait2, &["attribute_name"], ::$name2); -// // ... -// ]; -// } -fn mk_decls( - cx: &mut ExtCtxt<'_>, - custom_derives: &[ProcMacroDerive], - custom_attrs: &[ProcMacroDef], - custom_macros: &[ProcMacroDef], -) -> P { - let span = DUMMY_SP.fresh_expansion(ExpnId::root(), ExpnInfo::allow_unstable( - ExpnKind::Macro(MacroKind::Attr, sym::proc_macro), DUMMY_SP, cx.parse_sess.edition, - [sym::rustc_attrs, sym::proc_macro_internals][..].into(), - )); - - let hidden = cx.meta_list_item_word(span, sym::hidden); - let doc = cx.meta_list(span, sym::doc, vec![hidden]); - let doc_hidden = cx.attribute(span, doc); - - let proc_macro = Ident::with_empty_ctxt(sym::proc_macro); - let krate = cx.item(span, - proc_macro, - Vec::new(), - ast::ItemKind::ExternCrate(None)); - - let bridge = Ident::from_str("bridge"); - let client = Ident::from_str("client"); - let proc_macro_ty = Ident::from_str("ProcMacro"); - let custom_derive = Ident::from_str("custom_derive"); - let attr = Ident::from_str("attr"); - let bang = Ident::from_str("bang"); - let crate_kw = Ident::with_empty_ctxt(kw::Crate); - - let decls = { - let local_path = |sp: Span, name| { - cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![crate_kw, name])) - }; - let proc_macro_ty_method_path = |method| cx.expr_path(cx.path(span, vec![ - proc_macro, bridge, client, proc_macro_ty, method, - ])); - custom_derives.iter().map(|cd| { - cx.expr_call(span, proc_macro_ty_method_path(custom_derive), vec![ - cx.expr_str(cd.span, cd.trait_name), - cx.expr_vec_slice( - span, - cd.attrs.iter().map(|&s| cx.expr_str(cd.span, s)).collect::>() - ), - local_path(cd.span, cd.function_name), - ]) - }).chain(custom_attrs.iter().map(|ca| { - cx.expr_call(span, proc_macro_ty_method_path(attr), vec![ - cx.expr_str(ca.span, ca.function_name.name), - local_path(ca.span, ca.function_name), - ]) - })).chain(custom_macros.iter().map(|cm| { - cx.expr_call(span, proc_macro_ty_method_path(bang), vec![ - cx.expr_str(cm.span, cm.function_name.name), - local_path(cm.span, cm.function_name), - ]) - })).collect() - }; - - let decls_static = cx.item_static( - span, - Ident::from_str("_DECLS"), - cx.ty_rptr(span, - cx.ty(span, ast::TyKind::Slice( - cx.ty_path(cx.path(span, - vec![proc_macro, bridge, client, proc_macro_ty])))), - None, ast::Mutability::Immutable), - ast::Mutability::Immutable, - cx.expr_vec_slice(span, decls), - ).map(|mut i| { - let attr = cx.meta_word(span, sym::rustc_proc_macro_decls); - i.attrs.push(cx.attribute(span, attr)); - i.vis = respan(span, ast::VisibilityKind::Public); - i - }); - - let module = cx.item_mod( - span, - span, - ast::Ident::from_str("decls").gensym(), - vec![doc_hidden], - vec![krate, decls_static], - ).map(|mut i| { - i.vis = respan(span, ast::VisibilityKind::Public); - i - }); - - cx.monotonic_expander().flat_map_item(module).pop().unwrap() -} diff --git a/src/libsyntax_ext/proc_macro_harness.rs b/src/libsyntax_ext/proc_macro_harness.rs new file mode 100644 index 00000000000..fc6cd5dc94c --- /dev/null +++ b/src/libsyntax_ext/proc_macro_harness.rs @@ -0,0 +1,414 @@ +use std::mem; + +use syntax::ast::{self, Ident}; +use syntax::attr; +use syntax::source_map::{ExpnInfo, ExpnKind, respan}; +use syntax::ext::base::{ExtCtxt, MacroKind}; +use syntax::ext::build::AstBuilder; +use syntax::ext::expand::ExpansionConfig; +use syntax::ext::hygiene::ExpnId; +use syntax::ext::proc_macro::is_proc_macro_attr; +use syntax::mut_visit::MutVisitor; +use syntax::parse::ParseSess; +use syntax::ptr::P; +use syntax::symbol::{kw, sym}; +use syntax::visit::{self, Visitor}; + +use syntax_pos::{Span, DUMMY_SP}; + +struct ProcMacroDerive { + trait_name: ast::Name, + function_name: Ident, + span: Span, + attrs: Vec, +} + +struct ProcMacroDef { + function_name: Ident, + span: Span, +} + +struct CollectProcMacros<'a> { + derives: Vec, + attr_macros: Vec, + bang_macros: Vec, + in_root: bool, + handler: &'a errors::Handler, + is_proc_macro_crate: bool, + is_test_crate: bool, +} + +pub fn inject(sess: &ParseSess, + resolver: &mut dyn (::syntax::ext::base::Resolver), + mut krate: ast::Crate, + is_proc_macro_crate: bool, + has_proc_macro_decls: bool, + is_test_crate: bool, + num_crate_types: usize, + handler: &errors::Handler) -> ast::Crate { + let ecfg = ExpansionConfig::default("proc_macro".to_string()); + let mut cx = ExtCtxt::new(sess, ecfg, resolver); + + let (derives, attr_macros, bang_macros) = { + let mut collect = CollectProcMacros { + derives: Vec::new(), + attr_macros: Vec::new(), + bang_macros: Vec::new(), + in_root: true, + handler, + is_proc_macro_crate, + is_test_crate, + }; + if has_proc_macro_decls || is_proc_macro_crate { + visit::walk_crate(&mut collect, &krate); + } + (collect.derives, collect.attr_macros, collect.bang_macros) + }; + + if !is_proc_macro_crate { + return krate + } + + if num_crate_types > 1 { + handler.err("cannot mix `proc-macro` crate type with others"); + } + + if is_test_crate { + return krate; + } + + krate.module.items.push(mk_decls(&mut cx, &derives, &attr_macros, &bang_macros)); + + krate +} + +impl<'a> CollectProcMacros<'a> { + fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { + if self.is_proc_macro_crate && self.in_root && vis.node.is_pub() { + self.handler.span_err(sp, + "`proc-macro` crate types cannot \ + export any items other than functions \ + tagged with `#[proc_macro_derive]` currently"); + } + } + + fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { + // Once we've located the `#[proc_macro_derive]` attribute, verify + // that it's of the form `#[proc_macro_derive(Foo)]` or + // `#[proc_macro_derive(Foo, attributes(A, ..))]` + let list = match attr.meta_item_list() { + Some(list) => list, + None => return, + }; + if list.len() != 1 && list.len() != 2 { + self.handler.span_err(attr.span, + "attribute must have either one or two arguments"); + return + } + let trait_attr = match list[0].meta_item() { + Some(meta_item) => meta_item, + _ => { + self.handler.span_err(list[0].span(), "not a meta item"); + return + } + }; + let trait_ident = match trait_attr.ident() { + Some(trait_ident) if trait_attr.is_word() => trait_ident, + _ => { + self.handler.span_err(trait_attr.span, "must only be one word"); + return + } + }; + + if !trait_ident.name.can_be_raw() { + self.handler.span_err(trait_attr.span, + &format!("`{}` cannot be a name of derive macro", trait_ident)); + } + + let attributes_attr = list.get(1); + let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr { + if !attr.check_name(sym::attributes) { + self.handler.span_err(attr.span(), "second argument must be `attributes`") + } + attr.meta_item_list().unwrap_or_else(|| { + self.handler.span_err(attr.span(), + "attribute must be of form: `attributes(foo, bar)`"); + &[] + }).into_iter().filter_map(|attr| { + let attr = match attr.meta_item() { + Some(meta_item) => meta_item, + _ => { + self.handler.span_err(attr.span(), "not a meta item"); + return None; + } + }; + + let ident = match attr.ident() { + Some(ident) if attr.is_word() => ident, + _ => { + self.handler.span_err(attr.span, "must only be one word"); + return None; + } + }; + if !ident.name.can_be_raw() { + self.handler.span_err( + attr.span, + &format!("`{}` cannot be a name of derive helper attribute", ident), + ); + } + + Some(ident.name) + }).collect() + } else { + Vec::new() + }; + + if self.in_root && item.vis.node.is_pub() { + self.derives.push(ProcMacroDerive { + span: item.span, + trait_name: trait_ident.name, + function_name: item.ident, + attrs: proc_attrs, + }); + } else { + let msg = if !self.in_root { + "functions tagged with `#[proc_macro_derive]` must \ + currently reside in the root of the crate" + } else { + "functions tagged with `#[proc_macro_derive]` must be `pub`" + }; + self.handler.span_err(item.span, msg); + } + } + + fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) { + if self.in_root && item.vis.node.is_pub() { + self.attr_macros.push(ProcMacroDef { + span: item.span, + function_name: item.ident, + }); + } else { + let msg = if !self.in_root { + "functions tagged with `#[proc_macro_attribute]` must \ + currently reside in the root of the crate" + } else { + "functions tagged with `#[proc_macro_attribute]` must be `pub`" + }; + self.handler.span_err(item.span, msg); + } + } + + fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) { + if self.in_root && item.vis.node.is_pub() { + self.bang_macros.push(ProcMacroDef { + span: item.span, + function_name: item.ident, + }); + } else { + let msg = if !self.in_root { + "functions tagged with `#[proc_macro]` must \ + currently reside in the root of the crate" + } else { + "functions tagged with `#[proc_macro]` must be `pub`" + }; + self.handler.span_err(item.span, msg); + } + } +} + +impl<'a> Visitor<'a> for CollectProcMacros<'a> { + fn visit_item(&mut self, item: &'a ast::Item) { + if let ast::ItemKind::MacroDef(..) = item.node { + if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) { + let msg = + "cannot export macro_rules! macros from a `proc-macro` crate type currently"; + self.handler.span_err(item.span, msg); + } + } + + // First up, make sure we're checking a bare function. If we're not then + // we're just not interested in this item. + // + // If we find one, try to locate a `#[proc_macro_derive]` attribute on it. + let is_fn = match item.node { + ast::ItemKind::Fn(..) => true, + _ => false, + }; + + let mut found_attr: Option<&'a ast::Attribute> = None; + + for attr in &item.attrs { + if is_proc_macro_attr(&attr) { + if let Some(prev_attr) = found_attr { + let msg = if attr.path.segments[0].ident.name == + prev_attr.path.segments[0].ident.name { + format!("only one `#[{}]` attribute is allowed on any given function", + attr.path) + } else { + format!("`#[{}]` and `#[{}]` attributes cannot both be applied \ + to the same function", attr.path, prev_attr.path) + }; + + self.handler.struct_span_err(attr.span, &msg) + .span_note(prev_attr.span, "previous attribute here") + .emit(); + + return; + } + + found_attr = Some(attr); + } + } + + let attr = match found_attr { + None => { + self.check_not_pub_in_root(&item.vis, item.span); + let prev_in_root = mem::replace(&mut self.in_root, false); + visit::walk_item(self, item); + self.in_root = prev_in_root; + return; + }, + Some(attr) => attr, + }; + + if !is_fn { + let msg = format!("the `#[{}]` attribute may only be used on bare functions", + attr.path); + + self.handler.span_err(attr.span, &msg); + return; + } + + if self.is_test_crate { + return; + } + + if !self.is_proc_macro_crate { + let msg = format!("the `#[{}]` attribute is only usable with crates of the \ + `proc-macro` crate type", attr.path); + + self.handler.span_err(attr.span, &msg); + return; + } + + if attr.check_name(sym::proc_macro_derive) { + self.collect_custom_derive(item, attr); + } else if attr.check_name(sym::proc_macro_attribute) { + self.collect_attr_proc_macro(item); + } else if attr.check_name(sym::proc_macro) { + self.collect_bang_proc_macro(item); + }; + + let prev_in_root = mem::replace(&mut self.in_root, false); + visit::walk_item(self, item); + self.in_root = prev_in_root; + } + + fn visit_mac(&mut self, mac: &'a ast::Mac) { + visit::walk_mac(self, mac) + } +} + +// Creates a new module which looks like: +// +// #[doc(hidden)] +// mod $gensym { +// extern crate proc_macro; +// +// use proc_macro::bridge::client::ProcMacro; +// +// #[rustc_proc_macro_decls] +// static DECLS: &[ProcMacro] = &[ +// ProcMacro::custom_derive($name_trait1, &[], ::$name1); +// ProcMacro::custom_derive($name_trait2, &["attribute_name"], ::$name2); +// // ... +// ]; +// } +fn mk_decls( + cx: &mut ExtCtxt<'_>, + custom_derives: &[ProcMacroDerive], + custom_attrs: &[ProcMacroDef], + custom_macros: &[ProcMacroDef], +) -> P { + let span = DUMMY_SP.fresh_expansion(ExpnId::root(), ExpnInfo::allow_unstable( + ExpnKind::Macro(MacroKind::Attr, sym::proc_macro), DUMMY_SP, cx.parse_sess.edition, + [sym::rustc_attrs, sym::proc_macro_internals][..].into(), + )); + + let hidden = cx.meta_list_item_word(span, sym::hidden); + let doc = cx.meta_list(span, sym::doc, vec![hidden]); + let doc_hidden = cx.attribute(span, doc); + + let proc_macro = Ident::with_empty_ctxt(sym::proc_macro); + let krate = cx.item(span, + proc_macro, + Vec::new(), + ast::ItemKind::ExternCrate(None)); + + let bridge = Ident::from_str("bridge"); + let client = Ident::from_str("client"); + let proc_macro_ty = Ident::from_str("ProcMacro"); + let custom_derive = Ident::from_str("custom_derive"); + let attr = Ident::from_str("attr"); + let bang = Ident::from_str("bang"); + let crate_kw = Ident::with_empty_ctxt(kw::Crate); + + let decls = { + let local_path = |sp: Span, name| { + cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![crate_kw, name])) + }; + let proc_macro_ty_method_path = |method| cx.expr_path(cx.path(span, vec![ + proc_macro, bridge, client, proc_macro_ty, method, + ])); + custom_derives.iter().map(|cd| { + cx.expr_call(span, proc_macro_ty_method_path(custom_derive), vec![ + cx.expr_str(cd.span, cd.trait_name), + cx.expr_vec_slice( + span, + cd.attrs.iter().map(|&s| cx.expr_str(cd.span, s)).collect::>() + ), + local_path(cd.span, cd.function_name), + ]) + }).chain(custom_attrs.iter().map(|ca| { + cx.expr_call(span, proc_macro_ty_method_path(attr), vec![ + cx.expr_str(ca.span, ca.function_name.name), + local_path(ca.span, ca.function_name), + ]) + })).chain(custom_macros.iter().map(|cm| { + cx.expr_call(span, proc_macro_ty_method_path(bang), vec![ + cx.expr_str(cm.span, cm.function_name.name), + local_path(cm.span, cm.function_name), + ]) + })).collect() + }; + + let decls_static = cx.item_static( + span, + Ident::from_str("_DECLS"), + cx.ty_rptr(span, + cx.ty(span, ast::TyKind::Slice( + cx.ty_path(cx.path(span, + vec![proc_macro, bridge, client, proc_macro_ty])))), + None, ast::Mutability::Immutable), + ast::Mutability::Immutable, + cx.expr_vec_slice(span, decls), + ).map(|mut i| { + let attr = cx.meta_word(span, sym::rustc_proc_macro_decls); + i.attrs.push(cx.attribute(span, attr)); + i.vis = respan(span, ast::VisibilityKind::Public); + i + }); + + let module = cx.item_mod( + span, + span, + ast::Ident::from_str("decls").gensym(), + vec![doc_hidden], + vec![krate, decls_static], + ).map(|mut i| { + i.vis = respan(span, ast::VisibilityKind::Public); + i + }); + + cx.monotonic_expander().flat_map_item(module).pop().unwrap() +} diff --git a/src/libsyntax_ext/test.rs b/src/libsyntax_ext/test.rs index 36aeb3065ff..a2d93d01cec 100644 --- a/src/libsyntax_ext/test.rs +++ b/src/libsyntax_ext/test.rs @@ -30,7 +30,7 @@ pub fn expand_test_case( if !ecx.ecfg.should_test { return vec![]; } - let sp = attr_sp.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.mark)); + let sp = attr_sp.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.id)); let mut item = anno_item.expect_item(); item = item.map(|mut item| { item.vis = respan(item.vis.span, ast::VisibilityKind::Public); diff --git a/src/libsyntax_ext/test_harness.rs b/src/libsyntax_ext/test_harness.rs index 061f5c3408b..848c797856e 100644 --- a/src/libsyntax_ext/test_harness.rs +++ b/src/libsyntax_ext/test_harness.rs @@ -37,12 +37,14 @@ struct TestCtxt<'a> { // Traverse the crate, collecting all the test functions, eliding any // existing main functions, and synthesizing a main test harness -pub fn modify_for_testing(sess: &ParseSess, - resolver: &mut dyn Resolver, - should_test: bool, - krate: &mut ast::Crate, - span_diagnostic: &errors::Handler, - features: &Features) { +pub fn inject( + sess: &ParseSess, + resolver: &mut dyn Resolver, + should_test: bool, + krate: &mut ast::Crate, + span_diagnostic: &errors::Handler, + features: &Features, +) { // Check for #[reexport_test_harness_main = "some_name"] which // creates a `use __test::main as some_name;`. This needs to be // unconditional, so that the attribute is still marked as used in -- cgit 1.4.1-3-g733a5 From fc9bfd68b583852758d0a0f8ea67d5d5802f14d2 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sat, 27 Jul 2019 18:24:17 +0700 Subject: Treat doc comments separately --- src/libsyntax/parse/attr.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 810af9b9246..af484c886ab 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -12,7 +12,7 @@ use smallvec::smallvec; #[derive(Debug)] enum InnerAttributeParsePolicy<'a> { Permitted, - NotPermitted { reason: &'a str, prev_attr_sp: Option }, + NotPermitted { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option }, } const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \ @@ -43,8 +43,11 @@ impl<'a> Parser<'a> { DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG }; let inner_parse_policy = - InnerAttributeParsePolicy::NotPermitted { reason: inner_error_reason, - prev_attr_sp: attrs.last().and_then(|a| Some(a.span)) }; + InnerAttributeParsePolicy::NotPermitted { + reason: inner_error_reason, + saw_doc_comment: just_parsed_doc_comment, + prev_attr_sp: attrs.last().and_then(|a| Some(a.span)) + }; let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?; attrs.push(attr); just_parsed_doc_comment = false; @@ -78,8 +81,11 @@ impl<'a> Parser<'a> { let inner_parse_policy = if permit_inner { InnerAttributeParsePolicy::Permitted } else { - InnerAttributeParsePolicy::NotPermitted - { reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG, prev_attr_sp: None } + InnerAttributeParsePolicy::NotPermitted { + reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG, + saw_doc_comment: false, + prev_attr_sp: None + } }; self.parse_attribute_with_inner_parse_policy(inner_parse_policy) } @@ -117,8 +123,14 @@ impl<'a> Parser<'a> { // Emit error if inner attribute is encountered and not permitted if style == ast::AttrStyle::Inner { - if let InnerAttributeParsePolicy::NotPermitted { reason, prev_attr_sp } - = inner_parse_policy { + if let InnerAttributeParsePolicy::NotPermitted { reason, + saw_doc_comment, prev_attr_sp } = inner_parse_policy { + let prev_attr_note = if saw_doc_comment { + "previous doc comment" + } else { + "previous outer attribute" + }; + let mut diagnostic = self .diagnostic() .struct_span_err(attr_sp, reason); @@ -126,7 +138,7 @@ impl<'a> Parser<'a> { if let Some(prev_attr_sp) = prev_attr_sp { diagnostic .span_label(attr_sp, "not permitted following an outer attibute") - .span_label(prev_attr_sp, "previous outer attribute"); + .span_label(prev_attr_sp, prev_attr_note); } diagnostic -- cgit 1.4.1-3-g733a5 From 2787cb23f0f3178f5eae11d571ee5e026b79d7fb Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sat, 27 Jul 2019 18:24:48 +0700 Subject: Fix failing UI tests --- src/test/ui/issues/issue-45296.stderr | 4 ++-- src/test/ui/parser/inner-attr-after-doc-comment.stderr | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/test/ui/issues/issue-45296.stderr b/src/test/ui/issues/issue-45296.stderr index bc14d20b623..c0d4ce1243e 100644 --- a/src/test/ui/issues/issue-45296.stderr +++ b/src/test/ui/issues/issue-45296.stderr @@ -1,8 +1,8 @@ error: an inner attribute is not permitted in this context - --> $DIR/issue-45296.rs:4:7 + --> $DIR/issue-45296.rs:4:5 | LL | #![allow(unused_variables)] - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. diff --git a/src/test/ui/parser/inner-attr-after-doc-comment.stderr b/src/test/ui/parser/inner-attr-after-doc-comment.stderr index 0dde49a2913..b012abc25e7 100644 --- a/src/test/ui/parser/inner-attr-after-doc-comment.stderr +++ b/src/test/ui/parser/inner-attr-after-doc-comment.stderr @@ -1,8 +1,13 @@ error: an inner attribute is not permitted following an outer doc comment - --> $DIR/inner-attr-after-doc-comment.rs:6:3 + --> $DIR/inner-attr-after-doc-comment.rs:6:1 | -LL | #![recursion_limit="100"] - | ^ +LL | / /** +LL | | * My module +LL | | */ + | |___- previous doc comment +LL | +LL | #![recursion_limit="100"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not permitted following an outer attibute | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. -- cgit 1.4.1-3-g733a5 From 693be441f4c2d74805b855612556c54438c3022c Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sat, 27 Jul 2019 19:35:55 +0700 Subject: Fix ui/parser/attr test --- src/test/ui/parser/attr.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/ui/parser/attr.stderr b/src/test/ui/parser/attr.stderr index 5111b40603c..400a0276b3b 100644 --- a/src/test/ui/parser/attr.stderr +++ b/src/test/ui/parser/attr.stderr @@ -1,8 +1,8 @@ error: an inner attribute is not permitted in this context - --> $DIR/attr.rs:5:3 + --> $DIR/attr.rs:5:1 | LL | #![lang = "foo"] - | ^ + | ^^^^^^^^^^^^^^^^ | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. -- cgit 1.4.1-3-g733a5 From c0df742de8af58f06b6d6d4af3244ae470976f53 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 26 Jul 2019 01:18:35 +0300 Subject: tidy: libcoretest.rs -> unit_tests.rs --- src/tools/tidy/src/lib.rs | 2 +- src/tools/tidy/src/libcoretest.rs | 28 ---------------------------- src/tools/tidy/src/main.rs | 2 +- src/tools/tidy/src/unit_tests.rs | 28 ++++++++++++++++++++++++++++ 4 files changed, 30 insertions(+), 30 deletions(-) delete mode 100644 src/tools/tidy/src/libcoretest.rs create mode 100644 src/tools/tidy/src/unit_tests.rs diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 06c1a8c18ba..3d40bdced63 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -38,8 +38,8 @@ pub mod pal; pub mod deps; pub mod extdeps; pub mod ui_tests; +pub mod unit_tests; pub mod unstable_book; -pub mod libcoretest; fn filter_dirs(path: &Path) -> bool { let skip = [ diff --git a/src/tools/tidy/src/libcoretest.rs b/src/tools/tidy/src/libcoretest.rs deleted file mode 100644 index 579a20e1142..00000000000 --- a/src/tools/tidy/src/libcoretest.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Tidy check to ensure `#[test]` is not used directly inside `libcore`. -//! -//! `#![no_core]` libraries cannot be tested directly due to duplicating lang -//! item. All tests must be written externally in `libcore/tests`. - -use std::path::Path; - -pub fn check(path: &Path, bad: &mut bool) { - let libcore_path = path.join("libcore"); - super::walk( - &libcore_path, - &mut |subpath| t!(subpath.strip_prefix(&libcore_path)).starts_with("tests"), - &mut |entry, contents| { - let subpath = entry.path(); - if let Some("rs") = subpath.extension().and_then(|e| e.to_str()) { - let contents = contents.trim(); - if !contents.starts_with("//") && contents.contains("#[test]") { - tidy_error!( - bad, - "`{}` contains `#[test]`; libcore tests must be placed inside \ - `src/libcore/tests/`", - subpath.display() - ); - } - } - }, - ); -} diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 918762ed6e6..19f02f0a96e 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -27,7 +27,7 @@ fn main() { let collected = features::check(&path, &mut bad, verbose); pal::check(&path, &mut bad); unstable_book::check(&path, collected, &mut bad); - libcoretest::check(&path, &mut bad); + unit_tests::check(&path, &mut bad); if !args.iter().any(|s| *s == "--no-vendor") { deps::check(&path, &mut bad); } diff --git a/src/tools/tidy/src/unit_tests.rs b/src/tools/tidy/src/unit_tests.rs new file mode 100644 index 00000000000..579a20e1142 --- /dev/null +++ b/src/tools/tidy/src/unit_tests.rs @@ -0,0 +1,28 @@ +//! Tidy check to ensure `#[test]` is not used directly inside `libcore`. +//! +//! `#![no_core]` libraries cannot be tested directly due to duplicating lang +//! item. All tests must be written externally in `libcore/tests`. + +use std::path::Path; + +pub fn check(path: &Path, bad: &mut bool) { + let libcore_path = path.join("libcore"); + super::walk( + &libcore_path, + &mut |subpath| t!(subpath.strip_prefix(&libcore_path)).starts_with("tests"), + &mut |entry, contents| { + let subpath = entry.path(); + if let Some("rs") = subpath.extension().and_then(|e| e.to_str()) { + let contents = contents.trim(); + if !contents.starts_with("//") && contents.contains("#[test]") { + tidy_error!( + bad, + "`{}` contains `#[test]`; libcore tests must be placed inside \ + `src/libcore/tests/`", + subpath.display() + ); + } + } + }, + ); +} -- cgit 1.4.1-3-g733a5 From 6a4def0c9df85649336a52607347f613efcca918 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 26 Jul 2019 01:22:59 +0300 Subject: tidy: Fix a regression in `#[test]` detection in libcore `contents` is the whole file rather than a single line. --- src/tools/tidy/src/unit_tests.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/tools/tidy/src/unit_tests.rs b/src/tools/tidy/src/unit_tests.rs index 579a20e1142..6c61144f279 100644 --- a/src/tools/tidy/src/unit_tests.rs +++ b/src/tools/tidy/src/unit_tests.rs @@ -13,14 +13,16 @@ pub fn check(path: &Path, bad: &mut bool) { &mut |entry, contents| { let subpath = entry.path(); if let Some("rs") = subpath.extension().and_then(|e| e.to_str()) { - let contents = contents.trim(); - if !contents.starts_with("//") && contents.contains("#[test]") { - tidy_error!( - bad, - "`{}` contains `#[test]`; libcore tests must be placed inside \ - `src/libcore/tests/`", - subpath.display() - ); + for line in contents.lines() { + let line = line.trim(); + if !line.starts_with("//") && line.contains("#[test]") { + tidy_error!( + bad, + "`{}` contains `#[test]`; libcore tests must be placed inside \ + `src/libcore/tests/`", + subpath.display() + ); + } } } }, -- cgit 1.4.1-3-g733a5 From aecaa03cf607d92d5ef5346400dd5302de8e5303 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 26 Jul 2019 03:01:18 +0300 Subject: tidy: Add a check for inline unit tests and benchmarks --- src/tools/tidy/src/unit_tests.rs | 103 +++++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 19 deletions(-) diff --git a/src/tools/tidy/src/unit_tests.rs b/src/tools/tidy/src/unit_tests.rs index 6c61144f279..b789e5cfd48 100644 --- a/src/tools/tidy/src/unit_tests.rs +++ b/src/tools/tidy/src/unit_tests.rs @@ -1,28 +1,93 @@ -//! Tidy check to ensure `#[test]` is not used directly inside `libcore`. +//! Tidy check to ensure `#[test]` and `#[bench]` are not used directly inside +//! `libcore` or `liballoc`. //! -//! `#![no_core]` libraries cannot be tested directly due to duplicating lang -//! item. All tests must be written externally in `libcore/tests`. +//! `#![no_std]` libraries cannot be tested directly due to duplicating lang +//! items. All tests and benchmarks must be written externally in `libcore/{tests,benches}` +//! or `liballoc/{tests,benches}`. +//! +//! Outside of libcore and liballoc tests and benchmarks should be outlined into separate files +//! named `tests.rs` or `benches.rs`, or directories named `tests` or `benches` unconfigured +//! during normal build. use std::path::Path; -pub fn check(path: &Path, bad: &mut bool) { - let libcore_path = path.join("libcore"); +pub fn check(root_path: &Path, bad: &mut bool) { + let libcore = &root_path.join("libcore"); + let liballoc = &root_path.join("liballoc"); + let libcore_tests = &root_path.join("libcore/tests"); + let liballoc_tests = &root_path.join("liballoc/tests"); + let libcore_benches = &root_path.join("libcore/benches"); + let liballoc_benches = &root_path.join("liballoc/benches"); + let is_core_or_alloc = |path: &Path| { + let is_core = path.starts_with(libcore) && + !(path.starts_with(libcore_tests) || path.starts_with(libcore_benches)); + let is_alloc = path.starts_with(liballoc) && + !(path.starts_with(liballoc_tests) || path.starts_with(liballoc_benches)); + is_core || is_alloc + }; + let fixme = [ + "liballoc", + "libpanic_unwind/dwarf", + "librustc", + "librustc_data_structures", + "librustc_incremental/persist", + "librustc_lexer/src", + "librustc_target/spec", + "librustdoc", + "libserialize", + "libstd", + "libsyntax", + "libsyntax_pos", + "libterm/terminfo", + "libtest", + "tools/compiletest/src", + "tools/tidy/src", + ]; + + let mut skip = |path: &Path| { + let file_name = path.file_name().unwrap_or_default(); + if path.is_dir() { + super::filter_dirs(path) || + path.ends_with("src/test") || + path.ends_with("src/doc") || + (file_name == "tests" || file_name == "benches") && !is_core_or_alloc(path) || + fixme.iter().any(|p| path.ends_with(p)) + } else { + let extension = path.extension().unwrap_or_default(); + extension != "rs" || + (file_name == "tests.rs" || file_name == "benches.rs") && !is_core_or_alloc(path) + } + }; + super::walk( - &libcore_path, - &mut |subpath| t!(subpath.strip_prefix(&libcore_path)).starts_with("tests"), + root_path, + &mut skip, &mut |entry, contents| { - let subpath = entry.path(); - if let Some("rs") = subpath.extension().and_then(|e| e.to_str()) { - for line in contents.lines() { - let line = line.trim(); - if !line.starts_with("//") && line.contains("#[test]") { - tidy_error!( - bad, - "`{}` contains `#[test]`; libcore tests must be placed inside \ - `src/libcore/tests/`", - subpath.display() - ); - } + let path = entry.path(); + let is_libcore = path.starts_with(libcore); + let is_liballoc = path.starts_with(liballoc); + for (i, line) in contents.lines().enumerate() { + let line = line.trim(); + let is_test = || line.contains("#[test]") && !line.contains("`#[test]"); + let is_bench = || line.contains("#[bench]") && !line.contains("`#[bench]"); + if !line.starts_with("//") && (is_test() || is_bench()) { + let explanation = if is_libcore { + "libcore unit tests and benchmarks must be placed into \ + `libcore/tests` or `libcore/benches`" + } else if is_liballoc { + "liballoc unit tests and benchmarks must be placed into \ + `liballoc/tests` or `liballoc/benches`" + } else { + "unit tests and benchmarks must be placed into \ + separate files or directories named \ + `tests.rs`, `benches.rs`, `tests` or `benches`" + }; + let name = if is_test() { "test" } else { "bench" }; + tidy_error!( + bad, "`{}:{}` contains `#[{}]`; {}", + path.display(), i + 1, name, explanation, + ); + return; } } }, -- cgit 1.4.1-3-g733a5 From 75dfdcb065231811bc9977da1aaf0e66889cbbce Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Sat, 27 Jul 2019 22:18:40 +0200 Subject: ci: download awscli from our mirror This fixes multiple network issues we had when downloading awscli from PyPI on Azure Pipelines by vendoring awscli itself and its dependencies in our S3 bucket. Instructions on how to update the cache are present at the top of src/ci/install-awscli.sh --- .azure-pipelines/steps/run.yml | 42 +++++------------------------------------- src/ci/awscli-requirements.txt | 13 ------------- src/ci/install-awscli.sh | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 50 deletions(-) delete mode 100644 src/ci/awscli-requirements.txt create mode 100755 src/ci/install-awscli.sh diff --git a/.azure-pipelines/steps/run.yml b/.azure-pipelines/steps/run.yml index 015fd834885..1e49cc00921 100644 --- a/.azure-pipelines/steps/run.yml +++ b/.azure-pipelines/steps/run.yml @@ -138,43 +138,11 @@ steps: # Ensure the `aws` CLI is installed so we can deploy later on, cache docker # images, etc. -- bash: | - set -e - # Temporary code to debug #62967. - debug_failed_connections() { - echo "trying to ping pypi.org" - ping pypi.org -c10 || true - echo "trying to ping google.com" - ping google.com -c10 || true - echo "trying to ping 8.8.8.8" - ping 8.8.8.8 -c10 || true - echo "trying to download pypi.org" - curl https://pypi.org || true - echo "trying to download from our S3 bucket" - curl https://rust-lang-ci2.s3.amazonaws.com || true - echo "trying to dig pypi.org" - dig pypi.org || true - echo "trying to dig files.pythonhosted.org" - dig files.pythonhosted.org || true - echo "trying to connect to pypi.org with openssl" - echo | openssl s_client -connect pypi.org:443 || true - echo "trying to connect to files.pythonhosted.org with openssl" - echo | openssl s_client -connect files.pythonhosted.org:443 || true - } - debug_failed_connections_and_fail() { - debug_failed_connections - return 1 - } - source src/ci/shared.sh - sudo apt-get install -y python3-setuptools - debug_failed_connections - retry pip3 install -r src/ci/awscli-requirements.txt --upgrade --user || debug_failed_connections_and_fail - echo "##vso[task.prependpath]$HOME/.local/bin" - displayName: Install awscli (Linux) - condition: and(succeeded(), not(variables.SKIP_JOB), eq(variables['Agent.OS'], 'Linux')) -- script: pip install -r src/ci/awscli-requirements.txt - displayName: Install awscli (non-Linux) - condition: and(succeeded(), not(variables.SKIP_JOB), ne(variables['Agent.OS'], 'Linux')) +- bash: src/ci/install-awscli.sh + env: + AGENT_OS: $(Agent.OS) + condition: and(succeeded(), not(variables.SKIP_JOB)) + displayName: Install awscli # Configure our CI_JOB_NAME variable which log analyzers can use for the main # step to see what's going on. diff --git a/src/ci/awscli-requirements.txt b/src/ci/awscli-requirements.txt deleted file mode 100644 index c1ffa525a1b..00000000000 --- a/src/ci/awscli-requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -awscli==1.16.201 -botocore==1.12.191 -colorama==0.3.9 -docutils==0.14 -jmespath==0.9.4 -pyasn1==0.4.5 -python-dateutil==2.8.0 -PyYAML==5.1 -rsa==3.4.2 -s3transfer==0.2.1 -six==1.12.0 -urllib3==1.25.3 -futures==3.3.0; python_version < '3.0' diff --git a/src/ci/install-awscli.sh b/src/ci/install-awscli.sh new file mode 100755 index 00000000000..d491b9fbcdc --- /dev/null +++ b/src/ci/install-awscli.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# This script downloads and installs awscli from the packages mirrored in our +# own S3 bucket. This follows the recommendations at: +# +# https://packaging.python.org/guides/index-mirrors-and-caches/#caching-with-pip +# +# To create a new mirrored copy you can run the command: +# +# pip wheel awscli +# +# Before compressing please make sure all the wheels end with `-none-any.whl`. +# If that's not the case you'll need to remove the non-cross-platform ones and +# replace them with the .tar.gz downloaded from https://pypi.org. Also make +# sure it's possible to call this script with both Python 2 and Python 3. + +set -euo pipefail +IFS=$'\n\t' + +MIRROR="https://rust-lang-ci2.s3.amazonaws.com/rust-ci-mirror/2019-07-27-awscli.tar" +DEPS_DIR="/tmp/awscli-deps" + +pip="pip" +pipflags="" +if [[ "${AGENT_OS}" == "Linux" ]]; then + pip="pip3" + pipflags="--user" + + sudo apt-get install -y python3-setuptools + echo "##vso[task.prependpath]$HOME/.local/bin" +fi + +mkdir -p "${DEPS_DIR}" +curl "${MIRROR}" | tar xf - -C "${DEPS_DIR}" +"${pip}" install ${pipflags} --no-index "--find-links=${DEPS_DIR}" awscli +rm -rf "${DEPS_DIR}" -- cgit 1.4.1-3-g733a5