22//LICENSE
33//LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file.
44
5+ //! Extract the Rust half of the C macro type contract from fresh bindgen output.
6+ //!
7+ //! The frontend supplies C declarations and target facts; this collector records
8+ //! what the current binding build actually exposes: paths, storage, signatures,
9+ //! record fields, and integer values. Emission must reconcile both catalogs before
10+ //! using a Rust symbol. Ambiguous or unrepresentable declarations are omitted
11+ //! rather than repaired, so the transpiler can report a skip without changing
12+ //! bindgen's output. Checked-in documentation bindings are not semantic inputs.
13+
14+ /// Use the emitter catalog and result types that connect C inspection to generated binding
15+ /// publication.
516use pgrx_c_macros:: {
617 AliasBinding , BindingCatalog , BitfieldBinding , DeclarationCatalog , EnumBinding , FieldBinding ,
718 FunctionBinding , IntegerBinding , IntegerBindingRepresentation , IntegerConstant , IntegerKind ,
819 IntegerValue , RecordBinding , RecordKind , RustBindingType , TargetFacts , VariableBinding ,
920} ;
21+ /// Keep binding catalogs, header trees, and output maps ordered for deterministic generation
22+ /// and collision checks.
1023use std:: collections:: { BTreeMap , BTreeSet } ;
24+ /// Inspect actual Rust syntax and spans instead of reconstructing binding or generated-item
25+ /// facts from names.
1126use syn:: { Expr , Item , Lit , Type , UnOp } ;
1227
1328/// Discover bindgen's actual values and paths without changing its bindings.
@@ -72,6 +87,8 @@ pub(super) fn collect_bindings(
7287 bindings
7388}
7489
90+ /// Recover bit offsets and widths from actual bindgen getter bodies and verified storage units;
91+ /// getter names alone are not layout evidence.
7592fn collect_bitfields (
7693 items : & [ Item ] ,
7794 path : & mut Vec < String > ,
@@ -178,9 +195,18 @@ fn collect_bitfields(
178195 }
179196}
180197
198+ /// Collect literal storage-unit reads from a bindgen getter so ambiguous or unrelated methods
199+ /// cannot establish bitfield layout.
181200#[ derive( Default ) ]
182- struct BitfieldAccess ( Vec < ( String , u64 , u32 ) > ) ;
201+ struct BitfieldAccess (
202+ /// Observed storage field, bit offset, and width tuples collected from the getter body.
203+ Vec < ( String , u64 , u32 ) > ,
204+ ) ;
205+ /// Recover bitfield layout from actual getter access syntax rather than method naming
206+ /// conventions.
183207impl syn:: visit_mut:: VisitMut for BitfieldAccess {
208+ /// Record literal bindgen storage-unit reads while visiting the rest of the getter body,
209+ /// allowing ambiguous access shapes to be rejected.
184210 fn visit_expr_method_call_mut ( & mut self , call : & mut syn:: ExprMethodCall ) {
185211 if call. method == "get"
186212 && call. args . len ( ) == 2
@@ -198,12 +224,16 @@ impl syn::visit_mut::VisitMut for BitfieldAccess {
198224 }
199225}
200226
227+ /// Accept only a literal unsigned value for layout metadata rather than evaluating arbitrary
228+ /// Rust expressions.
201229fn literal_u64 ( expression : & Expr ) -> Option < u64 > {
202230 let Expr :: Lit ( literal) = expression else { return None } ;
203231 let Lit :: Int ( literal) = & literal. lit else { return None } ;
204232 literal. base10_parse ( ) . ok ( )
205233}
206234
235+ /// Recognize a bindgen bitfield storage unit with concrete byte-array storage so reported
236+ /// ranges can be bounded.
207237fn bitfield_unit_bytes ( ty : & Type ) -> Option < u64 > {
208238 let Type :: Path ( ty) = ty else { return None } ;
209239 let segment = ty. path . segments . last ( ) ?;
@@ -228,14 +258,23 @@ fn bitfield_unit_bytes(ty: &Type) -> Option<u64> {
228258/// collisions between public modules cannot accidentally select one C declaration.
229259#[ derive( Default ) ]
230260struct StructuredDuplicates {
261+ /// Foreign function names that cannot be selected unambiguously.
231262 functions : BTreeSet < String > ,
263+ /// Record names duplicated across accessible public modules.
232264 records : BTreeSet < String > ,
265+ /// Type alias paths repeated in the collected type namespace.
233266 types : BTreeSet < String > ,
267+ /// Enum names whose representation cannot be chosen uniquely.
234268 enums : BTreeSet < String > ,
269+ /// Foreign variable names that occur at multiple accessible paths.
235270 variables : BTreeSet < String > ,
236271}
237272
273+ /// Remove ambiguous bindings only after scanning the entire namespace, so traversal order
274+ /// cannot select a winner.
238275impl StructuredDuplicates {
276+ /// Remove all names observed more than once in a structured namespace so emission cannot
277+ /// choose an arbitrary binding.
239278 fn remove ( self , bindings : & mut BindingCatalog ) {
240279 for name in self . functions {
241280 bindings. functions . remove ( & name) ;
@@ -255,6 +294,8 @@ impl StructuredDuplicates {
255294 }
256295}
257296
297+ /// Insert a binding while remembering duplicate names for deferred removal after the full
298+ /// catalog scan.
258299fn insert_unique < T > (
259300 map : & mut BTreeMap < String , T > ,
260301 duplicates : & mut BTreeSet < String > ,
@@ -266,14 +307,17 @@ fn insert_unique<T>(
266307 }
267308}
268309
310+ /// Restrict the catalog to bindings generated code can name through public paths.
269311fn public ( vis : & syn:: Visibility ) -> bool {
270312 matches ! ( vis, syn:: Visibility :: Public ( _) )
271313}
272314
315+ /// Remove raw-identifier syntax for C-name matching while keeping actual Rust paths separately.
273316fn rust_name ( ident : & syn:: Ident ) -> String {
274317 ident. to_string ( ) . trim_start_matches ( "r#" ) . to_owned ( )
275318}
276319
320+ /// Extend the current public module path with the item's actual Rust identifier.
277321fn item_path ( path : & [ String ] , ident : & syn:: Ident ) -> Vec < String > {
278322 let mut result = path. to_vec ( ) ;
279323 result. push ( ident. to_string ( ) ) ;
@@ -349,6 +393,8 @@ fn flexible_array_helpers(items: &[Item], path: &mut Vec<String>) -> BTreeSet<Ve
349393 verified
350394}
351395
396+ /// Walk public bindings and record callable signatures, variables, aliases, record storage, and
397+ /// enum representation for C/Rust reconciliation.
352398fn collect_structures (
353399 items : & [ Item ] ,
354400 path : & mut Vec < String > ,
@@ -498,6 +544,8 @@ fn collect_structures(
498544 }
499545}
500546
547+ /// Capture named public field storage and representation attributes without claiming the
548+ /// corresponding C layout is already verified.
501549fn record_binding (
502550 ident : & syn:: Ident ,
503551 attrs : & [ syn:: Attribute ] ,
@@ -527,6 +575,7 @@ fn record_binding(
527575 RecordBinding { path : item_path ( path, ident) , kind, fields, packed, copy }
528576}
529577
578+ /// Parse representation and derive metadata used to establish actual Rust storage properties.
530579fn attribute_arguments ( attrs : & [ syn:: Attribute ] , name : & str ) -> Vec < syn:: Meta > {
531580 attrs
532581 . iter ( )
@@ -542,6 +591,8 @@ fn attribute_arguments(attrs: &[syn::Attribute], name: &str) -> Vec<syn::Meta> {
542591 . collect ( )
543592}
544593
594+ /// Record integral Rust enum storage and exact variant values, rejecting payload variants or
595+ /// unevaluable discriminants.
545596fn enum_binding (
546597 item : & syn:: ItemEnum ,
547598 path : & [ String ] ,
@@ -573,11 +624,15 @@ fn enum_binding(
573624 Some ( EnumBinding { path : item_path ( path, & item. ident ) , repr, variants } )
574625}
575626
627+ /// Keep the declared calling convention, including Rust defaults, so the emitter can reject an
628+ /// incompatible C call.
576629fn abi_name ( abi : Option < & syn:: Abi > ) -> String {
577630 abi. map ( |abi| abi. name . as_ref ( ) . map_or_else ( || "C" . into ( ) , syn:: LitStr :: value) )
578631 . unwrap_or_else ( || "Rust" . into ( ) )
579632}
580633
634+ /// Recover a foreign symbol's linker spelling so ordinary and cshim-backed calls remain
635+ /// distinguishable.
581636fn link_name ( attrs : & [ syn:: Attribute ] ) -> Option < String > {
582637 attrs. iter ( ) . find_map ( |attribute| {
583638 let syn:: Meta :: NameValue ( meta) = & attribute. meta else { return None } ;
@@ -590,6 +645,7 @@ fn link_name(attrs: &[syn::Attribute]) -> Option<String> {
590645 } )
591646}
592647
648+ /// Represent omitted Rust returns as unit and recursively catalog explicit return storage.
593649fn return_type (
594650 output : & syn:: ReturnType ,
595651 path : & [ String ] ,
@@ -603,6 +659,9 @@ fn return_type(
603659 }
604660}
605661
662+ /// Translate supported Rust binding storage into the catalog while preserving pointer
663+ /// mutability, callback ABI, wrappers, and named paths; reject unknown or excessively deep
664+ /// forms.
606665fn binding_type (
607666 ty : & Type ,
608667 path : & [ String ] ,
@@ -753,6 +812,8 @@ fn binding_type(
753812 }
754813}
755814
815+ /// Resolve relative public binding paths without letting a path escape the owning crate
816+ /// namespace.
756817fn resolve_path ( parts : & [ String ] , current : & [ String ] ) -> Option < Vec < String > > {
757818 let mut resolved = current. to_vec ( ) ;
758819 let mut position = 0 ;
@@ -777,6 +838,8 @@ fn resolve_path(parts: &[String], current: &[String]) -> Option<Vec<String>> {
777838 Some ( resolved)
778839}
779840
841+ /// Walk public integer bindings and retain representable values and paths, including associated
842+ /// enum constants and checked OID storage.
780843fn collect (
781844 items : & [ Item ] ,
782845 path : & mut Vec < String > ,
@@ -833,14 +896,23 @@ fn collect(
833896 }
834897}
835898
899+ /// Represent verified integer storage bounds independently of the C semantic kind used by the
900+ /// emitter.
836901#[ derive( Clone , Copy ) ]
837902struct Storage {
903+ /// Whether this Rust storage can represent negative C constants.
838904 signed : bool ,
905+ /// Width verified from primitive spelling or the selected target's standard C aliases.
839906 bits : u32 ,
907+ /// Whether storage accepts only zero or one rather than the full integer range.
840908 boolean : bool ,
841909}
842910
911+ /// Enforce the actual Rust storage range before a binding value can participate in symbolic C
912+ /// emission.
843913impl Storage {
914+ /// Check that the bindgen value fits its actual storage before it can be referenced as a C
915+ /// constant.
844916 fn contains ( self , value : i128 ) -> bool {
845917 if self . boolean {
846918 return ( 0 ..=1 ) . contains ( & value) ;
@@ -854,6 +926,8 @@ impl Storage {
854926 }
855927}
856928
929+ /// Recognize primitive or standard-library C integer storage using target widths; arbitrary
930+ /// same-spelled aliases provide no proof.
857931fn storage (
858932 ty : & Type ,
859933 aliases : & BTreeMap < String , & Type > ,
@@ -925,13 +999,17 @@ fn storage(
925999 } )
9261000}
9271001
1002+ /// Promote a recorded integer value to i128 for bounded signed arithmetic during literal
1003+ /// interpretation.
9281004fn numeric ( value : IntegerValue ) -> i128 {
9291005 match value {
9301006 IntegerValue :: Signed ( value) => value. into ( ) ,
9311007 IntegerValue :: Unsigned ( value) => value. into ( ) ,
9321008 }
9331009}
9341010
1011+ /// Read literal, boolean, negated, and grouped binding values without substituting a newly
1012+ /// evaluated expression for bindgen's output.
9351013fn rust_value ( expr : & Expr ) -> Option < IntegerValue > {
9361014 match expr {
9371015 Expr :: Lit ( expr) => match & expr. lit {
@@ -948,12 +1026,29 @@ fn rust_value(expr: &Expr) -> Option<IntegerValue> {
9481026 }
9491027}
9501028
1029+ /// Check catalog, publication, and invalidation invariants directly against the private
1030+ /// binding-build implementation.
9511031#[ cfg( test) ]
9521032mod tests {
1033+ //! Check catalog, publication, and invalidation invariants directly against the private
1034+ //! binding-build implementation.
1035+ //!
1036+ //! The tests inspect real parsed output, generated leaf maps, and compiler dependency
1037+ //! directives. Temporary input trees make success and rejection conditions explicit without
1038+ //! relying on a configured server.
1039+
1040+ /// Exercise the private implementation directly in its unit tests without widening the
1041+ /// production API.
9531042 use super :: * ;
1043+ /// Use the emitter catalog and result types that connect C inspection to generated binding
1044+ /// publication.
9541045 use pgrx_c_macros:: { ByteOrder , IntegerType , TypeCategory , TypeInfo } ;
1046+ /// Render parsed fixture syntax for the independent generated consumer without rewriting
1047+ /// semantic declarations.
9551048 use quote:: ToTokens ;
9561049
1050+ /// Construct explicit target integer facts for collector tests so storage checks do not
1051+ /// depend on the test host.
9571052 fn target ( ) -> TargetFacts {
9581053 TargetFacts {
9591054 triple : "x86_64-unknown-linux-gnu" . into ( ) ,
@@ -979,6 +1074,8 @@ mod tests {
9791074 }
9801075 }
9811076
1077+ /// Construct a compiler-side integer constant used to test reconciliation with
1078+ /// independently written binding syntax.
9821079 fn constant ( kind : IntegerKind , value : IntegerValue ) -> IntegerConstant {
9831080 IntegerConstant {
9841081 ty : TypeInfo {
@@ -995,6 +1092,8 @@ mod tests {
9951092 }
9961093 }
9971094
1095+ /// Checks actual bindgen values, primitive storage, and enum/OID paths without replacing
1096+ /// them with Clang's preferred values.
9981097 #[ test]
9991098 fn preserves_bindgen_values_storage_and_enum_oid_paths ( ) {
10001099 let file = syn:: parse_file (
@@ -1053,6 +1152,8 @@ pub mod Negative { pub type Type = ::core::ffi::c_int; pub const NEG: Type = -1;
10531152 ) ) ;
10541153 }
10551154
1155+ /// Checks that associated enum variants retain actual Rust paths and values for later C
1156+ /// disagreement validation.
10561157 #[ test]
10571158 fn collects_rustified_enum_symbols_without_replacing_bindgen_values ( ) {
10581159 let file = syn:: parse_file (
@@ -1083,6 +1184,8 @@ pub mod B { #[repr(u32)] pub enum Second { DUP = 1 } }
10831184 assert_eq ! ( file. to_token_stream( ) . to_string( ) , original) ;
10841185 }
10851186
1187+ /// Checks that duplicate paths, unproved storage, and nonintegral values are omitted
1188+ /// instead of guessed.
10861189 #[ test]
10871190 fn rejects_ambiguous_paths_unresolved_storage_and_nonintegral_constants ( ) {
10881191 let file = syn:: parse_file (
@@ -1112,6 +1215,8 @@ pub const CYCLE: Loop = 7;
11121215 ) ;
11131216 }
11141217
1218+ /// Checks that the catalog preserves actual foreign ABI, aliases, record fields, and nested
1219+ /// callback storage for reconciliation.
11151220 #[ test]
11161221 fn catalogs_actual_function_abi_aliases_records_and_callback_types ( ) {
11171222 let file = syn:: parse_file (
@@ -1206,6 +1311,8 @@ unsafe extern "C" {
12061311 assert ! ( !catalog. variables[ "Frozen" ] . mutable) ;
12071312 }
12081313
1314+ /// Checks storage-unit widths and offsets from genuine getter accesses while rejecting
1315+ /// unrelated getter-shaped methods.
12091316 #[ test]
12101317 fn recovers_bitfield_layout_from_actual_bindgen_getter_bodies ( ) {
12111318 let file = syn:: parse_file (
@@ -1232,6 +1339,8 @@ impl Bits {
12321339 assert_eq ! ( flag. ty, RustBindingType :: Integer { signed: false , bits: 32 } ) ;
12331340 }
12341341
1342+ /// Checks standard-library C aliases inside function signatures and fields using the
1343+ /// selected target facts.
12351344 #[ test]
12361345 fn collects_actual_std_os_raw_function_and_field_storage ( ) {
12371346 let file = syn:: parse_file (
@@ -1258,6 +1367,8 @@ unsafe extern "C" {
12581367 ) ;
12591368 }
12601369
1370+ /// Checks that ambiguous or unsupported signatures and record forms cannot enter the usable
1371+ /// binding catalog.
12611372 #[ test]
12621373 fn rejects_ambiguous_and_unrepresentable_structured_bindings ( ) {
12631374 let file = syn:: parse_file (
@@ -1288,6 +1399,8 @@ unsafe extern "C" {
12881399 matches!( & catalog. functions[ "good" ] . result, RustBindingType :: Pointer { pointee, .. } if * * pointee == RustBindingType :: Named { path: vec![ "core" . into( ) , "ffi" . into( ) , "c_void" . into( ) ] } )
12891400 ) ;
12901401 }
1402+ /// Checks generic storage helpers from their emitted definitions rather than trusting
1403+ /// conventional helper names.
12911404 #[ test]
12921405 fn verifies_generic_union_and_flexible_array_storage_definitions ( ) {
12931406 let file = syn:: parse_file ( r#"
0 commit comments