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("e!(#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("e!(#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("e!(#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("e!(#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("e!(#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("e!(#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