Class: RubyLint::Analysis::UndefinedMethods

Inherits:
Base show all
Defined in:
lib/ruby-lint/analysis/undefined_methods.rb

Overview

The UndefinedMethods class checks for the use of undefined methods/local variables and adds errors whenever needed. Based on the receiver of a method call the corresponding error message differs to make it easier to understand what is going on.

A simple example:

foobar        # => undefined method foobar
'test'.foobar # => undefined method foobar on an instance of String

Constant Summary

Constants inherited from Base

Base::SCOPES

Instance Attribute Summary

Attributes inherited from Base

#config, #report, #vm

Attributes inherited from Iterator

#arity_cache, #arity_cache Hash containing the amount of arguments for

Instance Method Summary collapse

Methods inherited from Base

#add_message, #after_initialize, analyze?, #current_scope, #error, #info, #previous_scope, register, #set_current_scope, #set_previous_scope, #warning

Methods included from MethodEvaluation

#unpack_block

Methods inherited from Iterator

#execute_callback, #initialize, #iterate, #skip_child_nodes!

Constructor Details

This class inherits a constructor from RubyLint::Iterator

Instance Method Details

#class_names_for_object(object) ⇒ String (private)

Parameters:

Returns:



116
117
118
119
120
121
122
123
124
# File 'lib/ruby-lint/analysis/undefined_methods.rb', line 116

def class_names_for_object(object)
  if object.parents.empty?
    klass = object.ruby_class ? object.ruby_class : object.name
  else
    klass = name_for_parents(object.parents)
  end

  return klass
end

#error_for(name, receiver, scope) ⇒ String (private)

Determines what error message to use for a method call.

Parameters:

Returns:



78
79
80
# File 'lib/ruby-lint/analysis/undefined_methods.rb', line 78

def error_for(name, receiver, scope)
  return receiver ? receiver_error(name, scope) : method_error(name)
end

#has_definition?(scope, name) ⇒ Boolean (private)

Parameters:

Returns:

  • (Boolean)


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/ruby-lint/analysis/undefined_methods.rb', line 48

def has_definition?(scope, name)
  type   = scope.method_call_type
  exists = scope.has_definition?(type, name)

  # Due to the way `parser` wraps block nodes (`(block (send) ...)`
  # opposed to `(send ... (block))`) we'll try to find the method in the
  # previous scope if we can't find it in the current block scope.
  if !exists and scope.block?
    prev   = previous_scope
    exists = prev.has_definition?(prev.method_call_type, name)
  end

  # If method_missing is defined we'll assume the method calls are
  # handled gracefully and not add any errors for them.
  if !exists and scope.has_definition?(type, 'method_missing')
    exists = true
  end

  return exists
end

#method_error(name) ⇒ String (private)

Parameters:

Returns:



86
87
88
# File 'lib/ruby-lint/analysis/undefined_methods.rb', line 86

def method_error(name)
  return "undefined method #{name}"
end

#name_for_parents(parents) ⇒ String (private)

Parameters:

  • parents (Array)

Returns:



130
131
132
133
134
135
136
# File 'lib/ruby-lint/analysis/undefined_methods.rb', line 130

def name_for_parents(parents)
  return parents[0].name if parents.length == 1

  segments = parents[0..-2].map(&:name)

  return segments.join(', ') + " or #{parents[-1].name}"
end

#on_send(node) ⇒ Object

Parameters:



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/ruby-lint/analysis/undefined_methods.rb', line 20

def on_send(node)
  receiver, name, _  = *node

  receiver = unpack_block(receiver)
  name     = name.to_s
  scope    = current_scope

  if receiver and vm.associations.key?(receiver)
    scope = vm.associations[receiver]

    # TODO: this should be handled in a more generic and especially in a
    # more nicer way.
    return if scope.parents.empty?
  end

  unless has_definition?(scope, name)
    message = error_for(name, receiver, scope)

    error(message, node)
  end
end

#receiver_error(name, scope) ⇒ String (private)

Returns a String containing the error message to use when calling an undefined method on a receiver.

Parameters:

Returns:



98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ruby-lint/analysis/undefined_methods.rb', line 98

def receiver_error(name, scope)
  klass = class_names_for_object(scope)

  if scope.instance?
    error = "undefined method #{name} on an instance of #{klass}"
  else
    error = "undefined method #{name} on #{scope.name}"
  end

  return error
end