class RuboCop::Cop::Style::BisectedAttrAccessor

Checks for places where ‘attr_reader` and `attr_writer` for the same method can be combined into single `attr_accessor`.

@example

# bad
class Foo
  attr_reader :bar
  attr_writer :bar
end

# good
class Foo
  attr_accessor :bar
end

Constants

MSG

Public Instance Methods

after_class(class_node) click to toggle source

Each offending macro is captured and registered in ‘on_class` but correction happens in `after_class` because a macro might have multiple attributes rewritten from it

# File lib/rubocop/cop/style/bisected_attr_accessor.rb, line 55
def after_class(class_node)
  @macros_to_rewrite[class_node].each do |macro|
    node = macro.node
    range = range_by_whole_lines(node.loc.expression, include_final_newline: true)

    correct(range) do |corrector|
      if macro.writer?
        correct_writer(corrector, macro, node, range)
      else
        correct_reader(corrector, macro, node, range)
      end
    end
  end
end
Also aliased as: after_sclass, after_module
after_module(class_node)
Alias for: after_class
after_sclass(class_node)
Alias for: after_class
on_class(class_node) click to toggle source
# File lib/rubocop/cop/style/bisected_attr_accessor.rb, line 33
def on_class(class_node)
  @macros_to_rewrite[class_node] = Set.new

  find_macros(class_node.body).each do |_visibility, macros|
    bisected = find_bisection(macros)
    next unless bisected.any?

    macros.each do |macro|
      attrs = macro.bisect(*bisected)
      next if attrs.none?

      @macros_to_rewrite[class_node] << macro
      attrs.each { |attr| register_offense(attr) }
    end
  end
end
Also aliased as: on_sclass, on_module
on_module(class_node)
Alias for: on_class
on_new_investigation() click to toggle source
# File lib/rubocop/cop/style/bisected_attr_accessor.rb, line 29
def on_new_investigation
  @macros_to_rewrite = {}
end
on_sclass(class_node)
Alias for: on_class

Private Instance Methods

correct_reader(corrector, macro, node, range) click to toggle source
# File lib/rubocop/cop/style/bisected_attr_accessor.rb, line 102
def correct_reader(corrector, macro, node, range)
  attr_accessor = "attr_accessor #{macro.bisected_names.join(', ')}\n"

  if macro.all_bisected?
    corrector.replace(range, "#{indent(node)}#{attr_accessor}")
  else
    correction = "#{indent(node)}attr_reader #{macro.rest.join(', ')}"
    corrector.insert_before(node, attr_accessor)
    corrector.replace(node, correction)
  end
end
correct_writer(corrector, macro, node, range) click to toggle source
# File lib/rubocop/cop/style/bisected_attr_accessor.rb, line 114
def correct_writer(corrector, macro, node, range)
  if macro.all_bisected?
    corrector.remove(range)
  else
    correction = "attr_writer #{macro.rest.join(', ')}"
    corrector.replace(node, correction)
  end
end
find_bisection(macros) click to toggle source
# File lib/rubocop/cop/style/bisected_attr_accessor.rb, line 91
def find_bisection(macros)
  # Find which attributes are defined in both readers and writers so that they
  # can be replaced with accessors.
  readers, writers = macros.partition(&:reader?)
  readers.flat_map(&:attr_names) & writers.flat_map(&:attr_names)
end
find_macros(class_def) click to toggle source
# File lib/rubocop/cop/style/bisected_attr_accessor.rb, line 74
def find_macros(class_def)
  # Find all the macros (`attr_reader`, `attr_writer`, etc.) in the class body
  # and turn them into `Macro` objects so that they can be processed.
  return [] if !class_def || class_def.def_type?

  send_nodes =
    if class_def.send_type?
      [class_def]
    else
      class_def.each_child_node(:send)
    end

  send_nodes.each_with_object([]) do |node, macros|
    macros << Macro.new(node) if Macro.macro?(node)
  end.group_by(&:visibility)
end
register_offense(attr) click to toggle source
# File lib/rubocop/cop/style/bisected_attr_accessor.rb, line 98
def register_offense(attr)
  add_offense(attr, message: format(MSG, name: attr.source))
end