Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion lib/ffi_generator/clang/cursor.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'zlib'

module FFIGenerate
module Clang

Expand Down Expand Up @@ -84,7 +86,15 @@ def result_type
end

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
Comment on lines +93 to +94
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
prefix = usr.split('@')[2]
prefix = 'unnamed' unless prefix
prefix = usr.split('@')[2].to_s
prefix = 'unnamed' if prefix.empty? || prefix !~ /\A[[:alpha:]]/

Copilot uses AI. Check for mistakes.
spelling = prefix + Zlib::crc32(usr).to_s
end
Comment on lines 88 to +96
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
spelling
end

def ==(other)
Expand Down
Loading