rdoc 8.0.0 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +1 -3
- data/RI.md +75 -75
- data/exe/rdoc +2 -2
- data/lib/rdoc/code_object/alias.rb +71 -69
- data/lib/rdoc/code_object/any_method.rb +305 -303
- data/lib/rdoc/code_object/attr.rb +150 -148
- data/lib/rdoc/code_object/class_module.rb +798 -792
- data/lib/rdoc/code_object/constant.rb +175 -173
- data/lib/rdoc/code_object/context/section.rb +142 -138
- data/lib/rdoc/code_object/context.rb +926 -958
- data/lib/rdoc/code_object/extend.rb +7 -5
- data/lib/rdoc/code_object/include.rb +7 -5
- data/lib/rdoc/code_object/method_attr.rb +326 -319
- data/lib/rdoc/code_object/mixin.rb +97 -95
- data/lib/rdoc/code_object/normal_class.rb +77 -78
- data/lib/rdoc/code_object/normal_module.rb +61 -59
- data/lib/rdoc/code_object/require.rb +23 -39
- data/lib/rdoc/code_object/single_class.rb +21 -19
- data/lib/rdoc/code_object/top_level.rb +212 -219
- data/lib/rdoc/code_object.rb +305 -303
- data/lib/rdoc/comment.rb +275 -273
- data/lib/rdoc/cross_reference.rb +192 -190
- data/lib/rdoc/encoding.rb +105 -103
- data/lib/rdoc/erb_partial.rb +13 -11
- data/lib/rdoc/erbio.rb +29 -27
- data/lib/rdoc/generator/aliki.rb +161 -153
- data/lib/rdoc/generator/darkfish.rb +645 -635
- data/lib/rdoc/generator/json_index.rb +233 -229
- data/lib/rdoc/generator/markup.rb +164 -146
- data/lib/rdoc/generator/pot/message_extractor.rb +57 -51
- data/lib/rdoc/generator/pot/po.rb +52 -51
- data/lib/rdoc/generator/pot/po_entry.rb +138 -132
- data/lib/rdoc/generator/pot.rb +85 -81
- data/lib/rdoc/generator/ri.rb +23 -19
- data/lib/rdoc/generator/template/aliki/DESIGN.md +6 -4
- data/lib/rdoc/generator/template/aliki/_footer.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_head.rhtml +10 -10
- data/lib/rdoc/generator/template/aliki/_header.rhtml +29 -44
- data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +4 -4
- data/lib/rdoc/generator/template/aliki/css/rdoc.css +207 -178
- data/lib/rdoc/generator/template/aliki/js/aliki.js +60 -84
- data/lib/rdoc/generator/template/darkfish/_footer.rhtml +1 -1
- data/lib/rdoc/generator.rb +48 -46
- data/lib/rdoc/i18n/locale.rb +99 -95
- data/lib/rdoc/i18n/text.rb +109 -105
- data/lib/rdoc/i18n.rb +7 -5
- data/lib/rdoc/markdown/byte_runtime.rb +80 -0
- data/lib/rdoc/markdown.kpeg +15 -11
- data/lib/rdoc/markdown.rb +40 -47
- data/lib/rdoc/markup/block_quote.rb +12 -8
- data/lib/rdoc/markup/document.rb +127 -123
- data/lib/rdoc/markup/formatter.rb +219 -215
- data/lib/rdoc/markup/include.rb +33 -29
- data/lib/rdoc/markup/indented_paragraph.rb +37 -33
- data/lib/rdoc/markup/inline_parser.rb +281 -277
- data/lib/rdoc/markup/list.rb +80 -88
- data/lib/rdoc/markup/list_item.rb +73 -85
- data/lib/rdoc/markup/paragraph.rb +23 -19
- data/lib/rdoc/markup/parser.rb +501 -497
- data/lib/rdoc/markup/pre_process.rb +283 -279
- data/lib/rdoc/markup/raw.rb +2 -2
- data/lib/rdoc/markup/rule.rb +16 -12
- data/lib/rdoc/markup/to_ansi.rb +143 -139
- data/lib/rdoc/markup/to_bs.rb +72 -68
- data/lib/rdoc/markup/to_html.rb +594 -565
- data/lib/rdoc/markup/to_html_crossref.rb +234 -230
- data/lib/rdoc/markup/to_html_snippet.rb +232 -227
- data/lib/rdoc/markup/to_joined_paragraph.rb +36 -32
- data/lib/rdoc/markup/to_label.rb +63 -59
- data/lib/rdoc/markup/to_markdown.rb +212 -208
- data/lib/rdoc/markup/to_rdoc.rb +336 -332
- data/lib/rdoc/markup/to_table_of_contents.rb +66 -62
- data/lib/rdoc/markup/to_test.rb +60 -56
- data/lib/rdoc/markup/to_tt_only.rb +84 -80
- data/lib/rdoc/markup/verbatim.rb +62 -58
- data/lib/rdoc/markup.rb +198 -196
- data/lib/rdoc/options.rb +1063 -1061
- data/lib/rdoc/parser/c.rb +1039 -1037
- data/lib/rdoc/parser/changelog.rb +319 -315
- data/lib/rdoc/parser/markdown.rb +17 -13
- data/lib/rdoc/parser/rbs.rb +239 -235
- data/lib/rdoc/parser/rd.rb +17 -13
- data/lib/rdoc/parser/ruby.rb +1245 -1124
- data/lib/rdoc/parser/ruby_colorizer.rb +263 -213
- data/lib/rdoc/parser/simple.rb +31 -27
- data/lib/rdoc/parser/text.rb +12 -8
- data/lib/rdoc/parser.rb +228 -220
- data/lib/rdoc/rbs_helper.rb +1 -1
- data/lib/rdoc/rd/inline.rb +57 -53
- data/lib/rdoc/rd.rb +90 -88
- data/lib/rdoc/rdoc.rb +500 -491
- data/lib/rdoc/ri/driver.rb +1140 -1135
- data/lib/rdoc/ri/formatter.rb +7 -3
- data/lib/rdoc/ri/paths.rb +140 -136
- data/lib/rdoc/ri/servlet.rb +354 -350
- data/lib/rdoc/ri/store.rb +4 -2
- data/lib/rdoc/ri/task.rb +55 -51
- data/lib/rdoc/ri.rb +14 -12
- data/lib/rdoc/rubygems_hook.rb +183 -181
- data/lib/rdoc/server.rb +349 -347
- data/lib/rdoc/stats/normal.rb +46 -42
- data/lib/rdoc/stats/quiet.rb +39 -35
- data/lib/rdoc/stats/verbose.rb +35 -31
- data/lib/rdoc/stats.rb +365 -363
- data/lib/rdoc/store.rb +888 -902
- data/lib/rdoc/task.rb +260 -256
- data/lib/rdoc/text.rb +135 -133
- data/lib/rdoc/token_stream.rb +101 -93
- data/lib/rdoc/tom_doc.rb +203 -201
- data/lib/rdoc/version.rb +1 -1
- metadata +4 -5
- data/lib/rdoc/markdown/literals.kpeg +0 -21
- data/lib/rdoc/markdown/literals.rb +0 -454
data/lib/rdoc/parser/ruby.rb
CHANGED
|
@@ -3,1263 +3,1384 @@
|
|
|
3
3
|
require 'prism'
|
|
4
4
|
require_relative '../rbs_helper'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# *
|
|
16
|
-
# *
|
|
17
|
-
# *
|
|
18
|
-
# *
|
|
19
|
-
# *
|
|
20
|
-
# *
|
|
21
|
-
# *
|
|
22
|
-
# *
|
|
23
|
-
# *
|
|
24
|
-
# *
|
|
25
|
-
# *
|
|
26
|
-
# *
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
# #
|
|
48
|
-
# #
|
|
49
|
-
# #
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
@size = 0
|
|
143
|
-
@token_listeners = nil
|
|
144
|
-
content = RDoc::Encoding.remove_magic_comment content
|
|
145
|
-
@content = content
|
|
146
|
-
@markup = @options.markup
|
|
147
|
-
@track_visibility = :nodoc != @options.visibility
|
|
148
|
-
@encoding = @options.encoding
|
|
149
|
-
|
|
150
|
-
@module_nesting = [[top_level, false]]
|
|
151
|
-
@container = top_level
|
|
152
|
-
@visibility = :public
|
|
153
|
-
@singleton = false
|
|
154
|
-
@in_proc_block = false
|
|
155
|
-
end
|
|
6
|
+
module RDoc
|
|
7
|
+
class Parser
|
|
8
|
+
# Parse and collect document from Ruby source code.
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# Extracts code elements from a source file returning a TopLevel object
|
|
12
|
+
# containing the constituent file elements.
|
|
13
|
+
#
|
|
14
|
+
# RubyParser understands how to document:
|
|
15
|
+
# * classes
|
|
16
|
+
# * modules
|
|
17
|
+
# * methods
|
|
18
|
+
# * constants
|
|
19
|
+
# * aliases
|
|
20
|
+
# * private, public, protected
|
|
21
|
+
# * private_class_method, public_class_method
|
|
22
|
+
# * private_constant, public_constant
|
|
23
|
+
# * module_function
|
|
24
|
+
# * attr, attr_reader, attr_writer, attr_accessor
|
|
25
|
+
# * extra accessors given on the command line
|
|
26
|
+
# * metaprogrammed methods
|
|
27
|
+
# * require
|
|
28
|
+
# * include
|
|
29
|
+
#
|
|
30
|
+
# == Method Arguments
|
|
31
|
+
#
|
|
32
|
+
# The parser extracts the arguments from the method definition. You can
|
|
33
|
+
# override this with a custom argument definition using the :args: directive:
|
|
34
|
+
#
|
|
35
|
+
# ##
|
|
36
|
+
# # This method tries over and over until it is tired
|
|
37
|
+
#
|
|
38
|
+
# def go_go_go(thing_to_try, tries = 10) # :args: thing_to_try
|
|
39
|
+
# puts thing_to_try
|
|
40
|
+
# go_go_go thing_to_try, tries - 1
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# If you have a more-complex set of overrides you can use the :call-seq:
|
|
44
|
+
# directive:
|
|
45
|
+
#
|
|
46
|
+
# ##
|
|
47
|
+
# # This method can be called with a range or an offset and length
|
|
48
|
+
# #
|
|
49
|
+
# # :call-seq:
|
|
50
|
+
# # my_method(Range)
|
|
51
|
+
# # my_method(offset, length)
|
|
52
|
+
#
|
|
53
|
+
# def my_method(*args)
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# The parser extracts +yield+ expressions from method bodies to gather the
|
|
57
|
+
# yielded argument names. If your method manually calls a block instead of
|
|
58
|
+
# yielding or you want to override the discovered argument names use
|
|
59
|
+
# the :yields: directive:
|
|
60
|
+
#
|
|
61
|
+
# ##
|
|
62
|
+
# # My method is awesome
|
|
63
|
+
#
|
|
64
|
+
# def my_method(&block) # :yields: happy, times
|
|
65
|
+
# block.call 1, 2
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
# == Metaprogrammed Methods
|
|
69
|
+
#
|
|
70
|
+
# To pick up a metaprogrammed method, the parser looks for a comment starting
|
|
71
|
+
# with '##' before a metaprogramming method call:
|
|
72
|
+
#
|
|
73
|
+
# ##
|
|
74
|
+
# # This is a meta-programmed method!
|
|
75
|
+
#
|
|
76
|
+
# add_my_method :meta_method, :arg1, :arg2
|
|
77
|
+
#
|
|
78
|
+
# The parser looks at the first argument to determine the name, in
|
|
79
|
+
# this example, :meta_method. If a name cannot be found, a warning is printed
|
|
80
|
+
# and 'unknown' is used.
|
|
81
|
+
#
|
|
82
|
+
# You can force the name of a method using the :method: directive:
|
|
83
|
+
#
|
|
84
|
+
# ##
|
|
85
|
+
# # :method: some_method!
|
|
86
|
+
#
|
|
87
|
+
# By default, meta-methods are instance methods. To indicate that a method is
|
|
88
|
+
# a singleton method instead use the :singleton-method: directive:
|
|
89
|
+
#
|
|
90
|
+
# ##
|
|
91
|
+
# # :singleton-method:
|
|
92
|
+
#
|
|
93
|
+
# You can also use the :singleton-method: directive with a name:
|
|
94
|
+
#
|
|
95
|
+
# ##
|
|
96
|
+
# # :singleton-method: some_method!
|
|
97
|
+
#
|
|
98
|
+
# You can define arguments for metaprogrammed methods via either the
|
|
99
|
+
# \:call-seq:, :arg: or :args: directives.
|
|
100
|
+
#
|
|
101
|
+
# Additionally you can mark a method as an attribute by
|
|
102
|
+
# using :attr:, :attr_reader:, :attr_writer: or :attr_accessor:. Just like
|
|
103
|
+
# for :method:, the name is optional.
|
|
104
|
+
#
|
|
105
|
+
# ##
|
|
106
|
+
# # :attr_reader: my_attr_name
|
|
107
|
+
#
|
|
108
|
+
# == Hidden methods and attributes
|
|
109
|
+
#
|
|
110
|
+
# You can provide documentation for methods that don't appear using
|
|
111
|
+
# the :method:, :singleton-method: and :attr: directives:
|
|
112
|
+
#
|
|
113
|
+
# ##
|
|
114
|
+
# # :attr_writer: ghost_writer
|
|
115
|
+
# # There is an attribute here, but you can't see it!
|
|
116
|
+
#
|
|
117
|
+
# ##
|
|
118
|
+
# # :method: ghost_method
|
|
119
|
+
# # There is a method here, but you can't see it!
|
|
120
|
+
#
|
|
121
|
+
# ##
|
|
122
|
+
# # this is a comment for a regular method
|
|
123
|
+
#
|
|
124
|
+
# def regular_method() end
|
|
125
|
+
#
|
|
126
|
+
# Note that by default, the :method: directive will be ignored if there is a
|
|
127
|
+
# standard rdocable item following it.
|
|
128
|
+
|
|
129
|
+
class Ruby < Parser
|
|
130
|
+
|
|
131
|
+
parse_files_matching(/\.rbw?$/)
|
|
132
|
+
|
|
133
|
+
# Matches an RBS inline type annotation line: #: followed by whitespace
|
|
134
|
+
RBS_SIG_LINE = /\A#:\s/ # :nodoc:
|
|
135
|
+
|
|
136
|
+
attr_accessor :visibility
|
|
137
|
+
attr_reader :container, :singleton, :in_proc_block
|
|
138
|
+
|
|
139
|
+
def initialize(top_level, content, options, stats)
|
|
140
|
+
super
|
|
156
141
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
142
|
+
content = handle_tab_width(content)
|
|
143
|
+
|
|
144
|
+
@size = 0
|
|
145
|
+
@token_listeners = nil
|
|
146
|
+
content = Encoding.remove_magic_comment content
|
|
147
|
+
@content = content
|
|
148
|
+
@colorizer_context = Parser::RubyColorizer::DeferredContext.new(content)
|
|
149
|
+
@markup = @options.markup
|
|
150
|
+
@track_visibility = :nodoc != @options.visibility
|
|
151
|
+
@encoding = @options.encoding
|
|
152
|
+
|
|
153
|
+
@module_nesting = [[top_level, false]]
|
|
154
|
+
@container = top_level
|
|
155
|
+
@visibility = :public
|
|
156
|
+
@singleton = false
|
|
157
|
+
@in_proc_block = false
|
|
158
|
+
@doc_state = :startdoc
|
|
159
|
+
end
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
# Applies document control directives (:startdoc:, :stopdoc: and :enddoc:)
|
|
162
|
+
# to the current lexical scope. The state is restored when the enclosing
|
|
163
|
+
# class/module scope is closed.
|
|
164
|
+
|
|
165
|
+
def apply_document_control_directive(directives)
|
|
166
|
+
directives.each do |directive, (_param, line)|
|
|
167
|
+
case directive
|
|
168
|
+
when 'startdoc', 'stopdoc'
|
|
169
|
+
# :enddoc: cannot be cancelled within the scope, even by :startdoc:
|
|
170
|
+
if @doc_state == :enddoc
|
|
171
|
+
@options.warn "#{@top_level.relative_name}:#{line}: :startdoc: is ignored after :enddoc:" if directive == 'startdoc'
|
|
172
|
+
next
|
|
173
|
+
end
|
|
174
|
+
@doc_state = directive.to_sym
|
|
175
|
+
if directive == 'startdoc' && !@container.ignored?
|
|
176
|
+
# Compatibility: `module Net #:nodoc:` followed by :stopdoc:/:startdoc:
|
|
177
|
+
# regions is a common pattern that expects :startdoc: to make the
|
|
178
|
+
# container documentable again. Containers ignored here were created
|
|
179
|
+
# in a suppressed region and need documentable contents to revive.
|
|
180
|
+
@container.start_doc
|
|
181
|
+
@container.force_documentation = true
|
|
182
|
+
end
|
|
183
|
+
when 'enddoc'
|
|
184
|
+
@doc_state = :enddoc
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
167
188
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def with_container(container, singleton: false)
|
|
171
|
-
old_container = @container
|
|
172
|
-
old_visibility = @visibility
|
|
173
|
-
old_singleton = @singleton
|
|
174
|
-
old_in_proc_block = @in_proc_block
|
|
175
|
-
@visibility = :public
|
|
176
|
-
@container = container
|
|
177
|
-
@singleton = singleton
|
|
178
|
-
@in_proc_block = false
|
|
179
|
-
@module_nesting.push([container, singleton])
|
|
180
|
-
yield container
|
|
181
|
-
ensure
|
|
182
|
-
@container = old_container
|
|
183
|
-
@visibility = old_visibility
|
|
184
|
-
@singleton = old_singleton
|
|
185
|
-
@in_proc_block = old_in_proc_block
|
|
186
|
-
@module_nesting.pop
|
|
187
|
-
end
|
|
189
|
+
# Returns true if code objects at the current position should not be
|
|
190
|
+
# documented, that is, inside a :stopdoc: or :enddoc: region.
|
|
188
191
|
|
|
189
|
-
|
|
190
|
-
|
|
192
|
+
def document_suppressed?
|
|
193
|
+
@track_visibility && @doc_state != :startdoc
|
|
194
|
+
end
|
|
191
195
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
@top_level.add_to_classes_or_modules container
|
|
196
|
-
end
|
|
196
|
+
# Makes a container that was created inside a :stopdoc:/:enddoc: region
|
|
197
|
+
# (thus ignored) documentable again when it receives documentable contents
|
|
198
|
+
# outside the region, possibly from another file.
|
|
197
199
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
+
def mark_container_documentable(container)
|
|
201
|
+
return if container.received_nodoc || !container.ignored?
|
|
202
|
+
record_location(container)
|
|
203
|
+
container.start_doc
|
|
204
|
+
mark_container_documentable(container.parent) if container.parent.is_a?(ClassModule)
|
|
205
|
+
end
|
|
200
206
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
@lines = @content.lines
|
|
205
|
-
result = Prism.parse_lex(@content)
|
|
206
|
-
@program_node, unordered_tokens = result.value
|
|
207
|
-
# Heredoc tokens are not in start_offset order.
|
|
208
|
-
# Need to sort them to use bsearch for finding tokens from location.
|
|
209
|
-
@prism_tokens = unordered_tokens.map(&:first).sort_by { |t| t.location.start_offset }
|
|
210
|
-
@line_nodes = {}
|
|
211
|
-
prepare_line_nodes(@program_node)
|
|
212
|
-
prepare_comments(result.comments)
|
|
213
|
-
return if @top_level.done_documenting
|
|
214
|
-
|
|
215
|
-
@first_non_meta_comment_start_line = nil
|
|
216
|
-
if (_line_no, start_line = @unprocessed_comments.first)
|
|
217
|
-
@first_non_meta_comment_start_line = start_line if start_line < @program_node.location.start_line
|
|
218
|
-
end
|
|
207
|
+
# Suppress `extend` and `include` within block
|
|
208
|
+
# because they might be a metaprogramming block
|
|
209
|
+
# example: `Module.new { include M }` `M.module_eval { include N }`
|
|
219
210
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
211
|
+
def with_in_proc_block
|
|
212
|
+
in_proc_block = @in_proc_block
|
|
213
|
+
@in_proc_block = true
|
|
214
|
+
yield
|
|
215
|
+
@in_proc_block = in_proc_block
|
|
216
|
+
end
|
|
223
217
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
218
|
+
# Dive into another container
|
|
219
|
+
|
|
220
|
+
def with_container(container, singleton: false)
|
|
221
|
+
old_container = @container
|
|
222
|
+
old_visibility = @visibility
|
|
223
|
+
old_singleton = @singleton
|
|
224
|
+
old_in_proc_block = @in_proc_block
|
|
225
|
+
old_doc_state = @doc_state
|
|
226
|
+
@visibility = :public
|
|
227
|
+
@container = container
|
|
228
|
+
@singleton = singleton
|
|
229
|
+
@in_proc_block = false
|
|
230
|
+
@module_nesting.push([container, singleton])
|
|
231
|
+
yield container
|
|
232
|
+
ensure
|
|
233
|
+
@container = old_container
|
|
234
|
+
@visibility = old_visibility
|
|
235
|
+
@singleton = old_singleton
|
|
236
|
+
@in_proc_block = old_in_proc_block
|
|
237
|
+
@doc_state = old_doc_state
|
|
238
|
+
@module_nesting.pop
|
|
239
|
+
end
|
|
229
240
|
|
|
230
|
-
|
|
231
|
-
|
|
241
|
+
# Records the location of this +container+ in the file for this parser and
|
|
242
|
+
# adds it to the list of classes and modules in the file.
|
|
232
243
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
node.compact_child_nodes.each do |child|
|
|
239
|
-
prepare_line_nodes(child)
|
|
240
|
-
end
|
|
241
|
-
end
|
|
244
|
+
def record_location(container) # :nodoc:
|
|
245
|
+
case container
|
|
246
|
+
when ClassModule
|
|
247
|
+
@top_level.add_to_classes_or_modules container
|
|
248
|
+
end
|
|
242
249
|
|
|
243
|
-
|
|
244
|
-
# Consecutive comment is linked to the next non-blank line.
|
|
245
|
-
#
|
|
246
|
-
# Example:
|
|
247
|
-
# 01| class A # modifier comment 1
|
|
248
|
-
# 02| def foo; end # modifier comment 2
|
|
249
|
-
# 03|
|
|
250
|
-
# 04| # consecutive comment 1 start_line: 4
|
|
251
|
-
# 05| # consecutive comment 1 linked to line: 7
|
|
252
|
-
# 06|
|
|
253
|
-
# 07| # consecutive comment 2 start_line: 7
|
|
254
|
-
# 08| # consecutive comment 2 linked to line: 10
|
|
255
|
-
# 09|
|
|
256
|
-
# 10| def bar; end # consecutive comment 2 linked to this line
|
|
257
|
-
# 11| end
|
|
258
|
-
|
|
259
|
-
def prepare_comments(comments)
|
|
260
|
-
current = []
|
|
261
|
-
consecutive_comments = [current]
|
|
262
|
-
@modifier_comments = {}
|
|
263
|
-
comments.each do |comment|
|
|
264
|
-
if comment.is_a? Prism::EmbDocComment
|
|
265
|
-
consecutive_comments << [comment] << (current = [])
|
|
266
|
-
elsif comment.location.start_line_slice.match?(/\S/)
|
|
267
|
-
text = comment.slice
|
|
268
|
-
text = RDoc::Encoding.change_encoding(text, @encoding) if @encoding
|
|
269
|
-
@modifier_comments[comment.location.start_line] = text
|
|
270
|
-
elsif current.empty? || current.last.location.end_line + 1 == comment.location.start_line
|
|
271
|
-
current << comment
|
|
272
|
-
else
|
|
273
|
-
consecutive_comments << (current = [comment])
|
|
250
|
+
container.record_location @top_level
|
|
274
251
|
end
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
252
|
+
|
|
253
|
+
# Scans this Ruby file for Ruby constructs
|
|
254
|
+
|
|
255
|
+
def scan
|
|
256
|
+
@lines = @content.lines
|
|
257
|
+
result = Prism.parse(@content)
|
|
258
|
+
@program_node = result.value
|
|
259
|
+
@line_nodes = {}
|
|
260
|
+
prepare_line_nodes(@program_node)
|
|
261
|
+
prepare_comments(result.comments)
|
|
262
|
+
return if @top_level.done_documenting
|
|
263
|
+
|
|
264
|
+
@first_non_meta_comment_start_line = nil
|
|
265
|
+
if (_line_no, start_line = @unprocessed_comments.first)
|
|
266
|
+
@first_non_meta_comment_start_line = start_line if start_line < @program_node.location.start_line
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
@program_node.accept(RDocVisitor.new(self, @top_level, @store))
|
|
270
|
+
process_comments_until(@lines.size + 1)
|
|
290
271
|
end
|
|
291
|
-
text = texts.join("\n")
|
|
292
|
-
text = RDoc::Encoding.change_encoding(text, @encoding) if @encoding
|
|
293
|
-
line_no += 1 while @lines[line_no - 1]&.match?(/\A\s*$/)
|
|
294
|
-
[line_no, start_line, text]
|
|
295
|
-
end
|
|
296
272
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
@markup = markup.downcase if markup
|
|
303
|
-
end
|
|
304
|
-
end
|
|
273
|
+
def should_document?(code_object) # :nodoc:
|
|
274
|
+
return true unless @track_visibility
|
|
275
|
+
return false if code_object.parent&.document_children == false
|
|
276
|
+
code_object.document_self
|
|
277
|
+
end
|
|
305
278
|
|
|
306
|
-
|
|
307
|
-
|
|
279
|
+
# Assign AST node to a line.
|
|
280
|
+
# This is used to show meta-method source code in the documentation.
|
|
308
281
|
|
|
309
|
-
|
|
310
|
-
|
|
282
|
+
def prepare_line_nodes(node) # :nodoc:
|
|
283
|
+
case node
|
|
284
|
+
when Prism::CallNode, Prism::DefNode
|
|
285
|
+
@line_nodes[node.location.start_line] ||= node
|
|
286
|
+
end
|
|
287
|
+
node.compact_child_nodes.each do |child|
|
|
288
|
+
prepare_line_nodes(child)
|
|
289
|
+
end
|
|
290
|
+
end
|
|
311
291
|
|
|
312
|
-
|
|
292
|
+
# Prepares comments for processing. Comments are grouped into consecutive.
|
|
293
|
+
# Consecutive comment is linked to the next non-blank line.
|
|
294
|
+
#
|
|
295
|
+
# Example:
|
|
296
|
+
# 01| class A # modifier comment 1
|
|
297
|
+
# 02| def foo; end # modifier comment 2
|
|
298
|
+
# 03|
|
|
299
|
+
# 04| # consecutive comment 1 start_line: 4
|
|
300
|
+
# 05| # consecutive comment 1 linked to line: 7
|
|
301
|
+
# 06|
|
|
302
|
+
# 07| # consecutive comment 2 start_line: 7
|
|
303
|
+
# 08| # consecutive comment 2 linked to line: 10
|
|
304
|
+
# 09|
|
|
305
|
+
# 10| def bar; end # consecutive comment 2 linked to this line
|
|
306
|
+
# 11| end
|
|
307
|
+
|
|
308
|
+
def prepare_comments(comments)
|
|
309
|
+
current = []
|
|
310
|
+
consecutive_comments = [current]
|
|
311
|
+
@modifier_comments = {}
|
|
312
|
+
comments.each do |comment|
|
|
313
|
+
if comment.is_a? Prism::EmbDocComment
|
|
314
|
+
consecutive_comments << [comment] << (current = [])
|
|
315
|
+
elsif comment.location.start_line_slice.match?(/\S/)
|
|
316
|
+
text = comment.slice
|
|
317
|
+
text = Encoding.change_encoding(text, @encoding) if @encoding
|
|
318
|
+
@modifier_comments[comment.location.start_line] = text
|
|
319
|
+
elsif current.empty? || current.last.location.end_line + 1 == comment.location.start_line
|
|
320
|
+
current << comment
|
|
321
|
+
else
|
|
322
|
+
consecutive_comments << (current = [comment])
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
consecutive_comments.reject!(&:empty?)
|
|
326
|
+
|
|
327
|
+
# Example: line_no = 5, start_line = 2, comment_text = "# comment_start_line\n# comment\n"
|
|
328
|
+
# 1| class A
|
|
329
|
+
# 2| # comment_start_line
|
|
330
|
+
# 3| # comment
|
|
331
|
+
# 4|
|
|
332
|
+
# 5| def f; end # comment linked to this line
|
|
333
|
+
# 6| end
|
|
334
|
+
@unprocessed_comments = consecutive_comments.map! do |comments|
|
|
335
|
+
start_line = comments.first.location.start_line
|
|
336
|
+
line_no = comments.last.location.end_line + (comments.last.location.end_column == 0 ? 0 : 1)
|
|
337
|
+
texts = comments.map do |c|
|
|
338
|
+
c.is_a?(Prism::EmbDocComment) ? c.slice.lines[1...-1].join : c.slice
|
|
339
|
+
end
|
|
340
|
+
text = texts.join("\n")
|
|
341
|
+
text = Encoding.change_encoding(text, @encoding) if @encoding
|
|
342
|
+
line_no += 1 while @lines[line_no - 1]&.match?(/\A\s*$/)
|
|
343
|
+
[line_no, start_line, text]
|
|
344
|
+
end
|
|
313
345
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
346
|
+
# The first comment is special. It defines markup for the rest of the comments.
|
|
347
|
+
_, first_comment_start_line, first_comment_text = @unprocessed_comments.first
|
|
348
|
+
if first_comment_text && @lines[0...first_comment_start_line - 1].all? { |l| l.match?(/\A\s*$/) }
|
|
349
|
+
_text, directives = @preprocess.parse_comment(first_comment_text, first_comment_start_line, :ruby)
|
|
350
|
+
markup, = directives['markup']
|
|
351
|
+
@markup = markup.downcase if markup
|
|
352
|
+
end
|
|
353
|
+
end
|
|
319
354
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
tokens = node ? syntax_highlighted_tokens(node) : []
|
|
323
|
-
tokens.each { |token| meth.token_stream << token }
|
|
355
|
+
# Creates an RDoc::Method on +container+ from +comment+ if there is a
|
|
356
|
+
# Signature section in the comment
|
|
324
357
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
end
|
|
358
|
+
def parse_comment_tomdoc(container, comment, line_no, start_line)
|
|
359
|
+
return if document_suppressed?
|
|
360
|
+
return unless signature = TomDoc.signature(comment)
|
|
329
361
|
|
|
330
|
-
|
|
331
|
-
@modifier_comments[line_no]&.match?(/\A#\s*:nodoc:/)
|
|
332
|
-
end
|
|
362
|
+
name, = signature.split %r%[ \(]%, 2
|
|
333
363
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
end
|
|
364
|
+
meth = AnyMethod.new name
|
|
365
|
+
record_location(meth)
|
|
366
|
+
meth.line = start_line
|
|
367
|
+
meth.call_seq = signature
|
|
368
|
+
return unless meth.name
|
|
340
369
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
case arg
|
|
345
|
-
when Prism::SymbolNode
|
|
346
|
-
arg.value
|
|
347
|
-
when Prism::StringNode
|
|
348
|
-
arg.unescaped
|
|
349
|
-
end
|
|
350
|
-
end || []
|
|
351
|
-
end
|
|
370
|
+
node = @line_nodes[line_no]
|
|
371
|
+
token_stream_loader = @colorizer_context.token_stream_loader(node.node_id) if node
|
|
372
|
+
meth.start_collecting_tokens(:ruby, loader: token_stream_loader)
|
|
352
373
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
handle_code_object_directives(@container, directives)
|
|
357
|
-
is_call_node = node.is_a?(Prism::CallNode)
|
|
358
|
-
singleton_method = false
|
|
359
|
-
visibility = @visibility
|
|
360
|
-
attributes = rw = line_no = method_name = nil
|
|
361
|
-
directives.each do |directive, (param, line)|
|
|
362
|
-
case directive
|
|
363
|
-
when 'attr', 'attr_reader', 'attr_writer', 'attr_accessor'
|
|
364
|
-
attributes = [param] if param
|
|
365
|
-
attributes ||= call_node_name_arguments(node) if is_call_node
|
|
366
|
-
rw = directive == 'attr_writer' ? 'W' : directive == 'attr_accessor' ? 'RW' : 'R'
|
|
367
|
-
when 'method'
|
|
368
|
-
method_name = param if param
|
|
369
|
-
line_no = line
|
|
370
|
-
when 'singleton-method'
|
|
371
|
-
method_name = param if param
|
|
372
|
-
line_no = line
|
|
373
|
-
singleton_method = true
|
|
374
|
-
visibility = :public
|
|
374
|
+
container.add_method meth
|
|
375
|
+
meth.comment = comment
|
|
376
|
+
@stats.add_method meth
|
|
375
377
|
end
|
|
376
|
-
end
|
|
377
378
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
a = RDoc::Attr.new(attr, rw, comment, singleton: @singleton)
|
|
381
|
-
a.store = @store
|
|
382
|
-
a.line = line_no
|
|
383
|
-
record_location(a)
|
|
384
|
-
@container.add_attribute(a)
|
|
385
|
-
a.visibility = visibility
|
|
386
|
-
end
|
|
387
|
-
elsif line_no || node
|
|
388
|
-
method_name ||= call_node_name_arguments(node).first if is_call_node
|
|
389
|
-
if node
|
|
390
|
-
tokens = syntax_highlighted_tokens(node)
|
|
391
|
-
line_no = node.location.start_line
|
|
392
|
-
else
|
|
393
|
-
tokens = []
|
|
379
|
+
def has_modifier_nodoc?(line_no) # :nodoc:
|
|
380
|
+
@modifier_comments[line_no]&.match?(/\A#\s*:nodoc:/)
|
|
394
381
|
end
|
|
395
|
-
internal_add_method(
|
|
396
|
-
method_name,
|
|
397
|
-
@container,
|
|
398
|
-
comment: comment,
|
|
399
|
-
directives: directives,
|
|
400
|
-
dont_rename_initialize: false,
|
|
401
|
-
line_no: line_no,
|
|
402
|
-
visibility: visibility,
|
|
403
|
-
singleton: @singleton || singleton_method,
|
|
404
|
-
params: nil,
|
|
405
|
-
calls_super: false,
|
|
406
|
-
block_params: nil,
|
|
407
|
-
tokens: tokens,
|
|
408
|
-
)
|
|
409
|
-
end
|
|
410
|
-
end
|
|
411
382
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
# Meta method comment should start with `##` but some comments does not follow this rule.
|
|
419
|
-
# For now, RDoc accepts them as a meta method comment if there is no node linked to it.
|
|
420
|
-
!@line_nodes[line_no] && INVALID_GHOST_METHOD_ACCEPT_DIRECTIVE_LIST.any? { |directive| directives.has_key?(directive) }
|
|
421
|
-
end
|
|
383
|
+
def handle_modifier_directive(code_object, line_no) # :nodoc:
|
|
384
|
+
if (comment_text = @modifier_comments[line_no])
|
|
385
|
+
_text, directives = @preprocess.parse_comment(comment_text, line_no, :ruby)
|
|
386
|
+
handle_code_object_directives(code_object, directives)
|
|
387
|
+
end
|
|
388
|
+
end
|
|
422
389
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
handle_meta_method_comment(comment, directives, nil)
|
|
429
|
-
else
|
|
430
|
-
handle_code_object_directives(@container, directives)
|
|
431
|
-
end
|
|
432
|
-
end
|
|
390
|
+
def call_node_name_arguments(call_node) # :nodoc:
|
|
391
|
+
return unless arguments_node = call_node.arguments
|
|
392
|
+
names = arguments_node.arguments.filter_map { |arg| argument_name(arg) }
|
|
393
|
+
names unless names.empty?
|
|
394
|
+
end
|
|
433
395
|
|
|
434
|
-
|
|
396
|
+
def call_node_name_argument(call_node) # :nodoc:
|
|
397
|
+
return unless call_node.arguments
|
|
398
|
+
argument_name(call_node.arguments.arguments.first)
|
|
399
|
+
end
|
|
435
400
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
@preprocess.run_post_processes(comment, @container)
|
|
444
|
-
elsif (comment_text, directives = parse_comment_text_to_directives(text, start_line))
|
|
445
|
-
handle_standalone_consecutive_comment_directive(comment_text, directives, text.start_with?(/#\#$/), line_no, start_line)
|
|
401
|
+
def argument_name(argument_node) # :nodoc:
|
|
402
|
+
case argument_node
|
|
403
|
+
when Prism::SymbolNode
|
|
404
|
+
argument_node.value
|
|
405
|
+
when Prism::StringNode
|
|
406
|
+
argument_node.unescaped
|
|
407
|
+
end
|
|
446
408
|
end
|
|
447
|
-
end
|
|
448
|
-
end
|
|
449
409
|
|
|
450
|
-
|
|
451
|
-
|
|
410
|
+
# Handles meta method comments
|
|
411
|
+
|
|
412
|
+
def handle_meta_method_comment(comment, directives, node)
|
|
413
|
+
apply_document_control_directive(directives)
|
|
414
|
+
handle_code_object_directives(@container, directives)
|
|
415
|
+
is_call_node = node.is_a?(Prism::CallNode)
|
|
416
|
+
singleton_method = false
|
|
417
|
+
visibility = @visibility
|
|
418
|
+
attributes = rw = line_no = method_name = nil
|
|
419
|
+
directives.each do |directive, (param, line)|
|
|
420
|
+
case directive
|
|
421
|
+
when 'attr', 'attr_reader', 'attr_writer', 'attr_accessor'
|
|
422
|
+
attributes = [param] if param
|
|
423
|
+
attributes ||= call_node_name_arguments(node) || [] if is_call_node
|
|
424
|
+
rw = directive == 'attr_writer' ? 'W' : directive == 'attr_accessor' ? 'RW' : 'R'
|
|
425
|
+
when 'method'
|
|
426
|
+
method_name = param if param
|
|
427
|
+
line_no = line
|
|
428
|
+
when 'singleton-method'
|
|
429
|
+
method_name = param if param
|
|
430
|
+
line_no = line
|
|
431
|
+
singleton_method = true
|
|
432
|
+
visibility = :public
|
|
433
|
+
end
|
|
434
|
+
end
|
|
452
435
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
436
|
+
return if document_suppressed?
|
|
437
|
+
|
|
438
|
+
if attributes
|
|
439
|
+
attributes.each do |attr|
|
|
440
|
+
a = Attr.new(attr, rw, comment, singleton: @singleton)
|
|
441
|
+
a.store = @store
|
|
442
|
+
a.line = line_no
|
|
443
|
+
a.visibility = visibility
|
|
444
|
+
record_location(a)
|
|
445
|
+
@container.add_attribute(a)
|
|
446
|
+
mark_container_documentable(@container)
|
|
447
|
+
end
|
|
448
|
+
elsif line_no || node
|
|
449
|
+
method_name ||= call_node_name_argument(node) if is_call_node
|
|
450
|
+
line_no = node.location.start_line if node
|
|
451
|
+
internal_add_method(
|
|
452
|
+
method_name,
|
|
453
|
+
@container,
|
|
454
|
+
comment: comment,
|
|
455
|
+
directives: directives,
|
|
456
|
+
line_no: line_no,
|
|
457
|
+
visibility: visibility,
|
|
458
|
+
singleton: @singleton || singleton_method,
|
|
459
|
+
params: nil,
|
|
460
|
+
calls_super: false,
|
|
461
|
+
block_params: nil,
|
|
462
|
+
node_id: node&.node_id,
|
|
463
|
+
)
|
|
464
|
+
end
|
|
465
|
+
end
|
|
458
466
|
|
|
459
|
-
|
|
467
|
+
INVALID_GHOST_METHOD_ACCEPT_DIRECTIVE_LIST = %w[
|
|
468
|
+
method singleton-method attr attr_reader attr_writer attr_accessor
|
|
469
|
+
].freeze
|
|
470
|
+
private_constant :INVALID_GHOST_METHOD_ACCEPT_DIRECTIVE_LIST
|
|
460
471
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
472
|
+
def normal_comment_treat_as_ghost_method_for_now?(directives, line_no) # :nodoc:
|
|
473
|
+
# Meta method comment should start with `##` but some comments does not follow this rule.
|
|
474
|
+
# For now, RDoc accepts them as a meta method comment if there is no node linked to it.
|
|
475
|
+
!@line_nodes[line_no] && INVALID_GHOST_METHOD_ACCEPT_DIRECTIVE_LIST.any? { |directive| directives.has_key?(directive) }
|
|
476
|
+
end
|
|
466
477
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
comment.format = markup&.downcase || @markup
|
|
479
|
-
if (section, directive_line = directives['section'])
|
|
480
|
-
# If comment has :section:, it is not a documentable comment for a code object
|
|
481
|
-
comment.text = extract_section_comment(comment_text, directive_line - start_line)
|
|
482
|
-
@container.set_current_section(section, comment)
|
|
483
|
-
return
|
|
484
|
-
end
|
|
485
|
-
@preprocess.run_post_processes(comment, @container)
|
|
486
|
-
[comment, directives, type_signature_lines]
|
|
487
|
-
end
|
|
478
|
+
def handle_standalone_consecutive_comment_directive(comment, directives, start_with_sharp_sharp, line_no, start_line) # :nodoc:
|
|
479
|
+
if start_with_sharp_sharp && start_line != @first_non_meta_comment_start_line
|
|
480
|
+
node = @line_nodes[line_no]
|
|
481
|
+
handle_meta_method_comment(comment, directives, node)
|
|
482
|
+
elsif normal_comment_treat_as_ghost_method_for_now?(directives, line_no) && start_line != @first_non_meta_comment_start_line
|
|
483
|
+
handle_meta_method_comment(comment, directives, nil)
|
|
484
|
+
else
|
|
485
|
+
apply_document_control_directive(directives)
|
|
486
|
+
handle_code_object_directives(@container, directives)
|
|
487
|
+
end
|
|
488
|
+
end
|
|
488
489
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
490
|
+
# Processes consecutive comments that were not linked to any documentable code until the given line number
|
|
491
|
+
|
|
492
|
+
def process_comments_until(line_no_until)
|
|
493
|
+
while !@unprocessed_comments.empty? && @unprocessed_comments.first[0] <= line_no_until
|
|
494
|
+
line_no, start_line, text = @unprocessed_comments.shift
|
|
495
|
+
if @markup == 'tomdoc'
|
|
496
|
+
comment = Comment.new(text, @top_level, :ruby)
|
|
497
|
+
comment.format = 'tomdoc'
|
|
498
|
+
parse_comment_tomdoc(@container, comment, line_no, start_line)
|
|
499
|
+
@preprocess.run_post_processes(comment, @container)
|
|
500
|
+
elsif (comment_text, directives = parse_comment_text_to_directives(text, start_line))
|
|
501
|
+
handle_standalone_consecutive_comment_directive(comment_text, directives, text.start_with?(/#\#$/), line_no, start_line)
|
|
502
|
+
end
|
|
503
|
+
end
|
|
504
|
+
end
|
|
492
505
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
comment_text.delete_prefix!(prefix)
|
|
496
|
-
# Comment is already normalized and doesn't end with a newline
|
|
497
|
-
comment_text.delete_suffix!(prefix.chomp)
|
|
498
|
-
comment_text
|
|
499
|
-
end
|
|
506
|
+
# Skips all undocumentable consecutive comments until the given line number.
|
|
507
|
+
# Undocumentable comments are comments written inside `def` or inside undocumentable class/module
|
|
500
508
|
|
|
501
|
-
|
|
509
|
+
def skip_comments_until(line_no_until)
|
|
510
|
+
while !@unprocessed_comments.empty? && @unprocessed_comments.first[0] <= line_no_until
|
|
511
|
+
@unprocessed_comments.shift
|
|
512
|
+
end
|
|
513
|
+
end
|
|
502
514
|
|
|
503
|
-
|
|
504
|
-
RDoc::Parser::RubyColorizer.partial_colorize(@content, node, @prism_tokens)
|
|
505
|
-
end
|
|
515
|
+
# Returns consecutive comment linked to the given line number
|
|
506
516
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
@container.methods_matching(names, singleton) do |m|
|
|
512
|
-
if m.parent != @container
|
|
513
|
-
m = m.dup
|
|
514
|
-
record_location(m)
|
|
515
|
-
new_methods << m
|
|
516
|
-
else
|
|
517
|
-
m.visibility = visibility
|
|
517
|
+
def consecutive_comment(line_no)
|
|
518
|
+
return unless @unprocessed_comments.first&.first == line_no
|
|
519
|
+
_line_no, start_line, text = @unprocessed_comments.shift
|
|
520
|
+
parse_comment_text_to_directives(text, start_line)
|
|
518
521
|
end
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
522
|
+
|
|
523
|
+
# Parses comment text and returns +[RDoc::Comment, directives, type_signature_lines]+,
|
|
524
|
+
# or +nil+ if the comment is a section header (which has no associated code
|
|
525
|
+
# object).
|
|
526
|
+
|
|
527
|
+
def parse_comment_text_to_directives(comment_text, start_line) # :nodoc:
|
|
528
|
+
type_signature_lines = extract_type_signature!(comment_text, start_line)
|
|
529
|
+
comment_text, directives = @preprocess.parse_comment(comment_text, start_line, :ruby)
|
|
530
|
+
comment = Comment.new(comment_text, @top_level, :ruby)
|
|
531
|
+
comment.normalized = true
|
|
532
|
+
comment.line = start_line
|
|
533
|
+
markup, = directives['markup']
|
|
534
|
+
comment.format = markup&.downcase || @markup
|
|
535
|
+
if (section, directive_line = directives['section'])
|
|
536
|
+
# If comment has :section:, it is not a documentable comment for a code object
|
|
537
|
+
comment.text = extract_section_comment(comment_text, directive_line - start_line)
|
|
538
|
+
@container.set_current_section(section, comment)
|
|
539
|
+
return
|
|
540
|
+
end
|
|
541
|
+
@preprocess.run_post_processes(comment, @container)
|
|
542
|
+
[comment, directives, type_signature_lines]
|
|
526
543
|
end
|
|
527
|
-
method.visibility = visibility
|
|
528
|
-
end
|
|
529
|
-
end
|
|
530
544
|
|
|
531
|
-
|
|
545
|
+
# Extracts the comment for this section from the normalized comment block.
|
|
546
|
+
# Removes all lines before the line that contains :section:
|
|
547
|
+
# If the comment also ends with the same content, remove it as well
|
|
532
548
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
s_m.singleton = true
|
|
540
|
-
new_methods << s_m
|
|
541
|
-
end
|
|
542
|
-
new_methods.each do |method|
|
|
543
|
-
case method
|
|
544
|
-
when RDoc::AnyMethod then
|
|
545
|
-
@container.add_method(method)
|
|
546
|
-
when RDoc::Attr then
|
|
547
|
-
@container.add_attribute(method)
|
|
549
|
+
def extract_section_comment(comment_text, prefix_line_count) # :nodoc:
|
|
550
|
+
prefix = comment_text.lines[0...prefix_line_count].join
|
|
551
|
+
comment_text.delete_prefix!(prefix)
|
|
552
|
+
# Comment is already normalized and doesn't end with a newline
|
|
553
|
+
comment_text.delete_suffix!(prefix.chomp)
|
|
554
|
+
comment_text
|
|
548
555
|
end
|
|
549
|
-
method.visibility = :public
|
|
550
|
-
end
|
|
551
|
-
end
|
|
552
556
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
557
|
+
# Handles `public :foo, :bar` `private :foo, :bar` and `protected :foo, :bar`
|
|
558
|
+
|
|
559
|
+
def change_method_visibility(names, visibility, singleton: @singleton)
|
|
560
|
+
new_methods = []
|
|
561
|
+
@container.methods_matching(names, singleton) do |m|
|
|
562
|
+
if m.parent != @container
|
|
563
|
+
# A copy of an ancestor's method must not be documented
|
|
564
|
+
# in a :stopdoc:/:enddoc: region
|
|
565
|
+
next if document_suppressed?
|
|
566
|
+
m = m.dup
|
|
567
|
+
record_location(m)
|
|
568
|
+
new_methods << m
|
|
569
|
+
else
|
|
570
|
+
m.visibility = visibility
|
|
571
|
+
end
|
|
572
|
+
end
|
|
573
|
+
new_methods.each do |method|
|
|
574
|
+
method.visibility = visibility
|
|
575
|
+
case method
|
|
576
|
+
when AnyMethod
|
|
577
|
+
@container.add_method(method)
|
|
578
|
+
when Attr
|
|
579
|
+
@container.add_attribute(method)
|
|
580
|
+
end
|
|
581
|
+
end
|
|
582
|
+
end
|
|
558
583
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
def add_alias_method(old_name, new_name, line_no)
|
|
562
|
-
comment, directives = consecutive_comment(line_no)
|
|
563
|
-
handle_code_object_directives(@container, directives) if directives
|
|
564
|
-
visibility = @container.find_method(old_name, @singleton)&.visibility || :public
|
|
565
|
-
a = RDoc::Alias.new(old_name, new_name, comment, singleton: @singleton)
|
|
566
|
-
handle_modifier_directive(a, line_no)
|
|
567
|
-
a.store = @store
|
|
568
|
-
a.line = line_no
|
|
569
|
-
record_location(a)
|
|
570
|
-
if should_document?(a)
|
|
571
|
-
@container.add_alias(a)
|
|
572
|
-
@container.find_method(new_name, @singleton)&.visibility = visibility
|
|
573
|
-
end
|
|
574
|
-
end
|
|
584
|
+
# Handles `module_function :foo, :bar`
|
|
575
585
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
return unless @container.document_children
|
|
582
|
-
|
|
583
|
-
names.each do |symbol|
|
|
584
|
-
a = RDoc::Attr.new(symbol.to_s, rw, comment, singleton: @singleton)
|
|
585
|
-
a.store = @store
|
|
586
|
-
a.line = line_no
|
|
587
|
-
a.type_signature_lines = type_signature_lines
|
|
588
|
-
record_location(a)
|
|
589
|
-
handle_modifier_directive(a, line_no)
|
|
590
|
-
@container.add_attribute(a) if should_document?(a)
|
|
591
|
-
a.visibility = visibility # should set after adding to container
|
|
592
|
-
end
|
|
593
|
-
end
|
|
586
|
+
def change_method_to_module_function(names)
|
|
587
|
+
@container.set_visibility_for(names, :private, false)
|
|
588
|
+
# In a :stopdoc:/:enddoc: region, the visibility of instance methods still
|
|
589
|
+
# changes but the singleton method copies must not be documented
|
|
590
|
+
return if document_suppressed?
|
|
594
591
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
592
|
+
new_methods = []
|
|
593
|
+
@container.methods_matching(names) do |m|
|
|
594
|
+
s_m = m.dup
|
|
595
|
+
record_location(s_m)
|
|
596
|
+
s_m.singleton = true
|
|
597
|
+
new_methods << s_m
|
|
598
|
+
end
|
|
599
|
+
new_methods.each do |method|
|
|
600
|
+
method.visibility = :public
|
|
601
|
+
case method
|
|
602
|
+
when AnyMethod
|
|
603
|
+
@container.add_method(method)
|
|
604
|
+
when Attr
|
|
605
|
+
@container.add_attribute(method)
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
609
|
|
|
610
|
-
|
|
610
|
+
def handle_code_object_directives(code_object, directives) # :nodoc:
|
|
611
|
+
directives.each do |directive, (param)|
|
|
612
|
+
# startdoc/stopdoc/enddoc are handled by apply_document_control_directive.
|
|
613
|
+
# They control the lexical scope of the parser, not the code object.
|
|
614
|
+
next if directive == 'startdoc' || directive == 'stopdoc' || directive == 'enddoc'
|
|
615
|
+
@preprocess.handle_directive('', directive, param, code_object)
|
|
616
|
+
end
|
|
617
|
+
end
|
|
611
618
|
|
|
612
|
-
|
|
613
|
-
add_includes_extends(names, RDoc::Include, line_no)
|
|
614
|
-
end
|
|
619
|
+
# Handles `alias foo bar` and `alias_method :foo, :bar`
|
|
615
620
|
|
|
616
|
-
|
|
621
|
+
def add_alias_method(old_name, new_name, line_no)
|
|
622
|
+
comment, directives = consecutive_comment(line_no)
|
|
623
|
+
apply_document_control_directive(directives) if directives
|
|
624
|
+
handle_code_object_directives(@container, directives) if directives
|
|
625
|
+
return if document_suppressed?
|
|
617
626
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
627
|
+
a = Alias.new(old_name, new_name, comment, singleton: @singleton)
|
|
628
|
+
handle_modifier_directive(a, line_no)
|
|
629
|
+
a.store = @store
|
|
630
|
+
a.line = line_no
|
|
631
|
+
record_location(a)
|
|
632
|
+
if should_document?(a)
|
|
633
|
+
mark_container_documentable(@container)
|
|
634
|
+
@container.add_alias(a)
|
|
635
|
+
end
|
|
636
|
+
end
|
|
621
637
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
638
|
+
# Handles `attr :a, :b`, `attr_reader :a, :b`, `attr_writer :a, :b` and `attr_accessor :a, :b`
|
|
639
|
+
|
|
640
|
+
def add_attributes(names, rw, line_no)
|
|
641
|
+
comment, directives, type_signature_lines = consecutive_comment(line_no)
|
|
642
|
+
apply_document_control_directive(directives) if directives
|
|
643
|
+
handle_code_object_directives(@container, directives) if directives
|
|
644
|
+
return if document_suppressed?
|
|
645
|
+
return unless @container.document_children
|
|
646
|
+
|
|
647
|
+
names.each do |symbol|
|
|
648
|
+
a = Attr.new(symbol.to_s, rw, comment, singleton: @singleton)
|
|
649
|
+
a.store = @store
|
|
650
|
+
a.line = line_no
|
|
651
|
+
a.type_signature_lines = type_signature_lines
|
|
652
|
+
a.visibility = visibility
|
|
653
|
+
record_location(a)
|
|
654
|
+
handle_modifier_directive(a, line_no)
|
|
655
|
+
if should_document?(a)
|
|
656
|
+
@container.add_attribute(a)
|
|
657
|
+
mark_container_documentable(@container)
|
|
658
|
+
end
|
|
659
|
+
end
|
|
660
|
+
end
|
|
645
661
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
662
|
+
# Adds includes/extends. Module name is resolved to full before adding.
|
|
663
|
+
|
|
664
|
+
def add_includes_extends(names, rdoc_class, line_no) # :nodoc:
|
|
665
|
+
comment, directives = consecutive_comment(line_no)
|
|
666
|
+
apply_document_control_directive(directives) if directives
|
|
667
|
+
handle_code_object_directives(@container, directives) if directives
|
|
668
|
+
return if document_suppressed?
|
|
669
|
+
|
|
670
|
+
mark_container_documentable(@container)
|
|
671
|
+
names.each do |name|
|
|
672
|
+
resolved_name = resolve_constant_path(name)
|
|
673
|
+
ie = @container.add(rdoc_class, resolved_name || name, '')
|
|
674
|
+
ie.store = @store
|
|
675
|
+
ie.line = line_no
|
|
676
|
+
ie.comment = comment
|
|
677
|
+
record_location(ie)
|
|
678
|
+
end
|
|
679
|
+
end
|
|
654
680
|
|
|
655
|
-
|
|
656
|
-
meth.call_seq = call_seq.lines.map(&:chomp).reject(&:empty?).join("\n") if call_seq
|
|
657
|
-
end
|
|
658
|
-
meth.name ||= meth.call_seq[/\A[^()\s]+/] if meth.call_seq
|
|
659
|
-
meth.name ||= 'unknown'
|
|
660
|
-
meth.store = @store
|
|
661
|
-
meth.line = line_no
|
|
662
|
-
container.add_method(meth) # should add after setting singleton and before setting visibility
|
|
663
|
-
meth.visibility = visibility
|
|
664
|
-
meth.params ||= params || '()'
|
|
665
|
-
meth.calls_super = calls_super
|
|
666
|
-
meth.block_params ||= block_params if block_params
|
|
667
|
-
meth.type_signature_lines = type_signature_lines
|
|
668
|
-
record_location(meth)
|
|
669
|
-
meth.start_collecting_tokens(:ruby)
|
|
670
|
-
tokens.each do |token|
|
|
671
|
-
meth.token_stream << token
|
|
672
|
-
end
|
|
681
|
+
# Handle `include Foo, Bar`
|
|
673
682
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
if !dont_rename_initialize && method_name == 'initialize' && !singleton
|
|
677
|
-
if meth.dont_rename_initialize
|
|
678
|
-
meth.visibility = :protected
|
|
679
|
-
else
|
|
680
|
-
meth.name = 'new'
|
|
681
|
-
meth.singleton = true
|
|
682
|
-
meth.visibility = :public
|
|
683
|
+
def add_includes(names, line_no) # :nodoc:
|
|
684
|
+
add_includes_extends(names, Include, line_no)
|
|
683
685
|
end
|
|
684
|
-
end
|
|
685
|
-
end
|
|
686
686
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
def find_or_create_lexical_module_path(module_name, create_mode)
|
|
692
|
-
root_name, *path, name = module_name.split('::')
|
|
693
|
-
add_module = ->(mod, name, mode) {
|
|
694
|
-
case mode
|
|
695
|
-
when :class
|
|
696
|
-
mod.add_class(RDoc::NormalClass, name, 'Object').tap { |m| m.store = @store }
|
|
697
|
-
when :module
|
|
698
|
-
mod.add_module(RDoc::NormalModule, name).tap { |m| m.store = @store }
|
|
699
|
-
end
|
|
700
|
-
}
|
|
701
|
-
if root_name.empty?
|
|
702
|
-
mod = @top_level
|
|
703
|
-
else
|
|
704
|
-
@module_nesting.reverse_each do |nesting, singleton|
|
|
705
|
-
next if singleton
|
|
706
|
-
mod = nesting.get_module_named(root_name)
|
|
707
|
-
break if mod
|
|
708
|
-
# If a constant is found and it is not a module or class, RDoc can't document about it.
|
|
709
|
-
# Return an anonymous module to avoid wrong document creation.
|
|
710
|
-
return RDoc::NormalModule.new(nil) if nesting.find_constant_named(root_name)
|
|
687
|
+
# Handle `extend Foo, Bar`
|
|
688
|
+
|
|
689
|
+
def add_extends(names, line_no) # :nodoc:
|
|
690
|
+
add_includes_extends(names, Extend, line_no)
|
|
711
691
|
end
|
|
712
|
-
last_nesting, = @module_nesting.reverse_each.find { |_, singleton| !singleton }
|
|
713
|
-
return mod || add_module.call(last_nesting, root_name, create_mode) unless name
|
|
714
|
-
mod ||= add_module.call(last_nesting, root_name, :module)
|
|
715
|
-
end
|
|
716
|
-
path.each do |name|
|
|
717
|
-
mod = mod.get_module_named(name) || add_module.call(mod, name, :module)
|
|
718
|
-
end
|
|
719
|
-
mod.get_module_named(name) || add_module.call(mod, name, create_mode)
|
|
720
|
-
end
|
|
721
692
|
|
|
722
|
-
|
|
693
|
+
# Adds a method defined by `def` syntax
|
|
694
|
+
|
|
695
|
+
def add_method(method_name, receiver_name:, receiver_fallback_type:, visibility:, singleton:, params:, calls_super:, block_params:, node_id:, start_line:, args_end_line:, end_line:)
|
|
696
|
+
comment, directives, type_signature_lines = consecutive_comment(start_line)
|
|
697
|
+
apply_document_control_directive(directives) if directives
|
|
698
|
+
handle_code_object_directives(@container, directives) if directives
|
|
699
|
+
# Resolve receiver after applying directives so that a namespace created
|
|
700
|
+
# here is marked as ignored when the comment starts a :stopdoc: region
|
|
701
|
+
receiver = receiver_name ? find_or_create_lexical_module_path(receiver_name, receiver_fallback_type) : @container
|
|
702
|
+
|
|
703
|
+
internal_add_method(
|
|
704
|
+
method_name,
|
|
705
|
+
receiver,
|
|
706
|
+
comment: comment,
|
|
707
|
+
directives: directives,
|
|
708
|
+
modifier_comment_lines: [start_line, args_end_line, end_line].uniq,
|
|
709
|
+
line_no: start_line,
|
|
710
|
+
visibility: visibility,
|
|
711
|
+
singleton: singleton,
|
|
712
|
+
params: params,
|
|
713
|
+
calls_super: calls_super,
|
|
714
|
+
block_params: block_params,
|
|
715
|
+
node_id: node_id,
|
|
716
|
+
type_signature_lines: type_signature_lines
|
|
717
|
+
)
|
|
718
|
+
end
|
|
723
719
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
mod ||= @top_level.get_module_named(owner_name)
|
|
734
|
-
[mod.full_name, path].compact.join('::') if mod
|
|
735
|
-
end
|
|
720
|
+
private def internal_add_method(method_name, container, comment:, directives:, modifier_comment_lines: nil, line_no:, visibility:, singleton:, params:, calls_super:, block_params:, node_id:, type_signature_lines: nil) # :nodoc:
|
|
721
|
+
meth = AnyMethod.new(method_name, singleton: singleton)
|
|
722
|
+
meth.comment = comment
|
|
723
|
+
handle_code_object_directives(meth, directives) if directives
|
|
724
|
+
modifier_comment_lines&.each do |line|
|
|
725
|
+
handle_modifier_directive(meth, line)
|
|
726
|
+
end
|
|
727
|
+
return if document_suppressed?
|
|
728
|
+
return unless should_document?(meth)
|
|
736
729
|
|
|
737
|
-
|
|
738
|
-
# using Ruby lexical nesting. Creates owner module if it does not exist.
|
|
739
|
-
|
|
740
|
-
def find_or_create_lexical_constant_owner_name(constant_path)
|
|
741
|
-
const_path, colon, name = constant_path.rpartition('::')
|
|
742
|
-
if colon.empty? # class Foo
|
|
743
|
-
# Within `class C` or `module C`, owner is C(== current container)
|
|
744
|
-
# Within `class <<C`, owner is C.singleton_class
|
|
745
|
-
# but RDoc don't track constants of a singleton class of module
|
|
746
|
-
[(@singleton ? nil : @container), name]
|
|
747
|
-
elsif const_path.empty? # class ::Foo
|
|
748
|
-
[@top_level, name]
|
|
749
|
-
else # `class Foo::Bar` or `class ::Foo::Bar`
|
|
750
|
-
[find_or_create_lexical_module_path(const_path, :module), name]
|
|
751
|
-
end
|
|
752
|
-
end
|
|
730
|
+
mark_container_documentable(container)
|
|
753
731
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
@store.find_class_or_module(full_name)
|
|
732
|
+
if directives && (call_seq, = directives['call-seq'])
|
|
733
|
+
meth.call_seq = call_seq.lines.map(&:chomp).reject(&:empty?).join("\n") if call_seq
|
|
734
|
+
end
|
|
735
|
+
meth.name ||= meth.call_seq[/\A[^()\s]+/] if meth.call_seq
|
|
736
|
+
meth.name ||= 'unknown'
|
|
737
|
+
meth.store = @store
|
|
738
|
+
meth.line = line_no
|
|
739
|
+
meth.visibility = visibility
|
|
740
|
+
meth.params ||= params || '()'
|
|
741
|
+
meth.calls_super = calls_super
|
|
742
|
+
meth.block_params ||= block_params if block_params
|
|
743
|
+
meth.type_signature_lines = type_signature_lines
|
|
744
|
+
# An instance method `initialize` is documented as `::new` unless the
|
|
745
|
+
# :notnew: directive is given
|
|
746
|
+
if method_name == 'initialize' && !singleton
|
|
747
|
+
if meth.dont_rename_initialize
|
|
748
|
+
meth.visibility = :protected
|
|
749
|
+
else
|
|
750
|
+
meth.name = 'new'
|
|
751
|
+
meth.singleton = true
|
|
752
|
+
meth.visibility = :public
|
|
753
|
+
end
|
|
777
754
|
end
|
|
778
|
-
if mod && constant.document_self
|
|
779
|
-
a = owner.add_module_alias(mod, alias_path, constant, @top_level)
|
|
780
|
-
a.store = @store
|
|
781
|
-
a.line = start_line
|
|
782
|
-
record_location(a)
|
|
783
|
-
end
|
|
784
|
-
end
|
|
785
755
|
|
|
786
|
-
|
|
756
|
+
record_location(meth)
|
|
757
|
+
container.add_method(meth)
|
|
758
|
+
token_stream_loader = @colorizer_context.token_stream_loader(node_id) if node_id
|
|
759
|
+
meth.start_collecting_tokens(:ruby, loader: token_stream_loader)
|
|
760
|
+
end
|
|
787
761
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
762
|
+
# Find or create module or class from a given module name using Ruby lexical
|
|
763
|
+
# nesting. If module or class does not exist, creates a module or a class
|
|
764
|
+
# according to `create_mode` argument.
|
|
765
|
+
|
|
766
|
+
def find_or_create_lexical_module_path(module_name, create_mode)
|
|
767
|
+
root_name, *path, name = module_name.split('::')
|
|
768
|
+
add_module = ->(mod, name, mode) {
|
|
769
|
+
created =
|
|
770
|
+
case mode
|
|
771
|
+
when :class
|
|
772
|
+
mod.add_class(NormalClass, name, 'Object').tap { |m| m.store = @store }
|
|
773
|
+
when :module
|
|
774
|
+
mod.add_module(NormalModule, name).tap { |m| m.store = @store }
|
|
775
|
+
end
|
|
776
|
+
# add_class/add_module may return an existing object created by another
|
|
777
|
+
# file (in_files is not empty then), which must not be ignored here.
|
|
778
|
+
# Documentable again when reopened or receiving contents outside the region.
|
|
779
|
+
created.ignore if document_suppressed? && created.in_files.empty?
|
|
780
|
+
created
|
|
781
|
+
}
|
|
782
|
+
if root_name.empty?
|
|
783
|
+
mod = @top_level
|
|
784
|
+
else
|
|
785
|
+
@module_nesting.reverse_each do |nesting, singleton|
|
|
786
|
+
next if singleton
|
|
787
|
+
mod = nesting.get_module_named(root_name)
|
|
788
|
+
break if mod
|
|
789
|
+
# If a constant is found and it is not a module or class, RDoc can't document about it.
|
|
790
|
+
# Return an anonymous module to avoid wrong document creation.
|
|
791
|
+
return NormalModule.new(nil) if nesting.find_constant_named(root_name)
|
|
792
|
+
end
|
|
793
|
+
last_nesting, = @module_nesting.reverse_each.find { |_, singleton| !singleton }
|
|
794
|
+
return mod || add_module.call(last_nesting, root_name, create_mode) unless name
|
|
795
|
+
mod ||= add_module.call(last_nesting, root_name, :module)
|
|
796
|
+
end
|
|
797
|
+
path.each do |name|
|
|
798
|
+
mod = mod.get_module_named(name) || add_module.call(mod, name, :module)
|
|
799
|
+
end
|
|
800
|
+
mod.get_module_named(name) || add_module.call(mod, name, create_mode)
|
|
801
|
+
end
|
|
792
802
|
|
|
793
|
-
|
|
794
|
-
return unless owner
|
|
803
|
+
# Resolves constant path to a full path by searching module nesting
|
|
795
804
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
805
|
+
def resolve_constant_path(constant_path)
|
|
806
|
+
owner_name, path = constant_path.split('::', 2)
|
|
807
|
+
return constant_path if owner_name.empty? # ::Foo, ::Foo::Bar
|
|
808
|
+
mod = nil
|
|
809
|
+
@module_nesting.reverse_each do |nesting, singleton|
|
|
810
|
+
next if singleton
|
|
811
|
+
mod = nesting.get_module_named(owner_name)
|
|
812
|
+
break if mod
|
|
813
|
+
end
|
|
814
|
+
mod ||= @top_level.get_module_named(owner_name)
|
|
815
|
+
[mod.full_name, path].compact.join('::') if mod
|
|
804
816
|
end
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
if
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
817
|
+
|
|
818
|
+
# Returns a pair of owner module and constant name from a given constant path
|
|
819
|
+
# using Ruby lexical nesting. Creates owner module if it does not exist.
|
|
820
|
+
|
|
821
|
+
def find_or_create_lexical_constant_owner_name(constant_path)
|
|
822
|
+
const_path, colon, name = constant_path.rpartition('::')
|
|
823
|
+
if colon.empty? # class Foo
|
|
824
|
+
# Within `class C` or `module C`, owner is C(== current container)
|
|
825
|
+
# Within `class <<C`, owner is C.singleton_class
|
|
826
|
+
# but RDoc don't track constants of a singleton class of module
|
|
827
|
+
[(@singleton ? nil : @container), name]
|
|
828
|
+
elsif const_path.empty? # class ::Foo
|
|
829
|
+
[@top_level, name]
|
|
830
|
+
else # `class Foo::Bar` or `class ::Foo::Bar`
|
|
831
|
+
[find_or_create_lexical_module_path(const_path, :module), name]
|
|
812
832
|
end
|
|
813
833
|
end
|
|
814
|
-
else
|
|
815
|
-
mod = owner.modules_hash[name] || owner.add_module(RDoc::NormalModule, name)
|
|
816
|
-
end
|
|
817
834
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
835
|
+
# Adds a constant
|
|
836
|
+
|
|
837
|
+
def add_constant(constant_name, rhs_name, start_line, end_line, alias_path: nil)
|
|
838
|
+
comment, directives = consecutive_comment(start_line)
|
|
839
|
+
apply_document_control_directive(directives) if directives
|
|
840
|
+
handle_code_object_directives(@container, directives) if directives
|
|
841
|
+
return if document_suppressed?
|
|
842
|
+
|
|
843
|
+
owner, name = find_or_create_lexical_constant_owner_name(constant_name)
|
|
844
|
+
return unless owner
|
|
845
|
+
|
|
846
|
+
constant = Constant.new(name, rhs_name, comment)
|
|
847
|
+
constant.store = @store
|
|
848
|
+
constant.line = start_line
|
|
849
|
+
constant.is_alias_for_path = alias_path
|
|
850
|
+
handle_modifier_directive(constant, start_line)
|
|
851
|
+
handle_modifier_directive(constant, end_line)
|
|
852
|
+
# A constant marked :nodoc: must not make an ignored owner documentable
|
|
853
|
+
mark_container_documentable(owner) if constant.document_self && owner.is_a?(ClassModule)
|
|
854
|
+
record_location(constant)
|
|
855
|
+
owner.add_constant(constant)
|
|
856
|
+
return unless alias_path
|
|
857
|
+
mod =
|
|
858
|
+
if alias_path.start_with?('::')
|
|
859
|
+
@store.find_class_or_module(alias_path)
|
|
860
|
+
else
|
|
861
|
+
full_name = resolve_constant_path(alias_path)
|
|
862
|
+
@store.find_class_or_module(full_name)
|
|
863
|
+
end
|
|
864
|
+
if mod && constant.document_self
|
|
865
|
+
a = owner.add_module_alias(mod, constant, @top_level)
|
|
866
|
+
a.store = @store
|
|
867
|
+
a.line = start_line
|
|
868
|
+
record_location(a)
|
|
869
|
+
end
|
|
870
|
+
end
|
|
826
871
|
|
|
827
|
-
|
|
872
|
+
# Adds module or class
|
|
873
|
+
|
|
874
|
+
def add_module_or_class(module_name, start_line, end_line, is_class: false, superclass_name: nil, superclass_expr: nil)
|
|
875
|
+
comment, directives = consecutive_comment(start_line)
|
|
876
|
+
apply_document_control_directive(directives) if directives
|
|
877
|
+
handle_code_object_directives(@container, directives) if directives
|
|
878
|
+
return unless @container.document_children
|
|
879
|
+
|
|
880
|
+
owner, name = find_or_create_lexical_constant_owner_name(module_name)
|
|
881
|
+
return unless owner
|
|
882
|
+
|
|
883
|
+
if is_class
|
|
884
|
+
# RDoc::NormalClass resolves superclass name despite of the lack of module nesting information.
|
|
885
|
+
# We need to fix it when RDoc::NormalClass resolved to a wrong constant name
|
|
886
|
+
if superclass_name
|
|
887
|
+
superclass_full_path = resolve_constant_path(superclass_name)
|
|
888
|
+
superclass = @store.find_class_or_module(superclass_full_path) if superclass_full_path
|
|
889
|
+
superclass_full_path ||= superclass_name
|
|
890
|
+
superclass_full_path = superclass_full_path.sub(/^::/, '')
|
|
891
|
+
end
|
|
892
|
+
# add_class should be done after resolving superclass
|
|
893
|
+
mod = owner.classes_hash[name]
|
|
894
|
+
unless mod
|
|
895
|
+
# add_class may return an existing class created by another file
|
|
896
|
+
# (in_files is not empty then), which must not be ignored here
|
|
897
|
+
mod = owner.add_class(NormalClass, name, superclass_name || superclass_expr || '::Object')
|
|
898
|
+
mod.ignore if document_suppressed? && mod.in_files.empty?
|
|
899
|
+
end
|
|
828
900
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
901
|
+
# Superclass with the same full path and superclass for BasicObject are not allowed
|
|
902
|
+
if superclass_name && mod.full_name != superclass_full_path && mod.full_name != 'BasicObject'
|
|
903
|
+
if superclass
|
|
904
|
+
mod.superclass = superclass
|
|
905
|
+
elsif mod.superclass.nil? || (mod.superclass.is_a?(String) || mod.superclass.name == 'Object') && mod.superclass != superclass_full_path
|
|
906
|
+
mod.superclass = superclass_full_path
|
|
907
|
+
end
|
|
908
|
+
end
|
|
909
|
+
else
|
|
910
|
+
mod = owner.modules_hash[name]
|
|
911
|
+
unless mod
|
|
912
|
+
mod = owner.add_module(NormalModule, name)
|
|
913
|
+
mod.ignore if document_suppressed? && mod.in_files.empty?
|
|
914
|
+
end
|
|
915
|
+
end
|
|
833
916
|
|
|
834
|
-
|
|
835
|
-
|
|
917
|
+
mod.store = @store
|
|
918
|
+
mod.line = start_line
|
|
919
|
+
handle_modifier_directive(mod, start_line)
|
|
920
|
+
handle_modifier_directive(mod, end_line)
|
|
921
|
+
unless document_suppressed?
|
|
922
|
+
# In a :stopdoc:/:enddoc: region, the container is still created as a
|
|
923
|
+
# namespace but is not recorded to this file nor documented.
|
|
924
|
+
# The body is also visited: an inner :startdoc: re-enables documentation
|
|
925
|
+
# in a :stopdoc: region (not in an :enddoc: region), and nested
|
|
926
|
+
# namespaces need to be created for later promotion from other files
|
|
927
|
+
if mod.ignored?
|
|
928
|
+
# Promotes the owner chain too, unless mod received :nodoc:
|
|
929
|
+
mark_container_documentable(mod)
|
|
930
|
+
else
|
|
931
|
+
# A class/module marked :nodoc: must not make an ignored owner documentable
|
|
932
|
+
mark_container_documentable(owner) if mod.document_self && owner.is_a?(ClassModule)
|
|
933
|
+
record_location(mod)
|
|
934
|
+
end
|
|
935
|
+
mod.add_comment(comment, @top_level) if comment
|
|
936
|
+
end
|
|
937
|
+
mod
|
|
938
|
+
end
|
|
836
939
|
|
|
837
|
-
|
|
838
|
-
sig_lines, doc_lines = lines.partition { |l| l.match?(RBS_SIG_LINE) }
|
|
839
|
-
return nil if sig_lines.empty?
|
|
940
|
+
private
|
|
840
941
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
942
|
+
# Extracts RBS type signature lines (#: ...) from raw comment text.
|
|
943
|
+
# Mutates the input text to remove the extracted lines.
|
|
944
|
+
# Returns an array of extracted type signature lines, or nil if none are
|
|
945
|
+
# found. The array may contain multiple lines for overloaded signatures.
|
|
845
946
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
end
|
|
947
|
+
def extract_type_signature!(text, start_line)
|
|
948
|
+
return nil unless text.include?('#:')
|
|
849
949
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
next if RDoc::RbsHelper.valid_type?(line)
|
|
854
|
-
@options.warn "#{@top_level.relative_name}:#{line_no + i}: invalid RBS type signature: #{line.inspect}"
|
|
855
|
-
end
|
|
856
|
-
end
|
|
950
|
+
lines = text.lines
|
|
951
|
+
sig_lines, doc_lines = lines.partition { |l| l.match?(RBS_SIG_LINE) }
|
|
952
|
+
return nil if sig_lines.empty?
|
|
857
953
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
@store = store
|
|
863
|
-
end
|
|
954
|
+
first_sig_line = start_line + lines.index(sig_lines.first)
|
|
955
|
+
text.replace(doc_lines.join)
|
|
956
|
+
type_signature_lines = sig_lines.map { |l| l.sub(RBS_SIG_LINE, '').strip }.reject(&:empty?)
|
|
957
|
+
return nil if type_signature_lines.empty?
|
|
864
958
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
super
|
|
868
|
-
else
|
|
869
|
-
# Visit with the order in text representation to handle this method comment
|
|
870
|
-
# # comment
|
|
871
|
-
# def f
|
|
872
|
-
# end if call_node
|
|
873
|
-
node.statements.accept(self)
|
|
874
|
-
node.predicate.accept(self)
|
|
959
|
+
warn_invalid_type_signature(type_signature_lines, first_sig_line)
|
|
960
|
+
type_signature_lines
|
|
875
961
|
end
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
case node.name
|
|
883
|
-
when :attr
|
|
884
|
-
_visit_call_attr_reader_writer_accessor(node, 'R')
|
|
885
|
-
when :attr_reader
|
|
886
|
-
_visit_call_attr_reader_writer_accessor(node, 'R')
|
|
887
|
-
when :attr_writer
|
|
888
|
-
_visit_call_attr_reader_writer_accessor(node, 'W')
|
|
889
|
-
when :attr_accessor
|
|
890
|
-
_visit_call_attr_reader_writer_accessor(node, 'RW')
|
|
891
|
-
when :include
|
|
892
|
-
_visit_call_include(node)
|
|
893
|
-
when :extend
|
|
894
|
-
_visit_call_extend(node)
|
|
895
|
-
when :public
|
|
896
|
-
super
|
|
897
|
-
_visit_call_public_private_protected(node, :public)
|
|
898
|
-
when :private
|
|
899
|
-
super
|
|
900
|
-
_visit_call_public_private_protected(node, :private)
|
|
901
|
-
when :protected
|
|
902
|
-
super
|
|
903
|
-
_visit_call_public_private_protected(node, :protected)
|
|
904
|
-
when :private_constant
|
|
905
|
-
_visit_call_private_constant(node)
|
|
906
|
-
when :public_constant
|
|
907
|
-
_visit_call_public_constant(node)
|
|
908
|
-
when :require
|
|
909
|
-
_visit_call_require(node)
|
|
910
|
-
when :alias_method
|
|
911
|
-
_visit_call_alias_method(node)
|
|
912
|
-
when :module_function
|
|
913
|
-
super
|
|
914
|
-
_visit_call_module_function(node)
|
|
915
|
-
when :public_class_method
|
|
916
|
-
super
|
|
917
|
-
_visit_call_public_private_class_method(node, :public)
|
|
918
|
-
when :private_class_method
|
|
919
|
-
super
|
|
920
|
-
_visit_call_public_private_class_method(node, :private)
|
|
921
|
-
else
|
|
922
|
-
super
|
|
962
|
+
|
|
963
|
+
def warn_invalid_type_signature(type_signature_lines, line_no)
|
|
964
|
+
type_signature_lines.each_with_index do |line, i|
|
|
965
|
+
next if RbsHelper.valid_method_type?(line)
|
|
966
|
+
next if RbsHelper.valid_type?(line)
|
|
967
|
+
@options.warn "#{@top_level.relative_name}:#{line_no + i}: invalid RBS type signature: #{line.inspect}"
|
|
923
968
|
end
|
|
924
|
-
else
|
|
925
|
-
super
|
|
926
969
|
end
|
|
927
|
-
end
|
|
928
970
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
end
|
|
971
|
+
class RDocVisitor < Prism::Visitor # :nodoc:
|
|
972
|
+
def initialize(scanner, top_level, store)
|
|
973
|
+
@scanner = scanner
|
|
974
|
+
@top_level = top_level
|
|
975
|
+
@store = store
|
|
976
|
+
end
|
|
936
977
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
978
|
+
def visit_if_node(node)
|
|
979
|
+
if node.end_keyword
|
|
980
|
+
super
|
|
981
|
+
else
|
|
982
|
+
# Visit with the order in text representation to handle this method comment
|
|
983
|
+
# # comment
|
|
984
|
+
# def f
|
|
985
|
+
# end if call_node
|
|
986
|
+
node.statements.accept(self)
|
|
987
|
+
node.predicate.accept(self)
|
|
988
|
+
end
|
|
989
|
+
end
|
|
990
|
+
alias visit_unless_node visit_if_node
|
|
991
|
+
|
|
992
|
+
def visit_call_node(node)
|
|
993
|
+
@scanner.process_comments_until(node.location.start_line - 1)
|
|
994
|
+
if node.receiver.nil?
|
|
995
|
+
case node.name
|
|
996
|
+
when :attr
|
|
997
|
+
_visit_call_attr_reader_writer_accessor(node, 'R')
|
|
998
|
+
when :attr_reader
|
|
999
|
+
_visit_call_attr_reader_writer_accessor(node, 'R')
|
|
1000
|
+
when :attr_writer
|
|
1001
|
+
_visit_call_attr_reader_writer_accessor(node, 'W')
|
|
1002
|
+
when :attr_accessor
|
|
1003
|
+
_visit_call_attr_reader_writer_accessor(node, 'RW')
|
|
1004
|
+
when :include
|
|
1005
|
+
_visit_call_include(node)
|
|
1006
|
+
when :extend
|
|
1007
|
+
_visit_call_extend(node)
|
|
1008
|
+
when :public
|
|
1009
|
+
super
|
|
1010
|
+
_visit_call_public_private_protected(node, :public)
|
|
1011
|
+
when :private
|
|
1012
|
+
super
|
|
1013
|
+
_visit_call_public_private_protected(node, :private)
|
|
1014
|
+
when :protected
|
|
1015
|
+
super
|
|
1016
|
+
_visit_call_public_private_protected(node, :protected)
|
|
1017
|
+
when :private_constant
|
|
1018
|
+
_visit_call_private_constant(node)
|
|
1019
|
+
when :public_constant
|
|
1020
|
+
_visit_call_public_constant(node)
|
|
1021
|
+
when :require
|
|
1022
|
+
_visit_call_require(node)
|
|
1023
|
+
when :alias_method
|
|
1024
|
+
_visit_call_alias_method(node)
|
|
1025
|
+
when :module_function
|
|
1026
|
+
super
|
|
1027
|
+
_visit_call_module_function(node)
|
|
1028
|
+
when :public_class_method
|
|
1029
|
+
super
|
|
1030
|
+
_visit_call_public_private_class_method(node, :public)
|
|
1031
|
+
when :private_class_method
|
|
1032
|
+
super
|
|
1033
|
+
_visit_call_public_private_class_method(node, :private)
|
|
1034
|
+
else
|
|
1035
|
+
super
|
|
1036
|
+
end
|
|
1037
|
+
else
|
|
1038
|
+
super
|
|
1039
|
+
end
|
|
1040
|
+
end
|
|
943
1041
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
@scanner.with_container(mod) do
|
|
951
|
-
node.body&.accept(self)
|
|
952
|
-
@scanner.process_comments_until(node.location.end_line)
|
|
1042
|
+
def visit_block_node(node)
|
|
1043
|
+
@scanner.with_in_proc_block do
|
|
1044
|
+
# include, extend and method definition inside block are not documentable.
|
|
1045
|
+
# visibility methods and attribute definition methods should be ignored inside block.
|
|
1046
|
+
super
|
|
1047
|
+
end
|
|
953
1048
|
end
|
|
954
|
-
else
|
|
955
|
-
@scanner.skip_comments_until(node.location.end_line)
|
|
956
|
-
end
|
|
957
|
-
end
|
|
958
1049
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
superclass_expr = node.superclass.slice if node.superclass && !superclass_name
|
|
965
|
-
class_name = constant_path_string(node.constant_path)
|
|
966
|
-
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
|
|
967
|
-
if klass
|
|
968
|
-
@scanner.with_container(klass) do
|
|
969
|
-
node.body&.accept(self)
|
|
970
|
-
@scanner.process_comments_until(node.location.end_line)
|
|
1050
|
+
def visit_alias_method_node(node)
|
|
1051
|
+
return if @scanner.in_proc_block
|
|
1052
|
+
@scanner.process_comments_until(node.location.start_line - 1)
|
|
1053
|
+
return unless node.old_name.is_a?(Prism::SymbolNode) && node.new_name.is_a?(Prism::SymbolNode)
|
|
1054
|
+
@scanner.add_alias_method(node.old_name.value.to_s, node.new_name.value.to_s, node.location.start_line)
|
|
971
1055
|
end
|
|
972
|
-
else
|
|
973
|
-
@scanner.skip_comments_until(node.location.end_line)
|
|
974
|
-
end
|
|
975
|
-
end
|
|
976
1056
|
|
|
977
|
-
|
|
978
|
-
|
|
1057
|
+
def visit_module_node(node)
|
|
1058
|
+
node.constant_path.accept(self)
|
|
1059
|
+
@scanner.process_comments_until(node.location.start_line - 1)
|
|
1060
|
+
module_name = constant_path_string(node.constant_path)
|
|
1061
|
+
mod = @scanner.add_module_or_class(module_name, node.location.start_line, node.location.end_line) if module_name
|
|
1062
|
+
if mod
|
|
1063
|
+
@scanner.with_container(mod) do
|
|
1064
|
+
node.body&.accept(self)
|
|
1065
|
+
@scanner.process_comments_until(node.location.end_line)
|
|
1066
|
+
end
|
|
1067
|
+
else
|
|
1068
|
+
@scanner.skip_comments_until(node.location.end_line)
|
|
1069
|
+
end
|
|
1070
|
+
end
|
|
979
1071
|
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1072
|
+
def visit_class_node(node)
|
|
1073
|
+
node.constant_path.accept(self)
|
|
1074
|
+
node.superclass&.accept(self)
|
|
1075
|
+
@scanner.process_comments_until(node.location.start_line - 1)
|
|
1076
|
+
superclass_name = constant_path_string(node.superclass) if node.superclass
|
|
1077
|
+
superclass_expr = node.superclass.slice if node.superclass && !superclass_name
|
|
1078
|
+
class_name = constant_path_string(node.constant_path)
|
|
1079
|
+
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
|
|
1080
|
+
if klass
|
|
1081
|
+
@scanner.with_container(klass) do
|
|
1082
|
+
node.body&.accept(self)
|
|
1083
|
+
@scanner.process_comments_until(node.location.end_line)
|
|
1084
|
+
end
|
|
1085
|
+
else
|
|
1086
|
+
@scanner.skip_comments_until(node.location.end_line)
|
|
1087
|
+
end
|
|
1088
|
+
end
|
|
985
1089
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1090
|
+
def visit_singleton_class_node(node)
|
|
1091
|
+
# A comment linked to the `class << ...` line (e.g. a document control
|
|
1092
|
+
# directive) belongs to the enclosing scope, not to the singleton scope
|
|
1093
|
+
@scanner.process_comments_until(node.location.start_line)
|
|
1094
|
+
|
|
1095
|
+
if @scanner.has_modifier_nodoc?(node.location.start_line)
|
|
1096
|
+
# Skip visiting inside the singleton class. Also skips creation of node.expression as a module
|
|
1097
|
+
@scanner.skip_comments_until(node.location.end_line)
|
|
1098
|
+
return
|
|
1099
|
+
end
|
|
1100
|
+
|
|
1101
|
+
expression = node.expression
|
|
1102
|
+
expression = expression.body.body.first if expression.is_a?(Prism::ParenthesesNode) && expression.body&.body&.size == 1
|
|
1103
|
+
|
|
1104
|
+
case expression
|
|
1105
|
+
when Prism::ConstantWriteNode
|
|
1106
|
+
# Accept `class << (NameErrorCheckers = Object.new)` as a module which is not actually a module
|
|
1107
|
+
mod = @scanner.container.add_module(NormalModule, expression.name.to_s)
|
|
1108
|
+
mod.ignore if @scanner.document_suppressed? && mod.in_files.empty?
|
|
1109
|
+
when Prism::ConstantPathNode, Prism::ConstantReadNode
|
|
1110
|
+
expression_name = constant_path_string(expression)
|
|
1111
|
+
# If a constant_path does not exist, RDoc creates a module
|
|
1112
|
+
mod = @scanner.find_or_create_lexical_module_path(expression_name, :module) if expression_name
|
|
1113
|
+
when Prism::SelfNode
|
|
1114
|
+
mod = @scanner.container if @scanner.container != @top_level
|
|
1115
|
+
end
|
|
1116
|
+
expression.accept(self)
|
|
1117
|
+
if mod
|
|
1118
|
+
@scanner.with_container(mod, singleton: true) do
|
|
1119
|
+
node.body&.accept(self)
|
|
1120
|
+
@scanner.process_comments_until(node.location.end_line)
|
|
1121
|
+
end
|
|
1122
|
+
else
|
|
1123
|
+
@scanner.skip_comments_until(node.location.end_line)
|
|
1124
|
+
end
|
|
1005
1125
|
end
|
|
1006
|
-
else
|
|
1007
|
-
@scanner.skip_comments_until(node.location.end_line)
|
|
1008
|
-
end
|
|
1009
|
-
end
|
|
1010
1126
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1127
|
+
def visit_def_node(node)
|
|
1128
|
+
start_line = node.location.start_line
|
|
1129
|
+
args_end_line = node.parameters&.location&.end_line || start_line
|
|
1130
|
+
end_line = node.location.end_line
|
|
1131
|
+
@scanner.process_comments_until(start_line - 1)
|
|
1016
1132
|
|
|
1017
|
-
|
|
1133
|
+
return if @scanner.in_proc_block
|
|
1018
1134
|
|
|
1019
|
-
case node.receiver
|
|
1020
|
-
when Prism::NilNode, Prism::TrueNode, Prism::FalseNode
|
|
1021
|
-
visibility = :public
|
|
1022
|
-
singleton = false
|
|
1023
|
-
receiver_name =
|
|
1024
1135
|
case node.receiver
|
|
1025
|
-
when Prism::NilNode
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1136
|
+
when Prism::NilNode, Prism::TrueNode, Prism::FalseNode
|
|
1137
|
+
visibility = :public
|
|
1138
|
+
singleton = false
|
|
1139
|
+
receiver_name =
|
|
1140
|
+
case node.receiver
|
|
1141
|
+
when Prism::NilNode
|
|
1142
|
+
'NilClass'
|
|
1143
|
+
when Prism::TrueNode
|
|
1144
|
+
'TrueClass'
|
|
1145
|
+
when Prism::FalseNode
|
|
1146
|
+
'FalseClass'
|
|
1147
|
+
end
|
|
1148
|
+
receiver_fallback_type = :class
|
|
1149
|
+
when Prism::SelfNode
|
|
1150
|
+
# singleton method of a singleton class is not documentable
|
|
1151
|
+
return if @scanner.singleton
|
|
1152
|
+
visibility = :public
|
|
1153
|
+
singleton = true
|
|
1154
|
+
when Prism::ConstantReadNode, Prism::ConstantPathNode
|
|
1155
|
+
visibility = :public
|
|
1156
|
+
singleton = true
|
|
1157
|
+
receiver_name = constant_path_string(node.receiver)
|
|
1158
|
+
receiver_fallback_type = :module
|
|
1159
|
+
return unless receiver_name
|
|
1160
|
+
when nil
|
|
1161
|
+
visibility = @scanner.visibility
|
|
1162
|
+
singleton = @scanner.singleton
|
|
1163
|
+
else
|
|
1164
|
+
# `def (unknown expression).method_name` is not documentable
|
|
1165
|
+
return
|
|
1031
1166
|
end
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
name = node.name.to_s
|
|
1052
|
-
params, block_params, calls_super = MethodSignatureVisitor.scan_signature(node)
|
|
1053
|
-
tokens = @scanner.syntax_highlighted_tokens(node)
|
|
1054
|
-
|
|
1055
|
-
@scanner.add_method(
|
|
1056
|
-
name,
|
|
1057
|
-
receiver_name: receiver_name,
|
|
1058
|
-
receiver_fallback_type: receiver_fallback_type,
|
|
1059
|
-
visibility: visibility,
|
|
1060
|
-
singleton: singleton,
|
|
1061
|
-
params: params,
|
|
1062
|
-
block_params: block_params,
|
|
1063
|
-
calls_super: calls_super,
|
|
1064
|
-
tokens: tokens,
|
|
1065
|
-
start_line: start_line,
|
|
1066
|
-
args_end_line: args_end_line,
|
|
1067
|
-
end_line: end_line
|
|
1068
|
-
)
|
|
1069
|
-
ensure
|
|
1070
|
-
@scanner.skip_comments_until(end_line)
|
|
1071
|
-
end
|
|
1167
|
+
name = node.name.to_s
|
|
1168
|
+
params, block_params, calls_super = MethodSignatureVisitor.scan_signature(node)
|
|
1169
|
+
@scanner.add_method(
|
|
1170
|
+
name,
|
|
1171
|
+
receiver_name: receiver_name,
|
|
1172
|
+
receiver_fallback_type: receiver_fallback_type,
|
|
1173
|
+
visibility: visibility,
|
|
1174
|
+
singleton: singleton,
|
|
1175
|
+
params: params,
|
|
1176
|
+
block_params: block_params,
|
|
1177
|
+
calls_super: calls_super,
|
|
1178
|
+
node_id: node.node_id,
|
|
1179
|
+
start_line: start_line,
|
|
1180
|
+
args_end_line: args_end_line,
|
|
1181
|
+
end_line: end_line
|
|
1182
|
+
)
|
|
1183
|
+
ensure
|
|
1184
|
+
@scanner.skip_comments_until(end_line)
|
|
1185
|
+
end
|
|
1072
1186
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1187
|
+
def visit_constant_path_write_node(node)
|
|
1188
|
+
@scanner.process_comments_until(node.location.start_line - 1)
|
|
1189
|
+
path = constant_path_string(node.target)
|
|
1190
|
+
return unless path
|
|
1191
|
+
|
|
1192
|
+
alias_path = constant_path_string(node.value)
|
|
1193
|
+
@scanner.add_constant(
|
|
1194
|
+
path,
|
|
1195
|
+
alias_path || node.value.slice,
|
|
1196
|
+
node.location.start_line,
|
|
1197
|
+
node.location.end_line,
|
|
1198
|
+
alias_path: alias_path
|
|
1199
|
+
)
|
|
1200
|
+
@scanner.skip_comments_until(node.location.end_line)
|
|
1201
|
+
# Do not traverse rhs not to document `A::B = Struct.new{def undocumentable_method; end}`
|
|
1202
|
+
end
|
|
1089
1203
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1204
|
+
def visit_constant_write_node(node)
|
|
1205
|
+
@scanner.process_comments_until(node.location.start_line - 1)
|
|
1206
|
+
alias_path = constant_path_string(node.value)
|
|
1207
|
+
@scanner.add_constant(
|
|
1208
|
+
node.name.to_s,
|
|
1209
|
+
alias_path || node.value.slice,
|
|
1210
|
+
node.location.start_line,
|
|
1211
|
+
node.location.end_line,
|
|
1212
|
+
alias_path: alias_path
|
|
1213
|
+
)
|
|
1214
|
+
@scanner.skip_comments_until(node.location.end_line)
|
|
1215
|
+
# Do not traverse rhs not to document `A = Struct.new{def undocumentable_method; end}`
|
|
1216
|
+
end
|
|
1103
1217
|
|
|
1104
|
-
|
|
1218
|
+
private
|
|
1105
1219
|
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1220
|
+
def constant_arguments_names(call_node)
|
|
1221
|
+
return unless call_node.arguments
|
|
1222
|
+
names = call_node.arguments.arguments.map { |arg| constant_path_string(arg) }
|
|
1223
|
+
names.all? ? names : nil
|
|
1224
|
+
end
|
|
1111
1225
|
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
arguments_node.arguments.map { |arg| arg.value.to_sym }
|
|
1116
|
-
end
|
|
1226
|
+
def call_node_name_arguments(call_node)
|
|
1227
|
+
@scanner.call_node_name_arguments(call_node)
|
|
1228
|
+
end
|
|
1117
1229
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
if symbols
|
|
1123
|
-
# module_function :foo, :bar
|
|
1124
|
-
return symbols.map(&:to_s)
|
|
1125
|
-
else
|
|
1126
|
-
return unless arguments_node.arguments.size == 1
|
|
1127
|
-
arg = arguments_node.arguments.first
|
|
1128
|
-
return unless arg.is_a?(Prism::DefNode)
|
|
1129
|
-
|
|
1130
|
-
if singleton
|
|
1131
|
-
# `private_class_method def foo; end` `private_class_method def not_self.foo; end` should be ignored
|
|
1132
|
-
return unless arg.receiver.is_a?(Prism::SelfNode)
|
|
1133
|
-
else
|
|
1134
|
-
# `module_function def something.foo` should be ignored
|
|
1135
|
-
return if arg.receiver
|
|
1230
|
+
def symbol_arguments(call_node)
|
|
1231
|
+
arguments_node = call_node.arguments
|
|
1232
|
+
return unless arguments_node && arguments_node.arguments.all? { |arg| arg.is_a?(Prism::SymbolNode)}
|
|
1233
|
+
arguments_node.arguments.map { |arg| arg.value.to_sym }
|
|
1136
1234
|
end
|
|
1137
|
-
# `module_function def foo; end` or `private_class_method def self.foo; end`
|
|
1138
|
-
[arg.name.to_s]
|
|
1139
|
-
end
|
|
1140
|
-
end
|
|
1141
1235
|
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1236
|
+
def visibility_method_arguments(call_node, singleton:)
|
|
1237
|
+
arguments_node = call_node.arguments
|
|
1238
|
+
return unless arguments_node
|
|
1239
|
+
names = call_node_name_arguments(call_node)
|
|
1240
|
+
if names
|
|
1241
|
+
# module_function :foo, "bar"
|
|
1242
|
+
return names
|
|
1243
|
+
else
|
|
1244
|
+
return unless arguments_node.arguments.size == 1
|
|
1245
|
+
arg = arguments_node.arguments.first
|
|
1246
|
+
return unless arg.is_a?(Prism::DefNode)
|
|
1247
|
+
|
|
1248
|
+
if singleton
|
|
1249
|
+
# `private_class_method def foo; end` `private_class_method def not_self.foo; end` should be ignored
|
|
1250
|
+
return unless arg.receiver.is_a?(Prism::SelfNode)
|
|
1251
|
+
else
|
|
1252
|
+
# `module_function def something.foo` should be ignored
|
|
1253
|
+
return if arg.receiver
|
|
1254
|
+
end
|
|
1255
|
+
# `module_function def foo; end` or `private_class_method def self.foo; end`
|
|
1256
|
+
[arg.name.to_s]
|
|
1257
|
+
end
|
|
1258
|
+
end
|
|
1151
1259
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1260
|
+
def constant_path_string(node)
|
|
1261
|
+
case node
|
|
1262
|
+
when Prism::ConstantReadNode
|
|
1263
|
+
node.name.to_s
|
|
1264
|
+
when Prism::ConstantPathNode
|
|
1265
|
+
parent_name = node.parent ? constant_path_string(node.parent) : ''
|
|
1266
|
+
"#{parent_name}::#{node.name}" if parent_name
|
|
1267
|
+
end
|
|
1268
|
+
end
|
|
1158
1269
|
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1270
|
+
def _visit_call_require(call_node)
|
|
1271
|
+
return if @scanner.document_suppressed?
|
|
1272
|
+
return unless call_node.arguments&.arguments&.size == 1
|
|
1273
|
+
arg = call_node.arguments.arguments.first
|
|
1274
|
+
return unless arg.is_a?(Prism::StringNode)
|
|
1275
|
+
@scanner.container.add_require(Require.new(arg.unescaped, nil))
|
|
1276
|
+
end
|
|
1164
1277
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1278
|
+
def _visit_call_module_function(call_node)
|
|
1279
|
+
return if @scanner.in_proc_block || @scanner.singleton
|
|
1280
|
+
names = visibility_method_arguments(call_node, singleton: false)&.map(&:to_s)
|
|
1281
|
+
@scanner.change_method_to_module_function(names) if names
|
|
1282
|
+
end
|
|
1170
1283
|
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
else # `public :foo, :bar`, `private def foo; end`
|
|
1177
|
-
names = visibility_method_arguments(call_node, singleton: false)
|
|
1178
|
-
@scanner.change_method_visibility(names, visibility) if names
|
|
1179
|
-
end
|
|
1180
|
-
end
|
|
1284
|
+
def _visit_call_public_private_class_method(call_node, visibility)
|
|
1285
|
+
return if @scanner.in_proc_block || @scanner.singleton
|
|
1286
|
+
names = visibility_method_arguments(call_node, singleton: true)
|
|
1287
|
+
@scanner.change_method_visibility(names, visibility, singleton: true) if names
|
|
1288
|
+
end
|
|
1181
1289
|
|
|
1182
|
-
|
|
1183
|
-
|
|
1290
|
+
def _visit_call_public_private_protected(call_node, visibility)
|
|
1291
|
+
return if @scanner.in_proc_block
|
|
1292
|
+
arguments_node = call_node.arguments
|
|
1293
|
+
if arguments_node.nil? # `public` `private`
|
|
1294
|
+
@scanner.visibility = visibility
|
|
1295
|
+
else # `public :foo, :bar`, `private def foo; end`
|
|
1296
|
+
names = visibility_method_arguments(call_node, singleton: false)
|
|
1297
|
+
@scanner.change_method_visibility(names, visibility) if names
|
|
1298
|
+
end
|
|
1299
|
+
end
|
|
1184
1300
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
@scanner.add_alias_method(old_name.to_s, new_name.to_s, call_node.location.start_line)
|
|
1188
|
-
end
|
|
1301
|
+
def _visit_call_alias_method(call_node)
|
|
1302
|
+
return if @scanner.in_proc_block
|
|
1189
1303
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1304
|
+
new_name, old_name, *rest = symbol_arguments(call_node)
|
|
1305
|
+
return unless old_name && new_name && rest.empty?
|
|
1306
|
+
@scanner.add_alias_method(old_name.to_s, new_name.to_s, call_node.location.start_line)
|
|
1307
|
+
end
|
|
1192
1308
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
return unless names
|
|
1309
|
+
def _visit_call_include(call_node)
|
|
1310
|
+
return if @scanner.in_proc_block
|
|
1196
1311
|
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
@scanner.add_includes(names, line_no)
|
|
1201
|
-
end
|
|
1202
|
-
end
|
|
1312
|
+
names = constant_arguments_names(call_node)
|
|
1313
|
+
line_no = call_node.location.start_line
|
|
1314
|
+
return unless names
|
|
1203
1315
|
|
|
1204
|
-
|
|
1205
|
-
|
|
1316
|
+
if @scanner.singleton
|
|
1317
|
+
@scanner.add_extends(names, line_no)
|
|
1318
|
+
else
|
|
1319
|
+
@scanner.add_includes(names, line_no)
|
|
1320
|
+
end
|
|
1321
|
+
end
|
|
1206
1322
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
end
|
|
1323
|
+
def _visit_call_extend(call_node)
|
|
1324
|
+
return if @scanner.in_proc_block
|
|
1210
1325
|
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
@scanner.container.set_constant_visibility_for(names.map(&:to_s), :public) if names
|
|
1215
|
-
end
|
|
1326
|
+
names = constant_arguments_names(call_node)
|
|
1327
|
+
@scanner.add_extends(names, call_node.location.start_line) if names && !@scanner.singleton
|
|
1328
|
+
end
|
|
1216
1329
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1330
|
+
def _visit_call_public_constant(call_node)
|
|
1331
|
+
return if @scanner.in_proc_block || @scanner.singleton
|
|
1332
|
+
names = call_node_name_arguments(call_node)
|
|
1333
|
+
@scanner.container.set_constant_visibility_for(names, :public) if names
|
|
1334
|
+
end
|
|
1222
1335
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1336
|
+
def _visit_call_private_constant(call_node)
|
|
1337
|
+
return if @scanner.in_proc_block || @scanner.singleton
|
|
1338
|
+
names = call_node_name_arguments(call_node)
|
|
1339
|
+
@scanner.container.set_constant_visibility_for(names, :private) if names
|
|
1340
|
+
end
|
|
1228
1341
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
def_node.body&.accept(visitor)
|
|
1234
|
-
params = "(#{def_node.parameters&.slice})"
|
|
1235
|
-
block_params = visitor.yields.first
|
|
1236
|
-
[params, block_params, visitor.calls_super]
|
|
1342
|
+
def _visit_call_attr_reader_writer_accessor(call_node, rw)
|
|
1343
|
+
return if @scanner.in_proc_block
|
|
1344
|
+
names = call_node_name_arguments(call_node)
|
|
1345
|
+
@scanner.add_attributes(names, rw, call_node.location.start_line) if names
|
|
1237
1346
|
end
|
|
1238
|
-
end
|
|
1239
1347
|
|
|
1240
|
-
|
|
1348
|
+
class MethodSignatureVisitor < Prism::Visitor # :nodoc:
|
|
1349
|
+
class << self
|
|
1350
|
+
def scan_signature(def_node)
|
|
1351
|
+
visitor = new
|
|
1352
|
+
def_node.body&.accept(visitor)
|
|
1353
|
+
params = "(#{def_node.parameters&.slice})"
|
|
1354
|
+
block_params = visitor.yields.first
|
|
1355
|
+
[params, block_params, visitor.calls_super]
|
|
1356
|
+
end
|
|
1357
|
+
end
|
|
1241
1358
|
|
|
1242
|
-
|
|
1243
|
-
@params = nil
|
|
1244
|
-
@calls_super = false
|
|
1245
|
-
@yields = []
|
|
1246
|
-
end
|
|
1359
|
+
attr_reader :params, :yields, :calls_super
|
|
1247
1360
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1361
|
+
def initialize
|
|
1362
|
+
@params = nil
|
|
1363
|
+
@calls_super = false
|
|
1364
|
+
@yields = []
|
|
1365
|
+
end
|
|
1251
1366
|
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1367
|
+
def visit_def_node(node)
|
|
1368
|
+
# stop traverse inside nested def
|
|
1369
|
+
end
|
|
1255
1370
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
end
|
|
1371
|
+
def visit_yield_node(node)
|
|
1372
|
+
@yields << (node.arguments&.slice || '')
|
|
1373
|
+
end
|
|
1260
1374
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1375
|
+
def visit_super_node(node)
|
|
1376
|
+
@calls_super = true
|
|
1377
|
+
super
|
|
1378
|
+
end
|
|
1379
|
+
|
|
1380
|
+
def visit_forwarding_super_node(node)
|
|
1381
|
+
@calls_super = true
|
|
1382
|
+
end
|
|
1383
|
+
end
|
|
1263
1384
|
end
|
|
1264
1385
|
end
|
|
1265
1386
|
end
|