Skip to content

Commit

Permalink
Do not expose unnamed projected types
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e committed Sep 7, 2020
1 parent b15c50b commit fe6898c
Show file tree
Hide file tree
Showing 19 changed files with 518 additions and 520 deletions.
142 changes: 82 additions & 60 deletions pin-project-internal/src/pin_project/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,62 +24,30 @@ pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> {
visitor.visit_data_mut(&mut input.data);

let mut cx = Context::new(&input.attrs, &input.vis, &input.ident, &mut input.generics)?;
let mut generate = GenerateTokens::default();
let packed_check;

let (mut items, scoped_items) = match &input.data {
match &input.data {
Data::Struct(data) => {
// Do this first for a better error message.
packed_check = Some(cx.ensure_not_packed(&data.fields)?);
cx.parse_struct(data)?
cx.parse_struct(data, &mut generate)?;
}
Data::Enum(data) => {
// We don't need to check for `#[repr(packed)]`,
// since it does not apply to enums.
packed_check = None;
cx.parse_enum(data)?
cx.parse_enum(data, &mut generate)?;
}
Data::Union(_) => {
return Err(error!(
input,
"#[pin_project] attribute may only be used on structs or enums"
));
}
};
}

let unpin_impl = cx.make_unpin_impl();
let drop_impl = cx.make_drop_impl();
let dummy_const = if cfg!(underscore_consts) {
format_ident!("_")
} else {
format_ident!("__SCOPE_{}", ident)
};
items.extend(quote! {
// All items except projected types are generated inside a `const` scope.
// This makes it impossible for user code to refer to these types.
// However, this prevents Rustdoc from displaying docs for any
// of our types. In particular, users cannot see the
// automatically generated `Unpin` impl for the '__UnpinStruct' types
//
// Previously, we provided a flag to correctly document the
// automatically generated `Unpin` impl by using def-site hygiene,
// but it is now removed.
//
// Refs:
// * https://github.com/rust-lang/rust/issues/63281
// * https://github.com/taiki-e/pin-project/pull/53#issuecomment-525906867
// * https://github.com/taiki-e/pin-project/pull/70
#[doc(hidden)]
#[allow(non_upper_case_globals)]
#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058
#[allow(clippy::used_underscore_binding)]
const #dummy_const: () = {
#scoped_items
#unpin_impl
#drop_impl
#packed_check
};
});
Ok(items)
Ok(generate.into_tokens(&cx, packed_check))
}

fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> {
Expand Down Expand Up @@ -116,6 +84,64 @@ fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> {
}
}

#[derive(Default)]
struct GenerateTokens {
exposed: TokenStream,
scoped: TokenStream,
}

impl GenerateTokens {
fn extend(&mut self, expose: bool, tokens: TokenStream) {
if expose {
self.exposed.extend(tokens);
} else {
self.scoped.extend(tokens);
}
}

fn into_tokens(self, cx: &Context<'_>, packed_check: Option<TokenStream>) -> TokenStream {
let mut tokens = self.exposed;
let scoped = self.scoped;

let unpin_impl = cx.make_unpin_impl();
let drop_impl = cx.make_drop_impl();

let dummy_const = if cfg!(underscore_consts) {
format_ident!("_")
} else {
format_ident!("__SCOPE_{}", cx.orig.ident)
};

tokens.extend(quote! {
// All items except projected types are generated inside a `const` scope.
// This makes it impossible for user code to refer to these types.
// However, this prevents Rustdoc from displaying docs for any
// of our types. In particular, users cannot see the
// automatically generated `Unpin` impl for the '__UnpinStruct' types
//
// Previously, we provided a flag to correctly document the
// automatically generated `Unpin` impl by using def-site hygiene,
// but it is now removed.
//
// Refs:
// * https://github.com/rust-lang/rust/issues/63281
// * https://github.com/taiki-e/pin-project/pull/53#issuecomment-525906867
// * https://github.com/taiki-e/pin-project/pull/70
#[doc(hidden)]
#[allow(non_upper_case_globals)]
#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058
#[allow(clippy::used_underscore_binding)]
const #dummy_const: () = {
#scoped
#unpin_impl
#drop_impl
#packed_check
};
});
tokens
}
}

struct Args {
/// `PinnedDrop` argument.
pinned_drop: Option<Span>,
Expand Down Expand Up @@ -483,28 +509,18 @@ impl<'a> Context<'a> {

/// Returns attributes used on projected types.
fn proj_attrs(&self) -> (TokenStream, TokenStream, TokenStream) {
// If the user gave it a name, it should appear in the document.
let doc_attr = quote!(#[doc(hidden)]);
let doc_proj = if self.project { None } else { Some(&doc_attr) };
let doc_proj_ref = if self.project_ref { None } else { Some(&doc_attr) };
let doc_proj_own =
if self.project_replace.ident().is_some() { None } else { Some(&doc_attr) };

let proj_mut = quote! {
#doc_proj
#[allow(dead_code)] // This lint warns unused fields/variants.
#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058
#[allow(clippy::mut_mut)] // This lint warns `&mut &mut <ty>`.
#[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326}
};
let proj_ref = quote! {
#doc_proj_ref
#[allow(dead_code)] // This lint warns unused fields/variants.
#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058
#[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326
};
let proj_own = quote! {
#doc_proj_own
#[allow(dead_code)] // This lint warns unused fields/variants.
#[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058
#[allow(unreachable_pub)] // This lint warns `pub` field in private struct.
Expand All @@ -515,7 +531,8 @@ impl<'a> Context<'a> {
fn parse_struct(
&mut self,
DataStruct { fields, .. }: &DataStruct,
) -> Result<(TokenStream, TokenStream)> {
generate: &mut GenerateTokens,
) -> Result<()> {
validate_struct(self.orig.ident, fields)?;

let ProjectedFields {
Expand Down Expand Up @@ -557,14 +574,16 @@ impl<'a> Context<'a> {
};

let (proj_attrs, proj_ref_attrs, proj_own_attrs) = self.proj_attrs();
let mut proj_items = quote! {
generate.extend(self.project, quote! {
#proj_attrs
#vis struct #proj_ident #proj_generics #where_clause_fields
});
generate.extend(self.project_ref, quote! {
#proj_ref_attrs
#vis struct #proj_ref_ident #proj_generics #where_clause_ref_fields
};
});
if self.project_replace.span().is_some() {
proj_items.extend(quote! {
generate.extend(self.project_replace.ident().is_some(), quote! {
#proj_own_attrs
#vis struct #proj_own_ident #orig_generics #where_clause_own_fields
});
Expand All @@ -583,15 +602,16 @@ impl<'a> Context<'a> {
let Self #proj_pat = &mut *__self_ptr;
#proj_own_body
};
let proj_impl = self.make_proj_impl(&proj_mut_body, &proj_ref_body, &proj_own_body);
generate.extend(false, self.make_proj_impl(&proj_mut_body, &proj_ref_body, &proj_own_body));

Ok((proj_items, proj_impl))
Ok(())
}

fn parse_enum(
&mut self,
DataEnum { brace_token, variants, .. }: &DataEnum,
) -> Result<(TokenStream, TokenStream)> {
generate: &mut GenerateTokens,
) -> Result<()> {
validate_enum(*brace_token, variants)?;

let ProjectedVariants {
Expand All @@ -613,18 +633,20 @@ impl<'a> Context<'a> {
let proj_where_clause = &self.proj.where_clause;

let (proj_attrs, proj_ref_attrs, proj_own_attrs) = self.proj_attrs();
let mut proj_items = quote! {
generate.extend(self.project, quote! {
#proj_attrs
#vis enum #proj_ident #proj_generics #proj_where_clause {
#proj_variants
}
});
generate.extend(self.project_ref, quote! {
#proj_ref_attrs
#vis enum #proj_ref_ident #proj_generics #proj_where_clause {
#proj_ref_variants
}
};
});
if self.project_replace.span().is_some() {
proj_items.extend(quote! {
generate.extend(self.project_replace.ident().is_some(), quote! {
#proj_own_attrs
#vis enum #proj_own_ident #orig_generics #orig_where_clause {
#proj_own_variants
Expand All @@ -648,9 +670,9 @@ impl<'a> Context<'a> {
#proj_own_arms
}
};
let proj_impl = self.make_proj_impl(&proj_mut_body, &proj_ref_body, &proj_own_body);
generate.extend(false, self.make_proj_impl(&proj_mut_body, &proj_ref_body, &proj_own_body));

Ok((proj_items, proj_impl))
Ok(())
}

fn visit_variants(&mut self, variants: &Variants) -> Result<ProjectedVariants> {
Expand Down
60 changes: 29 additions & 31 deletions tests/expand/tests/expand/default-enum.expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,39 @@ enum Enum<T, U> {
Unit,
}
#[doc(hidden)]
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::mut_mut)]
#[allow(clippy::type_repetition_in_bounds)]
enum __EnumProjection<'pin, T, U>
where
Enum<T, U>: 'pin,
{
Struct {
pinned: ::pin_project::__private::Pin<&'pin mut (T)>,
unpinned: &'pin mut (U),
},
Tuple(::pin_project::__private::Pin<&'pin mut (T)>, &'pin mut (U)),
Unit,
}
#[doc(hidden)]
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::type_repetition_in_bounds)]
enum __EnumProjectionRef<'pin, T, U>
where
Enum<T, U>: 'pin,
{
Struct {
pinned: ::pin_project::__private::Pin<&'pin (T)>,
unpinned: &'pin (U),
},
Tuple(::pin_project::__private::Pin<&'pin (T)>, &'pin (U)),
Unit,
}
#[doc(hidden)]
#[allow(non_upper_case_globals)]
#[allow(single_use_lifetimes)]
#[allow(clippy::used_underscore_binding)]
const _: () = {
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::mut_mut)]
#[allow(clippy::type_repetition_in_bounds)]
enum __EnumProjection<'pin, T, U>
where
Enum<T, U>: 'pin,
{
Struct {
pinned: ::pin_project::__private::Pin<&'pin mut (T)>,
unpinned: &'pin mut (U),
},
Tuple(::pin_project::__private::Pin<&'pin mut (T)>, &'pin mut (U)),
Unit,
}
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::type_repetition_in_bounds)]
enum __EnumProjectionRef<'pin, T, U>
where
Enum<T, U>: 'pin,
{
Struct {
pinned: ::pin_project::__private::Pin<&'pin (T)>,
unpinned: &'pin (U),
},
Tuple(::pin_project::__private::Pin<&'pin (T)>, &'pin (U)),
Unit,
}
impl<T, U> Enum<T, U> {
fn project<'pin>(
self: ::pin_project::__private::Pin<&'pin mut Self>,
Expand Down
44 changes: 21 additions & 23 deletions tests/expand/tests/expand/default-struct.expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,31 @@ struct Struct<T, U> {
unpinned: U,
}
#[doc(hidden)]
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::mut_mut)]
#[allow(clippy::type_repetition_in_bounds)]
struct __StructProjection<'pin, T, U>
where
Struct<T, U>: 'pin,
{
pinned: ::pin_project::__private::Pin<&'pin mut (T)>,
unpinned: &'pin mut (U),
}
#[doc(hidden)]
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::type_repetition_in_bounds)]
struct __StructProjectionRef<'pin, T, U>
where
Struct<T, U>: 'pin,
{
pinned: ::pin_project::__private::Pin<&'pin (T)>,
unpinned: &'pin (U),
}
#[doc(hidden)]
#[allow(non_upper_case_globals)]
#[allow(single_use_lifetimes)]
#[allow(clippy::used_underscore_binding)]
const _: () = {
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::mut_mut)]
#[allow(clippy::type_repetition_in_bounds)]
struct __StructProjection<'pin, T, U>
where
Struct<T, U>: 'pin,
{
pinned: ::pin_project::__private::Pin<&'pin mut (T)>,
unpinned: &'pin mut (U),
}
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::type_repetition_in_bounds)]
struct __StructProjectionRef<'pin, T, U>
where
Struct<T, U>: 'pin,
{
pinned: ::pin_project::__private::Pin<&'pin (T)>,
unpinned: &'pin (U),
}
impl<T, U> Struct<T, U> {
fn project<'pin>(
self: ::pin_project::__private::Pin<&'pin mut Self>,
Expand Down
Loading

0 comments on commit fe6898c

Please sign in to comment.