yard-sorbet 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.gitignore +40 -0
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +47 -0
  6. data/.travis.yml +7 -0
  7. data/CHANGELOG.md +10 -0
  8. data/Gemfile +14 -0
  9. data/LICENSE +201 -0
  10. data/README.md +17 -0
  11. data/Rakefile +11 -0
  12. data/lib/yard-sorbet.rb +12 -0
  13. data/lib/yard-sorbet/directives.rb +24 -0
  14. data/lib/yard-sorbet/sig_handler.rb +131 -0
  15. data/lib/yard-sorbet/sig_to_yard.rb +91 -0
  16. data/lib/yard-sorbet/struct_handler.rb +94 -0
  17. data/lib/yard-sorbet/version.rb +6 -0
  18. data/sorbet/config +2 -0
  19. data/sorbet/rbi/gems/ast.rbi +47 -0
  20. data/sorbet/rbi/gems/byebug.rbi +1039 -0
  21. data/sorbet/rbi/gems/codecov.rbi +19 -0
  22. data/sorbet/rbi/gems/coderay.rbi +91 -0
  23. data/sorbet/rbi/gems/docile.rbi +31 -0
  24. data/sorbet/rbi/gems/jaro_winkler.rbi +14 -0
  25. data/sorbet/rbi/gems/method_source.rbi +63 -0
  26. data/sorbet/rbi/gems/parallel.rbi +81 -0
  27. data/sorbet/rbi/gems/parser.rbi +920 -0
  28. data/sorbet/rbi/gems/pry-byebug.rbi +149 -0
  29. data/sorbet/rbi/gems/pry.rbi +1964 -0
  30. data/sorbet/rbi/gems/rainbow.rbi +117 -0
  31. data/sorbet/rbi/gems/rake.rbi +635 -0
  32. data/sorbet/rbi/gems/rspec-core.rbi +1686 -0
  33. data/sorbet/rbi/gems/rspec-expectations.rbi +387 -0
  34. data/sorbet/rbi/gems/rspec-mocks.rbi +819 -0
  35. data/sorbet/rbi/gems/rspec-support.rbi +270 -0
  36. data/sorbet/rbi/gems/rspec.rbi +14 -0
  37. data/sorbet/rbi/gems/rubocop-rspec.rbi +889 -0
  38. data/sorbet/rbi/gems/rubocop.rbi +7139 -0
  39. data/sorbet/rbi/gems/ruby-progressbar.rbi +304 -0
  40. data/sorbet/rbi/gems/simplecov-html.rbi +30 -0
  41. data/sorbet/rbi/gems/simplecov.rbi +225 -0
  42. data/sorbet/rbi/gems/site_ruby.rbi +114 -0
  43. data/sorbet/rbi/gems/unicode-display_width.rbi +16 -0
  44. data/sorbet/rbi/gems/yard.rbi +1181 -0
  45. data/sorbet/rbi/hidden-definitions/errors.txt +3045 -0
  46. data/sorbet/rbi/hidden-definitions/hidden.rbi +4469 -0
  47. data/sorbet/rbi/sorbet-typed/lib/bundler/all/bundler.rbi +8684 -0
  48. data/sorbet/rbi/sorbet-typed/lib/ruby/all/gem.rbi +4222 -0
  49. data/sorbet/rbi/sorbet-typed/lib/ruby/all/open3.rbi +111 -0
  50. data/sorbet/rbi/sorbet-typed/lib/ruby/all/resolv.rbi +543 -0
  51. data/spec/data/sig_handler.rb.txt +196 -0
  52. data/spec/data/struct_handler.rb.txt +25 -0
  53. data/spec/spec_helper.rb +111 -0
  54. data/spec/yard_sorbet/sig_handler_spec.rb +233 -0
  55. data/spec/yard_sorbet/struct_handler_spec.rb +48 -0
  56. data/yard-sorbet.gemspec +27 -0
  57. metadata +159 -6
@@ -0,0 +1,196 @@
1
+ class Signatures
2
+ # comment sig_void
3
+ sig {void}
4
+ def sig_void; end
5
+
6
+ # comment sig_override_void
7
+ sig.override.void
8
+ def sig_override_void; end
9
+
10
+ # comment sig_arguments
11
+ sig {params(a: Integer, b: Integer).void}
12
+ def sig_arguments(a, b); end
13
+
14
+ # comment sig_multiline_arguments
15
+ sig do
16
+ params(
17
+ a: Integer,
18
+ b: Integer,
19
+ ).void
20
+ end
21
+ def sig_multiline_arguments(a, b); end
22
+
23
+ # comment sig_multiline_comments
24
+ # comment sig_multiline_comments
25
+ sig {void}
26
+ def sig_multiline_comments; end
27
+
28
+ # comment sig_class_method
29
+ sig {void}
30
+ def self.sig_class_method; end
31
+
32
+ class << self
33
+ # comment reopening
34
+ sig {void}
35
+ def reopening; end
36
+ end
37
+
38
+ # At end of class
39
+ sig {void}
40
+ end
41
+
42
+ class Subclass < Signatures
43
+ # with subclass
44
+ sig {void}
45
+ def method; end
46
+ end
47
+
48
+ class ClassWithCode
49
+ # bar
50
+ sig {void}
51
+ def bar; end
52
+
53
+ puts 'foobar'
54
+
55
+ # foo
56
+ sig {void}
57
+ def foo; end
58
+ end
59
+
60
+ class Outer
61
+ # outer method
62
+ sig {void}
63
+ def outer; end
64
+
65
+ class Inner
66
+ # inner method
67
+ sig {void}
68
+ def inner; end
69
+ end
70
+
71
+ # outer method 2
72
+ sig {void}
73
+ def outer2; end
74
+ end
75
+
76
+ module Module
77
+ # module function
78
+ sig {void}
79
+ def self.foo; end
80
+
81
+ # module instance method
82
+ sig {void}
83
+ def bar; end
84
+ end
85
+
86
+ class SigReturn
87
+ sig {returns(Integer)}
88
+ def one; 1; end
89
+
90
+ # @deprecated do not use
91
+ sig {returns(Integer)}
92
+ def two; 2; end
93
+
94
+ # @return [Numeric]
95
+ sig {returns(Integer)}
96
+ def three; 3; end
97
+
98
+ # @return the number four
99
+ sig {returns(Integer)}
100
+ def four; 4; end
101
+
102
+ sig {params(int: Integer).returns(Float)}
103
+ def plus_one(int); int + 1.0; end
104
+
105
+ sig {returns(T.any(Numeric, String))}
106
+ def plus(a, b); a + b; end
107
+
108
+ sig {void}
109
+ def void_method; end
110
+ end
111
+
112
+ class SigAbstract
113
+ sig {abstract}
114
+ def one; end
115
+
116
+ # @abstract subclass must implement
117
+ sig {abstract}
118
+ def two; end
119
+
120
+ sig {abstract.returns(Boolean)}
121
+ def with_return; true; end
122
+
123
+ sig {abstract.void}
124
+ def with_void; end
125
+ end
126
+
127
+ class SigParams
128
+ # @param bar the thing
129
+ # @param baz [Object] the other thing
130
+ sig {params(bar: T.any(String, Symbol), baz: T.nilable(String)).void}
131
+ def foo(bar, baz); end
132
+
133
+ sig do
134
+ params(
135
+ blk: T.proc.params(arg0: String).returns(T::Array[Hash])
136
+ )
137
+ .returns(NilClass)
138
+ end
139
+ def blk_method(&blk); nil; end
140
+
141
+ sig do
142
+ override
143
+ .params(block: T.proc.params(
144
+ model: EmailConversation,
145
+ mutator: T.untyped,
146
+ ).void)
147
+ .void
148
+ end
149
+ def impl_blk_method(&block); end
150
+ end
151
+
152
+ class CollectionSigs
153
+ sig {params(arr: T::Array[String]).void}
154
+ def collection(arr); end
155
+
156
+ sig {params(arr: T::Array[T::Array[String]]).void}
157
+ def nested_collection(arr); end
158
+
159
+ sig {params(arr: T::Array[T.any(String, Symbol)]).returns(TrueClass)}
160
+ def mixed_collection(arr); true; end
161
+
162
+ sig {returns(T::Hash[String, Symbol])}
163
+ def hash_method; end
164
+
165
+ sig {returns([String, Integer])}
166
+ def fixed_array; ['', 0]; end
167
+
168
+ # @!visibility protected
169
+ sig {returns({foo: T.nilable(String)})}
170
+ def fixed_hash; {foo: nil}; end
171
+
172
+ sig do
173
+ params(
174
+ tos_acceptance: T.nilable({
175
+ date: Integer,
176
+ ip: String,
177
+ user_agent: T.nilable(String),
178
+ signator: T.nilable(String),
179
+ iovation_blackbox: T.nilable(String),
180
+ })
181
+ )
182
+ .returns(NilClass)
183
+ end
184
+ def fixed_param_hash(tos_acceptance); nil; end
185
+ end
186
+
187
+ class AttrSigs
188
+ sig {returns(String)}
189
+ attr_accessor :my_accessor
190
+
191
+ sig {returns(Integer)}
192
+ attr_reader :my_reader
193
+
194
+ sig {params(my_writer: T.nilable(Symbol)).returns(T.nilable(Symbol))}
195
+ attr_writer :my_writer
196
+ end
@@ -0,0 +1,25 @@
1
+ # Comment on PersonStruct
2
+ class PersonStruct < T:Struct
3
+ # A name
4
+ const :name, String
5
+ # An age
6
+ const :age, Integer
7
+ # An optional
8
+ const :optional, T.nilable(String)
9
+ const :mystery, T.untyped
10
+ end
11
+
12
+ class SpecializedPersonStruct < T::Struct
13
+ const :special, String
14
+
15
+ # This is a special intializer
16
+ def initialize(special:)
17
+ raise ArgumentError("bad human") if special != "special"
18
+ super
19
+ end
20
+ end
21
+
22
+ class DefaultPersonStruct < T::Struct
23
+ # This has a default
24
+ const :defaulted, String, default: 'hello'
25
+ end
@@ -0,0 +1,111 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/yard-sorbet'
5
+
6
+ if ENV['CI'] == 'true'
7
+ require 'simplecov'
8
+ require 'codecov'
9
+ SimpleCov.start
10
+ SimpleCov.formatter = SimpleCov::Formatter::Codecov
11
+ end
12
+
13
+ # This file was generated by the `rspec --init` command. Conventionally, all
14
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
15
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
16
+ # this file to always be loaded, without a need to explicitly require it in any
17
+ # files.
18
+ #
19
+ # Given that it is always loaded, you are encouraged to keep this file as
20
+ # light-weight as possible. Requiring heavyweight dependencies from this file
21
+ # will add to the boot time of your test suite on EVERY test run, even for an
22
+ # individual file that may not need all of that loaded. Instead, consider making
23
+ # a separate helper file that requires the additional dependencies and performs
24
+ # the additional setup, and require it from the spec files that actually need
25
+ # it.
26
+ #
27
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
28
+ RSpec.configure do |config|
29
+ # rspec-expectations config goes here. You can use an alternate
30
+ # assertion/expectation library such as wrong or the stdlib/minitest
31
+ # assertions if you prefer.
32
+ config.expect_with :rspec do |expectations|
33
+ # This option will default to `true` in RSpec 4. It makes the `description`
34
+ # and `failure_message` of custom matchers include text for helper methods
35
+ # defined using `chain`, e.g.:
36
+ # be_bigger_than(2).and_smaller_than(4).description
37
+ # # => "be bigger than 2 and smaller than 4"
38
+ # ...rather than:
39
+ # # => "be bigger than 2"
40
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
41
+ end
42
+
43
+ # rspec-mocks config goes here. You can use an alternate test double
44
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
45
+ config.mock_with :rspec do |mocks|
46
+ # Prevents you from mocking or stubbing a method that does not exist on
47
+ # a real object. This is generally recommended, and will default to
48
+ # `true` in RSpec 4.
49
+ mocks.verify_partial_doubles = true
50
+ end
51
+
52
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
53
+ # have no way to turn it off -- the option exists only for backwards
54
+ # compatibility in RSpec 3). It causes shared context metadata to be
55
+ # inherited by the metadata hash of host groups and examples, rather than
56
+ # triggering implicit auto-inclusion in groups with matching metadata.
57
+ config.shared_context_metadata_behavior = :apply_to_host_groups
58
+
59
+ # The settings below are suggested to provide a good initial experience
60
+ # with RSpec, but feel free to customize to your heart's content.
61
+
62
+ # This allows you to limit a spec run to individual examples or groups
63
+ # you care about by tagging them with `:focus` metadata. When nothing
64
+ # is tagged with `:focus`, all examples get run. RSpec also provides
65
+ # aliases for `it`, `describe`, and `context` that include `:focus`
66
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
67
+ config.filter_run_when_matching :focus
68
+
69
+ # Allows RSpec to persist some state between runs in order to support
70
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
71
+ # you configure your source control system to ignore this file.
72
+ config.example_status_persistence_file_path = 'spec/examples.txt'
73
+
74
+ # Limits the available syntax to the non-monkey patched syntax that is
75
+ # recommended. For more details, see:
76
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
77
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
78
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
79
+ config.disable_monkey_patching!
80
+
81
+ # This setting enables warnings. It's recommended, but in some cases may
82
+ # be too noisy due to issues in dependencies.
83
+ config.warnings = true
84
+
85
+ # Many RSpec users commonly either run the entire suite or an individual
86
+ # file, and it's useful to allow more verbose output when running an
87
+ # individual spec file.
88
+ if config.files_to_run.one?
89
+ # Use the documentation formatter for detailed output,
90
+ # unless a formatter has already been configured
91
+ # (e.g. via a command-line flag).
92
+ config.default_formatter = 'doc'
93
+ end
94
+
95
+ # Print the 10 slowest examples and example groups at the
96
+ # end of the spec run, to help surface which specs are running
97
+ # particularly slow.
98
+ config.profile_examples = 10
99
+
100
+ # Run specs in random order to surface order dependencies. If you find an
101
+ # order dependency and want to debug it, you can fix the order by providing
102
+ # the seed, which is printed after each run.
103
+ # --seed 1234
104
+ config.order = :random
105
+
106
+ # Seed global randomization in this process using the `--seed` CLI option.
107
+ # Setting this allows you to use `--seed` to deterministically reproduce
108
+ # test failures related to randomization by passing the same `--seed` value
109
+ # as the one that triggered the failure.
110
+ Kernel.srand config.seed
111
+ end
@@ -0,0 +1,233 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ require 'yard'
5
+
6
+ RSpec.describe YARDSorbet::SigHandler do
7
+ before do
8
+ YARD::Registry.clear
9
+ path = File.join(
10
+ File.expand_path('../data', __dir__),
11
+ 'sig_handler.rb.txt'
12
+ )
13
+ YARD::Parser::SourceParser.parse(path)
14
+ end
15
+
16
+ describe 'attaching to method' do
17
+ it 'handles signatures without arguments' do
18
+ node = YARD::Registry.at('Signatures#sig_void')
19
+ expect(node.docstring).to eq('comment sig_void')
20
+ end
21
+
22
+ it 'handles chaining' do
23
+ node = YARD::Registry.at('Signatures#sig_override_void')
24
+ expect(node.docstring).to eq('comment sig_override_void')
25
+ end
26
+
27
+ it 'handles arguments' do
28
+ node = YARD::Registry.at('Signatures#sig_arguments')
29
+ expect(node.docstring).to eq('comment sig_arguments')
30
+ end
31
+
32
+ it 'handles multiline arguments' do
33
+ node = YARD::Registry.at('Signatures#sig_multiline_arguments')
34
+ expect(node.docstring).to eq('comment sig_multiline_arguments')
35
+ end
36
+
37
+ it 'handles multiline comments' do
38
+ node = YARD::Registry.at('Signatures#sig_multiline_comments')
39
+ expect(node.docstring).to eq("comment sig_multiline_comments\ncomment sig_multiline_comments")
40
+ end
41
+
42
+ it 'handles class methods' do
43
+ node = YARD::Registry.at('Signatures.sig_class_method')
44
+ expect(node.docstring).to eq('comment sig_class_method')
45
+ end
46
+
47
+ it 'handles subclasses' do
48
+ node = YARD::Registry.at('Subclass#method')
49
+ expect(node.docstring).to eq('with subclass')
50
+ end
51
+
52
+ it 'handles classes executing code' do
53
+ node = YARD::Registry.at('ClassWithCode#foo')
54
+ expect(node.docstring).to eq('foo')
55
+ end
56
+
57
+ it 'handles nested classes' do
58
+ node = YARD::Registry.at('Outer#outer')
59
+ expect(node.docstring).to eq('outer method')
60
+
61
+ node = YARD::Registry.at('Outer#outer2')
62
+ expect(node.docstring).to eq('outer method 2')
63
+
64
+ node = YARD::Registry.at('Outer::Inner#inner')
65
+ expect(node.docstring).to eq('inner method')
66
+ end
67
+
68
+ it 'handles modules' do
69
+ node = YARD::Registry.at('Module.foo')
70
+ expect(node.docstring).to eq('module function')
71
+
72
+ node = YARD::Registry.at('Module#bar')
73
+ expect(node.docstring).to eq('module instance method')
74
+ end
75
+
76
+ it 'handles singleton class syntax' do
77
+ skip('TODO')
78
+ node = YARD::Registry.at('Signatures.reopening')
79
+ expect(node.docstring).to eq('comment reopening')
80
+ end
81
+ end
82
+
83
+ describe 'sig parsing' do
84
+ it 'parses return types' do
85
+ node = YARD::Registry.at('SigReturn#one')
86
+ expect(node.tag(:return).types).to eq(['Integer'])
87
+ end
88
+
89
+ it 'merges tags' do
90
+ node = YARD::Registry.at('SigReturn#two')
91
+ expect(node.tag(:return).types).to eq(['Integer'])
92
+ expect(node.tag(:deprecated).text).to eq('do not use')
93
+ end
94
+
95
+ it 'overrides explicit tag' do
96
+ node = YARD::Registry.at('SigReturn#three')
97
+ expect(node.tag(:return).types).to eq(['Integer'])
98
+ end
99
+
100
+ it 'merges comment' do
101
+ node = YARD::Registry.at('SigReturn#four')
102
+ expect(node.tag(:return).types).to eq(['Integer'])
103
+ expect(node.tag(:return).text).to eq('the number four')
104
+ end
105
+
106
+ it 'with params' do
107
+ node = YARD::Registry.at('SigReturn#plus_one')
108
+ expect(node.tag(:return).types).to eq(['Float'])
109
+ end
110
+
111
+ it 'with T syntax' do
112
+ node = YARD::Registry.at('SigReturn#plus')
113
+ expect(node.tag(:return).types).to eq(%w[Numeric String])
114
+ end
115
+
116
+ it 'with void sig' do
117
+ node = YARD::Registry.at('SigReturn#void_method')
118
+ expect(node.tag(:return).types).to eq(['void'])
119
+ end
120
+
121
+ it 'with abstract sig' do
122
+ node = YARD::Registry.at('SigAbstract#one')
123
+ expect(node.tag(:abstract).text).to eq('')
124
+ end
125
+
126
+ it 'merges abstract tag' do
127
+ node = YARD::Registry.at('SigAbstract#two')
128
+ expect(node.tag(:abstract).text).to eq('subclass must implement')
129
+ end
130
+
131
+ it 'with returns' do
132
+ node = YARD::Registry.at('SigAbstract#with_return')
133
+ expect(node.tag(:abstract).text).to eq('')
134
+ expect(node.tag(:return).types).to eq(['Boolean'])
135
+ end
136
+
137
+ it 'with void' do
138
+ node = YARD::Registry.at('SigAbstract#with_void')
139
+ expect(node.tag(:abstract).text).to eq('')
140
+ expect(node.tag(:return).types).to eq(['void'])
141
+ end
142
+
143
+ it 'params' do
144
+ node = YARD::Registry.at('SigParams#foo')
145
+ bar_tag = node.tags.find { |t| t.name == 'bar' }
146
+ expect(bar_tag.text).to eq('the thing')
147
+ expect(bar_tag.types).to eq(%w[String Symbol])
148
+ baz_tag = node.tags.find { |t| t.name == 'baz' }
149
+ expect(baz_tag.text).to eq('the other thing')
150
+ expect(baz_tag.types).to eq(%w[String nil])
151
+ end
152
+
153
+ it 'block param' do
154
+ node = YARD::Registry.at('SigParams#blk_method')
155
+ blk_tag = node.tags.find { |t| t.name == 'blk' }
156
+ expect(blk_tag.types).to eq(
157
+ ['T.proc.params(arg0: String).returns(T::Array[Hash])']
158
+ )
159
+ expect(node.tag(:return).types).to eq(['nil'])
160
+ end
161
+
162
+ it 'block param with newlines' do
163
+ node = YARD::Registry.at('SigParams#impl_blk_method')
164
+ blk_tag = node.tags.find { |t| t.name == 'block' }
165
+ expect(blk_tag.types).to eq(
166
+ ['T.proc.params( model: EmailConversation, mutator: T.untyped, ).void']
167
+ )
168
+ expect(node.tag(:return).types).to eq(['void'])
169
+ end
170
+
171
+ it 'T::Array' do
172
+ node = YARD::Registry.at('CollectionSigs#collection')
173
+ param_tag = node.tags.find { |t| t.name == 'arr' }
174
+ expect(param_tag.types).to eq(['Array<String>'])
175
+ end
176
+
177
+ it 'nested T::Array' do
178
+ node = YARD::Registry.at('CollectionSigs#nested_collection')
179
+ param_tag = node.tags.find { |t| t.name == 'arr' }
180
+ expect(param_tag.types).to eq(['Array<Array<String>>'])
181
+ end
182
+
183
+ it 'mixed T::Array' do
184
+ node = YARD::Registry.at('CollectionSigs#mixed_collection')
185
+ param_tag = node.tags.find { |t| t.name == 'arr' }
186
+ expect(param_tag.types).to eq(['Array<String, Symbol>'])
187
+ end
188
+
189
+ it 'T::Hash' do
190
+ node = YARD::Registry.at('CollectionSigs#hash_method')
191
+ expect(node.tag(:return).types).to eq(['Hash{String => Symbol}'])
192
+ end
193
+
194
+ it 'fixed Array' do
195
+ node = YARD::Registry.at('CollectionSigs#fixed_array')
196
+ expect(node.tag(:return).types).to eq(['Array(String, Integer)'])
197
+ end
198
+
199
+ it 'fixed Hash' do
200
+ node = YARD::Registry.at('CollectionSigs#fixed_hash')
201
+ expect(node.tag(:return).types).to eq(['Hash'])
202
+ expect(node.visibility).to eq(:protected)
203
+ end
204
+
205
+ it 'fixed param Hash' do
206
+ node = YARD::Registry.at('CollectionSigs#fixed_param_hash')
207
+ param_tag = node.tags.find { |t| t.name == 'tos_acceptance' }
208
+ expect(param_tag.types).to eq(%w[Hash nil])
209
+ end
210
+ end
211
+
212
+ describe 'attributes' do
213
+ it 'handles attr_accessor getter' do
214
+ node = YARD::Registry.at('AttrSigs#my_accessor')
215
+ expect(node.tag(:return).types).to eq(['String'])
216
+ end
217
+
218
+ it 'handles attr_accessor setter' do
219
+ node = YARD::Registry.at('AttrSigs#my_accessor=')
220
+ expect(node.tag(:return).types).to eq(['String'])
221
+ end
222
+
223
+ it 'handles attr_reader' do
224
+ node = YARD::Registry.at('AttrSigs#my_reader')
225
+ expect(node.tag(:return).types).to eq(['Integer'])
226
+ end
227
+
228
+ it 'handles attr_writer' do
229
+ node = YARD::Registry.at('AttrSigs#my_writer=')
230
+ expect(node.tag(:return).types).to eq(%w[Symbol nil])
231
+ end
232
+ end
233
+ end