From 161698e0df259f9e1c78d4d4198d9d685900dba9 Mon Sep 17 00:00:00 2001 From: Ekaterina Vaartis Date: Sun, 6 Dec 2020 15:20:55 +0300 Subject: [PATCH] Implement pre-defining structs/unions When a struct or a union is already defined in a lib with the same name as the transformer would generated, use that defined structure instead of generating a new one. One particular usecase for this is defining structures that use bitfields manually and then using those instead of the incorrectly generated by crystal_lib. --- spec/lib_body_transformer_spec.cr | 14 ++++++++++++++ src/crystal_lib/lib_body_transformer.cr | 9 +++++++++ src/crystal_lib/type_mapper.cr | 25 +++++++++++++++++-------- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/spec/lib_body_transformer_spec.cr b/spec/lib_body_transformer_spec.cr index 4df8ea7..f6e9736 100644 --- a/spec/lib_body_transformer_spec.cr +++ b/spec/lib_body_transformer_spec.cr @@ -261,4 +261,18 @@ describe LibBodyTransformer do end fun some_struct_with_other_struct_pointer(handle : StructBar*) ) + + # Verify that an additional struct does not get generated + assert_transform "simple", + %( + struct SomeStruct1 + x : LibC::Int + end + fun just_some_struct_1 + ), %( + struct SomeStruct1 + x : LibC::Int + end + fun just_some_struct_1 : SomeStruct1 + ) end diff --git a/src/crystal_lib/lib_body_transformer.cr b/src/crystal_lib/lib_body_transformer.cr index cc321d7..f65112e 100644 --- a/src/crystal_lib/lib_body_transformer.cr +++ b/src/crystal_lib/lib_body_transformer.cr @@ -81,6 +81,15 @@ class CrystalLib::LibBodyTransformer < Crystal::Transformer check_pending_definitions(node) end + def transform(node : Crystal::CStructOrUnionDef) + @mapper.predefined_structs_or_unions << node + + # There will be no pending definitions, since the node will + # be skipped and replaced with the current one when going through + # its usage in the header + node + end + def map_type(type) @mapper.map(type) end diff --git a/src/crystal_lib/type_mapper.cr b/src/crystal_lib/type_mapper.cr index 41ea9bc..f6520de 100644 --- a/src/crystal_lib/type_mapper.cr +++ b/src/crystal_lib/type_mapper.cr @@ -5,6 +5,7 @@ class CrystalLib::TypeMapper original_name : String getter pending_definitions + getter predefined_structs_or_unions @typedef_name : String? @@ -12,6 +13,7 @@ class CrystalLib::TypeMapper @pending_definitions = [] of Crystal::ASTNode @pending_structs = [] of PendingStruct @generated = {} of UInt64 => Crystal::ASTNode + @predefined_structs_or_unions = [] of Crystal::CStructOrUnionDef # When completing a struct's fields we keep that struct and the field name in # case we find a nested struct, such as in:# @@ -163,18 +165,25 @@ class CrystalLib::TypeMapper untouched_struct_name = check_anonymous_name(type.unscoped_name) struct_name = crystal_type_name(untouched_struct_name) - if type.fields.empty? - # For an empty struct we just return an alias to Void - struct_def = Crystal::Alias.new(path(struct_name), path(["Void"])).tap(&.doc = type.doc) + # Try finding a struct or union that has already been defined. If such + # a struct/union exist, use their definition instead and don't insert anything + found_predefined = @predefined_structs_or_unions.find { |predefined| predefined.name == struct_name } + if found_predefined + struct_def = found_predefined else - struct_def = Crystal::CStructOrUnionDef.new(struct_name, union: type.kind == :union).tap(&.doc = type.doc) + if type.fields.empty? + # For an empty struct we just return an alias to Void + struct_def = Crystal::Alias.new(path(struct_name), path(["Void"])).tap(&.doc = type.doc) + else + struct_def = Crystal::CStructOrUnionDef.new(struct_name, union: type.kind == :union).tap(&.doc = type.doc) + + # Leave struct body for later, because of possible recursiveness + @pending_structs << PendingStruct.new(struct_def, type, untouched_struct_name) + end - # Leave struct body for later, because of possible recursiveness - @pending_structs << PendingStruct.new(struct_def, type, untouched_struct_name) + @pending_definitions << struct_def unless @generated.has_key?(type.object_id) end - @pending_definitions << struct_def unless @generated.has_key?(type.object_id) - path(struct_name) end