diff --git a/lib/rbs/subtractor.rb b/lib/rbs/subtractor.rb index d772505c8..18c8a33c3 100644 --- a/lib/rbs/subtractor.rb +++ b/lib/rbs/subtractor.rb @@ -50,13 +50,46 @@ def call(minuend = @minuend, context: nil) context = _ = [context, decl.name] children = call(decl.each_decl.to_a, context: context) + - decl.each_member.reject { |m| member_exist?(owner, m, context: context) } + decl.each_member.filter_map { |m| subtract_member(owner, m, context: context) } children = filter_redundant_access_modifiers(children) return nil if children.empty? update_decl(decl, members: children) end + private def subtract_member(owner, member, context:) + if member.is_a?(AST::Members::AttrAccessor) + reader_exists = method_exist?(owner, member.name, member.kind) + writer_exists = method_exist?(owner, :"#{member.name}=", member.kind) + + case + when reader_exists && writer_exists + nil + when reader_exists + accessor_part(member, AST::Members::AttrWriter) + when writer_exists + accessor_part(member, AST::Members::AttrReader) + else + member + end + else + member unless member_exist?(owner, member, context: context) + end + end + + private def accessor_part(member, member_class) + member_class.new( + name: member.name, + type: member.type, + ivar_name: member.ivar_name, + kind: member.kind, + annotations: member.annotations, + location: member.location, + comment: member.comment, + visibility: member.visibility + ) + end + private def member_exist?(owner, member, context:) case member when AST::Members::MethodDefinition @@ -67,9 +100,6 @@ def call(minuend = @minuend, context: nil) method_exist?(owner, member.name, member.kind) when AST::Members::AttrWriter method_exist?(owner, :"#{member.name}=", member.kind) - when AST::Members::AttrAccessor - # TODO: It unexpectedly removes attr_accessor even if either reader or writer does not exist in the subtrahend. - method_exist?(owner, member.name, member.kind) || method_exist?(owner, :"#{member.name}=", member.kind) when AST::Members::InstanceVariable ivar_exist?(owner, member.name, :instance) when AST::Members::ClassInstanceVariable diff --git a/sig/subtractor.rbs b/sig/subtractor.rbs index 1a9019f1a..632bde4ab 100644 --- a/sig/subtractor.rbs +++ b/sig/subtractor.rbs @@ -11,6 +11,10 @@ module RBS private def filter_members: (decl_with_members, context: Resolver::context) -> decl_with_members? + private def subtract_member: (TypeName owner, AST::Members::t member, context: Resolver::context) -> AST::Members::t? + + private def accessor_part: (AST::Members::AttrAccessor member, singleton(AST::Members::AttrReader) | singleton(AST::Members::AttrWriter) member_class) -> (AST::Members::AttrReader | AST::Members::AttrWriter) + private def member_exist?: (TypeName owner, AST::Members::t, context: Resolver::context) -> boolish private def method_exist?: (TypeName owner, Symbol method_name, AST::Members::MethodDefinition::kind) -> boolish diff --git a/test/rbs/subtractor_test.rb b/test/rbs/subtractor_test.rb index 21100ecf9..da17c0c01 100644 --- a/test/rbs/subtractor_test.rb +++ b/test/rbs/subtractor_test.rb @@ -419,6 +419,8 @@ class C attr_writer b: untyped attr_writer c: untyped attr_accessor d: untyped + attr_accessor e: untyped + attr_accessor f: untyped end RBS @@ -428,6 +430,9 @@ def a: () -> Integer def b=: (String) -> String def c: (String) -> String def d: (String) -> String + def e=: (String) -> String + def f: () -> String + def f=: (String) -> String end RBS @@ -436,6 +441,8 @@ def d: (String) -> String assert_subtracted <<~RBS, subtracted class C attr_writer c: untyped + attr_writer d: untyped + attr_reader e: untyped end RBS end