123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Style::AccessModifierDeclarations

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, ::RuboCop::Cop::AutoCorrector, ::RuboCop::Cop::Base, ::RuboCop::ExcludeLimit, NodePattern::Macros, RuboCop::AST::Sexp
Instance Chain:
Inherits: RuboCop::Cop::Base
Defined in: lib/rubocop/cop/style/access_modifier_declarations.rb

Overview

Access modifiers should be declared to apply to a group of methods or inline before each method, depending on configuration. EnforcedStyle config covers only method definitions. Applications of visibility methods to symbols can be controlled using AllowModifiersOnSymbols config. Also, the visibility of attr* methods can be controlled using AllowModifiersOnAttrs config.

In Ruby 3.0, attr* methods now return an array of defined method names as symbols. So we can write the modifier and attr* in inline style. AllowModifiersOnAttrs config allows attr* methods to be written in inline style without modifying applications that have been maintained for a long time in group style. Furthermore, developers who are not very familiar with Ruby may know that the modifier applies to def, but they may not know that it also applies to attr* methods. It would be easier to understand if we could write attr* methods in inline style.

Examples:

EnforcedStyle: group (default)

# bad
class Foo

  private def bar; end
  private def baz; end

end

# good
class Foo

  private

  def bar; end
  def baz; end

end

EnforcedStyle: inline

# bad
class Foo

  private

  def bar; end
  def baz; end

end

# good
class Foo

  private def bar; end
  private def baz; end

end

AllowModifiersOnSymbols: true (default)

# good
class Foo

  private :bar, :baz
  private *%i[qux quux]
  private *METHOD_NAMES
  private *private_methods

end

AllowModifiersOnSymbols: false

# bad
class Foo

  private :bar, :baz
  private *%i[qux quux]
  private *METHOD_NAMES
  private *private_methods

end

AllowModifiersOnAttrs: true (default)

# good
class Foo

  public attr_reader :bar
  protected attr_writer :baz
  private attr_accessor :qux
  private attr :quux

  def public_method; end

  private

  def private_method; end

end

AllowModifiersOnAttrs: false

# bad
class Foo

  public attr_reader :bar
  protected attr_writer :baz
  private attr_accessor :qux
  private attr :quux

end

Cop Safety Information:

  • Autocorrection is not safe, because the visibility of dynamically defined methods can vary depending on the state determined by the group access modifier.

Constant Summary

::RuboCop::Cop::Base - Inherited

EMPTY_OFFENSES, RESTRICT_ON_SEND

::RuboCop::Cop::ConfigurableEnforcedStyle - Included

SYMBOL_TO_STRING_CACHE

::RuboCop::Cop::RangeHelp - Included

BYTE_ORDER_MARK, NOT_GIVEN

Class Attribute Summary

::RuboCop::Cop::AutoCorrector - Extended

::RuboCop::Cop::Base - Inherited

.gem_requirements, .lint?,
.support_autocorrect?

Returns if class supports autocorrect.

.support_multiple_source?

Override if your cop should be called repeatedly for multiple investigations Between calls to on_new_investigation and on_investigation_end, the result of processed_source will remain constant.

Class Method Summary

::RuboCop::Cop::Base - Inherited

.autocorrect_incompatible_with

List of cops that should not try to autocorrect at the same time as this cop.

.badge

Naming.

.callbacks_needed, .cop_name, .department,
.documentation_url

Returns a url to view this cops documentation online.

.exclude_from_registry

Call for abstract Cop classes.

.inherited,
.joining_forces

Override and return the Force class(es) you need to join.

.match?

Returns true if the cop name or the cop namespace matches any of the given names.

.new,
.requires_gem

Register a version requirement for the given gem name.

.restrict_on_send

::RuboCop::ExcludeLimit - Extended

exclude_limit

Sets up a configuration option to have an exclude limit tracked.

transform

Instance Attribute Summary

Instance Method Summary

::RuboCop::Cop::RangeHelp - Included

#add_range, #column_offset_between,
#contents_range

A range containing only the contents of a literal with delimiters (e.g.

#directions,
#effective_column

Returns the column attribute of the range, except if the range is on the first line and there’s a byte order mark at the beginning of that line, in which case 1 is subtracted from the column value.

#final_pos, #move_pos, #move_pos_str, #range_between, #range_by_whole_lines, #range_with_comments, #range_with_comments_and_lines, #range_with_surrounding_comma, #range_with_surrounding_space, #source_range

::RuboCop::Cop::ConfigurableEnforcedStyle - Included

::RuboCop::Cop::Base - Inherited

#add_global_offense

Adds an offense that has no particular location.

#add_offense

Adds an offense on the specified range (or node with an expression) Unless that offense is disabled for this range, a corrector will be yielded to provide the cop the opportunity to autocorrect the offense.

#begin_investigation

Called before any investigation.

#callbacks_needed,
#cop_config

Configuration Helpers.

#cop_name, #excluded_file?,
#external_dependency_checksum

This method should be overridden when a cop’s behavior depends on state that lives outside of these locations:

#inspect,
#message

Gets called if no message is specified when calling add_offense or add_global_offense Cops are discouraged to override this; instead pass your message directly.

#name

Alias for Base#cop_name.

#offenses,
#on_investigation_end

Called after all on_…​

#on_new_investigation

Called before all on_…​

#on_other_file

Called instead of all on_…​

#parse

There should be very limited reasons for a Cop to do it’s own parsing.

#parser_engine,
#ready

Called between investigations.

#relevant_file?,
#target_gem_version

Returns a gems locked versions (i.e.

#target_rails_version, #target_ruby_version, #annotate, #apply_correction, #attempt_correction,
#callback_argument

Reserved for Cop::Cop.

#complete_investigation

Called to complete an investigation.

#correct, #current_corrector,
#current_offense_locations

Reserved for Commissioner:

#current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, #file_name_matches_any?, #find_message, #find_severity, #range_for_original, #range_from_node_or_range,
#reset_investigation

Actually private methods.

#use_corrector

::RuboCop::Cop::AutocorrectLogic - Included

::RuboCop::Cop::IgnoredNode - Included

Constructor Details

This class inherits a constructor from RuboCop::Cop::Base

Instance Attribute Details

#group_style?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 210

def group_style?
  style == :group
end

#inline_style?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 214

def inline_style?
  style == :inline
end

Instance Method Details

#access_modifier_is_inlined?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 218

def access_modifier_is_inlined?(node)
  node.arguments.any?
end

#access_modifier_is_not_inlined?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 222

def access_modifier_is_not_inlined?(node)
  !access_modifier_is_inlined?(node)
end

#access_modifier_with_attr?(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 143

def_node_matcher :access_modifier_with_attr?, <<~PATTERN
  (send nil? {:private :protected :public :module_function}
    (send nil? {:attr :attr_reader :attr_writer :attr_accessor} _+))
PATTERN

#access_modifier_with_symbol?(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 136

def_node_matcher :access_modifier_with_symbol?, <<~PATTERN
  (send nil? {:private :protected :public :module_function}
    {(sym _)+ (splat {#percent_symbol_array? const send})}
  )
PATTERN

#allow_modifiers_on_attrs?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 193

def allow_modifiers_on_attrs?(node)
  cop_config['AllowModifiersOnAttrs'] && access_modifier_with_attr?(node)
end

#allow_modifiers_on_symbols?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 189

def allow_modifiers_on_symbols?(node)
  cop_config['AllowModifiersOnSymbols'] && access_modifier_with_symbol?(node)
end

#allowed?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 163

def allowed?(node)
  !node.access_modifier? ||
    ALLOWED_NODE_TYPES.include?(node.parent&.type) ||
    allow_modifiers_on_symbols?(node) ||
    allow_modifiers_on_attrs?(node)
end

#autocorrect(corrector, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 170

def autocorrect(corrector, node)
  case style
  when :group
    def_nodes = find_corresponding_def_nodes(node)
    return unless def_nodes.any?

    replace_defs(corrector, node, def_nodes)
  when :inline
    remove_nodes(corrector, node)
    select_grouped_def_nodes(node).each do |grouped_def_node|
      insert_inline_modifier(corrector, grouped_def_node, node.method_name)
    end
  end
end

#correctable_group_offense?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 203

def correctable_group_offense?(node)
  return false unless group_style?
  return false if allowed?(node)

  access_modifier_is_inlined?(node) && find_corresponding_def_nodes(node).any?
end

#def_source(node, def_nodes) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 305

def def_source(node, def_nodes)
  [
    *processed_source.ast_with_comments[node].map(&:text),
    *def_nodes.map(&:source)
  ].join("\n")
end

#find_argument_less_modifier_node(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 265

def find_argument_less_modifier_node(node)
  return unless (parent = node.parent)

  parent.each_child_node(:send).find do |child|
    child.method?(node.method_name) && child.arguments.empty?
  end
end

#find_corresponding_def_nodes(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 246

def find_corresponding_def_nodes(node)
  if access_modifier_with_symbol?(node)
    method_names = node.arguments.filter_map do |argument|
      next unless argument.sym_type?

      argument.respond_to?(:value) && argument.value
    end

    def_nodes = node.parent.each_child_node(:def).select do |child|
      method_names.include?(child.method_name)
    end

    # If there isn't a `def` node for each symbol, we will skip autocorrection.
    def_nodes.size == method_names.size ? def_nodes : []
  else
    [node.first_argument]
  end
end

#insert_inline_modifier(corrector, node, modifier_name) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 295

def insert_inline_modifier(corrector, node, modifier_name)
  corrector.insert_before(node, "#{modifier_name} ")
end

#message(range) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 236

def message(range)
  access_modifier = range.source

  if group_style?
    format(GROUP_STYLE_MESSAGE, access_modifier: access_modifier)
  elsif inline_style?
    format(INLINE_STYLE_MESSAGE, access_modifier: access_modifier)
  end
end

#offense?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 197

def offense?(node)
  (group_style? && access_modifier_is_inlined?(node) &&
    !node.parent&.if_type? && !right_siblings_same_inline_method?(node)) ||
    (inline_style? && access_modifier_is_not_inlined?(node))
end

#on_send(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 148

def on_send(node)
  return if allowed?(node)

  if offense?(node)
    add_offense(node.loc.selector) do |corrector|
      autocorrect(corrector, node)
    end
    opposite_style_detected
  else
    correct_style_detected
  end
end

#percent_symbol_array?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 185

def percent_symbol_array?(node)
  node.array_type? && node.percent_literal?(:symbol)
end

#remove_nodes(corrector, *nodes) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 299

def remove_nodes(corrector, *nodes)
  nodes.each do |node|
    corrector.remove(range_with_comments_and_lines(node))
  end
end

#replace_defs(corrector, node, def_nodes) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 279

def replace_defs(corrector, node, def_nodes)
  source = def_source(node, def_nodes)
  argument_less_modifier_node = find_argument_less_modifier_node(node)
  if argument_less_modifier_node
    corrector.insert_after(argument_less_modifier_node, "\n\n#{source}")
  elsif (ancestor = node.each_ancestor(:block, :class, :module).first)

    corrector.insert_before(ancestor.loc.end, "#{node.method_name}\n\n#{source}\n")
  else
    corrector.replace(node, "#{node.method_name}\n\n#{source}")
    return
  end

  remove_nodes(corrector, *def_nodes, node)
end

#right_siblings_same_inline_method?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 226

def right_siblings_same_inline_method?(node)
  node.right_siblings.any? do |sibling|
    sibling.send_type? &&
      correctable_group_offense?(sibling) &&
      sibling.method?(node.method_name) &&
      !sibling.arguments.empty? &&
      find_corresponding_def_nodes(sibling).any?
  end
end

#select_grouped_def_nodes(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/access_modifier_declarations.rb', line 273

def select_grouped_def_nodes(node)
  node.right_siblings.take_while do |sibling|
    !(sibling.send_type? && sibling.bare_access_modifier_declaration?)
  end.select(&:def_type?)
end