From 7f91cf1dc60c13d1f072824b21811c749353552c Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Mon, 13 Apr 2026 18:34:16 +0900 Subject: [PATCH] Fix NoMethodError in AST diff for record types SigTyRecordNode stored its fields as a Hash subnode, but the diff method only handles Node and Array subnodes. Split @fields into @keys (attrs) and @vals (subnodes) so diff works correctly when editing code with inline record type annotations. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/typeprof/core/ast/sig_type.rb | 24 +++++++++++++----------- scenario/rbs/record-diff.rb | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 scenario/rbs/record-diff.rb diff --git a/lib/typeprof/core/ast/sig_type.rb b/lib/typeprof/core/ast/sig_type.rb index cca910ac..009e1305 100644 --- a/lib/typeprof/core/ast/sig_type.rb +++ b/lib/typeprof/core/ast/sig_type.rb @@ -766,16 +766,18 @@ def show class SigTyRecordNode < SigTyNode def initialize(raw_decl, lenv) super(raw_decl, lenv) - @fields = raw_decl.fields.transform_values { |val| AST.create_rbs_type(val, lenv) } + @keys = raw_decl.fields.keys + @vals = raw_decl.fields.values.map { |val| AST.create_rbs_type(val, lenv) } end - attr_reader :fields - def subnodes = { fields: } + attr_reader :keys, :vals + def subnodes = { vals: } + def attrs = { keys: } def covariant_vertex0(genv, changes, vtx, subst) field_vertices = {} - @fields.each do |key, field_node| - field_vertices[key] = field_node.covariant_vertex(genv, changes, subst) + @keys.zip(@vals) do |key, val_node| + field_vertices[key] = val_node.covariant_vertex(genv, changes, subst) end # Create base Hash type for Record @@ -792,8 +794,8 @@ def covariant_vertex0(genv, changes, vtx, subst) def contravariant_vertex0(genv, changes, vtx, subst) field_vertices = {} - @fields.each do |key, field_node| - field_vertices[key] = field_node.contravariant_vertex(genv, changes, subst) + @keys.zip(@vals) do |key, val_node| + field_vertices[key] = val_node.contravariant_vertex(genv, changes, subst) end # Create base Hash type for Record @@ -816,9 +818,9 @@ def typecheck(genv, changes, vtx, subst) found_any = true case ty when Type::Hash - @fields.each do |key, field_node| + @keys.zip(@vals) do |key, val_node| val_vtx = ty.get_value(key) - return false unless field_node.typecheck(genv, changes, val_vtx, subst) + return false unless val_node.typecheck(genv, changes, val_vtx, subst) end return true end @@ -827,8 +829,8 @@ def typecheck(genv, changes, vtx, subst) end def show - field_strs = @fields.map do |key, field_node| - "#{ key }: #{ field_node.show }" + field_strs = @keys.zip(@vals).map do |key, val_node| + "#{ key }: #{ val_node.show }" end "{ #{ field_strs.join(", ") } }" end diff --git a/scenario/rbs/record-diff.rb b/scenario/rbs/record-diff.rb new file mode 100644 index 00000000..5d7a8f56 --- /dev/null +++ b/scenario/rbs/record-diff.rb @@ -0,0 +1,18 @@ +## update: test.rb +#: ({ name: String, age: Integer }) -> void +def accept_user(user) + name = user[:name] + age = user[:age] +end + +## update: test.rb +#: ({ name: String, age: Integer }) -> void +def accept_user(user) + name = user[:name] + age = user[:age] +end + +## assert: test.rb +class Object + def accept_user: ({ name: String, age: Integer }) -> (Integer | Object) +end