leftovers 0.2.3 → 0.3.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 +4 -4
- data/CHANGELOG.md +8 -0
- data/exe/leftovers +1 -1
- data/leftovers.gemspec +5 -2
- data/lib/config/rails.yml +1 -0
- data/lib/config/ruby.yml +8 -5
- data/lib/config/{selenium.yml → selenium-webdriver.yml} +0 -0
- data/lib/leftovers.rb +22 -17
- data/lib/leftovers/argument_rule.rb +29 -19
- data/lib/leftovers/ast/builder.rb +3 -0
- data/lib/leftovers/ast/node.rb +40 -54
- data/lib/leftovers/backports.rb +8 -28
- data/lib/leftovers/cli.rb +16 -10
- data/lib/leftovers/collector.rb +4 -4
- data/lib/leftovers/config.rb +38 -11
- data/lib/leftovers/core_ext.rb +7 -5
- data/lib/leftovers/definition.rb +7 -7
- data/lib/leftovers/definition_set.rb +0 -4
- data/lib/leftovers/file.rb +1 -1
- data/lib/leftovers/file_collector.rb +13 -16
- data/lib/leftovers/file_list.rb +9 -14
- data/lib/leftovers/haml.rb +9 -12
- data/lib/leftovers/hash_rule.rb +12 -10
- data/lib/leftovers/merged_config.rb +9 -8
- data/lib/leftovers/name_rule.rb +62 -17
- data/lib/leftovers/rake_task.rb +3 -3
- data/lib/leftovers/reporter.rb +1 -1
- data/lib/leftovers/rule.rb +22 -14
- data/lib/leftovers/transform_rule.rb +21 -23
- data/lib/leftovers/value_rule.rb +5 -4
- data/lib/leftovers/version.rb +1 -1
- metadata +49 -7
data/lib/leftovers/backports.rb
CHANGED
@@ -3,6 +3,14 @@
|
|
3
3
|
module Leftovers
|
4
4
|
ruby_version = Gem::Version.new(RUBY_VERSION)
|
5
5
|
unless ruby_version >= Gem::Version.new('2.5')
|
6
|
+
require 'set'
|
7
|
+
module SetCaseEq
|
8
|
+
refine ::Set do
|
9
|
+
def ===(value)
|
10
|
+
include?(value)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
6
14
|
module StringDeletePrefixSuffix
|
7
15
|
refine ::String do
|
8
16
|
def delete_prefix!(str)
|
@@ -24,33 +32,5 @@ module Leftovers
|
|
24
32
|
end
|
25
33
|
end
|
26
34
|
end
|
27
|
-
|
28
|
-
require 'yaml'
|
29
|
-
module YAMLSymbolizeNames
|
30
|
-
refine YAML.singleton_class do
|
31
|
-
alias_method :safe_load_without_symbolize_names, :safe_load
|
32
|
-
def safe_load(path, *args, symbolize_names: false, **kwargs)
|
33
|
-
if symbolize_names
|
34
|
-
symbolize_names!(safe_load_without_symbolize_names(path, *args, **kwargs))
|
35
|
-
else
|
36
|
-
safe_load_without_symbolize_names(path, *args, **kwargs)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def symbolize_names!(obj) # rubocop:disable Metrics/MethodLength
|
43
|
-
case obj
|
44
|
-
when Hash
|
45
|
-
obj.keys.each do |key| # rubocop:disable Style/HashEachMethods # each_key never finishes.
|
46
|
-
obj[key.to_sym] = symbolize_names!(obj.delete(key))
|
47
|
-
end
|
48
|
-
when Array
|
49
|
-
obj.map! { |ea| symbolize_names!(ea) }
|
50
|
-
end
|
51
|
-
obj
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
35
|
end
|
56
36
|
end
|
data/lib/leftovers/cli.rb
CHANGED
@@ -6,25 +6,31 @@ require_relative 'version'
|
|
6
6
|
|
7
7
|
module Leftovers
|
8
8
|
class CLI
|
9
|
-
attr_reader :argv, :stdout, :stderr
|
10
|
-
|
11
9
|
def initialize(argv: [], stdout: $stdout, stderr: $stderr)
|
12
10
|
@argv = argv
|
13
11
|
@stdout = stdout
|
14
12
|
@stderr = stderr
|
13
|
+
end
|
15
14
|
|
16
|
-
|
15
|
+
def run
|
16
|
+
catch(:leftovers_exit) do
|
17
|
+
Leftovers.reset
|
18
|
+
parse_options
|
17
19
|
|
18
|
-
|
20
|
+
Leftovers.run(stdout: stdout, stderr: stderr)
|
21
|
+
end
|
19
22
|
end
|
20
23
|
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :argv, :stdout, :stderr
|
27
|
+
|
21
28
|
def parse_options # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
22
29
|
opts = OptionParser.new
|
23
30
|
Leftovers.parallel = true
|
24
31
|
Leftovers.progress = true
|
25
32
|
|
26
33
|
opts.banner = 'Usage: leftovers [options]'
|
27
|
-
opts.on('-q', '--quiet', 'Silences output') { Leftovers.quiet = true }
|
28
34
|
opts.on('--[no-]parallel', 'Run in parallel or not, default --parallel') do |p|
|
29
35
|
Leftovers.parallel = p
|
30
36
|
end
|
@@ -32,16 +38,16 @@ module Leftovers
|
|
32
38
|
Leftovers.progress = p
|
33
39
|
end
|
34
40
|
opts.on('-v', '--version', 'Returns the current version') do
|
35
|
-
stdout.puts(Leftovers::
|
36
|
-
exit
|
41
|
+
stdout.puts(Leftovers::VERSION)
|
42
|
+
Leftovers.exit
|
37
43
|
end
|
38
44
|
opts.on('--dry-run', 'Output files that will be looked at') do
|
39
|
-
|
40
|
-
exit
|
45
|
+
Leftovers::FileList.new.each { |f| stdout.puts f.relative_path }
|
46
|
+
Leftovers.exit
|
41
47
|
end
|
42
48
|
opts.on('-h', '--help', 'Shows this message') do
|
43
49
|
stdout.puts(opts.help)
|
44
|
-
exit
|
50
|
+
Leftovers.exit
|
45
51
|
end
|
46
52
|
|
47
53
|
opts.parse(argv)
|
data/lib/leftovers/collector.rb
CHANGED
@@ -31,9 +31,9 @@ module Leftovers
|
|
31
31
|
|
32
32
|
def collect_file_list(list)
|
33
33
|
if Leftovers.parallel?
|
34
|
-
Parallel.each(list, finish: method(:
|
34
|
+
Parallel.each(list, finish: method(:finish_file), &method(:collect_file))
|
35
35
|
else
|
36
|
-
list.each { |file|
|
36
|
+
list.each { |file| finish_file(nil, nil, collect_file(file)) }
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -46,11 +46,11 @@ module Leftovers
|
|
46
46
|
|
47
47
|
def print_progress
|
48
48
|
Leftovers.print(
|
49
|
-
"
|
49
|
+
"\e[2Kchecked #{@count} files, collected #{@count_calls} calls, #{@count_definitions} definitions\r" # rubocop:disable Layout/LineLength
|
50
50
|
)
|
51
51
|
end
|
52
52
|
|
53
|
-
def
|
53
|
+
def finish_file(_, _, result) # rubocop:disable Metrics/MethodLength
|
54
54
|
@count += 1
|
55
55
|
@count_calls += result[:calls].length
|
56
56
|
@count_definitions += result[:definitions].length
|
data/lib/leftovers/config.rb
CHANGED
@@ -6,16 +6,12 @@ require_relative 'rule'
|
|
6
6
|
module Leftovers
|
7
7
|
class Config
|
8
8
|
# :nocov:
|
9
|
-
using ::Leftovers::
|
9
|
+
using ::Leftovers::SetCaseEq if defined?(::Leftovers::SetCaseEq)
|
10
10
|
# :nocov:
|
11
11
|
|
12
12
|
attr_reader :name
|
13
13
|
|
14
|
-
def initialize(
|
15
|
-
name,
|
16
|
-
path: ::File.join(__dir__, '..', 'config', "#{name}.yml"),
|
17
|
-
content: (::File.exist?(path) ? ::File.read(path) : '')
|
18
|
-
)
|
14
|
+
def initialize(name, path: nil, content: nil)
|
19
15
|
@name = name.to_sym
|
20
16
|
@path = path
|
21
17
|
@content = content
|
@@ -39,15 +35,46 @@ module Leftovers
|
|
39
35
|
|
40
36
|
def rules
|
41
37
|
@rules ||= Rule.wrap(yaml[:rules])
|
38
|
+
rescue Leftovers::ConfigError => e
|
39
|
+
warn "\e[31mConfig Error: (#{path}): #{e.message}\e[0m"
|
40
|
+
Leftovers.exit 1
|
42
41
|
end
|
43
42
|
|
44
43
|
private
|
45
44
|
|
46
|
-
def
|
47
|
-
@
|
48
|
-
rescue Psych::SyntaxError => e
|
49
|
-
warn "\e[31mError with config #{path}: #{e.message}\e[0m"
|
50
|
-
exit 1
|
45
|
+
def content
|
46
|
+
@content ||= ::File.exist?(path) ? ::File.read(path) : ''
|
51
47
|
end
|
48
|
+
|
49
|
+
def path
|
50
|
+
@path ||= ::File.expand_path("../config/#{name}.yml", __dir__)
|
51
|
+
end
|
52
|
+
|
53
|
+
def yaml # rubocop:disable Metrics/MethodLength
|
54
|
+
# :nocov:
|
55
|
+
@yaml ||= if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6')
|
56
|
+
Psych.safe_load(content, symbolize_names: true, filename: path) || {}
|
57
|
+
else
|
58
|
+
symbolize_names!(Psych.safe_load(content, [], [], false, path)) || {}
|
59
|
+
end
|
60
|
+
# :nocov:
|
61
|
+
rescue ::Psych::SyntaxError => e
|
62
|
+
warn "\e[31mConfig SyntaxError: #{e.message}\e[0m"
|
63
|
+
Leftovers.exit 1
|
64
|
+
end
|
65
|
+
|
66
|
+
# :nocov:
|
67
|
+
def symbolize_names!(obj) # rubocop:disable Metrics/MethodLength
|
68
|
+
case obj
|
69
|
+
when Hash
|
70
|
+
obj.keys.each do |key| # rubocop:disable Style/HashEachMethods # each_key never finishes.
|
71
|
+
obj[key.to_sym] = symbolize_names!(obj.delete(key))
|
72
|
+
end
|
73
|
+
when Array
|
74
|
+
obj.map! { |ea| symbolize_names!(ea) }
|
75
|
+
end
|
76
|
+
obj
|
77
|
+
end
|
78
|
+
# :nocov:
|
52
79
|
end
|
53
80
|
end
|
data/lib/leftovers/core_ext.rb
CHANGED
@@ -3,11 +3,13 @@
|
|
3
3
|
require 'set'
|
4
4
|
|
5
5
|
class Array
|
6
|
-
def leftovers_append(other)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
def leftovers_append(other) # rubocop:disable Metrics/MethodLength
|
7
|
+
return self if other.nil?
|
8
|
+
|
9
|
+
if other.respond_to?(:to_a)
|
10
|
+
concat(other.to_a)
|
11
|
+
else
|
12
|
+
self << other
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
data/lib/leftovers/definition.rb
CHANGED
@@ -4,13 +4,10 @@ module Leftovers
|
|
4
4
|
class Definition
|
5
5
|
attr_reader :name
|
6
6
|
alias_method :names, :name
|
7
|
-
alias_method :full_name, :name
|
8
|
-
attr_reader :name_s
|
9
|
-
alias_method :to_s, :name_s
|
10
7
|
attr_reader :test
|
11
8
|
alias_method :test?, :test
|
12
9
|
|
13
|
-
def initialize(
|
10
|
+
def initialize(
|
14
11
|
name,
|
15
12
|
method_node: nil,
|
16
13
|
location: method_node.loc.expression,
|
@@ -18,7 +15,6 @@ module Leftovers
|
|
18
15
|
test: method_node.test?
|
19
16
|
)
|
20
17
|
@name = name
|
21
|
-
@name_s = name.to_s.freeze
|
22
18
|
|
23
19
|
@location = location
|
24
20
|
@file = file
|
@@ -37,6 +33,10 @@ module Leftovers
|
|
37
33
|
@file.relative_path
|
38
34
|
end
|
39
35
|
|
36
|
+
def to_s
|
37
|
+
@name.to_s
|
38
|
+
end
|
39
|
+
|
40
40
|
def line
|
41
41
|
@location.line
|
42
42
|
end
|
@@ -46,7 +46,7 @@ module Leftovers
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def full_location
|
49
|
-
"#{path}:#{
|
49
|
+
"#{path}:#{line}:#{column}"
|
50
50
|
end
|
51
51
|
|
52
52
|
def highlighted_source(highlight = "\e[31m", normal = "\e[0m") # rubocop:disable Metrics/AbcSize
|
@@ -64,7 +64,7 @@ module Leftovers
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def skipped?
|
67
|
-
Leftovers.config.skip_rules.any? { |r| r.match?(@name,
|
67
|
+
Leftovers.config.skip_rules.any? { |r| r.match?(@name, path) }
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
data/lib/leftovers/file.rb
CHANGED
@@ -52,11 +52,9 @@ module Leftovers
|
|
52
52
|
def process_comments(comments) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
53
53
|
comments.each do |comment|
|
54
54
|
@allow_lines << comment.loc.line if comment.text.match?(LEFTOVERS_ALLOW_RE)
|
55
|
-
|
56
55
|
@test_lines << comment.loc.line if comment.text.match?(LEFTOVERS_TEST_RE)
|
57
56
|
|
58
57
|
next unless (match = comment.text.match(LEFTOVERS_CALL_RE))
|
59
|
-
next unless match[1]
|
60
58
|
|
61
59
|
match[1].scan(NAME_RE).each { |s| add_call(s.to_sym) }
|
62
60
|
end
|
@@ -64,13 +62,15 @@ module Leftovers
|
|
64
62
|
|
65
63
|
# grab method definitions
|
66
64
|
def on_def(node)
|
67
|
-
add_definition(node.
|
65
|
+
add_definition(node.name, node.loc.name)
|
68
66
|
|
69
67
|
super
|
70
68
|
end
|
71
69
|
|
72
70
|
def on_ivasgn(node)
|
73
|
-
add_definition(node.
|
71
|
+
add_definition(node.name, node.loc.name)
|
72
|
+
|
73
|
+
collect_rules(node)
|
74
74
|
|
75
75
|
super
|
76
76
|
end
|
@@ -78,7 +78,7 @@ module Leftovers
|
|
78
78
|
alias_method :on_cvasgn, :on_ivasgn
|
79
79
|
|
80
80
|
def on_ivar(node)
|
81
|
-
add_call(node.
|
81
|
+
add_call(node.name)
|
82
82
|
|
83
83
|
super
|
84
84
|
end
|
@@ -107,8 +107,7 @@ module Leftovers
|
|
107
107
|
def on_send(node)
|
108
108
|
super
|
109
109
|
|
110
|
-
add_call(node.
|
111
|
-
|
110
|
+
add_call(node.name)
|
112
111
|
collect_rules(node)
|
113
112
|
end
|
114
113
|
alias_method :on_csend, :on_send
|
@@ -116,14 +115,14 @@ module Leftovers
|
|
116
115
|
def on_const(node)
|
117
116
|
super
|
118
117
|
|
119
|
-
add_call(node.
|
118
|
+
add_call(node.name)
|
120
119
|
end
|
121
120
|
|
122
121
|
# grab e.g. :to_s in each(&:to_s)
|
123
122
|
def on_block_pass(node)
|
124
123
|
super
|
125
124
|
|
126
|
-
add_call(node.children.first.to_sym) if node.children.first
|
125
|
+
add_call(node.children.first.to_sym) if node.children.first.string_or_symbol?
|
127
126
|
end
|
128
127
|
|
129
128
|
# grab class Constant or module Constant
|
@@ -134,7 +133,7 @@ module Leftovers
|
|
134
133
|
|
135
134
|
node = node.children.first
|
136
135
|
|
137
|
-
add_definition(node.
|
136
|
+
add_definition(node.name, node.loc.name)
|
138
137
|
end
|
139
138
|
alias_method :on_module, :on_class
|
140
139
|
|
@@ -142,7 +141,7 @@ module Leftovers
|
|
142
141
|
def on_casgn(node)
|
143
142
|
super
|
144
143
|
|
145
|
-
add_definition(node.
|
144
|
+
add_definition(node.name, node.loc.name)
|
146
145
|
|
147
146
|
collect_rules(node)
|
148
147
|
end
|
@@ -177,22 +176,20 @@ module Leftovers
|
|
177
176
|
def collect_var_op_asgn(node)
|
178
177
|
name = node.children.first
|
179
178
|
|
180
|
-
return unless name
|
181
|
-
|
182
179
|
add_call(name)
|
183
180
|
end
|
184
181
|
|
185
182
|
def collect_send_op_asgn(node)
|
186
183
|
name = node.children[1]
|
187
184
|
|
188
|
-
return unless name
|
189
|
-
|
190
185
|
add_call(:"#{name}=")
|
191
186
|
end
|
192
187
|
|
193
188
|
def collect_op_asgn(node)
|
194
189
|
node = node.children.first
|
190
|
+
# :nocov: # don't need else, it's exhaustive for callers
|
195
191
|
case node.type
|
192
|
+
# :nocov:
|
196
193
|
when :send then collect_send_op_asgn(node)
|
197
194
|
when :ivasgn, :gvasgn, :cvasgn then collect_var_op_asgn(node)
|
198
195
|
end
|
@@ -200,7 +197,7 @@ module Leftovers
|
|
200
197
|
|
201
198
|
def collect_rules(node) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
202
199
|
Leftovers.config.rules.each do |rule|
|
203
|
-
next unless rule.match?(node.name,
|
200
|
+
next unless rule.match?(node.name, filename)
|
204
201
|
|
205
202
|
next if rule.skip?
|
206
203
|
|
data/lib/leftovers/file_list.rb
CHANGED
@@ -4,24 +4,19 @@ require 'fast_ignore'
|
|
4
4
|
require_relative 'file'
|
5
5
|
|
6
6
|
module Leftovers
|
7
|
-
class FileList
|
8
|
-
|
7
|
+
class FileList < ::FastIgnore
|
8
|
+
def initialize
|
9
|
+
super(
|
10
|
+
ignore_rules: Leftovers.config.exclude_paths,
|
11
|
+
include_rules: Leftovers.config.include_paths,
|
12
|
+
root: Leftovers.pwd
|
13
|
+
)
|
14
|
+
end
|
9
15
|
|
10
16
|
def each
|
11
|
-
|
17
|
+
super do |file|
|
12
18
|
yield(Leftovers::File.new(file))
|
13
19
|
end
|
14
20
|
end
|
15
|
-
|
16
|
-
def to_a
|
17
|
-
enum_for(:each).to_a
|
18
|
-
end
|
19
|
-
|
20
|
-
def fast_ignore
|
21
|
-
FastIgnore.new(
|
22
|
-
ignore_rules: Leftovers.config.exclude_paths,
|
23
|
-
include_rules: ['#!:ruby'] + Leftovers.config.include_paths
|
24
|
-
)
|
25
|
-
end
|
26
21
|
end
|
27
22
|
end
|
data/lib/leftovers/haml.rb
CHANGED
@@ -4,19 +4,16 @@ module Leftovers
|
|
4
4
|
module Haml
|
5
5
|
module_function
|
6
6
|
|
7
|
-
def precompile(file) # rubocop:disable Metrics/MethodLength
|
8
|
-
Leftovers.try_require('haml', message: <<~MESSAGE)
|
9
|
-
Skipped parsing
|
10
|
-
`gem install
|
7
|
+
def precompile(file, name) # rubocop:disable Metrics/MethodLength
|
8
|
+
return '' unless Leftovers.try_require('haml', message: <<~MESSAGE) # rubocop:disable Layout/EmptyLineAfterGuardClause
|
9
|
+
Skipped parsing #{name.relative_path}, because the haml gem was not available
|
10
|
+
`gem install haml`
|
11
11
|
MESSAGE
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
''
|
18
|
-
end
|
19
|
-
else
|
12
|
+
|
13
|
+
begin
|
14
|
+
::Haml::Engine.new(file).precompiled
|
15
|
+
rescue ::Haml::SyntaxError => e
|
16
|
+
Leftovers.warn "#{e.class}: #{e.message} #{name.relative_path}:#{e.line}"
|
20
17
|
''
|
21
18
|
end
|
22
19
|
end
|