improve_your_code 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +12 -0
- data/README.md +0 -0
- data/Rakefile +4 -0
- data/bin/improve_your_code +6 -0
- data/improve_your_code.gemspec +22 -0
- data/lib/improve_your_code/ast/ast_node_class_map.rb +39 -0
- data/lib/improve_your_code/ast/builder.rb +11 -0
- data/lib/improve_your_code/ast/node.rb +108 -0
- data/lib/improve_your_code/ast/object_refs.rb +32 -0
- data/lib/improve_your_code/ast/reference_collector.rb +29 -0
- data/lib/improve_your_code/ast/sexp_extensions.rb +15 -0
- data/lib/improve_your_code/ast/sexp_extensions/arguments.rb +89 -0
- data/lib/improve_your_code/ast/sexp_extensions/block.rb +38 -0
- data/lib/improve_your_code/ast/sexp_extensions/constant.rb +13 -0
- data/lib/improve_your_code/ast/sexp_extensions/if.rb +18 -0
- data/lib/improve_your_code/ast/sexp_extensions/methods.rb +81 -0
- data/lib/improve_your_code/ast/sexp_extensions/module.rb +64 -0
- data/lib/improve_your_code/ast/sexp_extensions/nested_assignables.rb +17 -0
- data/lib/improve_your_code/ast/sexp_extensions/self.rb +13 -0
- data/lib/improve_your_code/ast/sexp_extensions/send.rb +55 -0
- data/lib/improve_your_code/ast/sexp_extensions/symbols.rb +14 -0
- data/lib/improve_your_code/ast/sexp_extensions/variables.rb +45 -0
- data/lib/improve_your_code/cli/application.rb +32 -0
- data/lib/improve_your_code/cli/command/report_command.rb +39 -0
- data/lib/improve_your_code/cli/silencer.rb +24 -0
- data/lib/improve_your_code/code_comment.rb +52 -0
- data/lib/improve_your_code/context/attribute_context.rb +33 -0
- data/lib/improve_your_code/context/code_context.rb +121 -0
- data/lib/improve_your_code/context/method_context.rb +79 -0
- data/lib/improve_your_code/context/module_context.rb +77 -0
- data/lib/improve_your_code/context/root_context.rb +22 -0
- data/lib/improve_your_code/context/send_context.rb +16 -0
- data/lib/improve_your_code/context/singleton_attribute_context.rb +13 -0
- data/lib/improve_your_code/context/singleton_method_context.rb +29 -0
- data/lib/improve_your_code/context/statement_counter.rb +27 -0
- data/lib/improve_your_code/context/visibility_tracker.rb +52 -0
- data/lib/improve_your_code/context_builder.rb +121 -0
- data/lib/improve_your_code/detector_repository.rb +32 -0
- data/lib/improve_your_code/examiner.rb +48 -0
- data/lib/improve_your_code/report/formatter.rb +25 -0
- data/lib/improve_your_code/report/formatter/heading_formatter.rb +33 -0
- data/lib/improve_your_code/report/formatter/progress_formatter.rb +25 -0
- data/lib/improve_your_code/report/formatter/simple_warning_formatter.rb +13 -0
- data/lib/improve_your_code/report/text_report.rb +76 -0
- data/lib/improve_your_code/smell_configuration.rb +48 -0
- data/lib/improve_your_code/smell_detectors.rb +12 -0
- data/lib/improve_your_code/smell_detectors/base_detector.rb +114 -0
- data/lib/improve_your_code/smell_detectors/long_parameter_list.rb +44 -0
- data/lib/improve_your_code/smell_detectors/too_many_constants.rb +54 -0
- data/lib/improve_your_code/smell_detectors/too_many_instance_variables.rb +48 -0
- data/lib/improve_your_code/smell_detectors/too_many_methods.rb +47 -0
- data/lib/improve_your_code/smell_detectors/too_many_statements.rb +43 -0
- data/lib/improve_your_code/smell_detectors/uncommunicative_method_name.rb +58 -0
- data/lib/improve_your_code/smell_detectors/uncommunicative_module_name.rb +71 -0
- data/lib/improve_your_code/smell_detectors/uncommunicative_variable_name.rb +129 -0
- data/lib/improve_your_code/smell_detectors/unused_parameters.rb +24 -0
- data/lib/improve_your_code/smell_detectors/unused_private_method.rb +69 -0
- data/lib/improve_your_code/smell_warning.rb +70 -0
- data/lib/improve_your_code/source/source_code.rb +84 -0
- data/lib/improve_your_code/source/source_locator.rb +38 -0
- data/lib/improve_your_code/tree_dresser.rb +74 -0
- data/lib/improve_your_code/version.rb +7 -0
- metadata +141 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'code_context'
|
4
|
+
|
5
|
+
module ImproveYourCode
|
6
|
+
module Context
|
7
|
+
class AttributeContext < CodeContext
|
8
|
+
attr_accessor :visibility
|
9
|
+
|
10
|
+
def initialize(exp, send_expression)
|
11
|
+
@visibility = :public
|
12
|
+
@send_expression = send_expression
|
13
|
+
super exp
|
14
|
+
end
|
15
|
+
|
16
|
+
def full_comment
|
17
|
+
send_expression.full_comment || ''
|
18
|
+
end
|
19
|
+
|
20
|
+
def instance_method?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def apply_current_visibility(current_visibility)
|
25
|
+
self.visibility = current_visibility
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :send_expression
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../code_comment'
|
4
|
+
require_relative '../ast/object_refs'
|
5
|
+
require_relative 'statement_counter'
|
6
|
+
|
7
|
+
require 'forwardable'
|
8
|
+
|
9
|
+
module ImproveYourCode
|
10
|
+
module Context
|
11
|
+
class CodeContext
|
12
|
+
include Enumerable
|
13
|
+
extend Forwardable
|
14
|
+
delegate each_node: :exp
|
15
|
+
delegate %i[name type] => :exp
|
16
|
+
|
17
|
+
attr_reader :children, :parent, :exp, :statement_counter
|
18
|
+
|
19
|
+
def initialize(exp)
|
20
|
+
@exp = exp
|
21
|
+
@children = []
|
22
|
+
@statement_counter = StatementCounter.new
|
23
|
+
@refs = AST::ObjectRefs.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def local_nodes(type, ignored = [], &blk)
|
27
|
+
ignored += %i[casgn class module]
|
28
|
+
each_node(type, ignored, &blk)
|
29
|
+
end
|
30
|
+
|
31
|
+
def each(&block)
|
32
|
+
return enum_for(:each) unless block_given?
|
33
|
+
|
34
|
+
yield self
|
35
|
+
children.each do |child|
|
36
|
+
child.each(&block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def register_with_parent(parent)
|
41
|
+
@parent = parent.append_child_context(self) if parent
|
42
|
+
end
|
43
|
+
|
44
|
+
def append_child_context(child)
|
45
|
+
children << child
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def record_call_to(exp)
|
50
|
+
receiver = exp.receiver
|
51
|
+
type = receiver ? receiver.type : :self
|
52
|
+
line = exp.line
|
53
|
+
case type
|
54
|
+
when :lvar, :lvasgn
|
55
|
+
unless exp.object_creation_call?
|
56
|
+
refs.record_reference(name: receiver.name, line: line)
|
57
|
+
end
|
58
|
+
when :self
|
59
|
+
refs.record_reference(name: :self, line: line)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def record_use_of_self
|
64
|
+
refs.record_reference(name: :self)
|
65
|
+
end
|
66
|
+
|
67
|
+
def matches?(candidates)
|
68
|
+
my_fq_name = full_name
|
69
|
+
candidates.any? do |candidate|
|
70
|
+
candidate = Regexp.quote(candidate) if candidate.is_a?(String)
|
71
|
+
/#{candidate}/ =~ my_fq_name
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def full_name
|
76
|
+
exp.full_name('')
|
77
|
+
end
|
78
|
+
|
79
|
+
def config_for(detector_class)
|
80
|
+
parent_config_for(detector_class).merge(
|
81
|
+
configuration_via_code_commment[detector_class.smell_type] || {}
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
def number_of_statements
|
86
|
+
statement_counter.value
|
87
|
+
end
|
88
|
+
|
89
|
+
def singleton_method?
|
90
|
+
false
|
91
|
+
end
|
92
|
+
|
93
|
+
def instance_method?
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
97
|
+
def apply_current_visibility(_current_visibility); end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
attr_reader :refs
|
102
|
+
|
103
|
+
def configuration_via_code_commment
|
104
|
+
@configuration_via_code_commment ||=
|
105
|
+
CodeComment.new(
|
106
|
+
comment: full_comment,
|
107
|
+
line: exp.line,
|
108
|
+
source: exp.source
|
109
|
+
).config
|
110
|
+
end
|
111
|
+
|
112
|
+
def full_comment
|
113
|
+
exp.full_comment || ''
|
114
|
+
end
|
115
|
+
|
116
|
+
def parent_config_for(detector_class)
|
117
|
+
parent ? parent.config_for(detector_class) : {}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'code_context'
|
4
|
+
|
5
|
+
module ImproveYourCode
|
6
|
+
module Context
|
7
|
+
class MethodContext < CodeContext
|
8
|
+
attr_accessor :visibility
|
9
|
+
attr_reader :refs
|
10
|
+
|
11
|
+
def initialize(exp, parent_exp)
|
12
|
+
@parent_exp = parent_exp
|
13
|
+
@visibility = :public
|
14
|
+
super exp
|
15
|
+
end
|
16
|
+
|
17
|
+
def references_self?
|
18
|
+
exp.depends_on_instance?
|
19
|
+
end
|
20
|
+
|
21
|
+
def uses_param?(param)
|
22
|
+
(local_nodes(:lvar) + local_nodes(:lvasgn))
|
23
|
+
.find { |node| node.var_name == param.name }
|
24
|
+
end
|
25
|
+
|
26
|
+
def unused_params
|
27
|
+
exp.arguments.reject do |param|
|
28
|
+
param.anonymous_splat? ||
|
29
|
+
param.marked_unused? ||
|
30
|
+
uses_param?(param)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def uses_super_with_implicit_arguments?
|
35
|
+
(body = exp.body) && body.contains_nested_node?(:zsuper)
|
36
|
+
end
|
37
|
+
|
38
|
+
def default_assignments
|
39
|
+
@default_assignments ||=
|
40
|
+
exp.parameters.select(&:optional_argument?).map(&:children)
|
41
|
+
end
|
42
|
+
|
43
|
+
def method_context_class
|
44
|
+
self.class
|
45
|
+
end
|
46
|
+
|
47
|
+
def singleton_method?
|
48
|
+
false
|
49
|
+
end
|
50
|
+
|
51
|
+
def instance_method?
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def apply_current_visibility(current_visibility)
|
56
|
+
self.visibility = current_visibility
|
57
|
+
end
|
58
|
+
|
59
|
+
def module_function?
|
60
|
+
visibility == :module_function
|
61
|
+
end
|
62
|
+
|
63
|
+
def non_public_visibility?
|
64
|
+
visibility != :public
|
65
|
+
end
|
66
|
+
|
67
|
+
def full_comment
|
68
|
+
own = super
|
69
|
+
return own unless own.empty?
|
70
|
+
return parent_exp.full_comment if parent_exp
|
71
|
+
''
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
attr_reader :parent_exp
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'code_context'
|
4
|
+
require_relative 'attribute_context'
|
5
|
+
require_relative 'method_context'
|
6
|
+
require_relative 'visibility_tracker'
|
7
|
+
|
8
|
+
module ImproveYourCode
|
9
|
+
module Context
|
10
|
+
class ModuleContext < CodeContext
|
11
|
+
attr_reader :visibility_tracker
|
12
|
+
|
13
|
+
def initialize(exp)
|
14
|
+
super
|
15
|
+
@visibility_tracker = VisibilityTracker.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def append_child_context(child)
|
19
|
+
visibility_tracker.set_child_visibility(child)
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_context_class
|
24
|
+
MethodContext
|
25
|
+
end
|
26
|
+
|
27
|
+
def attribute_context_class
|
28
|
+
AttributeContext
|
29
|
+
end
|
30
|
+
|
31
|
+
def defined_instance_methods(visibility: :public)
|
32
|
+
instance_method_children.select do |context|
|
33
|
+
context.visibility == visibility
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def instance_method_calls
|
38
|
+
instance_method_children.flat_map do |context|
|
39
|
+
context.children.grep(SendContext)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def node_instance_methods
|
44
|
+
local_nodes(:def)
|
45
|
+
end
|
46
|
+
|
47
|
+
def descriptively_commented?
|
48
|
+
CodeComment.new(comment: exp.leading_comment).descriptive?
|
49
|
+
end
|
50
|
+
|
51
|
+
def namespace_module?
|
52
|
+
return false if exp.type == :casgn
|
53
|
+
children = exp.direct_children
|
54
|
+
children.any? && children.all? { |child| %i[casgn class module].include? child.type }
|
55
|
+
end
|
56
|
+
|
57
|
+
def track_visibility(visibility, names)
|
58
|
+
visibility_tracker.track_visibility children: instance_method_children,
|
59
|
+
visibility: visibility,
|
60
|
+
names: names
|
61
|
+
visibility_tracker.track_singleton_visibility children: singleton_method_children,
|
62
|
+
visibility: visibility,
|
63
|
+
names: names
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def instance_method_children
|
69
|
+
children.select(&:instance_method?)
|
70
|
+
end
|
71
|
+
|
72
|
+
def singleton_method_children
|
73
|
+
children.select(&:singleton_method?)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'code_context'
|
4
|
+
require_relative 'method_context'
|
5
|
+
|
6
|
+
module ImproveYourCode
|
7
|
+
module Context
|
8
|
+
class RootContext < CodeContext
|
9
|
+
def type
|
10
|
+
:root
|
11
|
+
end
|
12
|
+
|
13
|
+
def full_name
|
14
|
+
''
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_context_class
|
18
|
+
MethodContext
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'code_context'
|
4
|
+
|
5
|
+
module ImproveYourCode
|
6
|
+
module Context
|
7
|
+
class SendContext < CodeContext
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
def initialize(exp, name)
|
11
|
+
@name = name
|
12
|
+
super exp
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'method_context'
|
4
|
+
|
5
|
+
module ImproveYourCode
|
6
|
+
module Context
|
7
|
+
class SingletonMethodContext < MethodContext
|
8
|
+
def singleton_method?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def instance_method?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def module_function?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def defined_as_instance_method?
|
21
|
+
type == :def
|
22
|
+
end
|
23
|
+
|
24
|
+
def apply_current_visibility(current_visibility)
|
25
|
+
super if defined_as_instance_method?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../ast/node'
|
4
|
+
|
5
|
+
module ImproveYourCode
|
6
|
+
module Context
|
7
|
+
class StatementCounter
|
8
|
+
attr_reader :value
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@value = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def increase_by(sexp)
|
15
|
+
self.value = value + sexp.length if sexp
|
16
|
+
end
|
17
|
+
|
18
|
+
def decrease_by(number)
|
19
|
+
self.value = value - number
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_writer :value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImproveYourCode
|
4
|
+
module Context
|
5
|
+
class VisibilityTracker
|
6
|
+
VISIBILITY_MODIFIERS = %i[
|
7
|
+
private public protected
|
8
|
+
module_function
|
9
|
+
].freeze
|
10
|
+
VISIBILITY_MAP = {
|
11
|
+
public_class_method: :public,
|
12
|
+
private_class_method: :private
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@tracked_visibility = :public
|
17
|
+
end
|
18
|
+
|
19
|
+
def track_visibility(children:, visibility:, names:)
|
20
|
+
return unless VISIBILITY_MODIFIERS.include? visibility
|
21
|
+
|
22
|
+
if names.any?
|
23
|
+
children.each do |child|
|
24
|
+
child.visibility = visibility if names.include?(child.name)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
self.tracked_visibility = visibility
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def track_singleton_visibility(children:, visibility:, names:)
|
32
|
+
return if names.empty?
|
33
|
+
|
34
|
+
visibility = VISIBILITY_MAP[visibility]
|
35
|
+
|
36
|
+
return unless visibility
|
37
|
+
|
38
|
+
track_visibility(
|
39
|
+
children: children, visibility: visibility, names: names
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_child_visibility(child)
|
44
|
+
child.apply_current_visibility tracked_visibility
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
attr_accessor :tracked_visibility
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|