From 1fb992a22087afac7fb55d8c5ca5b711718cf058 Mon Sep 17 00:00:00 2001 From: Henri Chataing <henrichataing@google.com> Date: Tue, 17 Jan 2023 12:14:26 +0100 Subject: [PATCH] Annotate AST nodes with loc, size information Test: cargo build && cargo test Change-Id: Ibede334254e6faa7920139a68747792e87ed05a3 --- tools/pdl/src/ast.rs | 187 ++++---- tools/pdl/src/backends/intermediate.rs | 53 +- tools/pdl/src/backends/json.rs | 4 +- tools/pdl/src/backends/rust.rs | 20 +- tools/pdl/src/backends/rust/declarations.rs | 11 +- tools/pdl/src/backends/rust/parser.rs | 15 +- tools/pdl/src/backends/rust/serializer.rs | 13 +- tools/pdl/src/backends/rust/types.rs | 9 +- .../src/backends/rust_no_allocation/mod.rs | 19 +- .../rust_no_allocation/packet_parser.rs | 57 +-- .../rust_no_allocation/packet_serializer.rs | 53 +- .../src/backends/rust_no_allocation/test.rs | 55 +-- tools/pdl/src/lint.rs | 452 ++++++++++-------- tools/pdl/src/parser.rs | 269 ++++++----- 14 files changed, 652 insertions(+), 565 deletions(-) diff --git a/tools/pdl/src/ast.rs b/tools/pdl/src/ast.rs index e837b2882d5..4e708c04219 100644 --- a/tools/pdl/src/ast.rs +++ b/tools/pdl/src/ast.rs @@ -30,6 +30,11 @@ pub struct SourceRange { pub end: SourceLocation, } +pub trait Annotation: fmt::Debug + Serialize { + type FieldAnnotation: Default + fmt::Debug; + type DeclAnnotation: Default + fmt::Debug; +} + #[derive(Debug, Serialize)] #[serde(tag = "kind", rename = "comment")] pub struct Comment { @@ -70,34 +75,32 @@ pub struct Constraint { #[derive(Debug, Serialize, Clone)] #[serde(tag = "kind")] -pub enum Field { +pub enum FieldDesc { #[serde(rename = "checksum_field")] - Checksum { loc: SourceRange, field_id: String }, + Checksum { field_id: String }, #[serde(rename = "padding_field")] - Padding { loc: SourceRange, size: usize }, + Padding { size: usize }, #[serde(rename = "size_field")] - Size { loc: SourceRange, field_id: String, width: usize }, + Size { field_id: String, width: usize }, #[serde(rename = "count_field")] - Count { loc: SourceRange, field_id: String, width: usize }, + Count { field_id: String, width: usize }, #[serde(rename = "elementsize_field")] - ElementSize { loc: SourceRange, field_id: String, width: usize }, + ElementSize { field_id: String, width: usize }, #[serde(rename = "body_field")] - Body { loc: SourceRange }, + Body, #[serde(rename = "payload_field")] - Payload { loc: SourceRange, size_modifier: Option<String> }, + Payload { size_modifier: Option<String> }, #[serde(rename = "fixed_field")] Fixed { - loc: SourceRange, width: Option<usize>, value: Option<usize>, enum_id: Option<String>, tag_id: Option<String>, }, #[serde(rename = "reserved_field")] - Reserved { loc: SourceRange, width: usize }, + Reserved { width: usize }, #[serde(rename = "array_field")] Array { - loc: SourceRange, id: String, width: Option<usize>, type_id: Option<String>, @@ -105,11 +108,20 @@ pub enum Field { size: Option<usize>, }, #[serde(rename = "scalar_field")] - Scalar { loc: SourceRange, id: String, width: usize }, + Scalar { id: String, width: usize }, #[serde(rename = "typedef_field")] - Typedef { loc: SourceRange, id: String, type_id: String }, + Typedef { id: String, type_id: String }, #[serde(rename = "group_field")] - Group { loc: SourceRange, group_id: String, constraints: Vec<Constraint> }, + Group { group_id: String, constraints: Vec<Constraint> }, +} + +#[derive(Debug, Serialize)] +pub struct Field<A: Annotation> { + pub loc: SourceRange, + #[serde(skip_serializing)] + pub annot: A::FieldAnnotation, + #[serde(flatten)] + pub desc: FieldDesc, } #[derive(Debug, Serialize)] @@ -121,42 +133,49 @@ pub struct TestCase { #[derive(Debug, Serialize)] #[serde(tag = "kind")] -pub enum Decl { +pub enum DeclDesc<A: Annotation> { #[serde(rename = "checksum_declaration")] - Checksum { id: String, loc: SourceRange, function: String, width: usize }, + Checksum { id: String, function: String, width: usize }, #[serde(rename = "custom_field_declaration")] - CustomField { id: String, loc: SourceRange, width: Option<usize>, function: String }, + CustomField { id: String, width: Option<usize>, function: String }, #[serde(rename = "enum_declaration")] - Enum { id: String, loc: SourceRange, tags: Vec<Tag>, width: usize }, + Enum { id: String, tags: Vec<Tag>, width: usize }, #[serde(rename = "packet_declaration")] Packet { id: String, - loc: SourceRange, constraints: Vec<Constraint>, - fields: Vec<Field>, + fields: Vec<Field<A>>, parent_id: Option<String>, }, #[serde(rename = "struct_declaration")] Struct { id: String, - loc: SourceRange, constraints: Vec<Constraint>, - fields: Vec<Field>, + fields: Vec<Field<A>>, parent_id: Option<String>, }, #[serde(rename = "group_declaration")] - Group { id: String, loc: SourceRange, fields: Vec<Field> }, + Group { id: String, fields: Vec<Field<A>> }, #[serde(rename = "test_declaration")] - Test { loc: SourceRange, type_id: String, test_cases: Vec<TestCase> }, + Test { type_id: String, test_cases: Vec<TestCase> }, } #[derive(Debug, Serialize)] -pub struct File { +pub struct Decl<A: Annotation> { + pub loc: SourceRange, + #[serde(skip_serializing)] + pub annot: A::DeclAnnotation, + #[serde(flatten)] + pub desc: DeclDesc<A>, +} + +#[derive(Debug, Serialize)] +pub struct File<A: Annotation> { pub version: String, pub file: FileId, pub comments: Vec<Comment>, pub endianness: Endianness, - pub declarations: Vec<Decl>, + pub declarations: Vec<Decl<A>>, } impl SourceLocation { @@ -213,8 +232,8 @@ impl ops::Add<SourceRange> for SourceRange { } } -impl File { - pub fn new(file: FileId) -> File { +impl<A: Annotation> File<A> { + pub fn new(file: FileId) -> File<A> { File { version: "1,0".to_owned(), comments: vec![], @@ -230,92 +249,68 @@ impl File { } } -impl Decl { - pub fn loc(&self) -> &SourceRange { - match self { - Decl::Checksum { loc, .. } - | Decl::CustomField { loc, .. } - | Decl::Enum { loc, .. } - | Decl::Packet { loc, .. } - | Decl::Struct { loc, .. } - | Decl::Group { loc, .. } - | Decl::Test { loc, .. } => loc, - } - } - +impl<A: Annotation> Decl<A> { pub fn id(&self) -> Option<&str> { - match self { - Decl::Test { .. } => None, - Decl::Checksum { id, .. } - | Decl::CustomField { id, .. } - | Decl::Enum { id, .. } - | Decl::Packet { id, .. } - | Decl::Struct { id, .. } - | Decl::Group { id, .. } => Some(id), + match &self.desc { + DeclDesc::Test { .. } => None, + DeclDesc::Checksum { id, .. } + | DeclDesc::CustomField { id, .. } + | DeclDesc::Enum { id, .. } + | DeclDesc::Packet { id, .. } + | DeclDesc::Struct { id, .. } + | DeclDesc::Group { id, .. } => Some(id), } } -} -impl Field { - pub fn loc(&self) -> &SourceRange { - match self { - Field::Checksum { loc, .. } - | Field::Padding { loc, .. } - | Field::Size { loc, .. } - | Field::ElementSize { loc, .. } - | Field::Count { loc, .. } - | Field::Body { loc, .. } - | Field::Payload { loc, .. } - | Field::Fixed { loc, .. } - | Field::Reserved { loc, .. } - | Field::Array { loc, .. } - | Field::Scalar { loc, .. } - | Field::Typedef { loc, .. } - | Field::Group { loc, .. } => loc, - } + pub fn new(loc: SourceRange, desc: DeclDesc<A>) -> Decl<A> { + Decl { loc, annot: Default::default(), desc } } +} +impl<A: Annotation> Field<A> { pub fn id(&self) -> Option<&str> { - match self { - Field::Checksum { .. } - | Field::Padding { .. } - | Field::Size { .. } - | Field::ElementSize { .. } - | Field::Count { .. } - | Field::Body { .. } - | Field::Payload { .. } - | Field::Fixed { .. } - | Field::Reserved { .. } - | Field::Group { .. } => None, - Field::Array { id, .. } | Field::Scalar { id, .. } | Field::Typedef { id, .. } => { - Some(id) - } + match &self.desc { + FieldDesc::Checksum { .. } + | FieldDesc::Padding { .. } + | FieldDesc::Size { .. } + | FieldDesc::Count { .. } + | FieldDesc::ElementSize { .. } + | FieldDesc::Body + | FieldDesc::Payload { .. } + | FieldDesc::Fixed { .. } + | FieldDesc::Reserved { .. } + | FieldDesc::Group { .. } => None, + FieldDesc::Array { id, .. } + | FieldDesc::Scalar { id, .. } + | FieldDesc::Typedef { id, .. } => Some(id), } } pub fn is_bitfield(&self, scope: &lint::Scope<'_>) -> bool { - match self { - Field::Size { .. } - | Field::Count { .. } - | Field::Fixed { .. } - | Field::Reserved { .. } - | Field::Scalar { .. } => true, - Field::Typedef { type_id, .. } => { + match &self.desc { + FieldDesc::Size { .. } + | FieldDesc::Count { .. } + | FieldDesc::ElementSize { .. } + | FieldDesc::Fixed { .. } + | FieldDesc::Reserved { .. } + | FieldDesc::Scalar { .. } => true, + FieldDesc::Typedef { type_id, .. } => { let field = scope.typedef.get(type_id.as_str()); - matches!(field, Some(Decl::Enum { .. })) + matches!(field, Some(Decl { desc: DeclDesc::Enum { .. }, .. })) } _ => false, } } pub fn width(&self, scope: &lint::Scope<'_>) -> Option<usize> { - match self { - Field::Scalar { width, .. } - | Field::Size { width, .. } - | Field::Count { width, .. } - | Field::Reserved { width, .. } => Some(*width), - Field::Typedef { type_id, .. } => match scope.typedef.get(type_id.as_str()) { - Some(Decl::Enum { width, .. }) => Some(*width), + match &self.desc { + FieldDesc::Scalar { width, .. } + | FieldDesc::Size { width, .. } + | FieldDesc::Count { width, .. } + | FieldDesc::ElementSize { width, .. } + | FieldDesc::Reserved { width, .. } => Some(*width), + FieldDesc::Typedef { type_id, .. } => match scope.typedef.get(type_id.as_str()) { + Some(Decl { desc: DeclDesc::Enum { width, .. }, .. }) => Some(*width), _ => None, }, // TODO(mgeisler): padding, arrays, etc. diff --git a/tools/pdl/src/backends/intermediate.rs b/tools/pdl/src/backends/intermediate.rs index 67b506cf406..1ef6acab6da 100644 --- a/tools/pdl/src/backends/intermediate.rs +++ b/tools/pdl/src/backends/intermediate.rs @@ -1,6 +1,7 @@ use std::collections::{hash_map::Entry, HashMap}; use crate::ast; +use crate::parser; pub struct Schema<'a> { pub packets_and_structs: HashMap<&'a str, PacketOrStruct<'a>>, @@ -82,7 +83,7 @@ pub enum ComputedOffset<'a> { Alias(ComputedOffsetId<'a>), } -pub fn generate(file: &ast::File) -> Result<Schema, String> { +pub fn generate(file: &parser::ast::File) -> Result<Schema, String> { let mut schema = Schema { packets_and_structs: HashMap::new(), enums: HashMap::new() }; match file.endianness.value { ast::EndiannessValue::LittleEndian => {} @@ -96,13 +97,13 @@ pub fn generate(file: &ast::File) -> Result<Schema, String> { Ok(schema) } -fn process_decl<'a>(schema: &mut Schema<'a>, decl: &'a ast::Decl) { - match decl { - ast::Decl::Enum { id, tags, width, .. } => process_enum(schema, id, tags, *width), - ast::Decl::Packet { id, fields, .. } | ast::Decl::Struct { id, fields, .. } => { +fn process_decl<'a>(schema: &mut Schema<'a>, decl: &'a parser::ast::Decl) { + match &decl.desc { + ast::DeclDesc::Enum { id, tags, width, .. } => process_enum(schema, id, tags, *width), + ast::DeclDesc::Packet { id, fields, .. } | ast::DeclDesc::Struct { id, fields, .. } => { process_packet_or_struct(schema, id, fields) } - ast::Decl::Group { .. } => todo!(), + ast::DeclDesc::Group { .. } => todo!(), _ => unimplemented!("type {decl:?} not supported"), } } @@ -119,11 +120,18 @@ fn process_enum<'a>(schema: &mut Schema<'a>, id: &'a str, tags: &'a [ast::Tag], ); } -fn process_packet_or_struct<'a>(schema: &mut Schema<'a>, id: &'a str, fields: &'a [ast::Field]) { +fn process_packet_or_struct<'a>( + schema: &mut Schema<'a>, + id: &'a str, + fields: &'a [parser::ast::Field], +) { schema.packets_and_structs.insert(id, compute_getters(schema, fields)); } -fn compute_getters<'a>(schema: &Schema<'a>, fields: &'a [ast::Field]) -> PacketOrStruct<'a> { +fn compute_getters<'a>( + schema: &Schema<'a>, + fields: &'a [parser::ast::Field], +) -> PacketOrStruct<'a> { let mut prev_pos_id = None; let mut curr_pos_id = ComputedOffsetId::HeaderStart; let mut computed_values = HashMap::new(); @@ -142,16 +150,16 @@ fn compute_getters<'a>(schema: &Schema<'a>, fields: &'a [ast::Field]) -> PacketO // populate this only if we are an array with a knowable size let mut next_prev_pos_id = None; - let next_pos = match field { - ast::Field::Reserved { width, .. } => { + let next_pos = match &field.desc { + ast::FieldDesc::Reserved { width } => { ComputedOffset::ConstantPlusOffsetInBits(curr_pos_id, *width as i64) } - ast::Field::Scalar { id, width, .. } => { + ast::FieldDesc::Scalar { id, width } => { computed_offsets .insert(ComputedOffsetId::FieldOffset(id), ComputedOffset::Alias(curr_pos_id)); ComputedOffset::ConstantPlusOffsetInBits(curr_pos_id, *width as i64) } - ast::Field::Fixed { width, enum_id, .. } => { + ast::FieldDesc::Fixed { width, enum_id, .. } => { let offset = match (width, enum_id) { (Some(width), _) => *width, (_, Some(enum_id)) => schema.enums[enum_id.as_str()].width, @@ -159,32 +167,32 @@ fn compute_getters<'a>(schema: &Schema<'a>, fields: &'a [ast::Field]) -> PacketO }; ComputedOffset::ConstantPlusOffsetInBits(curr_pos_id, offset as i64) } - ast::Field::Size { field_id, width, .. } => { + ast::FieldDesc::Size { field_id, width } => { computed_values.insert( ComputedValueId::FieldSize(field_id), ComputedValue::ValueAt { offset: curr_pos_id, width: *width }, ); ComputedOffset::ConstantPlusOffsetInBits(curr_pos_id, *width as i64) } - ast::Field::Count { field_id, width, .. } => { + ast::FieldDesc::Count { field_id, width } => { computed_values.insert( ComputedValueId::FieldCount(field_id.as_str()), ComputedValue::ValueAt { offset: curr_pos_id, width: *width }, ); ComputedOffset::ConstantPlusOffsetInBits(curr_pos_id, *width as i64) } - ast::Field::ElementSize { field_id, width, .. } => { + ast::FieldDesc::ElementSize { field_id, width } => { computed_values.insert( ComputedValueId::FieldElementSize(field_id), ComputedValue::ValueAt { offset: curr_pos_id, width: *width }, ); ComputedOffset::ConstantPlusOffsetInBits(curr_pos_id, *width as i64) } - ast::Field::Group { .. } => { + ast::FieldDesc::Group { .. } => { unimplemented!("this should be removed by the linter...") } - ast::Field::Checksum { .. } => unimplemented!("checksum not supported"), - ast::Field::Body { .. } => { + ast::FieldDesc::Checksum { .. } => unimplemented!("checksum not supported"), + ast::FieldDesc::Body => { computed_offsets.insert( ComputedOffsetId::FieldOffset("_body_"), ComputedOffset::Alias(curr_pos_id), @@ -202,7 +210,7 @@ fn compute_getters<'a>(schema: &Schema<'a>, fields: &'a [ast::Field]) -> PacketO computed_offsets.insert(ComputedOffsetId::FieldEndOffset("_body_"), end_offset); end_offset } - ast::Field::Payload { size_modifier, .. } => { + ast::FieldDesc::Payload { size_modifier } => { if size_modifier.is_some() { unimplemented!("size modifiers not supported") } @@ -223,13 +231,12 @@ fn compute_getters<'a>(schema: &Schema<'a>, fields: &'a [ast::Field]) -> PacketO computed_offsets.insert(ComputedOffsetId::FieldEndOffset("_payload_"), end_offset); end_offset } - ast::Field::Array { + ast::FieldDesc::Array { id, width, type_id, size_modifier, size: statically_known_count, - .. } => { if size_modifier.is_some() { unimplemented!("size modifiers not supported") @@ -395,14 +402,14 @@ fn compute_getters<'a>(schema: &Schema<'a>, fields: &'a [ast::Field]) -> PacketO ComputedOffset::Alias(ComputedOffsetId::TrailerStart) } } - ast::Field::Padding { size, .. } => { + ast::FieldDesc::Padding { size } => { if let Some(prev_pos_id) = prev_pos_id { ComputedOffset::ConstantPlusOffsetInBits(prev_pos_id, *size as i64) } else { panic!("padding must follow array field with known total size") } } - ast::Field::Typedef { id, type_id, .. } => { + ast::FieldDesc::Typedef { id, type_id } => { computed_offsets .insert(ComputedOffsetId::FieldOffset(id), ComputedOffset::Alias(curr_pos_id)); diff --git a/tools/pdl/src/backends/json.rs b/tools/pdl/src/backends/json.rs index f113cbc5608..edf38d355df 100644 --- a/tools/pdl/src/backends/json.rs +++ b/tools/pdl/src/backends/json.rs @@ -1,9 +1,9 @@ //! Rust compiler backend. -use crate::ast; +use crate::parser; /// Turn the AST into a JSON representation. -pub fn generate(file: &ast::File) -> Result<String, String> { +pub fn generate(file: &parser::ast::File) -> Result<String, String> { serde_json::to_string_pretty(&file) .map_err(|err| format!("could not JSON serialize grammar: {err}")) } diff --git a/tools/pdl/src/backends/rust.rs b/tools/pdl/src/backends/rust.rs index 9ed3746fcdd..97408c4b3c2 100644 --- a/tools/pdl/src/backends/rust.rs +++ b/tools/pdl/src/backends/rust.rs @@ -12,6 +12,8 @@ use crate::{ast, lint}; use quote::{format_ident, quote}; use std::path::Path; +use crate::parser::ast as parser_ast; + mod declarations; mod parser; mod preamble; @@ -49,7 +51,7 @@ fn generate_packet_decl( // Packet: id: &str, _constraints: &[ast::Constraint], - fields: &[ast::Field], + fields: &[parser_ast::Field], _parent_id: Option<&str>, ) -> proc_macro2::TokenStream { // TODO(mgeisler): use the convert_case crate to convert between @@ -237,10 +239,14 @@ fn generate_enum_decl(id: &str, tags: &[ast::Tag]) -> proc_macro2::TokenStream { } } -fn generate_decl(scope: &lint::Scope<'_>, file: &ast::File, decl: &ast::Decl) -> String { - match decl { - ast::Decl::Packet { id, constraints, fields, parent_id, .. } - | ast::Decl::Struct { id, constraints, fields, parent_id, .. } => generate_packet_decl( +fn generate_decl( + scope: &lint::Scope<'_>, + file: &parser_ast::File, + decl: &parser_ast::Decl, +) -> String { + match &decl.desc { + ast::DeclDesc::Packet { id, constraints, fields, parent_id, .. } + | ast::DeclDesc::Struct { id, constraints, fields, parent_id, .. } => generate_packet_decl( scope, file.endianness.value, id, @@ -249,7 +255,7 @@ fn generate_decl(scope: &lint::Scope<'_>, file: &ast::File, decl: &ast::Decl) -> parent_id.as_deref(), ) .to_string(), - ast::Decl::Enum { id, tags, .. } => generate_enum_decl(id, tags).to_string(), + ast::DeclDesc::Enum { id, tags, .. } => generate_enum_decl(id, tags).to_string(), _ => todo!("unsupported Decl::{:?}", decl), } } @@ -258,7 +264,7 @@ fn generate_decl(scope: &lint::Scope<'_>, file: &ast::File, decl: &ast::Decl) -> /// /// The code is not formatted, pipe it through `rustfmt` to get /// readable source code. -pub fn generate(sources: &ast::SourceDatabase, file: &ast::File) -> String { +pub fn generate(sources: &ast::SourceDatabase, file: &parser_ast::File) -> String { let mut code = String::new(); let source = sources.get(file.file).expect("could not read source"); diff --git a/tools/pdl/src/backends/rust/declarations.rs b/tools/pdl/src/backends/rust/declarations.rs index 8120c5174a9..d332d3034a0 100644 --- a/tools/pdl/src/backends/rust/declarations.rs +++ b/tools/pdl/src/backends/rust/declarations.rs @@ -1,5 +1,6 @@ use crate::ast; use crate::backends::rust::types; +use crate::parser::ast as parser_ast; use quote::{format_ident, quote}; pub struct FieldDeclarations { @@ -11,23 +12,23 @@ impl FieldDeclarations { FieldDeclarations { code: Vec::new() } } - pub fn add(&mut self, field: &ast::Field) { - self.code.push(match field { - ast::Field::Scalar { id, width, .. } => { + pub fn add(&mut self, field: &parser_ast::Field) { + self.code.push(match &field.desc { + ast::FieldDesc::Scalar { id, width } => { let id = format_ident!("{id}"); let field_type = types::Integer::new(*width); quote! { #id: #field_type, } } - ast::Field::Typedef { id, type_id, .. } => { + ast::FieldDesc::Typedef { id, type_id } => { let id = format_ident!("{id}"); let field_type = format_ident!("{type_id}"); quote! { #id: #field_type, } } - ast::Field::Reserved { .. } => { + ast::FieldDesc::Reserved { .. } => { // Nothing to do here. quote! {} } diff --git a/tools/pdl/src/backends/rust/parser.rs b/tools/pdl/src/backends/rust/parser.rs index f9091c02435..877b49f777c 100644 --- a/tools/pdl/src/backends/rust/parser.rs +++ b/tools/pdl/src/backends/rust/parser.rs @@ -1,11 +1,12 @@ use crate::backends::rust::{mask_bits, types}; +use crate::parser::ast as parser_ast; use crate::{ast, lint}; use quote::{format_ident, quote}; /// A single bit-field. struct BitField<'a> { shift: usize, // The shift to apply to this field. - field: &'a ast::Field, + field: &'a parser_ast::Field, } pub struct FieldParser<'a> { @@ -38,7 +39,7 @@ impl<'a> FieldParser<'a> { } } - pub fn add(&mut self, field: &'a ast::Field) { + pub fn add(&mut self, field: &'a parser_ast::Field) { if field.is_bitfield(self.scope) { self.add_bit_field(field); return; @@ -47,7 +48,7 @@ impl<'a> FieldParser<'a> { todo!("not yet supported: {field:?}") } - fn add_bit_field(&mut self, field: &'a ast::Field) { + fn add_bit_field(&mut self, field: &'a parser_ast::Field) { self.chunk.push(BitField { shift: self.shift, field }); self.shift += field.width(self.scope).unwrap(); if self.shift % 8 != 0 { @@ -112,14 +113,14 @@ impl<'a> FieldParser<'a> { v = quote! { #v as #value_type }; } - self.code.push(match field { - ast::Field::Scalar { id, .. } => { + self.code.push(match &field.desc { + ast::FieldDesc::Scalar { id, .. } => { let id = format_ident!("{id}"); quote! { let #id = #v; } } - ast::Field::Typedef { id, type_id, .. } => { + ast::FieldDesc::Typedef { id, type_id } => { let id = format_ident!("{id}"); let type_id = format_ident!("{type_id}"); let from_u = format_ident!("from_u{}", value_type.width); @@ -130,7 +131,7 @@ impl<'a> FieldParser<'a> { let #id = #type_id::#from_u(#v).unwrap(); } } - ast::Field::Reserved { .. } => { + ast::FieldDesc::Reserved { .. } => { // Nothing to do here. quote! {} } diff --git a/tools/pdl/src/backends/rust/serializer.rs b/tools/pdl/src/backends/rust/serializer.rs index 5fd5fb13032..a57ba41f4a2 100644 --- a/tools/pdl/src/backends/rust/serializer.rs +++ b/tools/pdl/src/backends/rust/serializer.rs @@ -1,4 +1,5 @@ use crate::backends::rust::{mask_bits, types}; +use crate::parser::ast as parser_ast; use crate::{ast, lint}; use quote::{format_ident, quote}; @@ -36,7 +37,7 @@ impl<'a> FieldSerializer<'a> { } } - pub fn add(&mut self, field: &ast::Field) { + pub fn add(&mut self, field: &parser_ast::Field) { if field.is_bitfield(self.scope) { self.add_bit_field(field); return; @@ -45,11 +46,11 @@ impl<'a> FieldSerializer<'a> { todo!("not yet supported: {field:?}") } - fn add_bit_field(&mut self, field: &ast::Field) { + fn add_bit_field(&mut self, field: &parser_ast::Field) { let width = field.width(self.scope).unwrap(); - match field { - ast::Field::Scalar { id, width, .. } => { + match &field.desc { + ast::FieldDesc::Scalar { id, width } => { let field_name = format_ident!("{id}"); let field_type = types::Integer::new(*width); if field_type.width > *width { @@ -66,7 +67,7 @@ impl<'a> FieldSerializer<'a> { } self.chunk.push(BitField { value: quote!(self.#field_name), shift: self.shift }); } - ast::Field::Typedef { id, .. } => { + ast::FieldDesc::Typedef { id, .. } => { let field_name = format_ident!("{id}"); let field_type = types::Integer::new(width); let to_u = format_ident!("to_u{}", field_type.width); @@ -77,7 +78,7 @@ impl<'a> FieldSerializer<'a> { shift: self.shift, }); } - ast::Field::Reserved { .. } => { + ast::FieldDesc::Reserved { .. } => { // Nothing to do here. } _ => todo!(), diff --git a/tools/pdl/src/backends/rust/types.rs b/tools/pdl/src/backends/rust/types.rs index 55d2f28046a..259a1e6387f 100644 --- a/tools/pdl/src/backends/rust/types.rs +++ b/tools/pdl/src/backends/rust/types.rs @@ -1,6 +1,7 @@ //! Utility functions for dealing with Rust integer types. use crate::ast; +use crate::parser::ast as parser_ast; use quote::{format_ident, quote}; /// A Rust integer type such as `u8`. @@ -33,13 +34,13 @@ impl quote::ToTokens for Integer { } } -pub fn rust_type(field: &ast::Field) -> proc_macro2::TokenStream { - match field { - ast::Field::Scalar { width, .. } => { +pub fn rust_type(field: &parser_ast::Field) -> proc_macro2::TokenStream { + match &field.desc { + ast::FieldDesc::Scalar { width, .. } => { let field_type = Integer::new(*width); quote!(#field_type) } - ast::Field::Typedef { type_id, .. } => { + ast::FieldDesc::Typedef { type_id, .. } => { let field_type = format_ident!("{type_id}"); quote!(#field_type) } diff --git a/tools/pdl/src/backends/rust_no_allocation/mod.rs b/tools/pdl/src/backends/rust_no_allocation/mod.rs index de5dd800d92..a84f43d3bc7 100644 --- a/tools/pdl/src/backends/rust_no_allocation/mod.rs +++ b/tools/pdl/src/backends/rust_no_allocation/mod.rs @@ -23,6 +23,7 @@ use proc_macro2::TokenStream; use quote::quote; use crate::ast; +use crate::parser; use self::{ enums::generate_enum, packet_parser::generate_packet, @@ -31,7 +32,7 @@ use self::{ use super::intermediate::Schema; -pub fn generate(file: &ast::File, schema: &Schema) -> Result<String, String> { +pub fn generate(file: &parser::ast::File, schema: &Schema) -> Result<String, String> { match file.endianness.value { ast::EndiannessValue::LittleEndian => {} _ => unimplemented!("Only little_endian endianness supported"), @@ -43,9 +44,9 @@ pub fn generate(file: &ast::File, schema: &Schema) -> Result<String, String> { let mut children = HashMap::<&str, Vec<&str>>::new(); for decl in &file.declarations { - match decl { - ast::Decl::Packet { id, parent_id: Some(parent_id), .. } - | ast::Decl::Struct { id, parent_id: Some(parent_id), .. } => { + match &decl.desc { + ast::DeclDesc::Packet { id, parent_id: Some(parent_id), .. } + | ast::DeclDesc::Struct { id, parent_id: Some(parent_id), .. } => { children.entry(parent_id.as_str()).or_default().push(id.as_str()); } _ => {} @@ -69,14 +70,14 @@ pub fn generate(file: &ast::File, schema: &Schema) -> Result<String, String> { } fn generate_decl( - decl: &ast::Decl, + decl: &parser::ast::Decl, schema: &Schema, children: &HashMap<&str, Vec<&str>>, ) -> Result<TokenStream, String> { - match decl { - ast::Decl::Enum { id, tags, width, .. } => Ok(generate_enum(id, tags, *width)), - ast::Decl::Packet { id, fields, parent_id, .. } - | ast::Decl::Struct { id, fields, parent_id, .. } => { + match &decl.desc { + ast::DeclDesc::Enum { id, tags, width, .. } => Ok(generate_enum(id, tags, *width)), + ast::DeclDesc::Packet { id, fields, parent_id, .. } + | ast::DeclDesc::Struct { id, fields, parent_id, .. } => { let parser = generate_packet( id, fields, diff --git a/tools/pdl/src/backends/rust_no_allocation/packet_parser.rs b/tools/pdl/src/backends/rust_no_allocation/packet_parser.rs index 8fbf9b12e83..2382131fdee 100644 --- a/tools/pdl/src/backends/rust_no_allocation/packet_parser.rs +++ b/tools/pdl/src/backends/rust_no_allocation/packet_parser.rs @@ -4,6 +4,7 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote}; use crate::ast; +use crate::parser; use crate::backends::intermediate::{ ComputedOffsetId, ComputedValueId, PacketOrStruct, PacketOrStructLength, Schema, @@ -14,7 +15,7 @@ use super::utils::get_integer_type; pub fn generate_packet( id: &str, - fields: &[ast::Field], + fields: &[parser::ast::Field], parent_id: Option<&str>, schema: &Schema, curr_schema: &PacketOrStruct, @@ -38,22 +39,22 @@ pub fn generate_packet( ); let field_getters = fields.iter().map(|field| { - match field { - ast::Field::Padding { .. } - | ast::Field::Reserved { .. } - | ast::Field::Fixed { .. } - | ast::Field::ElementSize { .. } - | ast::Field::Count { .. } - | ast::Field::Size { .. } => { + match &field.desc { + ast::FieldDesc::Padding { .. } + | ast::FieldDesc::Reserved { .. } + | ast::FieldDesc::Fixed { .. } + | ast::FieldDesc::ElementSize { .. } + | ast::FieldDesc::Count { .. } + | ast::FieldDesc::Size { .. } => { // no-op, no getter generated for this type quote! {} } - ast::Field::Group { .. } => unreachable!(), - ast::Field::Checksum { .. } => { + ast::FieldDesc::Group { .. } => unreachable!(), + ast::FieldDesc::Checksum { .. } => { unimplemented!("checksums not yet supported with this backend") } - ast::Field::Payload { .. } | ast::Field::Body { .. } => { - let name = if matches!(field, ast::Field::Payload { .. }) { "_payload_"} else { "_body_"}; + ast::FieldDesc::Payload { .. } | ast::FieldDesc::Body => { + let name = if matches!(field.desc, ast::FieldDesc::Payload { .. }) { "_payload_"} else { "_body_"}; let payload_start_offset = ComputedOffsetId::FieldOffset(name).call_fn(); let payload_end_offset = ComputedOffsetId::FieldEndOffset(name).call_fn(); quote! { @@ -74,7 +75,7 @@ pub fn generate_packet( } } } - ast::Field::Array { id, width, type_id, .. } => { + ast::FieldDesc::Array { id, width, type_id, .. } => { let (elem_type, return_type) = if let Some(width) = width { let ident = get_integer_type(*width); (ident.clone(), quote!{ #ident }) @@ -140,7 +141,7 @@ pub fn generate_packet( } } } - ast::Field::Scalar { id, width, .. } => { + ast::FieldDesc::Scalar { id, width } => { let try_getter_name = format_ident!("try_get_{id}"); let getter_name = format_ident!("get_{id}"); let offset = ComputedOffsetId::FieldOffset(id).call_fn(); @@ -156,7 +157,7 @@ pub fn generate_packet( } } } - ast::Field::Typedef { id, type_id, .. } => { + ast::FieldDesc::Typedef { id, type_id } => { let try_getter_name = format_ident!("try_get_{id}"); let getter_name = format_ident!("get_{id}"); @@ -213,25 +214,25 @@ pub fn generate_packet( quote! { parent } }; - let field_validators = fields.iter().map(|field| match field { - ast::Field::Checksum { .. } => unimplemented!(), - ast::Field::Group { .. } => unreachable!(), - ast::Field::Padding { .. } - | ast::Field::Size { .. } - | ast::Field::Count { .. } - | ast::Field::ElementSize { .. } - | ast::Field::Body { .. } - | ast::Field::Fixed { .. } - | ast::Field::Reserved { .. } => { + let field_validators = fields.iter().map(|field| match &field.desc { + ast::FieldDesc::Checksum { .. } => unimplemented!(), + ast::FieldDesc::Group { .. } => unreachable!(), + ast::FieldDesc::Padding { .. } + | ast::FieldDesc::Size { .. } + | ast::FieldDesc::Count { .. } + | ast::FieldDesc::ElementSize { .. } + | ast::FieldDesc::Body + | ast::FieldDesc::Fixed { .. } + | ast::FieldDesc::Reserved { .. } => { quote! {} } - ast::Field::Payload { .. } => { + ast::FieldDesc::Payload { .. } => { quote! { self.try_get_payload()?; self.try_get_raw_payload()?; } } - ast::Field::Array { id, .. } => { + ast::FieldDesc::Array { id, .. } => { let iter_ident = format_ident!("try_get_{id}_iter"); quote! { for elem in self.#iter_ident()? { @@ -239,7 +240,7 @@ pub fn generate_packet( } } } - ast::Field::Scalar { id, .. } | ast::Field::Typedef { id, .. } => { + ast::FieldDesc::Scalar { id, .. } | ast::FieldDesc::Typedef { id, .. } => { let getter_ident = format_ident!("try_get_{id}"); quote! { self.#getter_ident()?; } } diff --git a/tools/pdl/src/backends/rust_no_allocation/packet_serializer.rs b/tools/pdl/src/backends/rust_no_allocation/packet_serializer.rs index d4d14e695d2..55071cdb22f 100644 --- a/tools/pdl/src/backends/rust_no_allocation/packet_serializer.rs +++ b/tools/pdl/src/backends/rust_no_allocation/packet_serializer.rs @@ -9,6 +9,7 @@ use crate::{ intermediate::{ComputedValue, ComputedValueId, PacketOrStruct, Schema}, rust_no_allocation::utils::get_integer_type, }, + parser, }; fn standardize_child(id: &str) -> &str { @@ -21,7 +22,7 @@ fn standardize_child(id: &str) -> &str { pub fn generate_packet_serializer( id: &str, parent_id: Option<&str>, - fields: &[ast::Field], + fields: &[parser::ast::Field], schema: &Schema, curr_schema: &PacketOrStruct, children: &HashMap<&str, Vec<&str>>, @@ -31,25 +32,25 @@ pub fn generate_packet_serializer( let builder_fields = fields .iter() .filter_map(|field| { - match field { - ast::Field::Padding { .. } - | ast::Field::Reserved { .. } - | ast::Field::Fixed { .. } - | ast::Field::ElementSize { .. } - | ast::Field::Count { .. } - | ast::Field::Size { .. } => { + match &field.desc { + ast::FieldDesc::Padding { .. } + | ast::FieldDesc::Reserved { .. } + | ast::FieldDesc::Fixed { .. } + | ast::FieldDesc::ElementSize { .. } + | ast::FieldDesc::Count { .. } + | ast::FieldDesc::Size { .. } => { // no-op, no getter generated for this type None } - ast::Field::Group { .. } => unreachable!(), - ast::Field::Checksum { .. } => { + ast::FieldDesc::Group { .. } => unreachable!(), + ast::FieldDesc::Checksum { .. } => { unimplemented!("checksums not yet supported with this backend") } - ast::Field::Body { .. } | ast::Field::Payload { .. } => { + ast::FieldDesc::Body | ast::FieldDesc::Payload { .. } => { let type_ident = format_ident!("{id}Child"); Some(("_child_", quote! { #type_ident })) } - ast::Field::Array { id, width, type_id, .. } => { + ast::FieldDesc::Array { id, width, type_id, .. } => { let element_type = if let Some(width) = width { get_integer_type(*width) } else if let Some(type_id) = type_id { @@ -63,11 +64,11 @@ pub fn generate_packet_serializer( }; Some((id.as_str(), quote! { Box<[#element_type]> })) } - ast::Field::Scalar { id, width, .. } => { + ast::FieldDesc::Scalar { id, width } => { let id_type = get_integer_type(*width); Some((id.as_str(), quote! { #id_type })) } - ast::Field::Typedef { id, type_id, .. } => { + ast::FieldDesc::Typedef { id, type_id } => { let type_ident = if schema.enums.contains_key(type_id.as_str()) { format_ident!("{type_id}") } else { @@ -85,9 +86,9 @@ pub fn generate_packet_serializer( let mut has_child = false; let serializer = fields.iter().map(|field| { - match field { - ast::Field::Checksum { .. } | ast::Field::Group { .. } => unimplemented!(), - ast::Field::Padding { size, .. } => { + match &field.desc { + ast::FieldDesc::Checksum { .. } | ast::FieldDesc::Group { .. } => unimplemented!(), + ast::FieldDesc::Padding { size, .. } => { quote! { if (most_recent_array_size_in_bits > #size * 8) { return Err(SerializeError::NegativePadding); @@ -95,7 +96,7 @@ pub fn generate_packet_serializer( writer.write_bits((#size * 8 - most_recent_array_size_in_bits) as usize, || Ok(0u64))?; } }, - ast::Field::Size { field_id, width, .. } => { + ast::FieldDesc::Size { field_id, width } => { let field_id = standardize_child(field_id); let field_ident = format_ident!("{field_id}"); @@ -137,11 +138,11 @@ pub fn generate_packet_serializer( })?; } } - ast::Field::Count { field_id, width, .. } => { + ast::FieldDesc::Count { field_id, width } => { let field_ident = format_ident!("{field_id}"); quote! { writer.write_bits(#width, || u64::try_from(self.#field_ident.len()).or(Err(SerializeError::IntegerConversionFailure)))?; } } - ast::Field::ElementSize { field_id, width, .. } => { + ast::FieldDesc::ElementSize { field_id, width } => { // TODO(aryarahul) - add validation for elementsize against all the other elements let field_ident = format_ident!("{field_id}"); quote! { @@ -158,14 +159,14 @@ pub fn generate_packet_serializer( writer.write_bits(#width, || get_element_size() )?; } } - ast::Field::Reserved { width, .. } => { + ast::FieldDesc::Reserved { width, .. } => { quote!{ writer.write_bits(#width, || Ok(0u64))?; } } - ast::Field::Scalar { width, id, .. } => { + ast::FieldDesc::Scalar { width, id } => { let field_ident = format_ident!("{id}"); quote! { writer.write_bits(#width, || Ok(self.#field_ident))?; } } - ast::Field::Fixed { width, enum_id, value, tag_id, .. } => { + ast::FieldDesc::Fixed { width, enum_id, value, tag_id } => { let width = if let Some(width) = width { quote! { #width } } else if let Some(enum_id) = enum_id { @@ -186,11 +187,11 @@ pub fn generate_packet_serializer( }; quote!{ writer.write_bits(#width, || Ok(#value))?; } } - ast::Field::Body { .. } | ast::Field::Payload { .. } => { + ast::FieldDesc::Body | ast::FieldDesc::Payload { .. } => { has_child = true; quote! { self._child_.serialize(writer)?; } } - ast::Field::Array { width, id, .. } => { + ast::FieldDesc::Array { width, id, .. } => { let id_ident = format_ident!("{id}"); if let Some(width) = width { quote! { @@ -209,7 +210,7 @@ pub fn generate_packet_serializer( } } } - ast::Field::Typedef { id, .. } => { + ast::FieldDesc::Typedef { id, .. } => { let id_ident = format_ident!("{id}"); quote! { self.#id_ident.serialize(writer)?; } } diff --git a/tools/pdl/src/backends/rust_no_allocation/test.rs b/tools/pdl/src/backends/rust_no_allocation/test.rs index 49851fba337..f6bc989fe9e 100644 --- a/tools/pdl/src/backends/rust_no_allocation/test.rs +++ b/tools/pdl/src/backends/rust_no_allocation/test.rs @@ -203,33 +203,34 @@ pub fn generate_test_file() -> Result<String, String> { let pdl = include_str!("../../../tests/canonical/le_rust_noalloc_test_file.pdl"); let ast = parse_inline(&mut ast::SourceDatabase::new(), "test.pdl".to_owned(), pdl.to_owned()) .expect("could not parse reference PDL"); - let packet_lookup = ast - .declarations - .iter() - .filter_map(|decl| match decl { - ast::Decl::Packet { id, fields, .. } | ast::Decl::Struct { id, fields, .. } => Some(( - id.as_str(), - fields - .iter() - .filter_map(|field| match field { - ast::Field::Body { .. } | ast::Field::Payload { .. } => { - Some(("payload", None)) - } - ast::Field::Array { id, type_id, .. } => match type_id { - Some(type_id) => Some((id.as_str(), Some(type_id.as_str()))), - None => Some((id.as_str(), None)), - }, - ast::Field::Typedef { id, type_id, .. } => { - Some((id.as_str(), Some(type_id.as_str()))) - } - ast::Field::Scalar { id, .. } => Some((id.as_str(), None)), - _ => None, - }) - .collect::<HashMap<_, _>>(), - )), - _ => None, - }) - .collect::<HashMap<_, _>>(); + let packet_lookup = + ast.declarations + .iter() + .filter_map(|decl| match &decl.desc { + ast::DeclDesc::Packet { id, fields, .. } + | ast::DeclDesc::Struct { id, fields, .. } => Some(( + id.as_str(), + fields + .iter() + .filter_map(|field| match &field.desc { + ast::FieldDesc::Body { .. } | ast::FieldDesc::Payload { .. } => { + Some(("payload", None)) + } + ast::FieldDesc::Array { id, type_id, .. } => match type_id { + Some(type_id) => Some((id.as_str(), Some(type_id.as_str()))), + None => Some((id.as_str(), None)), + }, + ast::FieldDesc::Typedef { id, type_id, .. } => { + Some((id.as_str(), Some(type_id.as_str()))) + } + ast::FieldDesc::Scalar { id, .. } => Some((id.as_str(), None)), + _ => None, + }) + .collect::<HashMap<_, _>>(), + )), + _ => None, + }) + .collect::<HashMap<_, _>>(); for PacketTest { packet, tests } in test_vectors.iter() { if !pdl.contains(packet) { diff --git a/tools/pdl/src/lint.rs b/tools/pdl/src/lint.rs index f0234ad6503..1b083b9257b 100644 --- a/tools/pdl/src/lint.rs +++ b/tools/pdl/src/lint.rs @@ -5,7 +5,41 @@ use codespan_reporting::term::termcolor; use std::collections::HashMap; use std::ptr; -use crate::ast::*; +use crate::{ast::*, parser}; + +pub mod ast { + use serde::Serialize; + + // Field and declaration size information. + #[derive(Default, Debug)] + #[allow(unused)] + pub enum Size { + // Constant size in bits. + Static(usize), + // Size indicated at packet parsing by + // a size or count field. + Dynamic, + // The size cannot be determined statically or at runtime. + // The packet assumes the largest possible size. + #[default] + Unknown, + } + + #[derive(Debug, Serialize)] + pub struct Annotation(); + + impl crate::ast::Annotation for Annotation { + type FieldAnnotation = Size; + type DeclAnnotation = Size; + } + + #[allow(unused)] + pub type Field = crate::ast::Field<Annotation>; + #[allow(unused)] + pub type Decl = crate::ast::Decl<Annotation>; + #[allow(unused)] + pub type File = crate::ast::File<Annotation>; +} /// Aggregate linter diagnostics. #[derive(Debug)] @@ -24,57 +58,57 @@ pub trait Lintable { #[derive(Debug)] pub struct Scope<'d> { // Collection of Group, Packet, Enum, Struct, Checksum, and CustomField declarations. - pub typedef: HashMap<String, &'d Decl>, + pub typedef: HashMap<String, &'d parser::ast::Decl>, // Collection of Packet, Struct, and Group scope declarations. - pub scopes: HashMap<&'d Decl, PacketScope<'d>>, + pub scopes: HashMap<&'d parser::ast::Decl, PacketScope<'d>>, } /// Gather information about a Packet, Struct, or Group declaration. #[derive(Debug)] pub struct PacketScope<'d> { // Checksum starts, indexed by the checksum field id. - checksums: HashMap<String, &'d Field>, + checksums: HashMap<String, &'d parser::ast::Field>, // Size or count fields, indexed by the field id. - sizes: HashMap<String, &'d Field>, + sizes: HashMap<String, &'d parser::ast::Field>, // Payload or body field. - payload: Option<&'d Field>, + payload: Option<&'d parser::ast::Field>, // Typedef, scalar, array fields. - named: HashMap<String, &'d Field>, + named: HashMap<String, &'d parser::ast::Field>, // Group fields. - groups: HashMap<String, &'d Field>, + groups: HashMap<String, &'d parser::ast::Field>, // Flattened field declarations. // Contains field declarations from the original Packet, Struct, or Group, // where Group fields have been substituted by their body. // Constrained Scalar or Typedef Group fields are substituted by a Fixed // field. - fields: Vec<&'d Field>, + fields: Vec<&'d parser::ast::Field>, // Constraint declarations gathered from Group inlining. constraints: HashMap<String, &'d Constraint>, // Local and inherited field declarations. Only named fields are preserved. // Saved here for reference for parent constraint resolving. - all_fields: HashMap<String, &'d Field>, + all_fields: HashMap<String, &'d parser::ast::Field>, // Local and inherited constraint declarations. // Saved here for constraint conflict checks. all_constraints: HashMap<String, &'d Constraint>, } -impl std::cmp::Eq for &Decl {} -impl<'d> std::cmp::PartialEq for &'d Decl { +impl std::cmp::Eq for &parser::ast::Decl {} +impl<'d> std::cmp::PartialEq for &'d parser::ast::Decl { fn eq(&self, other: &Self) -> bool { std::ptr::eq(*self, *other) } } -impl<'d> std::hash::Hash for &'d Decl { +impl<'d> std::hash::Hash for &'d parser::ast::Decl { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { std::ptr::hash(*self, state); } @@ -128,9 +162,9 @@ fn bit_width(val: usize) -> usize { impl<'d> PacketScope<'d> { /// Insert a field declaration into a packet scope. - fn insert(&mut self, field: &'d Field, result: &mut LintDiagnostics) { - match field { - Field::Checksum { loc, field_id, .. } => { + fn insert(&mut self, field: &'d parser::ast::Field, result: &mut LintDiagnostics) { + match &field.desc { + FieldDesc::Checksum { field_id, .. } => { self.checksums.insert(field_id.clone(), field).map(|prev| { result.push( Diagnostic::error() @@ -139,8 +173,8 @@ impl<'d> PacketScope<'d> { field_id )) .with_labels(vec![ - loc.primary(), - prev.loc() + field.loc.primary(), + prev.loc .secondary() .with_message("checksum start is first declared here"), ]), @@ -148,12 +182,12 @@ impl<'d> PacketScope<'d> { }) } - Field::Padding { .. } - | Field::Reserved { .. } - | Field::Fixed { .. } - | Field::ElementSize { .. } => None, + FieldDesc::Padding { .. } + | FieldDesc::Reserved { .. } + | FieldDesc::Fixed { .. } + | FieldDesc::ElementSize { .. } => None, - Field::Size { loc, field_id, .. } | Field::Count { loc, field_id, .. } => { + FieldDesc::Size { field_id, .. } | FieldDesc::Count { field_id, .. } => { self.sizes.insert(field_id.clone(), field).map(|prev| { result.push( Diagnostic::error() @@ -162,23 +196,21 @@ impl<'d> PacketScope<'d> { field_id )) .with_labels(vec![ - loc.primary(), - prev.loc().secondary().with_message("size is first declared here"), + field.loc.primary(), + prev.loc.secondary().with_message("size is first declared here"), ]), ) }) } - Field::Body { loc, .. } | Field::Payload { loc, .. } => { + FieldDesc::Body { .. } | FieldDesc::Payload { .. } => { if let Some(prev) = self.payload.as_ref() { result.push( Diagnostic::error() .with_message("redeclaration of payload or body field") .with_labels(vec![ - loc.primary(), - prev.loc() - .secondary() - .with_message("payload is first declared here"), + field.loc.primary(), + prev.loc.secondary().with_message("payload is first declared here"), ]), ) } @@ -186,21 +218,21 @@ impl<'d> PacketScope<'d> { None } - Field::Array { loc, id, .. } - | Field::Scalar { loc, id, .. } - | Field::Typedef { loc, id, .. } => self + FieldDesc::Array { id, .. } + | FieldDesc::Scalar { id, .. } + | FieldDesc::Typedef { id, .. } => self .named .insert(id.clone(), field) - .map(|prev| result.err_redeclared(id, "field", loc, prev.loc())), + .map(|prev| result.err_redeclared(id, "field", &field.loc, &prev.loc)), - Field::Group { loc, group_id, .. } => { + FieldDesc::Group { group_id, .. } => { self.groups.insert(group_id.clone(), field).map(|prev| { result.push( Diagnostic::error() .with_message(format!("duplicate group `{}` insertion", group_id)) .with_labels(vec![ - loc.primary(), - prev.loc() + field.loc.primary(), + prev.loc .secondary() .with_message(format!("`{}` is first used here", group_id)), ]), @@ -253,7 +285,7 @@ impl<'d> PacketScope<'d> { &mut self, scope: &Scope, packet_scope: &PacketScope<'d>, - group: &'d Field, + group: &'d parser::ast::Field, constraints: impl Iterator<Item = &'d Constraint>, result: &mut LintDiagnostics, ) { @@ -274,8 +306,8 @@ impl<'d> PacketScope<'d> { err_redeclared_by_group( result, format!("inserted group redeclares checksum start for `{}`", id), - group.loc(), - prev.loc(), + &group.loc, + &prev.loc, ) } } @@ -284,8 +316,8 @@ impl<'d> PacketScope<'d> { err_redeclared_by_group( result, format!("inserted group redeclares size or count for `{}`", id), - group.loc(), - prev.loc(), + &group.loc, + &prev.loc, ) } } @@ -293,8 +325,8 @@ impl<'d> PacketScope<'d> { (Some(prev), Some(next)) => err_redeclared_by_group( result, "inserted group redeclares payload or body field", - next.loc(), - prev.loc(), + &next.loc, + &prev.loc, ), (None, Some(payload)) => self.payload = Some(payload), _ => (), @@ -304,8 +336,8 @@ impl<'d> PacketScope<'d> { err_redeclared_by_group( result, format!("inserted group redeclares field `{}`", id), - group.loc(), - prev.loc(), + &group.loc, + &prev.loc, ) } } @@ -339,8 +371,11 @@ impl<'d> PacketScope<'d> { /// Return the field immediately preceding the selected field, or None /// if no such field exists. - fn get_preceding_field(&self, searched_field: &Field) -> Option<&Field> { - let mut preceding_field: Option<&Field> = None; + fn get_preceding_field( + &self, + searched_field: &parser::ast::Field, + ) -> Option<&parser::ast::Field> { + let mut preceding_field: Option<&parser::ast::Field> = None; for field in self.fields.iter() { if ptr::eq(*field, searched_field) { break; @@ -360,8 +395,8 @@ impl<'d> PacketScope<'d> { Diagnostic::warning() .with_message(format!("declaration of `{}` shadows parent field", id)) .with_labels(vec![ - f.loc().primary(), - prev.loc() + f.loc.primary(), + prev.loc .secondary() .with_message(format!("`{}` is first declared here", id)), ]), @@ -381,7 +416,11 @@ fn lint_constraint( ) { // Validate constraint value types. match (packet_scope.all_fields.get(&constraint.id), &constraint.value, &constraint.tag_id) { - (Some(Field::Scalar { loc: field_loc, width, .. }), Some(value), _) => { + ( + Some(Field { loc: field_loc, desc: FieldDesc::Scalar { width, .. }, .. }), + Some(value), + _, + ) => { if bit_width(*value) > *width { result.push( Diagnostic::error().with_message("invalid integer literal").with_labels(vec![ @@ -395,16 +434,15 @@ fn lint_constraint( } } - (Some(Field::Scalar { loc: field_loc, .. }), None, _) => { - result.push(Diagnostic::error().with_message("invalid literal type").with_labels(vec![ + (Some(Field { loc: field_loc, desc: FieldDesc::Scalar { .. }, .. }), None, _) => result + .push(Diagnostic::error().with_message("invalid literal type").with_labels(vec![ constraint.loc.primary().with_message("expected integer literal"), field_loc.secondary().with_message("the value is used here"), - ])) - } + ])), - (Some(Field::Typedef { type_id, loc: field_loc, .. }), _, _) => { + (Some(Field { loc: field_loc, desc: FieldDesc::Typedef { type_id, .. }, .. }), _, _) => { match (scope.typedef.get(type_id), &constraint.tag_id) { - (Some(Decl::Enum { tags, .. }), Some(tag_id)) => { + (Some(Decl { desc: DeclDesc::Enum { tags, .. }, .. }), Some(tag_id)) => { if !tags.iter().any(|t| &t.id == tag_id) { result.push( Diagnostic::error() @@ -451,7 +489,7 @@ fn lint_constraint( } impl<'d> Scope<'d> { - pub fn new(file: &File) -> Result<Scope<'_>, LintDiagnostics> { + pub fn new(file: &parser::ast::File) -> Result<Scope<'_>, LintDiagnostics> { let mut lint_diagnostics = LintDiagnostics::new(); let scope = file.scope(&mut lint_diagnostics); @@ -476,20 +514,20 @@ impl<'d> Scope<'d> { // - undeclared Packet or Struct parents, // - recursive Group insertion, // - recursive Packet or Struct inheritance. - fn finalize(&mut self, result: &mut LintDiagnostics) -> Vec<&'d Decl> { + fn finalize(&mut self, result: &mut LintDiagnostics) -> Vec<&'d parser::ast::Decl> { // Auxiliary function implementing BFS on Packet tree. enum Mark { Temporary, Permanent, } struct Context<'d> { - list: Vec<&'d Decl>, - visited: HashMap<&'d Decl, Mark>, - scopes: HashMap<&'d Decl, PacketScope<'d>>, + list: Vec<&'d parser::ast::Decl>, + visited: HashMap<&'d parser::ast::Decl, Mark>, + scopes: HashMap<&'d parser::ast::Decl, PacketScope<'d>>, } fn bfs<'s, 'd>( - decl: &'d Decl, + decl: &'d parser::ast::Decl, context: &'s mut Context<'d>, scope: &Scope<'d>, result: &mut LintDiagnostics, @@ -504,18 +542,17 @@ impl<'d> Scope<'d> { decl.kind(), decl.id().unwrap() )) - .with_labels(vec![decl.loc().primary()]), + .with_labels(vec![decl.loc.primary()]), ); return None; } _ => (), } - let (parent_id, fields) = match decl { - Decl::Packet { parent_id, fields, .. } | Decl::Struct { parent_id, fields, .. } => { - (parent_id.as_ref(), fields) - } - Decl::Group { fields, .. } => (None, fields), + let (parent_id, fields) = match &decl.desc { + DeclDesc::Packet { parent_id, fields, .. } + | DeclDesc::Struct { parent_id, fields, .. } => (parent_id.as_ref(), fields), + DeclDesc::Group { fields, .. } => (None, fields), _ => return None, }; @@ -524,8 +561,8 @@ impl<'d> Scope<'d> { // Iterate over Struct and Group fields. for f in fields { - match f { - Field::Group { group_id, constraints, .. } => { + match &f.desc { + FieldDesc::Group { group_id, constraints, .. } => { match scope.typedef.get(group_id) { None => result.push( Diagnostic::error() @@ -533,9 +570,9 @@ impl<'d> Scope<'d> { "undeclared group identifier `{}`", group_id )) - .with_labels(vec![f.loc().primary()]), + .with_labels(vec![f.loc.primary()]), ), - Some(group_decl @ Decl::Group { .. }) => { + Some(group_decl @ Decl { desc: DeclDesc::Group { .. }, .. }) => { // Recurse to flatten the inserted group. if let Some(rscope) = bfs(group_decl, context, scope, result) { // Inline the group fields and constraints into @@ -549,12 +586,12 @@ impl<'d> Scope<'d> { "invalid group field identifier `{}`", group_id )) - .with_labels(vec![f.loc().primary()]) + .with_labels(vec![f.loc.primary()]) .with_notes(vec!["hint: expected group identifier".to_owned()]), ), } } - Field::Typedef { type_id, .. } => { + FieldDesc::Typedef { type_id, .. } => { lscope.fields.push(f); match scope.typedef.get(type_id) { None => result.push( @@ -563,9 +600,9 @@ impl<'d> Scope<'d> { "undeclared typedef identifier `{}`", type_id )) - .with_labels(vec![f.loc().primary()]), + .with_labels(vec![f.loc.primary()]), ), - Some(struct_decl @ Decl::Struct { .. }) => { + Some(struct_decl @ Decl { desc: DeclDesc::Struct { .. }, .. }) => { bfs(struct_decl, context, scope, result); } Some(_) => (), @@ -577,24 +614,29 @@ impl<'d> Scope<'d> { // Iterate over parent declaration. let parent = parent_id.and_then(|id| scope.typedef.get(id)); - match (decl, parent) { - (Decl::Packet { parent_id: Some(_), .. }, None) - | (Decl::Struct { parent_id: Some(_), .. }, None) => result.push( + match (&decl.desc, parent) { + (DeclDesc::Packet { parent_id: Some(_), .. }, None) + | (DeclDesc::Struct { parent_id: Some(_), .. }, None) => result.push( Diagnostic::error() .with_message(format!( "undeclared parent identifier `{}`", parent_id.unwrap() )) - .with_labels(vec![decl.loc().primary()]) - .with_notes(vec![format!("hint: expected {} parent", decl.kind())]), - ), - (Decl::Packet { .. }, Some(Decl::Struct { .. })) - | (Decl::Struct { .. }, Some(Decl::Packet { .. })) => result.push( - Diagnostic::error() - .with_message(format!("invalid parent identifier `{}`", parent_id.unwrap())) - .with_labels(vec![decl.loc().primary()]) + .with_labels(vec![decl.loc.primary()]) .with_notes(vec![format!("hint: expected {} parent", decl.kind())]), ), + (DeclDesc::Packet { .. }, Some(Decl { desc: DeclDesc::Struct { .. }, .. })) + | (DeclDesc::Struct { .. }, Some(Decl { desc: DeclDesc::Packet { .. }, .. })) => { + result.push( + Diagnostic::error() + .with_message(format!( + "invalid parent identifier `{}`", + parent_id.unwrap() + )) + .with_labels(vec![decl.loc.primary()]) + .with_notes(vec![format!("hint: expected {} parent", decl.kind())]), + ) + } (_, Some(parent_decl)) => { if let Some(rscope) = bfs(parent_decl, context, scope, result) { // Import the parent fields and constraints into the current scope. @@ -623,22 +665,22 @@ impl<'d> Scope<'d> { } } -impl Field { +impl parser::ast::Field { fn kind(&self) -> &str { - match self { - Field::Checksum { .. } => "payload", - Field::Padding { .. } => "padding", - Field::Size { .. } => "size", - Field::Count { .. } => "count", - Field::ElementSize { .. } => "elementsize", - Field::Body { .. } => "body", - Field::Payload { .. } => "payload", - Field::Fixed { .. } => "fixed", - Field::Reserved { .. } => "reserved", - Field::Group { .. } => "group", - Field::Array { .. } => "array", - Field::Scalar { .. } => "scalar", - Field::Typedef { .. } => "typedef", + match &self.desc { + FieldDesc::Checksum { .. } => "payload", + FieldDesc::Padding { .. } => "padding", + FieldDesc::Size { .. } => "size", + FieldDesc::Count { .. } => "count", + FieldDesc::ElementSize { .. } => "elementsize", + FieldDesc::Body { .. } => "body", + FieldDesc::Payload { .. } => "payload", + FieldDesc::Fixed { .. } => "fixed", + FieldDesc::Reserved { .. } => "reserved", + FieldDesc::Group { .. } => "group", + FieldDesc::Array { .. } => "array", + FieldDesc::Scalar { .. } => "scalar", + FieldDesc::Typedef { .. } => "typedef", } } } @@ -676,20 +718,20 @@ fn lint_enum(tags: &[Tag], width: usize, result: &mut LintDiagnostics) { fn lint_checksum( scope: &Scope, packet_scope: &PacketScope, - decl: &Field, + decl: &parser::ast::Field, field_id: &str, result: &mut LintDiagnostics, ) { // Checksum field must be declared before // the checksum start. The field must be a typedef with // a valid checksum type. - let checksum_loc = decl.loc(); + let checksum_loc = &decl.loc; match packet_scope.named.get(field_id) { - Some(Field::Typedef { loc: field_loc, type_id, .. }) => { + Some(Field { loc: field_loc, desc: FieldDesc::Typedef { type_id, .. }, .. }) => { // Check declaration type of checksum field. match scope.typedef.get(type_id) { - Some(Decl::Checksum { .. }) => (), + Some(Decl { desc: DeclDesc::Checksum { .. }, .. }) => (), Some(decl) => result.push( Diagnostic::error() .with_message(format!("checksum start uses invalid field `{}`", field_id)) @@ -713,7 +755,7 @@ fn lint_checksum( .with_message(format!("checksum start uses invalid field `{}`", field_id)) .with_labels(vec![ checksum_loc.primary(), - field.loc().secondary().with_message(format!( + field.loc.secondary().with_message(format!( "`{}` is declared as {} field, expected typedef", field_id, field.kind() @@ -728,7 +770,7 @@ fn lint_checksum( fn lint_size( _scope: &Scope, packet_scope: &PacketScope, - decl: &Field, + decl: &parser::ast::Field, field_id: &str, _width: usize, result: &mut LintDiagnostics, @@ -738,16 +780,16 @@ fn lint_size( // The field must reference a valid body, payload or array // field. - let size_loc = decl.loc(); + let size_loc = &decl.loc; if field_id == "_payload_" { return match packet_scope.payload.as_ref() { - Some(Field::Body { .. }) => result.push( + Some(Field { desc: FieldDesc::Body { .. }, .. }) => result.push( Diagnostic::error() .with_message("size field uses undeclared payload field, did you mean _body_ ?") .with_labels(vec![size_loc.primary()]), ), - Some(Field::Payload { .. }) => (), + Some(Field { desc: FieldDesc::Payload { .. }, .. }) => (), Some(_) => unreachable!(), None => result.push( Diagnostic::error() @@ -758,12 +800,12 @@ fn lint_size( } if field_id == "_body_" { return match packet_scope.payload.as_ref() { - Some(Field::Payload { .. }) => result.push( + Some(Field { desc: FieldDesc::Payload { .. }, .. }) => result.push( Diagnostic::error() .with_message("size field uses undeclared body field, did you mean _payload_ ?") .with_labels(vec![size_loc.primary()]), ), - Some(Field::Body { .. }) => (), + Some(Field { desc: FieldDesc::Body { .. }, .. }) => (), Some(_) => unreachable!(), None => result.push( Diagnostic::error() @@ -774,20 +816,23 @@ fn lint_size( } match packet_scope.named.get(field_id) { - Some(Field::Array { size: Some(_), loc: array_loc, .. }) => result.push( - Diagnostic::warning() - .with_message(format!("size field uses array `{}` with static size", field_id)) - .with_labels(vec![ - size_loc.primary(), - array_loc.secondary().with_message(format!("`{}` is declared here", field_id)), - ]), - ), - Some(Field::Array { .. }) => (), + Some(Field { loc: array_loc, desc: FieldDesc::Array { size: Some(_), .. }, .. }) => result + .push( + Diagnostic::warning() + .with_message(format!("size field uses array `{}` with static size", field_id)) + .with_labels(vec![ + size_loc.primary(), + array_loc + .secondary() + .with_message(format!("`{}` is declared here", field_id)), + ]), + ), + Some(Field { desc: FieldDesc::Array { .. }, .. }) => (), Some(field) => result.push( Diagnostic::error() .with_message(format!("invalid `{}` field type", field_id)) .with_labels(vec![ - field.loc().primary().with_message(format!( + field.loc.primary().with_message(format!( "`{}` is declared as {}", field_id, field.kind() @@ -806,7 +851,7 @@ fn lint_size( fn lint_count( _scope: &Scope, packet_scope: &PacketScope, - decl: &Field, + decl: &parser::ast::Field, field_id: &str, _width: usize, result: &mut LintDiagnostics, @@ -815,23 +860,26 @@ fn lint_count( // The field must reference a valid array field. // Warning if the array already has a known size. - let count_loc = decl.loc(); + let count_loc = &decl.loc; match packet_scope.named.get(field_id) { - Some(Field::Array { size: Some(_), loc: array_loc, .. }) => result.push( - Diagnostic::warning() - .with_message(format!("count field uses array `{}` with static size", field_id)) - .with_labels(vec![ - count_loc.primary(), - array_loc.secondary().with_message(format!("`{}` is declared here", field_id)), - ]), - ), + Some(Field { loc: array_loc, desc: FieldDesc::Array { size: Some(_), .. }, .. }) => result + .push( + Diagnostic::warning() + .with_message(format!("count field uses array `{}` with static size", field_id)) + .with_labels(vec![ + count_loc.primary(), + array_loc + .secondary() + .with_message(format!("`{}` is declared here", field_id)), + ]), + ), - Some(Field::Array { .. }) => (), + Some(Field { desc: FieldDesc::Array { .. }, .. }) => (), Some(field) => result.push( Diagnostic::error() .with_message(format!("invalid `{}` field type", field_id)) .with_labels(vec![ - field.loc().primary().with_message(format!( + field.loc.primary().with_message(format!( "`{}` is declared as {}", field_id, field.kind() @@ -851,7 +899,7 @@ fn lint_count( fn lint_fixed( scope: &Scope, _packet_scope: &PacketScope, - decl: &Field, + decl: &parser::ast::Field, width: &Option<usize>, value: &Option<usize>, enum_id: &Option<String>, @@ -860,7 +908,7 @@ fn lint_fixed( ) { // By parsing constraint, we already have that either // (width and value) or (enum_id and tag_id) are Some. - let fixed_loc = decl.loc(); + let fixed_loc = decl.loc; if width.is_some() { // The value of a fixed field should have . @@ -876,7 +924,7 @@ fn lint_fixed( // The fixed field should reference a valid enum id and tag id // association. match scope.typedef.get(enum_id.as_ref().unwrap()) { - Some(Decl::Enum { tags, .. }) => { + Some(Decl { desc: DeclDesc::Enum { tags, .. }, .. }) => { match tags.iter().find(|t| &t.id == tag_id.as_ref().unwrap()) { Some(_) => (), None => result.push( @@ -915,7 +963,7 @@ fn lint_fixed( fn lint_array( scope: &Scope, _packet_scope: &PacketScope, - decl: &Field, + decl: &parser::ast::Field, _width: &Option<usize>, type_id: &Option<String>, _size_modifier: &Option<String>, @@ -927,13 +975,13 @@ fn lint_array( // type_id must reference a valid enum or packet type. // TODO(hchataing) unbounded arrays should have a matching size // or count field - let array_loc = decl.loc(); + let array_loc = decl.loc; if type_id.is_some() { match scope.typedef.get(type_id.as_ref().unwrap()) { - Some(Decl::Enum { .. }) - | Some(Decl::Struct { .. }) - | Some(Decl::CustomField { .. }) => (), + Some(Decl { desc: DeclDesc::Enum { .. }, .. }) + | Some(Decl { desc: DeclDesc::Struct { .. }, .. }) + | Some(Decl { desc: DeclDesc::CustomField { .. }, .. }) => (), Some(decl) => result.push( Diagnostic::error() .with_message(format!( @@ -961,12 +1009,12 @@ fn lint_array( fn lint_padding( _scope: &Scope, packet_scope: &PacketScope, - decl: &Field, + decl: &parser::ast::Field, _size: usize, result: &mut LintDiagnostics, ) { // The padding field must follow an array field. - let padding_loc = decl.loc(); + let padding_loc = decl.loc; match packet_scope.get_preceding_field(decl) { None => result.push( @@ -977,14 +1025,14 @@ fn lint_padding( "hint: padding fields must be placed after an array field".to_owned() ]), ), - Some(Field::Array { .. }) => (), + Some(Field { desc: FieldDesc::Array { .. }, .. }) => (), Some(preceding_field) => result.push( Diagnostic::error() .with_message(format!( "padding field cannot be placed after {} field", preceding_field.kind() )) - .with_labels(vec![padding_loc.primary(), preceding_field.loc().secondary()]) + .with_labels(vec![padding_loc.primary(), preceding_field.loc.secondary()]) .with_notes(vec![ "hint: padding fields must be placed after an array field".to_owned() ]), @@ -996,20 +1044,20 @@ fn lint_padding( fn lint_typedef( scope: &Scope, _packet_scope: &PacketScope, - decl: &Field, + decl: &parser::ast::Field, type_id: &str, result: &mut LintDiagnostics, ) { // The typedef field must reference a valid struct, enum, // custom_field, or checksum type. // TODO(hchataing) checksum fields should have a matching checksum start - let typedef_loc = decl.loc(); + let typedef_loc = decl.loc; match scope.typedef.get(type_id) { - Some(Decl::Enum { .. }) - | Some(Decl::Struct { .. }) - | Some(Decl::CustomField { .. }) - | Some(Decl::Checksum { .. }) => (), + Some(Decl { desc: DeclDesc::Enum { .. }, .. }) + | Some(Decl { desc: DeclDesc::Struct { .. }, .. }) + | Some(Decl { desc: DeclDesc::CustomField { .. }, .. }) + | Some(Decl { desc: DeclDesc::Checksum { .. }, .. }) => (), Some(decl) => result.push( Diagnostic::error() @@ -1034,42 +1082,43 @@ fn lint_typedef( fn lint_field( scope: &Scope, packet_scope: &PacketScope, - decl: &Field, + decl: &parser::ast::Field, result: &mut LintDiagnostics, ) { - match decl { - Field::Checksum { field_id, .. } => { + match &decl.desc { + FieldDesc::Checksum { field_id, .. } => { lint_checksum(scope, packet_scope, decl, field_id, result) } - Field::Size { field_id, width, .. } => { + FieldDesc::Size { field_id, width, .. } => { lint_size(scope, packet_scope, decl, field_id, *width, result) } - Field::Count { field_id, width, .. } => { + FieldDesc::Count { field_id, width, .. } => { lint_count(scope, packet_scope, decl, field_id, *width, result) } - Field::ElementSize { .. } => { /* TODO(aryarahul) */ } - Field::Fixed { width, value, enum_id, tag_id, .. } => { + FieldDesc::ElementSize { .. } => { /* TODO(aryarahul) */ } + FieldDesc::Fixed { width, value, enum_id, tag_id, .. } => { lint_fixed(scope, packet_scope, decl, width, value, enum_id, tag_id, result) } - Field::Array { width, type_id, size_modifier, size, .. } => { + FieldDesc::Array { width, type_id, size_modifier, size, .. } => { lint_array(scope, packet_scope, decl, width, type_id, size_modifier, size, result) } - Field::Typedef { type_id, .. } => lint_typedef(scope, packet_scope, decl, type_id, result), - Field::Padding { size, .. } => lint_padding(scope, packet_scope, decl, *size, result), - Field::Reserved { .. } - | Field::Scalar { .. } - | Field::Body { .. } - | Field::Payload { .. } => (), - Field::Group { .. } => unreachable!(), + FieldDesc::Typedef { type_id, .. } => { + lint_typedef(scope, packet_scope, decl, type_id, result) + } + FieldDesc::Padding { size, .. } => lint_padding(scope, packet_scope, decl, *size, result), + FieldDesc::Reserved { .. } + | FieldDesc::Scalar { .. } + | FieldDesc::Body { .. } + | FieldDesc::Payload { .. } => (), + FieldDesc::Group { .. } => unreachable!(), } } // Helper for linting a packet declaration. fn lint_packet( scope: &Scope, - decl: &Decl, + decl: &parser::ast::Decl, id: &str, - loc: &SourceRange, constraints: &[Constraint], parent_id: &Option<String>, result: &mut LintDiagnostics, @@ -1088,7 +1137,7 @@ fn lint_packet( "packet `{}` has field constraints, but no parent declaration", id )) - .with_labels(vec![loc.primary()]) + .with_labels(vec![decl.loc.primary()]) .with_notes(vec!["hint: expected parent declaration".to_owned()]), ) } @@ -1105,9 +1154,8 @@ fn lint_packet( // Helper for linting a struct declaration. fn lint_struct( scope: &Scope, - decl: &Decl, + decl: &parser::ast::Decl, id: &str, - loc: &SourceRange, constraints: &[Constraint], parent_id: &Option<String>, result: &mut LintDiagnostics, @@ -1126,7 +1174,7 @@ fn lint_struct( "struct `{}` has field constraints, but no parent declaration", id )) - .with_labels(vec![loc.primary()]) + .with_labels(vec![decl.loc.primary()]) .with_notes(vec!["hint: expected parent declaration".to_owned()]), ) } @@ -1140,10 +1188,10 @@ fn lint_struct( } } -impl Decl { +impl parser::ast::Decl { fn constraints(&self) -> impl Iterator<Item = &Constraint> { - match self { - Decl::Packet { constraints, .. } | Decl::Struct { constraints, .. } => { + match &self.desc { + DeclDesc::Packet { constraints, .. } | DeclDesc::Struct { constraints, .. } => { Some(constraints.iter()) } _ => None, @@ -1153,10 +1201,10 @@ impl Decl { } fn scope<'d>(&'d self, result: &mut LintDiagnostics) -> Option<PacketScope<'d>> { - match self { - Decl::Packet { fields, .. } - | Decl::Struct { fields, .. } - | Decl::Group { fields, .. } => { + match &self.desc { + DeclDesc::Packet { fields, .. } + | DeclDesc::Struct { fields, .. } + | DeclDesc::Group { fields, .. } => { let mut scope = PacketScope { checksums: HashMap::new(), sizes: HashMap::new(), @@ -1179,36 +1227,36 @@ impl Decl { } fn lint<'d>(&'d self, scope: &Scope<'d>, result: &mut LintDiagnostics) { - match self { - Decl::Checksum { .. } | Decl::CustomField { .. } => (), - Decl::Enum { tags, width, .. } => lint_enum(tags, *width, result), - Decl::Packet { id, loc, constraints, parent_id, .. } => { - lint_packet(scope, self, id, loc, constraints, parent_id, result) + match &self.desc { + DeclDesc::Checksum { .. } | DeclDesc::CustomField { .. } => (), + DeclDesc::Enum { tags, width, .. } => lint_enum(tags, *width, result), + DeclDesc::Packet { id, constraints, parent_id, .. } => { + lint_packet(scope, self, id, constraints, parent_id, result) } - Decl::Struct { id, loc, constraints, parent_id, .. } => { - lint_struct(scope, self, id, loc, constraints, parent_id, result) + DeclDesc::Struct { id, constraints, parent_id, .. } => { + lint_struct(scope, self, id, constraints, parent_id, result) } // Groups are finalizeed before linting, to make sure // potential errors are raised only once. - Decl::Group { .. } => (), - Decl::Test { .. } => (), + DeclDesc::Group { .. } => (), + DeclDesc::Test { .. } => (), } } fn kind(&self) -> &str { - match self { - Decl::Checksum { .. } => "checksum", - Decl::CustomField { .. } => "custom field", - Decl::Enum { .. } => "enum", - Decl::Packet { .. } => "packet", - Decl::Struct { .. } => "struct", - Decl::Group { .. } => "group", - Decl::Test { .. } => "test", + match &self.desc { + DeclDesc::Checksum { .. } => "checksum", + DeclDesc::CustomField { .. } => "custom field", + DeclDesc::Enum { .. } => "enum", + DeclDesc::Packet { .. } => "packet", + DeclDesc::Struct { .. } => "struct", + DeclDesc::Group { .. } => "group", + DeclDesc::Test { .. } => "test", } } } -impl File { +impl parser::ast::File { fn scope<'d>(&'d self, result: &mut LintDiagnostics) -> Scope<'d> { let mut scope = Scope { typedef: HashMap::new(), scopes: HashMap::new() }; @@ -1219,7 +1267,7 @@ impl File { for decl in &self.declarations { if let Some(id) = decl.id() { if let Some(prev) = scope.typedef.insert(id.to_string(), decl) { - result.err_redeclared(id, decl.kind(), decl.loc(), prev.loc()) + result.err_redeclared(id, decl.kind(), &decl.loc, &prev.loc) } } if let Some(lscope) = decl.scope(result) { @@ -1232,7 +1280,7 @@ impl File { } } -impl Lintable for File { +impl Lintable for parser::ast::File { fn lint(&self) -> LintDiagnostics { Scope::new(self).err().unwrap_or_else(LintDiagnostics::new) } diff --git a/tools/pdl/src/parser.rs b/tools/pdl/src/parser.rs index a08c2bceb27..13e32605e58 100644 --- a/tools/pdl/src/parser.rs +++ b/tools/pdl/src/parser.rs @@ -1,10 +1,25 @@ -use super::ast; use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::files; use pest::iterators::{Pair, Pairs}; use pest::{Parser, Token}; use std::iter::{Filter, Peekable}; +pub mod ast { + use serde::Serialize; + + #[derive(Debug, Serialize)] + pub struct Annotation; + + impl crate::ast::Annotation for Annotation { + type FieldAnnotation = (); + type DeclAnnotation = (); + } + + pub type Field = crate::ast::Field<Annotation>; + pub type Decl = crate::ast::Decl<Annotation>; + pub type File = crate::ast::File<Annotation>; +} + // Generate the PDL parser. // TODO: use #[grammar = "pdl.pest"] // currently not possible because CARGO_MANIFEST_DIR is not set @@ -139,11 +154,11 @@ pub struct PDLParser; type Node<'i> = Pair<'i, Rule>; type NodeIterator<'i> = Peekable<Filter<Pairs<'i, Rule>, fn(&Node<'i>) -> bool>>; -type Context<'a> = (ast::FileId, &'a Vec<usize>); +type Context<'a> = (crate::ast::FileId, &'a Vec<usize>); trait Helpers<'i> { fn children(self) -> NodeIterator<'i>; - fn as_loc(&self, context: &Context) -> ast::SourceRange; + fn as_loc(&self, context: &Context) -> crate::ast::SourceRange; fn as_string(&self) -> String; fn as_usize(&self) -> Result<usize, String>; } @@ -153,12 +168,12 @@ impl<'i> Helpers<'i> for Node<'i> { self.into_inner().filter((|n| n.as_rule() != Rule::COMMENT) as fn(&Self) -> bool).peekable() } - fn as_loc(&self, context: &Context) -> ast::SourceRange { + fn as_loc(&self, context: &Context) -> crate::ast::SourceRange { let span = self.as_span(); - ast::SourceRange { + crate::ast::SourceRange { file: context.0, - start: ast::SourceLocation::new(span.start_pos().pos(), context.1), - end: ast::SourceLocation::new(span.end_pos().pos(), context.1), + start: crate::ast::SourceLocation::new(span.start_pos().pos(), context.1), + end: crate::ast::SourceLocation::new(span.end_pos().pos(), context.1), } } @@ -245,22 +260,22 @@ fn parse_size_modifier_opt(iter: &mut NodeIterator<'_>) -> Option<String> { maybe(iter, Rule::size_modifier).map(|n| n.as_string()) } -fn parse_endianness(node: Node<'_>, context: &Context) -> Result<ast::Endianness, String> { +fn parse_endianness(node: Node<'_>, context: &Context) -> Result<crate::ast::Endianness, String> { if node.as_rule() != Rule::endianness_declaration { err_unexpected_rule(Rule::endianness_declaration, node.as_rule()) } else { - Ok(ast::Endianness { + Ok(crate::ast::Endianness { loc: node.as_loc(context), value: match node.as_str() { - "little_endian_packets" => ast::EndiannessValue::LittleEndian, - "big_endian_packets" => ast::EndiannessValue::BigEndian, + "little_endian_packets" => crate::ast::EndiannessValue::LittleEndian, + "big_endian_packets" => crate::ast::EndiannessValue::BigEndian, _ => unreachable!(), }, }) } } -fn parse_constraint(node: Node<'_>, context: &Context) -> Result<ast::Constraint, String> { +fn parse_constraint(node: Node<'_>, context: &Context) -> Result<crate::ast::Constraint, String> { if node.as_rule() != Rule::constraint { err_unexpected_rule(Rule::constraint, node.as_rule()) } else { @@ -268,19 +283,19 @@ fn parse_constraint(node: Node<'_>, context: &Context) -> Result<ast::Constraint let mut children = node.children(); let id = parse_identifier(&mut children)?; let (tag_id, value) = parse_identifier_or_integer(&mut children)?; - Ok(ast::Constraint { id, loc, value, tag_id }) + Ok(crate::ast::Constraint { id, loc, value, tag_id }) } } fn parse_constraint_list_opt( iter: &mut NodeIterator<'_>, context: &Context, -) -> Result<Vec<ast::Constraint>, String> { +) -> Result<Vec<crate::ast::Constraint>, String> { maybe(iter, Rule::constraint_list) .map_or(Ok(vec![]), |n| n.children().map(|n| parse_constraint(n, context)).collect()) } -fn parse_enum_tag(node: Node<'_>, context: &Context) -> Result<ast::Tag, String> { +fn parse_enum_tag(node: Node<'_>, context: &Context) -> Result<crate::ast::Tag, String> { if node.as_rule() != Rule::enum_tag { err_unexpected_rule(Rule::enum_tag, node.as_rule()) } else { @@ -288,14 +303,14 @@ fn parse_enum_tag(node: Node<'_>, context: &Context) -> Result<ast::Tag, String> let mut children = node.children(); let id = parse_identifier(&mut children)?; let value = parse_integer(&mut children)?; - Ok(ast::Tag { id, loc, value }) + Ok(crate::ast::Tag { id, loc, value }) } } fn parse_enum_tag_list( iter: &mut NodeIterator<'_>, context: &Context, -) -> Result<Vec<ast::Tag>, String> { +) -> Result<Vec<crate::ast::Tag>, String> { expect(iter, Rule::enum_tag_list) .and_then(|n| n.children().map(|n| parse_enum_tag(n, context)).collect()) } @@ -304,84 +319,88 @@ fn parse_field(node: Node<'_>, context: &Context) -> Result<ast::Field, String> let loc = node.as_loc(context); let rule = node.as_rule(); let mut children = node.children(); - Ok(match rule { - Rule::checksum_field => { - let field_id = parse_identifier(&mut children)?; - ast::Field::Checksum { loc, field_id } - } - Rule::padding_field => { - let size = parse_integer(&mut children)?; - ast::Field::Padding { loc, size } - } - Rule::size_field => { - let field_id = match children.next() { - Some(n) if n.as_rule() == Rule::identifier => n.as_string(), - Some(n) if n.as_rule() == Rule::payload_identifier => n.as_string(), - Some(n) if n.as_rule() == Rule::body_identifier => n.as_string(), - Some(n) => err_unexpected_rule(Rule::identifier, n.as_rule())?, - None => err_missing_rule(Rule::identifier)?, - }; - let width = parse_integer(&mut children)?; - ast::Field::Size { loc, field_id, width } - } - Rule::count_field => { - let field_id = parse_identifier(&mut children)?; - let width = parse_integer(&mut children)?; - ast::Field::Count { loc, field_id, width } - } - Rule::elementsize_field => { - let field_id = parse_identifier(&mut children)?; - let width = parse_integer(&mut children)?; - ast::Field::ElementSize { loc, field_id, width } - } - Rule::body_field => ast::Field::Body { loc }, - Rule::payload_field => { - let size_modifier = parse_size_modifier_opt(&mut children); - ast::Field::Payload { loc, size_modifier } - } - Rule::fixed_field => { - let (tag_id, value) = parse_identifier_or_integer(&mut children)?; - let (enum_id, width) = parse_identifier_or_integer(&mut children)?; - ast::Field::Fixed { loc, enum_id, tag_id, width, value } - } - Rule::reserved_field => { - let width = parse_integer(&mut children)?; - ast::Field::Reserved { loc, width } - } - Rule::array_field => { - let id = parse_identifier(&mut children)?; - let (type_id, width) = parse_identifier_or_integer(&mut children)?; - let (size, size_modifier) = match children.next() { - Some(n) if n.as_rule() == Rule::integer => (Some(n.as_usize()?), None), - Some(n) if n.as_rule() == Rule::size_modifier => (None, Some(n.as_string())), - Some(n) => { - return Err(format!( - "expected rule {:?} or {:?}, got {:?}", - Rule::integer, - Rule::size_modifier, - n.as_rule() - )) - } - None => (None, None), - }; - ast::Field::Array { loc, id, type_id, width, size, size_modifier } - } - Rule::scalar_field => { - let id = parse_identifier(&mut children)?; - let width = parse_integer(&mut children)?; - ast::Field::Scalar { loc, id, width } - } - Rule::typedef_field => { - let id = parse_identifier(&mut children)?; - let type_id = parse_identifier(&mut children)?; - ast::Field::Typedef { loc, id, type_id } - } - Rule::group_field => { - let group_id = parse_identifier(&mut children)?; - let constraints = parse_constraint_list_opt(&mut children, context)?; - ast::Field::Group { loc, group_id, constraints } - } - _ => return Err(format!("expected rule *_field, got {:?}", rule)), + Ok(crate::ast::Field { + loc, + annot: Default::default(), + desc: match rule { + Rule::checksum_field => { + let field_id = parse_identifier(&mut children)?; + crate::ast::FieldDesc::Checksum { field_id } + } + Rule::padding_field => { + let size = parse_integer(&mut children)?; + crate::ast::FieldDesc::Padding { size } + } + Rule::size_field => { + let field_id = match children.next() { + Some(n) if n.as_rule() == Rule::identifier => n.as_string(), + Some(n) if n.as_rule() == Rule::payload_identifier => n.as_string(), + Some(n) if n.as_rule() == Rule::body_identifier => n.as_string(), + Some(n) => err_unexpected_rule(Rule::identifier, n.as_rule())?, + None => err_missing_rule(Rule::identifier)?, + }; + let width = parse_integer(&mut children)?; + crate::ast::FieldDesc::Size { field_id, width } + } + Rule::count_field => { + let field_id = parse_identifier(&mut children)?; + let width = parse_integer(&mut children)?; + crate::ast::FieldDesc::Count { field_id, width } + } + Rule::elementsize_field => { + let field_id = parse_identifier(&mut children)?; + let width = parse_integer(&mut children)?; + crate::ast::FieldDesc::ElementSize { field_id, width } + } + Rule::body_field => crate::ast::FieldDesc::Body, + Rule::payload_field => { + let size_modifier = parse_size_modifier_opt(&mut children); + crate::ast::FieldDesc::Payload { size_modifier } + } + Rule::fixed_field => { + let (tag_id, value) = parse_identifier_or_integer(&mut children)?; + let (enum_id, width) = parse_identifier_or_integer(&mut children)?; + crate::ast::FieldDesc::Fixed { enum_id, tag_id, width, value } + } + Rule::reserved_field => { + let width = parse_integer(&mut children)?; + crate::ast::FieldDesc::Reserved { width } + } + Rule::array_field => { + let id = parse_identifier(&mut children)?; + let (type_id, width) = parse_identifier_or_integer(&mut children)?; + let (size, size_modifier) = match children.next() { + Some(n) if n.as_rule() == Rule::integer => (Some(n.as_usize()?), None), + Some(n) if n.as_rule() == Rule::size_modifier => (None, Some(n.as_string())), + Some(n) => { + return Err(format!( + "expected rule {:?} or {:?}, got {:?}", + Rule::integer, + Rule::size_modifier, + n.as_rule() + )) + } + None => (None, None), + }; + crate::ast::FieldDesc::Array { id, type_id, width, size, size_modifier } + } + Rule::scalar_field => { + let id = parse_identifier(&mut children)?; + let width = parse_integer(&mut children)?; + crate::ast::FieldDesc::Scalar { id, width } + } + Rule::typedef_field => { + let id = parse_identifier(&mut children)?; + let type_id = parse_identifier(&mut children)?; + crate::ast::FieldDesc::Typedef { id, type_id } + } + Rule::group_field => { + let group_id = parse_identifier(&mut children)?; + let constraints = parse_constraint_list_opt(&mut children, context)?; + crate::ast::FieldDesc::Group { group_id, constraints } + } + _ => return Err(format!("expected rule *_field, got {:?}", rule)), + }, }) } @@ -403,7 +422,7 @@ fn parse_field_list_opt<'i>( fn parse_toplevel(root: Node<'_>, context: &Context) -> Result<ast::File, String> { let mut toplevel_comments = vec![]; - let mut file = ast::File::new(context.0); + let mut file = crate::ast::File::new(context.0); let mut comment_start = vec![]; for token in root.clone().tokens() { @@ -411,11 +430,11 @@ fn parse_toplevel(root: Node<'_>, context: &Context) -> Result<ast::File, String Token::Start { rule: Rule::COMMENT, pos } => comment_start.push(pos), Token::End { rule: Rule::COMMENT, pos } => { let start_pos = comment_start.pop().unwrap(); - file.comments.push(ast::Comment { - loc: ast::SourceRange { + file.comments.push(crate::ast::Comment { + loc: crate::ast::SourceRange { file: context.0, - start: ast::SourceLocation::new(start_pos.pos(), context.1), - end: ast::SourceLocation::new(pos.pos(), context.1), + start: crate::ast::SourceLocation::new(start_pos.pos(), context.1), + end: crate::ast::SourceLocation::new(pos.pos(), context.1), }, text: start_pos.span(&pos).as_str().to_owned(), }) @@ -434,21 +453,30 @@ fn parse_toplevel(root: Node<'_>, context: &Context) -> Result<ast::File, String let id = parse_identifier(&mut children)?; let width = parse_integer(&mut children)?; let function = parse_string(&mut children)?; - file.declarations.push(ast::Decl::Checksum { id, loc, function, width }) + file.declarations.push(crate::ast::Decl::new( + loc, + crate::ast::DeclDesc::Checksum { id, function, width }, + )) } Rule::custom_field_declaration => { let mut children = node.children(); let id = parse_identifier(&mut children)?; let width = parse_integer_opt(&mut children)?; let function = parse_string(&mut children)?; - file.declarations.push(ast::Decl::CustomField { id, loc, function, width }) + file.declarations.push(crate::ast::Decl::new( + loc, + crate::ast::DeclDesc::CustomField { id, function, width }, + )) } Rule::enum_declaration => { let mut children = node.children(); let id = parse_identifier(&mut children)?; let width = parse_integer(&mut children)?; let tags = parse_enum_tag_list(&mut children, context)?; - file.declarations.push(ast::Decl::Enum { id, loc, width, tags }) + file.declarations.push(crate::ast::Decl::new( + loc, + crate::ast::DeclDesc::Enum { id, width, tags }, + )) } Rule::packet_declaration => { let mut children = node.children(); @@ -456,13 +484,10 @@ fn parse_toplevel(root: Node<'_>, context: &Context) -> Result<ast::File, String let parent_id = parse_identifier_opt(&mut children)?; let constraints = parse_constraint_list_opt(&mut children, context)?; let fields = parse_field_list_opt(&mut children, context)?; - file.declarations.push(ast::Decl::Packet { - id, + file.declarations.push(crate::ast::Decl::new( loc, - parent_id, - constraints, - fields, - }) + crate::ast::DeclDesc::Packet { id, parent_id, constraints, fields }, + )) } Rule::struct_declaration => { let mut children = node.children(); @@ -470,19 +495,17 @@ fn parse_toplevel(root: Node<'_>, context: &Context) -> Result<ast::File, String let parent_id = parse_identifier_opt(&mut children)?; let constraints = parse_constraint_list_opt(&mut children, context)?; let fields = parse_field_list_opt(&mut children, context)?; - file.declarations.push(ast::Decl::Struct { - id, + file.declarations.push(crate::ast::Decl::new( loc, - parent_id, - constraints, - fields, - }) + crate::ast::DeclDesc::Struct { id, parent_id, constraints, fields }, + )) } Rule::group_declaration => { let mut children = node.children(); let id = parse_identifier(&mut children)?; let fields = parse_field_list(&mut children, context)?; - file.declarations.push(ast::Decl::Group { id, loc, fields }) + file.declarations + .push(crate::ast::Decl::new(loc, crate::ast::DeclDesc::Group { id, fields })) } Rule::test_declaration => {} Rule::EOI => (), @@ -498,10 +521,10 @@ fn parse_toplevel(root: Node<'_>, context: &Context) -> Result<ast::File, String /// The file is added to the compilation database under the provided /// name. pub fn parse_inline( - sources: &mut ast::SourceDatabase, + sources: &mut crate::ast::SourceDatabase, name: String, source: String, -) -> Result<ast::File, Diagnostic<ast::FileId>> { +) -> Result<ast::File, Diagnostic<crate::ast::FileId>> { let root = PDLParser::parse(Rule::file, &source) .map_err(|e| { Diagnostic::error() @@ -520,9 +543,9 @@ pub fn parse_inline( /// database. Returns the constructed AST, or a descriptive error /// message in case of syntax error. pub fn parse_file( - sources: &mut ast::SourceDatabase, + sources: &mut crate::ast::SourceDatabase, name: String, -) -> Result<ast::File, Diagnostic<ast::FileId>> { +) -> Result<ast::File, Diagnostic<crate::ast::FileId>> { let source = std::fs::read_to_string(&name).map_err(|e| { Diagnostic::error().with_message(format!("failed to read input file '{}': {}", &name, e)) })?; @@ -537,12 +560,12 @@ mod test { fn endianness_is_set() { // The file starts out with a placeholder little-endian value. // This tests that we update it while parsing. - let mut db = ast::SourceDatabase::new(); + let mut db = crate::ast::SourceDatabase::new(); let file = parse_inline(&mut db, String::from("stdin"), String::from(" big_endian_packets ")) .unwrap(); - assert_eq!(file.endianness.value, ast::EndiannessValue::BigEndian); - assert_ne!(file.endianness.loc, ast::SourceRange::default()); + assert_eq!(file.endianness.value, crate::ast::EndiannessValue::BigEndian); + assert_ne!(file.endianness.loc, crate::ast::SourceRange::default()); } #[test] -- GitLab