22
33use proc_macro2:: { Span , TokenStream } ;
44use quote:: { format_ident, quote, quote_spanned} ;
5+ use std:: collections:: HashSet ;
56use syn:: {
67 braced,
78 parse:: { End , Parse } ,
89 parse_quote,
910 punctuated:: Punctuated ,
1011 spanned:: Spanned ,
11- token, Attribute , Block , Expr , ExprCall , ExprPath , Ident , Path , Token , Type ,
12+ token,
13+ visit:: { self , Visit } ,
14+ Attribute , Block , Expr , ExprCall , ExprPath , Ident , Path , Token , Type ,
1215} ;
1316
1417use crate :: diagnostics:: { DiagCtxt , ErrorGuaranteed } ;
1518
19+ struct AccessorScanner < ' a > {
20+ field_names : HashSet < & ' a Ident > ,
21+ used_fields : HashSet < Ident > ,
22+ }
23+
24+ impl < ' a > Visit < ' _ > for AccessorScanner < ' a > {
25+ fn visit_ident ( & mut self , i : & Ident ) {
26+ if self . field_names . contains ( i) {
27+ self . used_fields . insert ( i. clone ( ) ) ;
28+ }
29+ visit:: visit_ident ( self , i) ;
30+ }
31+ }
32+
1633pub ( crate ) struct Initializer {
1734 attrs : Vec < InitializerAttribute > ,
1835 this : Option < This > ,
@@ -145,6 +162,27 @@ pub(crate) fn expand(
145162 } ;
146163 // `mixed_site` ensures that the data is not accessible to the user-controlled code.
147164 let data = Ident :: new ( "__data" , Span :: mixed_site ( ) ) ;
165+ let mut field_names = HashSet :: new ( ) ;
166+ for field in & fields {
167+ if let Some ( ident) = field. kind . ident ( ) {
168+ field_names. insert ( ident) ;
169+ }
170+ }
171+ let mut scanner = AccessorScanner {
172+ field_names,
173+ used_fields : HashSet :: new ( ) ,
174+ } ;
175+ for field in & fields {
176+ match & field. kind {
177+ InitializerKind :: Value {
178+ value : Some ( ( _, expr) ) ,
179+ ..
180+ } => scanner. visit_expr ( expr) ,
181+ InitializerKind :: Init { value, .. } => scanner. visit_expr ( value) ,
182+ InitializerKind :: Code { block, .. } => scanner. visit_block ( block) ,
183+ _ => { }
184+ }
185+ }
148186 let init_fields = init_fields (
149187 & fields,
150188 pinned,
@@ -153,6 +191,7 @@ pub(crate) fn expand(
153191 . any ( |attr| matches ! ( attr, InitializerAttribute :: DisableInitializedFieldAccess ) ) ,
154192 & data,
155193 & slot,
194+ & scanner. used_fields ,
156195 ) ;
157196 let field_check = make_field_check ( & fields, init_kind, & path) ;
158197 Ok ( quote ! { {
@@ -239,6 +278,7 @@ fn init_fields(
239278 generate_initialized_accessors : bool ,
240279 data : & Ident ,
241280 slot : & Ident ,
281+ used_fields : & HashSet < Ident > ,
242282) -> TokenStream {
243283 let mut guards = vec ! [ ] ;
244284 let mut guard_attrs = vec ! [ ] ;
@@ -272,13 +312,14 @@ fn init_fields(
272312 unsafe { & mut ( * #slot) . #ident }
273313 }
274314 } ;
275- let accessor = generate_initialized_accessors. then ( || {
276- quote ! {
277- #( #cfgs) *
278- #[ allow( unused_variables) ]
279- let #ident = #accessor;
280- }
281- } ) ;
315+ let accessor = ( generate_initialized_accessors && used_fields. contains ( ident) )
316+ . then ( || {
317+ quote ! {
318+ #( #cfgs) *
319+ #[ allow( unused_variables) ]
320+ let #ident = #accessor;
321+ }
322+ } ) ;
282323 quote ! {
283324 #( #attrs) *
284325 {
@@ -326,13 +367,14 @@ fn init_fields(
326367 } ,
327368 )
328369 } ;
329- let accessor = generate_initialized_accessors. then ( || {
330- quote ! {
331- #( #cfgs) *
332- #[ allow( unused_variables) ]
333- let #ident = #accessor;
334- }
335- } ) ;
370+ let accessor = ( generate_initialized_accessors && used_fields. contains ( ident) )
371+ . then ( || {
372+ quote ! {
373+ #( #cfgs) *
374+ #[ allow( unused_variables) ]
375+ let #ident = #accessor;
376+ }
377+ } ) ;
336378 quote ! {
337379 #( #attrs) *
338380 {
0 commit comments