class RuboCop::Cop::Style::AccessorGrouping
end
attr_reader :baz
attr_reader :bar
class Foo
# good
end
attr_reader :bar, :baz
class Foo
# bad
@example EnforcedStyle: separated
end
attr_reader :baz
may_be_intended_annotation :baz
attr_reader :bax
sig { returns(String) }
attr_reader :bar
# may be intended comment for bar.
class Foo
# good
end
attr_reader :bar, :bax, :baz
class Foo
# good
end
attr_reader :baz
attr_reader :bax
attr_reader :bar
class Foo
# bad
@example EnforcedStyle: grouped (default)
as it might be intended like Sorbet.
NOTE: If there is a method call before the accessor method it is always allowed
but it can be configured to enforce separating them in multiple declarations.
By default it enforces accessors to be placed in grouped declarations,
Checks for grouping of accessors in ‘class` and `module` bodies.
def autocorrect(corrector, node)
def autocorrect(corrector, node) if (preferred_accessors = preferred_accessors(node)) corrector.replace(node, preferred_accessors) else range = range_with_surrounding_space(node.source_range, side: :left) corrector.remove(range) end end
def check(send_node)
def check(send_node) return if previous_line_comment?(send_node) || !groupable_accessor?(send_node) return unless (grouped_style? && groupable_sibling_accessors(send_node).size > 1) || (separated_style? && send_node.arguments.size > 1) message = message(send_node) add_offense(send_node, message: message) do |corrector| autocorrect(corrector, send_node) end end
def class_send_elements(class_node)
def class_send_elements(class_node) class_def = class_node.body if !class_def || class_def.def_type? [] elsif class_def.send_type? [class_def] else class_def.each_child_node(:send).to_a end end
def group_accessors(node, accessors)
def group_accessors(node, accessors) accessor_names = accessors.flat_map { |accessor| accessor.arguments.map(&:source) }.uniq "#{node.method_name} #{accessor_names.join(', ')}" end
def groupable_accessor?(node)
def groupable_accessor?(node) return true unless (previous_expression = node.left_siblings.last) # Accessors with Sorbet `sig { ... }` blocks shouldn't be groupable. if previous_expression.block_type? previous_expression.child_nodes.each do |child_node| break previous_expression = child_node if child_node.send_type? end end return true unless previous_expression.send_type? previous_expression.attribute_accessor? || previous_expression.access_modifier? end
def groupable_sibling_accessors(send_node)
def groupable_sibling_accessors(send_node) send_node.parent.each_child_node(:send).select do |sibling| sibling.attribute_accessor? && sibling.method?(send_node.method_name) && node_visibility(sibling) == node_visibility(send_node) && groupable_accessor?(sibling) && !previous_line_comment?(sibling) end end
def grouped_style?
def grouped_style? style == :grouped end
def message(send_node)
def message(send_node) msg = grouped_style? ? GROUPED_MSG : SEPARATED_MSG format(msg, accessor: send_node.method_name) end
def on_class(node)
def on_class(node) class_send_elements(node).each do |macro| next unless macro.attribute_accessor? check(macro) end end
def preferred_accessors(node)
def preferred_accessors(node) if grouped_style? accessors = groupable_sibling_accessors(node) group_accessors(node, accessors) if node.loc == accessors.first.loc else separate_accessors(node) end end
def previous_line_comment?(node)
def previous_line_comment?(node) comment_line?(processed_source[node.first_line - 2]) end
def separate_accessors(node)
def separate_accessors(node) node.arguments.flat_map do |arg| lines = [ *processed_source.ast_with_comments[arg].map(&:text), "#{node.method_name} #{arg.source}" ] if arg == node.arguments.first lines else indent = ' ' * node.loc.column lines.map { |line| "#{indent}#{line}" } end end.join("\n") end
def separated_style?
def separated_style? style == :separated end