Class: RuboCop::Cop::Lint::DuplicateMethods
| Relationships & Source Files | |
| Super Chains via Extension / Inclusion / Inheritance | |
|
Class Chain:
self,
::RuboCop::Cop::Base,
::RuboCop::ExcludeLimit,
NodePattern::Macros,
RuboCop::AST::Sexp
|
|
|
Instance Chain:
self,
::RuboCop::Cop::ProjectIndexHelp,
::RuboCop::Cop::Base,
::RuboCop::Cop::AutocorrectLogic,
::RuboCop::Cop::IgnoredNode,
::RuboCop::Util,
RuboCop::AST::Sexp
|
|
| Inherits: |
RuboCop::Cop::Base
|
| 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
-
INDEXABLE_METHOD_NAME =
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 152
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
-
MSG =
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 147'Method `%<method>s` is defined at both %<defined>s and %<current>s.'
::RuboCop::Cop::Base - Inherited
EMPTY_OFFENSES, RESTRICT_ON_SEND
::RuboCop::Cop::ProjectIndexHelp - Included
Class Attribute Summary
::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 |
Class Method Summary
-
.new(config = nil, options = nil) ⇒ DuplicateMethods
constructor
No
RESTRICT_ON_SEND: the delegating method names are configurable (DelegatingMethods), so #on_send must see every call.
::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 | Reserved for Commissioner. |
::RuboCop::ExcludeLimit - Extended
| exclude_limit | Sets up a configuration option to have an exclude limit tracked. |
| transform | |
Instance Attribute Summary
::RuboCop::Cop::Base - Inherited
::RuboCop::Cop::AutocorrectLogic - Included
| #autocorrect?, #autocorrect_enabled?, #autocorrect_requested?, #autocorrect_with_disable_uncorrectable?, #correctable?, #disable_uncorrectable?, #safe_autocorrect?, | |
| #skipped_unsafe_correction_with_disable_uncorrectable? | Whether a correction exists but is skipped because it is unsafe in a safe autocorrect run, while |
Instance Method Summary
- #alias_method?(node)
- #class_new_block?(node)
- #class_or_module_new_block?(node)
-
#delegate_args(node)
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)
- #delegators?(node)
- #method_alias?(node)
- #on_alias(node)
- #on_def(node)
- #on_defs(node)
- #on_new_investigation
-
#on_send(node)
Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity.
- #sym_name(node)
-
#anon_block_identity(anon_block)
private
Internal identity for an anonymous block, used as a scope key.
- #anon_block_scope_id(anon_block) private
- #anonymous_class_block(node) private
- #check_const_receiver(node, name, const_name) private
- #check_cross_file_duplicate(node, method_name) private
- #check_self_receiver(node, name) private
- #cross_file_prior_definition(method_name) private
-
#cross_file_self_alias_trick?(definitions) ⇒ Boolean
private
An alias of the method alongside one of its definitions in another file may be the self-alias trick marking an intentional redefinition there, so no offense is registered.
- #delegate_prefix(node) private
-
#delegating_method?(node) ⇒ Boolean
private
Recognizing a delegating call as a method definition is Active Support behavior, so it stays gated on
ActiveSupportExtensionsEnabledas it was when onlydelegatewas supported. - #delegating_methods private
- #first_indexed_definition(definitions) private
- #found_attr(node, args, readable: false, writable: false) private
- #found_instance_method(node, name) private
- #found_method(node, method_name, scope_id: nil) private
- #found_redefinition(node, method_name, key, scope) private
- #found_sclass_method(node, name) private
- #hash_value(node, key) private
- #humanize_scope(scope) private
- #index_source_location(definition) private
- #indexed_declaration_definitions(namespace, name) private
- #indexed_definitions(owner, separator, name) private
- #inside_condition?(node) ⇒ Boolean private
-
#intentional_cross_file_redefinition?(node, method_name, key) ⇒ Boolean
private
The self-alias trick (
alias foo foooralias_method :foo, :fooright before adef) suppresses Ruby’s method redefinition warning, signaling an intentional redefinition of a method defined in another file. - #location(node) private
- #lookup_constant(node, const_name) private
- #message_for_dup(node, method_name, key) private
- #method_key(node, method_name) private
- #named_receiver(node) private
- #on_attr(node, attr_name, args) private
- #on_delegate(node, method_names) private
- #qualified_name(enclosing, namespace, mod_name) private
-
#scope_receiver(parent, anon_block)
private
When a Class.new block is passed as an argument to a named-receiver method call (e.g. T.cast(Class.new(Base) do …
- #source_location(node) private
- #track_self_alias(node, name) private
- #writer_attr_definition?(definition) ⇒ Boolean private
::RuboCop::Cop::ProjectIndexHelp - Included
| #external_dependency_checksum, #compute_project_index_signature, | |
| #definitions_in_other_files | Returns the definitions among |
| #fully_resolved_index_ancestry? | Whether every link of the declaration’s ancestry is resolved in the index: no definition of any ancestor has an unresolved superclass or mixin reference. |
| #indexed_singleton_member | A namespace without any singleton method has no singleton-class declaration of its own, so the lookup starts from the first ancestor that has one; its |
| #indexed_singleton_of | The declaration of `declaration’s singleton class, or nil when no singleton method is defined on it anywhere in the project. |
| #inherited_index_member? | Whether an ancestor of |
| #lexical_nesting_of | The lexical nesting the node’s constants resolve through, outermost first. |
| #prior_definition_in_other_file, #project_index_signature, | |
| #resolve_constant_in_index | Resolves a constant node the way Ruby does: the first segment through the lexical nesting and every following segment inside the previous one. |
| #resolved_mixin_references?, #resolved_superclass_reference?, #same_file? | |
::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 |
| #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, #covering_disabled_range, #current_corrector, | |
| #current_offense_locations | Reserved for Commissioner: |
| #current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, | |
| #enabled_lines? | A multi-line offense is suppressed by a directive on any line of its range, not only its first line, matching the intuition that the directive is attached to the offending code. |
| #file_name_matches_any?, #find_message, #find_severity, #matches_absolute_include_pattern?, #range_for_original, #range_from_node_or_range, | |
| #reset_investigation | Actually private methods. |
| #suppression_reason | The |
| #use_corrector | |
::RuboCop::Cop::AutocorrectLogic - Included
::RuboCop::Cop::IgnoredNode - Included
Constructor Details
.new(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.
# 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)
[ GitHub ]# 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
#anon_block_identity(anon_block) (private)
Internal identity for an anonymous block, used as a scope key.
Includes the source range’s begin position to distinguish blocks
that share the same line (e.g. two Class.new calls separated by ;).
The user-facing offense message still uses #source_location, which
shows only path:line.
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 618
def anon_block_identity(anon_block) range = anon_block.source_range "#{smart_path(range.source_buffer.name)}:#{range.line}:#{range.begin_pos}" end
#anon_block_scope_id(anon_block) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 387
def anon_block_scope_id(anon_block) parent = anon_block.parent return unless parent&.type?(:any_block, :begin, :call, :casgn, :any_def) if (receiver = scope_receiver(parent, anon_block)) "#{receiver.source}.#{parent.method_name}" elsif !parent.begin_type? || parent.parent&.any_block_type? anon_block_identity(anon_block) end end
#anonymous_class_block(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 378
def anonymous_class_block(node) first_block = node.each_ancestor(:block).first return unless class_or_module_new_block?(first_block) return if first_block.parent&.type?(:lvasgn) return if node.each_ancestor(:sclass).any? { |s| !s.children.first.self_type? } first_block end
#check_const_receiver(node, name, const_name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 291
def check_const_receiver(node, name, const_name) qualified = lookup_constant(node, const_name) return unless qualified found_method(node, "#{qualified}.#{name}") end
#check_cross_file_duplicate(node, method_name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 545
def check_cross_file_duplicate(node, method_name) return unless project_index return if @self_aliased.include?(method_name) return if node.each_ancestor(:any_def).any? return unless (prior = cross_file_prior_definition(method_name)) = format(MSG, method: method_name, defined: index_source_location(prior), current: source_location(node)) add_offense(location(node), message: ) end
#check_self_receiver(node, name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 298
def check_self_receiver(node, name) enclosing = node.parent_module_name if enclosing found_method(node, "#{enclosing}.#{name}") elsif (anon_block = anonymous_class_block(node)) scope = qualified_name(anon_block.parent_module_name, nil, 'Object') found_method(node, "#{scope}.#{name}", scope_id: anon_block_scope_id(anon_block)) end end
#class_new_block?(node)
[ GitHub ]# 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)
[ GitHub ]# 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
#cross_file_prior_definition(method_name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 557
def cross_file_prior_definition(method_name) return unless (match = INDEXABLE_METHOD_NAME.match(method_name)) definitions = definitions_in_other_files( indexed_definitions(match[:owner], match[:separator], match[:name]) ) return if definitions.empty? || cross_file_self_alias_trick?(definitions) first_indexed_definition(definitions) end
#cross_file_self_alias_trick?(definitions) ⇒ Boolean (private)
An alias of the method alongside one of its definitions in another file may be
the self-alias trick marking an intentional redefinition there, so no offense
is registered. A genuine alias duplicate is still reported when the alias
itself is inspected.
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 595
def cross_file_self_alias_trick?(definitions) aliases, others = definitions.partition do |definition| definition.is_a?(Rubydex::MethodAliasDefinition) end alias_paths = aliases.map { |definition| definition.location.to_file_path } others.any? { |definition| alias_paths.include?(definition.location.to_file_path) } end
#delegate_args(node)
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.
# 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
#delegate_prefix(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 340
def delegate_prefix(node) kwargs_node = node.last_argument return unless (prefix = hash_value(kwargs_node, :prefix)) if prefix.true_type? hash_value(kwargs_node, :to).value elsif prefix.type?(:sym, :str) prefix.value end end
#delegating_method?(node) ⇒ Boolean (private)
Recognizing a delegating call as a method definition is Active
Support behavior, so it stays gated on ActiveSupportExtensionsEnabled
as it was when only delegate was supported. DelegatingMethods lets
a project register its own delegate-shaped macros.
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 321
def delegating_method?(node) active_support_extensions_enabled? && delegating_methods.include?(node.method_name.to_s) end
#delegating_methods (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 326
def delegating_methods cop_config.fetch('DelegatingMethods', ['delegate']) end
#delegator?(node)
[ GitHub ]# 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)
[ GitHub ]# 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
#first_indexed_definition(definitions) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 604
def first_indexed_definition(definitions) definitions.find { |definition| !definition.is_a?(Rubydex::MethodAliasDefinition) } end
#found_attr(node, args, readable: false, writable: false) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 497
def found_attr(node, args, readable: false, writable: false) args.each do |arg| name = sym_name(arg) next unless name found_instance_method(node, name) if readable found_instance_method(node, "#{name}=") if writable end end
#found_instance_method(node, name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 356
def found_instance_method(node, name) if (scope = node.parent_module_name) found_method(node, "#{humanize_scope(scope)}#{name}") elsif (anon_block = anonymous_class_block(node)) base = qualified_name(anon_block.parent_module_name, nil, 'Object') scope = node.each_ancestor(:sclass).any? ? "#<Class:#{base}>" : base found_method( node, "#{humanize_scope(scope)}#{name}", scope_id: anon_block_scope_id(anon_block) ) else found_sclass_method(node, name) end end
#found_method(node, method_name, scope_id: nil) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 427
def found_method(node, method_name, scope_id: nil) key = method_key(node, method_name) key = "#{key}@#{scope_id}" if scope_id scope = node.each_ancestor(:rescue, :ensure).first&.type if @definitions.key?(key) found_redefinition(node, method_name, key, scope) else @definitions[key] = node check_cross_file_duplicate(node, method_name) if scope_id.nil? && scope.nil? end end
#found_redefinition(node, method_name, key, scope) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 440
def found_redefinition(node, method_name, key, scope) if scope && !@scopes[scope].include?(key) @definitions[key] = node @scopes[scope] << key elsif intentional_cross_file_redefinition?(node, method_name, key) @definitions[key] = node else add_offense(location(node), message: (node, method_name, key)) end end
#found_sclass_method(node, name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 417
def found_sclass_method(node, name) singleton_ancestor = node.each_ancestor.find(&:sclass_type?) return unless singleton_ancestor singleton_receiver_node = singleton_ancestor.children[0] return unless singleton_receiver_node.send_type? found_method(node, "#{singleton_receiver_node.method_name}.#{name}") end
#hash_value(node, key) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 352
def hash_value(node, key) node.pairs.find { |pair| pair.key.value == key }&.value end
#humanize_scope(scope) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 370
def humanize_scope(scope) scope = scope.sub( /(?:(?<name>.*)::)#<Class:\k<name>>|#<Class:(?<name>.*)>(?:::)?/, '\k<name>.' ) scope.end_with?('.') ? scope : "#{scope}#" end
#index_source_location(definition) (private)
[ GitHub ]#indexed_declaration_definitions(namespace, name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 582
def indexed_declaration_definitions(namespace, name) project_index["#{namespace}##{name}()"]&.definitions.to_a end
#indexed_definitions(owner, separator, name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 568
def indexed_definitions(owner, separator, name) namespace = separator == '.' ? "#{owner}::<#{owner.split('::').last}>" : owner if name.match?(/\A\w+=\z/) # Rubydex indexes `attr_writer :foo` under `foo` rather than `foo=`, so # writer definitions come from both the `foo=` and the `foo` declarations. indexed_declaration_definitions(namespace, name) + indexed_declaration_definitions(namespace, name.delete_suffix('=')) .select { |definition| writer_attr_definition?(definition) } else indexed_declaration_definitions(namespace, name).grep_v(Rubydex::AttrWriterDefinition) end end
#inside_condition?(node) ⇒ Boolean (private)
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 308
def inside_condition?(node) node.ancestors.any?(&:if_type?) end
#intentional_cross_file_redefinition?(node, method_name, key) ⇒ Boolean (private)
The self-alias trick (alias foo foo or alias_method :foo, :foo right before
a def) suppresses Ruby’s method redefinition warning, signaling an intentional
redefinition of a method defined in another file.
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 454
def intentional_cross_file_redefinition?(node, method_name, key) @self_aliased.include?(method_name) && @definitions[key].source_range.source_buffer.name != node.source_range.source_buffer.name end
#location(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 475
def location(node) if node.any_def_type? node.loc.keyword.join(node.loc.name) else node.source_range end end
#lookup_constant(node, const_name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 507
def lookup_constant(node, const_name) # this method is quite imperfect and can be fooled # to do much better, we would need to do global analysis of the whole # codebase node.each_ancestor(:class, :module, :casgn) do |ancestor| namespace, mod_name = *ancestor.defined_module loop do if mod_name == const_name return qualified_name(ancestor.parent_module_name, namespace, mod_name) end break if namespace.nil? namespace, mod_name = *namespace end end end
#message_for_dup(node, method_name, key) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 312
def (node, method_name, key) format(MSG, method: method_name, defined: source_location(@definitions[key]), current: source_location(node)) end
#method_alias?(node)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 193
def_node_matcher :method_alias?, <<~PATTERN (alias (sym $_name) (sym $_original_name)) PATTERN
#method_key(node, method_name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 467
def method_key(node, method_name) if (ancestor_def = node.each_ancestor(:any_def).first) "#{ancestor_def.method_name}.#{method_name}" else method_name end end
#named_receiver(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 412
def named_receiver(node) receiver = node.receiver receiver unless class_or_module_new_block?(receiver) end
#on_alias(node)
[ GitHub ]# 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_attr(node, attr_name, args) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 483
def on_attr(node, attr_name, args) case attr_name when :attr writable = args.size == 2 && args.last.true_type? found_attr(node, [args.first], readable: true, writable: writable) when :attr_reader found_attr(node, args, readable: true) when :attr_writer found_attr(node, args, writable: true) when :attr_accessor found_attr(node, args, readable: true, writable: true) end end
#on_def(node)
[ GitHub ]# 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)
[ GitHub ]# 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_delegate(node, method_names) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 330
def on_delegate(node, method_names) name_prefix = delegate_prefix(node) method_names.each do |name| name = "#{name_prefix}_#{name}" if name_prefix found_instance_method(node, name) end end
#on_new_investigation
[ GitHub ]# 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)
Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
# 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
#qualified_name(enclosing, namespace, mod_name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 525
def qualified_name(enclosing, namespace, mod_name) if enclosing != 'Object' if namespace "#{enclosing}::#{namespace.const_name}::#{mod_name}" else "#{enclosing}::#{mod_name}" end elsif namespace "#{namespace.const_name}::#{mod_name}" else mod_name end end
#scope_receiver(parent, anon_block) (private)
When a Class.new block is passed as an argument to a named-receiver method call (e.g. T.cast(Class.new(Base) do … end, …)), the receiver-based scope id (e.g. "T.cast") is the same for every call, causing false positives for methods defined in distinct anonymous classes. Return nil so the block falls through to the unique source-location-based scope id. Module.new blocks are excluded because they may be intentionally mixed into the same target via prepend/include/extend.
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 406
def scope_receiver(parent, anon_block) return if class_new_block?(anon_block) && parent.call_type? named_receiver(parent) end
#source_location(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 539
def source_location(node) range = node.source_range path = smart_path(range.source_buffer.name) "#{path}:#{range.line}" end
#sym_name(node)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 245
def_node_matcher :sym_name, '(sym $_name)'
#track_self_alias(node, name) (private)
[ GitHub ]# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 460
def track_self_alias(node, name) scope = node.parent_module_name return unless scope @self_aliased << "#{humanize_scope(scope)}#{name}" end
#writer_attr_definition?(definition) ⇒ Boolean (private)
# File 'lib/rubocop/cop/lint/duplicate_methods.rb', line 586
def writer_attr_definition?(definition) definition.is_a?(Rubydex::AttrWriterDefinition) || definition.is_a?(Rubydex::AttrAccessorDefinition) end