sorbet-eraser 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: efd5822039bf577e0c4a37171d7e91abd9c7bed250c9d09c779f52faa0d84cf6
4
- data.tar.gz: 1a88eab81b0932ab400f5aeaf08e43cb5107fc08c135e232cc444205f76fdcf3
3
+ metadata.gz: 5ddbc396f3f164a3d0e67e9cf7046733ce52f792eb0abc17e9f5f056f415cd5e
4
+ data.tar.gz: 965ef9955b75bba0a2702ea127d5923d7e4b1a55ed2b2e6dca239970adbb0fe1
5
5
  SHA512:
6
- metadata.gz: a64889bcbdf6affe397a394ed3a0841a166ff376e806e050967d4676910670a5f75d774e25c2490fae410c2b5e9263f07c07597ae6c0a0c08ae226343032ed0f
7
- data.tar.gz: 50b223fde3f5901535888d6e06f685ba128c6bda051d516fe37f8b825ce9c328bfddaff54bb19a52aa1694309be11a74732ceb0b582e00179cf939d8eba760ba
6
+ metadata.gz: 5deb006431e8e31ed089fe6e8fba281f3c65487fc6996a853b05bb0d9600ff034232e7319fb44912a69dc89b60b160900b9b503a17991d19da2bf02f5803c5f8
7
+ data.tar.gz: a6a20f8aa21b2a2c9e75a4c7e83db5b28556516296d16f55eedd0113da807cbf0c3a827fbeda7eaf9fb95d2a29a9f0c4ddc9090d638e00469989520d2c492f1d
data/CHANGELOG.md CHANGED
@@ -6,11 +6,25 @@ 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.3.0] - 2023-06-27
10
+
11
+ ### Added
12
+
13
+ - Support for the `default` and `without_accessors` options for `T::Struct`.
14
+
15
+ ## [0.2.0] - 2023-06-26
16
+
17
+ ### Added
18
+
19
+ - Better support for `T::Struct` subclasses.
20
+
9
21
  ## [0.1.1] - 2021-11-17
10
22
 
11
23
  ### Changed
12
24
 
13
25
  - Require MFA for releasing.
14
26
 
15
- [unreleased]: https://github.com/kddnewton/sorbet-eraser/compare/v0.1.1...HEAD
27
+ [unreleased]: https://github.com/kddnewton/sorbet-eraser/compare/v0.3.0...HEAD
28
+ [0.3.0]: https://github.com/kddnewton/sorbet-eraser/compare/v0.2.0...v0.3.0
29
+ [0.2.0]: https://github.com/kddnewton/sorbet-eraser/compare/v0.1.1...v0.2.0
16
30
  [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.2.0)
4
+ sorbet-eraser (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -39,6 +39,8 @@ bundle exec sorbet-eraser '**/*.rb'
39
39
 
40
40
  It accepts any number of filepaths/patterns on the command line and will modify the source files with their erased contents. Alternatively, you can programmatically use this gem through the `Sorbet::Eraser.erase(source)` API, where `source` is a string that represents valid Ruby code. Ruby code without the listed constructs will be returned.
41
41
 
42
+ If you used any runtime structures like `T::Struct` or `T::Enum` you'll need a runtime shim. For that, you can add `require "t"` to your codebase, which ships with this gem, or in your gemfile `gem "sorbet-eraser", require: "t"`.
43
+
42
44
  ### Status
43
45
 
44
46
  Below is a table of the status of each `sorbet-runtime` construct and its current support status.
@@ -59,7 +59,10 @@ module Sorbet
59
59
  end
60
60
 
61
61
  def to_s
62
- @to_s ||= "<#{event} #{body.map(&:to_s).join(" ")}>"
62
+ @repr ||= begin
63
+ children = body.map { |child| child.is_a?(Array) ? child.map(&:to_s) : child }
64
+ "<#{event} #{children.join(" ")}>"
65
+ end
63
66
  end
64
67
  end
65
68
 
@@ -108,25 +111,83 @@ module Sorbet
108
111
  line_counts[lineno - 1][column]
109
112
  end
110
113
 
114
+ def find_loc(args)
115
+ ranges = []
116
+
117
+ args.each do |arg|
118
+ case arg
119
+ when Node
120
+ ranges << arg.range if arg.range
121
+ when Array
122
+ ranges << find_loc(arg)
123
+ end
124
+ end
125
+
126
+ case ranges.length
127
+ when 0
128
+ nil
129
+ when 1
130
+ ranges.first
131
+ else
132
+ ranges.first.begin...ranges.last.end
133
+ end
134
+ end
135
+
111
136
  # Loop through all of the scanner events and define a basic method that
112
137
  # wraps everything into a node class.
113
138
  SCANNER_EVENTS.each do |event|
114
139
  define_method(:"on_#{event}") do |value|
115
- range = loc.then { |start| start..(start + (value&.size || 0)) }
140
+ range = loc.then { |start| start...(start + (value&.size || 0)) }
116
141
  Node.new(:"@#{event}", [value], range)
117
142
  end
118
143
  end
119
144
 
145
+ # Better location information for aref.
146
+ def on_aref(recv, arg)
147
+ rend = arg.range.end + source[arg.range.end..].index("]") + 1
148
+ Node.new(:aref, [recv, arg], recv.range.begin...rend)
149
+ end
150
+
151
+ # Better location information for arg_paren.
152
+ def on_arg_paren(arg)
153
+ rbegin = source[..arg.range.begin].rindex("(")
154
+ rend = arg.range.end + source[arg.range.end..].index(")") + 1
155
+ Node.new(:arg_paren, [arg], rbegin...rend)
156
+ end
157
+
158
+ # Better location information for brace_block.
159
+ def on_brace_block(params, body)
160
+ rbegin = source[...(params || body).range.begin].rindex("{")
161
+ rend = body.range.end + source[body.range.end..].index("}") + 1
162
+ Node.new(:brace_block, [params, body], rbegin...rend)
163
+ end
164
+
165
+ # Better location information for do_block.
166
+ def on_do_block(params, body)
167
+ rbegin = source[...(params || body).range.begin].rindex("do")
168
+ rend = body.range.end + source[body.range.end..].index("end") + 3
169
+ Node.new(:do_block, [params, body], rbegin...rend)
170
+ end
171
+
172
+ # Track the parsing errors for nicer error messages.
173
+ def on_parse_error(error)
174
+ errors << "line #{lineno}: #{error}"
175
+ end
176
+
120
177
  # Loop through the parser events and generate a method for each event. If
121
178
  # it's one of the _new methods, then use arrays like SexpBuilderPP. If
122
179
  # it's an _add method then just append to the array. If it's a normal
123
180
  # method, then create a new node and determine its bounds.
181
+ handled = private_instance_methods(false)
182
+
124
183
  PARSER_EVENT_TABLE.each do |event, arity|
184
+ next if handled.include?(:"on_#{event}")
185
+
125
186
  if event =~ /\A(.+)_new\z/ && event != :assoc_new
126
187
  prefix = $1.to_sym
127
188
 
128
189
  define_method(:"on_#{event}") do
129
- Node.new(prefix, [], loc.then { |start| start..start })
190
+ Node.new(prefix, [], nil)
130
191
  end
131
192
  elsif event =~ /_add\z/
132
193
  define_method(:"on_#{event}") do |node, value|
@@ -134,29 +195,17 @@ module Sorbet
134
195
  if node.body.empty?
135
196
  value.range
136
197
  else
137
- (node.range.begin..value.range.end)
198
+ (node.range.begin...value.range.end)
138
199
  end
139
200
 
140
201
  node.class.new(node.event, node.body + [value], range)
141
202
  end
142
- elsif event == :parse_error
143
- # skip this, as we're going to define it below
144
203
  else
145
204
  define_method(:"on_#{event}") do |*args|
146
- first, *, last = args.grep(Node).map(&:range)
147
-
148
- first ||= loc.then { |start| start..start }
149
- last ||= first
150
-
151
- Node.new(event, args, first.begin..[last.end, loc].max)
205
+ Node.new(event, args, find_loc(args))
152
206
  end
153
207
  end
154
208
  end
155
-
156
- # Track the parsing errors for nicer error messages.
157
- def on_parse_error(error)
158
- errors << "line #{lineno}: #{error}"
159
- end
160
209
  end
161
210
  end
162
211
  end
@@ -101,69 +101,80 @@ module Sorbet
101
101
  end
102
102
 
103
103
  def on_method_add_arg(call, arg_paren)
104
- # T.must(foo)
105
- # T.reveal_type(foo)
106
- # T.unsafe(foo)
107
- if call.match?(/<call <var_ref <@const T>> <@period \.> <@ident (?:must|reveal_type|unsafe)>>/) &&
108
- arg_paren.match?(/<arg_paren <args_add_block <args .+> false>>/)
104
+ if call.match?(/<call <var_ref <@const T>> <@period \.> <@ident (?:must|reveal_type|unsafe)>>/) && arg_paren.match?(/<arg_paren <args_add_block <args .+> false>>/)
105
+ # T.must(foo)
106
+ # T.reveal_type(foo)
107
+ # T.unsafe(foo)
109
108
  patterns << TOneArgMethodCallParensPattern.new(call.range.begin...arg_paren.range.end)
110
- end
111
-
112
- # T.assert_type!(foo, bar)
113
- # T.cast(foo, bar)
114
- # T.let(foo, bar)
115
- if call.match?(/\A<call <var_ref <@const T>> <@period \.> <@ident (?:assert_type!|cast|let)>>\z/) &&
116
- arg_paren.match?(/<arg_paren <args_add_block <args .+> false>>/)
117
- patterns <<
118
- TTwoArgMethodCallParensPattern.new(
119
- call.range.begin...arg_paren.range.end,
120
- comma: arg_paren.body[0].body[0].body[0].range.end - call.range.begin
121
- )
122
- end
123
-
124
- # T.bind(self, foo)
125
- if call.match?(/<call <var_ref <@const T>> <@period \.> <@ident bind>>/) &&
126
- arg_paren.match?(/<arg_paren <args_add_block <args <var_ref <@kw self>> .+> false>>/)
127
- patterns <<
128
- TTwoArgMethodCallParensPattern.new(
129
- call.range.begin...arg_paren.range.end,
130
- comma: arg_paren.body[0].body[0].body[0].range.end - call.range.begin
131
- )
132
- end
133
-
134
- # abstract!
135
- # final!
136
- # interface!
137
- if call.match?(/<fcall <@ident (?:abstract|final|interface)!>>/) &&
138
- arg_paren.match?("<args >")
109
+ elsif call.match?(/\A<call <var_ref <@const T>> <@period \.> <@ident (?:assert_type!|cast|let)>>\z/) && arg_paren.match?(/<arg_paren <args_add_block <args .+> false>>/)
110
+ # T.assert_type!(foo, bar)
111
+ # T.cast(foo, bar)
112
+ # T.let(foo, bar)
113
+ patterns << TTwoArgMethodCallParensPattern.new(
114
+ call.range.begin...arg_paren.range.end,
115
+ comma: arg_paren.body[0].body[0].body[0].range.end - call.range.begin
116
+ )
117
+ elsif call.match?(/<call <var_ref <@const T>> <@period \.> <@ident bind>>/) && arg_paren.match?(/<arg_paren <args_add_block <args <var_ref <@kw self>> .+> false>>/)
118
+ # T.bind(self, foo)
119
+ patterns << TTwoArgMethodCallParensPattern.new(
120
+ call.range.begin...arg_paren.range.end,
121
+ comma: arg_paren.body[0].body[0].body[0].range.end - call.range.begin
122
+ )
123
+ elsif call.match?(/<fcall <@ident (?:abstract|final|interface)!>>/) && arg_paren.match?("<args >")
124
+ # abstract!
125
+ # final!
126
+ # interface!
139
127
  patterns << DeclarationPattern.new(call.range.begin...arg_paren.range.end)
140
- end
141
-
142
- # mixes_in_class_methods(foo)
143
- if call.match?("<fcall <@ident mixes_in_class_methods>>") &&
144
- arg_paren.match?(/<arg_paren <args_add_block <args <.+>>> false>>/)
128
+ elsif call.match?("<fcall <@ident mixes_in_class_methods>>") && arg_paren.match?(/<arg_paren <args_add_block <args <.+>>> false>>/)
129
+ # mixes_in_class_methods(foo)
145
130
  patterns << MixesInClassMethodsPattern.new(call.range.begin...arg_paren.range.end)
146
131
  end
147
132
 
148
133
  super
149
134
  end
150
135
 
151
- # prop :foo, String
152
- # const :foo, String
153
- class PropPattern < Pattern
136
+ # prop :foo, String => prop :foo
137
+ # const :foo, String => const :foo
138
+ class PropWithoutOptionsPattern < Pattern
154
139
  def replace(segment)
155
- segment.gsub(/((?:prop|const)\s+:.+),(\s*)([^\n;,]+)(.*)/m) do
156
- "#{$1} #{$2}#{blank($3)}#{$4}"
140
+ segment.dup.tap do |replacement|
141
+ range = metadata.fetch(:comma)..-1
142
+ replacement[range] = blank(replacement[range])
143
+ end
144
+ end
145
+ end
146
+
147
+ # prop :foo, String, default: "" => prop :foo, default: ""
148
+ # const :foo, String, default: "" => const :foo, default: ""
149
+ class PropWithOptionsPattern < Pattern
150
+ def replace(segment)
151
+ segment.dup.tap do |replacement|
152
+ first_comma = metadata.fetch(:first_comma)
153
+ second_comma = metadata.fetch(:second_comma)
154
+
155
+ range = (first_comma + 1)..second_comma
156
+ replacement[range] = blank(replacement[range])
157
157
  end
158
158
  end
159
159
  end
160
160
 
161
161
  def on_command(ident, args_add_block)
162
162
  if ident.match?(/<@ident (?:const|prop)>/)
163
- # prop :foo, String
164
- # const :foo, String
165
- if args_add_block.match?(/<args_add_block <args <symbol_literal <symbol <@ident .+>>> <.+> false>/)
166
- patterns << PropPattern.new(ident.range.begin...args_add_block.range.end)
163
+ if args_add_block.match?(/<args_add_block <args <symbol_literal <symbol <@ident .+?>>> <.+> <bare_assoc_hash .+> false>/)
164
+ # prop :foo, String, default: ""
165
+ # const :foo, String, default: ""
166
+ patterns << PropWithOptionsPattern.new(
167
+ ident.range.begin..args_add_block.range.end,
168
+ first_comma: args_add_block.body[0].body[0].range.end - ident.range.begin,
169
+ second_comma: args_add_block.body[0].body[1].range.end - ident.range.begin
170
+ )
171
+ elsif args_add_block.match?(/<args_add_block <args <symbol_literal <symbol <@ident .+?>>> <.+> false>/)
172
+ # prop :foo, String
173
+ # const :foo, String
174
+ patterns << PropWithoutOptionsPattern.new(
175
+ ident.range.begin..args_add_block.range.end,
176
+ comma: args_add_block.body[0].body[0].range.end - ident.range.begin
177
+ )
167
178
  end
168
179
  end
169
180
 
@@ -171,15 +182,11 @@ module Sorbet
171
182
  end
172
183
 
173
184
  def on_command_call(var_ref, period, ident, args_add_block)
174
- if var_ref.match?("<var_ref <@const T>>") && period.match?("<@period .>")
185
+ if var_ref.match?("<var_ref <@const T>>") && period.match?("<@period .>") && ident.match?(/<@ident (?:must|reveal_type|unsafe)>/) && args_add_block.match?(/<args_add_block <args <.+>> false>/) && args_add_block.body[0].body.length == 1
175
186
  # T.must foo
176
187
  # T.reveal_type foo
177
188
  # T.unsafe foo
178
- if ident.match?(/<@ident (?:must|reveal_type|unsafe)>/) &&
179
- args_add_block.match?(/<args_add_block <args <.+>> false>/) &&
180
- args_add_block.body[0].body.length == 1
181
- patterns << TMustNoParensPattern.new(var_ref.range.begin...args_add_block.range.end)
182
- end
189
+ patterns << TMustNoParensPattern.new(var_ref.range.begin..args_add_block.range.end)
183
190
  end
184
191
 
185
192
  super
@@ -194,10 +201,22 @@ module Sorbet
194
201
  end
195
202
  end
196
203
 
204
+ # sig do foo end =>
205
+ class SigBlockPattern < Pattern
206
+ def replace(segment)
207
+ segment.gsub(/(sig\s*do.+end)(.*)/m) do
208
+ "#{blank($1)}#{$2}"
209
+ end
210
+ end
211
+ end
212
+
197
213
  def on_stmts_add(node, value)
198
- # sig { foo }
199
214
  if value.match?(/<method_add_block <method_add_arg <fcall <@ident sig>> <args >> <brace_block <stmts .+>>>/)
215
+ # sig { foo }
200
216
  patterns << SigBracesPattern.new(value.range)
217
+ elsif value.match?(/<method_add_block <method_add_arg <fcall <@ident sig>> <args >> <do_block <bodystmt .+>>>/)
218
+ # sig do foo end
219
+ patterns << SigBlockPattern.new(value.range)
201
220
  end
202
221
 
203
222
  super
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sorbet
4
4
  module Eraser
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
data/lib/t/props.rb CHANGED
@@ -20,31 +20,37 @@ module T
20
20
  end
21
21
 
22
22
  def prop(name, rules = {})
23
- create_prop(name)
24
- attr_accessor name
23
+ create_prop(name, rules)
24
+ attr_accessor(name) unless rules[:without_accessors]
25
25
  end
26
26
 
27
27
  def const(name, rules = {})
28
- create_prop(name)
29
- attr_reader name
28
+ create_prop(name, rules)
29
+ attr_reader(name) unless rules[:without_accessors]
30
30
  end
31
31
 
32
32
  private
33
33
 
34
- def create_prop(name)
35
- props << name
36
- props.sort!
34
+ def create_prop(name, rules)
35
+ props << [name, rules]
36
+ props.sort_by!(&:first)
37
37
  end
38
38
  end
39
39
 
40
40
  # Here we're going to check against the props that have been defined on the
41
41
  # class level and set appropriate values.
42
42
  def initialize(hash = {})
43
- if self.class.props == hash.keys.sort
44
- hash.each { |key, value| instance_variable_set("@#{key}", value) }
45
- else
46
- raise ArgumentError, "Expected keys #{self.class.props} but got #{hash.keys.sort}"
43
+ self.class.props.each do |name, rules|
44
+ if hash.key?(name)
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
47
51
  end
52
+
53
+ raise ArgumentError, "unknown keyword: #{hash.keys.first}" unless hash.empty?
48
54
  end
49
55
 
50
56
  # This module is entirely empty because we haven't implemented anything from
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorbet-eraser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Newton
@@ -88,7 +88,7 @@ licenses:
88
88
  - MIT
89
89
  metadata:
90
90
  bug_tracker_uri: https://github.com/kddnewton/sorbet-eraser/issues
91
- changelog_uri: https://github.com/kddnewton/sorbet-eraser/blob/v0.2.0/CHANGELOG.md
91
+ changelog_uri: https://github.com/kddnewton/sorbet-eraser/blob/v0.3.0/CHANGELOG.md
92
92
  source_code_uri: https://github.com/kddnewton/sorbet-eraser
93
93
  rubygems_mfa_required: 'true'
94
94
  post_install_message: