sorbet-eraser 0.3.0 → 0.4.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 +20 -1
- data/Gemfile.lock +3 -2
- data/README.md +81 -30
- data/lib/sorbet/eraser/parser.rb +105 -23
- data/lib/sorbet/eraser/patterns.rb +36 -12
- data/lib/{t → sorbet/eraser/t}/props.rb +1 -7
- data/lib/sorbet/eraser/t.rb +75 -0
- data/lib/sorbet/eraser/version.rb +1 -1
- data/lib/t.rb +1 -45
- metadata +7 -6
- /data/lib/{t → sorbet/eraser/t}/enum.rb +0 -0
- /data/lib/{t → sorbet/eraser/t}/struct.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3859ee512cb01ed5b84df15af3dedaa629c30d122e80a7436821841f87184562
|
4
|
+
data.tar.gz: 8392610b8fb03ea78d2572b684c2ccf9b4806f082043e3b2614550a370eeea13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02ee32fc8bcf3d1951ddd9577a4f46c181a01f196eb6c2af34bb8bb0b68a0ebc3fb28515d0ca1e391d2d1ee305d3012aafd943918df643dd2a3a44c5272a38b2
|
7
|
+
data.tar.gz: fa7b5e80e3fda49d50df015532110f6b4050450c5348f4d9f0ac6f40c8e2b411882a5348b38e305419b74e0bf6e833b887997bb6757e440fb3acc552e286d7e4
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,23 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [0.4.0] - 2023-07-03
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- `require "t"` now requires a file that only requires `"sorbet/eraser/t"`, so they are effectively the same thing. If you're in a situation where you need to load a different `"t"`, then you can manually require `"sorbet/eraser/t"` and it should work.
|
14
|
+
- Replace all `typed:` comments with `typed: ignore`.
|
15
|
+
|
16
|
+
## [0.3.1] - 2023-06-27
|
17
|
+
|
18
|
+
### Added
|
19
|
+
|
20
|
+
- Shims for `T::Configuration`, `T::Private::RuntimeLevels`, and `T::Methods`.
|
21
|
+
|
22
|
+
### Changed
|
23
|
+
|
24
|
+
- Fixed various parsing bugs due to incorrect location.
|
25
|
+
|
9
26
|
## [0.3.0] - 2023-06-27
|
10
27
|
|
11
28
|
### Added
|
@@ -24,7 +41,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
24
41
|
|
25
42
|
- Require MFA for releasing.
|
26
43
|
|
27
|
-
[unreleased]: https://github.com/kddnewton/sorbet-eraser/compare/v0.
|
44
|
+
[unreleased]: https://github.com/kddnewton/sorbet-eraser/compare/v0.4.0...HEAD
|
45
|
+
[0.4.0]: https://github.com/kddnewton/sorbet-eraser/compare/v0.3.1...v0.4.0
|
46
|
+
[0.3.1]: https://github.com/kddnewton/sorbet-eraser/compare/v0.3.0...v0.3.1
|
28
47
|
[0.3.0]: https://github.com/kddnewton/sorbet-eraser/compare/v0.2.0...v0.3.0
|
29
48
|
[0.2.0]: https://github.com/kddnewton/sorbet-eraser/compare/v0.1.1...v0.2.0
|
30
49
|
[0.1.1]: https://github.com/kddnewton/sorbet-eraser/compare/f6a712...v0.1.1
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
sorbet-eraser (0.
|
4
|
+
sorbet-eraser (0.4.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -10,6 +10,7 @@ GEM
|
|
10
10
|
rake (13.0.6)
|
11
11
|
|
12
12
|
PLATFORMS
|
13
|
+
arm64-darwin-21
|
13
14
|
arm64-darwin-22
|
14
15
|
x86_64-darwin-19
|
15
16
|
x86_64-darwin-21
|
@@ -22,4 +23,4 @@ DEPENDENCIES
|
|
22
23
|
sorbet-eraser!
|
23
24
|
|
24
25
|
BUNDLED WITH
|
25
|
-
2.
|
26
|
+
2.4.12
|
data/README.md
CHANGED
@@ -5,9 +5,41 @@
|
|
5
5
|
|
6
6
|
Erase all traces of `sorbet-runtime` code.
|
7
7
|
|
8
|
-
|
8
|
+
[Sorbet](https://sorbet.org/) is a type checker for Ruby. To annotate types in your Ruby code, you use constructs like `sig` and `T.let`. Sorbet then uses a static analysis tool to check that your code is type safe. At runtime, these types are enforced by the `sorbet-runtime` gem that provides implementations of all of these constructs.
|
9
9
|
|
10
|
-
|
10
|
+
Sometimes, you want to use Sorbet for development, but don't want to run `sorbet-runtime` in production. This may be because you have a performance-critical application, or because you're writing a library and you don't want to impose a runtime dependency on your users.
|
11
|
+
|
12
|
+
To handle these use cases, `sorbet-eraser` provides a way to erase all traces of `sorbet-runtime` code from your source code. This means that you can use Sorbet for development, but not have to worry about `sorbet-runtime` in production. For example,
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# typed: true
|
16
|
+
|
17
|
+
class HelloWorld
|
18
|
+
extend T::Sig
|
19
|
+
|
20
|
+
sig { returns(String) }
|
21
|
+
def hello
|
22
|
+
T.let("World!", String)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
will be transformed into
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
|
31
|
+
|
32
|
+
class HelloWorld
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
def hello
|
37
|
+
"World!"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
Notice that the `extend T::Sig` and `sig` constructs have been removed from your source code. Notice also that all line and column information has been preserved 1:1, so that stack traces and tracepoints will still be accurate.
|
11
43
|
|
12
44
|
## Installation
|
13
45
|
|
@@ -27,47 +59,66 @@ Or install it yourself as:
|
|
27
59
|
|
28
60
|
## Usage
|
29
61
|
|
30
|
-
There are two ways to use this gem, depending on your needs.
|
62
|
+
There are two ways to use this gem, depending on your needs. You can erase `sorbet-runtime` code ahead of time or just in time.
|
31
63
|
|
32
|
-
|
64
|
+
### Ahead of time
|
33
65
|
|
34
|
-
|
66
|
+
To erase `sorbet-runtime` code ahead of time, you would either use the CLI provided with this gem or the Ruby API. With the CLI, you would run:
|
35
67
|
|
36
68
|
```bash
|
37
69
|
bundle exec sorbet-eraser '**/*.rb'
|
38
70
|
```
|
39
71
|
|
40
|
-
It accepts any number of filepaths/patterns on the command line and will modify the source files with their erased contents.
|
72
|
+
It accepts any number of filepaths/patterns on the command line and will modify the source files in place with their erased contents. If you would instead prefer to script it yourself using the Ruby API, you would run:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
Sorbet::Eraser.erase(source)
|
76
|
+
```
|
77
|
+
|
78
|
+
where `source` is a string that represents valid Ruby code.
|
79
|
+
|
80
|
+
### Just in time
|
81
|
+
|
82
|
+
If you're looking to avoid a build step like the one described above, you can instead erase your code immediately before it is compiled by the Ruby virtual machine. To do, call:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
require "sorbet/eraser/autoload"
|
86
|
+
```
|
87
|
+
|
88
|
+
as soon as possible when your application is first booting. This will hook into the autoload process to erase all `sorbet-runtime` code before it gets passed to Ruby to parse. Note that the tradeoff here is that it eliminates the need for a build step, but slows down your parse/boot time.
|
89
|
+
|
90
|
+
### Runtime structures
|
41
91
|
|
42
|
-
If you used any runtime structures like `T::Struct` or `T::Enum` you'll need a runtime shim.
|
92
|
+
If you used any runtime structures like `T::Struct` or `T::Enum` you'll need a runtime shim. We provide very basic versions of these in the `sorbet-eraser` gem, and they are required automatically.
|
43
93
|
|
44
94
|
### Status
|
45
95
|
|
46
96
|
Below is a table of the status of each `sorbet-runtime` construct and its current support status.
|
47
97
|
|
48
|
-
| Construct | Status | Replacement
|
49
|
-
| --------------------------------------------------- | ------ |
|
50
|
-
| `
|
51
|
-
| `
|
52
|
-
| `
|
53
|
-
| `
|
54
|
-
| `
|
55
|
-
| `class Foo < T::
|
56
|
-
| `class Foo < T::
|
57
|
-
| `class Foo < T::
|
58
|
-
| `
|
59
|
-
| `include T::Props
|
60
|
-
| `include T::Props::
|
61
|
-
| `
|
62
|
-
| `
|
63
|
-
| `T.
|
64
|
-
| `T.
|
65
|
-
| `T.
|
66
|
-
| `T.
|
67
|
-
| `T.
|
68
|
-
| `T.
|
69
|
-
| `T.
|
70
|
-
| `T.
|
98
|
+
| Construct | Status | Replacement |
|
99
|
+
| --------------------------------------------------- | ------ | ----------------- |
|
100
|
+
| `# typed: foo` | ✅ | `# typed: ignore` |
|
101
|
+
| `extend T::*` | ✅ | Shimmed |
|
102
|
+
| `abstract!`, `final!`, `interface!`, `sealed!` | ✅ | Shimmed |
|
103
|
+
| `mixes_in_class_methods(*)`, `requires_ancestor(*)` | ✅ | Shimmed |
|
104
|
+
| `type_member(*)`, `type_template(*)` | ✅ | Shimmed |
|
105
|
+
| `class Foo < T::Enum` | ✅ | Shimmed |
|
106
|
+
| `class Foo < T::InexactStruct` | 🛠 | Shimmed |
|
107
|
+
| `class Foo < T::Struct` | 🛠 | Shimmed |
|
108
|
+
| `class Foo < T::ImmutableStruct` | 🛠 | Shimmed |
|
109
|
+
| `include T::Props` | 🛠 | Shimmed |
|
110
|
+
| `include T::Props::Serializable` | 🛠 | Shimmed |
|
111
|
+
| `include T::Props::Constructor` | 🛠 | Shimmed |
|
112
|
+
| `sig` | ✅ | Removed |
|
113
|
+
| `T.absurd(foo)` | ✅ | Shimmed |
|
114
|
+
| `T.assert_type!(foo, bar)` | ✅ | `foo` |
|
115
|
+
| `T.bind(self, foo)` | ✅ | `self` |
|
116
|
+
| `T.cast(foo, bar)` | ✅ | `foo` |
|
117
|
+
| `T.let(foo, bar)` | ✅ | `foo` |
|
118
|
+
| `T.must(foo)` | ✅ | `foo` |
|
119
|
+
| `T.reveal_type(foo)` | ✅ | `foo` |
|
120
|
+
| `T.type_alias { foo }` | ✅ | Shimmed |
|
121
|
+
| `T.unsafe(foo)` | ✅ | `foo` |
|
71
122
|
|
72
123
|
In the above table, for `Status`:
|
73
124
|
|
data/lib/sorbet/eraser/parser.rb
CHANGED
@@ -70,7 +70,7 @@ module Sorbet
|
|
70
70
|
class ParsingError < StandardError
|
71
71
|
end
|
72
72
|
|
73
|
-
attr_reader :source, :line_counts, :errors, :patterns
|
73
|
+
attr_reader :source, :line_counts, :errors, :patterns, :heredocs
|
74
74
|
|
75
75
|
def initialize(source)
|
76
76
|
super(source)
|
@@ -86,11 +86,12 @@ module Sorbet
|
|
86
86
|
@line_counts << MultiByteString.new(last_index, line)
|
87
87
|
end
|
88
88
|
|
89
|
-
last_index += line.
|
89
|
+
last_index += line.bytesize
|
90
90
|
end
|
91
91
|
|
92
92
|
@errors = []
|
93
93
|
@patterns = []
|
94
|
+
@heredocs = []
|
94
95
|
end
|
95
96
|
|
96
97
|
def self.erase(source)
|
@@ -133,15 +134,6 @@ module Sorbet
|
|
133
134
|
end
|
134
135
|
end
|
135
136
|
|
136
|
-
# Loop through all of the scanner events and define a basic method that
|
137
|
-
# wraps everything into a node class.
|
138
|
-
SCANNER_EVENTS.each do |event|
|
139
|
-
define_method(:"on_#{event}") do |value|
|
140
|
-
range = loc.then { |start| start...(start + (value&.size || 0)) }
|
141
|
-
Node.new(:"@#{event}", [value], range)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
137
|
# Better location information for aref.
|
146
138
|
def on_aref(recv, arg)
|
147
139
|
rend = arg.range.end + source[arg.range.end..].index("]") + 1
|
@@ -150,23 +142,90 @@ module Sorbet
|
|
150
142
|
|
151
143
|
# Better location information for arg_paren.
|
152
144
|
def on_arg_paren(arg)
|
153
|
-
|
154
|
-
|
155
|
-
|
145
|
+
if arg
|
146
|
+
rbegin = source[..arg.range.begin].rindex("(")
|
147
|
+
rend = arg.range.end + source[arg.range.end..].index(")") + 1
|
148
|
+
Node.new(:arg_paren, [arg], rbegin...rend)
|
149
|
+
else
|
150
|
+
segment = source[..loc]
|
151
|
+
Node.new(:arg_paren, [arg], segment.rindex("(")...(segment.rindex(")") + 1))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
LISTS = { qsymbols: "%i", qwords: "%w", symbols: "%I", words: "%W" }.freeze
|
156
|
+
TERMINATORS = { "[" => "]", "{" => "}", "(" => ")", "<" => ">" }.freeze
|
157
|
+
|
158
|
+
# Better location information for array.
|
159
|
+
def on_array(arg)
|
160
|
+
case arg&.event
|
161
|
+
when nil
|
162
|
+
segment = source[..loc]
|
163
|
+
Node.new(:array, [arg], segment.rindex("[")...(segment.rindex("]") + 1))
|
164
|
+
when :qsymbols, :qwords, :symbols, :words
|
165
|
+
rbegin = source[...arg.range.begin].rindex(LISTS.fetch(arg.event))
|
166
|
+
rend = source[arg.range.end..].index(TERMINATORS.fetch(source[rbegin + 2]) { source[rbegin + 2] }) + arg.range.end + 1
|
167
|
+
Node.new(:array, [arg], rbegin...rend)
|
168
|
+
else
|
169
|
+
Node.new(:array, [arg], arg.range)
|
170
|
+
end
|
156
171
|
end
|
157
172
|
|
158
173
|
# Better location information for brace_block.
|
159
174
|
def on_brace_block(params, body)
|
160
|
-
|
161
|
-
|
162
|
-
|
175
|
+
if params || body.range
|
176
|
+
rbegin = source[...(params || body).range.begin].rindex("{")
|
177
|
+
|
178
|
+
rend = body.range&.end || params.range.end
|
179
|
+
rend = rend + source[rend..].index("}") + 1
|
180
|
+
|
181
|
+
Node.new(:brace_block, [params, body], rbegin...rend)
|
182
|
+
else
|
183
|
+
segment = source[..loc]
|
184
|
+
Node.new(:brace_block, [params, body], segment.rindex("{")...(segment.rindex("}") + 1))
|
185
|
+
end
|
163
186
|
end
|
164
187
|
|
165
188
|
# Better location information for do_block.
|
166
189
|
def on_do_block(params, body)
|
167
|
-
|
168
|
-
|
169
|
-
|
190
|
+
if params || body.range
|
191
|
+
rbegin = source[...(params || body).range.begin].rindex("do")
|
192
|
+
|
193
|
+
rend = body.range&.end || params.range.end
|
194
|
+
rend = rend + source[rend..].index("end") + 3
|
195
|
+
|
196
|
+
Node.new(:do_block, [params, body], rbegin...rend)
|
197
|
+
else
|
198
|
+
segment = source[..loc]
|
199
|
+
Node.new(:do_block, [params, body], segment.rindex("do")...(segment.rindex("end") + 3))
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Better location information for hash.
|
204
|
+
def on_hash(arg)
|
205
|
+
if arg
|
206
|
+
Node.new(:hash, [arg], arg.range)
|
207
|
+
else
|
208
|
+
segment = source[..loc]
|
209
|
+
Node.new(:hash, [arg], segment.rindex("{")...(segment.rindex("}") + 1))
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Track the open heredocs so we can replace the string literal ranges with
|
214
|
+
# the range of their declarations.
|
215
|
+
def on_heredoc_beg(value)
|
216
|
+
range = loc.then { |start| start...(start + value.bytesize) }
|
217
|
+
heredocs << [range, value, nil]
|
218
|
+
|
219
|
+
Node.new(:@heredoc_beg, [value], range)
|
220
|
+
end
|
221
|
+
|
222
|
+
# If a heredoc ends, then the next string literal event will be the
|
223
|
+
# heredoc.
|
224
|
+
def on_heredoc_end(value)
|
225
|
+
range = loc.then { |start| start...(start + value.bytesize) }
|
226
|
+
heredocs.find { |(_, beg_arg, end_arg)| beg_arg.include?(value.strip) && end_arg.nil? }[2] = value
|
227
|
+
|
228
|
+
Node.new(:@heredoc_end, [value], range)
|
170
229
|
end
|
171
230
|
|
172
231
|
# Track the parsing errors for nicer error messages.
|
@@ -174,12 +233,33 @@ module Sorbet
|
|
174
233
|
errors << "line #{lineno}: #{error}"
|
175
234
|
end
|
176
235
|
|
236
|
+
# Better location information for string_literal taking into account
|
237
|
+
# heredocs.
|
238
|
+
def on_string_literal(arg)
|
239
|
+
if heredoc = heredocs.find { |(_, _, end_arg)| end_arg }
|
240
|
+
Node.new(:string_literal, [arg], heredocs.delete(heredoc)[0])
|
241
|
+
else
|
242
|
+
Node.new(:string_literal, [arg], arg.range)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
handled = private_instance_methods(false)
|
247
|
+
|
248
|
+
# Loop through all of the scanner events and define a basic method that
|
249
|
+
# wraps everything into a node class.
|
250
|
+
SCANNER_EVENTS.each do |event|
|
251
|
+
next if handled.include?(:"on_#{event}")
|
252
|
+
|
253
|
+
define_method(:"on_#{event}") do |value|
|
254
|
+
range = loc.then { |start| start...(start + (value&.bytesize || 0)) }
|
255
|
+
Node.new(:"@#{event}", [value], range)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
177
259
|
# Loop through the parser events and generate a method for each event. If
|
178
260
|
# it's one of the _new methods, then use arrays like SexpBuilderPP. If
|
179
261
|
# it's an _add method then just append to the array. If it's a normal
|
180
262
|
# method, then create a new node and determine its bounds.
|
181
|
-
handled = private_instance_methods(false)
|
182
|
-
|
183
263
|
PARSER_EVENT_TABLE.each do |event, arity|
|
184
264
|
next if handled.include?(:"on_#{event}")
|
185
265
|
|
@@ -194,8 +274,10 @@ module Sorbet
|
|
194
274
|
range =
|
195
275
|
if node.body.empty?
|
196
276
|
value.range
|
197
|
-
|
277
|
+
elsif node.range && value.range
|
198
278
|
(node.range.begin...value.range.end)
|
279
|
+
else
|
280
|
+
node.range || value.range
|
199
281
|
end
|
200
282
|
|
201
283
|
node.class.new(node.event, node.body + [value], range)
|
@@ -100,13 +100,37 @@ module Sorbet
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
+
# typed: ignore
|
104
|
+
# typed: false
|
105
|
+
# typed: true
|
106
|
+
# typed: strong
|
107
|
+
class TypedCommentPattern < Pattern
|
108
|
+
def replace(segment)
|
109
|
+
segment.gsub(/(\A#\s*typed:\s*)(?:ignore|false|true|strong)(\s*)\z/) do
|
110
|
+
"#{$1}ignore#{$2}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def on_comment(comment)
|
116
|
+
super.tap do |node|
|
117
|
+
if lineno == 1 && comment.match?(/\A#\s*typed:\s*(?:ignore|false|true|strong)\s*\z/)
|
118
|
+
# typed: ignore
|
119
|
+
# typed: false
|
120
|
+
# typed: true
|
121
|
+
# typed: strong
|
122
|
+
patterns << TypedCommentPattern.new(node.range)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
103
127
|
def on_method_add_arg(call, arg_paren)
|
104
|
-
if call.match?(
|
128
|
+
if call.match?(/\A<call <var_ref <@const T>> <@period \.> <@ident (?:must|reveal_type|unsafe)>>\z/) && arg_paren.match?(/\A<arg_paren <args_add_block <args .+> false>>\z/)
|
105
129
|
# T.must(foo)
|
106
130
|
# T.reveal_type(foo)
|
107
131
|
# T.unsafe(foo)
|
108
132
|
patterns << TOneArgMethodCallParensPattern.new(call.range.begin...arg_paren.range.end)
|
109
|
-
elsif call.match?(/\A<call <var_ref <@const T>> <@period \.> <@ident (?:assert_type!|cast|let)>>\z/) && arg_paren.match?(
|
133
|
+
elsif call.match?(/\A<call <var_ref <@const T>> <@period \.> <@ident (?:assert_type!|cast|let)>>\z/) && arg_paren.match?(/\A<arg_paren <args_add_block <args .+> false>>\z/)
|
110
134
|
# T.assert_type!(foo, bar)
|
111
135
|
# T.cast(foo, bar)
|
112
136
|
# T.let(foo, bar)
|
@@ -114,18 +138,18 @@ module Sorbet
|
|
114
138
|
call.range.begin...arg_paren.range.end,
|
115
139
|
comma: arg_paren.body[0].body[0].body[0].range.end - call.range.begin
|
116
140
|
)
|
117
|
-
elsif call.match?(
|
141
|
+
elsif call.match?(/\A<call <var_ref <@const T>> <@period \.> <@ident bind>>\z/) && arg_paren.match?(/\A<arg_paren <args_add_block <args <var_ref <@kw self>> .+> false>>\z/)
|
118
142
|
# T.bind(self, foo)
|
119
143
|
patterns << TTwoArgMethodCallParensPattern.new(
|
120
144
|
call.range.begin...arg_paren.range.end,
|
121
145
|
comma: arg_paren.body[0].body[0].body[0].range.end - call.range.begin
|
122
146
|
)
|
123
|
-
elsif call.match?(
|
147
|
+
elsif call.match?(/\A<fcall <@ident (?:abstract|final|interface)!>>\z/) && arg_paren.match?("<args >")
|
124
148
|
# abstract!
|
125
149
|
# final!
|
126
150
|
# interface!
|
127
|
-
patterns << DeclarationPattern.new(call.range
|
128
|
-
elsif call.match?("<fcall <@ident mixes_in_class_methods>>") && arg_paren.match?(
|
151
|
+
patterns << DeclarationPattern.new(call.range)
|
152
|
+
elsif call.match?("<fcall <@ident mixes_in_class_methods>>") && arg_paren.match?(/\A<arg_paren <args_add_block <args <.+>>> false>>\z/)
|
129
153
|
# mixes_in_class_methods(foo)
|
130
154
|
patterns << MixesInClassMethodsPattern.new(call.range.begin...arg_paren.range.end)
|
131
155
|
end
|
@@ -159,8 +183,8 @@ module Sorbet
|
|
159
183
|
end
|
160
184
|
|
161
185
|
def on_command(ident, args_add_block)
|
162
|
-
if ident.match?(
|
163
|
-
if args_add_block.match?(
|
186
|
+
if ident.match?(/\A<@ident (?:const|prop)>\z/)
|
187
|
+
if args_add_block.match?(/\A<args_add_block <args <symbol_literal <symbol <@ident .+?>>> <.+> <bare_assoc_hash .+> false>\z/)
|
164
188
|
# prop :foo, String, default: ""
|
165
189
|
# const :foo, String, default: ""
|
166
190
|
patterns << PropWithOptionsPattern.new(
|
@@ -168,7 +192,7 @@ module Sorbet
|
|
168
192
|
first_comma: args_add_block.body[0].body[0].range.end - ident.range.begin,
|
169
193
|
second_comma: args_add_block.body[0].body[1].range.end - ident.range.begin
|
170
194
|
)
|
171
|
-
elsif args_add_block.match?(
|
195
|
+
elsif args_add_block.match?(/\A<args_add_block <args <symbol_literal <symbol <@ident .+?>>> <.+> false>\z/)
|
172
196
|
# prop :foo, String
|
173
197
|
# const :foo, String
|
174
198
|
patterns << PropWithoutOptionsPattern.new(
|
@@ -182,7 +206,7 @@ module Sorbet
|
|
182
206
|
end
|
183
207
|
|
184
208
|
def on_command_call(var_ref, period, ident, args_add_block)
|
185
|
-
if var_ref.match?("<var_ref <@const T>>") && period.match?("<@period .>") && ident.match?(
|
209
|
+
if var_ref.match?("<var_ref <@const T>>") && period.match?("<@period .>") && ident.match?(/\A<@ident (?:must|reveal_type|unsafe)>\z/) && args_add_block.match?(/\A<args_add_block <args <.+>> false>\z/) && args_add_block.body[0].body.length == 1
|
186
210
|
# T.must foo
|
187
211
|
# T.reveal_type foo
|
188
212
|
# T.unsafe foo
|
@@ -211,10 +235,10 @@ module Sorbet
|
|
211
235
|
end
|
212
236
|
|
213
237
|
def on_stmts_add(node, value)
|
214
|
-
if value.match?(
|
238
|
+
if value.match?(/\A<method_add_block <method_add_arg <fcall <@ident sig>> <args >> <brace_block <stmts .+>>>\z/)
|
215
239
|
# sig { foo }
|
216
240
|
patterns << SigBracesPattern.new(value.range)
|
217
|
-
elsif value.match?(
|
241
|
+
elsif value.match?(/\A<method_add_block <method_add_arg <fcall <@ident sig>> <args >> <do_block <bodystmt .+>>>\z/)
|
218
242
|
# sig do foo end
|
219
243
|
patterns << SigBlockPattern.new(value.range)
|
220
244
|
end
|
@@ -41,13 +41,7 @@ module T
|
|
41
41
|
# class level and set appropriate values.
|
42
42
|
def initialize(hash = {})
|
43
43
|
self.class.props.each do |name, rules|
|
44
|
-
|
45
|
-
instance_variable_set("@#{name}", hash.delete(name))
|
46
|
-
elsif rules.key?(:default)
|
47
|
-
instance_variable_set("@#{name}", rules[:default])
|
48
|
-
else
|
49
|
-
raise ArgumentError, "missing keyword: #{name}"
|
50
|
-
end
|
44
|
+
instance_variable_set("@#{name}", hash.key?(name) ? hash.delete(name) : rules[:default])
|
51
45
|
end
|
52
46
|
|
53
47
|
raise ArgumentError, "unknown keyword: #{hash.keys.first}" unless hash.empty?
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sorbet/eraser/t/enum"
|
4
|
+
require "sorbet/eraser/t/props"
|
5
|
+
require "sorbet/eraser/t/struct"
|
6
|
+
|
7
|
+
# For some constructs, it doesn't make as much sense to entirely remove them
|
8
|
+
# since they're actually used to change runtime behavior. For example, T.absurd
|
9
|
+
# will always raise an error. In this case instead of removing the content, we
|
10
|
+
# can just shim it.
|
11
|
+
module T
|
12
|
+
# These methods should really not be being called in a loop or any other kind
|
13
|
+
# of hot path, so here we're just going to shim them.
|
14
|
+
module Helpers
|
15
|
+
def abstract!; end
|
16
|
+
def interface!; end
|
17
|
+
def final!; end
|
18
|
+
def sealed!; end
|
19
|
+
def mixes_in_class_methods(*); end
|
20
|
+
def requires_ancestor(*); end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Similar to the Helpers module, these things should only be called a couple
|
24
|
+
# of times, so shimming them here.
|
25
|
+
module Generic
|
26
|
+
include Helpers
|
27
|
+
def type_member(*, **); end
|
28
|
+
def type_template(*, **); end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Keeping this module as a thing so that if there's any kind of weird
|
32
|
+
# reflection going on like is_a?(T::Sig) it will still work.
|
33
|
+
module Sig
|
34
|
+
end
|
35
|
+
|
36
|
+
# I really don't want to be shimming this, but there are places where people
|
37
|
+
# try to reference these values.
|
38
|
+
module Private
|
39
|
+
module RuntimeLevels
|
40
|
+
def self.default_checked_level; :never; end
|
41
|
+
end
|
42
|
+
|
43
|
+
module Methods
|
44
|
+
module MethodHooks
|
45
|
+
end
|
46
|
+
|
47
|
+
module SingletonMethodHooks
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.signature_for_method(method); method; end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# I also don't want to shim this, but there are places where people will
|
55
|
+
# reference it.
|
56
|
+
module Configuration
|
57
|
+
class << self
|
58
|
+
attr_accessor :inline_type_error_handler,
|
59
|
+
:call_validation_error_handler,
|
60
|
+
:sig_builder_error_handler
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Type aliases don't actually do anything, but they are usually assigned to
|
65
|
+
# constants, so in that case we need to return something.
|
66
|
+
def self.type_alias
|
67
|
+
Object.new
|
68
|
+
end
|
69
|
+
|
70
|
+
# Absurd always raises a TypeError within Sorbet, so mirroring that behavior
|
71
|
+
# here when T.absurd is called.
|
72
|
+
def self.absurd(value)
|
73
|
+
raise TypeError, value
|
74
|
+
end
|
75
|
+
end
|
data/lib/t.rb
CHANGED
@@ -1,47 +1,3 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "t
|
4
|
-
require "t/props"
|
5
|
-
require "t/struct"
|
6
|
-
|
7
|
-
# For some constructs, it doesn't make as much sense to entirely remove them
|
8
|
-
# since they're actually used to change runtime behavior. For example, T.absurd
|
9
|
-
# will always raise an error. In this case instead of removing the content, we
|
10
|
-
# can just shim it.
|
11
|
-
module T
|
12
|
-
# These methods should really not be being called in a loop or any other kind
|
13
|
-
# of hot path, so here we're just going to shim them.
|
14
|
-
module Helpers
|
15
|
-
def abstract!; end
|
16
|
-
def interface!; end
|
17
|
-
def final!; end
|
18
|
-
def sealed!; end
|
19
|
-
def mixes_in_class_methods(*); end
|
20
|
-
def requires_ancestor(*); end
|
21
|
-
end
|
22
|
-
|
23
|
-
# Similar to the Helpers module, these things should only be called a couple
|
24
|
-
# of times, so shimming them here.
|
25
|
-
module Generic
|
26
|
-
include Helpers
|
27
|
-
def type_member(*, **); end
|
28
|
-
def type_template(*, **); end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Keeping this module as a thing so that if there's any kind of weird
|
32
|
-
# reflection going on like is_a?(T::Sig) it will still work.
|
33
|
-
module Sig
|
34
|
-
end
|
35
|
-
|
36
|
-
# Type aliases don't actually do anything, but they are usually assigned to
|
37
|
-
# constants, so in that case we need to return something.
|
38
|
-
def self.type_alias
|
39
|
-
Object.new
|
40
|
-
end
|
41
|
-
|
42
|
-
# Absurd always raises a TypeError within Sorbet, so mirroring that behavior
|
43
|
-
# here when T.absurd is called.
|
44
|
-
def self.absurd(value)
|
45
|
-
raise TypeError, value
|
46
|
-
end
|
47
|
-
end
|
3
|
+
require "sorbet/eraser/t"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sorbet-eraser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Newton
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -77,18 +77,19 @@ files:
|
|
77
77
|
- lib/sorbet/eraser/cli.rb
|
78
78
|
- lib/sorbet/eraser/parser.rb
|
79
79
|
- lib/sorbet/eraser/patterns.rb
|
80
|
+
- lib/sorbet/eraser/t.rb
|
81
|
+
- lib/sorbet/eraser/t/enum.rb
|
82
|
+
- lib/sorbet/eraser/t/props.rb
|
83
|
+
- lib/sorbet/eraser/t/struct.rb
|
80
84
|
- lib/sorbet/eraser/version.rb
|
81
85
|
- lib/t.rb
|
82
|
-
- lib/t/enum.rb
|
83
|
-
- lib/t/props.rb
|
84
|
-
- lib/t/struct.rb
|
85
86
|
- sorbet-eraser.gemspec
|
86
87
|
homepage: https://github.com/kddnewton/sorbet-eraser
|
87
88
|
licenses:
|
88
89
|
- MIT
|
89
90
|
metadata:
|
90
91
|
bug_tracker_uri: https://github.com/kddnewton/sorbet-eraser/issues
|
91
|
-
changelog_uri: https://github.com/kddnewton/sorbet-eraser/blob/v0.
|
92
|
+
changelog_uri: https://github.com/kddnewton/sorbet-eraser/blob/v0.4.0/CHANGELOG.md
|
92
93
|
source_code_uri: https://github.com/kddnewton/sorbet-eraser
|
93
94
|
rubygems_mfa_required: 'true'
|
94
95
|
post_install_message:
|
File without changes
|
File without changes
|