Class: RuboCop::Cop::Lint::DuplicateMethods
- Includes:
- ProjectIndexHelp
- Defined in:
- lib/rubocop/cop/lint/duplicate_methods.rb
Overview
Checks for duplicated instance (or singleton) method definitions.
NOTE: Aliasing a method to itself is allowed, as it indicates that the developer intends to suppress Ruby's method redefinition warnings. See https://bugs.ruby-lang.org/issues/13574.
By default the cop can only detect duplicates within a single file.
When AllCops/UseProjectIndex is enabled and the rubydex gem is installed,
the cop additionally consults the project-wide index and reports methods
whose duplicate definition lives in another file.
NOTE: The project index does not record whether a definition in another file is wrapped in a conditional, so a platform-specific redefinition in another file may still be reported. Aliasing the method to itself (see above) before redefining marks the redefinition as intentional and is respected across files.
Constant Summary collapse
- MSG =
'Method `%<method>s` is defined at both %<defined>s and %<current>s.'- INDEXABLE_METHOD_NAME =
Method names the cop registers that can be looked up in the project index: a fully qualified namespace followed by
#(instance) or.(singleton) and the method name. /\A(?<owner>[A-Z]\w*(?:::[A-Z]\w*)*)(?<separator>[#.])(?<name>[^#.]+)\z/.freeze
Constants included from ProjectIndexHelp
ProjectIndexHelp::BUILTIN_DOCUMENT_URI, ProjectIndexHelp::FILE_URI_PREFIX, ProjectIndexHelp::WINDOWS_DRIVE_PREFIX
Constants inherited from Base
Instance Attribute Summary
Attributes inherited from Base
#config, #processed_source, #project_index
Instance Method Summary collapse
- #alias_method?(node) ⇒ Object
- #class_new_block?(node) ⇒ Object
- #class_or_module_new_block?(node) ⇒ Object
-
#delegate_args(node) ⇒ Object
Matches the argument shape of an Active Support
delegatecall (delegate :a, :b, to: :target), regardless of the method name; the name is checked separately againstDelegatingMethods. - #delegator?(node) ⇒ Object
- #delegators?(node) ⇒ Object
-
#initialize(config = nil, options = nil) ⇒ DuplicateMethods
constructor
No
RESTRICT_ON_SEND: the delegating method names are configurable (DelegatingMethods), soon_sendmust see every call. - #method_alias?(node) ⇒ Object
- #on_alias(node) ⇒ Object
- #on_def(node) ⇒ Object
- #on_defs(node) ⇒ Object
- #on_new_investigation ⇒ Object
-
#on_send(node) ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity.
- #sym_name(node) ⇒ Object
Methods included from ProjectIndexHelp
Methods inherited from Base
#active_support_extensions_enabled?, #add_global_offense, #add_offense, #always_autocorrect?, autocorrect_incompatible_with, 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, #inspect, joining_forces, lint?, match?, #message, #offenses, #on_investigation_end, #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
Constructor Details
#initialize(config = nil, options = nil) ⇒ DuplicateMethods
No RESTRICT_ON_SEND: the delegating method names are configurable
(DelegatingMethods), so on_send must see every call. The handler
returns quickly for calls that are not method definitions, and
benchmarks show no measurable cost over the previous fixed list.
159 160 161 162 163 164 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 159 def initialize(config = nil, = nil) super @definitions = {} @scopes = Hash.new { |hash, key| hash[key] = [] } @self_aliased = Set.new end |
Instance Method Details
#alias_method?(node) ⇒ Object
211 212 213 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 211 def_node_matcher :alias_method?, <<~PATTERN (send nil? :alias_method (sym $_name) (sym $_original_name)) PATTERN |
#class_new_block?(node) ⇒ Object
255 256 257 258 259 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 255 def_node_matcher :class_new_block?, <<~PATTERN (block (send (const _ :Class) :new ...) ...) PATTERN |
#class_or_module_new_block?(node) ⇒ Object
248 249 250 251 252 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 248 def_node_matcher :class_or_module_new_block?, <<~PATTERN (block (send (const _ {:Class :Module}) :new ...) ...) PATTERN |
#delegate_args(node) ⇒ Object
Matches the argument shape of an Active Support delegate call
(delegate :a, :b, to: :target), regardless of the method name; the
name is checked separately against DelegatingMethods.
219 220 221 222 223 224 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 219 def_node_matcher :delegate_args, <<~PATTERN (send nil? _ ({sym str} $_)+ (hash <(pair (sym :to) {sym str}) ...>) ) PATTERN |
#delegator?(node) ⇒ Object
227 228 229 230 231 232 233 234 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 227 def_node_matcher :delegator?, <<~PATTERN (send nil? {:def_delegator :def_instance_delegator} { {sym str} ({sym str} $_) | {sym str} {sym str} ({sym str} $_) } ) PATTERN |
#delegators?(node) ⇒ Object
237 238 239 240 241 242 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 237 def_node_matcher :delegators?, <<~PATTERN (send nil? {:def_delegators :def_instance_delegators} {sym str} ({sym str} $_)+ ) PATTERN |
#method_alias?(node) ⇒ Object
193 194 195 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 193 def_node_matcher :method_alias?, <<~PATTERN (alias (sym $_name) (sym $_original_name)) PATTERN |
#on_alias(node) ⇒ Object
197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 197 def on_alias(node) name, original_name = method_alias?(node) return unless name && original_name if name == original_name track_self_alias(node, name) return end return if node.ancestors.any?(&:if_type?) found_instance_method(node, name) end |
#on_def(node) ⇒ Object
173 174 175 176 177 178 179 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 173 def on_def(node) # if a method definition is inside an if, it is very likely # that a different definition is used depending on platform, etc. return if node.each_ancestor.any?(&:if_type?) found_instance_method(node, node.method_name) end |
#on_defs(node) ⇒ Object
181 182 183 184 185 186 187 188 189 190 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 181 def on_defs(node) return if node.each_ancestor.any?(&:if_type?) if node.receiver.const_type? _, const_name = *node.receiver check_const_receiver(node, node.method_name, const_name) elsif node.receiver.self_type? check_self_receiver(node, node.method_name) end end |
#on_new_investigation ⇒ Object
166 167 168 169 170 171 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 166 def on_new_investigation # The self-alias trick declares an intentional redefinition only within # the file that uses it, so the tracked names do not carry over. @self_aliased = Set.new super end |
#on_send(node) ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 261 def on_send(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity name, original_name = alias_method?(node) if name && original_name if name == original_name track_self_alias(node, name) return end return if inside_condition?(node) found_instance_method(node, name) elsif (attr = node.attribute_accessor?) on_attr(node, *attr) elsif delegating_method?(node) && (names = delegate_args(node)) return if inside_condition?(node) on_delegate(node, names) elsif (name = delegator?(node)) return if inside_condition?(node) found_instance_method(node, name) elsif (names = delegators?(node)) return if inside_condition?(node) names.each { |name| found_instance_method(node, name) } end end |
#sym_name(node) ⇒ Object
245 |
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 245 def_node_matcher :sym_name, '(sym $_name)' |