Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to syn-2 (Continuation) #354

Merged
merged 4 commits into from
May 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
fail-fast: false
matrix:
rust:
- '1.37'
- '1.56'
- stable
- beta
- nightly
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
- run: cargo build --manifest-path tests/no-std/Cargo.toml --target thumbv6m-none-eabi
- run: cargo build --manifest-path tests/rust-2015/Cargo.toml --target thumbv6m-none-eabi
- run: cargo minimal-versions build --workspace --all-features --ignore-private
if: matrix.rust != '1.37'
if: matrix.rust != '1.56'

miri:
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "pin-project"
version = "1.0.12"
edition = "2018"
rust-version = "1.37"
rust-version = "1.56"
license = "Apache-2.0 OR MIT"
repository = "https://github.com/taiki-e/pin-project"
keywords = ["pin", "macros", "attribute"]
Expand Down Expand Up @@ -39,7 +39,7 @@ trybuild = "=1.0.67"

# For test on MSRV.
once_cell = "=1.14"
serde = "=1.0.156"
serde = "1.0.159"
toml = "=0.5.9"

[patch.crates-io]
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![crates.io](https://img.shields.io/crates/v/pin-project?style=flat-square&logo=rust)](https://crates.io/crates/pin-project)
[![docs.rs](https://img.shields.io/badge/docs.rs-pin--project-blue?style=flat-square&logo=docs.rs)](https://docs.rs/pin-project)
[![license](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue?style=flat-square)](#license)
[![rustc](https://img.shields.io/badge/rustc-1.37+-blue?style=flat-square&logo=rust)](https://www.rust-lang.org)
[![rustc](https://img.shields.io/badge/rustc-1.56+-blue?style=flat-square&logo=rust)](https://www.rust-lang.org)
[![build status](https://img.shields.io/github/actions/workflow/status/taiki-e/pin-project/ci.yml?branch=main&style=flat-square&logo=github)](https://github.com/taiki-e/pin-project/actions)

<!-- tidy:crate-doc:start -->
Expand All @@ -18,7 +18,7 @@ Add this to your `Cargo.toml`:
pin-project = "1"
```

*Compiler support: requires rustc 1.37+*
*Compiler support: requires rustc 1.56+*

## Examples

Expand Down
4 changes: 2 additions & 2 deletions pin-project-internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "pin-project-internal"
version = "1.0.12"
edition = "2018"
rust-version = "1.37"
rust-version = "1.56"
license = "Apache-2.0 OR MIT"
repository = "https://github.com/taiki-e/pin-project"
keywords = ["pin", "macros", "attribute"]
Expand All @@ -20,7 +20,7 @@ proc-macro = true
[dependencies]
proc-macro2 = "1"
quote = "1"
syn = { version = "1.0.56", features = ["full", "visit-mut"] }
syn = { version = "2.0.1", features = ["full", "visit-mut"] }

[dev-dependencies]
pin-project = { path = ".." }
11 changes: 5 additions & 6 deletions pin-project-internal/src/pin_project/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> {
impl Parse for Input {
fn parse(input: ParseStream<'_>) -> Result<Self> {
Ok(Self((|| {
let content = input.parenthesized().ok()?;
let private = content.parse::<Ident>().ok()?;
let private = input.parse::<Ident>().ok()?;
if private == "__private" {
content.parenthesized().ok()?.parse::<TokenStream>().ok()
input.parenthesized().ok()?.parse::<TokenStream>().ok()
} else {
None
}
Expand All @@ -31,10 +30,10 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> {
bail!(attr, "duplicate #[pin_project] attribute");
}

let mut attrs = attrs.iter().filter(|attr| attr.path.is_ident(PIN));
let mut attrs = attrs.iter().filter(|attr| attr.path().is_ident(PIN));

let prev = if let Some(attr) = attrs.next() {
(attr, syn::parse2::<Input>(attr.tokens.clone()).unwrap().0)
(attr, syn::parse2::<Input>(attr.meta.require_list()?.tokens.clone())?.0)
} else {
// This only fails if another macro removes `#[pin]`.
bail!(TokenStream::new(), "#[pin_project] attribute has been removed");
Expand All @@ -46,7 +45,7 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> {
// has the same span as `#[pin_project]`, it is possible
// that a useless error message will be generated.
// So, use the span of `prev_attr` if it is not a valid attribute.
let res = syn::parse2::<Input>(attr.tokens.clone()).unwrap().0;
let res = syn::parse2::<Input>(attr.meta.require_list()?.tokens.clone())?.0;
let span = match (prev_res, res) {
(Some(_), _) => attr,
(None, _) => prev_attr,
Expand Down
59 changes: 27 additions & 32 deletions pin-project-internal/src/pin_project/derive.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use proc_macro2::{Delimiter, Group, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::{
parse_quote, token, visit_mut::VisitMut, Attribute, Data, DataEnum, DeriveInput, Error, Field,
Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Index, Lifetime, LifetimeDef, Meta,
MetaList, MetaNameValue, NestedMeta, Result, Token, Type, Variant, Visibility, WhereClause,
parse_quote, punctuated::Punctuated, token, visit_mut::VisitMut, Attribute, Data, DataEnum,
DeriveInput, Error, Field, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Index,
Lifetime, LifetimeParam, Meta, Result, Token, Type, Variant, Visibility, WhereClause,
};

use super::{
Expand Down Expand Up @@ -305,7 +305,7 @@ fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> {
fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> {
if variants.is_empty() {
return Err(Error::new(
brace_token.span,
brace_token.span.join(),
"#[pin_project] attribute may not be used on enums without variants",
));
}
Expand Down Expand Up @@ -569,7 +569,9 @@ fn visit_fields<'a>(
let mut proj_move = TokenStream::new();
let mut pinned_bindings = Vec::with_capacity(fields.len());

for (i, Field { attrs, vis, ident, colon_token, ty }) in fields.iter().enumerate() {
for (i, Field { attrs, vis, ident, colon_token, ty, mutability: _ }) in
fields.iter().enumerate()
{
let binding = ident.clone().unwrap_or_else(|| format_ident!("_{}", i));
proj_pat.extend(quote!(#binding,));
let lifetime = &cx.proj.lifetime;
Expand Down Expand Up @@ -768,7 +770,7 @@ fn make_unpin_impl(cx: &Context<'_>) -> TokenStream {
// This ensures that any unused type parameters
// don't end up with `Unpin` bounds.
let lifetime_fields = cx.orig.generics.lifetimes().enumerate().map(
|(i, LifetimeDef { lifetime, .. })| {
|(i, LifetimeParam { lifetime, .. })| {
let field_ident = format_ident!("__lifetime{}", i);
quote!(#field_ident: &#lifetime ())
},
Expand Down Expand Up @@ -1016,33 +1018,26 @@ fn make_proj_impl(
/// - Generates a function that borrows fields without an unsafe block and
/// forbidding `unaligned_references` lint.
fn ensure_not_packed(orig: &OriginalType<'_>, fields: Option<&Fields>) -> Result<TokenStream> {
for meta in orig.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
if let Meta::List(list) = meta {
for attr in orig.attrs {
if let Meta::List(ref list) = attr.meta {
if list.path.is_ident("repr") {
for repr in list.nested.iter() {
match repr {
NestedMeta::Meta(Meta::Path(path))
| NestedMeta::Meta(Meta::List(MetaList { path, .. }))
| NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, .. })) => {
if path.is_ident("packed") {
let msg = if fields.is_none() {
// #[repr(packed)] cannot be apply on enums and will be rejected by rustc.
// However, we should not rely on the behavior of rustc that rejects this.
// https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001
"#[repr(packed)] attribute should be applied to a struct or union"
} else if let NestedMeta::Meta(Meta::NameValue(..)) = repr {
// #[repr(packed = "")] is not valid format of #[repr(packed)] and will be
// rejected by rustc.
// However, we should not rely on the behavior of rustc that rejects this.
// https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001
"#[repr(packed)] attribute should not be name-value pair"
} else {
"#[pin_project] attribute may not be used on #[repr(packed)] types"
};
bail!(repr, msg);
}
}
NestedMeta::Lit(..) => {}
for repr in list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)? {
if repr.path().is_ident("packed") {
let msg = if fields.is_none() {
// #[repr(packed)] cannot be apply on enums and will be rejected by rustc.
// However, we should not rely on the behavior of rustc that rejects this.
// https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001
"#[repr(packed)] attribute should be applied to a struct or union"
} else if repr.require_name_value().is_ok() {
// #[repr(packed = "")] is not valid format of #[repr(packed)] and will be
// rejected by rustc.
// However, we should not rely on the behavior of rustc that rejects this.
// https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001
"#[repr(packed)] attribute should not be name-value pair"
} else {
"#[pin_project] attribute may not be used on #[repr(packed)] types"
};
bail!(repr, msg);
}
}
}
Expand Down
59 changes: 34 additions & 25 deletions pin-project-internal/src/pinned_drop.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::{
parse_quote, spanned::Spanned, visit_mut::VisitMut, Error, FnArg, GenericArgument, ImplItem,
ItemImpl, Pat, PatIdent, Path, PathArguments, Result, ReturnType, Signature, Token, Type,
TypePath, TypeReference,
parse_quote, spanned::Spanned, token::Colon, visit_mut::VisitMut, Error, FnArg,
GenericArgument, Ident, ImplItem, ItemImpl, Pat, PatIdent, PatType, Path, PathArguments,
Result, ReturnType, Signature, Token, Type, TypePath, TypeReference,
};

use crate::utils::{parse_as_empty, prepend_underscore_to_self, ReplaceReceiver, SliceExt};
use crate::utils::{ReplaceReceiver, SliceExt};

pub(crate) fn attribute(args: &TokenStream, mut input: ItemImpl) -> TokenStream {
let res = (|| -> Result<()> {
parse_as_empty(args)?;
if !args.is_empty() {
bail!(args, "unexpected argument: `{}`", args)
}
validate_impl(&input)?;
expand_impl(&mut input);
Ok(())
Expand Down Expand Up @@ -85,7 +87,7 @@ fn validate_impl(item: &ItemImpl) -> Result<()> {
ImplItem::Type(item) => {
bail!(item, "type `{}` is not a member of trait `PinnedDrop`", item.ident)
}
ImplItem::Method(method) => {
ImplItem::Fn(method) => {
validate_sig(&method.sig)?;
if i == 0 {
Ok(())
Expand Down Expand Up @@ -124,14 +126,15 @@ fn validate_sig(sig: &Signature) -> Result<()> {

match sig.inputs.len() {
1 => {}
0 => return Err(Error::new(sig.paren_token.span, INVALID_ARGUMENT)),
0 => return Err(Error::new(sig.paren_token.span.join(), INVALID_ARGUMENT)),
_ => bail!(sig.inputs, INVALID_ARGUMENT),
}

if let Some(FnArg::Typed(arg)) = sig.receiver() {
if let Some(arg) = sig.receiver() {
// (mut) self: <path>
if let Some(path) = get_ty_path(&arg.ty) {
let ty = path.segments.last().unwrap();
let ty =
path.segments.last().expect("Type paths should always have at least one segment");
if let PathArguments::AngleBracketed(args) = &ty.arguments {
// (mut) self: (<path>::)<ty><&mut <elem>..>
if let Some(GenericArgument::Type(Type::Reference(TypeReference {
Expand Down Expand Up @@ -175,25 +178,16 @@ fn validate_sig(sig: &Signature) -> Result<()> {
// }
//
fn expand_impl(item: &mut ItemImpl) {
fn get_arg_pat(arg: &mut FnArg) -> Option<&mut PatIdent> {
if let FnArg::Typed(arg) = arg {
if let Pat::Ident(ident) = &mut *arg.pat {
return Some(ident);
}
}
None
}

// `PinnedDrop` is a private trait and should not appear in docs.
item.attrs.push(parse_quote!(#[doc(hidden)]));

let path = &mut item.trait_.as_mut().unwrap().1;
let path = &mut item.trait_.as_mut().expect("unexpected inherent impl").1;
*path = parse_quote_spanned! { path.span() =>
::pin_project::__private::PinnedDrop
};

let method =
if let ImplItem::Method(method) = &mut item.items[0] { method } else { unreachable!() };
if let ImplItem::Fn(method) = &mut item.items[0] { method } else { unreachable!() };

// `fn drop(mut self: Pin<&mut Self>)` -> `fn __drop_inner<T>(mut __self: Pin<&mut Receiver>)`
let drop_inner = {
Expand All @@ -203,8 +197,20 @@ fn expand_impl(item: &mut ItemImpl) {
drop_inner.block.stmts.insert(0, parse_quote!(fn #ident() {}));
drop_inner.sig.ident = ident;
drop_inner.sig.generics = item.generics.clone();
let self_pat = get_arg_pat(&mut drop_inner.sig.inputs[0]).unwrap();
prepend_underscore_to_self(&mut self_pat.ident);
let receiver = drop_inner.sig.receiver().expect("drop() should have a receiver").clone();
let pat = Box::new(Pat::Ident(PatIdent {
attrs: Vec::new(),
by_ref: None,
mutability: receiver.mutability,
ident: Ident::new("__self", receiver.self_token.span()),
subpat: None,
}));
drop_inner.sig.inputs[0] = FnArg::Typed(PatType {
attrs: receiver.attrs,
pat,
colon_token: Colon::default(),
ty: receiver.ty,
});
let self_ty = if let Type::Path(ty) = &*item.self_ty { ty } else { unreachable!() };
let mut visitor = ReplaceReceiver(self_ty);
visitor.visit_signature_mut(&mut drop_inner.sig);
Expand All @@ -214,9 +220,12 @@ fn expand_impl(item: &mut ItemImpl) {

// `fn drop(mut self: Pin<&mut Self>)` -> `unsafe fn drop(self: Pin<&mut Self>)`
method.sig.unsafety = Some(<Token![unsafe]>::default());
let self_pat = get_arg_pat(&mut method.sig.inputs[0]).unwrap();
self_pat.mutability = None;
let self_token = &self_pat.ident;
let self_token = if let FnArg::Receiver(ref mut rec) = method.sig.inputs[0] {
rec.mutability = None;
&rec.self_token
} else {
panic!("drop() should have a receiver")
};

method.block.stmts = parse_quote! {
#[allow(clippy::needless_pass_by_value)] // This lint does not warn the receiver.
Expand Down
Loading