@@ -10,43 +10,93 @@ class SigHandler < YARD::Handlers::Ruby::Base
1010 handles method_call ( :sig )
1111 namespace_only
1212
13- # These node types attached to sigs represent attr_* declarations
14- ATTR_NODE_TYPES = T . let ( %i[ command fcall ] . freeze , T ::Array [ Symbol ] )
15- private_constant :ATTR_NODE_TYPES
13+ # YARD types that can have docstrings attached to them
14+ Documentable = T . type_alias do
15+ T . any (
16+ YARD ::CodeObjects ::MethodObject , YARD ::Parser ::Ruby ::MethodCallNode , YARD ::Parser ::Ruby ::MethodDefinitionNode
17+ )
18+ end
19+ private_constant :Documentable
1620
1721 # Swap the method definition docstring and the sig docstring.
1822 # Parse relevant parts of the `sig` and include them as well.
1923 sig { void }
2024 def process
2125 method_node = NodeUtils . get_method_node ( NodeUtils . sibling_node ( statement ) )
22- docstring , directives = Directives . extract_directives ( statement . docstring )
23- parse_sig ( method_node , docstring )
24- method_node . docstring = docstring . to_raw
25- Directives . add_directives ( method_node . docstring , directives )
26+ case method_node
27+ when YARD :: Parser :: Ruby :: MethodDefinitionNode then process_def ( method_node )
28+ when YARD :: Parser :: Ruby :: MethodCallNode then process_attr ( method_node )
29+ end
2630 statement . docstring = nil
2731 end
2832
2933 private
3034
31- sig { params ( method_node : YARD ::Parser ::Ruby ::AstNode , docstring : YARD ::Docstring ) . void }
32- def parse_sig ( method_node , docstring )
35+ sig { params ( def_node : YARD ::Parser ::Ruby ::MethodDefinitionNode ) . void }
36+ def process_def ( def_node )
37+ separator = scope == :instance && def_node . type == :def ? '#' : '.'
38+ registered = YARD ::Registry . at ( "#{ namespace } #{ separator } #{ def_node . method_name ( true ) } " )
39+ if registered
40+ parse_node ( registered , registered . docstring )
41+ # Since we're probably in an RBI file, delete the def node, which could otherwise erroneously override the
42+ # visibility setting
43+ NodeUtils . delete_node ( def_node )
44+ else
45+ parse_node ( def_node , statement . docstring )
46+ end
47+ end
48+
49+ sig { params ( attr_node : YARD ::Parser ::Ruby ::MethodCallNode ) . void }
50+ def process_attr ( attr_node )
51+ return if merged_into_attr? ( attr_node )
52+
53+ parse_node ( attr_node , statement . docstring , include_params : false )
54+ end
55+
56+ # An attr* sig can be merged into a previous attr* docstring if it is the only parameter passed to the attr*
57+ # declaration. This is to avoid needing to rewrite the source code to separate merged and unmerged attr*
58+ # declarations.
59+ sig { params ( attr_node : YARD ::Parser ::Ruby ::MethodCallNode ) . returns ( T ::Boolean ) }
60+ def merged_into_attr? ( attr_node )
61+ names = NodeUtils . validated_attribute_names ( attr_node )
62+ return false if names . size != 1
63+
64+ attrs = namespace . attributes [ scope ] [ names [ 0 ] ]
65+ return false if attrs . nil? || attrs . empty?
66+
67+ document_attr_methods ( attrs . values . compact )
68+ attr_node . docstring = nil
69+ true
70+ end
71+
72+ sig { params ( method_objects : T ::Array [ YARD ::CodeObjects ::MethodObject ] ) . void }
73+ def document_attr_methods ( method_objects )
74+ method_objects . each { parse_node ( _1 , _1 . docstring , include_params : false ) }
75+ end
76+
77+ sig { params ( attach_to : Documentable , docstring : T . nilable ( String ) , include_params : T ::Boolean ) . void }
78+ def parse_node ( attach_to , docstring , include_params : true )
79+ existing_docstring = docstring . is_a? ( YARD ::Docstring )
80+ docstring , directives = Directives . extract_directives ( docstring ) unless existing_docstring
81+ parse_sig ( docstring , include_params : include_params )
82+ attach_to . docstring = docstring . to_raw
83+ Directives . add_directives ( attach_to . docstring , directives ) unless existing_docstring
84+ end
85+
86+ sig { params ( docstring : YARD ::Docstring , include_params : T ::Boolean ) . void }
87+ def parse_sig ( docstring , include_params : true )
3388 NodeUtils . bfs_traverse ( statement ) do |node |
3489 case node . source
3590 when 'returns' then parse_return ( node , docstring )
36- when 'params' then parse_params ( method_node , node , docstring )
91+ when 'params' then parse_params ( node , docstring ) if include_params
3792 when 'void' then TagUtils . upsert_tag ( docstring , 'return' , TagUtils ::VOID_RETURN_TYPE )
3893 when 'abstract' then TagUtils . upsert_tag ( docstring , 'abstract' )
3994 end
4095 end
4196 end
4297
43- sig do
44- params ( method_node : YARD ::Parser ::Ruby ::AstNode , node : YARD ::Parser ::Ruby ::AstNode , docstring : YARD ::Docstring )
45- . void
46- end
47- def parse_params ( method_node , node , docstring )
48- return if ATTR_NODE_TYPES . include? ( method_node . type )
49-
98+ sig { params ( node : YARD ::Parser ::Ruby ::AstNode , docstring : YARD ::Docstring ) . void }
99+ def parse_params ( node , docstring )
50100 sibling = NodeUtils . sibling_node ( node )
51101 sibling . dig ( 0 , 0 ) . each do |param |
52102 param_name = param . dig ( 0 , 0 )
0 commit comments