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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3a81cdf7aa2de07a1fa7de9913869dd0d13c9d2b
|
4
|
+
data.tar.gz: 9e90b1d298fb90594093b3e2ed0486e99f248d15
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d163763a9847ea7a6a3420a0dd7cb7ef23704fb98b0590706cc8987ae634a71d070f4715027163240b8830e34a9dec1093970c6214c93efaed949331cb462e2b
|
7
|
+
data.tar.gz: f9f073e2c4a4d112a45e4ae720cd8f6990d4110fab0b443d0ac45ed06d3cbd3ed282a39d95b4db019a2e570e003a7ecbca99174a15f28cfcb028f63eff32b93f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), 'lib/improve_your_code/version')
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'improve_your_code'
|
7
|
+
s.version = ImproveYourCode::Version::STRING
|
8
|
+
|
9
|
+
s.authors = ['Ilya Umanets']
|
10
|
+
s.default_executable = 'improve_your_code'
|
11
|
+
s.description =
|
12
|
+
'ImproveYourCode is a tool that examines Ruby classes, modules and methods and reports ' \
|
13
|
+
'any code smells it finds.'
|
14
|
+
|
15
|
+
s.files = `git ls-files -z`.split("\0")
|
16
|
+
s.executables = s.files.grep(%r{^bin/}).map { |path| File.basename(path) }
|
17
|
+
s.required_ruby_version = '>= 2.1.0'
|
18
|
+
s.summary = 'Code smell detector for Ruby'
|
19
|
+
|
20
|
+
s.add_runtime_dependency 'parser', '< 2.5', '>= 2.4.0.0'
|
21
|
+
s.add_runtime_dependency 'rainbow', '~> 2.0'
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'node'
|
4
|
+
require_relative 'sexp_extensions'
|
5
|
+
|
6
|
+
module ImproveYourCode
|
7
|
+
module AST
|
8
|
+
class ASTNodeClassMap
|
9
|
+
def initialize
|
10
|
+
@klass_map = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def klass_for(type)
|
14
|
+
klass_map[type] ||= Class.new(Node).tap do |klass|
|
15
|
+
extension = extension_map[type]
|
16
|
+
|
17
|
+
klass.send :include, extension if extension
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def extension_map
|
22
|
+
@extension_map ||=
|
23
|
+
begin
|
24
|
+
assoc = SexpExtensions.constants.map do |const|
|
25
|
+
[
|
26
|
+
const.to_s.sub(/Node$/, '').downcase.to_sym,
|
27
|
+
SexpExtensions.const_get(const)
|
28
|
+
]
|
29
|
+
end
|
30
|
+
Hash[assoc]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :klass_map
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../cli/silencer'
|
4
|
+
|
5
|
+
ImproveYourCode::CLI::Silencer.silently do
|
6
|
+
require 'parser'
|
7
|
+
end
|
8
|
+
|
9
|
+
module ImproveYourCode
|
10
|
+
module AST
|
11
|
+
class Node < ::Parser::AST::Node
|
12
|
+
def initialize(type, children = [], options = {})
|
13
|
+
@comments = options.fetch(:comments, [])
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def full_comment
|
18
|
+
comments.map(&:text).join("\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
def leading_comment
|
22
|
+
line = location.line
|
23
|
+
comment_lines = comments.select do |comment|
|
24
|
+
comment.location.line < line
|
25
|
+
end
|
26
|
+
comment_lines.map(&:text).join("\n")
|
27
|
+
end
|
28
|
+
|
29
|
+
def line
|
30
|
+
loc&.line
|
31
|
+
end
|
32
|
+
|
33
|
+
def each_node(target_type, ignoring = [], &blk)
|
34
|
+
if block_given?
|
35
|
+
look_for_type(target_type, ignoring, &blk)
|
36
|
+
else
|
37
|
+
result = []
|
38
|
+
look_for_type(target_type, ignoring) { |exp| result << exp }
|
39
|
+
result
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def find_nodes(target_types, ignoring = [])
|
44
|
+
result = []
|
45
|
+
look_for_types(target_types, ignoring) { |exp| result << exp }
|
46
|
+
result
|
47
|
+
end
|
48
|
+
|
49
|
+
def contains_nested_node?(target_type)
|
50
|
+
look_for_type(target_type) { |_elem| return true }
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
def format_to_ruby
|
55
|
+
if location
|
56
|
+
lines = location.expression.source.split("\n").map(&:strip)
|
57
|
+
case lines.length
|
58
|
+
when 1 then lines.first
|
59
|
+
when 2 then lines.join('; ')
|
60
|
+
else [lines.first, lines.last].join(' ... ')
|
61
|
+
end
|
62
|
+
else
|
63
|
+
to_s
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def length
|
68
|
+
1
|
69
|
+
end
|
70
|
+
|
71
|
+
def statements
|
72
|
+
[self]
|
73
|
+
end
|
74
|
+
|
75
|
+
def source
|
76
|
+
loc.expression.source_buffer.name
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def look_for_type(target_type, ignoring = [], &blk)
|
82
|
+
each_sexp do |elem|
|
83
|
+
elem.look_for_type(target_type, ignoring, &blk) unless ignoring.include?(elem.type)
|
84
|
+
end
|
85
|
+
yield self if type == target_type
|
86
|
+
end
|
87
|
+
|
88
|
+
def look_for_types(target_types, ignoring = [], &blk)
|
89
|
+
return if ignoring.include?(type)
|
90
|
+
if target_types.include? type
|
91
|
+
yield self
|
92
|
+
else
|
93
|
+
each_sexp do |elem|
|
94
|
+
elem.look_for_types(target_types, ignoring, &blk)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
attr_reader :comments
|
102
|
+
|
103
|
+
def each_sexp
|
104
|
+
children.each { |elem| yield elem if elem.is_a? ::Parser::AST::Node }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImproveYourCode
|
4
|
+
module AST
|
5
|
+
class ObjectRefs
|
6
|
+
def initialize
|
7
|
+
@refs = Hash.new { |refs, name| refs[name] = [] }
|
8
|
+
end
|
9
|
+
|
10
|
+
def record_reference(name:, line: nil)
|
11
|
+
refs[name] << line
|
12
|
+
end
|
13
|
+
|
14
|
+
def most_popular
|
15
|
+
max = refs.values.map(&:size).max
|
16
|
+
refs.select { |_name, refs| refs.size == max }
|
17
|
+
end
|
18
|
+
|
19
|
+
def references_to(name)
|
20
|
+
refs[name]
|
21
|
+
end
|
22
|
+
|
23
|
+
def self_is_max?
|
24
|
+
refs.empty? || most_popular.keys.include?(:self)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :refs
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImproveYourCode
|
4
|
+
module AST
|
5
|
+
class ReferenceCollector
|
6
|
+
def initialize(ast)
|
7
|
+
@ast = ast
|
8
|
+
end
|
9
|
+
|
10
|
+
def num_refs_to_self
|
11
|
+
(explicit_self_calls + implicit_self_calls).size
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :ast
|
17
|
+
|
18
|
+
def explicit_self_calls
|
19
|
+
%i[self super zsuper ivar ivasgn].flat_map do |node_type|
|
20
|
+
ast.each_node(node_type)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def implicit_self_calls
|
25
|
+
ast.each_node(:send).reject(&:receiver)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'reference_collector'
|
4
|
+
|
5
|
+
require_relative 'sexp_extensions/arguments'
|
6
|
+
require_relative 'sexp_extensions/block'
|
7
|
+
require_relative 'sexp_extensions/constant'
|
8
|
+
require_relative 'sexp_extensions/if'
|
9
|
+
require_relative 'sexp_extensions/methods'
|
10
|
+
require_relative 'sexp_extensions/module'
|
11
|
+
require_relative 'sexp_extensions/nested_assignables'
|
12
|
+
require_relative 'sexp_extensions/self'
|
13
|
+
require_relative 'sexp_extensions/send'
|
14
|
+
require_relative 'sexp_extensions/symbols'
|
15
|
+
require_relative 'sexp_extensions/variables'
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImproveYourCode
|
4
|
+
module AST
|
5
|
+
module SexpExtensions
|
6
|
+
module ArgNodeBase
|
7
|
+
def name
|
8
|
+
children.first
|
9
|
+
end
|
10
|
+
|
11
|
+
def marked_unused?
|
12
|
+
plain_name.start_with?('_')
|
13
|
+
end
|
14
|
+
|
15
|
+
def plain_name
|
16
|
+
name.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def block?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def optional_argument?
|
24
|
+
false
|
25
|
+
end
|
26
|
+
|
27
|
+
def anonymous_splat?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def components
|
32
|
+
[self]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module ArgNode
|
37
|
+
include ArgNodeBase
|
38
|
+
end
|
39
|
+
|
40
|
+
module KwargNode
|
41
|
+
include ArgNodeBase
|
42
|
+
end
|
43
|
+
|
44
|
+
module OptargNode
|
45
|
+
include ArgNodeBase
|
46
|
+
|
47
|
+
def optional_argument?
|
48
|
+
true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module KwoptargNode
|
53
|
+
include ArgNodeBase
|
54
|
+
|
55
|
+
def optional_argument?
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module BlockargNode
|
61
|
+
include ArgNodeBase
|
62
|
+
|
63
|
+
def block?
|
64
|
+
true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
module RestargNode
|
69
|
+
include ArgNodeBase
|
70
|
+
|
71
|
+
def anonymous_splat?
|
72
|
+
!name
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
module KwrestargNode
|
77
|
+
include ArgNodeBase
|
78
|
+
|
79
|
+
def anonymous_splat?
|
80
|
+
!name
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
module ShadowargNode
|
85
|
+
include ArgNodeBase
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImproveYourCode
|
4
|
+
module AST
|
5
|
+
module SexpExtensions
|
6
|
+
# Utility methods for :block nodes.
|
7
|
+
module BlockNode
|
8
|
+
def call
|
9
|
+
children.first
|
10
|
+
end
|
11
|
+
|
12
|
+
def args
|
13
|
+
children[1]
|
14
|
+
end
|
15
|
+
|
16
|
+
def block
|
17
|
+
children[2]
|
18
|
+
end
|
19
|
+
|
20
|
+
def parameters
|
21
|
+
children[1] || []
|
22
|
+
end
|
23
|
+
|
24
|
+
def parameter_names
|
25
|
+
parameters.children
|
26
|
+
end
|
27
|
+
|
28
|
+
def simple_name
|
29
|
+
:block
|
30
|
+
end
|
31
|
+
|
32
|
+
def without_block_arguments?
|
33
|
+
args.components.empty?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|