diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d17e0c2..3e1bbba1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ name: CI on: push: pull_request: + workflow_dispatch: schedule: [cron: "40 1 * * *"] permissions: @@ -51,9 +52,11 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly + - name: Enable type layout randomization + run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV - run: cargo test - run: cargo test --no-default-features - - run: cargo test --no-default-features -- --ignored # run the ignored test to make sure the `proc-macro` feature is disabled + - run: cargo test --no-default-features --test features -- --ignored make_sure_no_proc_macro # run the ignored test to make sure the `proc-macro` feature is disabled - run: cargo test --features span-locations - run: cargo test --manifest-path tests/ui/Cargo.toml - name: RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo test diff --git a/Cargo.toml b/Cargo.toml index d653e6f1..0ceed566 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proc-macro2" -version = "1.0.51" # remember to update html_root_url +version = "1.0.52" # remember to update html_root_url authors = ["David Tolnay ", "Alex Crichton "] autobenches = false categories = ["development-tools::procedural-macro-helpers"] @@ -25,6 +25,7 @@ unicode-ident = "1.0" [dev-dependencies] quote = { version = "1.0", default_features = false } +rustversion = "1" [features] proc-macro = [] diff --git a/src/extra.rs b/src/extra.rs new file mode 100644 index 00000000..cbce1626 --- /dev/null +++ b/src/extra.rs @@ -0,0 +1,100 @@ +//! Items which do not have a correspondence to any API in the proc_macro crate, +//! but are necessary to include in proc-macro2. + +use crate::fallback; +use crate::imp; +use crate::marker::Marker; +use crate::Span; +use core::fmt::{self, Debug}; + +/// An object that holds a [`Group`]'s `span_open()` and `span_close()` together +/// (in a more compact representation than holding those 2 spans individually. +/// +/// [`Group`]: crate::Group +#[derive(Copy, Clone)] +pub struct DelimSpan { + inner: DelimSpanEnum, + _marker: Marker, +} + +#[derive(Copy, Clone)] +enum DelimSpanEnum { + #[cfg(wrap_proc_macro)] + Compiler { + join: proc_macro::Span, + #[cfg(not(no_group_open_close))] + open: proc_macro::Span, + #[cfg(not(no_group_open_close))] + close: proc_macro::Span, + }, + Fallback(fallback::Span), +} + +impl DelimSpan { + pub(crate) fn new(group: &imp::Group) -> Self { + #[cfg(wrap_proc_macro)] + let inner = match group { + imp::Group::Compiler(group) => DelimSpanEnum::Compiler { + join: group.span(), + #[cfg(not(no_group_open_close))] + open: group.span_open(), + #[cfg(not(no_group_open_close))] + close: group.span_close(), + }, + imp::Group::Fallback(group) => DelimSpanEnum::Fallback(group.span()), + }; + + #[cfg(not(wrap_proc_macro))] + let inner = DelimSpanEnum::Fallback(group.span()); + + DelimSpan { + inner, + _marker: Marker, + } + } + + /// Returns a span covering the entire delimited group. + pub fn join(&self) -> Span { + match &self.inner { + #[cfg(wrap_proc_macro)] + DelimSpanEnum::Compiler { join, .. } => Span::_new(imp::Span::Compiler(*join)), + DelimSpanEnum::Fallback(span) => Span::_new_fallback(*span), + } + } + + /// Returns a span for the opening punctuation of the group only. + pub fn open(&self) -> Span { + match &self.inner { + #[cfg(wrap_proc_macro)] + DelimSpanEnum::Compiler { + #[cfg(not(no_group_open_close))] + open, + #[cfg(no_group_open_close)] + join: open, + .. + } => Span::_new(imp::Span::Compiler(*open)), + DelimSpanEnum::Fallback(span) => Span::_new_fallback(span.first_byte()), + } + } + + /// Returns a span for the closing punctuation of the group only. + pub fn close(&self) -> Span { + match &self.inner { + #[cfg(wrap_proc_macro)] + DelimSpanEnum::Compiler { + #[cfg(not(no_group_open_close))] + close, + #[cfg(no_group_open_close)] + join: close, + .. + } => Span::_new(imp::Span::Compiler(*close)), + DelimSpanEnum::Fallback(span) => Span::_new_fallback(span.last_byte()), + } + } +} + +impl Debug for DelimSpan { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(&self.join(), f) + } +} diff --git a/src/fallback.rs b/src/fallback.rs index 587395d6..fc3cd515 100644 --- a/src/fallback.rs +++ b/src/fallback.rs @@ -94,7 +94,7 @@ fn push_token_from_proc_macro(mut vec: RcVecMut, token: TokenTree) { if literal.repr.starts_with('-') { push_negative_literal(vec, literal); } else { - vec.push(TokenTree::Literal(crate::Literal::_new_stable(literal))); + vec.push(TokenTree::Literal(crate::Literal::_new_fallback(literal))); } } _ => vec.push(token), @@ -104,9 +104,9 @@ fn push_token_from_proc_macro(mut vec: RcVecMut, token: TokenTree) { fn push_negative_literal(mut vec: RcVecMut, mut literal: Literal) { literal.repr.remove(0); let mut punct = crate::Punct::new('-', Spacing::Alone); - punct.set_span(crate::Span::_new_stable(literal.span)); + punct.set_span(crate::Span::_new_fallback(literal.span)); vec.push(TokenTree::Punct(punct)); - vec.push(TokenTree::Literal(crate::Literal::_new_stable(literal))); + vec.push(TokenTree::Literal(crate::Literal::_new_fallback(literal))); } } @@ -555,12 +555,12 @@ impl Span { } #[cfg(not(span_locations))] - fn first_byte(self) -> Self { + pub(crate) fn first_byte(self) -> Self { self } #[cfg(span_locations)] - fn first_byte(self) -> Self { + pub(crate) fn first_byte(self) -> Self { Span { lo: self.lo, hi: cmp::min(self.lo.saturating_add(1), self.hi), @@ -568,12 +568,12 @@ impl Span { } #[cfg(not(span_locations))] - fn last_byte(self) -> Self { + pub(crate) fn last_byte(self) -> Self { self } #[cfg(span_locations)] - fn last_byte(self) -> Self { + pub(crate) fn last_byte(self) -> Self { Span { lo: cmp::max(self.hi.saturating_sub(1), self.lo), hi: self.hi, diff --git a/src/lib.rs b/src/lib.rs index 261c167f..4eb756bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,7 +86,7 @@ //! a different thread. // Proc-macro2 types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/proc-macro2/1.0.51")] +#![doc(html_root_url = "https://docs.rs/proc-macro2/1.0.52")] #![cfg_attr( any(proc_macro_span, super_unstable), feature(proc_macro_span, proc_macro_span_shrink) @@ -98,6 +98,7 @@ clippy::cast_possible_truncation, clippy::doc_markdown, clippy::items_after_statements, + clippy::let_underscore_untyped, clippy::manual_assert, clippy::must_use_candidate, clippy::needless_doctest_main, @@ -133,6 +134,8 @@ mod detection; #[doc(hidden)] pub mod fallback; +pub mod extra; + #[cfg(not(wrap_proc_macro))] use crate::fallback as imp; #[path = "wrapper.rs"] @@ -142,6 +145,7 @@ mod imp; #[cfg(span_locations)] mod location; +use crate::extra::DelimSpan; use crate::marker::Marker; use core::cmp::Ordering; use core::fmt::{self, Debug, Display}; @@ -183,7 +187,7 @@ impl TokenStream { } } - fn _new_stable(inner: fallback::TokenStream) -> Self { + fn _new_fallback(inner: fallback::TokenStream) -> Self { TokenStream { inner: inner.into(), _marker: Marker, @@ -377,7 +381,7 @@ impl Span { } } - fn _new_stable(inner: fallback::Span) -> Self { + fn _new_fallback(inner: fallback::Span) -> Self { Span { inner: inner.into(), _marker: Marker, @@ -664,7 +668,7 @@ impl Group { Group { inner } } - fn _new_stable(inner: fallback::Group) -> Self { + fn _new_fallback(inner: fallback::Group) -> Self { Group { inner: inner.into(), } @@ -681,7 +685,8 @@ impl Group { } } - /// Returns the delimiter of this `Group` + /// Returns the punctuation used as the delimiter for this group: a set of + /// parentheses, square brackets, or curly braces. pub fn delimiter(&self) -> Delimiter { self.inner.delimiter() } @@ -725,6 +730,13 @@ impl Group { Span::_new(self.inner.span_close()) } + /// Returns an object that holds this group's `span_open()` and + /// `span_close()` together (in a more compact representation than holding + /// those 2 spans individually). + pub fn delim_span(&self) -> DelimSpan { + DelimSpan::new(&self.inner) + } + /// Configures the span for this `Group`'s delimiters, but not its internal /// tokens. /// @@ -1081,7 +1093,7 @@ impl Literal { } } - fn _new_stable(inner: fallback::Literal) -> Self { + fn _new_fallback(inner: fallback::Literal) -> Self { Literal { inner: inner.into(), _marker: Marker, diff --git a/src/parse.rs b/src/parse.rs index 2a87948a..82291da1 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -217,13 +217,13 @@ pub(crate) fn token_stream(mut input: Cursor) -> Result { hi: input.off, }); trees = outer; - trees.push_token_from_parser(TokenTree::Group(crate::Group::_new_stable(g))); + trees.push_token_from_parser(TokenTree::Group(crate::Group::_new_fallback(g))); } else { let (rest, mut tt) = match leaf_token(input) { Ok((rest, tt)) => (rest, tt), Err(Reject) => return Err(lex_error(input)), }; - tt.set_span(crate::Span::_new_stable(Span { + tt.set_span(crate::Span::_new_fallback(Span { #[cfg(span_locations)] lo, #[cfg(span_locations)] @@ -251,7 +251,7 @@ fn lex_error(cursor: Cursor) -> LexError { fn leaf_token(input: Cursor) -> PResult { if let Ok((input, l)) = literal(input) { // must be parsed before ident - Ok((input, TokenTree::Literal(crate::Literal::_new_stable(l)))) + Ok((input, TokenTree::Literal(crate::Literal::_new_fallback(l)))) } else if let Ok((input, p)) = punct(input) { Ok((input, TokenTree::Punct(p))) } else if let Ok((input, i)) = ident(input) { @@ -795,7 +795,7 @@ fn doc_comment<'a>(input: Cursor<'a>, trees: &mut TokenStreamBuilder) -> PResult #[cfg(span_locations)] let lo = input.off; let (rest, (comment, inner)) = doc_comment_contents(input)?; - let span = crate::Span::_new_stable(Span { + let span = crate::Span::_new_fallback(Span { #[cfg(span_locations)] lo, #[cfg(span_locations)] @@ -831,7 +831,7 @@ fn doc_comment<'a>(input: Cursor<'a>, trees: &mut TokenStreamBuilder) -> PResult bracketed.push_token_from_parser(TokenTree::Punct(equal)); bracketed.push_token_from_parser(TokenTree::Literal(literal)); let group = Group::new(Delimiter::Bracket, bracketed.build()); - let mut group = crate::Group::_new_stable(group); + let mut group = crate::Group::_new_fallback(group); group.set_span(span); trees.push_token_from_parser(TokenTree::Group(group)); diff --git a/src/wrapper.rs b/src/wrapper.rs index f5ec06b6..720dcdd3 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -40,7 +40,7 @@ impl LexError { } fn mismatch() -> ! { - panic!("stable/nightly mismatch") + panic!("compiler/fallback mismatch") } impl DeferredTokenStream { diff --git a/tests/marker.rs b/tests/marker.rs index 4fb2beb8..5b45733b 100644 --- a/tests/marker.rs +++ b/tests/marker.rs @@ -1,3 +1,5 @@ +#![allow(clippy::extra_unused_type_parameters)] + use proc_macro2::{ Delimiter, Group, Ident, LexError, Literal, Punct, Spacing, Span, TokenStream, TokenTree, }; diff --git a/tests/test_size.rs b/tests/test_size.rs new file mode 100644 index 00000000..46e58db4 --- /dev/null +++ b/tests/test_size.rs @@ -0,0 +1,42 @@ +extern crate proc_macro; + +use std::mem; + +#[rustversion::attr(before(1.32), ignore)] +#[test] +fn test_proc_macro_span_size() { + assert_eq!(mem::size_of::(), 4); + assert_eq!(mem::size_of::>(), 4); +} + +#[cfg_attr(not(all(not(wrap_proc_macro), not(span_locations))), ignore)] +#[test] +fn test_proc_macro2_fallback_span_size_without_locations() { + assert_eq!(mem::size_of::(), 0); + assert_eq!(mem::size_of::>(), 1); +} + +#[cfg_attr(not(all(not(wrap_proc_macro), span_locations)), ignore)] +#[test] +fn test_proc_macro2_fallback_span_size_with_locations() { + assert_eq!(mem::size_of::(), 8); + assert_eq!(mem::size_of::>(), 12); +} + +#[rustversion::attr(before(1.32), ignore)] +#[rustversion::attr( + since(1.32), + cfg_attr(not(all(wrap_proc_macro, not(span_locations))), ignore) +)] +#[test] +fn test_proc_macro2_wrapper_span_size_without_locations() { + assert_eq!(mem::size_of::(), 4); + assert_eq!(mem::size_of::>(), 8); +} + +#[cfg_attr(not(all(wrap_proc_macro, span_locations)), ignore)] +#[test] +fn test_proc_macro2_wrapper_span_size_with_locations() { + assert_eq!(mem::size_of::(), 12); + assert_eq!(mem::size_of::>(), 12); +}