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,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ module SexpExtensions
6
+ module ConstNode
7
+ def simple_name
8
+ :Struct
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ module SexpExtensions
6
+ # Utility methods for :if nodes.
7
+ module IfNode
8
+ def condition
9
+ children.first
10
+ end
11
+
12
+ def body_nodes(type, ignoring = [])
13
+ children[1..-1].compact.flat_map { |child| child.find_nodes(type, ignoring) }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ module SexpExtensions
6
+ # Base module for utility methods for :def and :defs nodes.
7
+ module MethodNodeBase
8
+ def arguments
9
+ parameters.reject(&:block?)
10
+ end
11
+
12
+ def arg_names
13
+ arguments.map(&:name)
14
+ end
15
+
16
+ def parameters
17
+ argslist.components
18
+ end
19
+
20
+ def parameter_names
21
+ parameters.map(&:name)
22
+ end
23
+
24
+ def name_without_bang
25
+ name.to_s.chop
26
+ end
27
+
28
+ def ends_with_bang?
29
+ name[-1] == '!'
30
+ end
31
+ end
32
+
33
+ # Utility methods for :def nodes.
34
+ module DefNode
35
+ include MethodNodeBase
36
+
37
+ def name
38
+ children.first
39
+ end
40
+
41
+ def argslist
42
+ children[1]
43
+ end
44
+
45
+ def body
46
+ children[2]
47
+ end
48
+
49
+ def full_name(outer)
50
+ [outer, name].reject(&:empty?).join('#')
51
+ end
52
+ end
53
+
54
+ # Utility methods for :defs nodes.
55
+ module DefsNode
56
+ include MethodNodeBase
57
+
58
+ def receiver
59
+ children.first
60
+ end
61
+
62
+ def name
63
+ children[1]
64
+ end
65
+
66
+ def argslist
67
+ children[2]
68
+ end
69
+
70
+ def body
71
+ children[3]
72
+ end
73
+
74
+ def full_name(outer)
75
+ prefix = outer == '' ? '' : "#{outer}#"
76
+ "#{prefix}#{receiver.name}.#{name}"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ module SexpExtensions
6
+ module ConstantDefiningNodeBase
7
+ def full_name(outer)
8
+ [outer, name].reject(&:empty?).join('::')
9
+ end
10
+
11
+ def simple_name
12
+ name.split('::').last
13
+ end
14
+ end
15
+
16
+ module ModuleNodeBase
17
+ include ConstantDefiningNodeBase
18
+
19
+ def name
20
+ children.first.format_to_ruby
21
+ end
22
+ end
23
+
24
+ module ModuleNode
25
+ include ModuleNodeBase
26
+ end
27
+
28
+ module ClassNode
29
+ include ModuleNodeBase
30
+ end
31
+
32
+ module CasgnNode
33
+ include ConstantDefiningNodeBase
34
+
35
+ def defines_module?
36
+ call = constant_definition
37
+ call&.module_creation_call?
38
+ end
39
+
40
+ def name
41
+ children[1].to_s
42
+ end
43
+
44
+ def value
45
+ children[2]
46
+ end
47
+
48
+ private
49
+
50
+ def constant_definition
51
+ return nil unless value
52
+
53
+ case value.type
54
+ when :block
55
+ call = value.call
56
+ call if call.type == :send
57
+ when :send
58
+ value
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ module SexpExtensions
6
+ module NestedAssignables
7
+ def components
8
+ children.flat_map(&:components)
9
+ end
10
+ end
11
+
12
+ module ArgsNode
13
+ include NestedAssignables
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ module SexpExtensions
6
+ module SelfNode
7
+ def name
8
+ :self
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ module SexpExtensions
6
+ # Utility methods for :send nodes.
7
+ module SendNode
8
+ ATTR_DEFN_METHODS = %i[attr_writer attr_accessor].freeze
9
+
10
+ def receiver
11
+ children.first
12
+ end
13
+
14
+ def name
15
+ children[1]
16
+ end
17
+
18
+ def args
19
+ children[2..-1]
20
+ end
21
+
22
+ def participants
23
+ ([receiver] + args).compact
24
+ end
25
+
26
+ def module_creation_call?
27
+ object_creation_call? && module_creation_receiver?
28
+ end
29
+
30
+ def module_creation_receiver?
31
+ receiver&.type == :const &&
32
+ %i[Class Struct].include?(receiver.simple_name)
33
+ end
34
+
35
+ def object_creation_call?
36
+ name == :new
37
+ end
38
+
39
+ def attribute_writer?
40
+ ATTR_DEFN_METHODS.include?(name) ||
41
+ attr_with_writable_flag?
42
+ end
43
+
44
+ # Handles the case where we create an attribute writer via:
45
+ # attr :foo, true
46
+ def attr_with_writable_flag?
47
+ name == :attr && args.any? && args.last.type == :true
48
+ end
49
+ end
50
+
51
+ Op_AsgnNode = SendNode
52
+ CSendNode = SendNode
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ module SexpExtensions
6
+ # Utility methods for :sym nodes.
7
+ module SymNode
8
+ def name
9
+ children.first
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ImproveYourCode
4
+ module AST
5
+ module SexpExtensions
6
+ # Base module for utility methods for nodes representing variables.
7
+ module VariableBase
8
+ def name
9
+ children.first
10
+ end
11
+ end
12
+
13
+ # Utility methods for :cvar nodes.
14
+ module CvarNode
15
+ include VariableBase
16
+ end
17
+
18
+ # Utility methods for :ivar nodes.
19
+ module IvarNode
20
+ include VariableBase
21
+ end
22
+
23
+ # Utility methods for :ivasgn nodes.
24
+ module IvasgnNode
25
+ include VariableBase
26
+ end
27
+
28
+ # Utility methods for :lvar nodes.
29
+ module LvarNode
30
+ include VariableBase
31
+
32
+ alias var_name name
33
+ end
34
+
35
+ # Utility methods for :gvar nodes.
36
+ module GvarNode
37
+ include VariableBase
38
+ end
39
+
40
+ LvasgnNode = LvarNode
41
+ CvasgnNode = CvarNode
42
+ CvdeclNode = CvarNode
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../source/source_locator'
4
+ require_relative 'command/report_command'
5
+
6
+ module ImproveYourCode
7
+ module CLI
8
+ class Application
9
+ def execute
10
+ enable_rainbow
11
+
12
+ command = Command::ReportCommand.new(sources: sources)
13
+
14
+ command.execute
15
+
16
+ true
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :command
22
+
23
+ def enable_rainbow
24
+ Rainbow.enabled = true
25
+ end
26
+
27
+ def sources
28
+ Source::SourceLocator.new(['.']).sources
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../examiner'
4
+ require_relative '../../report/text_report'
5
+
6
+ module ImproveYourCode
7
+ module CLI
8
+ module Command
9
+ class ReportCommand
10
+ def initialize(sources:)
11
+ @sources = sources
12
+ end
13
+
14
+ def execute
15
+ populate_reporter_with_smells
16
+ reporter.show
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :sources
22
+
23
+ def smell_names
24
+ @smell_names ||= []
25
+ end
26
+
27
+ def populate_reporter_with_smells
28
+ sources.each do |source|
29
+ reporter.add_examiner Examiner.new(source)
30
+ end
31
+ end
32
+
33
+ def reporter
34
+ @reporter ||= Report::TextReport.new
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+
5
+ module ImproveYourCode
6
+ module CLI
7
+ # CLI silencer
8
+ module Silencer
9
+ module_function
10
+
11
+ def silently
12
+ old_verbose = $VERBOSE
13
+ $VERBOSE = false
14
+ $stderr = StringIO.new
15
+ $stdout = StringIO.new
16
+ yield
17
+ ensure
18
+ $VERBOSE = old_verbose
19
+ $stderr = STDERR
20
+ $stdout = STDOUT
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ require_relative 'smell_detectors/base_detector'
6
+
7
+ module ImproveYourCode
8
+ class CodeComment
9
+ CONFIGURATION_REGEX = /
10
+ :improve_your_code: # prefix
11
+ (\w+) # smell detector e.g.: UncommunicativeVariableName
12
+ (
13
+ :? # legacy separator
14
+ \s*
15
+ (\{.*?\}) # optional details in hash style e.g.: { max_methods: 30 }
16
+ )?
17
+ /x
18
+ SANITIZE_REGEX = /(#|\n|\s)+/ # Matches '#', newlines and > 1 whitespaces.
19
+ DISABLE_DETECTOR_CONFIGURATION = '{ enabled: false }'
20
+ MINIMUM_CONTENT_LENGTH = 2
21
+ LEGACY_SEPARATOR = ':'
22
+
23
+ attr_reader :config
24
+
25
+ def initialize(comment:, line: nil, source: nil)
26
+ @original_comment = comment
27
+ @line = line
28
+ @source = source
29
+ @config = Hash.new { |hash, key| hash[key] = {} }
30
+
31
+ @original_comment.scan(CONFIGURATION_REGEX) do |detector_name, _option_string, options|
32
+ @config.merge! detector_name => YAML.safe_load(options || DISABLE_DETECTOR_CONFIGURATION,
33
+ [Regexp])
34
+ end
35
+ end
36
+
37
+ def descriptive?
38
+ sanitized_comment.split(/\s+/).length >= MINIMUM_CONTENT_LENGTH
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :original_comment, :source, :line
44
+
45
+ def sanitized_comment
46
+ @sanitized_comment ||= original_comment
47
+ .gsub(CONFIGURATION_REGEX, '')
48
+ .gsub(SANITIZE_REGEX, ' ')
49
+ .strip
50
+ end
51
+ end
52
+ end