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,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,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,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
|