spoom 1.8.7 → 1.8.8
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/lib/spoom/cli/deadcode.rb +2 -2
- data/lib/spoom/cli/srb/coverage.rb +5 -5
- data/lib/spoom/colors.rb +26 -25
- data/lib/spoom/context/exec.rb +20 -5
- data/lib/spoom/context/git.rb +12 -3
- data/lib/spoom/coverage/d3.rb +36 -12
- data/lib/spoom/coverage/snapshot.rb +147 -21
- data/lib/spoom/deadcode/definition.rb +48 -23
- data/lib/spoom/deadcode/remover.rb +49 -17
- data/lib/spoom/deadcode/send.rb +35 -7
- data/lib/spoom/file_tree.rb +16 -4
- data/lib/spoom/model/builder.rb +1 -1
- data/lib/spoom/model/model.rb +27 -5
- data/lib/spoom/model/reference.rb +28 -8
- data/lib/spoom/sorbet/lsp/base.rb +0 -1
- data/lib/spoom/sorbet/lsp/structures.rb +151 -39
- data/lib/spoom/sorbet/translate/rbs_comments_to_sorbet_sigs/base_translator.rb +3 -23
- data/lib/spoom/sorbet/translate/rbs_comments_to_sorbet_sigs.rb +6 -8
- data/lib/spoom/sorbet/translate/sorbet_sigs_to_rbs_comments.rb +3 -4
- data/lib/spoom/sorbet/translate.rb +3 -2
- data/lib/spoom/version.rb +1 -1
- data/rbi/spoom.rbi +445 -146
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f156e3fd1596713d8fb893ba5e4e0cdc127fc9ee1a21006c751773178e4eb1d3
|
|
4
|
+
data.tar.gz: 84ce5c28958b0fe1b45251336dc283e005738b3c0d280a7f8cb5b643d4b8e8da
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98c8db47ce06755ac7ca5b3426f4f0413d611297240f28d19e767f3c76230c5ed963f54e51b092b6d5361f41b4c7a000423355ef4d4eb445d6ebe334c870c5f5
|
|
7
|
+
data.tar.gz: 4c7a33cef72bc373fe5f7bd9f3230669d301baab006a26f5ce7c0483d627e3dd01e30b5a215282d3c537c77b10b5cc20eff739259bec6bfbb55851b6a654eb39
|
data/lib/spoom/cli/deadcode.rb
CHANGED
|
@@ -106,7 +106,7 @@ module Spoom
|
|
|
106
106
|
index.definitions.each do |name, definitions|
|
|
107
107
|
$stderr.puts " #{blue(name)}"
|
|
108
108
|
definitions.each do |definition|
|
|
109
|
-
$stderr.puts " #{yellow(definition.kind.
|
|
109
|
+
$stderr.puts " #{yellow(definition.kind.to_s)} #{gray(definition.location.to_s)}"
|
|
110
110
|
end
|
|
111
111
|
end
|
|
112
112
|
$stderr.puts
|
|
@@ -116,7 +116,7 @@ module Spoom
|
|
|
116
116
|
$stderr.puts "\nReferences:"
|
|
117
117
|
index.references.values.flatten.sort_by(&:name).each do |references|
|
|
118
118
|
name = references.name
|
|
119
|
-
kind = references.kind.
|
|
119
|
+
kind = references.kind.to_s
|
|
120
120
|
loc = references.location.to_s
|
|
121
121
|
$stderr.puts " #{blue(name)} #{yellow(kind)} #{gray(loc)}"
|
|
122
122
|
end
|
|
@@ -156,11 +156,11 @@ module Spoom
|
|
|
156
156
|
end.filter(&:commit_timestamp).sort_by!(&:commit_timestamp)
|
|
157
157
|
|
|
158
158
|
palette = Spoom::Coverage::D3::ColorPalette.new(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
159
|
+
ignore_color: options[:color_ignore],
|
|
160
|
+
false_color: options[:color_false],
|
|
161
|
+
true_color: options[:color_true],
|
|
162
|
+
strict_color: options[:color_strict],
|
|
163
|
+
strong_color: options[:color_strong],
|
|
164
164
|
)
|
|
165
165
|
|
|
166
166
|
report = Spoom::Coverage.report(context, snapshots, palette: palette)
|
data/lib/spoom/colors.rb
CHANGED
|
@@ -2,34 +2,35 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
module Spoom
|
|
5
|
-
class Color
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
BOLD = new("\e[1m")
|
|
5
|
+
class Color
|
|
6
|
+
#: String
|
|
7
|
+
attr_reader :ansi_code
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
YELLOW = new("\e[33m")
|
|
14
|
-
BLUE = new("\e[34m")
|
|
15
|
-
MAGENTA = new("\e[35m")
|
|
16
|
-
CYAN = new("\e[36m")
|
|
17
|
-
WHITE = new("\e[37m")
|
|
18
|
-
|
|
19
|
-
LIGHT_BLACK = new("\e[90m")
|
|
20
|
-
LIGHT_RED = new("\e[91m")
|
|
21
|
-
LIGHT_GREEN = new("\e[92m")
|
|
22
|
-
LIGHT_YELLOW = new("\e[93m")
|
|
23
|
-
LIGHT_BLUE = new("\e[94m")
|
|
24
|
-
LIGHT_MAGENTA = new("\e[95m")
|
|
25
|
-
LIGHT_CYAN = new("\e[96m")
|
|
26
|
-
LIGHT_WHITE = new("\e[97m")
|
|
9
|
+
#: (String) -> void
|
|
10
|
+
def initialize(ansi_code)
|
|
11
|
+
@ansi_code = ansi_code
|
|
27
12
|
end
|
|
28
13
|
|
|
29
|
-
#:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
14
|
+
CLEAR = new("\e[0m") #: Color
|
|
15
|
+
BOLD = new("\e[1m") #: Color
|
|
16
|
+
|
|
17
|
+
BLACK = new("\e[30m") #: Color
|
|
18
|
+
RED = new("\e[31m") #: Color
|
|
19
|
+
GREEN = new("\e[32m") #: Color
|
|
20
|
+
YELLOW = new("\e[33m") #: Color
|
|
21
|
+
BLUE = new("\e[34m") #: Color
|
|
22
|
+
MAGENTA = new("\e[35m") #: Color
|
|
23
|
+
CYAN = new("\e[36m") #: Color
|
|
24
|
+
WHITE = new("\e[37m") #: Color
|
|
25
|
+
|
|
26
|
+
LIGHT_BLACK = new("\e[90m") #: Color
|
|
27
|
+
LIGHT_RED = new("\e[91m") #: Color
|
|
28
|
+
LIGHT_GREEN = new("\e[92m") #: Color
|
|
29
|
+
LIGHT_YELLOW = new("\e[93m") #: Color
|
|
30
|
+
LIGHT_BLUE = new("\e[94m") #: Color
|
|
31
|
+
LIGHT_MAGENTA = new("\e[95m") #: Color
|
|
32
|
+
LIGHT_CYAN = new("\e[96m") #: Color
|
|
33
|
+
LIGHT_WHITE = new("\e[97m") #: Color
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
module Colorize
|
data/lib/spoom/context/exec.rb
CHANGED
|
@@ -4,11 +4,26 @@
|
|
|
4
4
|
require "shellwords"
|
|
5
5
|
|
|
6
6
|
module Spoom
|
|
7
|
-
class ExecResult
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
class ExecResult
|
|
8
|
+
#: String
|
|
9
|
+
attr_reader :out
|
|
10
|
+
|
|
11
|
+
#: String?
|
|
12
|
+
attr_reader :err
|
|
13
|
+
|
|
14
|
+
#: bool
|
|
15
|
+
attr_reader :status
|
|
16
|
+
|
|
17
|
+
#: Integer
|
|
18
|
+
attr_reader :exit_code
|
|
19
|
+
|
|
20
|
+
#: (out: String, status: bool, exit_code: Integer, ?err: String?) -> void
|
|
21
|
+
def initialize(out:, status:, exit_code:, err: nil)
|
|
22
|
+
@out = out
|
|
23
|
+
@err = err
|
|
24
|
+
@status = status
|
|
25
|
+
@exit_code = exit_code
|
|
26
|
+
end
|
|
12
27
|
|
|
13
28
|
#: -> String
|
|
14
29
|
def to_s
|
data/lib/spoom/context/git.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
module Spoom
|
|
5
5
|
module Git
|
|
6
|
-
class Commit
|
|
6
|
+
class Commit
|
|
7
7
|
class << self
|
|
8
8
|
# Parse a line formatted as `%h %at` into a `Commit`
|
|
9
9
|
#: (String string) -> Commit?
|
|
@@ -16,8 +16,17 @@ module Spoom
|
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
#: String
|
|
20
|
+
attr_reader :sha
|
|
21
|
+
|
|
22
|
+
#: Time
|
|
23
|
+
attr_reader :time
|
|
24
|
+
|
|
25
|
+
#: (sha: String, time: Time) -> void
|
|
26
|
+
def initialize(sha:, time:)
|
|
27
|
+
@sha = sha
|
|
28
|
+
@time = time
|
|
29
|
+
end
|
|
21
30
|
|
|
22
31
|
#: -> Integer
|
|
23
32
|
def timestamp
|
data/lib/spoom/coverage/d3.rb
CHANGED
|
@@ -63,17 +63,17 @@ module Spoom
|
|
|
63
63
|
function strictnessColor(strictness) {
|
|
64
64
|
switch(strictness) {
|
|
65
65
|
case "ignore":
|
|
66
|
-
return "#{palette.
|
|
66
|
+
return "#{palette.ignore_color}";
|
|
67
67
|
case "false":
|
|
68
|
-
return "#{palette.
|
|
68
|
+
return "#{palette.false_color}";
|
|
69
69
|
case "true":
|
|
70
|
-
return "#{palette.
|
|
70
|
+
return "#{palette.true_color}";
|
|
71
71
|
case "strict":
|
|
72
|
-
return "#{palette.
|
|
72
|
+
return "#{palette.strict_color}";
|
|
73
73
|
case "strong":
|
|
74
|
-
return "#{palette.
|
|
74
|
+
return "#{palette.strong_color}";
|
|
75
75
|
}
|
|
76
|
-
return "#{palette.
|
|
76
|
+
return "#{palette.false_color}";
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
function toPercent(value, sum) {
|
|
@@ -98,12 +98,36 @@ module Spoom
|
|
|
98
98
|
end
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
class ColorPalette
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
class ColorPalette
|
|
102
|
+
#: String
|
|
103
|
+
attr_accessor :ignore_color
|
|
104
|
+
|
|
105
|
+
#: String
|
|
106
|
+
attr_accessor :false_color
|
|
107
|
+
|
|
108
|
+
#: String
|
|
109
|
+
attr_accessor :true_color
|
|
110
|
+
|
|
111
|
+
#: String
|
|
112
|
+
attr_accessor :strict_color
|
|
113
|
+
|
|
114
|
+
#: String
|
|
115
|
+
attr_accessor :strong_color
|
|
116
|
+
|
|
117
|
+
#: (
|
|
118
|
+
#| ignore_color: String,
|
|
119
|
+
#| false_color: String,
|
|
120
|
+
#| true_color: String,
|
|
121
|
+
#| strict_color: String,
|
|
122
|
+
#| strong_color: String
|
|
123
|
+
#| ) -> void
|
|
124
|
+
def initialize(ignore_color:, false_color:, true_color:, strict_color:, strong_color:)
|
|
125
|
+
@ignore_color = ignore_color
|
|
126
|
+
@false_color = false_color
|
|
127
|
+
@true_color = true_color
|
|
128
|
+
@strict_color = strict_color
|
|
129
|
+
@strong_color = strong_color
|
|
130
|
+
end
|
|
107
131
|
end
|
|
108
132
|
end
|
|
109
133
|
end
|
|
@@ -3,26 +3,126 @@
|
|
|
3
3
|
|
|
4
4
|
module Spoom
|
|
5
5
|
module Coverage
|
|
6
|
-
class Snapshot
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
6
|
+
class Snapshot
|
|
7
|
+
#: Integer
|
|
8
|
+
attr_accessor :timestamp
|
|
9
|
+
|
|
10
|
+
#: String?
|
|
11
|
+
attr_accessor :version_static
|
|
12
|
+
|
|
13
|
+
#: String?
|
|
14
|
+
attr_accessor :version_runtime
|
|
15
|
+
|
|
16
|
+
#: Integer
|
|
17
|
+
attr_accessor :duration
|
|
18
|
+
|
|
19
|
+
#: String?
|
|
20
|
+
attr_accessor :commit_sha
|
|
21
|
+
|
|
22
|
+
#: Integer?
|
|
23
|
+
attr_accessor :commit_timestamp
|
|
24
|
+
|
|
25
|
+
#: Integer
|
|
26
|
+
attr_accessor :files
|
|
27
|
+
|
|
28
|
+
#: Integer
|
|
29
|
+
attr_accessor :rbi_files
|
|
30
|
+
|
|
31
|
+
#: Integer
|
|
32
|
+
attr_accessor :modules
|
|
33
|
+
|
|
34
|
+
#: Integer
|
|
35
|
+
attr_accessor :classes
|
|
36
|
+
|
|
37
|
+
#: Integer
|
|
38
|
+
attr_accessor :singleton_classes
|
|
39
|
+
|
|
40
|
+
#: Integer
|
|
41
|
+
attr_accessor :methods_without_sig
|
|
42
|
+
|
|
43
|
+
#: Integer
|
|
44
|
+
attr_accessor :methods_with_sig
|
|
45
|
+
|
|
46
|
+
#: Integer
|
|
47
|
+
attr_accessor :calls_untyped
|
|
48
|
+
|
|
49
|
+
#: Integer
|
|
50
|
+
attr_accessor :calls_typed
|
|
51
|
+
|
|
52
|
+
#: Hash[String, Integer]
|
|
53
|
+
attr_accessor :sigils
|
|
54
|
+
|
|
55
|
+
#: Integer
|
|
56
|
+
attr_accessor :methods_with_sig_excluding_rbis
|
|
57
|
+
|
|
58
|
+
#: Integer
|
|
59
|
+
attr_accessor :methods_without_sig_excluding_rbis
|
|
60
|
+
|
|
61
|
+
#: Hash[String, Integer]
|
|
62
|
+
attr_accessor :sigils_excluding_rbis
|
|
63
|
+
|
|
64
|
+
#: (
|
|
65
|
+
#| ?timestamp: Integer,
|
|
66
|
+
#| ?version_static: String?,
|
|
67
|
+
#| ?version_runtime: String?,
|
|
68
|
+
#| ?duration: Integer,
|
|
69
|
+
#| ?commit_sha: String?,
|
|
70
|
+
#| ?commit_timestamp: Integer?,
|
|
71
|
+
#| ?files: Integer,
|
|
72
|
+
#| ?rbi_files: Integer,
|
|
73
|
+
#| ?modules: Integer,
|
|
74
|
+
#| ?classes: Integer,
|
|
75
|
+
#| ?singleton_classes: Integer,
|
|
76
|
+
#| ?methods_without_sig: Integer,
|
|
77
|
+
#| ?methods_with_sig: Integer,
|
|
78
|
+
#| ?calls_untyped: Integer,
|
|
79
|
+
#| ?calls_typed: Integer,
|
|
80
|
+
#| ?sigils: Hash[String, Integer],
|
|
81
|
+
#| ?methods_with_sig_excluding_rbis: Integer,
|
|
82
|
+
#| ?methods_without_sig_excluding_rbis: Integer,
|
|
83
|
+
#| ?sigils_excluding_rbis: Hash[String, Integer],
|
|
84
|
+
#| ) -> void
|
|
85
|
+
def initialize(
|
|
86
|
+
timestamp: Time.new.getutc.to_i,
|
|
87
|
+
version_static: nil,
|
|
88
|
+
version_runtime: nil,
|
|
89
|
+
duration: 0,
|
|
90
|
+
commit_sha: nil,
|
|
91
|
+
commit_timestamp: nil,
|
|
92
|
+
files: 0,
|
|
93
|
+
rbi_files: 0,
|
|
94
|
+
modules: 0,
|
|
95
|
+
classes: 0,
|
|
96
|
+
singleton_classes: 0,
|
|
97
|
+
methods_without_sig: 0,
|
|
98
|
+
methods_with_sig: 0,
|
|
99
|
+
calls_untyped: 0,
|
|
100
|
+
calls_typed: 0,
|
|
101
|
+
sigils: Hash.new(0),
|
|
102
|
+
methods_with_sig_excluding_rbis: 0,
|
|
103
|
+
methods_without_sig_excluding_rbis: 0,
|
|
104
|
+
sigils_excluding_rbis: Hash.new(0)
|
|
105
|
+
)
|
|
106
|
+
@timestamp = timestamp
|
|
107
|
+
@version_static = version_static
|
|
108
|
+
@version_runtime = version_runtime
|
|
109
|
+
@duration = duration
|
|
110
|
+
@commit_sha = commit_sha
|
|
111
|
+
@commit_timestamp = commit_timestamp
|
|
112
|
+
@files = files
|
|
113
|
+
@rbi_files = rbi_files
|
|
114
|
+
@modules = modules
|
|
115
|
+
@classes = classes
|
|
116
|
+
@singleton_classes = singleton_classes
|
|
117
|
+
@methods_without_sig = methods_without_sig
|
|
118
|
+
@methods_with_sig = methods_with_sig
|
|
119
|
+
@calls_untyped = calls_untyped
|
|
120
|
+
@calls_typed = calls_typed
|
|
121
|
+
@sigils = sigils
|
|
122
|
+
@methods_with_sig_excluding_rbis = methods_with_sig_excluding_rbis
|
|
123
|
+
@methods_without_sig_excluding_rbis = methods_without_sig_excluding_rbis
|
|
124
|
+
@sigils_excluding_rbis = sigils_excluding_rbis
|
|
125
|
+
end
|
|
26
126
|
|
|
27
127
|
# The strictness name as found in the Sorbet metrics file
|
|
28
128
|
STRICTNESSES = ["ignore", "false", "true", "strict", "strong", "stdlib"].freeze #: Array[String]
|
|
@@ -35,7 +135,33 @@ module Spoom
|
|
|
35
135
|
|
|
36
136
|
#: (*untyped arg) -> String
|
|
37
137
|
def to_json(*arg)
|
|
38
|
-
|
|
138
|
+
to_h #: untyped
|
|
139
|
+
.to_json(*arg)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
#: -> Hash[String, untyped]
|
|
143
|
+
def to_h
|
|
144
|
+
{
|
|
145
|
+
"timestamp" => timestamp,
|
|
146
|
+
"version_static" => version_static,
|
|
147
|
+
"version_runtime" => version_runtime,
|
|
148
|
+
"duration" => duration,
|
|
149
|
+
"commit_sha" => commit_sha,
|
|
150
|
+
"commit_timestamp" => commit_timestamp,
|
|
151
|
+
"files" => files,
|
|
152
|
+
"rbi_files" => rbi_files,
|
|
153
|
+
"modules" => modules,
|
|
154
|
+
"classes" => classes,
|
|
155
|
+
"singleton_classes" => singleton_classes,
|
|
156
|
+
"methods_with_sig" => methods_with_sig,
|
|
157
|
+
"methods_without_sig" => methods_without_sig,
|
|
158
|
+
"calls_typed" => calls_typed,
|
|
159
|
+
"calls_untyped" => calls_untyped,
|
|
160
|
+
"sigils" => sigils,
|
|
161
|
+
"methods_with_sig_excluding_rbis" => methods_with_sig_excluding_rbis,
|
|
162
|
+
"methods_without_sig_excluding_rbis" => methods_without_sig_excluding_rbis,
|
|
163
|
+
"sigils_excluding_rbis" => sigils_excluding_rbis,
|
|
164
|
+
}
|
|
39
165
|
end
|
|
40
166
|
|
|
41
167
|
class << self
|
|
@@ -4,34 +4,59 @@
|
|
|
4
4
|
module Spoom
|
|
5
5
|
module Deadcode
|
|
6
6
|
# A definition is a class, module, method, constant, etc. being defined in the code
|
|
7
|
-
class Definition
|
|
8
|
-
class Kind
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Class = new("class")
|
|
13
|
-
Constant = new("constant")
|
|
14
|
-
Method = new("method")
|
|
15
|
-
Module = new("module")
|
|
7
|
+
class Definition
|
|
8
|
+
class Kind
|
|
9
|
+
#: (String) -> void
|
|
10
|
+
def initialize(name)
|
|
11
|
+
@name = name
|
|
16
12
|
end
|
|
17
|
-
end
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
# A definition is marked as `DEAD` if it has no reference with the same name
|
|
24
|
-
DEAD = new
|
|
25
|
-
# A definition can be marked as `IGNORED` if it is not relevant for the analysis
|
|
26
|
-
IGNORED = new
|
|
14
|
+
# @override
|
|
15
|
+
#: -> String
|
|
16
|
+
def to_s
|
|
17
|
+
@name
|
|
27
18
|
end
|
|
19
|
+
|
|
20
|
+
AttrReader = new("attr_reader") #: Kind
|
|
21
|
+
AttrWriter = new("attr_writer") #: Kind
|
|
22
|
+
Class = new("class") #: Kind
|
|
23
|
+
Constant = new("constant") #: Kind
|
|
24
|
+
Method = new("method") #: Kind
|
|
25
|
+
Module = new("module") #: Kind
|
|
28
26
|
end
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
class Status
|
|
29
|
+
# A definition is marked as `ALIVE` if it has at least one reference with the same name
|
|
30
|
+
ALIVE = new #: Status
|
|
31
|
+
# A definition is marked as `DEAD` if it has no reference with the same name
|
|
32
|
+
DEAD = new #: Status
|
|
33
|
+
# A definition can be marked as `IGNORED` if it is not relevant for the analysis
|
|
34
|
+
IGNORED = new #: Status
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
#: Kind
|
|
38
|
+
attr_reader :kind
|
|
39
|
+
|
|
40
|
+
#: String
|
|
41
|
+
attr_reader :name
|
|
42
|
+
|
|
43
|
+
#: String
|
|
44
|
+
attr_reader :full_name
|
|
45
|
+
|
|
46
|
+
#: Location
|
|
47
|
+
attr_reader :location
|
|
48
|
+
|
|
49
|
+
#: Status
|
|
50
|
+
attr_reader :status
|
|
51
|
+
|
|
52
|
+
#: (kind: Kind, name: String, full_name: String, location: Location, ?status: Status) -> void
|
|
53
|
+
def initialize(kind:, name:, full_name:, location:, status: Status::DEAD)
|
|
54
|
+
@kind = kind
|
|
55
|
+
@name = name
|
|
56
|
+
@full_name = full_name
|
|
57
|
+
@location = location
|
|
58
|
+
@status = status
|
|
59
|
+
end
|
|
35
60
|
|
|
36
61
|
# Kind
|
|
37
62
|
|
|
@@ -356,21 +356,9 @@ module Spoom
|
|
|
356
356
|
|
|
357
357
|
#: (NodeContext context) -> void
|
|
358
358
|
def delete_node_and_comments_and_sigs(context)
|
|
359
|
-
start_line = context.node.location.start_line
|
|
360
|
-
end_line = context.node.location.end_line
|
|
361
|
-
|
|
362
|
-
# TODO: remove once Prism location are fixed
|
|
363
359
|
node = context.node
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
Prism::ConstantAndWriteNode, Prism::ConstantOrWriteNode,
|
|
367
|
-
Prism::ConstantPathWriteNode, Prism::ConstantPathOperatorWriteNode,
|
|
368
|
-
Prism::ConstantPathAndWriteNode, Prism::ConstantPathOrWriteNode
|
|
369
|
-
value = node.value
|
|
370
|
-
if value.is_a?(Prism::StringNode)
|
|
371
|
-
end_line = value.closing_loc&.start_line || value.location.end_line
|
|
372
|
-
end
|
|
373
|
-
end
|
|
360
|
+
start_line = node.location.start_line
|
|
361
|
+
end_line = node_end_line(node)
|
|
374
362
|
|
|
375
363
|
# Adjust the lines to remove to include sigs attached to the node
|
|
376
364
|
first_node = context.attached_sigs.first || context.node
|
|
@@ -388,13 +376,16 @@ module Spoom
|
|
|
388
376
|
# if there is, we only want to delete lines up to the last comment found
|
|
389
377
|
if before
|
|
390
378
|
to_node = first_comment || node
|
|
391
|
-
comment = @node_context.comments_between_lines(before
|
|
379
|
+
comment = @node_context.comments_between_lines(node_end_line(before), to_node.location.start_line).last
|
|
392
380
|
before = comment if comment
|
|
393
381
|
end
|
|
394
382
|
|
|
395
|
-
|
|
383
|
+
# `before` may have been swapped for a comment just above, so resolve its end line after that
|
|
384
|
+
before_end_line = node_end_line(before) if before
|
|
385
|
+
|
|
386
|
+
if before_end_line && before_end_line < start_line - 1
|
|
396
387
|
# There is a node before and a blank line
|
|
397
|
-
start_line =
|
|
388
|
+
start_line = before_end_line + 1
|
|
398
389
|
elsif before.nil?
|
|
399
390
|
# There is no node before, check if there is a blank line
|
|
400
391
|
parent_context = context.parent_context
|
|
@@ -417,6 +408,45 @@ module Spoom
|
|
|
417
408
|
delete_lines(start_line, end_line)
|
|
418
409
|
end
|
|
419
410
|
|
|
411
|
+
# Prism reports a node ending in a heredoc as ending on the heredoc's opening line rather than
|
|
412
|
+
# its closing terminator (e.g. `def_node_matcher :foo, <<~PATTERN` spanning several lines).
|
|
413
|
+
# Return the last line the node actually occupies so the heredoc body isn't mistaken for blank
|
|
414
|
+
# filler, either when deleting the node itself or when accounting for the node that follows it.
|
|
415
|
+
# TODO: remove once Prism locations are fixed
|
|
416
|
+
#: ((Prism::Node | Prism::Comment) node) -> Integer
|
|
417
|
+
def node_end_line(node)
|
|
418
|
+
end_line = node.location.end_line
|
|
419
|
+
return end_line unless node.is_a?(Prism::Node)
|
|
420
|
+
|
|
421
|
+
stack = [node] #: Array[Prism::Node]
|
|
422
|
+
until stack.empty?
|
|
423
|
+
child = stack.pop
|
|
424
|
+
next unless child
|
|
425
|
+
|
|
426
|
+
heredoc_end = heredoc_terminator_line(child)
|
|
427
|
+
end_line = heredoc_end if heredoc_end && heredoc_end > end_line
|
|
428
|
+
stack.concat(child.compact_child_nodes)
|
|
429
|
+
end
|
|
430
|
+
end_line
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# The last line occupied by `node` if it is a heredoc string (its closing terminator), or `nil`
|
|
434
|
+
# otherwise.
|
|
435
|
+
#: (Prism::Node node) -> Integer?
|
|
436
|
+
def heredoc_terminator_line(node)
|
|
437
|
+
case node
|
|
438
|
+
when Prism::StringNode, Prism::InterpolatedStringNode,
|
|
439
|
+
Prism::XStringNode, Prism::InterpolatedXStringNode
|
|
440
|
+
opening = node.opening_loc
|
|
441
|
+
return unless opening&.slice&.start_with?("<<")
|
|
442
|
+
|
|
443
|
+
# The terminator's location runs to the newline (column 0 of the next line), so use its
|
|
444
|
+
# start line rather than its end line.
|
|
445
|
+
closing = node.closing_loc
|
|
446
|
+
closing ? closing.start_line : node.location.end_line
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
420
450
|
#: (Integer start_line, Integer end_line) -> void
|
|
421
451
|
def delete_lines(start_line, end_line)
|
|
422
452
|
lines = @new_source.lines
|
|
@@ -688,6 +718,8 @@ module Spoom
|
|
|
688
718
|
node.is_a?(Prism::DefNode)
|
|
689
719
|
when Definition::Kind::Module
|
|
690
720
|
node.is_a?(Prism::ModuleNode)
|
|
721
|
+
else
|
|
722
|
+
raise Error, "Unsupported node kind: #{node.class}"
|
|
691
723
|
end
|
|
692
724
|
end
|
|
693
725
|
end
|
data/lib/spoom/deadcode/send.rb
CHANGED
|
@@ -4,13 +4,41 @@
|
|
|
4
4
|
module Spoom
|
|
5
5
|
module Deadcode
|
|
6
6
|
# An abstraction to simplify handling of Prism::CallNode nodes.
|
|
7
|
-
class Send
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
class Send
|
|
8
|
+
#: Prism::CallNode
|
|
9
|
+
attr_reader :node
|
|
10
|
+
|
|
11
|
+
#: String
|
|
12
|
+
attr_reader :name
|
|
13
|
+
|
|
14
|
+
#: Prism::Node?
|
|
15
|
+
attr_reader :recv
|
|
16
|
+
|
|
17
|
+
#: Array[Prism::Node]
|
|
18
|
+
attr_reader :args
|
|
19
|
+
|
|
20
|
+
#: Prism::Node?
|
|
21
|
+
attr_reader :block
|
|
22
|
+
|
|
23
|
+
#: Location
|
|
24
|
+
attr_reader :location
|
|
25
|
+
|
|
26
|
+
#: (
|
|
27
|
+
#| node: Prism::CallNode,
|
|
28
|
+
#| name: String,
|
|
29
|
+
#| location: Location,
|
|
30
|
+
#| ?recv: Prism::Node?,
|
|
31
|
+
#| ?args: Array[Prism::Node],
|
|
32
|
+
#| ?block: Prism::Node?
|
|
33
|
+
#| ) -> void
|
|
34
|
+
def initialize(node:, name:, location:, recv: nil, args: [], block: nil)
|
|
35
|
+
@node = node
|
|
36
|
+
@name = name
|
|
37
|
+
@recv = recv
|
|
38
|
+
@args = args
|
|
39
|
+
@block = block
|
|
40
|
+
@location = location
|
|
41
|
+
end
|
|
14
42
|
|
|
15
43
|
#: [T] (Class[T] arg_type) { (T arg) -> void } -> void
|
|
16
44
|
def each_arg(arg_type, &block)
|