@@ -31,10 +31,21 @@ def initialize(top_level, content, options, stats)
31
31
@track_visibility = :nodoc != @options . visibility
32
32
@encoding = @options . encoding
33
33
34
- @module_nesting = [ top_level ]
34
+ @module_nesting = [ [ top_level , false ] ]
35
35
@container = top_level
36
36
@visibility = :public
37
37
@singleton = false
38
+ @in_proc_block = false
39
+ end
40
+
41
+ # Suppress `extend` and `include` within block
42
+ # because they might be a metaprogramming block
43
+ # example: `Module.new { include M }` `M.module_eval { include N }`
44
+
45
+ def with_in_proc_block
46
+ @in_proc_block = true
47
+ yield
48
+ @in_proc_block = false
38
49
end
39
50
40
51
# Dive into another container
@@ -43,22 +54,24 @@ def with_container(container, singleton: false)
43
54
old_container = @container
44
55
old_visibility = @visibility
45
56
old_singleton = @singleton
57
+ old_in_proc_block = @in_proc_block
46
58
@visibility = :public
47
59
@container = container
48
60
@singleton = singleton
61
+ @in_proc_block = false
49
62
unless singleton
50
- @module_nesting . push container
51
-
52
63
# Need to update module parent chain to emulate Module.nesting.
53
64
# This mechanism is inaccurate and needs to be fixed.
54
65
container . parent = old_container
55
66
end
67
+ @module_nesting . push ( [ container , singleton ] )
56
68
yield container
57
69
ensure
58
70
@container = old_container
59
71
@visibility = old_visibility
60
72
@singleton = old_singleton
61
- @module_nesting . pop unless singleton
73
+ @in_proc_block = old_in_proc_block
74
+ @module_nesting . pop
62
75
end
63
76
64
77
# Records the location of this +container+ in the file for this parser and
@@ -204,6 +217,10 @@ def parse_comment_tomdoc(container, comment, line_no, start_line)
204
217
@stats . add_method meth
205
218
end
206
219
220
+ def has_modifier_nodoc? ( line_no ) # :nodoc:
221
+ @modifier_comments [ line_no ] &.text &.match? ( /\A #\s *:nodoc:/ )
222
+ end
223
+
207
224
def handle_modifier_directive ( code_object , line_no ) # :nodoc:
208
225
comment = @modifier_comments [ line_no ]
209
226
@preprocess . handle ( comment . text , code_object ) if comment
@@ -467,6 +484,7 @@ def add_attributes(names, rw, line_no)
467
484
end
468
485
469
486
def add_includes_extends ( names , rdoc_class , line_no ) # :nodoc:
487
+ return if @in_proc_block
470
488
comment = consecutive_comment ( line_no )
471
489
handle_consecutive_comment_directive ( @container , comment )
472
490
names . each do |name |
@@ -492,7 +510,9 @@ def add_extends(names, line_no) # :nodoc:
492
510
493
511
# Adds a method defined by `def` syntax
494
512
495
- def add_method ( name , receiver_name :, receiver_fallback_type :, visibility :, singleton :, params :, calls_super :, block_params :, tokens :, start_line :, end_line :)
513
+ def add_method ( name , receiver_name :, receiver_fallback_type :, visibility :, singleton :, params :, calls_super :, block_params :, tokens :, start_line :, args_end_line :, end_line :)
514
+ return if @in_proc_block
515
+
496
516
receiver = receiver_name ? find_or_create_module_path ( receiver_name , receiver_fallback_type ) : @container
497
517
meth = RDoc ::AnyMethod . new ( nil , name )
498
518
if ( comment = consecutive_comment ( start_line ) )
@@ -504,20 +524,10 @@ def add_method(name, receiver_name:, receiver_fallback_type:, visibility:, singl
504
524
meth . comment = comment
505
525
end
506
526
handle_modifier_directive ( meth , start_line )
527
+ handle_modifier_directive ( meth , args_end_line )
507
528
handle_modifier_directive ( meth , end_line )
508
529
return unless should_document? ( meth )
509
530
510
-
511
- if meth . name == 'initialize' && !singleton
512
- if meth . dont_rename_initialize
513
- visibility = :protected
514
- else
515
- meth . name = 'new'
516
- singleton = true
517
- visibility = :public
518
- end
519
- end
520
-
521
531
internal_add_method (
522
532
receiver ,
523
533
meth ,
@@ -529,6 +539,18 @@ def add_method(name, receiver_name:, receiver_fallback_type:, visibility:, singl
529
539
block_params : block_params ,
530
540
tokens : tokens
531
541
)
542
+
543
+ # Rename after add_method to register duplicated 'new' and 'initialize'
544
+ # defined in c and ruby just like the old parser did.
545
+ if meth . name == 'initialize' && !singleton
546
+ if meth . dont_rename_initialize
547
+ meth . visibility = :protected
548
+ else
549
+ meth . name = 'new'
550
+ meth . singleton = true
551
+ meth . visibility = :public
552
+ end
553
+ end
532
554
end
533
555
534
556
private def internal_add_method ( container , meth , line_no :, visibility :, singleton :, params :, calls_super :, block_params :, tokens :) # :nodoc:
@@ -565,12 +587,17 @@ def find_or_create_module_path(module_name, create_mode)
565
587
if root_name . empty?
566
588
mod = @top_level
567
589
else
568
- @module_nesting . reverse_each do |nesting |
590
+ @module_nesting . reverse_each do |nesting , singleton |
591
+ next if singleton
569
592
mod = nesting . find_module_named ( root_name )
570
593
break if mod
594
+ # If a constant is found and it is not a module or class, RDoc can't document about it.
595
+ # Return an anonymous module to avoid wrong document creation.
596
+ return RDoc ::NormalModule . new ( nil ) if nesting . find_constant_named ( root_name )
571
597
end
572
- return mod || add_module . call ( @top_level , root_name , create_mode ) unless name
573
- mod ||= add_module . call ( @top_level , root_name , :module )
598
+ last_nesting , = @module_nesting . reverse_each . find { |_ , singleton | !singleton }
599
+ return mod || add_module . call ( last_nesting , root_name , create_mode ) unless name
600
+ mod ||= add_module . call ( last_nesting , root_name , :module )
574
601
end
575
602
path . each do |name |
576
603
mod = mod . find_module_named ( name ) || add_module . call ( mod , name , :module )
@@ -584,7 +611,8 @@ def resolve_constant_path(constant_path)
584
611
owner_name , path = constant_path . split ( '::' , 2 )
585
612
return constant_path if owner_name . empty? # ::Foo, ::Foo::Bar
586
613
mod = nil
587
- @module_nesting . reverse_each do |nesting |
614
+ @module_nesting . reverse_each do |nesting , singleton |
615
+ next if singleton
588
616
mod = nesting . find_module_named ( owner_name )
589
617
break if mod
590
618
end
@@ -598,7 +626,10 @@ def resolve_constant_path(constant_path)
598
626
def find_or_create_constant_owner_name ( constant_path )
599
627
const_path , colon , name = constant_path . rpartition ( '::' )
600
628
if colon . empty? # class Foo
601
- [ @container , name ]
629
+ # Within `class C` or `module C`, owner is C(== current container)
630
+ # Within `class <<C`, owner is C.singleton_class
631
+ # but RDoc don't track constants of a singleton class of module
632
+ [ ( @singleton ? nil : @container ) , name ]
602
633
elsif const_path . empty? # class ::Foo
603
634
[ @top_level , name ]
604
635
else # `class Foo::Bar` or `class ::Foo::Bar`
@@ -612,6 +643,8 @@ def add_constant(constant_name, rhs_name, start_line, end_line)
612
643
comment = consecutive_comment ( start_line )
613
644
handle_consecutive_comment_directive ( @container , comment )
614
645
owner , name = find_or_create_constant_owner_name ( constant_name )
646
+ return unless owner
647
+
615
648
constant = RDoc ::Constant . new ( name , rhs_name , comment )
616
649
constant . store = @store
617
650
constant . line = start_line
@@ -635,26 +668,29 @@ def add_constant(constant_name, rhs_name, start_line, end_line)
635
668
636
669
# Adds module or class
637
670
638
- def add_module_or_class ( module_name , start_line , end_line , is_class : false , superclass_name : nil )
671
+ def add_module_or_class ( module_name , start_line , end_line , is_class : false , superclass_name : nil , superclass_expr : nil )
639
672
comment = consecutive_comment ( start_line )
640
673
handle_consecutive_comment_directive ( @container , comment )
641
674
return unless @container . document_children
642
675
643
676
owner , name = find_or_create_constant_owner_name ( module_name )
677
+ return unless owner
678
+
644
679
if is_class
645
680
# RDoc::NormalClass resolves superclass name despite of the lack of module nesting information.
646
681
# We need to fix it when RDoc::NormalClass resolved to a wrong constant name
647
682
if superclass_name
648
683
superclass_full_path = resolve_constant_path ( superclass_name )
649
684
superclass = @store . find_class_or_module ( superclass_full_path ) if superclass_full_path
650
685
superclass_full_path ||= superclass_name
686
+ superclass_full_path = superclass_full_path . sub ( /^::/ , '' )
651
687
end
652
688
# add_class should be done after resolving superclass
653
- mod = owner . classes_hash [ name ] || owner . add_class ( RDoc ::NormalClass , name , superclass_name || '::Object' )
689
+ mod = owner . classes_hash [ name ] || owner . add_class ( RDoc ::NormalClass , name , superclass_name || superclass_expr || '::Object' )
654
690
if superclass_name
655
691
if superclass
656
692
mod . superclass = superclass
657
- elsif mod . superclass . is_a? ( String ) && mod . superclass != superclass_full_path
693
+ elsif ( mod . superclass . is_a? ( String ) || mod . superclass . name == 'Object' ) && mod . superclass != superclass_full_path
658
694
mod . superclass = superclass_full_path
659
695
end
660
696
end
@@ -678,6 +714,20 @@ def initialize(scanner, top_level, store)
678
714
@store = store
679
715
end
680
716
717
+ def visit_if_node ( node )
718
+ if node . end_keyword
719
+ super
720
+ else
721
+ # Visit with the order in text representation to handle this method comment
722
+ # # comment
723
+ # def f
724
+ # end if call_node
725
+ node . statements . accept ( self )
726
+ node . predicate . accept ( self )
727
+ end
728
+ end
729
+ alias visit_unless_node visit_if_node
730
+
681
731
def visit_call_node ( node )
682
732
@scanner . process_comments_until ( node . location . start_line - 1 )
683
733
if node . receiver . nil?
@@ -715,26 +765,35 @@ def visit_call_node(node)
715
765
when :private_class_method
716
766
_visit_call_public_private_class_method ( node , :private ) { super }
717
767
else
768
+ node . arguments &.accept ( self )
718
769
super
719
770
end
720
771
else
721
772
super
722
773
end
723
774
end
724
775
776
+ def visit_block_node ( node )
777
+ @scanner . with_in_proc_block do
778
+ # include, extend and method definition inside block are not documentable
779
+ super
780
+ end
781
+ end
782
+
725
783
def visit_alias_method_node ( node )
726
784
@scanner . process_comments_until ( node . location . start_line - 1 )
727
785
return unless node . old_name . is_a? ( Prism ::SymbolNode ) && node . new_name . is_a? ( Prism ::SymbolNode )
728
786
@scanner . add_alias_method ( node . old_name . value . to_s , node . new_name . value . to_s , node . location . start_line )
729
787
end
730
788
731
789
def visit_module_node ( node )
790
+ node . constant_path . accept ( self )
732
791
@scanner . process_comments_until ( node . location . start_line - 1 )
733
792
module_name = constant_path_string ( node . constant_path )
734
793
mod = @scanner . add_module_or_class ( module_name , node . location . start_line , node . location . end_line ) if module_name
735
794
if mod
736
795
@scanner . with_container ( mod ) do
737
- super
796
+ node . body &. accept ( self )
738
797
@scanner . process_comments_until ( node . location . end_line )
739
798
end
740
799
else
@@ -743,13 +802,16 @@ def visit_module_node(node)
743
802
end
744
803
745
804
def visit_class_node ( node )
805
+ node . constant_path . accept ( self )
806
+ node . superclass &.accept ( self )
746
807
@scanner . process_comments_until ( node . location . start_line - 1 )
747
808
superclass_name = constant_path_string ( node . superclass ) if node . superclass
809
+ superclass_expr = node . superclass . slice if node . superclass && !superclass_name
748
810
class_name = constant_path_string ( node . constant_path )
749
- klass = @scanner . add_module_or_class ( class_name , node . location . start_line , node . location . end_line , is_class : true , superclass_name : superclass_name ) if class_name
811
+ klass = @scanner . add_module_or_class ( class_name , node . location . start_line , node . location . end_line , is_class : true , superclass_name : superclass_name , superclass_expr : superclass_expr ) if class_name
750
812
if klass
751
813
@scanner . with_container ( klass ) do
752
- super
814
+ node . body &. accept ( self )
753
815
@scanner . process_comments_until ( node . location . end_line )
754
816
end
755
817
else
@@ -760,6 +822,12 @@ def visit_class_node(node)
760
822
def visit_singleton_class_node ( node )
761
823
@scanner . process_comments_until ( node . location . start_line - 1 )
762
824
825
+ if @scanner . has_modifier_nodoc? ( node . location . start_line )
826
+ # Skip visiting inside the singleton class. Also skips creation of node.expression as a module
827
+ @scanner . skip_comments_until ( node . location . end_line )
828
+ return
829
+ end
830
+
763
831
expression = node . expression
764
832
expression = expression . body . body . first if expression . is_a? ( Prism ::ParenthesesNode ) && expression . body &.body &.size == 1
765
833
@@ -774,9 +842,10 @@ def visit_singleton_class_node(node)
774
842
when Prism ::SelfNode
775
843
mod = @scanner . container if @scanner . container != @top_level
776
844
end
845
+ expression . accept ( self )
777
846
if mod
778
847
@scanner . with_container ( mod , singleton : true ) do
779
- super
848
+ node . body &. accept ( self )
780
849
@scanner . process_comments_until ( node . location . end_line )
781
850
end
782
851
else
@@ -786,6 +855,7 @@ def visit_singleton_class_node(node)
786
855
787
856
def visit_def_node ( node )
788
857
start_line = node . location . start_line
858
+ args_end_line = node . parameters &.location &.end_line || start_line
789
859
end_line = node . location . end_line
790
860
@scanner . process_comments_until ( start_line - 1 )
791
861
@@ -836,6 +906,7 @@ def visit_def_node(node)
836
906
calls_super : calls_super ,
837
907
tokens : tokens ,
838
908
start_line : start_line ,
909
+ args_end_line : args_end_line ,
839
910
end_line : end_line
840
911
)
841
912
ensure
@@ -944,7 +1015,7 @@ def _visit_call_public_private_protected(call_node, visibility)
944
1015
@scanner . visibility = visibility
945
1016
else # `public :foo, :bar`, `private def foo; end`
946
1017
yield
947
- names = visibility_method_arguments ( call_node , singleton : @scanner . singleton )
1018
+ names = visibility_method_arguments ( call_node , singleton : false )
948
1019
@scanner . change_method_visibility ( names , visibility ) if names
949
1020
end
950
1021
end
0 commit comments