Skip to content

Commit

Permalink
Add struct_constructor?, class_definition? and module_definition? mat…
Browse files Browse the repository at this point in the history
…chers

Closes #28
  • Loading branch information
tejasbubane authored and marcandre committed Jul 19, 2020
1 parent 5ddb0cc commit e2df776
Show file tree
Hide file tree
Showing 3 changed files with 285 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,7 @@
* [#55](https://github.com/rubocop-hq/rubocop-ast/pull/55): Add `ProcessedSource#line_with_comment?`. ([@marcandre][])
* [#63](https://github.com/rubocop-hq/rubocop-ast/pull/63): NodePattern now supports patterns as arguments to predicate and functions. ([@marcandre][])
* [#64](https://github.com/rubocop-hq/rubocop-ast/pull/64): Add `Node#global_const?`. ([@marcandre][])
* [#28](https://github.com/rubocop-hq/rubocop-ast/issues/28): Add `struct_constructor?`, `class_definition?` and `module_definition?` matchers. ([@tejasbubane][])

### Bug fixes

Expand Down
15 changes: 15 additions & 0 deletions lib/rubocop/ast/node.rb
Expand Up @@ -510,6 +510,21 @@ def guard_clause?
(block (send #global_const?({:Class :Module}) :new ...) ...)}
PATTERN

def_node_matcher :struct_constructor?, <<~PATTERN
(block (send #global_const?(:Struct) :new ...) _ $_)
PATTERN

def_node_matcher :class_definition?, <<~PATTERN
{(class _ _ $_)
(sclass _ $_)
(block (send #global_const?({:Struct :Class}) :new ...) _ $_)}
PATTERN

def_node_matcher :module_definition?, <<~PATTERN
{(module _ $_)
(block (send #global_const?(:Module) :new ...) _ $_)}
PATTERN

# Some expressions are evaluated for their value, some for their side
# effects, and some for both
# If we know that an expression is useful only for its side effects, that
Expand Down
269 changes: 269 additions & 0 deletions spec/rubocop/ast/node_spec.rb
Expand Up @@ -373,4 +373,273 @@ def used?
end
end
end

describe '#class_constructor?' do
context 'class definition with a block' do
let(:src) { 'Class.new { a = 42 }' }

it 'matches' do
expect(node.class_constructor?).to eq(true)
end
end

context 'module definition with a block' do
let(:src) { 'Module.new { a = 42 }' }

it 'matches' do
expect(node.class_constructor?).to eq(true)
end
end

context 'class definition' do
let(:src) { 'class Foo; a = 42; end' }

it 'does not match' do
expect(node.class_constructor?).to eq(nil)
end
end

context 'class definition on outer scope' do
let(:src) { '::Class.new { a = 42 }' }

it 'matches' do
expect(node.class_constructor?).to eq(true)
end
end
end

describe '#struct_constructor?' do
context 'struct definition with a block' do
let(:src) { 'Struct.new { a = 42 }' }

it 'matches' do
expect(node.struct_constructor?).to eq(node.body)
end
end

context 'struct definition without block' do
let(:src) { 'Struct.new(:foo, :bar)' }

it 'does not match' do
expect(node.struct_constructor?).to eq(nil)
end
end

context '::Struct' do
let(:src) { '::Struct.new { a = 42 }' }

it 'matches' do
expect(node.struct_constructor?).to eq(node.body)
end
end
end

describe '#class_definition?' do
context 'without inheritance' do
let(:src) { 'class Foo; a = 42; end' }

it 'matches' do
expect(node.class_definition?).to eq(node.body)
end
end

context 'with inheritance' do
let(:src) { 'class Foo < Bar; a = 42; end' }

it 'matches' do
expect(node.class_definition?).to eq(node.body)
end
end

context 'with ::ClassName' do
let(:src) { 'class ::Foo < Bar; a = 42; end' }

it 'matches' do
expect(node.class_definition?).to eq(node.body)
end
end

context 'with Struct' do
let(:src) do
<<~RUBY
Person = Struct.new(:name, :age) do
a = 2
def details; end
end
RUBY
end

it 'matches' do
class_node = node.children.last
expect(class_node.class_definition?).to eq(class_node.body)
end
end

context 'constant defined as Struct without block' do
let(:src) { 'Person = Struct.new(:name, :age)' }

it 'does not match' do
expect(node.class_definition?).to eq(nil)
end
end

context 'with Class.new' do
let(:src) do
<<~RUBY
Person = Class.new do
a = 2
def details; end
end
RUBY
end

it 'matches' do
class_node = node.children.last
expect(class_node.class_definition?).to eq(class_node.body)
end
end

context 'namespaced class' do
let(:src) do
<<~RUBY
class Foo::Bar::Baz
BAZ = 2
def variables; end
end
RUBY
end

it 'matches' do
expect(node.class_definition?).to eq(node.body)
end
end

context 'with self singleton class' do
let(:src) do
<<~RUBY
class << self
BAZ = 2
def variables; end
end
RUBY
end

it 'matches' do
expect(node.class_definition?).to eq(node.body)
end
end

context 'with object singleton class' do
let(:src) do
<<~RUBY
class << foo
BAZ = 2
def variables; end
end
RUBY
end

it 'matches' do
expect(node.class_definition?).to eq(node.body)
end
end
end

describe '#module_definition?' do
context 'using module keyword' do
let(:src) { 'module Foo; A = 42; end' }

it 'matches' do
expect(node.module_definition?).to eq(node.body)
end
end

context 'with ::ModuleName' do
let(:src) { 'module ::Foo; A = 42; end' }

it 'matches' do
expect(node.module_definition?).to eq(node.body)
end
end

context 'with Module.new' do
let(:src) do
<<~RUBY
Person = Module.new do
a = 2
def details; end
end
RUBY
end

it 'matches' do
module_node = node.children.last
expect(module_node.module_definition?).to eq(module_node.body)
end
end

context 'prepend Module.new' do
let(:src) do
<<~RUBY
prepend(Module.new do
a = 2
def details; end
end)
RUBY
end

it 'matches' do
module_node = node.children.last
expect(module_node.module_definition?).to eq(module_node.body)
end
end

context 'nested modules' do
let(:src) do
<<~RUBY
module Foo
module Bar
BAZ = 2
def variables; end
end
end
RUBY
end

it 'matches' do
expect(node.module_definition?).to eq(node.body)
end
end

context 'namespaced modules' do
let(:src) do
<<~RUBY
module Foo::Bar::Baz
BAZ = 2
def variables; end
end
RUBY
end

it 'matches' do
expect(node.module_definition?).to eq(node.body)
end
end

context 'included module definition' do
let(:src) do
<<~RUBY
include(Module.new do
BAZ = 2
def variables; end
end)
RUBY
end

it 'matches' do
module_node = node.children.last
expect(module_node.module_definition?).to eq(module_node.body)
end
end
end
end

0 comments on commit e2df776

Please sign in to comment.