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.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +12 -0
  4. data/README.md +0 -0
  5. data/Rakefile +4 -0
  6. data/bin/improve_your_code +6 -0
  7. data/improve_your_code.gemspec +22 -0
  8. data/lib/improve_your_code/ast/ast_node_class_map.rb +39 -0
  9. data/lib/improve_your_code/ast/builder.rb +11 -0
  10. data/lib/improve_your_code/ast/node.rb +108 -0
  11. data/lib/improve_your_code/ast/object_refs.rb +32 -0
  12. data/lib/improve_your_code/ast/reference_collector.rb +29 -0
  13. data/lib/improve_your_code/ast/sexp_extensions.rb +15 -0
  14. data/lib/improve_your_code/ast/sexp_extensions/arguments.rb +89 -0
  15. data/lib/improve_your_code/ast/sexp_extensions/block.rb +38 -0
  16. data/lib/improve_your_code/ast/sexp_extensions/constant.rb +13 -0
  17. data/lib/improve_your_code/ast/sexp_extensions/if.rb +18 -0
  18. data/lib/improve_your_code/ast/sexp_extensions/methods.rb +81 -0
  19. data/lib/improve_your_code/ast/sexp_extensions/module.rb +64 -0
  20. data/lib/improve_your_code/ast/sexp_extensions/nested_assignables.rb +17 -0
  21. data/lib/improve_your_code/ast/sexp_extensions/self.rb +13 -0
  22. data/lib/improve_your_code/ast/sexp_extensions/send.rb +55 -0
  23. data/lib/improve_your_code/ast/sexp_extensions/symbols.rb +14 -0
  24. data/lib/improve_your_code/ast/sexp_extensions/variables.rb +45 -0
  25. data/lib/improve_your_code/cli/application.rb +32 -0
  26. data/lib/improve_your_code/cli/command/report_command.rb +39 -0
  27. data/lib/improve_your_code/cli/silencer.rb +24 -0
  28. data/lib/improve_your_code/code_comment.rb +52 -0
  29. data/lib/improve_your_code/context/attribute_context.rb +33 -0
  30. data/lib/improve_your_code/context/code_context.rb +121 -0
  31. data/lib/improve_your_code/context/method_context.rb +79 -0
  32. data/lib/improve_your_code/context/module_context.rb +77 -0
  33. data/lib/improve_your_code/context/root_context.rb +22 -0
  34. data/lib/improve_your_code/context/send_context.rb +16 -0
  35. data/lib/improve_your_code/context/singleton_attribute_context.rb +13 -0
  36. data/lib/improve_your_code/context/singleton_method_context.rb +29 -0
  37. data/lib/improve_your_code/context/statement_counter.rb +27 -0
  38. data/lib/improve_your_code/context/visibility_tracker.rb +52 -0
  39. data/lib/improve_your_code/context_builder.rb +121 -0
  40. data/lib/improve_your_code/detector_repository.rb +32 -0
  41. data/lib/improve_your_code/examiner.rb +48 -0
  42. data/lib/improve_your_code/report/formatter.rb +25 -0
  43. data/lib/improve_your_code/report/formatter/heading_formatter.rb +33 -0
  44. data/lib/improve_your_code/report/formatter/progress_formatter.rb +25 -0
  45. data/lib/improve_your_code/report/formatter/simple_warning_formatter.rb +13 -0
  46. data/lib/improve_your_code/report/text_report.rb +76 -0
  47. data/lib/improve_your_code/smell_configuration.rb +48 -0
  48. data/lib/improve_your_code/smell_detectors.rb +12 -0
  49. data/lib/improve_your_code/smell_detectors/base_detector.rb +114 -0
  50. data/lib/improve_your_code/smell_detectors/long_parameter_list.rb +44 -0
  51. data/lib/improve_your_code/smell_detectors/too_many_constants.rb +54 -0
  52. data/lib/improve_your_code/smell_detectors/too_many_instance_variables.rb +48 -0
  53. data/lib/improve_your_code/smell_detectors/too_many_methods.rb +47 -0
  54. data/lib/improve_your_code/smell_detectors/too_many_statements.rb +43 -0
  55. data/lib/improve_your_code/smell_detectors/uncommunicative_method_name.rb +58 -0
  56. data/lib/improve_your_code/smell_detectors/uncommunicative_module_name.rb +71 -0
  57. data/lib/improve_your_code/smell_detectors/uncommunicative_variable_name.rb +129 -0
  58. data/lib/improve_your_code/smell_detectors/unused_parameters.rb +24 -0
  59. data/lib/improve_your_code/smell_detectors/unused_private_method.rb +69 -0
  60. data/lib/improve_your_code/smell_warning.rb +70 -0
  61. data/lib/improve_your_code/source/source_code.rb +84 -0
  62. data/lib/improve_your_code/source/source_locator.rb +38 -0
  63. data/lib/improve_your_code/tree_dresser.rb +74 -0
  64. data/lib/improve_your_code/version.rb +7 -0
  65. 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,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'attribute_context'
4
+
5
+ module ImproveYourCode
6
+ module Context
7
+ class SingletonAttributeContext < AttributeContext
8
+ def instance_method?
9
+ false
10
+ end
11
+ end
12
+ end
13
+ 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