@@ -69,6 +69,7 @@ def compile_method(tree, symbol_name, constant, method, visibility = RBI::Public
6969
7070 begin
7171 signature = signature_of! ( method )
72+ signature ||= inferred_attr_writer_signature ( method , constant )
7273 method = signature . method if signature #: UnboundMethod
7374
7475 case @pipeline . method_definition_in_gem ( method . name , constant )
@@ -192,6 +193,60 @@ def method_names_by_visibility(mod)
192193 }
193194 end
194195
196+ #: (UnboundMethod method, Module[top] constant) -> untyped
197+ def inferred_attr_writer_signature ( method , constant )
198+ reader_method = attr_reader_for_writer ( method , constant )
199+ return unless reader_method
200+
201+ reader_signature = signature_of ( reader_method )
202+ return unless reader_signature
203+
204+ build_attr_writer_signature ( method , reader_method , reader_signature )
205+ end
206+
207+ #: (UnboundMethod method, Module[top] constant) -> UnboundMethod?
208+ def attr_reader_for_writer ( method , constant )
209+ method_name = method . name . to_s
210+ return unless method_name . end_with? ( "=" )
211+ return unless method . parameters == [ [ :req ] ]
212+
213+ reader_method = constant . instance_method ( method_name . delete_suffix ( "=" ) . to_sym )
214+ return unless method_owned_by_constant? ( reader_method , constant )
215+
216+ reader_method
217+ rescue NameError
218+ nil
219+ end
220+
221+ #: (UnboundMethod writer_method, UnboundMethod reader_method, untyped reader_signature) -> untyped
222+ def build_attr_writer_signature ( writer_method , reader_method , reader_signature )
223+ return unless reader_signature . arg_types . empty?
224+ return unless reader_signature . kwarg_types . empty?
225+ return if reader_signature . rest_type
226+ return if reader_signature . keyrest_type
227+ return if reader_signature . block_type
228+ return unless same_source_location? ( writer_method , reader_signature . method )
229+
230+ T ::Private ::Methods ::Signature . new (
231+ method : writer_method ,
232+ method_name : writer_method . name ,
233+ raw_arg_types : { reader_method . name => reader_signature . return_type } ,
234+ raw_return_type : T ::Private ::Types ::Void ::Private ::INSTANCE ,
235+ bind : nil ,
236+ mode : reader_signature . mode ,
237+ check_level : reader_signature . check_level ,
238+ on_failure : reader_signature . on_failure ,
239+ override_allow_incompatible : reader_signature . override_allow_incompatible ,
240+ defined_raw : reader_signature . defined_raw ,
241+ )
242+ end
243+
244+ #: (UnboundMethod method, UnboundMethod other_method) -> bool
245+ def same_source_location? ( method , other_method )
246+ source_location = method . source_location
247+ !!source_location && source_location == other_method . source_location
248+ end
249+
195250 #: (Module[top] constant, String method_name) -> bool
196251 def struct_method? ( constant , method_name )
197252 return false unless T ::Props ::ClassMethods === constant
0 commit comments