1 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
2 | |
3 | use proc_macro2::{Delimiter, Group, Span, TokenStream}; |
4 | use quote::{ToTokens as _, format_ident, quote, quote_spanned}; |
5 | use syn::{ |
6 | Attribute, Error, Field, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Index, Item, |
7 | Lifetime, LifetimeParam, Meta, Result, Token, Type, Variant, Visibility, WhereClause, |
8 | parse_quote, punctuated::Punctuated, token, visit_mut::VisitMut as _, |
9 | }; |
10 | |
11 | use super::{ |
12 | PIN, |
13 | args::{Args, ProjReplace, UnpinImpl, parse_args}, |
14 | }; |
15 | use crate::utils::{ |
16 | ReplaceReceiver, SliceExt as _, Variants, determine_lifetime_name, determine_visibility, |
17 | insert_lifetime_and_bound, |
18 | }; |
19 | |
20 | pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> { |
21 | let mut input: Item = syn::parse2(input)?; |
22 | |
23 | let mut cx; |
24 | let mut generate = GenerateTokens::default(); |
25 | |
26 | match &mut input { |
27 | Item::Struct(input) => { |
28 | let ident = &input.ident; |
29 | let ty_generics = input.generics.split_for_impl().1; |
30 | let self_ty = parse_quote!(#ident #ty_generics); |
31 | let mut visitor = ReplaceReceiver(&self_ty); |
32 | visitor.visit_item_struct_mut(input); |
33 | cx = Context::new(&input.attrs, &input.vis, &input.ident, &mut input.generics, Struct)?; |
34 | parse_struct(&mut cx, &input.fields, &mut generate)?; |
35 | } |
36 | Item::Enum(input) => { |
37 | let ident = &input.ident; |
38 | let ty_generics = input.generics.split_for_impl().1; |
39 | let self_ty = parse_quote!(#ident #ty_generics); |
40 | let mut visitor = ReplaceReceiver(&self_ty); |
41 | visitor.visit_item_enum_mut(input); |
42 | cx = Context::new(&input.attrs, &input.vis, &input.ident, &mut input.generics, Enum)?; |
43 | parse_enum(&mut cx, input.brace_token, &input.variants, &mut generate)?; |
44 | } |
45 | _ => bail!(input, "#[pin_project] attribute may only be used on structs or enums" ), |
46 | } |
47 | |
48 | Ok(generate.into_tokens(&cx)) |
49 | } |
50 | |
51 | #[derive (Default)] |
52 | struct GenerateTokens { |
53 | exposed: TokenStream, |
54 | scoped: TokenStream, |
55 | } |
56 | |
57 | impl GenerateTokens { |
58 | fn extend(&mut self, expose: bool, tokens: TokenStream) { |
59 | if expose { |
60 | self.exposed.extend(tokens); |
61 | } else { |
62 | self.scoped.extend(tokens); |
63 | } |
64 | } |
65 | |
66 | fn into_tokens(self, cx: &Context<'_>) -> TokenStream { |
67 | let mut tokens = self.exposed; |
68 | let scoped = self.scoped; |
69 | |
70 | let unpin_impl = make_unpin_impl(cx); |
71 | let drop_impl = make_drop_impl(cx); |
72 | let allowed_lints = global_allowed_lints(); |
73 | |
74 | tokens.extend(quote! { |
75 | // All items except projected types are generated inside a `const` scope. |
76 | // This makes it impossible for user code to refer to these types. |
77 | // However, this prevents Rustdoc from displaying docs for any |
78 | // of our types. In particular, users cannot see the |
79 | // automatically generated `Unpin` impl for the '__UnpinStruct' types |
80 | // |
81 | // Previously, we provided a flag to correctly document the |
82 | // automatically generated `Unpin` impl by using def-site hygiene, |
83 | // but it is now removed. |
84 | // |
85 | // Refs: |
86 | // - https://github.com/rust-lang/rust/issues/63281 |
87 | // - https://github.com/taiki-e/pin-project/pull/53#issuecomment-525906867 |
88 | // - https://github.com/taiki-e/pin-project/pull/70 |
89 | #[allow( |
90 | unused_qualifications, |
91 | #allowed_lints |
92 | clippy::elidable_lifetime_names, |
93 | clippy::missing_const_for_fn, |
94 | clippy::needless_lifetimes, |
95 | clippy::semicolon_if_nothing_returned, |
96 | clippy::use_self, |
97 | clippy::used_underscore_binding |
98 | )] |
99 | const _: () = { |
100 | #[allow(unused_extern_crates)] |
101 | extern crate pin_project as _pin_project; |
102 | #scoped |
103 | #unpin_impl |
104 | #drop_impl |
105 | }; |
106 | }); |
107 | tokens |
108 | } |
109 | } |
110 | |
111 | /// Returns attributes that should be applied to all generated code. |
112 | fn global_allowed_lints() -> TokenStream { |
113 | quote! { |
114 | deprecated, |
115 | explicit_outlives_requirements, // https://github.com/rust-lang/rust/issues/60993 |
116 | single_use_lifetimes, // https://github.com/rust-lang/rust/issues/55058 |
117 | unreachable_pub, // This lint warns `pub` field in private struct. |
118 | unused_tuple_struct_fields, |
119 | // This lint warns of `clippy::*` generated by external macros. |
120 | // We allow this lint for compatibility with older compilers. |
121 | clippy::unknown_clippy_lints, |
122 | clippy::absolute_paths, |
123 | clippy::min_ident_chars, |
124 | clippy::pattern_type_mismatch, |
125 | clippy::pub_with_shorthand, |
126 | clippy::redundant_pub_crate, // This lint warns `pub(crate)` field in private struct. |
127 | clippy::single_char_lifetime_names, |
128 | clippy::type_repetition_in_bounds, // https://github.com/rust-lang/rust-clippy/issues/4326 |
129 | } |
130 | } |
131 | |
132 | /// Returns attributes used on projected types. |
133 | fn proj_allowed_lints(cx: &Context<'_>) -> (TokenStream, TokenStream, TokenStream) { |
134 | let global_allowed_lints = global_allowed_lints(); |
135 | let proj_mut_allowed_lints = if cx.project { Some(&global_allowed_lints) } else { None }; |
136 | let proj_mut = quote! { |
137 | #[allow( |
138 | dead_code, // This lint warns unused fields/variants. |
139 | #proj_mut_allowed_lints |
140 | clippy::missing_docs_in_private_items, |
141 | clippy::mut_mut // This lint warns `&mut &mut <ty>`. |
142 | )] |
143 | }; |
144 | let proj_ref_allowed_lints = if cx.project_ref { Some(&global_allowed_lints) } else { None }; |
145 | let proj_ref = quote! { |
146 | #[allow( |
147 | dead_code, // This lint warns unused fields/variants. |
148 | #proj_ref_allowed_lints |
149 | clippy::missing_docs_in_private_items, |
150 | clippy::ref_option_ref // This lint warns `&Option<&<ty>>`. |
151 | )] |
152 | }; |
153 | let proj_own_allowed_lints = |
154 | if cx.project_replace.ident().is_some() { Some(&global_allowed_lints) } else { None }; |
155 | let variant_size_differences = if cx.kind == Enum { |
156 | Some(quote! { variant_size_differences, clippy::large_enum_variant, }) |
157 | } else { |
158 | None |
159 | }; |
160 | let proj_own = quote! { |
161 | #[allow( |
162 | dead_code, // This lint warns unused fields/variants. |
163 | #proj_own_allowed_lints |
164 | #variant_size_differences |
165 | clippy::missing_docs_in_private_items |
166 | )] |
167 | }; |
168 | (proj_mut, proj_ref, proj_own) |
169 | } |
170 | |
171 | struct Context<'a> { |
172 | /// The original type. |
173 | orig: OriginalType<'a>, |
174 | /// The projected types. |
175 | proj: ProjectedType, |
176 | /// Types of the pinned fields. |
177 | pinned_fields: Vec<&'a Type>, |
178 | /// Kind of the original type: struct or enum |
179 | kind: TypeKind, |
180 | |
181 | /// `PinnedDrop` argument. |
182 | pinned_drop: Option<Span>, |
183 | /// `UnsafeUnpin` or `!Unpin` argument. |
184 | unpin_impl: UnpinImpl, |
185 | /// `project` argument. |
186 | project: bool, |
187 | /// `project_ref` argument. |
188 | project_ref: bool, |
189 | /// `project_replace [= <ident>]` argument. |
190 | project_replace: ProjReplace, |
191 | } |
192 | |
193 | impl<'a> Context<'a> { |
194 | fn new( |
195 | attrs: &'a [Attribute], |
196 | vis: &'a Visibility, |
197 | ident: &'a Ident, |
198 | generics: &'a mut Generics, |
199 | kind: TypeKind, |
200 | ) -> Result<Self> { |
201 | let Args { pinned_drop, unpin_impl, project, project_ref, project_replace } = |
202 | parse_args(attrs)?; |
203 | |
204 | if let Some(name) = [project.as_ref(), project_ref.as_ref(), project_replace.ident()] |
205 | .iter() |
206 | .filter_map(Option::as_ref) |
207 | .find(|name| **name == ident) |
208 | { |
209 | bail!(name, "name ` {}` is the same as the original type name" , name); |
210 | } |
211 | |
212 | let mut lifetime_name = String::from("'pin" ); |
213 | determine_lifetime_name(&mut lifetime_name, generics); |
214 | let lifetime = Lifetime::new(&lifetime_name, Span::call_site()); |
215 | |
216 | let ty_generics = generics.split_for_impl().1; |
217 | let ty_generics_as_generics = parse_quote!(#ty_generics); |
218 | let mut proj_generics = generics.clone(); |
219 | let pred = insert_lifetime_and_bound( |
220 | &mut proj_generics, |
221 | lifetime.clone(), |
222 | &ty_generics_as_generics, |
223 | ident, |
224 | ); |
225 | let mut where_clause = generics.make_where_clause().clone(); |
226 | where_clause.predicates.push(pred); |
227 | |
228 | let own_ident = project_replace |
229 | .ident() |
230 | .cloned() |
231 | .unwrap_or_else(|| format_ident!("__ {}ProjectionOwned" , ident)); |
232 | |
233 | Ok(Self { |
234 | kind, |
235 | pinned_drop, |
236 | unpin_impl, |
237 | project: project.is_some(), |
238 | project_ref: project_ref.is_some(), |
239 | project_replace, |
240 | proj: ProjectedType { |
241 | vis: determine_visibility(vis), |
242 | mut_ident: project.unwrap_or_else(|| format_ident!("__ {}Projection" , ident)), |
243 | ref_ident: project_ref.unwrap_or_else(|| format_ident!("__ {}ProjectionRef" , ident)), |
244 | own_ident, |
245 | lifetime, |
246 | generics: proj_generics, |
247 | where_clause, |
248 | }, |
249 | orig: OriginalType { attrs, vis, ident, generics }, |
250 | pinned_fields: vec![], |
251 | }) |
252 | } |
253 | } |
254 | |
255 | #[derive (Copy, Clone, PartialEq)] |
256 | enum TypeKind { |
257 | Enum, |
258 | Struct, |
259 | } |
260 | use self::TypeKind::{Enum, Struct}; |
261 | |
262 | struct OriginalType<'a> { |
263 | /// Attributes of the original type. |
264 | attrs: &'a [Attribute], |
265 | /// Visibility of the original type. |
266 | vis: &'a Visibility, |
267 | /// Name of the original type. |
268 | ident: &'a Ident, |
269 | /// Generics of the original type. |
270 | generics: &'a Generics, |
271 | } |
272 | |
273 | struct ProjectedType { |
274 | /// Visibility of the projected types. |
275 | vis: Visibility, |
276 | /// Name of the projected type returned by `project` method. |
277 | mut_ident: Ident, |
278 | /// Name of the projected type returned by `project_ref` method. |
279 | ref_ident: Ident, |
280 | /// Name of the projected type returned by `project_replace` method. |
281 | own_ident: Ident, |
282 | /// Lifetime on the generated projected types. |
283 | lifetime: Lifetime, |
284 | /// Generics of the projected types. |
285 | generics: Generics, |
286 | /// `where` clause of the projected types. This has an additional |
287 | /// bound generated by `insert_lifetime_and_bound` |
288 | where_clause: WhereClause, |
289 | } |
290 | |
291 | struct ProjectedVariants { |
292 | proj_variants: TokenStream, |
293 | proj_ref_variants: TokenStream, |
294 | proj_own_variants: TokenStream, |
295 | proj_arms: TokenStream, |
296 | proj_ref_arms: TokenStream, |
297 | proj_own_arms: TokenStream, |
298 | } |
299 | |
300 | #[derive (Default)] |
301 | struct ProjectedFields { |
302 | proj_pat: TokenStream, |
303 | proj_body: TokenStream, |
304 | proj_own_body: TokenStream, |
305 | proj_fields: TokenStream, |
306 | proj_ref_fields: TokenStream, |
307 | proj_own_fields: TokenStream, |
308 | } |
309 | |
310 | fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> { |
311 | if fields.is_empty() { |
312 | let msg: &'static str = "#[pin_project] attribute may not be used on structs with zero fields" ; |
313 | if let Fields::Unit = fields { |
314 | bail!(ident, msg) |
315 | } |
316 | bail!(fields, msg) |
317 | } |
318 | Ok(()) |
319 | } |
320 | |
321 | fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> { |
322 | if variants.is_empty() { |
323 | return Err(Error::new( |
324 | span:brace_token.span.join(), |
325 | message:"#[pin_project] attribute may not be used on enums without variants" , |
326 | )); |
327 | } |
328 | let has_field: bool = variants.iter().try_fold(init:false, |has_field: bool, v: &Variant| { |
329 | if let Some((_, e: &Expr)) = &v.discriminant { |
330 | bail!(e, "#[pin_project] attribute may not be used on enums with discriminants" ); |
331 | } else if let Some(attr: &Attribute) = v.attrs.find(PIN) { |
332 | bail!(attr, "#[pin] attribute may only be used on fields of structs or variants" ); |
333 | } else if v.fields.is_empty() { |
334 | Ok(has_field) |
335 | } else { |
336 | Ok(true) |
337 | } |
338 | })?; |
339 | if has_field { |
340 | Ok(()) |
341 | } else { |
342 | bail!(variants, "#[pin_project] attribute may not be used on enums with zero fields" ); |
343 | } |
344 | } |
345 | |
346 | fn parse_struct<'a>( |
347 | cx: &mut Context<'a>, |
348 | fields: &'a Fields, |
349 | generate: &mut GenerateTokens, |
350 | ) -> Result<()> { |
351 | // Do this first for a better error message. |
352 | let packed_check = ensure_not_packed(&cx.orig, Some(fields))?; |
353 | |
354 | validate_struct(cx.orig.ident, fields)?; |
355 | |
356 | let ProjectedFields { |
357 | proj_pat, |
358 | proj_body, |
359 | proj_fields, |
360 | proj_ref_fields, |
361 | proj_own_fields, |
362 | proj_own_body, |
363 | } = match fields { |
364 | Fields::Named(_) => visit_fields(cx, None, fields, Delimiter::Brace)?, |
365 | Fields::Unnamed(_) => visit_fields(cx, None, fields, Delimiter::Parenthesis)?, |
366 | Fields::Unit => unreachable!(), |
367 | }; |
368 | |
369 | let proj_ident = &cx.proj.mut_ident; |
370 | let proj_ref_ident = &cx.proj.ref_ident; |
371 | let proj_own_ident = &cx.proj.own_ident; |
372 | let vis = &cx.proj.vis; |
373 | let mut orig_generics = cx.orig.generics.clone(); |
374 | let orig_where_clause = orig_generics.where_clause.take(); |
375 | let proj_generics = &cx.proj.generics; |
376 | let proj_where_clause = &cx.proj.where_clause; |
377 | |
378 | // For tuple structs, we need to generate `(T1, T2) where Foo: Bar` |
379 | // For non-tuple structs, we need to generate `where Foo: Bar { field1: T }` |
380 | let (where_clause_fields, where_clause_ref_fields, where_clause_own_fields) = match fields { |
381 | Fields::Named(_) => ( |
382 | quote!(#proj_where_clause #proj_fields), |
383 | quote!(#proj_where_clause #proj_ref_fields), |
384 | quote!(#orig_where_clause #proj_own_fields), |
385 | ), |
386 | Fields::Unnamed(_) => ( |
387 | quote!(#proj_fields #proj_where_clause;), |
388 | quote!(#proj_ref_fields #proj_where_clause;), |
389 | quote!(#proj_own_fields #orig_where_clause;), |
390 | ), |
391 | Fields::Unit => unreachable!(), |
392 | }; |
393 | |
394 | let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx); |
395 | generate.extend(cx.project, quote! { |
396 | #proj_attrs |
397 | #vis struct #proj_ident #proj_generics #where_clause_fields |
398 | }); |
399 | generate.extend(cx.project_ref, quote! { |
400 | #proj_ref_attrs |
401 | #vis struct #proj_ref_ident #proj_generics #where_clause_ref_fields |
402 | }); |
403 | if cx.project_replace.span().is_some() { |
404 | generate.extend(cx.project_replace.ident().is_some(), quote! { |
405 | #proj_own_attrs |
406 | #vis struct #proj_own_ident #orig_generics #where_clause_own_fields |
407 | }); |
408 | } |
409 | |
410 | let proj_mut_body = quote! { |
411 | let Self #proj_pat = self.get_unchecked_mut(); |
412 | #proj_ident #proj_body |
413 | }; |
414 | let proj_ref_body = quote! { |
415 | let Self #proj_pat = self.get_ref(); |
416 | #proj_ref_ident #proj_body |
417 | }; |
418 | let proj_own_body = quote! { |
419 | let Self #proj_pat = &mut *__self_ptr; |
420 | #proj_own_body |
421 | }; |
422 | generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body)); |
423 | |
424 | generate.extend(false, packed_check); |
425 | Ok(()) |
426 | } |
427 | |
428 | fn parse_enum<'a>( |
429 | cx: &mut Context<'a>, |
430 | brace_token: token::Brace, |
431 | variants: &'a Punctuated<Variant, Token![,]>, |
432 | generate: &mut GenerateTokens, |
433 | ) -> Result<()> { |
434 | if let ProjReplace::Unnamed { span } = &cx.project_replace { |
435 | return Err(Error::new( |
436 | *span, |
437 | "`project_replace` argument requires a value when used on enums" , |
438 | )); |
439 | } |
440 | |
441 | // #[repr(packed)] cannot be apply on enums and will be rejected by rustc. |
442 | // However, we should not rely on the behavior of rustc that rejects this. |
443 | // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 |
444 | // |
445 | // Do this first for a better error message. |
446 | ensure_not_packed(&cx.orig, None)?; |
447 | |
448 | validate_enum(brace_token, variants)?; |
449 | |
450 | let ProjectedVariants { |
451 | proj_variants, |
452 | proj_ref_variants, |
453 | proj_own_variants, |
454 | proj_arms, |
455 | proj_ref_arms, |
456 | proj_own_arms, |
457 | } = visit_variants(cx, variants)?; |
458 | |
459 | let proj_ident = &cx.proj.mut_ident; |
460 | let proj_ref_ident = &cx.proj.ref_ident; |
461 | let proj_own_ident = &cx.proj.own_ident; |
462 | let vis = &cx.proj.vis; |
463 | let mut orig_generics = cx.orig.generics.clone(); |
464 | let orig_where_clause = orig_generics.where_clause.take(); |
465 | let proj_generics = &cx.proj.generics; |
466 | let proj_where_clause = &cx.proj.where_clause; |
467 | |
468 | let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx); |
469 | if cx.project { |
470 | generate.extend(true, quote! { |
471 | #proj_attrs |
472 | #vis enum #proj_ident #proj_generics #proj_where_clause { |
473 | #proj_variants |
474 | } |
475 | }); |
476 | } |
477 | if cx.project_ref { |
478 | generate.extend(true, quote! { |
479 | #proj_ref_attrs |
480 | #vis enum #proj_ref_ident #proj_generics #proj_where_clause { |
481 | #proj_ref_variants |
482 | } |
483 | }); |
484 | } |
485 | if cx.project_replace.ident().is_some() { |
486 | generate.extend(true, quote! { |
487 | #proj_own_attrs |
488 | #vis enum #proj_own_ident #orig_generics #orig_where_clause { |
489 | #proj_own_variants |
490 | } |
491 | }); |
492 | } |
493 | |
494 | let proj_mut_body = quote! { |
495 | match self.get_unchecked_mut() { |
496 | #proj_arms |
497 | } |
498 | }; |
499 | let proj_ref_body = quote! { |
500 | match self.get_ref() { |
501 | #proj_ref_arms |
502 | } |
503 | }; |
504 | let proj_own_body = quote! { |
505 | match &mut *__self_ptr { |
506 | #proj_own_arms |
507 | } |
508 | }; |
509 | generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body)); |
510 | |
511 | Ok(()) |
512 | } |
513 | |
514 | fn visit_variants<'a>(cx: &mut Context<'a>, variants: &'a Variants) -> Result<ProjectedVariants> { |
515 | let mut proj_variants = TokenStream::new(); |
516 | let mut proj_ref_variants = TokenStream::new(); |
517 | let mut proj_own_variants = TokenStream::new(); |
518 | let mut proj_arms = TokenStream::new(); |
519 | let mut proj_ref_arms = TokenStream::new(); |
520 | let mut proj_own_arms = TokenStream::new(); |
521 | |
522 | for Variant { ident, fields, .. } in variants { |
523 | let ProjectedFields { |
524 | proj_pat, |
525 | proj_body, |
526 | proj_fields, |
527 | proj_ref_fields, |
528 | proj_own_fields, |
529 | proj_own_body, |
530 | } = match fields { |
531 | Fields::Named(_) => visit_fields(cx, Some(ident), fields, Delimiter::Brace)?, |
532 | Fields::Unnamed(_) => visit_fields(cx, Some(ident), fields, Delimiter::Parenthesis)?, |
533 | Fields::Unit => ProjectedFields { |
534 | proj_own_body: proj_own_body(cx, Some(ident), None, &[]), |
535 | ..Default::default() |
536 | }, |
537 | }; |
538 | |
539 | let proj_ident = &cx.proj.mut_ident; |
540 | let proj_ref_ident = &cx.proj.ref_ident; |
541 | proj_variants.extend(quote! { |
542 | #ident #proj_fields, |
543 | }); |
544 | proj_ref_variants.extend(quote! { |
545 | #ident #proj_ref_fields, |
546 | }); |
547 | proj_own_variants.extend(quote! { |
548 | #ident #proj_own_fields, |
549 | }); |
550 | proj_arms.extend(quote! { |
551 | Self::#ident #proj_pat => #proj_ident::#ident #proj_body, |
552 | }); |
553 | proj_ref_arms.extend(quote! { |
554 | Self::#ident #proj_pat => #proj_ref_ident::#ident #proj_body, |
555 | }); |
556 | proj_own_arms.extend(quote! { |
557 | Self::#ident #proj_pat => { #proj_own_body } |
558 | }); |
559 | } |
560 | |
561 | Ok(ProjectedVariants { |
562 | proj_variants, |
563 | proj_ref_variants, |
564 | proj_own_variants, |
565 | proj_arms, |
566 | proj_ref_arms, |
567 | proj_own_arms, |
568 | }) |
569 | } |
570 | |
571 | fn visit_fields<'a>( |
572 | cx: &mut Context<'a>, |
573 | variant_ident: Option<&Ident>, |
574 | fields: &'a Fields, |
575 | delim: Delimiter, |
576 | ) -> Result<ProjectedFields> { |
577 | fn surround(delim: Delimiter, tokens: TokenStream) -> TokenStream { |
578 | Group::new(delim, tokens).into_token_stream() |
579 | } |
580 | |
581 | let mut proj_pat = TokenStream::new(); |
582 | let mut proj_body = TokenStream::new(); |
583 | let mut proj_fields = TokenStream::new(); |
584 | let mut proj_ref_fields = TokenStream::new(); |
585 | let mut proj_own_fields = TokenStream::new(); |
586 | let mut proj_move = TokenStream::new(); |
587 | let mut pinned_bindings = Vec::with_capacity(fields.len()); |
588 | |
589 | for (i, Field { attrs, vis, ident, colon_token, ty, .. }) in fields.iter().enumerate() { |
590 | let binding = ident.clone().unwrap_or_else(|| format_ident!("_ {}" , i)); |
591 | proj_pat.extend(quote!(#binding,)); |
592 | let lifetime = &cx.proj.lifetime; |
593 | if attrs.position_exact(PIN)?.is_some() { |
594 | proj_fields.extend(quote! { |
595 | #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime mut (#ty)>, |
596 | }); |
597 | proj_ref_fields.extend(quote! { |
598 | #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime (#ty)>, |
599 | }); |
600 | proj_own_fields.extend(quote! { |
601 | #vis #ident #colon_token ::pin_project::__private::PhantomData<#ty>, |
602 | }); |
603 | proj_body.extend(quote! { |
604 | #ident #colon_token _pin_project::__private::Pin::new_unchecked(#binding), |
605 | }); |
606 | proj_move.extend(quote! { |
607 | #ident #colon_token _pin_project::__private::PhantomData, |
608 | }); |
609 | |
610 | cx.pinned_fields.push(ty); |
611 | pinned_bindings.push(binding); |
612 | } else { |
613 | proj_fields.extend(quote! { |
614 | #vis #ident #colon_token &#lifetime mut (#ty), |
615 | }); |
616 | proj_ref_fields.extend(quote! { |
617 | #vis #ident #colon_token &#lifetime (#ty), |
618 | }); |
619 | proj_own_fields.extend(quote! { |
620 | #vis #ident #colon_token #ty, |
621 | }); |
622 | proj_body.extend(quote! { |
623 | #binding, |
624 | }); |
625 | proj_move.extend(quote! { |
626 | #ident #colon_token _pin_project::__private::ptr::read(#binding), |
627 | }); |
628 | } |
629 | } |
630 | |
631 | let proj_pat = surround(delim, proj_pat); |
632 | let proj_body = surround(delim, proj_body); |
633 | let proj_fields = surround(delim, proj_fields); |
634 | let proj_ref_fields = surround(delim, proj_ref_fields); |
635 | let proj_own_fields = surround(delim, proj_own_fields); |
636 | |
637 | let proj_move = Group::new(delim, proj_move); |
638 | let proj_own_body = proj_own_body(cx, variant_ident, Some(&proj_move), &pinned_bindings); |
639 | |
640 | Ok(ProjectedFields { |
641 | proj_pat, |
642 | proj_body, |
643 | proj_own_body, |
644 | proj_fields, |
645 | proj_ref_fields, |
646 | proj_own_fields, |
647 | }) |
648 | } |
649 | |
650 | /// Generates the processing that `project_replace` does for the struct or each variant. |
651 | /// |
652 | /// Note: `pinned_fields` must be in declaration order. |
653 | fn proj_own_body( |
654 | cx: &Context<'_>, |
655 | variant_ident: Option<&Ident>, |
656 | proj_move: Option<&Group>, |
657 | pinned_fields: &[Ident], |
658 | ) -> TokenStream { |
659 | let ident = &cx.proj.own_ident; |
660 | let proj_own = match variant_ident { |
661 | Some(variant_ident) => quote!(#ident::#variant_ident), |
662 | None => quote!(#ident), |
663 | }; |
664 | |
665 | // The fields of the struct and the active enum variant are dropped |
666 | // in declaration order. |
667 | // Refs: https://doc.rust-lang.org/reference/destructors.html |
668 | let pinned_fields = pinned_fields.iter().rev(); |
669 | |
670 | quote! { |
671 | // First, extract all the unpinned fields. |
672 | let __result = #proj_own #proj_move; |
673 | |
674 | // Now create guards to drop all the pinned fields. |
675 | // |
676 | // Due to a compiler bug (https://github.com/rust-lang/rust/issues/47949) |
677 | // this must be in its own scope, or else `__result` will not be dropped |
678 | // if any of the destructors panic. |
679 | { |
680 | #( |
681 | let __guard = _pin_project::__private::UnsafeDropInPlaceGuard::new(#pinned_fields); |
682 | )* |
683 | } |
684 | |
685 | // Finally, return the result. |
686 | __result |
687 | } |
688 | } |
689 | |
690 | /// Creates `Unpin` implementation for the original type. |
691 | /// |
692 | /// The kind of `Unpin` impl generated depends on `unpin_impl` field: |
693 | /// - `UnpinImpl::Unsafe` - Implements `Unpin` via `UnsafeUnpin` impl. |
694 | /// - `UnpinImpl::Negative` - Generates `Unpin` impl with bounds that will never be true. |
695 | /// - `UnpinImpl::Default` - Generates `Unpin` impl that requires `Unpin` for all pinned fields. |
696 | fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { |
697 | match cx.unpin_impl { |
698 | UnpinImpl::Unsafe(span) => { |
699 | let mut proj_generics = cx.proj.generics.clone(); |
700 | let orig_ident = cx.orig.ident; |
701 | let lifetime = &cx.proj.lifetime; |
702 | |
703 | // Make the error message highlight `UnsafeUnpin` argument. |
704 | proj_generics.make_where_clause().predicates.push(parse_quote_spanned! { span => |
705 | _pin_project::__private::PinnedFieldsOf< |
706 | _pin_project::__private::Wrapper<#lifetime, Self> |
707 | >: _pin_project::UnsafeUnpin |
708 | }); |
709 | |
710 | let (impl_generics, _, where_clause) = proj_generics.split_for_impl(); |
711 | let ty_generics = cx.orig.generics.split_for_impl().1; |
712 | |
713 | quote_spanned! { span => |
714 | impl #impl_generics _pin_project::__private::Unpin for #orig_ident #ty_generics |
715 | #where_clause |
716 | { |
717 | } |
718 | } |
719 | } |
720 | UnpinImpl::Negative(span) => { |
721 | let mut proj_generics = cx.proj.generics.clone(); |
722 | let orig_ident = cx.orig.ident; |
723 | let lifetime = &cx.proj.lifetime; |
724 | |
725 | // TODO: Using `<unsized type>: Sized` here allow emulating real negative_impls... |
726 | // https://github.com/taiki-e/pin-project/issues/340#issuecomment-2428002670 |
727 | proj_generics.make_where_clause().predicates.push(parse_quote! { |
728 | _pin_project::__private::PinnedFieldsOf<_pin_project::__private::Wrapper< |
729 | #lifetime, _pin_project::__private::PhantomPinned |
730 | >>: _pin_project::__private::Unpin |
731 | }); |
732 | |
733 | let (impl_generics, _, where_clause) = proj_generics.split_for_impl(); |
734 | let ty_generics = cx.orig.generics.split_for_impl().1; |
735 | |
736 | // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be |
737 | // call-site span. |
738 | let unsafety = <Token![unsafe]>::default(); |
739 | quote_spanned! { span => |
740 | #[doc(hidden)] |
741 | impl #impl_generics _pin_project::__private::Unpin for #orig_ident #ty_generics |
742 | #where_clause |
743 | { |
744 | } |
745 | |
746 | // Generate a dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. |
747 | // |
748 | // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` |
749 | // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` |
750 | // impl, they'll get a "conflicting implementations of trait" error when |
751 | // coherence checks are run. |
752 | #[doc(hidden)] |
753 | #unsafety impl #impl_generics _pin_project::UnsafeUnpin for #orig_ident #ty_generics |
754 | #where_clause |
755 | { |
756 | } |
757 | } |
758 | } |
759 | UnpinImpl::Default => { |
760 | let mut impl_where_clause = cx.orig.generics.where_clause.clone().unwrap(); |
761 | |
762 | // Generate a field in our new struct for every |
763 | // pinned field in the original type. |
764 | let fields = cx.pinned_fields.iter().enumerate().map(|(i, ty)| { |
765 | let field_ident = format_ident!("__field {}" , i); |
766 | quote!(#field_ident: #ty) |
767 | }); |
768 | |
769 | // We could try to determine the subset of type parameters |
770 | // and lifetimes that are actually used by the pinned fields |
771 | // (as opposed to those only used by unpinned fields). |
772 | // However, this would be tricky and error-prone, since |
773 | // it's possible for users to create types that would alias |
774 | // with generic parameters (e.g. 'struct T'). |
775 | // |
776 | // Instead, we generate a use of every single type parameter |
777 | // and lifetime used in the original struct. For type parameters, |
778 | // we generate code like this: |
779 | // |
780 | // ``` |
781 | // struct AlwaysUnpin<T: ?Sized>(PhantomData<T>) {} |
782 | // impl<T: ?Sized> Unpin for AlwaysUnpin<T> {} |
783 | // |
784 | // ... |
785 | // _field: AlwaysUnpin<(A, B, C)> |
786 | // ``` |
787 | // |
788 | // This ensures that any unused type parameters |
789 | // don't end up with `Unpin` bounds. |
790 | let lifetime_fields = cx.orig.generics.lifetimes().enumerate().map( |
791 | |(i, LifetimeParam { lifetime, .. })| { |
792 | let field_ident = format_ident!("__lifetime {}" , i); |
793 | quote!(#field_ident: &#lifetime ()) |
794 | }, |
795 | ); |
796 | |
797 | let orig_ident = cx.orig.ident; |
798 | let struct_ident = format_ident!("__ {}" , orig_ident); |
799 | let vis = cx.orig.vis; |
800 | let lifetime = &cx.proj.lifetime; |
801 | let type_params = cx.orig.generics.type_params().map(|t| &t.ident); |
802 | let proj_generics = &cx.proj.generics; |
803 | let (impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl(); |
804 | let (_, ty_generics, ty_where_clause) = cx.orig.generics.split_for_impl(); |
805 | |
806 | impl_where_clause.predicates.push(parse_quote! { |
807 | _pin_project::__private::PinnedFieldsOf<#struct_ident #proj_ty_generics>: |
808 | _pin_project::__private::Unpin |
809 | }); |
810 | |
811 | quote! { |
812 | // This needs to have the same visibility as the original type, |
813 | // due to the limitations of the 'public in private' error. |
814 | // |
815 | // Our goal is to implement the public trait `Unpin` for |
816 | // a potentially public user type. Because of this, rust |
817 | // requires that any types mentioned in the where clause of |
818 | // our `Unpin` impl also be public. This means that our generated |
819 | // `__UnpinStruct` type must also be public. |
820 | // However, we ensure that the user can never actually reference |
821 | // this 'public' type by creating this type in the inside of `const`. |
822 | #[allow(missing_debug_implementations, unnameable_types)] |
823 | #vis struct #struct_ident #proj_generics #ty_where_clause { |
824 | __pin_project_use_generics: _pin_project::__private::AlwaysUnpin< |
825 | #lifetime, (#(_pin_project::__private::PhantomData<#type_params>),*) |
826 | >, |
827 | |
828 | #(#fields,)* |
829 | #(#lifetime_fields,)* |
830 | } |
831 | |
832 | impl #impl_generics _pin_project::__private::Unpin for #orig_ident #ty_generics |
833 | #impl_where_clause |
834 | { |
835 | } |
836 | |
837 | // Generate a dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. |
838 | // |
839 | // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` |
840 | // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` |
841 | // impl, they'll get a "conflicting implementations of trait" error when |
842 | // coherence checks are run. |
843 | #[doc(hidden)] |
844 | unsafe impl #impl_generics _pin_project::UnsafeUnpin for #orig_ident #ty_generics |
845 | #impl_where_clause |
846 | { |
847 | } |
848 | } |
849 | } |
850 | } |
851 | } |
852 | |
853 | #[allow (clippy::doc_overindented_list_items)] |
854 | /// Creates `Drop` implementation for the original type. |
855 | /// |
856 | /// The kind of `Drop` impl generated depends on `pinned_drop` field: |
857 | /// - `Some` - implements `Drop` via `PinnedDrop` impl. |
858 | /// - `None` - generates code that ensures that `Drop` trait is not implemented, |
859 | /// instead of generating `Drop` impl. |
860 | fn make_drop_impl(cx: &Context<'_>) -> TokenStream { |
861 | let ident = cx.orig.ident; |
862 | let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl(); |
863 | |
864 | if let Some(span) = cx.pinned_drop { |
865 | // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be |
866 | // call-site span. |
867 | let unsafety = <Token![unsafe]>::default(); |
868 | quote_spanned! { span => |
869 | impl #impl_generics _pin_project::__private::Drop for #ident #ty_generics |
870 | #where_clause |
871 | { |
872 | #[allow(clippy::missing_inline_in_public_items)] |
873 | fn drop(&mut self) { |
874 | #unsafety { |
875 | // Safety - we're in 'drop', so we know that 'self' will |
876 | // never move again. |
877 | let __pinned_self = _pin_project::__private::Pin::new_unchecked(self); |
878 | // We call `pinned_drop` only once. Since `PinnedDrop::drop` |
879 | // is an unsafe method and a private API, it is never called again in safe |
880 | // code *unless the user uses a maliciously crafted macro*. |
881 | _pin_project::__private::PinnedDrop::drop(__pinned_self); |
882 | } |
883 | } |
884 | } |
885 | } |
886 | } else { |
887 | // If the user does not provide a `PinnedDrop` impl, |
888 | // we need to ensure that they don't provide a `Drop` impl of their |
889 | // own. |
890 | // Based on https://github.com/upsuper/assert-impl/blob/f503255b292ab0ba8d085b657f4065403cfa46eb/src/lib.rs#L80-L87 |
891 | // |
892 | // We create a new identifier for each struct, so that the traits |
893 | // for different types do not conflict with each other. |
894 | // |
895 | // Another approach would be to provide an empty Drop impl, |
896 | // which would conflict with a user-provided Drop impl. |
897 | // However, this would trigger the compiler's special handling |
898 | // of Drop types (e.g. fields cannot be moved out of a Drop type). |
899 | // This approach prevents the creation of needless Drop impls, |
900 | // giving users more flexibility. |
901 | let trait_ident = format_ident!(" {}MustNotImplDrop" , ident); |
902 | |
903 | quote! { |
904 | // There are two possible cases: |
905 | // 1. The user type does not implement Drop. In this case, |
906 | // the first blanket impl will not apply to it. This code |
907 | // will compile, as there is only one impl of MustNotImplDrop for the user type |
908 | // 2. The user type does impl Drop. This will make the blanket impl applicable, |
909 | // which will then conflict with the explicit MustNotImplDrop impl below. |
910 | // This will result in a compilation error, which is exactly what we want. |
911 | trait #trait_ident {} |
912 | #[allow(clippy::drop_bounds, drop_bounds)] |
913 | impl<T: _pin_project::__private::Drop> #trait_ident for T {} |
914 | impl #impl_generics #trait_ident for #ident #ty_generics #where_clause {} |
915 | |
916 | // Generate a dummy impl of `PinnedDrop`, to ensure that the user cannot implement it. |
917 | // Since the user did not pass `PinnedDrop` to `#[pin_project]`, any `PinnedDrop` |
918 | // impl will not actually be called. Unfortunately, we can't detect this situation |
919 | // directly from either the `#[pin_project]` or `#[pinned_drop]` attributes, since |
920 | // we don't know what other attributes/impl may exist. |
921 | // |
922 | // To ensure that users don't accidentally write a non-functional `PinnedDrop` |
923 | // impls, we emit one ourselves. If the user ends up writing a `PinnedDrop` impl, |
924 | // they'll get a "conflicting implementations of trait" error when coherence |
925 | // checks are run. |
926 | #[doc(hidden)] |
927 | impl #impl_generics _pin_project::__private::PinnedDrop for #ident #ty_generics |
928 | #where_clause |
929 | { |
930 | unsafe fn drop(self: _pin_project::__private::Pin<&mut Self>) {} |
931 | } |
932 | } |
933 | } |
934 | } |
935 | |
936 | /// Creates an implementation of the projection methods. |
937 | /// |
938 | /// On structs, both the `project` and `project_ref` methods are always generated, |
939 | /// and the `project_replace` method is only generated if `ProjReplace::span` is `Some`. |
940 | /// |
941 | /// On enums, only methods that the returned projected type is named will be generated. |
942 | fn make_proj_impl( |
943 | cx: &Context<'_>, |
944 | proj_body: &TokenStream, |
945 | proj_ref_body: &TokenStream, |
946 | proj_own_body: &TokenStream, |
947 | ) -> TokenStream { |
948 | let vis = &cx.proj.vis; |
949 | let lifetime = &cx.proj.lifetime; |
950 | let orig_ident = cx.orig.ident; |
951 | let proj_ident = &cx.proj.mut_ident; |
952 | let proj_ref_ident = &cx.proj.ref_ident; |
953 | let proj_own_ident = &cx.proj.own_ident; |
954 | |
955 | let orig_ty_generics = cx.orig.generics.split_for_impl().1; |
956 | let proj_ty_generics = cx.proj.generics.split_for_impl().1; |
957 | let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl(); |
958 | // TODO: For enums and project_replace, dead_code warnings should not be |
959 | // allowed because methods are not generated unless explicitly specified. |
960 | // However, there is currently no good way to allow warnings for generated |
961 | // code, so we allow warnings for all methods for now. |
962 | let allow_dead_code = quote! { #[allow(dead_code)] }; |
963 | |
964 | let mut project = Some(quote! { |
965 | #allow_dead_code |
966 | #[inline] |
967 | #vis fn project<#lifetime>( |
968 | self: _pin_project::__private::Pin<&#lifetime mut Self>, |
969 | ) -> #proj_ident #proj_ty_generics { |
970 | unsafe { |
971 | #proj_body |
972 | } |
973 | } |
974 | }); |
975 | let mut project_ref = Some(quote! { |
976 | #allow_dead_code |
977 | #[inline] |
978 | #vis fn project_ref<#lifetime>( |
979 | self: _pin_project::__private::Pin<&#lifetime Self>, |
980 | ) -> #proj_ref_ident #proj_ty_generics { |
981 | unsafe { |
982 | #proj_ref_body |
983 | } |
984 | } |
985 | }); |
986 | let mut project_replace = cx.project_replace.span().map(|span| { |
987 | // It is enough to only set the span of the signature. |
988 | let sig = quote_spanned! { span => |
989 | #vis fn project_replace( |
990 | self: _pin_project::__private::Pin<&mut Self>, |
991 | __replacement: Self, |
992 | ) -> #proj_own_ident #orig_ty_generics |
993 | }; |
994 | quote! { |
995 | #allow_dead_code |
996 | #[inline] |
997 | #sig { |
998 | unsafe { |
999 | let __self_ptr: *mut Self = self.get_unchecked_mut(); |
1000 | |
1001 | // Destructors will run in reverse order, so next create a guard to overwrite |
1002 | // `self` with the replacement value without calling destructors. |
1003 | let __guard = _pin_project::__private::UnsafeOverwriteGuard::new( |
1004 | __self_ptr, |
1005 | __replacement, |
1006 | ); |
1007 | |
1008 | #proj_own_body |
1009 | } |
1010 | } |
1011 | } |
1012 | }); |
1013 | |
1014 | if cx.kind == Enum { |
1015 | if !cx.project { |
1016 | project = None; |
1017 | } |
1018 | if !cx.project_ref { |
1019 | project_ref = None; |
1020 | } |
1021 | if cx.project_replace.ident().is_none() { |
1022 | project_replace = None; |
1023 | } |
1024 | } |
1025 | |
1026 | quote! { |
1027 | impl #impl_generics #orig_ident #ty_generics #where_clause { |
1028 | #project |
1029 | #project_ref |
1030 | #project_replace |
1031 | } |
1032 | } |
1033 | } |
1034 | |
1035 | /// Checks that the `[repr(packed)]` attribute is not included. |
1036 | /// |
1037 | /// This currently does two checks: |
1038 | /// - Checks the attributes of structs to ensure there is no `[repr(packed)]`. |
1039 | /// - Generates a function that borrows fields without an unsafe block and |
1040 | /// forbidding `unaligned_references` lint. |
1041 | fn ensure_not_packed(orig: &OriginalType<'_>, fields: Option<&Fields>) -> Result<TokenStream> { |
1042 | for attr in orig.attrs { |
1043 | if let Meta::List(ref list) = attr.meta { |
1044 | if list.path.is_ident("repr" ) { |
1045 | for repr in list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)? { |
1046 | if repr.path().is_ident("packed" ) { |
1047 | let msg = if fields.is_none() { |
1048 | // #[repr(packed)] cannot be apply on enums and will be rejected by rustc. |
1049 | // However, we should not rely on the behavior of rustc that rejects this. |
1050 | // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 |
1051 | "#[repr(packed)] attribute should be applied to a struct or union" |
1052 | } else if repr.require_name_value().is_ok() { |
1053 | // #[repr(packed = "")] is not valid format of #[repr(packed)] and will be |
1054 | // rejected by rustc. |
1055 | // However, we should not rely on the behavior of rustc that rejects this. |
1056 | // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 |
1057 | "#[repr(packed)] attribute should not be name-value pair" |
1058 | } else { |
1059 | "#[pin_project] attribute may not be used on #[repr(packed)] types" |
1060 | }; |
1061 | bail!(repr, msg); |
1062 | } |
1063 | } |
1064 | } |
1065 | } |
1066 | } |
1067 | |
1068 | let fields = match fields { |
1069 | Some(fields) => fields, |
1070 | None => return Ok(TokenStream::new()), |
1071 | }; |
1072 | |
1073 | // Workaround for https://github.com/taiki-e/pin-project/issues/32 |
1074 | // Through the tricky use of proc macros, it's possible to bypass |
1075 | // the above check for the `repr` attribute. |
1076 | // To ensure that it's impossible to use pin projections on a `#[repr(packed)]` |
1077 | // struct, we generate code like this: |
1078 | // |
1079 | // ``` |
1080 | // #[forbid(unaligned_references)] |
1081 | // fn assert_not_repr_packed(val: &MyStruct) { |
1082 | // let _field_1 = &val.field_1; |
1083 | // let _field_2 = &val.field_2; |
1084 | // ... |
1085 | // let _field_n = &val.field_n; |
1086 | // } |
1087 | // ``` |
1088 | // |
1089 | // Taking a reference to a packed field is UB, and applying |
1090 | // `#[forbid(unaligned_references)]` makes sure that doing this is a hard error. |
1091 | // |
1092 | // If the struct ends up having `#[repr(packed)]` applied somehow, |
1093 | // this will generate an (unfriendly) error message. Under all reasonable |
1094 | // circumstances, we'll detect the `#[repr(packed)]` attribute, and generate |
1095 | // a much nicer error above. |
1096 | // |
1097 | // There is one exception: If the type of a struct field has an alignment of 1 |
1098 | // (e.g. u8), it is always safe to take a reference to it, even if the struct |
1099 | // is `#[repr(packed)]`. If the struct is composed entirely of types of |
1100 | // alignment 1, our generated method will not trigger an error if the |
1101 | // struct is `#[repr(packed)]`. |
1102 | // |
1103 | // Fortunately, this should have no observable consequence - `#[repr(packed)]` |
1104 | // is essentially a no-op on such a type. Nevertheless, we include a test |
1105 | // to ensure that the compiler doesn't ever try to copy the fields on |
1106 | // such a struct when trying to drop it - which is reason we prevent |
1107 | // `#[repr(packed)]` in the first place. |
1108 | // |
1109 | // See also https://github.com/taiki-e/pin-project/pull/34. |
1110 | // |
1111 | // Note: |
1112 | // - Lint-based tricks aren't perfect, but they're much better than nothing: |
1113 | // https://github.com/taiki-e/pin-project-lite/issues/26 |
1114 | // |
1115 | // - Enable both unaligned_references and safe_packed_borrows lints |
1116 | // because unaligned_references lint does not exist in older compilers: |
1117 | // https://github.com/taiki-e/pin-project-lite/pull/55 |
1118 | // https://github.com/rust-lang/rust/pull/82525 |
1119 | let mut field_refs = vec![]; |
1120 | match fields { |
1121 | Fields::Named(FieldsNamed { named, .. }) => { |
1122 | for Field { ident, .. } in named { |
1123 | field_refs.push(quote!(&this.#ident)); |
1124 | } |
1125 | } |
1126 | Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { |
1127 | for (index, _) in unnamed.iter().enumerate() { |
1128 | let index = Index::from(index); |
1129 | field_refs.push(quote!(&this.#index)); |
1130 | } |
1131 | } |
1132 | Fields::Unit => {} |
1133 | } |
1134 | |
1135 | let (impl_generics, ty_generics, where_clause) = orig.generics.split_for_impl(); |
1136 | let ident = orig.ident; |
1137 | Ok(quote! { |
1138 | #[forbid(unaligned_references, safe_packed_borrows)] |
1139 | fn __assert_not_repr_packed #impl_generics (this: &#ident #ty_generics) #where_clause { |
1140 | #(let _ = #field_refs;)* |
1141 | } |
1142 | }) |
1143 | } |
1144 | |