@@ -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,66 @@ 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 = T . let ( constant . instance_method ( method_name . delete_suffix ( "=" ) . to_sym ) , UnboundMethod )
214+ reader_method = original_method ( reader_method )
215+ return unless same_source_location? ( method , reader_method )
216+ return unless method_owned_by_constant? ( reader_method , constant )
217+
218+ reader_method
219+ rescue NameError
220+ nil
221+ end
222+
223+ #: (UnboundMethod writer_method, UnboundMethod reader_method, untyped reader_signature) -> untyped
224+ def build_attr_writer_signature ( writer_method , reader_method , reader_signature )
225+ return unless reader_signature . arg_types . empty?
226+ return unless reader_signature . kwarg_types . empty?
227+ return if reader_signature . rest_type
228+ return if reader_signature . keyrest_type
229+ return if reader_signature . block_type
230+
231+ T ::Private ::Methods ::Signature . new (
232+ method : writer_method ,
233+ method_name : writer_method . name ,
234+ raw_arg_types : { reader_method . name => reader_signature . return_type } ,
235+ raw_return_type : reader_signature . return_type ,
236+ bind : nil ,
237+ mode : reader_signature . mode ,
238+ check_level : reader_signature . check_level ,
239+ on_failure : reader_signature . on_failure ,
240+ override_allow_incompatible : reader_signature . override_allow_incompatible ,
241+ defined_raw : reader_signature . defined_raw ,
242+ )
243+ end
244+
245+ #: (UnboundMethod method) -> UnboundMethod
246+ def original_method ( method )
247+ T . let ( signature_of ( method ) &.method || method , UnboundMethod )
248+ end
249+
250+ #: (UnboundMethod method, UnboundMethod other_method) -> bool
251+ def same_source_location? ( method , other_method )
252+ source_location = method . source_location
253+ !!source_location && source_location == other_method . source_location
254+ end
255+
195256 #: (Module[top] constant, String method_name) -> bool
196257 def struct_method? ( constant , method_name )
197258 return false unless T ::Props ::ClassMethods === constant
0 commit comments