Class: RuboCop::Cop::Style::InverseMethods

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Includes:
RangeHelp
Defined in:
lib/rubocop/cop/style/inverse_methods.rb

Overview

Checks for usages of not (not or !) called on a method when an inverse of that method can be used instead.

Methods that can be inverted by a not (not or !) should be defined in InverseMethods.

Methods that are inverted by inverting the return of the block that is passed to the method should be defined in InverseBlocks.

Examples:

# bad
!foo.none?
!foo.any? { |f| f.even? }
!foo.blank?
!(foo == bar)
foo.select { |f| !f.even? }
foo.reject { |f| f != 7 }

# good
foo.none?
foo.blank?
foo.any? { |f| f.even? }
foo != bar
foo == bar
!!('foo' =~ /^\w+$/)
!(foo.class < Numeric) # Checking class hierarchy is allowed
# Blocks with guard clauses are ignored:
foo.select do |f|
  next if f.zero?
  f != 1
end

Constant Summary collapse

MSG =
'Use `%<inverse>s` instead of inverting `%<method>s`.'
CLASS_COMPARISON_METHODS =
%i[<= >= < >].freeze
SAFE_NAVIGATION_INCOMPATIBLE_METHODS =
(CLASS_COMPARISON_METHODS + %i[any? none?]).freeze
EQUALITY_METHODS =
%i[== != =~ !~ <= >= < >].freeze
NEGATED_EQUALITY_METHODS =
%i[!= !~].freeze
CAMEL_CASE =
/[A-Z]+[a-z]+/.freeze
RESTRICT_ON_SEND =
[:!].freeze

Constants included from RangeHelp

RangeHelp::BYTE_ORDER_MARK, RangeHelp::NOT_GIVEN

Instance Attribute Summary

Attributes inherited from Base

#config, #processed_source, #project_index

Class Method Summary collapse

Instance Method Summary collapse

Methods included from AutoCorrector

support_autocorrect?

Methods inherited from Base

#active_support_extensions_enabled?, #add_global_offense, #add_offense, #always_autocorrect?, badge, #begin_investigation, #callbacks_needed, callbacks_needed, #config_to_allow_offenses, #config_to_allow_offenses=, #contextual_autocorrect?, #cop_config, cop_name, #cop_name, department, documentation_url, exclude_from_registry, #excluded_file?, #external_dependency_checksum, inherited, #initialize, #inspect, joining_forces, lint?, match?, #offenses, #on_investigation_end, #on_new_investigation, #on_other_file, #parse, #parser_engine, #ready, #relevant_file?, requires_gem, #string_literals_frozen_by_default?, support_autocorrect?, support_multiple_source?, #target_gem_version, #target_rails_version, #target_ruby_version

Methods included from ExcludeLimit

cop_dir_for, #exclude_limit, read_limits

Methods included from AutocorrectLogic

#autocorrect?, #autocorrect_enabled?, #autocorrect_requested?, #autocorrect_with_disable_uncorrectable?, #correctable?, #disable_uncorrectable?, #safe_autocorrect?

Methods included from IgnoredNode

#ignore_node, #ignored_node?, #part_of_ignored_node?

Methods included from Util

silence_warnings

Constructor Details

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

Class Method Details

.autocorrect_incompatible_withObject



56
57
58
# File 'lib/rubocop/cop/style/inverse_methods.rb', line 56

def self.autocorrect_incompatible_with
  [Style::Not, Style::SymbolProc]
end

Instance Method Details

#inverse_block?(node) ⇒ Object



70
71
72
73
74
75
76
# File 'lib/rubocop/cop/style/inverse_methods.rb', line 70

def_node_matcher :inverse_block?, <<~PATTERN
  (any_block $(call (...) $_) ... { $(call ... :!)
                                    $(send (...) {:!= :!~} ...)
                                    (begin ... $(call ... :!))
                                    (begin ... $(send (...) {:!= :!~} ...))
                                  })
PATTERN

#inverse_candidate?(node) ⇒ Object



61
62
63
64
65
66
67
# File 'lib/rubocop/cop/style/inverse_methods.rb', line 61

def_node_matcher :inverse_candidate?, <<~PATTERN
  {
    (send $(call $(...) $_ $...) :!)
    (send (any_block $(call $(...) $_) $...) :!)
    (send (begin $(call $(...) $_ $...)) :!)
  }
PATTERN

#on_block(node) ⇒ Object Also known as: on_numblock, on_itblock



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/rubocop/cop/style/inverse_methods.rb', line 92

def on_block(node)
  inverse_block?(node) do |_method_call, method, block|
    return unless inverse_blocks.key?(method)
    return if negated?(node) && negated?(node.parent)
    return if node.each_node(:next).any?

    # Inverse method offenses inside of the block of an inverse method
    # offense, such as `y.reject { |key, _value| !(key =~ /c\d/) }`,
    # can cause autocorrection to apply improper corrections.
    ignore_node(block)
    add_offense(node, message: message(method, inverse_blocks[method])) do |corrector|
      correct_inverse_block(corrector, node)
    end
  end
end

#on_send(node) ⇒ Object Also known as: on_csend



78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/rubocop/cop/style/inverse_methods.rb', line 78

def on_send(node)
  inverse_candidate?(node) do |method_call, lhs, method, rhs|
    return unless inverse_methods.key?(method)
    return if negated?(node) || safe_navigation_incompatible?(method_call)
    return if part_of_ignored_node?(node)
    return if possible_class_hierarchy_check?(lhs, rhs, method)

    add_offense(node, message: message(method, inverse_methods[method])) do |corrector|
      correct_inverse_method(corrector, node)
    end
  end
end