Skip to content

Commit

Permalink
[Fix rubocop#2456] Nested method definitions are allowed inside eval …
Browse files Browse the repository at this point in the history
…block
  • Loading branch information
alexdowad committed Dec 10, 2015
1 parent b96ae95 commit 45b7aae
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -23,6 +23,7 @@
* [#2463](https://github.com/bbatsov/rubocop/issues/2463): Allow comments before an access modifier. ([@codebeige][])
* [#2471](https://github.com/bbatsov/rubocop/issues/2471): `Style/MethodName` doesn't choke on methods which are defined inside methods. ([@alexdowad][])
* [#2449](https://github.com/bbatsov/rubocop/issues/2449): `Style/StabbyLambdaParentheses` only checks lambdas in the arrow form. ([@lumeet][])
* [#2456](https://github.com/bbatsov/rubocop/issues/2456): `Lint/NestedMethodDefinition` doesn't register offenses for method definitions inside an eval block (either `instance_eval`, `class_eval`, or `module_eval`). ([@alexdowad][])

### Changes

Expand Down
19 changes: 17 additions & 2 deletions lib/rubocop/cop/lint/nested_method_definition.rb
Expand Up @@ -8,23 +8,38 @@ module Lint
# @example
# # `bar` definition actually produces methods in the same scope
# # as the outer `foo` method. Furthermore, the `bar` method
# # will be redefined every time the `foo` is invoked
# # will be redefined every time `foo` is invoked.
# def foo
# def bar
# end
# end
#
class NestedMethodDefinition < Cop
include OnMethodDef
extend RuboCop::NodePattern::Macros

MSG = 'Method definitions must not be nested. ' \
'Use `lambda` instead.'

def_node_matcher :eval_call?, <<-PATTERN
(block (send _ {:instance_eval :class_eval :module_eval} ...) ...)
PATTERN

def on_method_def(node, _method_name, _args, _body)
node.each_descendant(:def) do |nested_def_node|
find_nested_defs(node) do |nested_def_node|
add_offense(nested_def_node, :expression)
end
end

def find_nested_defs(node, &block)
node.each_child_node do |child|
if child.def_type? || child.defs_type?
block.call(child)
elsif !eval_call?(child)
find_nested_defs(child, &block)
end
end
end
end
end
end
Expand Down
38 changes: 37 additions & 1 deletion spec/rubocop/cop/lint/nested_method_definition_spec.rb
Expand Up @@ -36,13 +36,49 @@
expect(cop.offenses.size).to eq(0)
end

it 'does not register an offense for a nested class method definition' do
it 'registers an offense for a nested class method definition' do
inspect_source(cop, ['class Foo',
' def self.x',
' def self.y',
' end',
' end',
'end'])
expect(cop.offenses.size).to eq(1)
end

it 'does not register offense for nested definition inside instance_eval' do
inspect_source(cop, ['class Foo',
' def x(obj)',
' obj.instance_eval do',
' def y',
' end',
' end',
' end',
'end'])
expect(cop.offenses.size).to eq(0)
end

it 'does not register offense for nested definition inside class_eval' do
inspect_source(cop, ['class Foo',
' def x(klass)',
' klass.class_eval do',
' def y',
' end',
' end',
' end',
'end'])
expect(cop.offenses.size).to eq(0)
end

it 'does not register offense for nested definition inside module_eval' do
inspect_source(cop, ['class Foo',
' def self.define(mod)',
' mod.module_eval do',
' def y',
' end',
' end',
' end',
'end'])
expect(cop.offenses.size).to eq(0)
end
end

0 comments on commit 45b7aae

Please sign in to comment.