From 29e8d53a714085839cee1329abd9fc51524b4f3d Mon Sep 17 00:00:00 2001 From: frankwang <73262844+Frank-III@users.noreply.github.com> Date: Sun, 9 Feb 2025 10:40:33 -0800 Subject: [PATCH] feat: Add position property to StructField, Variant, and subtypes (#764) close: #758 it seems we have to add another vertex kind to get the struct field position? let me know if there is a better way to do it! I would add tests for struct fields, union fields, enum struct variants fields/tuple variants fields/plain variants/unit variants if we think the code is good --- src/adapter/edges.rs | 46 +- src/adapter/enum_variant.rs | 4 + src/adapter/mod.rs | 4 + src/adapter/origin.rs | 11 + src/adapter/properties.rs | 26 + src/adapter/tests.rs | 491 ++++++++++++++++++ src/adapter/vertex.rs | 17 +- src/rustdoc_schema.graphql | 25 + test_crates/enum_variants_position/Cargo.toml | 6 + test_crates/enum_variants_position/src/lib.rs | 21 + test_crates/struct_fields_position/Cargo.toml | 6 + test_crates/struct_fields_position/src/lib.rs | 33 ++ test_crates/union_fields_position/Cargo.toml | 6 + test_crates/union_fields_position/src/lib.rs | 21 + 14 files changed, 700 insertions(+), 17 deletions(-) create mode 100644 test_crates/enum_variants_position/Cargo.toml create mode 100644 test_crates/enum_variants_position/src/lib.rs create mode 100644 test_crates/struct_fields_position/Cargo.toml create mode 100644 test_crates/struct_fields_position/src/lib.rs create mode 100644 test_crates/union_fields_position/Cargo.toml create mode 100644 test_crates/union_fields_position/src/lib.rs diff --git a/src/adapter/edges.rs b/src/adapter/edges.rs index 08ee1d98..27ad5d0e 100644 --- a/src/adapter/edges.rs +++ b/src/adapter/edges.rs @@ -383,8 +383,11 @@ pub(super) fn resolve_struct_edge<'a, V: AsVertex> + 'a>( rustdoc_types::StructKind::Plain { fields, .. } => Box::new(fields.iter()), }; - Box::new(field_ids_iter.map(move |field_id| { - origin.make_item_vertex(item_index.get(field_id).expect("missing item")) + Box::new(field_ids_iter.enumerate().map(move |(index, field_id)| { + origin.make_positioned_item_vertex( + index + 1, + item_index.get(field_id).expect("missing item"), + ) })) }), _ => unreachable!("resolve_struct_edge {edge_name}"), @@ -418,19 +421,25 @@ pub(super) fn resolve_variant_edge<'a, V: AsVertex> + 'a>( match &item.kind { VariantKind::Plain => Box::new(std::iter::empty()), VariantKind::Tuple(fields) => { - Box::new(fields.iter().filter(|x| x.is_some()).map(move |field_id| { - origin.make_item_vertex( - item_index - .get(field_id.as_ref().unwrap()) - .expect("missing item"), - ) - })) + Box::new(fields.iter().filter(|x| x.is_some()).enumerate().map( + move |(index, field_id)| { + origin.make_positioned_item_vertex( + index + 1, + item_index + .get(field_id.as_ref().unwrap()) + .expect("missing item"), + ) + }, + )) } VariantKind::Struct { fields, has_stripped_fields: _, - } => Box::new(fields.iter().map(move |field_id| { - origin.make_item_vertex(item_index.get(field_id).expect("missing item")) + } => Box::new(fields.iter().enumerate().map(move |(index, field_id)| { + origin.make_positioned_item_vertex( + index + 1, + item_index.get(field_id).expect("missing item"), + ) })), } }), @@ -568,9 +577,18 @@ pub(super) fn resolve_union_edge<'a, V: AsVertex> + 'a>( } }; - Box::new(union_item.fields.iter().map(move |field_id| { - origin.make_item_vertex(item_index.get(field_id).expect("missing item")) - })) + Box::new( + union_item + .fields + .iter() + .enumerate() + .map(move |(index, field_id)| { + origin.make_positioned_item_vertex( + index + 1, + item_index.get(field_id).expect("missing item"), + ) + }), + ) }), _ => unreachable!("resolve_union_edge {edge_name}"), } diff --git a/src/adapter/enum_variant.rs b/src/adapter/enum_variant.rs index 40e39215..839f37be 100644 --- a/src/adapter/enum_variant.rs +++ b/src/adapter/enum_variant.rs @@ -78,6 +78,10 @@ impl<'a> EnumVariant<'a> { pub(super) fn item(&self) -> &'a Item { self.item } + + pub(super) fn position(&self) -> i64 { + self.index as i64 + 1 + } } enum DiscriminantValue { diff --git a/src/adapter/mod.rs b/src/adapter/mod.rs index 10e8b2df..c1181ec3 100644 --- a/src/adapter/mod.rs +++ b/src/adapter/mod.rs @@ -140,7 +140,11 @@ impl<'a> Adapter<'a> for &'a RustdocAdapter<'a> { } "Module" => properties::resolve_module_property(contexts, property_name), "Struct" => properties::resolve_struct_property(contexts, property_name), + "StructField" => properties::resolve_struct_field_property(contexts, property_name), "Enum" => properties::resolve_enum_property(contexts, property_name), + "Variant" | "PlainVariant" | "TupleVariant" | "StructVariant" => { + properties::resolve_enum_variant_property(contexts, property_name) + } "Union" => properties::resolve_union_property(contexts, property_name), "Span" => properties::resolve_span_property(contexts, property_name), "Path" => properties::resolve_path_property(contexts, property_name), diff --git a/src/adapter/origin.rs b/src/adapter/origin.rs index 4033831b..707634a9 100644 --- a/src/adapter/origin.rs +++ b/src/adapter/origin.rs @@ -27,6 +27,17 @@ impl Origin { } } + pub(super) fn make_positioned_item_vertex<'a>( + &self, + index: usize, + item: &'a Item, + ) -> Vertex<'a> { + Vertex { + origin: *self, + kind: VertexKind::PositionedItem(index, item), + } + } + pub(super) fn make_span_vertex<'a>(&self, span: &'a Span) -> Vertex<'a> { Vertex { origin: *self, diff --git a/src/adapter/properties.rs b/src/adapter/properties.rs index 3a309c1d..0b11c8d5 100644 --- a/src/adapter/properties.rs +++ b/src/adapter/properties.rs @@ -127,6 +127,19 @@ pub(super) fn resolve_struct_property<'a, V: AsVertex> + 'a>( } } +pub(super) fn resolve_struct_field_property<'a, V: AsVertex> + 'a>( + contexts: ContextIterator<'a, V>, + property_name: &str, +) -> ContextOutcomeIterator<'a, V, FieldValue> { + match property_name { + "position" => resolve_property_with(contexts, |vertex| { + let (index, _) = vertex.as_positioned_item().expect("not a PositionedItem"); + (index as i64).into() + }), + _ => unreachable!("StructField property {property_name}"), + } +} + pub(super) fn resolve_span_property<'a, V: AsVertex> + 'a>( contexts: ContextIterator<'a, V>, property_name: &str, @@ -182,6 +195,19 @@ pub(super) fn resolve_union_property<'a, V: AsVertex> + 'a>( } } +pub(super) fn resolve_enum_variant_property<'a, V: AsVertex> + 'a>( + contexts: ContextIterator<'a, V>, + property_name: &str, +) -> ContextOutcomeIterator<'a, V, FieldValue> { + match property_name { + "position" => resolve_property_with(contexts, |vertex| { + let variant = vertex.as_variant().expect("not a Variant vertex"); + variant.position().into() + }), + _ => unreachable!("EnumVariant property {property_name}"), + } +} + pub(super) fn resolve_path_property<'a, V: AsVertex> + 'a>( contexts: ContextIterator<'a, V>, property_name: &str, diff --git a/src/adapter/tests.rs b/src/adapter/tests.rs index 3f0e50f2..ee03d430 100644 --- a/src/adapter/tests.rs +++ b/src/adapter/tests.rs @@ -5208,3 +5208,494 @@ fn generic_param_positions() { expected_results.sort_unstable(); similar_asserts::assert_eq!(expected_results, results); } + +#[test] +fn enum_variant_positions() { + get_test_data!(data, enum_variants_position); + let adapter = RustdocAdapter::new(&data, None); + let adapter = Arc::new(&adapter); + + let query = r#" + { + Crate { + item { + ... on Enum { + enum_name: name @output + variant { + variant_name: name @output + variant_position: position @output + variant_typename: __typename @output + } + } + } + } + } + "#; + + let variables: BTreeMap<&str, &str> = BTreeMap::new(); + let schema = + Schema::parse(include_str!("../rustdoc_schema.graphql")).expect("schema failed to parse"); + + #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)] + struct Output { + enum_name: String, + variant_name: String, + variant_position: i64, + variant_typename: String, + } + + let mut results: Vec = + trustfall::execute_query(&schema, adapter.clone(), query, variables) + .expect("failed to run query") + .map(|row| row.try_into_struct().expect("shape mismatch")) + .collect(); + results.sort_unstable(); + + let mut expected_results = vec![ + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "First".into(), + variant_position: 1, + variant_typename: "PlainVariant".into(), + }, + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "Second".into(), + variant_position: 2, + variant_typename: "TupleVariant".into(), + }, + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "Third".into(), + variant_position: 3, + variant_typename: "StructVariant".into(), + }, + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "Fourth".into(), + variant_position: 4, + variant_typename: "PlainVariant".into(), + }, + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "Fifth".into(), + variant_position: 5, + variant_typename: "TupleVariant".into(), + }, + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "Sixth".into(), + variant_position: 6, + variant_typename: "StructVariant".into(), + }, + Output { + enum_name: "WithDiscriminants".into(), + variant_name: "A".into(), + variant_position: 1, + variant_typename: "PlainVariant".into(), + }, + Output { + enum_name: "WithDiscriminants".into(), + variant_name: "B".into(), + variant_position: 2, + variant_typename: "PlainVariant".into(), + }, + Output { + enum_name: "WithDiscriminants".into(), + variant_name: "C".into(), + variant_position: 3, + variant_typename: "PlainVariant".into(), + }, + Output { + enum_name: "WithDiscriminants".into(), + variant_name: "D".into(), + variant_position: 4, + variant_typename: "PlainVariant".into(), + }, + Output { + enum_name: "WithDiscriminants".into(), + variant_name: "E".into(), + variant_position: 5, + variant_typename: "PlainVariant".into(), + }, + ]; + expected_results.sort_unstable(); + + similar_asserts::assert_eq!(expected_results, results); +} + +#[test] +fn enum_struct_variant_fields() { + get_test_data!(data, enum_variants_position); + let adapter = RustdocAdapter::new(&data, None); + let adapter = Arc::new(&adapter); + + let query = r#" + { + Crate { + item { + ... on Enum { + enum_name: name @output + variant { + ... on StructVariant { + variant_name: name @output + field { + struct_field_name: name @output + struct_field_position: position @output + } + } + } + } + } + } + } + "#; + + let variables: BTreeMap<&str, &str> = BTreeMap::new(); + let schema = + Schema::parse(include_str!("../rustdoc_schema.graphql")).expect("schema failed to parse"); + + #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)] + struct Output { + enum_name: String, + variant_name: String, + struct_field_name: String, + struct_field_position: i64, + } + + let mut results: Vec = + trustfall::execute_query(&schema, adapter.clone(), query, variables) + .expect("failed to run query") + .map(|row| row.try_into_struct().expect("shape mismatch")) + .collect(); + results.sort_unstable(); + + let mut expected_results = vec![ + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "Third".into(), + struct_field_name: "x".into(), + struct_field_position: 1, + }, + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "Third".into(), + struct_field_name: "y".into(), + struct_field_position: 2, + }, + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "Sixth".into(), + struct_field_name: "name".into(), + struct_field_position: 1, + }, + ]; + + expected_results.sort_unstable(); + + similar_asserts::assert_eq!(expected_results, results); +} + +#[test] +fn enum_tuple_variant_fields() { + get_test_data!(data, enum_variants_position); + let adapter = RustdocAdapter::new(&data, None); + let adapter = Arc::new(&adapter); + + let query = r#" + { + Crate { + item { + ... on Enum { + enum_name: name @output + variant { + ... on TupleVariant { + variant_name: name @output + field { + tuple_field_name: name @output + tuple_field_position: position @output + } + } + } + } + } + } + } + "#; + + let variables: BTreeMap<&str, &str> = BTreeMap::new(); + let schema = + Schema::parse(include_str!("../rustdoc_schema.graphql")).expect("schema failed to parse"); + + #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)] + struct Output { + enum_name: String, + variant_name: String, + tuple_field_name: String, + tuple_field_position: i64, + } + + let mut results: Vec = + trustfall::execute_query(&schema, adapter.clone(), query, variables) + .expect("failed to run query") + .map(|row| row.try_into_struct().expect("shape mismatch")) + .collect(); + results.sort_unstable(); + + let mut expected_results = vec![ + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "Second".into(), + tuple_field_name: "0".into(), + tuple_field_position: 1, + }, + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "Second".into(), + tuple_field_name: "1".into(), + tuple_field_position: 2, + }, + Output { + enum_name: "AllVariantTypes".into(), + variant_name: "Fifth".into(), + tuple_field_name: "0".into(), + tuple_field_position: 1, + }, + ]; + + expected_results.sort_unstable(); + + similar_asserts::assert_eq!(expected_results, results); +} + +#[test] +fn struct_field_positions() { + get_test_data!(data, struct_fields_position); + let adapter = RustdocAdapter::new(&data, None); + let adapter = Arc::new(&adapter); + + let query = r#" + { + Crate { + item { + ... on Struct { + struct_name: name @output + struct_type @output + field { + field_name: name @output + field_position: position @output + } + } + } + } + } + "#; + + let variables: BTreeMap<&str, &str> = BTreeMap::new(); + let schema = + Schema::parse(include_str!("../rustdoc_schema.graphql")).expect("schema failed to parse"); + + #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)] + struct Output { + struct_name: String, + struct_type: String, + field_name: String, + field_position: i64, + } + + let mut results: Vec = + trustfall::execute_query(&schema, adapter.clone(), query, variables) + .expect("failed to run query") + .map(|row| row.try_into_struct().expect("shape mismatch")) + .collect(); + results.sort_unstable(); + + let mut expected_results = vec![ + Output { + struct_name: "PlainStruct".into(), + struct_type: "plain".into(), + field_name: "first".into(), + field_position: 1, + }, + Output { + struct_name: "PlainStruct".into(), + struct_type: "plain".into(), + field_name: "second".into(), + field_position: 2, + }, + Output { + struct_name: "PlainStruct".into(), + struct_type: "plain".into(), + field_name: "third".into(), + field_position: 3, + }, + Output { + struct_name: "TupleStruct".into(), + struct_type: "tuple".into(), + field_name: "0".into(), + field_position: 1, + }, + Output { + struct_name: "TupleStruct".into(), + struct_type: "tuple".into(), + field_name: "1".into(), + field_position: 2, + }, + Output { + struct_name: "TupleStruct".into(), + struct_type: "tuple".into(), + field_name: "2".into(), + field_position: 3, + }, + Output { + struct_name: "ReprCStruct".into(), + struct_type: "plain".into(), + field_name: "a".into(), + field_position: 1, + }, + Output { + struct_name: "ReprCStruct".into(), + struct_type: "plain".into(), + field_name: "b".into(), + field_position: 2, + }, + Output { + struct_name: "ReprCStruct".into(), + struct_type: "plain".into(), + field_name: "c".into(), + field_position: 3, + }, + Output { + struct_name: "ReprPackedStruct".into(), + struct_type: "plain".into(), + field_name: "x".into(), + field_position: 1, + }, + Output { + struct_name: "ReprPackedStruct".into(), + struct_type: "plain".into(), + field_name: "y".into(), + field_position: 2, + }, + Output { + struct_name: "ReprPackedStruct".into(), + struct_type: "plain".into(), + field_name: "z".into(), + field_position: 3, + }, + Output { + struct_name: "ReprCTupleStruct".into(), + struct_type: "tuple".into(), + field_name: "0".into(), + field_position: 1, + }, + Output { + struct_name: "ReprCTupleStruct".into(), + struct_type: "tuple".into(), + field_name: "1".into(), + field_position: 2, + }, + Output { + struct_name: "ReprCTupleStruct".into(), + struct_type: "tuple".into(), + field_name: "2".into(), + field_position: 3, + }, + Output { + struct_name: "ReprTransparentStruct".into(), + struct_type: "plain".into(), + field_name: "inner".into(), + field_position: 1, + }, + ]; + expected_results.sort_unstable(); + + similar_asserts::assert_eq!(expected_results, results); +} + +#[test] +fn union_field_positions() { + get_test_data!(data, union_fields_position); + let adapter = RustdocAdapter::new(&data, None); + let adapter = Arc::new(&adapter); + + let query = r#" + { + Crate { + item { + ... on Union { + union_name: name @output + field { + field_name: name @output + field_position: position @output + } + } + } + } + } + "#; + + let variables: BTreeMap<&str, &str> = BTreeMap::new(); + let schema = + Schema::parse(include_str!("../rustdoc_schema.graphql")).expect("schema failed to parse"); + + #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, serde::Deserialize)] + struct Output { + union_name: String, + field_name: String, + field_position: i64, + } + + let mut results: Vec = + trustfall::execute_query(&schema, adapter.clone(), query, variables) + .expect("failed to run query") + .map(|row| row.try_into_struct().expect("shape mismatch")) + .collect(); + results.sort_unstable(); + + let mut expected_results = vec![ + Output { + union_name: "SimpleUnion".into(), + field_name: "first".into(), + field_position: 1, + }, + Output { + union_name: "SimpleUnion".into(), + field_name: "second".into(), + field_position: 2, + }, + Output { + union_name: "SimpleUnion".into(), + field_name: "third".into(), + field_position: 3, + }, + Output { + union_name: "UnionWithDifferentSizes".into(), + field_name: "small".into(), + field_position: 1, + }, + Output { + union_name: "UnionWithDifferentSizes".into(), + field_name: "medium".into(), + field_position: 2, + }, + Output { + union_name: "UnionWithDifferentSizes".into(), + field_name: "large".into(), + field_position: 3, + }, + Output { + union_name: "UnionWithCompoundTypes".into(), + field_name: "int_array".into(), + field_position: 1, + }, + Output { + union_name: "UnionWithCompoundTypes".into(), + field_name: "float_array".into(), + field_position: 2, + }, + ]; + expected_results.sort_unstable(); + + similar_asserts::assert_eq!(expected_results, results); +} diff --git a/src/adapter/vertex.rs b/src/adapter/vertex.rs index c7fe6c4c..a4fe0013 100644 --- a/src/adapter/vertex.rs +++ b/src/adapter/vertex.rs @@ -77,6 +77,9 @@ pub enum VertexKind<'a> { #[non_exhaustive] Feature(Feature<'a>), + + #[non_exhaustive] + PositionedItem(usize, &'a Item), } impl Typename for Vertex<'_> { @@ -84,8 +87,8 @@ impl Typename for Vertex<'_> { /// intended to fulfill resolution requests for the __typename property. #[inline] fn typename(&self) -> &'static str { - match &self.kind { - VertexKind::Item(item) => match &item.inner { + match self.kind { + VertexKind::Item(item) | VertexKind::PositionedItem(_, item) => match &item.inner { rustdoc_types::ItemEnum::Module { .. } => "Module", rustdoc_types::ItemEnum::Struct(..) => "Struct", rustdoc_types::ItemEnum::Enum(..) => "Enum", @@ -125,7 +128,7 @@ impl Typename for Vertex<'_> { VertexKind::FunctionParameter(..) => "FunctionParameter", VertexKind::FunctionAbi(..) => "FunctionAbi", VertexKind::Discriminant(..) => "Discriminant", - VertexKind::Variant(ev) => match ev.variant().kind { + VertexKind::Variant(ref ev) => match ev.variant().kind { VariantKind::Plain => "PlainVariant", VariantKind::Tuple(..) => "TupleVariant", VariantKind::Struct { .. } => "StructVariant", @@ -180,6 +183,14 @@ impl<'a> Vertex<'a> { match &self.kind { VertexKind::Item(item) => Some(item), VertexKind::Variant(variant) => Some(variant.item()), + VertexKind::PositionedItem(_, item) => Some(item), + _ => None, + } + } + + pub(super) fn as_positioned_item(&self) -> Option<(usize, &'a Item)> { + match &self.kind { + VertexKind::PositionedItem(index, item) => Some((*index, item)), _ => None, } } diff --git a/src/rustdoc_schema.graphql b/src/rustdoc_schema.graphql index fcf803ed..3869fa72 100644 --- a/src/rustdoc_schema.graphql +++ b/src/rustdoc_schema.graphql @@ -377,6 +377,11 @@ type StructField implements Item { visibility_limit: String! + """ + The 1-based position of this field in the struct's definition. + """ + position: Int! + # edges from Item span: Span attribute: [Attribute!] @@ -520,6 +525,11 @@ interface Variant implements Item { """ public_api_eligible: Boolean! + """ + The 1-based position of this variant in the enum's definition. + """ + position: Int! + visibility_limit: String! # edges from Item @@ -607,6 +617,11 @@ type PlainVariant implements Item & Variant { visibility_limit: String! + """ + The 1-based position of this variant in the enum's definition. + """ + position: Int! + # edges from Item span: Span attribute: [Attribute!] @@ -679,6 +694,11 @@ type TupleVariant implements Item & Variant { """ public_api_eligible: Boolean! + """ + The 1-based position of this variant in the enum's definition. + """ + position: Int! + visibility_limit: String! # edges from Item @@ -755,6 +775,11 @@ type StructVariant implements Item & Variant { visibility_limit: String! + """ + The 1-based position of this variant in the enum's definition. + """ + position: Int! + # edges from Item span: Span attribute: [Attribute!] diff --git a/test_crates/enum_variants_position/Cargo.toml b/test_crates/enum_variants_position/Cargo.toml new file mode 100644 index 00000000..81b277fb --- /dev/null +++ b/test_crates/enum_variants_position/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "enum_variants_position" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/test_crates/enum_variants_position/src/lib.rs b/test_crates/enum_variants_position/src/lib.rs new file mode 100644 index 00000000..6afe112d --- /dev/null +++ b/test_crates/enum_variants_position/src/lib.rs @@ -0,0 +1,21 @@ +pub enum AllVariantTypes { + First, // plain/unit variant + Second(u32, String), // tuple variant + Third { x: i32, y: i32 }, // struct variant + Fourth, // another plain variant + Fifth(bool), // another tuple variant + Sixth { name: String }, // another struct variant +} + +// Test explicit discriminant values +#[repr(u8)] +pub enum WithDiscriminants { + A, // 0 + B = 5, // 5 + C, // 6 + D = 10, // 10 + E, // 11 +} + +// Test empty enum +pub enum Empty {} diff --git a/test_crates/struct_fields_position/Cargo.toml b/test_crates/struct_fields_position/Cargo.toml new file mode 100644 index 00000000..7894e1d2 --- /dev/null +++ b/test_crates/struct_fields_position/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "struct_fields_position" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/test_crates/struct_fields_position/src/lib.rs b/test_crates/struct_fields_position/src/lib.rs new file mode 100644 index 00000000..59767fc2 --- /dev/null +++ b/test_crates/struct_fields_position/src/lib.rs @@ -0,0 +1,33 @@ +pub struct PlainStruct { + pub first: i32, + pub second: String, + pub third: bool, +} + +pub struct TupleStruct(pub i32, pub String, pub bool); + +#[repr(C)] +pub struct ReprCStruct { + pub a: i32, + pub b: String, + pub c: bool, +} + +#[repr(packed)] +pub struct ReprPackedStruct { + pub x: i32, + pub y: String, + pub z: bool, +} + +#[repr(C)] +pub struct ReprCTupleStruct(pub i32, pub String, pub bool); + +#[repr(transparent)] +pub struct ReprTransparentStruct { + pub inner: i32, +} + +pub struct EmptyStruct {} + +pub struct UnitStruct; diff --git a/test_crates/union_fields_position/Cargo.toml b/test_crates/union_fields_position/Cargo.toml new file mode 100644 index 00000000..648e4d86 --- /dev/null +++ b/test_crates/union_fields_position/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "union_fields_position" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/test_crates/union_fields_position/src/lib.rs b/test_crates/union_fields_position/src/lib.rs new file mode 100644 index 00000000..b645367e --- /dev/null +++ b/test_crates/union_fields_position/src/lib.rs @@ -0,0 +1,21 @@ +#[repr(C)] +pub union SimpleUnion { + pub first: i32, + pub second: f32, + pub third: u32, +} + +#[repr(C)] +pub union UnionWithDifferentSizes { + pub small: u8, + pub medium: u32, + pub large: u64, +} + +#[repr(C)] +pub union UnionWithCompoundTypes { + pub int_array: [i32; 4], + pub float_array: [f32; 4], +} + +// Empty union is not allowed in Rust