leftovers 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|