Skip to content

Commit

Permalink
Autofix for Style/StructInheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
bubaflub committed Mar 21, 2020
1 parent ea1d48f commit 02c8b44
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -12,6 +12,7 @@
* [#7786](https://github.com/rubocop-hq/rubocop/pull/7786): Support Ruby 2.7's pattern match for `Layout/ElseAlignment` cop. ([@koic][])
* [#7784](https://github.com/rubocop-hq/rubocop/pull/7784): Support Ruby 2.7's numbered parameter for `Lint/SafeNavigationChain`. ([@koic][])
* [#7331](https://github.com/rubocop-hq/rubocop/issues/7331): Add `forbidden` option to `Style/ModuleFunction` cop. ([@weh][])
* [#7499](https://github.com/rubocop-hq/rubocop/pull/7499): Autocorrection for `Style/StructInheritance`. ([@bubaflub][])

### Bug fixes

Expand Down Expand Up @@ -4411,3 +4412,4 @@
[@nikitasakov]: https://github.com/nikitasakov
[@dmolesUC]: https://github.com/dmolesUC
[@yuritomanek]: https://github.com/yuritomanek
[@bubaflub]: https://github.com/bubaflub
2 changes: 2 additions & 0 deletions config/default.yml
Expand Up @@ -3758,7 +3758,9 @@ Style/StructInheritance:
Description: 'Checks for inheritance from Struct.new.'
StyleGuide: '#no-extend-struct-new'
Enabled: true
SafeAutoCorrect: false
VersionAdded: '0.29'
VersionChanged: '0.81'

Style/SymbolArray:
Description: 'Use %i or %I for arrays of symbols.'
Expand Down
4 changes: 2 additions & 2 deletions lib/rubocop/ast/node/class_node.rb
Expand Up @@ -6,9 +6,9 @@ module AST
# node when the builder constructs the AST, making its methods available
# to all `class` nodes within RuboCop.
class ClassNode < Node
# The identifer for this `class` node.
# The identifier for this `class` node.
#
# @return [Node] the identifer of the class
# @return [Node] the identifier of the class
def identifier
node_parts[0]
end
Expand Down
44 changes: 44 additions & 0 deletions lib/rubocop/cop/style/struct_inheritance.rb
Expand Up @@ -20,6 +20,8 @@ module Style
# end
# end
class StructInheritance < Cop
include RangeHelp

MSG = "Don't extend an instance initialized by `Struct.new`. " \
'Use a block to customize the struct.'

Expand All @@ -33,6 +35,48 @@ def on_class(node)
{(send (const nil? :Struct) :new ...)
(block (send (const nil? :Struct) :new ...) ...)}
PATTERN

def autocorrect(class_node)
lambda do |corrector|
return nil if struct_definition_has_block?(class_node)

remove_class_keyword(class_node, corrector)

corrector.replace(class_node.loc.operator, '=')

transform_class_body(class_node, corrector)
end
end

private

def struct_definition_has_block?(class_node)
class_node.parent_class.is_a?(RuboCop::AST::BlockNode)
end

def remove_class_keyword(class_node, corrector)
corrector.remove(
range_with_surrounding_space(
range: class_node.loc.keyword,
side: :right
)
)
end

def transform_class_body(class_node, corrector)
body = class_node.body

if body
corrector.insert_after(class_node.parent_class.source_range, ' do')
else
corrector.remove(
range_with_surrounding_space(
range: class_node.loc.end,
side: :right
)
)
end
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion manual/cops_style.md
Expand Up @@ -6747,7 +6747,7 @@ This cop identifies places where `lstrip.rstrip` can be replaced by

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Enabled | Yes | No | 0.29 | -
Enabled | Yes | Yes (Unsafe) | 0.29 | 0.81

This cop checks for inheritance from Struct.new.

Expand Down
62 changes: 62 additions & 0 deletions spec/rubocop/cop/style/struct_inheritance_spec.rb
Expand Up @@ -46,4 +46,66 @@ def age
end
RUBY
end

it 'autocorrects simple inline class with no block' do
new_source = autocorrect_source(<<~RUBY)
class Person < Struct.new(:first_name, :last_name)
end
RUBY

expect(new_source).to eq(<<~RUBY)
Person = Struct.new(:first_name, :last_name)
RUBY
end

it 'autocorrects a class with a body' do
new_source = autocorrect_source(<<~RUBY)
class Person < Struct.new(:first_name, :last_name)
def age
42
end
end
RUBY

expect(new_source).to eq(<<~RUBY)
Person = Struct.new(:first_name, :last_name) do
def age
42
end
end
RUBY
end

it 'ignores a class with an empty block and empty body' do
original_source = <<~RUBY
class Person < Struct.new(:first_name, :last_name) do end
end
RUBY

expect(autocorrect_source(original_source)).to eq(original_source)
end

it 'ignores a class with a body and an empty block' do
original_source = <<~RUBY
class Person < Struct.new(:first_name, :last_name) do end
def age
42
end
end
RUBY

expect(autocorrect_source(original_source)).to eq(original_source)
end

it 'ignores a class with a body and a block' do
original_source = <<~RUBY
class Person < Struct.new(:first_name, :last_name) do def baz; end end
def age
42
end
end
RUBY

expect(autocorrect_source(original_source)).to eq(original_source)
end
end

0 comments on commit 02c8b44

Please sign in to comment.