debride 1.13.0 → 1.15.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 31ff20d8c8b7d749e113ffd4eac634fa13c52e9c84461e52e92c07bbd98a40f1
4
- data.tar.gz: dbeb5f7cc0129cc46f38f2a0b68d4b30d5b70d9f0dd7e3581a006e82adfcf2de
3
+ metadata.gz: 77baff9dbc4bab278d0e6b6fe16772ecdce26b00c4c7b24469e0680f0d608c41
4
+ data.tar.gz: 885b6274f63f3d76c6a106543d28be50f177fcc6fa6adad7cf642c0b3ef7f0d8
5
5
  SHA512:
6
- metadata.gz: 293fc6a6d550e86d05a8d4784956fd1a886fea928b9b562a06502a0732e4f34af79aaebbd4f85ebe7aa9bc31fdb7281ec66487a73a2ef2853df467888197eac7
7
- data.tar.gz: 9de34932869d721f0f2b98fd719ad0ef07d632d8588542cd4c1d4c3d5f7a57539dad80d858acfd2fb37b3a94b7d26f36ee8204e92a7cefeefd37e11653b9fe30
6
+ metadata.gz: 421c0cb4b4aa45ed2e7897254549def50af4f4491a029676bcc7671aa026e8cda45b9774073eef8d3bffa15665f7df0466e1b6a68f236fc0b0c2ee12806461a5
7
+ data.tar.gz: 2cc4e04095596764513090fffe356835d3af36c192395dbe7753470801e623f826bf72c54371de30bf1d9006d4cf109809cffc1419e887607914623cc8a1bb82
checksums.yaml.gz.sig CHANGED
Binary file
data/History.rdoc CHANGED
@@ -1,3 +1,25 @@
1
+ === 1.15.0 / 2026-01-02
2
+
3
+ * 1 minor enhancement:
4
+
5
+ * Added process_hash to handle hash shortcut syntax in case it is a call. (TSMMark)
6
+
7
+ * 6 bug fixes:
8
+
9
+ * Bump prism dependency to 1.7+.
10
+ * Load debride with require_relative in bin/debride.
11
+ * Minor fixes for prism 1.7 and ruby 4.0.
12
+ * Oops! Fixed --legacy option definition.
13
+ * Properly handle scope/alias_method with interoplation. (alagos)
14
+ * Removed prism patches now that they've been upstreamed.
15
+
16
+ === 1.14.0 / 2025-12-11
17
+
18
+ * 2 minor enhancements:
19
+
20
+ * Bumped path_expander to 2.0.0.
21
+ * Switched to prism for ruby parsing. Use --legacy for RubyParser.
22
+
1
23
  === 1.13.0 / 2025-06-10
2
24
 
3
25
  * 2 minor enhancements:
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ Hoe::add_include_dirs("../../sexp_processor/dev/lib",
11
11
  "../../path_expander/dev/lib",
12
12
  "lib")
13
13
 
14
- Hoe.plugin :isolate
14
+ Hoe.plugin :isolate_binaries
15
15
  Hoe.plugin :seattlerb
16
16
  Hoe.plugin :rdoc
17
17
  Hoe.plugin :cov
@@ -21,8 +21,8 @@ Hoe.spec "debride" do
21
21
  license "MIT"
22
22
 
23
23
  dependency "sexp_processor", "~> 4.17"
24
- dependency "ruby_parser", "~> 3.20"
25
- dependency "path_expander", "~> 1.0"
24
+ dependency "prism", "~> 1.7"
25
+ dependency "path_expander", "~> 2.0"
26
26
  end
27
27
 
28
28
  def run dir, whitelist
data/bin/debride CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env -S ruby -w
2
2
 
3
- require "debride"
3
+ require_relative "../lib/debride"
4
4
 
5
5
  Debride.run(ARGV).report
data/lib/debride.rb CHANGED
@@ -3,16 +3,22 @@
3
3
  require "optparse"
4
4
  require "set"
5
5
  require "stringio"
6
+ require "timeout"
6
7
 
7
- require "ruby_parser"
8
8
  require "sexp_processor"
9
9
  require "path_expander"
10
10
 
11
+ require "prism"
12
+ require "prism/translation/ruby_parser"
13
+
14
+ class NotRubyParser < Prism::Translation::RubyParser # compatibility layer
15
+ end
16
+
11
17
  ##
12
18
  # A static code analyzer that points out possible dead methods.
13
19
 
14
20
  class Debride < MethodBasedSexpProcessor
15
- VERSION = "1.13.0" # :nodoc:
21
+ VERSION = "1.15.0" # :nodoc:
16
22
  PROJECT = "debride"
17
23
 
18
24
  def self.load_plugins proj = PROJECT
@@ -47,14 +53,12 @@ class Debride < MethodBasedSexpProcessor
47
53
  # Top level runner for bin/debride.
48
54
 
49
55
  def self.run args
50
- opt = parse_options args
51
-
52
- debride = Debride.new opt
56
+ debride = Debride.new parse_options args
53
57
 
54
58
  extensions = self.file_extensions
55
59
  glob = "**/*.{#{extensions.join(",")}}"
56
60
  expander = PathExpander.new(args, glob)
57
- files = expander.process
61
+ files = expander.process.to_a
58
62
  excl = debride.option[:exclude]
59
63
  excl.map! { |fd| File.directory?(fd) ? "#{fd}/**/*" : fd } if excl
60
64
 
@@ -97,8 +101,9 @@ class Debride < MethodBasedSexpProcessor
97
101
  raise "Unhandled type: #{path_or_io.class}:#{path_or_io.inspect}"
98
102
  end
99
103
 
100
- RubyParser.new.process(file, path, option[:timeout])
101
- rescue Racc::ParseError, RegexpError => e
104
+ parser = option[:parser].new
105
+ parser.process file, path, option[:timeout]
106
+ rescue RubyParser::SyntaxError, RegexpError => e
102
107
  warn "Parse Error parsing #{path}. Skipping."
103
108
  warn " #{e.message}"
104
109
  rescue Timeout::Error
@@ -161,6 +166,11 @@ class Debride < MethodBasedSexpProcessor
161
166
  options[:minimum] = n
162
167
  end
163
168
 
169
+ opts.on "--legacy", "Use RubyParser for parsing." do
170
+ require "ruby_parser"
171
+ option[:parser] = RubyParser
172
+ end
173
+
164
174
  opts.on("-v", "--verbose", "Verbose. Show progress processing files.") do
165
175
  options[:verbose] = true
166
176
  end
@@ -208,6 +218,9 @@ class Debride < MethodBasedSexpProcessor
208
218
  self.option = { :whitelist => [] }.merge options
209
219
  self.known = Hash.new { |h,k| h[k] = Set.new }
210
220
  self.called = Set.new
221
+
222
+ option[:parser] ||= NotRubyParser
223
+
211
224
  super()
212
225
  end
213
226
 
@@ -330,27 +343,30 @@ class Debride < MethodBasedSexpProcessor
330
343
  end
331
344
  when *RAILS_MACRO_METHODS
332
345
  # s(:call, nil, :has_one, s(:lit, :has_one_relation), ...)
333
- _, _, _, (_, name), * = sexp
346
+ _, _, _, name, * = sexp
334
347
 
335
348
  # try to detect route scope vs model scope
336
349
  if context.include? :module or context.include? :class then
337
- file, line = sexp.file, sexp.line
338
- record_method name, file, line
350
+ if string_like? name then
351
+ file, line = sexp.file, sexp.line
352
+ record_method name.last, file, line
353
+ end
339
354
  end
340
355
  when /_path$/ then
341
356
  method_name = method_name.to_s.delete_suffix("_path").to_sym if option[:rails]
342
357
  when /^deliver_/ then
343
358
  method_name = method_name.to_s.delete_prefix("deliver_").to_sym if option[:rails]
344
359
  when :alias_method then
360
+ # s(:call, nil, :alias_method, lhs, rhs)
345
361
  _, _, _, lhs, rhs = sexp
346
362
 
347
- if Sexp === lhs and Sexp === rhs then
348
- lhs = lhs.last
349
- rhs = rhs.last
363
+ if string_like? lhs and string_like? rhs then
364
+ lhs = lhs.last.to_sym
365
+ rhs = rhs.last.to_sym
350
366
 
351
367
  record_method lhs, sexp.file, sexp.line
352
368
 
353
- called << rhs
369
+ called << rhs # TODO: why?
354
370
  end
355
371
  end
356
372
 
@@ -361,6 +377,10 @@ class Debride < MethodBasedSexpProcessor
361
377
  sexp
362
378
  end
363
379
 
380
+ def string_like? s
381
+ Sexp === s and %i[lit str].include?(s.sexp_type)
382
+ end
383
+
364
384
  def process_alias exp
365
385
  _, (_, lhs), (_, rhs) = exp
366
386
 
@@ -463,6 +483,21 @@ class Debride < MethodBasedSexpProcessor
463
483
 
464
484
  alias process_safe_call process_call
465
485
 
486
+ def process_hash sexp
487
+ _, *pairs = sexp
488
+
489
+ pairs.each_slice 2 do |k, v|
490
+ if v.nil? && k.sexp_type == :lit then
491
+ called << k.last
492
+ else
493
+ process k
494
+ process v
495
+ end
496
+ end
497
+
498
+ sexp
499
+ end
500
+
466
501
  ##
467
502
  # Calculate the difference between known methods and called methods.
468
503
 
data/test/test_debride.rb CHANGED
@@ -20,6 +20,7 @@ class TestDebride < Minitest::Test
20
20
  :process_const,
21
21
  :process_defn,
22
22
  :process_defs,
23
+ :process_hash,
23
24
  :process_op_asgn2,
24
25
  :process_rb,
25
26
  :process_safe_call,
@@ -46,13 +47,15 @@ class TestDebride < Minitest::Test
46
47
  assert_equal exp_arg, arg
47
48
  end
48
49
 
49
- def assert_process exp, ruby, opts = {}
50
+ def assert_process exp, ruby, called:nil, **opts
50
51
  io = StringIO.new ruby
51
52
 
52
53
  debride = Debride.new opts
53
54
  debride.process debride.process_rb io
54
55
 
55
56
  assert_equal exp, debride.missing
57
+ assert_equal Set[*called], debride.called if called
58
+
56
59
  debride
57
60
  end
58
61
 
@@ -240,6 +243,40 @@ class TestDebride < Minitest::Test
240
243
  assert_process [], s
241
244
  end
242
245
 
246
+ def test_call__alias_method__symbol
247
+ ruby = <<~'RUBY'
248
+ class QuarterPounder
249
+ alias_method :get_x, :x
250
+ end
251
+ RUBY
252
+
253
+ exp = [["QuarterPounder", [:get_x]]]
254
+
255
+ assert_process exp, ruby, called:%i[x alias_method]
256
+ end
257
+
258
+ def test_call__alias_method__string
259
+ ruby = <<~'RUBY'
260
+ class QuarterPounder
261
+ alias_method "get_x", "x"
262
+ end
263
+ RUBY
264
+
265
+ exp = [["QuarterPounder", [:get_x]]]
266
+
267
+ assert_process exp, ruby, called:%i[x alias_method]
268
+ end
269
+
270
+ def test_call__alias_method__interpolated
271
+ ruby = <<~'RUBY'
272
+ class QuarterPounder
273
+ alias_method "get_#{integr}", integr
274
+ end
275
+ RUBY
276
+
277
+ assert_process [], ruby, called:%i[integr alias_method]
278
+ end
279
+
243
280
  def test_alias_method_chain
244
281
  ruby = <<-RUBY.strip
245
282
  class QuarterPounder
@@ -342,13 +379,52 @@ class TestDebride < Minitest::Test
342
379
  end
343
380
 
344
381
  def test_block_pass_empty
382
+ ruby = <<~RUBY
383
+ def g(&)
384
+ f(&) # block forwarding
385
+ end
386
+ g
387
+ RUBY
388
+
389
+ assert_process [], ruby
390
+ end
391
+
392
+ def test_hash__shorthand
345
393
  ruby = <<-RUBY.strip
346
- f(&) # block forwarding
394
+ class Seattle
395
+ def self.status
396
+ { raining: }
397
+ end
398
+
399
+ def self.raining
400
+ true
401
+ end
402
+ end
403
+
404
+ Seattle.status
347
405
  RUBY
348
406
 
349
407
  assert_process [], ruby
350
408
  end
351
409
 
410
+ def test_hash__not_shorthand
411
+ ruby = <<-RUBY.strip
412
+ class Seattle
413
+ def self.status
414
+ { missing: }
415
+ end
416
+
417
+ def self.raining
418
+ true
419
+ end
420
+ end
421
+
422
+ Seattle.status
423
+ RUBY
424
+
425
+ assert_process [["Seattle", [:raining]]], ruby
426
+ end
427
+
352
428
  def test_safe_navigation_operator
353
429
  ruby = <<-RUBY.strip
354
430
  class Seattle
@@ -409,7 +485,7 @@ class TestDebride < Minitest::Test
409
485
  end
410
486
 
411
487
  def test_rails_dsl_macro_definitions
412
- ruby = <<-RUBY.strip
488
+ ruby = <<~'RUBY'
413
489
  class RailsModel
414
490
  has_one :has_one_relation
415
491
  has_one :uncalled_has_one_relation
@@ -421,6 +497,9 @@ class TestDebride < Minitest::Test
421
497
  has_and_belongs_to_many :uncalled_has_and_belongs_to_many_relation
422
498
  scope :scope_method
423
499
  scope :uncalled_scope_method
500
+ scope :"interpolated_#{method}"
501
+ scope "another_#{method}"
502
+ scope "yet_another_#{method}".to_sym
424
503
 
425
504
  def instance_method_caller
426
505
  has_one_relation
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debride
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.0
4
+ version: 1.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Davis
@@ -45,53 +45,53 @@ dependencies:
45
45
  - !ruby/object:Gem::Version
46
46
  version: '4.17'
47
47
  - !ruby/object:Gem::Dependency
48
- name: ruby_parser
48
+ name: prism
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '3.20'
53
+ version: '1.7'
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '3.20'
60
+ version: '1.7'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: path_expander
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '1.0'
67
+ version: '2.0'
68
68
  type: :runtime
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '1.0'
74
+ version: '2.0'
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: rdoc
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - ">="
80
80
  - !ruby/object:Gem::Version
81
- version: '4.0'
81
+ version: '6.0'
82
82
  - - "<"
83
83
  - !ruby/object:Gem::Version
84
- version: '7'
84
+ version: '8'
85
85
  type: :development
86
86
  prerelease: false
87
87
  version_requirements: !ruby/object:Gem::Requirement
88
88
  requirements:
89
89
  - - ">="
90
90
  - !ruby/object:Gem::Version
91
- version: '4.0'
91
+ version: '6.0'
92
92
  - - "<"
93
93
  - !ruby/object:Gem::Version
94
- version: '7'
94
+ version: '8'
95
95
  - !ruby/object:Gem::Dependency
96
96
  name: simplecov
97
97
  requirement: !ruby/object:Gem::Requirement
@@ -112,14 +112,14 @@ dependencies:
112
112
  requirements:
113
113
  - - "~>"
114
114
  - !ruby/object:Gem::Version
115
- version: '4.2'
115
+ version: '4.5'
116
116
  type: :development
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - "~>"
121
121
  - !ruby/object:Gem::Version
122
- version: '4.2'
122
+ version: '4.5'
123
123
  description: Analyze code for potentially uncalled / dead methods, now with auto-removal.
124
124
  email:
125
125
  - ryand-ruby@zenspider.com
@@ -164,7 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
164
  - !ruby/object:Gem::Version
165
165
  version: '0'
166
166
  requirements: []
167
- rubygems_version: 3.6.9
167
+ rubygems_version: 3.7.2
168
168
  specification_version: 4
169
169
  summary: Analyze code for potentially uncalled / dead methods, now with auto-removal.
170
170
  test_files: []
metadata.gz.sig CHANGED
Binary file