• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crate::analyzer::ast as analyzer_ast;
16 use crate::backends::rust::{
17     constraint_to_value, find_constrained_parent_fields, mask_bits, types, ToUpperCamelCase,
18 };
19 use crate::{ast, lint};
20 use quote::{format_ident, quote};
21 use std::collections::{BTreeSet, HashMap};
22 
size_field_ident(id: &str) -> proc_macro2::Ident23 fn size_field_ident(id: &str) -> proc_macro2::Ident {
24     format_ident!("{}_size", id.trim_matches('_'))
25 }
26 
27 /// A single bit-field.
28 struct BitField<'a> {
29     shift: usize, // The shift to apply to this field.
30     field: &'a analyzer_ast::Field,
31 }
32 
33 pub struct FieldParser<'a> {
34     scope: &'a lint::Scope<'a>,
35     endianness: ast::EndiannessValue,
36     packet_name: &'a str,
37     span: &'a proc_macro2::Ident,
38     chunk: Vec<BitField<'a>>,
39     code: Vec<proc_macro2::TokenStream>,
40     shift: usize,
41     offset: usize,
42 }
43 
44 impl<'a> FieldParser<'a> {
new( scope: &'a lint::Scope<'a>, endianness: ast::EndiannessValue, packet_name: &'a str, span: &'a proc_macro2::Ident, ) -> FieldParser<'a>45     pub fn new(
46         scope: &'a lint::Scope<'a>,
47         endianness: ast::EndiannessValue,
48         packet_name: &'a str,
49         span: &'a proc_macro2::Ident,
50     ) -> FieldParser<'a> {
51         FieldParser {
52             scope,
53             endianness,
54             packet_name,
55             span,
56             chunk: Vec::new(),
57             code: Vec::new(),
58             shift: 0,
59             offset: 0,
60         }
61     }
62 
add(&mut self, field: &'a analyzer_ast::Field)63     pub fn add(&mut self, field: &'a analyzer_ast::Field) {
64         match &field.desc {
65             _ if self.scope.is_bitfield(field) => self.add_bit_field(field),
66             ast::FieldDesc::Padding { .. } => todo!("Padding fields are not supported"),
67             ast::FieldDesc::Array { id, width, type_id, size, .. } => self.add_array_field(
68                 id,
69                 *width,
70                 type_id.as_deref(),
71                 *size,
72                 self.scope.get_field_declaration(field),
73             ),
74             ast::FieldDesc::Typedef { id, type_id } => self.add_typedef_field(id, type_id),
75             ast::FieldDesc::Payload { size_modifier, .. } => {
76                 self.add_payload_field(size_modifier.as_deref())
77             }
78             ast::FieldDesc::Body { .. } => self.add_payload_field(None),
79             _ => todo!("{field:?}"),
80         }
81     }
82 
add_bit_field(&mut self, field: &'a analyzer_ast::Field)83     fn add_bit_field(&mut self, field: &'a analyzer_ast::Field) {
84         self.chunk.push(BitField { shift: self.shift, field });
85         self.shift += self.scope.get_field_width(field, false).unwrap();
86         if self.shift % 8 != 0 {
87             return;
88         }
89 
90         let size = self.shift / 8;
91         let end_offset = self.offset + size;
92 
93         let wanted = proc_macro2::Literal::usize_unsuffixed(size);
94         self.check_size(&quote!(#wanted));
95 
96         let chunk_type = types::Integer::new(self.shift);
97         // TODO(mgeisler): generate Rust variable names which cannot
98         // conflict with PDL field names. An option would be to start
99         // Rust variable names with `_`, but that has a special
100         // semantic in Rust.
101         let chunk_name = format_ident!("chunk");
102 
103         let get = types::get_uint(self.endianness, self.shift, self.span);
104         if self.chunk.len() > 1 {
105             // Multiple values: we read into a local variable.
106             self.code.push(quote! {
107                 let #chunk_name = #get;
108             });
109         }
110 
111         let single_value = self.chunk.len() == 1; // && self.chunk[0].offset == 0;
112         for BitField { shift, field } in self.chunk.drain(..) {
113             let mut v = if single_value {
114                 // Single value: read directly.
115                 quote! { #get }
116             } else {
117                 // Multiple values: read from `chunk_name`.
118                 quote! { #chunk_name }
119             };
120 
121             if shift > 0 {
122                 let shift = proc_macro2::Literal::usize_unsuffixed(shift);
123                 v = quote! { (#v >> #shift) }
124             }
125 
126             let width = self.scope.get_field_width(field, false).unwrap();
127             let value_type = types::Integer::new(width);
128             if !single_value && width < value_type.width {
129                 // Mask value if we grabbed more than `width` and if
130                 // `as #value_type` doesn't already do the masking.
131                 let mask = mask_bits(width, "u64");
132                 v = quote! { (#v & #mask) };
133             }
134 
135             if value_type.width < chunk_type.width {
136                 v = quote! { #v as #value_type };
137             }
138 
139             self.code.push(match &field.desc {
140                 ast::FieldDesc::Scalar { id, .. } => {
141                     let id = format_ident!("{id}");
142                     quote! {
143                         let #id = #v;
144                     }
145                 }
146                 ast::FieldDesc::FixedEnum { enum_id, tag_id, .. } => {
147                     let enum_id = format_ident!("{enum_id}");
148                     let tag_id = format_ident!("{}", tag_id.to_upper_camel_case());
149                     quote! {
150                         if #v != #value_type::from(#enum_id::#tag_id)  {
151                             return Err(Error::InvalidFixedValue {
152                                 expected: #value_type::from(#enum_id::#tag_id) as u64,
153                                 actual: #v as u64,
154                             });
155                         }
156                     }
157                 }
158                 ast::FieldDesc::FixedScalar { value, .. } => {
159                     let value = proc_macro2::Literal::usize_unsuffixed(*value);
160                     quote! {
161                         if #v != #value {
162                             return Err(Error::InvalidFixedValue {
163                                 expected: #value,
164                                 actual: #v as u64,
165                             });
166                         }
167                     }
168                 }
169                 ast::FieldDesc::Typedef { id, type_id } => {
170                     let field_name = id;
171                     let type_name = type_id;
172                     let packet_name = &self.packet_name;
173                     let id = format_ident!("{id}");
174                     let type_id = format_ident!("{type_id}");
175                     quote! {
176                         let #id = #type_id::try_from(#v).map_err(|_| Error::InvalidEnumValueError {
177                             obj: #packet_name.to_string(),
178                             field: #field_name.to_string(),
179                             value: #v as u64,
180                             type_: #type_name.to_string(),
181                         })?;
182                     }
183                 }
184                 ast::FieldDesc::Reserved { .. } => {
185                     if single_value {
186                         let span = self.span;
187                         let size = proc_macro2::Literal::usize_unsuffixed(size);
188                         quote! {
189                             #span.get_mut().advance(#size);
190                         }
191                     } else {
192                         //  Otherwise we don't need anything: we will
193                         //  have advanced past the reserved field when
194                         //  reading the chunk above.
195                         quote! {}
196                     }
197                 }
198                 ast::FieldDesc::Size { field_id, .. } => {
199                     let id = size_field_ident(field_id);
200                     quote! {
201                         let #id = #v as usize;
202                     }
203                 }
204                 ast::FieldDesc::Count { field_id, .. } => {
205                     let id = format_ident!("{field_id}_count");
206                     quote! {
207                         let #id = #v as usize;
208                     }
209                 }
210                 _ => todo!(),
211             });
212         }
213 
214         self.offset = end_offset;
215         self.shift = 0;
216     }
217 
packet_scope(&self) -> Option<&lint::PacketScope>218     fn packet_scope(&self) -> Option<&lint::PacketScope> {
219         self.scope.scopes.get(self.scope.typedef.get(self.packet_name)?)
220     }
221 
find_count_field(&self, id: &str) -> Option<proc_macro2::Ident>222     fn find_count_field(&self, id: &str) -> Option<proc_macro2::Ident> {
223         match self.packet_scope()?.get_array_size_field(id)?.desc {
224             ast::FieldDesc::Count { .. } => Some(format_ident!("{id}_count")),
225             _ => None,
226         }
227     }
228 
find_size_field(&self, id: &str) -> Option<proc_macro2::Ident>229     fn find_size_field(&self, id: &str) -> Option<proc_macro2::Ident> {
230         match self.packet_scope()?.get_array_size_field(id)?.desc {
231             ast::FieldDesc::Size { .. } => Some(size_field_ident(id)),
232             _ => None,
233         }
234     }
235 
payload_field_offset_from_end(&self) -> Option<usize>236     fn payload_field_offset_from_end(&self) -> Option<usize> {
237         let packet_scope = self.packet_scope().unwrap();
238         let mut fields = packet_scope.iter_fields();
239         fields.find(|f| {
240             matches!(f.desc, ast::FieldDesc::Body { .. } | ast::FieldDesc::Payload { .. })
241         })?;
242 
243         let mut offset = 0;
244         for field in fields {
245             if let Some(width) = self.scope.get_field_width(field, false) {
246                 offset += width;
247             } else {
248                 return None;
249             }
250         }
251 
252         Some(offset)
253     }
254 
check_size(&mut self, wanted: &proc_macro2::TokenStream)255     fn check_size(&mut self, wanted: &proc_macro2::TokenStream) {
256         let packet_name = &self.packet_name;
257         let span = self.span;
258         self.code.push(quote! {
259             if #span.get().remaining() < #wanted {
260                 return Err(Error::InvalidLengthError {
261                     obj: #packet_name.to_string(),
262                     wanted: #wanted,
263                     got: #span.get().remaining(),
264                 });
265             }
266         });
267     }
268 
add_array_field( &mut self, id: &str, width: Option<usize>, type_id: Option<&str>, size: Option<usize>, decl: Option<&analyzer_ast::Decl>, )269     fn add_array_field(
270         &mut self,
271         id: &str,
272         // `width`: the width in bits of the array elements (if Some).
273         width: Option<usize>,
274         // `type_id`: the enum type of the array elements (if Some).
275         // Mutually exclusive with `width`.
276         type_id: Option<&str>,
277         // `size`: the size of the array in number of elements (if
278         // known). If None, the array is a Vec with a dynamic size.
279         size: Option<usize>,
280         decl: Option<&analyzer_ast::Decl>,
281     ) {
282         enum ElementWidth {
283             Static(usize), // Static size in bytes.
284             Unknown,
285         }
286         let element_width = match width.or_else(|| self.scope.get_decl_width(decl?, false)) {
287             Some(w) => {
288                 assert_eq!(w % 8, 0, "Array element size ({w}) is not a multiple of 8");
289                 ElementWidth::Static(w / 8)
290             }
291             None => ElementWidth::Unknown,
292         };
293 
294         // The "shape" of the array, i.e., the number of elements
295         // given via a static count, a count field, a size field, or
296         // unknown.
297         enum ArrayShape {
298             Static(usize),                  // Static count
299             CountField(proc_macro2::Ident), // Count based on count field
300             SizeField(proc_macro2::Ident),  // Count based on size and field
301             Unknown,                        // Variable count based on remaining bytes
302         }
303         let array_shape = if let Some(count) = size {
304             ArrayShape::Static(count)
305         } else if let Some(count_field) = self.find_count_field(id) {
306             ArrayShape::CountField(count_field)
307         } else if let Some(size_field) = self.find_size_field(id) {
308             ArrayShape::SizeField(size_field)
309         } else {
310             ArrayShape::Unknown
311         };
312 
313         // TODO size modifier
314 
315         // TODO padded_size
316 
317         let id = format_ident!("{id}");
318         let span = self.span;
319 
320         let parse_element = self.parse_array_element(self.span, width, type_id, decl);
321         match (element_width, &array_shape) {
322             (ElementWidth::Unknown, ArrayShape::SizeField(size_field)) => {
323                 // The element width is not known, but the array full
324                 // octet size is known by size field. Parse elements
325                 // item by item as a vector.
326                 self.check_size(&quote!(#size_field));
327                 let parse_element =
328                     self.parse_array_element(&format_ident!("head"), width, type_id, decl);
329                 self.code.push(quote! {
330                     let (head, tail) = #span.get().split_at(#size_field);
331                     let mut head = &mut Cell::new(head);
332                     #span.replace(tail);
333                     let mut #id = Vec::new();
334                     while !head.get().is_empty() {
335                         #id.push(#parse_element?);
336                     }
337                 });
338             }
339             (ElementWidth::Unknown, ArrayShape::Static(count)) => {
340                 // The element width is not known, but the array
341                 // element count is known statically. Parse elements
342                 // item by item as an array.
343                 let count = syn::Index::from(*count);
344                 self.code.push(quote! {
345                     // TODO(mgeisler): use
346                     // https://doc.rust-lang.org/std/array/fn.try_from_fn.html
347                     // when stabilized.
348                     let #id = (0..#count)
349                         .map(|_| #parse_element)
350                         .collect::<Result<Vec<_>>>()?
351                         .try_into()
352                         .map_err(|_| Error::InvalidPacketError)?;
353                 });
354             }
355             (ElementWidth::Unknown, ArrayShape::CountField(count_field)) => {
356                 // The element width is not known, but the array
357                 // element count is known by the count field. Parse
358                 // elements item by item as a vector.
359                 self.code.push(quote! {
360                     let #id = (0..#count_field)
361                         .map(|_| #parse_element)
362                         .collect::<Result<Vec<_>>>()?;
363                 });
364             }
365             (ElementWidth::Unknown, ArrayShape::Unknown) => {
366                 // Neither the count not size is known, parse elements
367                 // until the end of the span.
368                 self.code.push(quote! {
369                     let mut #id = Vec::new();
370                     while !#span.get().is_empty() {
371                         #id.push(#parse_element?);
372                     }
373                 });
374             }
375             (ElementWidth::Static(element_width), ArrayShape::Static(count)) => {
376                 // The element width is known, and the array element
377                 // count is known statically.
378                 let count = syn::Index::from(*count);
379                 // This creates a nicely formatted size.
380                 let array_size = if element_width == 1 {
381                     quote!(#count)
382                 } else {
383                     let element_width = syn::Index::from(element_width);
384                     quote!(#count * #element_width)
385                 };
386                 self.check_size(&array_size);
387                 self.code.push(quote! {
388                     // TODO(mgeisler): use
389                     // https://doc.rust-lang.org/std/array/fn.try_from_fn.html
390                     // when stabilized.
391                     let #id = (0..#count)
392                         .map(|_| #parse_element)
393                         .collect::<Result<Vec<_>>>()?
394                         .try_into()
395                         .map_err(|_| Error::InvalidPacketError)?;
396                 });
397             }
398             (ElementWidth::Static(_), ArrayShape::CountField(count_field)) => {
399                 // The element width is known, and the array element
400                 // count is known dynamically by the count field.
401                 self.check_size(&quote!(#count_field));
402                 self.code.push(quote! {
403                     let #id = (0..#count_field)
404                         .map(|_| #parse_element)
405                         .collect::<Result<Vec<_>>>()?;
406                 });
407             }
408             (ElementWidth::Static(element_width), ArrayShape::SizeField(_))
409             | (ElementWidth::Static(element_width), ArrayShape::Unknown) => {
410                 // The element width is known, and the array full size
411                 // is known by size field, or unknown (in which case
412                 // it is the remaining span length).
413                 let array_size = if let ArrayShape::SizeField(size_field) = &array_shape {
414                     self.check_size(&quote!(#size_field));
415                     quote!(#size_field)
416                 } else {
417                     quote!(#span.get().remaining())
418                 };
419                 let count_field = format_ident!("{id}_count");
420                 let array_count = if element_width != 1 {
421                     let element_width = syn::Index::from(element_width);
422                     self.code.push(quote! {
423                         if #array_size % #element_width != 0 {
424                             return Err(Error::InvalidArraySize {
425                                 array: #array_size,
426                                 element: #element_width,
427                             });
428                         }
429                         let #count_field = #array_size / #element_width;
430                     });
431                     quote!(#count_field)
432                 } else {
433                     array_size
434                 };
435 
436                 self.code.push(quote! {
437                     let mut #id = Vec::with_capacity(#array_count);
438                     for _ in 0..#array_count {
439                         #id.push(#parse_element?);
440                     }
441                 });
442             }
443         }
444     }
445 
446     /// Parse typedef fields.
447     ///
448     /// This is only for non-enum fields: enums are parsed via
449     /// add_bit_field.
add_typedef_field(&mut self, id: &str, type_id: &str)450     fn add_typedef_field(&mut self, id: &str, type_id: &str) {
451         assert_eq!(self.shift, 0, "Typedef field does not start on an octet boundary");
452 
453         let decl = self.scope.typedef[type_id];
454         if let ast::DeclDesc::Struct { parent_id: Some(_), .. } = &decl.desc {
455             panic!("Derived struct used in typedef field");
456         }
457 
458         let span = self.span;
459         let id = format_ident!("{id}");
460         let type_id = format_ident!("{type_id}");
461 
462         match self.scope.get_decl_width(decl, true) {
463             None => self.code.push(quote! {
464                 let #id = #type_id::parse_inner(&mut #span)?;
465             }),
466             Some(width) => {
467                 assert_eq!(width % 8, 0, "Typedef field type size is not a multiple of 8");
468                 let width = syn::Index::from(width / 8);
469                 self.code.push(if let ast::DeclDesc::Checksum { .. } = &decl.desc {
470                     // TODO: handle checksum fields.
471                     quote! {
472                         #span.get_mut().advance(#width);
473                     }
474                 } else {
475                     quote! {
476                         let (head, tail) = #span.get().split_at(#width);
477                         #span.replace(tail);
478                         let #id = #type_id::parse(head)?;
479                     }
480                 });
481             }
482         }
483     }
484 
485     /// Parse body and payload fields.
add_payload_field(&mut self, size_modifier: Option<&str>)486     fn add_payload_field(&mut self, size_modifier: Option<&str>) {
487         let span = self.span;
488         let packet_scope = self.packet_scope().unwrap();
489         let payload_size_field = packet_scope.get_payload_size_field();
490         let offset_from_end = self.payload_field_offset_from_end();
491 
492         if size_modifier.is_some() {
493             todo!(
494                 "Unsupported size modifier for {packet}: {size_modifier:?}",
495                 packet = self.packet_name
496             );
497         }
498 
499         if self.shift != 0 {
500             if payload_size_field.is_some() {
501                 panic!("Unexpected payload size for non byte aligned payload");
502             }
503 
504             //let rounded_size = self.shift / 8 + if self.shift % 8 == 0 { 0 } else { 1 };
505             //let padding_bits = 8 * rounded_size - self.shift;
506             //let reserved_field =
507             //    ast::Field::Reserved { loc: ast::SourceRange::default(), width: padding_bits };
508             //TODO: self.add_bit_field(&reserved_field); --
509             // reserved_field does not live long enough.
510 
511             // TODO: consume span of rounded size
512         } else {
513             // TODO: consume span
514         }
515 
516         if let Some(ast::FieldDesc::Size { field_id, .. }) = &payload_size_field.map(|f| &f.desc) {
517             // The payload or body has a known size. Consume the
518             // payload and update the span in case fields are placed
519             // after the payload.
520             let size_field = size_field_ident(field_id);
521             self.check_size(&quote!(#size_field ));
522             self.code.push(quote! {
523                 let payload = &#span.get()[..#size_field];
524                 #span.get_mut().advance(#size_field);
525             });
526         } else if offset_from_end == Some(0) {
527             // The payload or body is the last field of a packet,
528             // consume the remaining span.
529             self.code.push(quote! {
530                 let payload = #span.get();
531                 #span.get_mut().advance(payload.len());
532             });
533         } else if let Some(offset_from_end) = offset_from_end {
534             // The payload or body is followed by fields of static
535             // size. Consume the span that is not reserved for the
536             // following fields.
537             assert_eq!(
538                 offset_from_end % 8,
539                 0,
540                 "Payload field offset from end of packet is not a multiple of 8"
541             );
542             let offset_from_end = syn::Index::from(offset_from_end / 8);
543             self.check_size(&quote!(#offset_from_end));
544             self.code.push(quote! {
545                 let payload = &#span.get()[..#span.get().len() - #offset_from_end];
546                 #span.get_mut().advance(payload.len());
547             });
548         }
549 
550         let decl = self.scope.typedef[self.packet_name];
551         if let ast::DeclDesc::Struct { .. } = &decl.desc {
552             self.code.push(quote! {
553                 let payload = Vec::from(payload);
554             });
555         }
556     }
557 
558     /// Parse a single array field element from `span`.
parse_array_element( &self, span: &proc_macro2::Ident, width: Option<usize>, type_id: Option<&str>, decl: Option<&analyzer_ast::Decl>, ) -> proc_macro2::TokenStream559     fn parse_array_element(
560         &self,
561         span: &proc_macro2::Ident,
562         width: Option<usize>,
563         type_id: Option<&str>,
564         decl: Option<&analyzer_ast::Decl>,
565     ) -> proc_macro2::TokenStream {
566         if let Some(width) = width {
567             let get_uint = types::get_uint(self.endianness, width, span);
568             return quote! {
569                 Ok::<_, Error>(#get_uint)
570             };
571         }
572 
573         if let Some(ast::DeclDesc::Enum { id, width, .. }) = decl.map(|decl| &decl.desc) {
574             let get_uint = types::get_uint(self.endianness, *width, span);
575             let type_id = format_ident!("{id}");
576             let packet_name = &self.packet_name;
577             return quote! {
578                 #type_id::try_from(#get_uint).map_err(|_| Error::InvalidEnumValueError {
579                     obj: #packet_name.to_string(),
580                     field: String::new(), // TODO(mgeisler): fill out or remove
581                     value: 0,
582                     type_: #id.to_string(),
583                 })
584             };
585         }
586 
587         let type_id = format_ident!("{}", type_id.unwrap());
588         quote! {
589             #type_id::parse_inner(#span)
590         }
591     }
592 
done(&mut self)593     pub fn done(&mut self) {
594         let decl = self.scope.typedef[self.packet_name];
595         if let ast::DeclDesc::Struct { .. } = &decl.desc {
596             return; // Structs don't parse the child structs recursively.
597         }
598 
599         let packet_scope = &self.scope.scopes[&decl];
600         let children = self.scope.iter_children(self.packet_name).collect::<Vec<_>>();
601         if children.is_empty() && packet_scope.get_payload_field().is_none() {
602             return;
603         }
604 
605         let child_ids = children
606             .iter()
607             .map(|child| format_ident!("{}", child.id().unwrap()))
608             .collect::<Vec<_>>();
609         let child_ids_data = child_ids.iter().map(|ident| format_ident!("{ident}Data"));
610 
611         // Set of field names (sorted by name).
612         let mut constrained_fields = BTreeSet::new();
613         // Maps (child name, field name) -> value.
614         let mut constraint_values = HashMap::new();
615 
616         for child in children.iter() {
617             match &child.desc {
618                 ast::DeclDesc::Packet { id, constraints, .. }
619                 | ast::DeclDesc::Struct { id, constraints, .. } => {
620                     for constraint in constraints.iter() {
621                         constrained_fields.insert(&constraint.id);
622                         constraint_values.insert(
623                             (id.as_str(), &constraint.id),
624                             constraint_to_value(packet_scope, constraint),
625                         );
626                     }
627                 }
628                 _ => unreachable!("Invalid child: {child:?}"),
629             }
630         }
631 
632         let wildcard = quote!(_);
633         let match_values = children.iter().map(|child| {
634             let child_id = child.id().unwrap();
635             let values = constrained_fields.iter().map(|field_name| {
636                 constraint_values.get(&(child_id, field_name)).unwrap_or(&wildcard)
637             });
638             quote! {
639                 (#(#values),*)
640             }
641         });
642         let constrained_field_idents =
643             constrained_fields.iter().map(|field| format_ident!("{field}"));
644         let child_parse_args = children.iter().map(|child| {
645             let fields = find_constrained_parent_fields(self.scope, child.id().unwrap())
646                 .map(|field| format_ident!("{}", field.id().unwrap()));
647             quote!(#(, #fields)*)
648         });
649         let packet_data_child = format_ident!("{}DataChild", self.packet_name);
650         self.code.push(quote! {
651             let child = match (#(#constrained_field_idents),*) {
652                 #(#match_values if #child_ids_data::conforms(&payload) => {
653                     let mut cell = Cell::new(payload);
654                     let child_data = #child_ids_data::parse_inner(&mut cell #child_parse_args)?;
655                     // TODO(mgeisler): communicate back to user if !cell.get().is_empty()?
656                     #packet_data_child::#child_ids(Arc::new(child_data))
657                 }),*
658                 _ if !payload.is_empty() => {
659                     #packet_data_child::Payload(Bytes::copy_from_slice(payload))
660                 }
661                 _ => #packet_data_child::None,
662             };
663         });
664     }
665 }
666 
667 impl quote::ToTokens for FieldParser<'_> {
to_tokens(&self, tokens: &mut proc_macro2::TokenStream)668     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
669         let code = &self.code;
670         tokens.extend(quote! {
671             #(#code)*
672         });
673     }
674 }
675 
676 #[cfg(test)]
677 mod tests {
678     use super::*;
679     use crate::analyzer;
680     use crate::ast;
681     use crate::parser::parse_inline;
682 
683     /// Parse a string fragment as a PDL file.
684     ///
685     /// # Panics
686     ///
687     /// Panics on parse errors.
parse_str(text: &str) -> analyzer_ast::File688     pub fn parse_str(text: &str) -> analyzer_ast::File {
689         let mut db = ast::SourceDatabase::new();
690         let file =
691             parse_inline(&mut db, String::from("stdin"), String::from(text)).expect("parse error");
692         analyzer::analyze(&file).expect("analyzer error")
693     }
694 
695     #[test]
test_find_fields_static()696     fn test_find_fields_static() {
697         let code = "
698               little_endian_packets
699               packet P {
700                 a: 24[3],
701               }
702             ";
703         let file = parse_str(code);
704         let scope = lint::Scope::new(&file);
705         let span = format_ident!("bytes");
706         let parser = FieldParser::new(&scope, file.endianness.value, "P", &span);
707         assert_eq!(parser.find_size_field("a"), None);
708         assert_eq!(parser.find_count_field("a"), None);
709     }
710 
711     #[test]
test_find_fields_dynamic_count()712     fn test_find_fields_dynamic_count() {
713         let code = "
714               little_endian_packets
715               packet P {
716                 _count_(b): 24,
717                 b: 16[],
718               }
719             ";
720         let file = parse_str(code);
721         let scope = lint::Scope::new(&file);
722         let span = format_ident!("bytes");
723         let parser = FieldParser::new(&scope, file.endianness.value, "P", &span);
724         assert_eq!(parser.find_size_field("b"), None);
725         assert_eq!(parser.find_count_field("b"), Some(format_ident!("b_count")));
726     }
727 
728     #[test]
test_find_fields_dynamic_size()729     fn test_find_fields_dynamic_size() {
730         let code = "
731               little_endian_packets
732               packet P {
733                 _size_(c): 8,
734                 c: 24[],
735               }
736             ";
737         let file = parse_str(code);
738         let scope = lint::Scope::new(&file);
739         let span = format_ident!("bytes");
740         let parser = FieldParser::new(&scope, file.endianness.value, "P", &span);
741         assert_eq!(parser.find_size_field("c"), Some(format_ident!("c_size")));
742         assert_eq!(parser.find_count_field("c"), None);
743     }
744 }
745