Conversation
There was a problem hiding this comment.
Pull request overview
Fixes invalid Ruby output when generating bindings for anonymous/unnamed C structs, unions, and enums by ensuring such declarations receive a usable synthesized name.
Changes:
- Add
zlibdependency for hashing. - Update
Clang::Cursor#spellingto detect clang’s(unnamed at ...)spellings for struct/union/enum cursors. - Generate a deterministic replacement name using the cursor USR plus a CRC32 hash.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| prefix = usr.split('@')[2] | ||
| prefix = 'unnamed' unless prefix |
There was a problem hiding this comment.
The fallback prefix = 'unnamed' unless prefix doesn’t cover cases where usr.split('@')[2] returns an empty string (or a numeric-only segment). In that case spelling can become something like "123…", which later turns into a Ruby class/constant name starting with _ (e.g. _123…) and is still invalid for class/FFI::Struct constants. Please treat nil and empty/invalid prefixes the same (e.g., default to a leading alpha prefix like unnamed), so the synthesized identifier is always a valid Ruby constant/symbol.
| prefix = usr.split('@')[2] | |
| prefix = 'unnamed' unless prefix | |
| prefix = usr.split('@')[2].to_s | |
| prefix = 'unnamed' if prefix.empty? || prefix !~ /\A[[:alpha:]]/ |
| def spelling | ||
| String.from_c(C.get_cursor_spelling(@c)).to_s | ||
| spelling = String.from_c(C.get_cursor_spelling(@c)).to_s | ||
| # Fix names for unnamed items | ||
| if [:struct_decl, :union_decl, :enum_decl].include?(kind) && spelling.match?(/(struct|union|enum) \(unnamed at/) | ||
| usr = String.from_c(C.get_cursor_usr(@c)).to_s | ||
| prefix = usr.split('@')[2] | ||
| prefix = 'unnamed' unless prefix | ||
| spelling = prefix + Zlib::crc32(usr).to_s | ||
| end |
There was a problem hiding this comment.
Synthesizing a non-empty Cursor#spelling for unnamed structs/unions/enums will prevent downstream renaming paths that rely on name.nil? (e.g., nested anonymous records get renamed from the containing type + field name in generator.rb:318, and anonymous records/enums under typedefs get renamed in generator.rb:417). This can cause large, breaking output-name changes (and less readable names) even when a better name is available. Consider moving the "unnamed" handling to the generator (or otherwise allowing typedef/field-based renames to override), and only mint a synthetic name when the declaration would otherwise remain unnamed in the final output.
Currently if you try to generate mappings that contain unnamed/anonymous C structs, unions or enums then it will generate invalid Ruby code.
For example this C code:
will produce this Ruby code:
Which is not valid Ruby.
Same issue for:
And
This PR fixes these issues so these unnamed items will still get name.
For example this is how they'll look like