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 rubocop#28
  • Loading branch information
tejasbubane committed Jul 5, 2020
1 parent a1f1c17 commit 6dbdb3a
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@
### New features

* [#50](https://github.com/rubocop-hq/rubocop-ast/pull/50): Support find pattern matching for Ruby 2.8 (3.0) parser. ([@koic][])
* [#28](https://github.com/rubocop-hq/rubocop-ast/issues/28): Add `struct_constructor?`, `class_definition?` and `module_definition?` matchers. ([@tejasbubane][])

## 0.1.0 (2020-06-26)

Expand All @@ -21,6 +22,7 @@
* [#46](https://github.com/rubocop-hq/rubocop-ast/pull/46): Basic support for [non-legacy AST output from parser](https://github.com/whitequark/parser/#usage). Note that there is no support (yet) in main RuboCop gem. Expect `emit_forward_arg` to be set to `true` in v1.0 ([@marcandre][])
* [#48](https://github.com/rubocop-hq/rubocop-ast/pull/48): Support `Parser::Ruby28` for Ruby 2.8 (3.0) parser (experimental). ([@koic][])
* [#35](https://github.com/rubocop-hq/rubocop-ast/pull/35): NodePattern now accepts `%named_param` and `%CONST`. The macros `def_node_pattern` and `def_node_search` accept default named parameters. ([@marcandre][])
* [#31](https://github.com/rubocop-hq/rubocop-ast/pull/31): Use `param === node` to match params, which allows Regexp, Proc, Set, etc. ([@marcandre][])

## 0.0.3 (2020-05-15)

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

def_node_matcher :struct_constructor?, <<~PATTERN
(block (send (const {nil? (cbase)} :Struct) :new ...) ...)
PATTERN

def_node_matcher :class_definition?, <<~PATTERN
{(class (const _ _) ...)
(casgn nil? _ (block (send (const {nil? (cbase)} {:Struct :Class}) :new ...) ...))
(sclass {(self) (send nil? _)} ...)}
PATTERN

def_node_matcher :module_definition?, <<~PATTERN
{(module (const _ _) ...)
(casgn nil? _ (block (send (const {nil? (cbase)} :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
226 changes: 226 additions & 0 deletions spec/rubocop/ast/node_spec.rb
Expand Up @@ -373,4 +373,230 @@ 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
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(true)
end
end

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

it 'does not matches' 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(true)
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(true)
end
end

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

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

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

it 'matches' do
expect(node.class_definition?).to eq(true)
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
expect(node.class_definition?).to eq(true)
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
expect(node.class_definition?).to eq(true)
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(true)
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(true)
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(true)
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(true)
end
end

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

it 'matches' do
expect(node.module_definition?).to eq(true)
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
expect(node.module_definition?).to eq(true)
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(true)
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(true)
end
end
end
end

0 comments on commit 6dbdb3a

Please sign in to comment.