-
-
Notifications
You must be signed in to change notification settings - Fork 3k
/
struct_inheritance.rb
83 lines (69 loc) · 2.1 KB
/
struct_inheritance.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# frozen_string_literal: true
module RuboCop
module Cop
module Style
# This cop checks for inheritance from Struct.new.
#
# @example
# # bad
# class Person < Struct.new(:first_name, :last_name)
# def age
# 42
# end
# end
#
# # good
# Person = Struct.new(:first_name, :last_name) do
# def age
# 42
# end
# end
class StructInheritance < Cop
include RangeHelp
MSG = "Don't extend an instance initialized by `Struct.new`. " \
'Use a block to customize the struct.'
def on_class(node)
return unless struct_constructor?(node.parent_class)
add_offense(node, location: node.parent_class.source_range)
end
def_node_matcher :struct_constructor?, <<~PATTERN
{(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
end