123456789_123456789_123456789_123456789_123456789_

Class: RBS::Subtractor

Relationships & Source Files
Inherits: Object
Defined in: lib/rbs/subtractor.rb

Class Method Summary

Instance Method Summary

Constructor Details

.new(minuend, subtrahend) ⇒ Subtractor

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 5

def initialize(minuend, subtrahend)
  @minuend = minuend
  @subtrahend = subtrahend
end

Instance Method Details

#absolute_typename(name, context:) (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 177

private def absolute_typename(name, context:)
  while context
    ns = context[1] or raise
    name = name.with_prefix(ns.to_namespace)
    context = _ = context[0]
  end
  name.absolute!
end

#access_modifier?(decl) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 160

private def access_modifier?(decl)
  decl.is_a?(AST::Members::Public) || decl.is_a?(AST::Members::Private)
end

#call(minuend = @minuend, context: nil)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 10

def call(minuend = @minuend, context: nil)
  minuend.map do |decl|
    case decl
    when AST::Declarations::Constant
      name = absolute_typename(decl.name, context: context)
      decl unless @subtrahend.constant_name?(name)
    when AST::Declarations::Interface
      name = absolute_typename(decl.name, context: context)
      decl unless @subtrahend.interface_name?(name)
    when AST::Declarations::Class, AST::Declarations::Module
      name = absolute_typename(decl.name, context: context)
      case
      when @subtrahend.class_decl?(name) && decl.is_a?(AST::Declarations::Class)
        filter_members(decl, context: context)
      when @subtrahend.module_decl?(name) && decl.is_a?(AST::Declarations::Module)
        filter_members(decl, context: context)
      when @subtrahend.constant_name?(name)
        nil
      else
        decl
      end
    when AST::Declarations::Global
      decl unless @subtrahend.global_decls[decl.name]
    when AST::Declarations::TypeAlias
      name = absolute_typename(decl.name, context: context)
      decl unless @subtrahend.type_alias_decls[name]
    when AST::Declarations::ClassAlias
      name = absolute_typename(decl.new_name, context: context)
      decl unless @subtrahend.class_alias?(name) || @subtrahend.class_decl?(name)
    when AST::Declarations::ModuleAlias
      name = absolute_typename(decl.new_name, context: context)
      decl unless @subtrahend.module_alias?(name) || @subtrahend.module_decl?(name)
    else
      raise "unknwon decl: #{(_ = decl).class}"
    end
  end.compact
end

#cvar_exist?(owner, name) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 118

private def cvar_exist?(owner, name)
  each_member(owner).any? do |m|
    case m
    when AST::Members::ClassVariable
      m.name == name
    end
  end
end

#each_member(owner, &block) (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 127

private def each_member(owner, &block)
  return enum_for((__method__ or raise), owner) unless block

  entry = @subtrahend.class_decls[owner]
  return unless entry
  decls = entry.decls.map { |d| d.decl }

  decls.each { |d| d.members.each { |m| block.call(m) } }
end

#filter_members(decl, context:) (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 48

private def filter_members(decl, context:)
  owner = absolute_typename(decl.name, context: context)

  context = _ = [context, decl.name]
  children = call(decl.each_decl.to_a, context: context) +
    decl.each_member.reject { |m| member_exist?(owner, m, context: context) }
  children = filter_redundunt_access_modifiers(children)
  return nil if children.empty?

  update_decl(decl, members: children)
end

#filter_redundunt_access_modifiers(decls) (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 148

private def filter_redundunt_access_modifiers(decls)
  decls = decls.dup
  decls.pop while access_modifier?(decls.last)
  decls = decls.map.with_index do |decl, i|
    if access_modifier?(decl) && access_modifier?(decls[i + 1])
      nil
    else
      decl
    end
  end.compact
end

#ivar_exist?(owner, name, kind) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 106

private def ivar_exist?(owner, name, kind)
  each_member(owner).any? do |m|
    case m
    when AST::Members::InstanceVariable
      m.name == name
    when AST::Members::Attribute
      ivar_name = m.ivar_name == false ? nil : m.ivar_name || :"@#{m.name}"
      ivar_name == name && m.kind == kind
    end
  end
end

#member_exist?(owner, member, context:) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 60

private def member_exist?(owner, member, context:)
  case member
  when AST::Members::MethodDefinition
    method_exist?(owner, member.name, member.kind)
  when AST::Members::Alias
    method_exist?(owner, member.new_name, member.kind)
  when AST::Members::AttrReader
    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
    ivar_exist?(owner, member.name, :singleton)
  when AST::Members::ClassVariable
    cvar_exist?(owner, member.name)
  when AST::Members::Include, AST::Members::Extend, AST::Members::Prepend
    mixin_exist?(owner, member, context: context)
  when AST::Members::Public, AST::Members::Private
    # They should not be removed even if the subtrahend has them.
    false
  else
    raise "unknown member: #{(_ = member).class}"
  end
end

#method_exist?(owner, method_name, kind) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 89

private def method_exist?(owner, method_name, kind)
  each_member(owner).any? do |m|
    case m
    when AST::Members::MethodDefinition
      m.name == method_name && m.kind == kind
    when AST::Members::Alias
      m.new_name == method_name && m.kind == kind
    when AST::Members::AttrReader
      m.name == method_name && m.kind == kind
    when AST::Members::AttrWriter
      :"#{m.name}=" == method_name && m.kind == kind
    when AST::Members::AttrAccessor
      (m.name == method_name || :"#{m.name}=" == method_name) && m.kind == kind
    end
  end
end

#mixin_exist?(owner, mixin, context:) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 137

private def mixin_exist?(owner, mixin, context:)
  candidates = typename_candidates(mixin.name, context: context)
  each_member(owner).any? do |m|
    case m
    when mixin.class
      # @type var m: AST::Members::Include | AST::Members::Extend | AST::Members::Prepend
      candidates.include?(m.name)
    end
  end
end

#typename_candidates(name, context:) (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 186

private def typename_candidates(name, context:)
  ret = [name.absolute!, name.relative!]
  return ret if name.absolute?

  while context
    ns = context[1] or raise
    name = name.with_prefix(ns.to_namespace)
    ret.concat [name.absolute!, name.relative!]

    context = _ = context[0]
  end

  ret
end

#update_decl(decl, members:) (private)

[ GitHub ]

  
# File 'lib/rbs/subtractor.rb', line 164

private def update_decl(decl, members:)
  case decl
  when AST::Declarations::Class
    decl.class.new(name: decl.name, type_params: decl.type_params, super_class: decl.super_class,
                    annotations: decl.annotations, location: decl.location, comment: decl.comment,
                    members: members)
  when AST::Declarations::Module
    decl.class.new(name: decl.name, type_params: decl.type_params, self_types: decl.self_types,
                    annotations: decl.annotations, location: decl.location, comment: decl.comment,
                    members: members)
  end
end