rdoc 7.2.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 +4 -7
- data/LICENSE.rdoc +4 -0
- data/README.md +43 -2
- data/RI.md +75 -75
- data/doc/markup_reference/markdown.md +104 -3
- data/exe/rdoc +2 -2
- data/lib/rdoc/code_object/alias.rb +70 -74
- data/lib/rdoc/code_object/any_method.rb +305 -298
- data/lib/rdoc/code_object/attr.rb +150 -143
- data/lib/rdoc/code_object/class_module.rb +801 -765
- data/lib/rdoc/code_object/constant.rb +178 -150
- data/lib/rdoc/code_object/context/section.rb +133 -160
- data/lib/rdoc/code_object/context.rb +925 -952
- 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 +325 -324
- 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 -213
- data/lib/rdoc/code_object.rb +305 -305
- data/lib/rdoc/comment.rb +274 -337
- data/lib/rdoc/cross_reference.rb +194 -212
- 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 +165 -140
- data/lib/rdoc/generator/darkfish.rb +647 -631
- data/lib/rdoc/generator/json_index.rb +233 -229
- data/lib/rdoc/generator/markup.rb +165 -122
- 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 +538 -0
- data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_footer.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_head.rhtml +11 -11
- data/lib/rdoc/generator/template/aliki/_header.rhtml +29 -44
- data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +8 -6
- data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +8 -6
- data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +2 -2
- data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +4 -4
- data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/class.rhtml +56 -46
- data/lib/rdoc/generator/template/aliki/css/rdoc.css +538 -283
- data/lib/rdoc/generator/template/aliki/index.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/js/aliki.js +80 -102
- data/lib/rdoc/generator/template/aliki/page.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +1 -1
- data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +2 -2
- data/lib/rdoc/generator/template/darkfish/_footer.rhtml +1 -1
- data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +8 -6
- data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +8 -6
- data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +1 -1
- data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +1 -1
- data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +1 -1
- data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +5 -5
- data/lib/rdoc/generator/template/darkfish/class.rhtml +18 -21
- data/lib/rdoc/generator/template/darkfish/css/rdoc.css +0 -1
- data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +3 -3
- 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 +30 -21
- data/lib/rdoc/markdown.rb +329 -151
- data/lib/rdoc/markup/block_quote.rb +12 -8
- data/lib/rdoc/markup/document.rb +127 -123
- data/lib/rdoc/markup/formatter.rb +215 -221
- data/lib/rdoc/markup/heading.rb +1 -4
- 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 +284 -305
- 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 +600 -493
- data/lib/rdoc/markup/to_html_crossref.rb +221 -191
- data/lib/rdoc/markup/to_html_snippet.rb +232 -227
- data/lib/rdoc/markup/to_joined_paragraph.rb +40 -41
- 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 +83 -86
- data/lib/rdoc/markup/verbatim.rb +62 -58
- data/lib/rdoc/markup.rb +198 -196
- data/lib/rdoc/options.rb +1063 -1076
- data/lib/rdoc/parser/c.rb +1039 -1036
- data/lib/rdoc/parser/changelog.rb +319 -315
- data/lib/rdoc/parser/markdown.rb +17 -13
- data/lib/rdoc/parser/rbs.rb +279 -0
- data/lib/rdoc/parser/rd.rb +17 -13
- data/lib/rdoc/parser/ruby.rb +1231 -2222
- data/lib/rdoc/parser/ruby_colorizer.rb +303 -0
- data/lib/rdoc/parser/simple.rb +31 -27
- data/lib/rdoc/parser/text.rb +12 -8
- data/lib/rdoc/parser.rb +230 -221
- data/lib/rdoc/rbs_helper.rb +186 -0
- data/lib/rdoc/rd/inline.rb +57 -53
- data/lib/rdoc/rd.rb +90 -88
- data/lib/rdoc/rdoc.rb +547 -366
- data/lib/rdoc/ri/driver.rb +1141 -1130
- data/lib/rdoc/ri/formatter.rb +7 -3
- data/lib/rdoc/ri/paths.rb +140 -136
- data/lib/rdoc/ri/servlet.rb +456 -0
- data/lib/rdoc/ri/store.rb +4 -2
- data/lib/rdoc/ri/task.rb +55 -51
- data/lib/rdoc/ri.rb +14 -11
- data/lib/rdoc/rubygems_hook.rb +194 -192
- data/lib/rdoc/server.rb +462 -0
- 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 +363 -338
- data/lib/rdoc/store.rb +919 -725
- data/lib/rdoc/task.rb +260 -255
- data/lib/rdoc/text.rb +130 -245
- data/lib/rdoc/token_stream.rb +101 -115
- data/lib/rdoc/tom_doc.rb +203 -201
- data/lib/rdoc/version.rb +1 -1
- data/lib/rdoc.rb +35 -7
- data/lib/rubygems_plugin.rb +2 -11
- data/rdoc-logo.svg +43 -0
- data/rdoc.gemspec +6 -4
- metadata +36 -20
- data/lib/rdoc/code_object/anon_class.rb +0 -10
- data/lib/rdoc/code_object/ghost_method.rb +0 -6
- data/lib/rdoc/code_object/meta_method.rb +0 -6
- data/lib/rdoc/markdown/literals.kpeg +0 -21
- data/lib/rdoc/markdown/literals.rb +0 -454
- data/lib/rdoc/parser/prism_ruby.rb +0 -1112
- data/lib/rdoc/parser/ripper_state_lex.rb +0 -302
- data/lib/rdoc/parser/ruby_tools.rb +0 -163
- data/lib/rdoc/servlet.rb +0 -452
data/lib/rdoc/parser/ruby.rb
CHANGED
|
@@ -1,2378 +1,1387 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
##
|
|
3
|
-
# This file contains stuff stolen outright from:
|
|
4
|
-
#
|
|
5
|
-
# rtags.rb -
|
|
6
|
-
# ruby-lex.rb - ruby lexcal analyzer
|
|
7
|
-
# ruby-token.rb - ruby tokens
|
|
8
|
-
# by Keiju ISHITSUKA (Nippon Rational Inc.)
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
if ENV['RDOC_USE_PRISM_PARSER']
|
|
12
|
-
require 'rdoc/parser/prism_ruby'
|
|
13
|
-
RDoc::Parser.const_set(:Ruby, RDoc::Parser::PrismRuby)
|
|
14
|
-
puts "========================================================================="
|
|
15
|
-
puts "RDoc is using the experimental Prism parser to generate the documentation"
|
|
16
|
-
puts "========================================================================="
|
|
17
|
-
return
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
require 'ripper'
|
|
21
|
-
require_relative 'ripper_state_lex'
|
|
22
|
-
|
|
23
|
-
##
|
|
24
|
-
# Extracts code elements from a source file returning a TopLevel object
|
|
25
|
-
# containing the constituent file elements.
|
|
26
|
-
#
|
|
27
|
-
# This file is based on rtags
|
|
28
|
-
#
|
|
29
|
-
# RubyParser understands how to document:
|
|
30
|
-
# * classes
|
|
31
|
-
# * modules
|
|
32
|
-
# * methods
|
|
33
|
-
# * constants
|
|
34
|
-
# * aliases
|
|
35
|
-
# * private, public, protected
|
|
36
|
-
# * private_class_function, public_class_function
|
|
37
|
-
# * private_constant, public_constant
|
|
38
|
-
# * module_function
|
|
39
|
-
# * attr, attr_reader, attr_writer, attr_accessor
|
|
40
|
-
# * extra accessors given on the command line
|
|
41
|
-
# * metaprogrammed methods
|
|
42
|
-
# * require
|
|
43
|
-
# * include
|
|
44
|
-
#
|
|
45
|
-
# == Method Arguments
|
|
46
|
-
#
|
|
47
|
-
#--
|
|
48
|
-
# NOTE: I don't think this works, needs tests, remove the paragraph following
|
|
49
|
-
# this block when known to work
|
|
50
|
-
#
|
|
51
|
-
# The parser extracts the arguments from the method definition. You can
|
|
52
|
-
# override this with a custom argument definition using the :args: directive:
|
|
53
|
-
#
|
|
54
|
-
# ##
|
|
55
|
-
# # This method tries over and over until it is tired
|
|
56
|
-
#
|
|
57
|
-
# def go_go_go(thing_to_try, tries = 10) # :args: thing_to_try
|
|
58
|
-
# puts thing_to_try
|
|
59
|
-
# go_go_go thing_to_try, tries - 1
|
|
60
|
-
# end
|
|
61
|
-
#
|
|
62
|
-
# If you have a more-complex set of overrides you can use the :call-seq:
|
|
63
|
-
# directive:
|
|
64
|
-
#++
|
|
65
|
-
#
|
|
66
|
-
# The parser extracts the arguments from the method definition. You can
|
|
67
|
-
# override this with a custom argument definition using the :call-seq:
|
|
68
|
-
# directive:
|
|
69
|
-
#
|
|
70
|
-
# ##
|
|
71
|
-
# # This method can be called with a range or an offset and length
|
|
72
|
-
# #
|
|
73
|
-
# # :call-seq:
|
|
74
|
-
# # my_method(Range)
|
|
75
|
-
# # my_method(offset, length)
|
|
76
|
-
#
|
|
77
|
-
# def my_method(*args)
|
|
78
|
-
# end
|
|
79
|
-
#
|
|
80
|
-
# The parser extracts +yield+ expressions from method bodies to gather the
|
|
81
|
-
# yielded argument names. If your method manually calls a block instead of
|
|
82
|
-
# yielding or you want to override the discovered argument names use
|
|
83
|
-
# the :yields: directive:
|
|
84
|
-
#
|
|
85
|
-
# ##
|
|
86
|
-
# # My method is awesome
|
|
87
|
-
#
|
|
88
|
-
# def my_method(&block) # :yields: happy, times
|
|
89
|
-
# block.call 1, 2
|
|
90
|
-
# end
|
|
91
|
-
#
|
|
92
|
-
# == Metaprogrammed Methods
|
|
93
|
-
#
|
|
94
|
-
# To pick up a metaprogrammed method, the parser looks for a comment starting
|
|
95
|
-
# with '##' before an identifier:
|
|
96
|
-
#
|
|
97
|
-
# ##
|
|
98
|
-
# # This is a meta-programmed method!
|
|
99
|
-
#
|
|
100
|
-
# add_my_method :meta_method, :arg1, :arg2
|
|
101
|
-
#
|
|
102
|
-
# The parser looks at the token after the identifier to determine the name, in
|
|
103
|
-
# this example, :meta_method. If a name cannot be found, a warning is printed
|
|
104
|
-
# and 'unknown' is used.
|
|
105
|
-
#
|
|
106
|
-
# You can force the name of a method using the :method: directive:
|
|
107
|
-
#
|
|
108
|
-
# ##
|
|
109
|
-
# # :method: some_method!
|
|
110
|
-
#
|
|
111
|
-
# By default, meta-methods are instance methods. To indicate that a method is
|
|
112
|
-
# a singleton method instead use the :singleton-method: directive:
|
|
113
|
-
#
|
|
114
|
-
# ##
|
|
115
|
-
# # :singleton-method:
|
|
116
|
-
#
|
|
117
|
-
# You can also use the :singleton-method: directive with a name:
|
|
118
|
-
#
|
|
119
|
-
# ##
|
|
120
|
-
# # :singleton-method: some_method!
|
|
121
|
-
#
|
|
122
|
-
# You can define arguments for metaprogrammed methods via either the
|
|
123
|
-
# \:call-seq:, :arg: or :args: directives.
|
|
124
|
-
#
|
|
125
|
-
# Additionally you can mark a method as an attribute by
|
|
126
|
-
# using :attr:, :attr_reader:, :attr_writer: or :attr_accessor:. Just like
|
|
127
|
-
# for :method:, the name is optional.
|
|
128
|
-
#
|
|
129
|
-
# ##
|
|
130
|
-
# # :attr_reader: my_attr_name
|
|
131
|
-
#
|
|
132
|
-
# == Hidden methods and attributes
|
|
133
|
-
#
|
|
134
|
-
# You can provide documentation for methods that don't appear using
|
|
135
|
-
# the :method:, :singleton-method: and :attr: directives:
|
|
136
|
-
#
|
|
137
|
-
# ##
|
|
138
|
-
# # :attr_writer: ghost_writer
|
|
139
|
-
# # There is an attribute here, but you can't see it!
|
|
140
|
-
#
|
|
141
|
-
# ##
|
|
142
|
-
# # :method: ghost_method
|
|
143
|
-
# # There is a method here, but you can't see it!
|
|
144
|
-
#
|
|
145
|
-
# ##
|
|
146
|
-
# # this is a comment for a regular method
|
|
147
|
-
#
|
|
148
|
-
# def regular_method() end
|
|
149
|
-
#
|
|
150
|
-
# Note that by default, the :method: directive will be ignored if there is a
|
|
151
|
-
# standard rdocable item following it.
|
|
152
|
-
|
|
153
|
-
class RDoc::Parser::Ruby < RDoc::Parser
|
|
154
|
-
|
|
155
|
-
parse_files_matching(/\.rbw?$/)
|
|
156
|
-
|
|
157
|
-
include RDoc::TokenStream
|
|
158
|
-
include RDoc::Parser::RubyTools
|
|
159
|
-
|
|
160
|
-
##
|
|
161
|
-
# RDoc::NormalClass type
|
|
162
|
-
|
|
163
|
-
NORMAL = "::"
|
|
164
|
-
|
|
165
|
-
##
|
|
166
|
-
# RDoc::SingleClass type
|
|
167
|
-
|
|
168
|
-
SINGLE = "<<"
|
|
169
|
-
|
|
170
|
-
##
|
|
171
|
-
# Creates a new Ruby parser.
|
|
172
|
-
|
|
173
|
-
def initialize(top_level, content, options, stats)
|
|
174
|
-
super
|
|
175
|
-
|
|
176
|
-
content = handle_tab_width(content)
|
|
177
|
-
|
|
178
|
-
@size = 0
|
|
179
|
-
@token_listeners = nil
|
|
180
|
-
content = RDoc::Encoding.remove_magic_comment content
|
|
181
|
-
@scanner = RDoc::Parser::RipperStateLex.parse(content)
|
|
182
|
-
@content = content
|
|
183
|
-
@scanner_point = 0
|
|
184
|
-
@prev_seek = nil
|
|
185
|
-
@markup = @options.markup
|
|
186
|
-
@track_visibility = :nodoc != @options.visibility
|
|
187
|
-
@encoding = @options.encoding
|
|
188
|
-
|
|
189
|
-
reset
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
##
|
|
193
|
-
# Return +true+ if +tk+ is a newline.
|
|
194
|
-
|
|
195
|
-
def tk_nl?(tk)
|
|
196
|
-
:on_nl == tk[:kind] or :on_ignored_nl == tk[:kind]
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
##
|
|
200
|
-
# Retrieves the read token stream and replaces +pattern+ with +replacement+
|
|
201
|
-
# using gsub. If the result is only a ";" returns an empty string.
|
|
202
|
-
|
|
203
|
-
def get_tkread_clean(pattern, replacement) # :nodoc:
|
|
204
|
-
read = get_tkread.gsub(pattern, replacement).strip
|
|
205
|
-
return '' if read == ';'
|
|
206
|
-
read
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
##
|
|
210
|
-
# Extracts the visibility information for the visibility token +tk+
|
|
211
|
-
# and +single+ class type identifier.
|
|
212
|
-
#
|
|
213
|
-
# Returns the visibility type (a string), the visibility (a symbol) and
|
|
214
|
-
# +singleton+ if the methods following should be converted to singleton
|
|
215
|
-
# methods.
|
|
216
|
-
|
|
217
|
-
def get_visibility_information(tk, single) # :nodoc:
|
|
218
|
-
vis_type = tk[:text]
|
|
219
|
-
singleton = single == SINGLE
|
|
220
|
-
|
|
221
|
-
vis =
|
|
222
|
-
case vis_type
|
|
223
|
-
when 'private' then :private
|
|
224
|
-
when 'protected' then :protected
|
|
225
|
-
when 'public' then :public
|
|
226
|
-
when 'private_class_method' then
|
|
227
|
-
singleton = true
|
|
228
|
-
:private
|
|
229
|
-
when 'public_class_method' then
|
|
230
|
-
singleton = true
|
|
231
|
-
:public
|
|
232
|
-
when 'module_function' then
|
|
233
|
-
singleton = true
|
|
234
|
-
:public
|
|
235
|
-
else
|
|
236
|
-
raise RDoc::Error, "Invalid visibility: #{tk.name}"
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
return vis_type, vis, singleton
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
##
|
|
243
|
-
# Look for the first comment in a file that isn't a shebang line.
|
|
244
|
-
|
|
245
|
-
def collect_first_comment
|
|
246
|
-
skip_tkspace
|
|
247
|
-
comment = ''.dup
|
|
248
|
-
comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
|
|
249
|
-
first_line = true
|
|
250
|
-
first_comment_tk_kind = nil
|
|
251
|
-
line_no = nil
|
|
252
|
-
|
|
253
|
-
tk = get_tk
|
|
254
|
-
|
|
255
|
-
while tk && (:on_comment == tk[:kind] or :on_embdoc == tk[:kind])
|
|
256
|
-
comment_body = retrieve_comment_body(tk)
|
|
257
|
-
if first_line and comment_body =~ /\A#!/ then
|
|
258
|
-
skip_tkspace
|
|
259
|
-
tk = get_tk
|
|
260
|
-
elsif first_line and comment_body =~ /\A#\s*-\*-/ then
|
|
261
|
-
first_line = false
|
|
262
|
-
skip_tkspace
|
|
263
|
-
tk = get_tk
|
|
264
|
-
else
|
|
265
|
-
break if first_comment_tk_kind and not first_comment_tk_kind === tk[:kind]
|
|
266
|
-
first_comment_tk_kind = tk[:kind]
|
|
267
|
-
|
|
268
|
-
line_no = tk[:line_no] if first_line
|
|
269
|
-
first_line = false
|
|
270
|
-
comment << comment_body
|
|
271
|
-
tk = get_tk
|
|
272
|
-
|
|
273
|
-
if :on_nl === tk then
|
|
274
|
-
skip_tkspace_without_nl
|
|
275
|
-
tk = get_tk
|
|
276
|
-
end
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
unget_tk tk
|
|
281
|
-
|
|
282
|
-
new_comment comment, line_no
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
##
|
|
286
|
-
# Consumes trailing whitespace from the token stream
|
|
287
|
-
|
|
288
|
-
def consume_trailing_spaces # :nodoc:
|
|
289
|
-
skip_tkspace_without_nl
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
##
|
|
293
|
-
# Creates a new attribute in +container+ with +name+.
|
|
294
|
-
|
|
295
|
-
def create_attr(container, single, name, rw, comment) # :nodoc:
|
|
296
|
-
att = RDoc::Attr.new get_tkread, name, rw, comment, singleton: single == SINGLE
|
|
297
|
-
record_location att
|
|
298
|
-
|
|
299
|
-
container.add_attribute att
|
|
300
|
-
@stats.add_attribute att
|
|
301
|
-
|
|
302
|
-
att
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
##
|
|
306
|
-
# Creates a module alias in +container+ at +rhs_name+ (or at the top-level
|
|
307
|
-
# for "::") with the name from +constant+.
|
|
308
|
-
|
|
309
|
-
def create_module_alias(container, constant, rhs_name) # :nodoc:
|
|
310
|
-
mod = if rhs_name =~ /^::/ then
|
|
311
|
-
@store.find_class_or_module rhs_name
|
|
312
|
-
else
|
|
313
|
-
container.find_module_named rhs_name
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
container.add_module_alias mod, rhs_name, constant, @top_level
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
##
|
|
320
|
-
# Aborts with +msg+
|
|
321
|
-
|
|
322
|
-
def error(msg)
|
|
323
|
-
msg = make_message msg
|
|
324
|
-
|
|
325
|
-
abort msg
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
##
|
|
329
|
-
# Looks for a true or false token.
|
|
330
|
-
|
|
331
|
-
def get_bool
|
|
332
|
-
skip_tkspace
|
|
333
|
-
tk = get_tk
|
|
334
|
-
if :on_kw == tk[:kind] && 'true' == tk[:text]
|
|
335
|
-
true
|
|
336
|
-
elsif :on_kw == tk[:kind] && ('false' == tk[:text] || 'nil' == tk[:text])
|
|
337
|
-
false
|
|
338
|
-
else
|
|
339
|
-
unget_tk tk
|
|
340
|
-
true
|
|
341
|
-
end
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
##
|
|
345
|
-
# Look for the name of a class of module (optionally with a leading :: or
|
|
346
|
-
# with :: separated named) and return the ultimate name, the associated
|
|
347
|
-
# container, and the given name (with the ::).
|
|
348
|
-
|
|
349
|
-
def get_class_or_module(container, ignore_constants = false)
|
|
350
|
-
skip_tkspace
|
|
351
|
-
name_t = get_tk
|
|
352
|
-
given_name = ''.dup
|
|
353
|
-
|
|
354
|
-
# class ::A -> A is in the top level
|
|
355
|
-
if :on_op == name_t[:kind] and '::' == name_t[:text] then # bug
|
|
356
|
-
name_t = get_tk
|
|
357
|
-
container = @top_level
|
|
358
|
-
given_name << '::'
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
skip_tkspace_without_nl
|
|
362
|
-
given_name << name_t[:text]
|
|
363
|
-
|
|
364
|
-
is_self = name_t[:kind] == :on_op && name_t[:text] == '<<'
|
|
365
|
-
new_modules = []
|
|
366
|
-
while !is_self && (tk = peek_tk) and :on_op == tk[:kind] and '::' == tk[:text] do
|
|
367
|
-
prev_container = container
|
|
368
|
-
container = container.find_module_named name_t[:text]
|
|
369
|
-
container ||=
|
|
370
|
-
if ignore_constants then
|
|
371
|
-
c = RDoc::NormalModule.new name_t[:text]
|
|
372
|
-
c.store = @store
|
|
373
|
-
new_modules << [prev_container, c]
|
|
374
|
-
c
|
|
375
|
-
else
|
|
376
|
-
c = prev_container.add_module RDoc::NormalModule, name_t[:text]
|
|
377
|
-
c.ignore unless prev_container.document_children
|
|
378
|
-
@top_level.add_to_classes_or_modules c
|
|
379
|
-
c
|
|
380
|
-
end
|
|
381
|
-
|
|
382
|
-
record_location container
|
|
383
|
-
|
|
384
|
-
get_tk
|
|
385
|
-
skip_tkspace
|
|
386
|
-
if :on_lparen == peek_tk[:kind] # ProcObjectInConstant::()
|
|
387
|
-
parse_method_or_yield_parameters
|
|
388
|
-
break
|
|
389
|
-
end
|
|
390
|
-
name_t = get_tk
|
|
391
|
-
unless :on_const == name_t[:kind] || :on_ident == name_t[:kind]
|
|
392
|
-
raise RDoc::Error, "Invalid class or module definition: #{given_name}"
|
|
393
|
-
end
|
|
394
|
-
if prev_container == container and !ignore_constants
|
|
395
|
-
given_name = name_t[:text]
|
|
396
|
-
else
|
|
397
|
-
given_name << '::' + name_t[:text]
|
|
398
|
-
end
|
|
399
|
-
end
|
|
400
|
-
|
|
401
|
-
skip_tkspace_without_nl
|
|
402
|
-
|
|
403
|
-
return [container, name_t, given_name, new_modules]
|
|
404
|
-
end
|
|
405
|
-
|
|
406
|
-
##
|
|
407
|
-
# Skip opening parentheses and yield the block.
|
|
408
|
-
# Skip closing parentheses too when exists.
|
|
409
|
-
|
|
410
|
-
def skip_parentheses(&block)
|
|
411
|
-
left_tk = peek_tk
|
|
412
|
-
|
|
413
|
-
if :on_lparen == left_tk[:kind]
|
|
414
|
-
get_tk
|
|
415
|
-
|
|
416
|
-
ret = skip_parentheses(&block)
|
|
417
|
-
|
|
418
|
-
right_tk = peek_tk
|
|
419
|
-
if :on_rparen == right_tk[:kind]
|
|
420
|
-
get_tk
|
|
421
|
-
end
|
|
422
|
-
|
|
423
|
-
ret
|
|
424
|
-
else
|
|
425
|
-
yield
|
|
426
|
-
end
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
##
|
|
430
|
-
# Return a superclass, which can be either a constant of an expression
|
|
431
|
-
|
|
432
|
-
def get_class_specification
|
|
433
|
-
tk = peek_tk
|
|
434
|
-
if tk.nil?
|
|
435
|
-
return ''
|
|
436
|
-
elsif :on_kw == tk[:kind] && 'self' == tk[:text]
|
|
437
|
-
return 'self'
|
|
438
|
-
elsif :on_gvar == tk[:kind]
|
|
439
|
-
return ''
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
res = get_constant
|
|
443
|
-
|
|
444
|
-
skip_tkspace_without_nl
|
|
445
|
-
|
|
446
|
-
get_tkread # empty out read buffer
|
|
447
|
-
|
|
448
|
-
tk = get_tk
|
|
449
|
-
return res unless tk
|
|
450
|
-
|
|
451
|
-
case tk[:kind]
|
|
452
|
-
when :on_nl, :on_comment, :on_embdoc, :on_semicolon then
|
|
453
|
-
unget_tk(tk)
|
|
454
|
-
return res
|
|
455
|
-
end
|
|
456
|
-
|
|
457
|
-
res += parse_call_parameters(tk)
|
|
458
|
-
res
|
|
459
|
-
end
|
|
460
|
-
|
|
461
|
-
##
|
|
462
|
-
# Parse a constant, which might be qualified by one or more class or module
|
|
463
|
-
# names
|
|
464
|
-
|
|
465
|
-
def get_constant
|
|
466
|
-
res = ""
|
|
467
|
-
skip_tkspace_without_nl
|
|
468
|
-
tk = get_tk
|
|
469
|
-
|
|
470
|
-
while tk && ((:on_op == tk[:kind] && '::' == tk[:text]) || :on_const == tk[:kind]) do
|
|
471
|
-
res += tk[:text]
|
|
472
|
-
tk = get_tk
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
unget_tk(tk)
|
|
476
|
-
res
|
|
477
|
-
end
|
|
478
|
-
|
|
479
|
-
##
|
|
480
|
-
# Get an included module that may be surrounded by parens
|
|
481
|
-
|
|
482
|
-
def get_included_module_with_optional_parens
|
|
483
|
-
skip_tkspace_without_nl
|
|
484
|
-
get_tkread
|
|
485
|
-
tk = get_tk
|
|
486
|
-
end_token = get_end_token tk
|
|
487
|
-
return '' unless end_token
|
|
488
|
-
|
|
489
|
-
nest = 0
|
|
490
|
-
continue = false
|
|
491
|
-
only_constant = true
|
|
492
|
-
|
|
493
|
-
while tk != nil do
|
|
494
|
-
is_element_of_constant = false
|
|
495
|
-
case tk[:kind]
|
|
496
|
-
when :on_semicolon then
|
|
497
|
-
break if nest == 0
|
|
498
|
-
when :on_lbracket then
|
|
499
|
-
nest += 1
|
|
500
|
-
when :on_rbracket then
|
|
501
|
-
nest -= 1
|
|
502
|
-
when :on_lbrace then
|
|
503
|
-
nest += 1
|
|
504
|
-
when :on_rbrace then
|
|
505
|
-
nest -= 1
|
|
506
|
-
if nest <= 0
|
|
507
|
-
# we might have a.each { |i| yield i }
|
|
508
|
-
unget_tk(tk) if nest < 0
|
|
509
|
-
break
|
|
510
|
-
end
|
|
511
|
-
when :on_lparen then
|
|
512
|
-
nest += 1
|
|
513
|
-
when end_token[:kind] then
|
|
514
|
-
if end_token[:kind] == :on_rparen
|
|
515
|
-
nest -= 1
|
|
516
|
-
break if nest <= 0
|
|
517
|
-
else
|
|
518
|
-
break if nest <= 0
|
|
519
|
-
end
|
|
520
|
-
when :on_rparen then
|
|
521
|
-
nest -= 1
|
|
522
|
-
when :on_comment, :on_embdoc then
|
|
523
|
-
@read.pop
|
|
524
|
-
if :on_nl == end_token[:kind] and "\n" == tk[:text][-1] and
|
|
525
|
-
(!continue or (tk[:state] & Ripper::EXPR_LABEL) != 0) then
|
|
526
|
-
break if !continue and nest <= 0
|
|
527
|
-
end
|
|
528
|
-
when :on_comma then
|
|
529
|
-
continue = true
|
|
530
|
-
when :on_ident then
|
|
531
|
-
continue = false if continue
|
|
532
|
-
when :on_kw then
|
|
533
|
-
case tk[:text]
|
|
534
|
-
when 'def', 'do', 'case', 'for', 'begin', 'class', 'module'
|
|
535
|
-
nest += 1
|
|
536
|
-
when 'if', 'unless', 'while', 'until', 'rescue'
|
|
537
|
-
# postfix if/unless/while/until/rescue must be EXPR_LABEL
|
|
538
|
-
nest += 1 unless (tk[:state] & Ripper::EXPR_LABEL) != 0
|
|
539
|
-
when 'end'
|
|
540
|
-
nest -= 1
|
|
541
|
-
break if nest == 0
|
|
542
|
-
end
|
|
543
|
-
when :on_const then
|
|
544
|
-
is_element_of_constant = true
|
|
545
|
-
when :on_op then
|
|
546
|
-
is_element_of_constant = true if '::' == tk[:text]
|
|
547
|
-
end
|
|
548
|
-
only_constant = false unless is_element_of_constant
|
|
549
|
-
tk = get_tk
|
|
550
|
-
end
|
|
551
|
-
|
|
552
|
-
if only_constant
|
|
553
|
-
get_tkread_clean(/\s+/, ' ')
|
|
554
|
-
else
|
|
555
|
-
''
|
|
556
|
-
end
|
|
557
|
-
end
|
|
558
|
-
|
|
559
|
-
##
|
|
560
|
-
# Little hack going on here. In the statement:
|
|
561
|
-
#
|
|
562
|
-
# f = 2*(1+yield)
|
|
563
|
-
#
|
|
564
|
-
# We see the RPAREN as the next token, so we need to exit early. This still
|
|
565
|
-
# won't catch all cases (such as "a = yield + 1"
|
|
566
|
-
|
|
567
|
-
def get_end_token(tk) # :nodoc:
|
|
568
|
-
case tk[:kind]
|
|
569
|
-
when :on_lparen
|
|
570
|
-
token = RDoc::Parser::RipperStateLex::Token.new
|
|
571
|
-
token[:kind] = :on_rparen
|
|
572
|
-
token[:text] = ')'
|
|
573
|
-
token
|
|
574
|
-
when :on_rparen
|
|
575
|
-
nil
|
|
576
|
-
else
|
|
577
|
-
token = RDoc::Parser::RipperStateLex::Token.new
|
|
578
|
-
token[:kind] = :on_nl
|
|
579
|
-
token[:text] = "\n"
|
|
580
|
-
token
|
|
581
|
-
end
|
|
582
|
-
end
|
|
583
|
-
|
|
584
|
-
##
|
|
585
|
-
# Retrieves the method container for a singleton method.
|
|
586
|
-
|
|
587
|
-
def get_method_container(container, name_t) # :nodoc:
|
|
588
|
-
prev_container = container
|
|
589
|
-
container = container.find_module_named(name_t[:text])
|
|
590
|
-
|
|
591
|
-
unless container then
|
|
592
|
-
constant = prev_container.constants.find do |const|
|
|
593
|
-
const.name == name_t[:text]
|
|
594
|
-
end
|
|
595
|
-
|
|
596
|
-
if constant then
|
|
597
|
-
parse_method_dummy prev_container
|
|
598
|
-
return
|
|
599
|
-
end
|
|
600
|
-
end
|
|
601
|
-
|
|
602
|
-
unless container then
|
|
603
|
-
# TODO seems broken, should starting at Object in @store
|
|
604
|
-
obj = name_t[:text].split("::").inject(Object) do |state, item|
|
|
605
|
-
state.const_get(item)
|
|
606
|
-
end rescue nil
|
|
607
|
-
|
|
608
|
-
type = obj.class == Class ? RDoc::NormalClass : RDoc::NormalModule
|
|
609
2
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
end
|
|
613
|
-
|
|
614
|
-
if type == RDoc::NormalClass then
|
|
615
|
-
sclass = obj.superclass ? obj.superclass.name : nil
|
|
616
|
-
container = prev_container.add_class type, name_t[:text], sclass
|
|
617
|
-
else
|
|
618
|
-
container = prev_container.add_module type, name_t[:text]
|
|
619
|
-
end
|
|
620
|
-
|
|
621
|
-
record_location container
|
|
622
|
-
end
|
|
623
|
-
|
|
624
|
-
container
|
|
625
|
-
end
|
|
626
|
-
|
|
627
|
-
##
|
|
628
|
-
# Extracts a name or symbol from the token stream.
|
|
629
|
-
|
|
630
|
-
def get_symbol_or_name
|
|
631
|
-
tk = get_tk
|
|
632
|
-
case tk[:kind]
|
|
633
|
-
when :on_symbol then
|
|
634
|
-
text = tk[:text].sub(/^:/, '')
|
|
635
|
-
|
|
636
|
-
next_tk = peek_tk
|
|
637
|
-
if next_tk && :on_op == next_tk[:kind] && '=' == next_tk[:text] then
|
|
638
|
-
get_tk
|
|
639
|
-
text << '='
|
|
640
|
-
end
|
|
641
|
-
|
|
642
|
-
text
|
|
643
|
-
when :on_ident, :on_const, :on_gvar, :on_cvar, :on_ivar, :on_op, :on_kw then
|
|
644
|
-
tk[:text]
|
|
645
|
-
when :on_tstring, :on_dstring then
|
|
646
|
-
tk[:text][1..-2]
|
|
647
|
-
else
|
|
648
|
-
raise RDoc::Error, "Name or symbol expected (got #{tk})"
|
|
649
|
-
end
|
|
650
|
-
end
|
|
651
|
-
|
|
652
|
-
##
|
|
653
|
-
# Marks containers between +container+ and +ancestor+ as ignored
|
|
654
|
-
|
|
655
|
-
def suppress_parents(container, ancestor) # :nodoc:
|
|
656
|
-
while container and container != ancestor do
|
|
657
|
-
container.suppress unless container.documented?
|
|
658
|
-
container = container.parent
|
|
659
|
-
end
|
|
660
|
-
end
|
|
661
|
-
|
|
662
|
-
##
|
|
663
|
-
# Look for directives in a normal comment block:
|
|
664
|
-
#
|
|
665
|
-
# # :stopdoc:
|
|
666
|
-
# # Don't display comment from this point forward
|
|
667
|
-
#
|
|
668
|
-
# This routine modifies its +comment+ parameter.
|
|
669
|
-
|
|
670
|
-
def look_for_directives_in(container, comment)
|
|
671
|
-
@preprocess.handle comment, container do |directive, param|
|
|
672
|
-
case directive
|
|
673
|
-
when 'method', 'singleton-method',
|
|
674
|
-
'attr', 'attr_accessor', 'attr_reader', 'attr_writer' then
|
|
675
|
-
false # handled elsewhere
|
|
676
|
-
when 'section' then
|
|
677
|
-
break unless container.kind_of?(RDoc::Context)
|
|
678
|
-
container.set_current_section param, comment.dup
|
|
679
|
-
comment.text = ''
|
|
680
|
-
break
|
|
681
|
-
end
|
|
682
|
-
end
|
|
683
|
-
|
|
684
|
-
comment.remove_private
|
|
685
|
-
end
|
|
686
|
-
|
|
687
|
-
##
|
|
688
|
-
# Adds useful info about the parser to +message+
|
|
689
|
-
|
|
690
|
-
def make_message(message)
|
|
691
|
-
prefix = "#{@file_name}:".dup
|
|
692
|
-
|
|
693
|
-
tk = peek_tk
|
|
694
|
-
prefix << "#{tk[:line_no]}:#{tk[:char_no]}:" if tk
|
|
695
|
-
|
|
696
|
-
"#{prefix} #{message}"
|
|
697
|
-
end
|
|
698
|
-
|
|
699
|
-
##
|
|
700
|
-
# Creates a comment with the correct format
|
|
701
|
-
|
|
702
|
-
def new_comment(comment, line_no = nil)
|
|
703
|
-
c = RDoc::Comment.new comment, @top_level, :ruby
|
|
704
|
-
c.line = line_no
|
|
705
|
-
c.format = @markup
|
|
706
|
-
c
|
|
707
|
-
end
|
|
708
|
-
|
|
709
|
-
##
|
|
710
|
-
# Creates an RDoc::Attr for the name following +tk+, setting the comment to
|
|
711
|
-
# +comment+.
|
|
712
|
-
|
|
713
|
-
def parse_attr(context, single, tk, comment)
|
|
714
|
-
line_no = tk[:line_no]
|
|
715
|
-
|
|
716
|
-
args = parse_symbol_arg 1
|
|
717
|
-
if args.size > 0 then
|
|
718
|
-
name = args[0]
|
|
719
|
-
rw = "R"
|
|
720
|
-
skip_tkspace_without_nl
|
|
721
|
-
tk = get_tk
|
|
722
|
-
|
|
723
|
-
if :on_comma == tk[:kind] then
|
|
724
|
-
rw = "RW" if get_bool
|
|
725
|
-
else
|
|
726
|
-
unget_tk tk
|
|
727
|
-
end
|
|
728
|
-
|
|
729
|
-
att = create_attr context, single, name, rw, comment
|
|
730
|
-
att.line = line_no
|
|
731
|
-
|
|
732
|
-
read_documentation_modifiers att, RDoc::ATTR_MODIFIERS
|
|
733
|
-
else
|
|
734
|
-
warn "'attr' ignored - looks like a variable"
|
|
735
|
-
end
|
|
736
|
-
end
|
|
737
|
-
|
|
738
|
-
##
|
|
739
|
-
# Creates an RDoc::Attr for each attribute listed after +tk+, setting the
|
|
740
|
-
# comment for each to +comment+.
|
|
741
|
-
|
|
742
|
-
def parse_attr_accessor(context, single, tk, comment)
|
|
743
|
-
line_no = tk[:line_no]
|
|
744
|
-
|
|
745
|
-
args = parse_symbol_arg
|
|
746
|
-
rw = "?"
|
|
747
|
-
|
|
748
|
-
tmp = RDoc::CodeObject.new
|
|
749
|
-
read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
|
|
750
|
-
# TODO In most other places we let the context keep track of document_self
|
|
751
|
-
# and add found items appropriately but here we do not. I'm not sure why.
|
|
752
|
-
return if @track_visibility and not tmp.document_self
|
|
753
|
-
|
|
754
|
-
case tk[:text]
|
|
755
|
-
when "attr_reader" then rw = "R"
|
|
756
|
-
when "attr_writer" then rw = "W"
|
|
757
|
-
when "attr_accessor" then rw = "RW"
|
|
758
|
-
else
|
|
759
|
-
rw = '?'
|
|
760
|
-
end
|
|
761
|
-
|
|
762
|
-
for name in args
|
|
763
|
-
att = create_attr context, single, name, rw, comment
|
|
764
|
-
att.line = line_no
|
|
765
|
-
end
|
|
766
|
-
end
|
|
767
|
-
|
|
768
|
-
##
|
|
769
|
-
# Parses an +alias+ in +context+ with +comment+
|
|
770
|
-
|
|
771
|
-
def parse_alias(context, single, tk, comment)
|
|
772
|
-
line_no = tk[:line_no]
|
|
773
|
-
|
|
774
|
-
skip_tkspace
|
|
775
|
-
|
|
776
|
-
if :on_lparen === peek_tk[:kind] then
|
|
777
|
-
get_tk
|
|
778
|
-
skip_tkspace
|
|
779
|
-
end
|
|
780
|
-
|
|
781
|
-
new_name = get_symbol_or_name
|
|
782
|
-
|
|
783
|
-
skip_tkspace
|
|
784
|
-
if :on_comma === peek_tk[:kind] then
|
|
785
|
-
get_tk
|
|
786
|
-
skip_tkspace
|
|
787
|
-
end
|
|
788
|
-
|
|
789
|
-
begin
|
|
790
|
-
old_name = get_symbol_or_name
|
|
791
|
-
rescue RDoc::Error
|
|
792
|
-
return
|
|
793
|
-
end
|
|
794
|
-
|
|
795
|
-
al = RDoc::Alias.new(get_tkread, old_name, new_name, comment, singleton: single == SINGLE)
|
|
796
|
-
record_location al
|
|
797
|
-
al.line = line_no
|
|
798
|
-
|
|
799
|
-
read_documentation_modifiers al, RDoc::ATTR_MODIFIERS
|
|
800
|
-
if al.document_self or not @track_visibility
|
|
801
|
-
context.add_alias al
|
|
802
|
-
@stats.add_alias al
|
|
803
|
-
end
|
|
804
|
-
|
|
805
|
-
al
|
|
806
|
-
end
|
|
807
|
-
|
|
808
|
-
##
|
|
809
|
-
# Extracts call parameters from the token stream.
|
|
810
|
-
|
|
811
|
-
def parse_call_parameters(tk)
|
|
812
|
-
end_token = case tk[:kind]
|
|
813
|
-
when :on_lparen
|
|
814
|
-
:on_rparen
|
|
815
|
-
when :on_rparen
|
|
816
|
-
return ""
|
|
817
|
-
else
|
|
818
|
-
:on_nl
|
|
819
|
-
end
|
|
820
|
-
nest = 0
|
|
821
|
-
|
|
822
|
-
loop do
|
|
823
|
-
break if tk.nil?
|
|
824
|
-
case tk[:kind]
|
|
825
|
-
when :on_semicolon
|
|
826
|
-
break
|
|
827
|
-
when :on_lparen
|
|
828
|
-
nest += 1
|
|
829
|
-
when end_token
|
|
830
|
-
if end_token == :on_rparen
|
|
831
|
-
nest -= 1
|
|
832
|
-
break if RDoc::Parser::RipperStateLex.end?(tk) and nest <= 0
|
|
833
|
-
else
|
|
834
|
-
break if RDoc::Parser::RipperStateLex.end?(tk)
|
|
835
|
-
end
|
|
836
|
-
when :on_comment, :on_embdoc
|
|
837
|
-
unget_tk(tk)
|
|
838
|
-
break
|
|
839
|
-
when :on_op
|
|
840
|
-
if tk[:text] =~ /^(.{1,2})?=$/
|
|
841
|
-
unget_tk(tk)
|
|
842
|
-
break
|
|
843
|
-
end
|
|
844
|
-
end
|
|
845
|
-
tk = get_tk
|
|
846
|
-
end
|
|
847
|
-
|
|
848
|
-
get_tkread_clean "\n", " "
|
|
849
|
-
end
|
|
850
|
-
|
|
851
|
-
##
|
|
852
|
-
# Parses a class in +context+ with +comment+
|
|
853
|
-
|
|
854
|
-
def parse_class(container, single, tk, comment)
|
|
855
|
-
line_no = tk[:line_no]
|
|
856
|
-
|
|
857
|
-
declaration_context = container
|
|
858
|
-
container, name_t, given_name, = get_class_or_module container
|
|
859
|
-
|
|
860
|
-
if name_t[:kind] == :on_const
|
|
861
|
-
cls = parse_class_regular container, declaration_context, single,
|
|
862
|
-
name_t, given_name, comment
|
|
863
|
-
elsif name_t[:kind] == :on_op && name_t[:text] == '<<'
|
|
864
|
-
case name = skip_parentheses { get_class_specification }
|
|
865
|
-
when 'self', container.name
|
|
866
|
-
read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
|
|
867
|
-
parse_statements container, SINGLE
|
|
868
|
-
return # don't update line
|
|
869
|
-
else
|
|
870
|
-
cls = parse_class_singleton container, name, comment
|
|
871
|
-
end
|
|
872
|
-
else
|
|
873
|
-
warn "Expected class name or '<<'. Got #{name_t[:kind]}: #{name_t[:text].inspect}"
|
|
874
|
-
return
|
|
875
|
-
end
|
|
876
|
-
|
|
877
|
-
cls.line = line_no
|
|
878
|
-
|
|
879
|
-
# after end modifiers
|
|
880
|
-
read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
|
|
881
|
-
|
|
882
|
-
cls
|
|
883
|
-
end
|
|
884
|
-
|
|
885
|
-
##
|
|
886
|
-
# Parses and creates a regular class
|
|
887
|
-
|
|
888
|
-
def parse_class_regular(container, declaration_context, single, # :nodoc:
|
|
889
|
-
name_t, given_name, comment)
|
|
890
|
-
superclass = '::Object'
|
|
891
|
-
|
|
892
|
-
if given_name =~ /^::/ then
|
|
893
|
-
declaration_context = @top_level
|
|
894
|
-
given_name = $'
|
|
895
|
-
end
|
|
896
|
-
|
|
897
|
-
tk = peek_tk
|
|
898
|
-
if tk[:kind] == :on_op && tk[:text] == '<' then
|
|
899
|
-
get_tk
|
|
900
|
-
skip_tkspace
|
|
901
|
-
superclass = get_class_specification
|
|
902
|
-
superclass = '(unknown)' if superclass.empty?
|
|
903
|
-
end
|
|
904
|
-
|
|
905
|
-
cls_type = single == SINGLE ? RDoc::SingleClass : RDoc::NormalClass
|
|
906
|
-
cls = declaration_context.add_class cls_type, given_name, superclass
|
|
907
|
-
cls.ignore unless container.document_children
|
|
908
|
-
|
|
909
|
-
read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS
|
|
910
|
-
record_location cls
|
|
911
|
-
|
|
912
|
-
cls.add_comment comment, @top_level
|
|
913
|
-
|
|
914
|
-
@top_level.add_to_classes_or_modules cls
|
|
915
|
-
@stats.add_class cls
|
|
916
|
-
|
|
917
|
-
suppress_parents container, declaration_context unless cls.document_self
|
|
918
|
-
|
|
919
|
-
parse_statements cls
|
|
920
|
-
|
|
921
|
-
cls
|
|
922
|
-
end
|
|
923
|
-
|
|
924
|
-
##
|
|
925
|
-
# Parses a singleton class in +container+ with the given +name+ and
|
|
926
|
-
# +comment+.
|
|
927
|
-
|
|
928
|
-
def parse_class_singleton(container, name, comment) # :nodoc:
|
|
929
|
-
other = @store.find_class_named name
|
|
930
|
-
|
|
931
|
-
unless other then
|
|
932
|
-
if name =~ /^::/ then
|
|
933
|
-
name = $'
|
|
934
|
-
container = @top_level
|
|
935
|
-
end
|
|
936
|
-
|
|
937
|
-
other = container.add_module RDoc::NormalModule, name
|
|
938
|
-
record_location other
|
|
939
|
-
|
|
940
|
-
# class << $gvar
|
|
941
|
-
other.ignore if name.empty?
|
|
942
|
-
|
|
943
|
-
other.add_comment comment, @top_level
|
|
944
|
-
end
|
|
945
|
-
|
|
946
|
-
# notify :nodoc: all if not a constant-named class/module
|
|
947
|
-
# (and remove any comment)
|
|
948
|
-
unless name =~ /\A(::)?[A-Z]/ then
|
|
949
|
-
other.document_self = nil
|
|
950
|
-
other.document_children = false
|
|
951
|
-
other.clear_comment
|
|
952
|
-
end
|
|
3
|
+
require 'prism'
|
|
4
|
+
require_relative '../rbs_helper'
|
|
953
5
|
|
|
954
|
-
|
|
955
|
-
|
|
6
|
+
module RDoc
|
|
7
|
+
class Parser
|
|
8
|
+
# Parse and collect document from Ruby source code.
|
|
956
9
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
elsif :on_nl == tk[:kind] then
|
|
1076
|
-
if nest <= 0 and RDoc::Parser::RipperStateLex.end?(tk) then
|
|
1077
|
-
unget_tk tk
|
|
1078
|
-
break
|
|
1079
|
-
end
|
|
1080
|
-
elsif :on_op == tk[:kind] && '::' == tk[:text]
|
|
1081
|
-
rhs_name << '::'
|
|
1082
|
-
end
|
|
1083
|
-
tk = get_tk
|
|
1084
|
-
end
|
|
1085
|
-
|
|
1086
|
-
body ? body : get_tkread_clean(/^[ \t]+/, '')
|
|
1087
|
-
end
|
|
1088
|
-
|
|
1089
|
-
##
|
|
1090
|
-
# Generates an RDoc::Method or RDoc::Attr from +comment+ by looking for
|
|
1091
|
-
# \:method: or :attr: directives in +comment+.
|
|
1092
|
-
|
|
1093
|
-
def parse_comment(container, tk, comment)
|
|
1094
|
-
return parse_comment_tomdoc container, tk, comment if @markup == 'tomdoc'
|
|
1095
|
-
column = tk[:char_no]
|
|
1096
|
-
line_no = comment.line.nil? ? tk[:line_no] : comment.line
|
|
1097
|
-
|
|
1098
|
-
comment.text = comment.text.sub(/(^# +:?)(singleton-)(method:)/, '\1\3')
|
|
1099
|
-
singleton = !!$~
|
|
1100
|
-
|
|
1101
|
-
co =
|
|
1102
|
-
if (comment.text = comment.text.sub(/^# +:?method: *(\S*).*?\n/i, '')) && !!$~ then
|
|
1103
|
-
line_no += $`.count("\n")
|
|
1104
|
-
parse_comment_ghost container, comment.text, $1, column, line_no, comment
|
|
1105
|
-
elsif (comment.text = comment.text.sub(/# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i, '')) && !!$~ then
|
|
1106
|
-
parse_comment_attr container, $1, $3, comment
|
|
1107
|
-
end
|
|
1108
|
-
|
|
1109
|
-
if co then
|
|
1110
|
-
co.singleton = singleton
|
|
1111
|
-
co.line = line_no
|
|
1112
|
-
end
|
|
1113
|
-
|
|
1114
|
-
true
|
|
1115
|
-
end
|
|
1116
|
-
|
|
1117
|
-
##
|
|
1118
|
-
# Parse a comment that is describing an attribute in +container+ with the
|
|
1119
|
-
# given +name+ and +comment+.
|
|
1120
|
-
|
|
1121
|
-
def parse_comment_attr(container, type, name, comment) # :nodoc:
|
|
1122
|
-
return if name.empty?
|
|
1123
|
-
|
|
1124
|
-
rw = case type
|
|
1125
|
-
when 'attr_reader' then 'R'
|
|
1126
|
-
when 'attr_writer' then 'W'
|
|
1127
|
-
else 'RW'
|
|
1128
|
-
end
|
|
1129
|
-
|
|
1130
|
-
create_attr container, NORMAL, name, rw, comment
|
|
1131
|
-
end
|
|
1132
|
-
|
|
1133
|
-
def parse_comment_ghost(container, text, name, column, line_no, # :nodoc:
|
|
1134
|
-
comment)
|
|
1135
|
-
name = nil if name.empty?
|
|
1136
|
-
|
|
1137
|
-
meth = RDoc::GhostMethod.new get_tkread, name
|
|
1138
|
-
record_location meth
|
|
1139
|
-
|
|
1140
|
-
meth.start_collecting_tokens(:ruby)
|
|
1141
|
-
indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
|
|
1142
|
-
position_comment = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
|
|
1143
|
-
position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
|
|
1144
|
-
newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
|
|
1145
|
-
meth.add_tokens [position_comment, newline, indent]
|
|
1146
|
-
|
|
1147
|
-
meth.params =
|
|
1148
|
-
if text.sub!(/^#\s+:?args?:\s*(.*?)\s*$/i, '') then
|
|
1149
|
-
$1
|
|
1150
|
-
else
|
|
1151
|
-
''
|
|
1152
|
-
end
|
|
1153
|
-
|
|
1154
|
-
comment.normalize
|
|
1155
|
-
meth.call_seq = comment.extract_call_seq
|
|
1156
|
-
|
|
1157
|
-
return unless meth.name
|
|
1158
|
-
|
|
1159
|
-
container.add_method meth
|
|
1160
|
-
|
|
1161
|
-
meth.comment = comment
|
|
1162
|
-
|
|
1163
|
-
@stats.add_method meth
|
|
1164
|
-
|
|
1165
|
-
meth
|
|
1166
|
-
end
|
|
1167
|
-
|
|
1168
|
-
##
|
|
1169
|
-
# Creates an RDoc::Method on +container+ from +comment+ if there is a
|
|
1170
|
-
# Signature section in the comment
|
|
1171
|
-
|
|
1172
|
-
def parse_comment_tomdoc(container, tk, comment)
|
|
1173
|
-
return unless signature = RDoc::TomDoc.signature(comment)
|
|
1174
|
-
column = tk[:char_no]
|
|
1175
|
-
line_no = tk[:line_no]
|
|
1176
|
-
|
|
1177
|
-
name, = signature.split %r%[ \(]%, 2
|
|
1178
|
-
|
|
1179
|
-
meth = RDoc::GhostMethod.new get_tkread, name
|
|
1180
|
-
record_location meth
|
|
1181
|
-
meth.line = line_no
|
|
1182
|
-
|
|
1183
|
-
meth.start_collecting_tokens(:ruby)
|
|
1184
|
-
indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
|
|
1185
|
-
position_comment = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
|
|
1186
|
-
position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
|
|
1187
|
-
newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
|
|
1188
|
-
meth.add_tokens [position_comment, newline, indent]
|
|
1189
|
-
|
|
1190
|
-
meth.call_seq = signature
|
|
1191
|
-
|
|
1192
|
-
comment.normalize
|
|
1193
|
-
|
|
1194
|
-
return unless meth.name
|
|
1195
|
-
|
|
1196
|
-
container.add_method meth
|
|
1197
|
-
|
|
1198
|
-
meth.comment = comment
|
|
1199
|
-
|
|
1200
|
-
@stats.add_method meth
|
|
1201
|
-
end
|
|
1202
|
-
|
|
1203
|
-
##
|
|
1204
|
-
# Parses an +include+ or +extend+, indicated by the +klass+ and adds it to
|
|
1205
|
-
# +container+ # with +comment+
|
|
1206
|
-
|
|
1207
|
-
def parse_extend_or_include(klass, container, comment) # :nodoc:
|
|
1208
|
-
loop do
|
|
1209
|
-
skip_tkspace_comment
|
|
1210
|
-
|
|
1211
|
-
name = get_included_module_with_optional_parens
|
|
1212
|
-
|
|
1213
|
-
unless name.empty? then
|
|
1214
|
-
obj = container.add klass, name, comment
|
|
1215
|
-
record_location obj
|
|
1216
|
-
end
|
|
1217
|
-
|
|
1218
|
-
return if peek_tk.nil? || :on_comma != peek_tk[:kind]
|
|
1219
|
-
|
|
1220
|
-
get_tk
|
|
1221
|
-
end
|
|
1222
|
-
end
|
|
1223
|
-
|
|
1224
|
-
##
|
|
1225
|
-
# Parses an +included+ with a block feature of ActiveSupport::Concern.
|
|
1226
|
-
|
|
1227
|
-
def parse_included_with_activesupport_concern(container, comment) # :nodoc:
|
|
1228
|
-
skip_tkspace_without_nl
|
|
1229
|
-
tk = get_tk
|
|
1230
|
-
unless tk[:kind] == :on_lbracket || (tk[:kind] == :on_kw && tk[:text] == 'do')
|
|
1231
|
-
unget_tk tk
|
|
1232
|
-
return nil # should be a block
|
|
1233
|
-
end
|
|
1234
|
-
|
|
1235
|
-
parse_statements container
|
|
1236
|
-
|
|
1237
|
-
container
|
|
1238
|
-
end
|
|
1239
|
-
|
|
1240
|
-
##
|
|
1241
|
-
# Parses identifiers that can create new methods or change visibility.
|
|
1242
|
-
#
|
|
1243
|
-
# Returns true if the comment was not consumed.
|
|
1244
|
-
|
|
1245
|
-
def parse_identifier(container, single, tk, comment) # :nodoc:
|
|
1246
|
-
case tk[:text]
|
|
1247
|
-
when 'private', 'protected', 'public', 'private_class_method',
|
|
1248
|
-
'public_class_method', 'module_function' then
|
|
1249
|
-
parse_visibility container, single, tk
|
|
1250
|
-
return true
|
|
1251
|
-
when 'private_constant', 'public_constant'
|
|
1252
|
-
parse_constant_visibility container, single, tk
|
|
1253
|
-
return true
|
|
1254
|
-
when 'attr' then
|
|
1255
|
-
parse_attr container, single, tk, comment
|
|
1256
|
-
when /^attr_(reader|writer|accessor)$/ then
|
|
1257
|
-
parse_attr_accessor container, single, tk, comment
|
|
1258
|
-
when 'alias_method' then
|
|
1259
|
-
parse_alias container, single, tk, comment
|
|
1260
|
-
when 'require', 'include' then
|
|
1261
|
-
# ignore
|
|
1262
|
-
else
|
|
1263
|
-
if comment.text =~ /\A#\#$/ then
|
|
1264
|
-
case comment.text
|
|
1265
|
-
when /^# +:?attr(_reader|_writer|_accessor)?:/ then
|
|
1266
|
-
parse_meta_attr container, single, tk, comment
|
|
1267
|
-
else
|
|
1268
|
-
method = parse_meta_method container, single, tk, comment
|
|
1269
|
-
method.params = container.params if
|
|
1270
|
-
container.params
|
|
1271
|
-
method.block_params = container.block_params if
|
|
1272
|
-
container.block_params
|
|
1273
|
-
end
|
|
1274
|
-
end
|
|
1275
|
-
end
|
|
1276
|
-
|
|
1277
|
-
false
|
|
1278
|
-
end
|
|
1279
|
-
|
|
1280
|
-
##
|
|
1281
|
-
# Parses a meta-programmed attribute and creates an RDoc::Attr.
|
|
1282
|
-
#
|
|
1283
|
-
# To create foo and bar attributes on class C with comment "My attributes":
|
|
1284
|
-
#
|
|
1285
|
-
# class C
|
|
1286
|
-
#
|
|
1287
|
-
# ##
|
|
1288
|
-
# # :attr:
|
|
1289
|
-
# #
|
|
1290
|
-
# # My attributes
|
|
1291
|
-
#
|
|
1292
|
-
# my_attr :foo, :bar
|
|
1293
|
-
#
|
|
1294
|
-
# end
|
|
1295
|
-
#
|
|
1296
|
-
# To create a foo attribute on class C with comment "My attribute":
|
|
1297
|
-
#
|
|
1298
|
-
# class C
|
|
1299
|
-
#
|
|
1300
|
-
# ##
|
|
1301
|
-
# # :attr: foo
|
|
1302
|
-
# #
|
|
1303
|
-
# # My attribute
|
|
1304
|
-
#
|
|
1305
|
-
# my_attr :foo, :bar
|
|
1306
|
-
#
|
|
1307
|
-
# end
|
|
1308
|
-
|
|
1309
|
-
def parse_meta_attr(context, single, tk, comment)
|
|
1310
|
-
args = parse_symbol_arg
|
|
1311
|
-
rw = "?"
|
|
1312
|
-
|
|
1313
|
-
# If nodoc is given, don't document any of them
|
|
1314
|
-
|
|
1315
|
-
tmp = RDoc::CodeObject.new
|
|
1316
|
-
read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
|
|
1317
|
-
|
|
1318
|
-
regexp = /^# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i
|
|
1319
|
-
if regexp =~ comment.text then
|
|
1320
|
-
comment.text = comment.text.sub(regexp, '')
|
|
1321
|
-
rw = case $1
|
|
1322
|
-
when 'attr_reader' then 'R'
|
|
1323
|
-
when 'attr_writer' then 'W'
|
|
1324
|
-
else 'RW'
|
|
1325
|
-
end
|
|
1326
|
-
name = $3 unless $3.empty?
|
|
1327
|
-
end
|
|
1328
|
-
|
|
1329
|
-
if name then
|
|
1330
|
-
att = create_attr context, single, name, rw, comment
|
|
1331
|
-
else
|
|
1332
|
-
args.each do |attr_name|
|
|
1333
|
-
att = create_attr context, single, attr_name, rw, comment
|
|
1334
|
-
end
|
|
1335
|
-
end
|
|
1336
|
-
|
|
1337
|
-
att
|
|
1338
|
-
end
|
|
1339
|
-
|
|
1340
|
-
##
|
|
1341
|
-
# Parses a meta-programmed method
|
|
1342
|
-
|
|
1343
|
-
def parse_meta_method(container, single, tk, comment)
|
|
1344
|
-
column = tk[:char_no]
|
|
1345
|
-
line_no = tk[:line_no]
|
|
1346
|
-
|
|
1347
|
-
start_collecting_tokens(:ruby)
|
|
1348
|
-
add_token tk
|
|
1349
|
-
add_token_listener self
|
|
1350
|
-
|
|
1351
|
-
skip_tkspace_without_nl
|
|
1352
|
-
|
|
1353
|
-
comment.text = comment.text.sub(/(^# +:?)(singleton-)(method:)/, '\1\3')
|
|
1354
|
-
singleton = !!$~
|
|
1355
|
-
|
|
1356
|
-
name = parse_meta_method_name comment, tk
|
|
1357
|
-
|
|
1358
|
-
return unless name
|
|
1359
|
-
|
|
1360
|
-
meth = RDoc::MetaMethod.new get_tkread, name, singleton: singleton
|
|
1361
|
-
record_location meth
|
|
1362
|
-
meth.line = line_no
|
|
1363
|
-
|
|
1364
|
-
remove_token_listener self
|
|
1365
|
-
|
|
1366
|
-
meth.start_collecting_tokens(:ruby)
|
|
1367
|
-
indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
|
|
1368
|
-
position_comment = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
|
|
1369
|
-
position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
|
|
1370
|
-
newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
|
|
1371
|
-
meth.add_tokens [position_comment, newline, indent]
|
|
1372
|
-
meth.add_tokens @token_stream
|
|
1373
|
-
|
|
1374
|
-
parse_meta_method_params container, single, meth, tk, comment
|
|
1375
|
-
|
|
1376
|
-
meth.comment = comment
|
|
1377
|
-
|
|
1378
|
-
@stats.add_method meth
|
|
1379
|
-
|
|
1380
|
-
meth
|
|
1381
|
-
end
|
|
1382
|
-
|
|
1383
|
-
##
|
|
1384
|
-
# Parses the name of a metaprogrammed method. +comment+ is used to
|
|
1385
|
-
# determine the name while +tk+ is used in an error message if the name
|
|
1386
|
-
# cannot be determined.
|
|
1387
|
-
|
|
1388
|
-
def parse_meta_method_name(comment, tk) # :nodoc:
|
|
1389
|
-
if comment.text.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
|
|
1390
|
-
return $1 unless $1.empty?
|
|
1391
|
-
end
|
|
1392
|
-
|
|
1393
|
-
name_t = get_tk
|
|
1394
|
-
|
|
1395
|
-
if :on_symbol == name_t[:kind] then
|
|
1396
|
-
name_t[:text][1..-1]
|
|
1397
|
-
elsif :on_tstring == name_t[:kind] then
|
|
1398
|
-
name_t[:text][1..-2]
|
|
1399
|
-
elsif :on_op == name_t[:kind] && '=' == name_t[:text] then # ignore
|
|
1400
|
-
remove_token_listener self
|
|
1401
|
-
|
|
1402
|
-
nil
|
|
1403
|
-
else
|
|
1404
|
-
warn "unknown name token #{name_t.inspect} for meta-method '#{tk[:text]}'"
|
|
1405
|
-
'unknown'
|
|
1406
|
-
end
|
|
1407
|
-
end
|
|
1408
|
-
|
|
1409
|
-
##
|
|
1410
|
-
# Parses the parameters and block for a meta-programmed method.
|
|
1411
|
-
|
|
1412
|
-
def parse_meta_method_params(container, single, meth, tk, comment) # :nodoc:
|
|
1413
|
-
token_listener meth do
|
|
1414
|
-
meth.params = ''
|
|
1415
|
-
|
|
1416
|
-
look_for_directives_in meth, comment
|
|
1417
|
-
comment.normalize
|
|
1418
|
-
meth.call_seq = comment.extract_call_seq
|
|
1419
|
-
|
|
1420
|
-
container.add_method meth
|
|
1421
|
-
|
|
1422
|
-
last_tk = tk
|
|
1423
|
-
|
|
1424
|
-
while tk = get_tk do
|
|
1425
|
-
if :on_semicolon == tk[:kind] then
|
|
1426
|
-
break
|
|
1427
|
-
elsif :on_nl == tk[:kind] then
|
|
1428
|
-
break unless last_tk and :on_comma == last_tk[:kind]
|
|
1429
|
-
elsif :on_sp == tk[:kind] then
|
|
1430
|
-
# expression continues
|
|
1431
|
-
elsif :on_kw == tk[:kind] && 'do' == tk[:text] then
|
|
1432
|
-
parse_statements container, single, meth
|
|
1433
|
-
break
|
|
1434
|
-
else
|
|
1435
|
-
last_tk = tk
|
|
1436
|
-
end
|
|
1437
|
-
end
|
|
1438
|
-
end
|
|
1439
|
-
end
|
|
1440
|
-
|
|
1441
|
-
##
|
|
1442
|
-
# Parses a normal method defined by +def+
|
|
1443
|
-
|
|
1444
|
-
def parse_method(container, single, tk, comment)
|
|
1445
|
-
singleton = nil
|
|
1446
|
-
added_container = false
|
|
1447
|
-
name = nil
|
|
1448
|
-
column = tk[:char_no]
|
|
1449
|
-
line_no = tk[:line_no]
|
|
1450
|
-
|
|
1451
|
-
start_collecting_tokens(:ruby)
|
|
1452
|
-
add_token tk
|
|
1453
|
-
|
|
1454
|
-
token_listener self do
|
|
1455
|
-
prev_container = container
|
|
1456
|
-
name, container, singleton = parse_method_name container
|
|
1457
|
-
added_container = container != prev_container
|
|
1458
|
-
end
|
|
1459
|
-
|
|
1460
|
-
return unless name
|
|
1461
|
-
|
|
1462
|
-
meth = RDoc::AnyMethod.new get_tkread, name, singleton: single == SINGLE ? true : singleton
|
|
1463
|
-
look_for_directives_in meth, comment
|
|
1464
|
-
if singleton
|
|
1465
|
-
# `current_line_visibility' is useless because it works against
|
|
1466
|
-
# the normal method named as same as the singleton method, after
|
|
1467
|
-
# the latter was defined. Of course these are different things.
|
|
1468
|
-
container.current_line_visibility = :public
|
|
1469
|
-
end
|
|
1470
|
-
|
|
1471
|
-
record_location meth
|
|
1472
|
-
meth.line = line_no
|
|
1473
|
-
|
|
1474
|
-
meth.start_collecting_tokens(:ruby)
|
|
1475
|
-
indent = RDoc::Parser::RipperStateLex::Token.new(1, 1, :on_sp, ' ' * column)
|
|
1476
|
-
token = RDoc::Parser::RipperStateLex::Token.new(line_no, 1, :on_comment)
|
|
1477
|
-
token[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
|
|
1478
|
-
newline = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
|
|
1479
|
-
meth.add_tokens [token, newline, indent]
|
|
1480
|
-
meth.add_tokens @token_stream
|
|
1481
|
-
|
|
1482
|
-
parse_method_params_and_body container, single, meth, added_container
|
|
1483
|
-
|
|
1484
|
-
comment.normalize
|
|
1485
|
-
meth.call_seq = comment.extract_call_seq
|
|
1486
|
-
|
|
1487
|
-
meth.comment = comment
|
|
1488
|
-
|
|
1489
|
-
# after end modifiers
|
|
1490
|
-
read_documentation_modifiers meth, RDoc::METHOD_MODIFIERS
|
|
1491
|
-
|
|
1492
|
-
@stats.add_method meth
|
|
1493
|
-
end
|
|
1494
|
-
|
|
1495
|
-
##
|
|
1496
|
-
# Parses the parameters and body of +meth+
|
|
1497
|
-
|
|
1498
|
-
def parse_method_params_and_body(container, single, meth, added_container)
|
|
1499
|
-
token_listener meth do
|
|
1500
|
-
parse_method_parameters meth
|
|
1501
|
-
|
|
1502
|
-
if meth.document_self or not @track_visibility then
|
|
1503
|
-
container.add_method meth
|
|
1504
|
-
elsif added_container then
|
|
1505
|
-
container.document_self = false
|
|
1506
|
-
end
|
|
1507
|
-
|
|
1508
|
-
# Having now read the method parameters and documentation modifiers, we
|
|
1509
|
-
# now know whether we have to rename #initialize to ::new
|
|
1510
|
-
|
|
1511
|
-
if meth.name == "initialize" && !meth.singleton then
|
|
1512
|
-
if meth.dont_rename_initialize then
|
|
1513
|
-
meth.visibility = :protected
|
|
1514
|
-
else
|
|
1515
|
-
meth.singleton = true
|
|
1516
|
-
meth.name = "new"
|
|
1517
|
-
meth.visibility = :public
|
|
1518
|
-
end
|
|
1519
|
-
end
|
|
1520
|
-
|
|
1521
|
-
parse_statements container, single, meth
|
|
1522
|
-
end
|
|
1523
|
-
end
|
|
1524
|
-
|
|
1525
|
-
##
|
|
1526
|
-
# Parses a method that needs to be ignored.
|
|
1527
|
-
|
|
1528
|
-
def parse_method_dummy(container)
|
|
1529
|
-
dummy = RDoc::Context.new
|
|
1530
|
-
dummy.parent = container
|
|
1531
|
-
dummy.store = container.store
|
|
1532
|
-
skip_method dummy
|
|
1533
|
-
end
|
|
1534
|
-
|
|
1535
|
-
##
|
|
1536
|
-
# Parses the name of a method in +container+.
|
|
1537
|
-
#
|
|
1538
|
-
# Returns the method name, the container it is in (for def Foo.name) and if
|
|
1539
|
-
# it is a singleton or regular method.
|
|
1540
|
-
|
|
1541
|
-
def parse_method_name(container) # :nodoc:
|
|
1542
|
-
skip_tkspace
|
|
1543
|
-
name_t = get_tk
|
|
1544
|
-
back_tk = skip_tkspace_without_nl
|
|
1545
|
-
singleton = false
|
|
1546
|
-
|
|
1547
|
-
dot = get_tk
|
|
1548
|
-
if dot[:kind] == :on_period || (dot[:kind] == :on_op && dot[:text] == '::') then
|
|
1549
|
-
singleton = true
|
|
1550
|
-
|
|
1551
|
-
name, container = parse_method_name_singleton container, name_t
|
|
1552
|
-
else
|
|
1553
|
-
unget_tk dot
|
|
1554
|
-
back_tk.reverse_each do |token|
|
|
1555
|
-
unget_tk token
|
|
1556
|
-
end
|
|
1557
|
-
|
|
1558
|
-
name = parse_method_name_regular container, name_t
|
|
1559
|
-
end
|
|
1560
|
-
|
|
1561
|
-
return name, container, singleton
|
|
1562
|
-
end
|
|
1563
|
-
|
|
1564
|
-
##
|
|
1565
|
-
# For the given +container+ and initial name token +name_t+ the method name
|
|
1566
|
-
# is parsed from the token stream for a regular method.
|
|
1567
|
-
|
|
1568
|
-
def parse_method_name_regular(container, name_t) # :nodoc:
|
|
1569
|
-
if :on_op == name_t[:kind] && (%w{* & [] []= <<}.include?(name_t[:text])) then
|
|
1570
|
-
name_t[:text]
|
|
1571
|
-
else
|
|
1572
|
-
unless [:on_kw, :on_const, :on_ident].include?(name_t[:kind]) then
|
|
1573
|
-
warn "expected method name token, . or ::, got #{name_t.inspect}"
|
|
1574
|
-
skip_method container
|
|
1575
|
-
return
|
|
1576
|
-
end
|
|
1577
|
-
name_t[:text]
|
|
1578
|
-
end
|
|
1579
|
-
end
|
|
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.
|
|
1580
128
|
|
|
1581
|
-
|
|
1582
|
-
# For the given +container+ and initial name token +name_t+ the method name
|
|
1583
|
-
# and the new +container+ (if necessary) are parsed from the token stream
|
|
1584
|
-
# for a singleton method.
|
|
1585
|
-
|
|
1586
|
-
def parse_method_name_singleton(container, name_t) # :nodoc:
|
|
1587
|
-
skip_tkspace
|
|
1588
|
-
name_t2 = get_tk
|
|
1589
|
-
|
|
1590
|
-
if (:on_kw == name_t[:kind] && 'self' == name_t[:text]) || (:on_op == name_t[:kind] && '%' == name_t[:text]) then
|
|
1591
|
-
# NOTE: work around '[' being consumed early
|
|
1592
|
-
if :on_lbracket == name_t2[:kind]
|
|
1593
|
-
get_tk
|
|
1594
|
-
name = '[]'
|
|
1595
|
-
else
|
|
1596
|
-
name = name_t2[:text]
|
|
1597
|
-
end
|
|
1598
|
-
elsif :on_const == name_t[:kind] then
|
|
1599
|
-
name = name_t2[:text]
|
|
129
|
+
class Ruby < Parser
|
|
1600
130
|
|
|
1601
|
-
|
|
131
|
+
parse_files_matching(/\.rbw?$/)
|
|
1602
132
|
|
|
1603
|
-
|
|
133
|
+
# Matches an RBS inline type annotation line: #: followed by whitespace
|
|
134
|
+
RBS_SIG_LINE = /\A#:\s/ # :nodoc:
|
|
1604
135
|
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
parse_method_dummy container
|
|
136
|
+
attr_accessor :visibility
|
|
137
|
+
attr_reader :container, :singleton, :in_proc_block
|
|
1608
138
|
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
klass_name = "#{name_t[:text].capitalize}Class"
|
|
1612
|
-
container = @store.find_class_named klass_name
|
|
1613
|
-
container ||= @top_level.add_class RDoc::NormalClass, klass_name
|
|
139
|
+
def initialize(top_level, content, options, stats)
|
|
140
|
+
super
|
|
1614
141
|
|
|
1615
|
-
|
|
1616
|
-
else
|
|
1617
|
-
warn "unexpected method name token #{name_t.inspect}"
|
|
1618
|
-
# break
|
|
1619
|
-
skip_method container
|
|
142
|
+
content = handle_tab_width(content)
|
|
1620
143
|
|
|
1621
|
-
|
|
1622
|
-
|
|
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
|
|
1623
152
|
|
|
1624
|
-
|
|
1625
|
-
|
|
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
|
|
1626
160
|
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
nest -= 1
|
|
1652
|
-
if nest <= 0
|
|
1653
|
-
# we might have a.each { |i| yield i }
|
|
1654
|
-
unget_tk(tk) if nest < 0
|
|
1655
|
-
break
|
|
1656
|
-
end
|
|
1657
|
-
when :on_lparen then
|
|
1658
|
-
nest += 1
|
|
1659
|
-
when end_token[:kind] then
|
|
1660
|
-
if end_token[:kind] == :on_rparen
|
|
1661
|
-
nest -= 1
|
|
1662
|
-
break if nest <= 0
|
|
1663
|
-
else
|
|
1664
|
-
break
|
|
1665
|
-
end
|
|
1666
|
-
when :on_rparen then
|
|
1667
|
-
nest -= 1
|
|
1668
|
-
when :on_comment, :on_embdoc then
|
|
1669
|
-
@read.pop
|
|
1670
|
-
if :on_nl == end_token[:kind] and "\n" == tk[:text][-1] and
|
|
1671
|
-
(!continue or (tk[:state] & Ripper::EXPR_LABEL) != 0) then
|
|
1672
|
-
if method && method.block_params.nil? then
|
|
1673
|
-
unget_tk tk
|
|
1674
|
-
read_documentation_modifiers method, modifiers
|
|
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
|
|
1675
185
|
end
|
|
1676
|
-
break if !continue and nest <= 0
|
|
1677
186
|
end
|
|
1678
|
-
when :on_comma then
|
|
1679
|
-
continue = true
|
|
1680
|
-
when :on_ident then
|
|
1681
|
-
continue = false if continue
|
|
1682
187
|
end
|
|
1683
|
-
tk = get_tk
|
|
1684
|
-
end
|
|
1685
|
-
|
|
1686
|
-
get_tkread_clean(/\s+/, ' ')
|
|
1687
|
-
end
|
|
1688
188
|
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
# containing:
|
|
1692
|
-
#
|
|
1693
|
-
# # yields: ....
|
|
1694
|
-
#
|
|
1695
|
-
# and add this as the block_params for the method
|
|
189
|
+
# Returns true if code objects at the current position should not be
|
|
190
|
+
# documented, that is, inside a :stopdoc: or :enddoc: region.
|
|
1696
191
|
|
|
1697
|
-
|
|
1698
|
-
|
|
192
|
+
def document_suppressed?
|
|
193
|
+
@track_visibility && @doc_state != :startdoc
|
|
194
|
+
end
|
|
1699
195
|
|
|
1700
|
-
|
|
1701
|
-
|
|
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.
|
|
1702
199
|
|
|
1703
|
-
|
|
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
|
|
1704
206
|
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
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 }`
|
|
1708
210
|
|
|
1709
|
-
|
|
1710
|
-
|
|
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
|
|
1711
217
|
|
|
1712
|
-
|
|
1713
|
-
|
|
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
|
|
1714
240
|
|
|
1715
|
-
|
|
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.
|
|
1716
243
|
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
244
|
+
def record_location(container) # :nodoc:
|
|
245
|
+
case container
|
|
246
|
+
when ClassModule
|
|
247
|
+
@top_level.add_to_classes_or_modules container
|
|
248
|
+
end
|
|
1720
249
|
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
parse_statements mod
|
|
250
|
+
container.record_location @top_level
|
|
251
|
+
end
|
|
1724
252
|
|
|
1725
|
-
|
|
1726
|
-
read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS
|
|
253
|
+
# Scans this Ruby file for Ruby constructs
|
|
1727
254
|
|
|
1728
|
-
|
|
1729
|
-
|
|
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
|
|
1730
263
|
|
|
1731
|
-
|
|
1732
|
-
|
|
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
|
|
1733
268
|
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
269
|
+
@program_node.accept(RDocVisitor.new(self, @top_level, @store))
|
|
270
|
+
process_comments_until(@lines.size + 1)
|
|
271
|
+
end
|
|
1737
272
|
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
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
|
|
1742
278
|
|
|
1743
|
-
|
|
279
|
+
# Assign AST node to a line.
|
|
280
|
+
# This is used to show meta-method source code in the documentation.
|
|
1744
281
|
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
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
|
|
1751
291
|
|
|
1752
|
-
|
|
1753
|
-
|
|
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
|
|
1754
345
|
|
|
1755
|
-
|
|
1756
|
-
|
|
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
|
|
1757
354
|
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
when :on_nl, :on_semicolon, :on_comment then
|
|
1761
|
-
break
|
|
1762
|
-
when :on_comma then
|
|
1763
|
-
skip_tkspace_without_nl
|
|
355
|
+
# Creates an RDoc::Method on +container+ from +comment+ if there is a
|
|
356
|
+
# Signature section in the comment
|
|
1764
357
|
|
|
1765
|
-
|
|
1766
|
-
|
|
358
|
+
def parse_comment_tomdoc(container, comment, line_no, start_line)
|
|
359
|
+
return if document_suppressed?
|
|
360
|
+
return unless signature = TomDoc.signature(comment)
|
|
1767
361
|
|
|
1768
|
-
|
|
1769
|
-
end
|
|
1770
|
-
end
|
|
362
|
+
name, = signature.split %r%[ \(]%, 2
|
|
1771
363
|
|
|
1772
|
-
|
|
1773
|
-
|
|
364
|
+
meth = AnyMethod.new name
|
|
365
|
+
record_location(meth)
|
|
366
|
+
meth.line = start_line
|
|
367
|
+
meth.call_seq = signature
|
|
368
|
+
return unless meth.name
|
|
1774
369
|
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
else
|
|
1779
|
-
tk[:text]
|
|
1780
|
-
end
|
|
1781
|
-
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)
|
|
1782
373
|
|
|
1783
|
-
|
|
1784
|
-
|
|
374
|
+
container.add_method meth
|
|
375
|
+
meth.comment = comment
|
|
376
|
+
@stats.add_method meth
|
|
377
|
+
end
|
|
1785
378
|
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
comment = RDoc::Encoding.change_encoding comment, @encoding if @encoding
|
|
379
|
+
def has_modifier_nodoc?(line_no) # :nodoc:
|
|
380
|
+
@modifier_comments[line_no]&.match?(/\A#\s*:nodoc:/)
|
|
381
|
+
end
|
|
1790
382
|
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
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
|
|
1794
389
|
|
|
1795
|
-
|
|
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
|
|
1796
395
|
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
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
|
|
1800
400
|
|
|
1801
|
-
|
|
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
|
|
408
|
+
end
|
|
1802
409
|
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
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
|
|
1823
433
|
end
|
|
1824
434
|
end
|
|
1825
435
|
|
|
1826
|
-
if
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
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)
|
|
1834
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
|
|
1835
466
|
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
comment += comment_body
|
|
1841
|
-
comment << "\n" unless comment_body =~ /\n\z/
|
|
1842
|
-
|
|
1843
|
-
if comment_body.size > 1 && comment_body =~ /\n\z/ then
|
|
1844
|
-
skip_tkspace_without_nl # leading spaces
|
|
1845
|
-
end
|
|
1846
|
-
tk = get_tk
|
|
1847
|
-
end
|
|
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
|
|
1848
471
|
|
|
1849
|
-
|
|
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
|
|
1850
477
|
|
|
1851
|
-
|
|
1852
|
-
|
|
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
|
|
1853
489
|
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
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)
|
|
1858
502
|
end
|
|
503
|
+
end
|
|
504
|
+
end
|
|
1859
505
|
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
506
|
+
# Skips all undocumentable consecutive comments until the given line number.
|
|
507
|
+
# Undocumentable comments are comments written inside `def` or inside undocumentable class/module
|
|
508
|
+
|
|
509
|
+
def skip_comments_until(line_no_until)
|
|
510
|
+
while !@unprocessed_comments.empty? && @unprocessed_comments.first[0] <= line_no_until
|
|
511
|
+
@unprocessed_comments.shift
|
|
1863
512
|
end
|
|
513
|
+
end
|
|
1864
514
|
|
|
1865
|
-
|
|
1866
|
-
keep_comment = true
|
|
1867
|
-
container.current_line_visibility = nil
|
|
515
|
+
# Returns consecutive comment linked to the given line number
|
|
1868
516
|
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
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)
|
|
521
|
+
end
|
|
1873
522
|
|
|
1874
|
-
|
|
1875
|
-
|
|
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]
|
|
543
|
+
end
|
|
1876
544
|
|
|
1877
|
-
|
|
1878
|
-
|
|
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
|
|
1879
548
|
|
|
1880
|
-
|
|
1881
|
-
|
|
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
|
|
555
|
+
end
|
|
1882
556
|
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
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
|
|
1886
569
|
else
|
|
1887
|
-
|
|
1888
|
-
end
|
|
1889
|
-
|
|
1890
|
-
when 'until', 'while' then
|
|
1891
|
-
if (tk[:state] & Ripper::EXPR_LABEL) == 0
|
|
1892
|
-
nest += 1
|
|
1893
|
-
skip_optional_do_after_expression
|
|
570
|
+
m.visibility = visibility
|
|
1894
571
|
end
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
skip_for_variable
|
|
1904
|
-
skip_optional_do_after_expression
|
|
1905
|
-
|
|
1906
|
-
when 'case', 'do', 'if', 'unless', 'begin' then
|
|
1907
|
-
if (tk[:state] & Ripper::EXPR_LABEL) == 0
|
|
1908
|
-
nest += 1
|
|
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)
|
|
1909
580
|
end
|
|
581
|
+
end
|
|
582
|
+
end
|
|
1910
583
|
|
|
1911
|
-
|
|
1912
|
-
current_method.calls_super = true if current_method
|
|
1913
|
-
|
|
1914
|
-
when 'rescue' then
|
|
1915
|
-
parse_rescue
|
|
1916
|
-
|
|
1917
|
-
when 'end' then
|
|
1918
|
-
nest -= 1
|
|
1919
|
-
if nest == 0 then
|
|
1920
|
-
container.ongoing_visibility = save_visibility
|
|
584
|
+
# Handles `module_function :foo, :bar`
|
|
1921
585
|
|
|
1922
|
-
|
|
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?
|
|
1923
591
|
|
|
1924
|
-
|
|
1925
|
-
|
|
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
|
|
1926
598
|
end
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
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
|
|
1931
607
|
end
|
|
608
|
+
end
|
|
1932
609
|
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
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)
|
|
1936
616
|
end
|
|
617
|
+
end
|
|
1937
618
|
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
619
|
+
# Handles `alias foo bar` and `alias_method :foo, :bar`
|
|
620
|
+
|
|
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?
|
|
626
|
+
|
|
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)
|
|
1947
635
|
end
|
|
1948
|
-
|
|
1949
|
-
else
|
|
1950
|
-
try_parse_comment = nest == 1
|
|
1951
636
|
end
|
|
1952
637
|
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
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
|
|
1958
660
|
end
|
|
1959
661
|
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
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
|
|
1965
679
|
end
|
|
1966
680
|
|
|
1967
|
-
|
|
1968
|
-
end
|
|
1969
|
-
|
|
1970
|
-
container.params = nil
|
|
1971
|
-
container.block_params = nil
|
|
1972
|
-
end
|
|
1973
|
-
|
|
1974
|
-
##
|
|
1975
|
-
# Parse up to +no+ symbol arguments
|
|
1976
|
-
|
|
1977
|
-
def parse_symbol_arg(no = nil)
|
|
1978
|
-
skip_tkspace_comment
|
|
681
|
+
# Handle `include Foo, Bar`
|
|
1979
682
|
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
else
|
|
1984
|
-
parse_symbol_arg_space no, tk
|
|
1985
|
-
end
|
|
1986
|
-
end
|
|
1987
|
-
|
|
1988
|
-
##
|
|
1989
|
-
# Parses up to +no+ symbol arguments surrounded by () and places them in
|
|
1990
|
-
# +args+.
|
|
683
|
+
def add_includes(names, line_no) # :nodoc:
|
|
684
|
+
add_includes_extends(names, Include, line_no)
|
|
685
|
+
end
|
|
1991
686
|
|
|
1992
|
-
|
|
1993
|
-
args = []
|
|
687
|
+
# Handle `extend Foo, Bar`
|
|
1994
688
|
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
if tk1 = parse_symbol_in_arg
|
|
1998
|
-
args.push tk1
|
|
1999
|
-
break if no and args.size >= no
|
|
689
|
+
def add_extends(names, line_no) # :nodoc:
|
|
690
|
+
add_includes_extends(names, Extend, line_no)
|
|
2000
691
|
end
|
|
2001
692
|
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
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
|
+
)
|
|
2010
718
|
end
|
|
2011
|
-
end
|
|
2012
|
-
|
|
2013
|
-
args
|
|
2014
|
-
end
|
|
2015
|
-
|
|
2016
|
-
##
|
|
2017
|
-
# Parses up to +no+ symbol arguments separated by spaces and places them in
|
|
2018
|
-
# +args+.
|
|
2019
719
|
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
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)
|
|
2028
729
|
|
|
2029
|
-
|
|
2030
|
-
skip_tkspace_without_nl
|
|
730
|
+
mark_container_documentable(container)
|
|
2031
731
|
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
unget_tk tk1
|
|
2035
|
-
break
|
|
732
|
+
if directives && (call_seq, = directives['call-seq'])
|
|
733
|
+
meth.call_seq = call_seq.lines.map(&:chomp).reject(&:empty?).join("\n") if call_seq
|
|
2036
734
|
end
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
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
|
|
2042
754
|
end
|
|
2043
|
-
end
|
|
2044
|
-
|
|
2045
|
-
args
|
|
2046
|
-
end
|
|
2047
755
|
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
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
|
|
761
|
+
|
|
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
|
|
2067
802
|
|
|
2068
|
-
|
|
2069
|
-
comment = collect_first_comment
|
|
803
|
+
# Resolves constant path to a full path by searching module nesting
|
|
2070
804
|
|
|
2071
|
-
|
|
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
|
|
816
|
+
end
|
|
2072
817
|
|
|
2073
|
-
|
|
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]
|
|
832
|
+
end
|
|
833
|
+
end
|
|
2074
834
|
|
|
2075
|
-
|
|
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
|
|
2076
871
|
|
|
2077
|
-
|
|
2078
|
-
|
|
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
|
|
2079
900
|
|
|
2080
|
-
|
|
2081
|
-
|
|
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
|
|
2082
916
|
|
|
2083
|
-
|
|
2084
|
-
|
|
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
|
|
2085
939
|
|
|
2086
|
-
|
|
2087
|
-
vis_type, vis, singleton = get_visibility_information tk, single
|
|
940
|
+
private
|
|
2088
941
|
|
|
2089
|
-
|
|
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.
|
|
2090
946
|
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
# often writes
|
|
2094
|
-
#
|
|
2095
|
-
# protected unless $TESTING
|
|
2096
|
-
#
|
|
2097
|
-
if [:on_nl, :on_semicolon].include?(ptk[:kind]) || (:on_kw == ptk[:kind] && (['if', 'unless'].include?(ptk[:text]))) then
|
|
2098
|
-
container.ongoing_visibility = vis
|
|
2099
|
-
elsif :on_kw == ptk[:kind] && 'def' == ptk[:text]
|
|
2100
|
-
container.current_line_visibility = vis
|
|
2101
|
-
else
|
|
2102
|
-
update_visibility container, vis_type, vis, singleton
|
|
2103
|
-
end
|
|
2104
|
-
end
|
|
947
|
+
def extract_type_signature!(text, start_line)
|
|
948
|
+
return nil unless text.include?('#:')
|
|
2105
949
|
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
def parse_constant_visibility(container, single, tk)
|
|
2110
|
-
args = parse_symbol_arg
|
|
2111
|
-
case tk[:text]
|
|
2112
|
-
when 'private_constant'
|
|
2113
|
-
vis = :private
|
|
2114
|
-
when 'public_constant'
|
|
2115
|
-
vis = :public
|
|
2116
|
-
else
|
|
2117
|
-
raise RDoc::Error, 'Unreachable'
|
|
2118
|
-
end
|
|
2119
|
-
container.set_constant_visibility_for args, vis
|
|
2120
|
-
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?
|
|
2121
953
|
|
|
2122
|
-
|
|
2123
|
-
|
|
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?
|
|
2124
958
|
|
|
2125
|
-
|
|
2126
|
-
|
|
959
|
+
warn_invalid_type_signature(type_signature_lines, first_sig_line)
|
|
960
|
+
type_signature_lines
|
|
961
|
+
end
|
|
2127
962
|
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
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}"
|
|
968
|
+
end
|
|
969
|
+
end
|
|
2131
970
|
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
# or:
|
|
2139
|
-
#
|
|
2140
|
-
# class MyClass # :nodoc:
|
|
2141
|
-
#
|
|
2142
|
-
# We return the directive name and any parameters as a two element array if
|
|
2143
|
-
# the name is in +allowed+. A directive can be found anywhere up to the end
|
|
2144
|
-
# of the current line.
|
|
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
|
|
2145
977
|
|
|
2146
|
-
|
|
2147
|
-
|
|
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
|
|
2148
1041
|
|
|
2149
|
-
|
|
2150
|
-
|
|
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
|
|
1048
|
+
end
|
|
2151
1049
|
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
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)
|
|
1055
|
+
end
|
|
2156
1056
|
|
|
2157
|
-
|
|
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
|
|
2158
1071
|
|
|
2159
|
-
|
|
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
|
|
2160
1089
|
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
unless tokens.length == 1 and (:on_comment == tokens.first[:kind] or :on_embdoc == tokens.first[:kind]) then
|
|
2166
|
-
tokens.reverse_each do |token|
|
|
2167
|
-
unget_tk token
|
|
2168
|
-
end
|
|
2169
|
-
end
|
|
2170
|
-
end
|
|
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)
|
|
2171
1094
|
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
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
|
|
2177
1100
|
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
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
|
|
1125
|
+
end
|
|
2181
1126
|
|
|
2182
|
-
|
|
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)
|
|
1132
|
+
|
|
1133
|
+
return if @scanner.in_proc_block
|
|
1134
|
+
|
|
1135
|
+
case node.receiver
|
|
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
|
|
1166
|
+
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
|
|
2183
1186
|
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
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
|
|
2187
1203
|
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
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
|
|
2192
1217
|
|
|
2193
|
-
|
|
2194
|
-
# Records the location of this +container+ in the file for this parser and
|
|
2195
|
-
# adds it to the list of classes and modules in the file.
|
|
1218
|
+
private
|
|
2196
1219
|
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
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
|
|
2202
1225
|
|
|
2203
|
-
|
|
2204
|
-
|
|
1226
|
+
def call_node_name_arguments(call_node)
|
|
1227
|
+
@scanner.call_node_name_arguments(call_node)
|
|
1228
|
+
end
|
|
2205
1229
|
|
|
2206
|
-
|
|
2207
|
-
|
|
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 }
|
|
1234
|
+
end
|
|
2208
1235
|
|
|
2209
|
-
|
|
2210
|
-
|
|
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
|
|
2211
1259
|
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
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
|
|
2215
1269
|
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
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))
|
|
2222
1276
|
end
|
|
2223
1277
|
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
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
|
|
2228
1282
|
end
|
|
2229
|
-
first_tk_index = @scanner.find_index { |tk| tk[:line_no] == now_line_no }
|
|
2230
|
-
last_tk_index = @scanner.find_index { |tk| tk[:line_no] == now_line_no + 1 }
|
|
2231
|
-
last_tk_index = last_tk_index ? last_tk_index - 1 : @scanner.size - 1
|
|
2232
|
-
code = @scanner[first_tk_index..last_tk_index].map{ |t| t[:text] }.join
|
|
2233
1283
|
|
|
2234
|
-
|
|
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
|
|
2235
1289
|
|
|
2236
|
-
|
|
2237
|
-
|
|
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
|
|
2238
1300
|
|
|
2239
|
-
|
|
1301
|
+
def _visit_call_alias_method(call_node)
|
|
1302
|
+
return if @scanner.in_proc_block
|
|
2240
1303
|
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
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)
|
|
2244
1307
|
end
|
|
2245
1308
|
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
end
|
|
1309
|
+
def _visit_call_include(call_node)
|
|
1310
|
+
return if @scanner.in_proc_block
|
|
2249
1311
|
|
|
2250
|
-
|
|
2251
|
-
|
|
1312
|
+
names = constant_arguments_names(call_node)
|
|
1313
|
+
line_no = call_node.location.start_line
|
|
1314
|
+
return unless names
|
|
2252
1315
|
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
tk = get_tk
|
|
2259
|
-
|
|
2260
|
-
b_nest = 0
|
|
2261
|
-
nest = 0
|
|
2262
|
-
|
|
2263
|
-
loop do
|
|
2264
|
-
break unless tk
|
|
2265
|
-
case tk[:kind]
|
|
2266
|
-
when :on_semicolon, :on_nl, :on_ignored_nl then
|
|
2267
|
-
break if b_nest.zero?
|
|
2268
|
-
when :on_lparen then
|
|
2269
|
-
nest += 1
|
|
2270
|
-
when :on_rparen then
|
|
2271
|
-
nest -= 1
|
|
2272
|
-
when :on_kw then
|
|
2273
|
-
case tk[:text]
|
|
2274
|
-
when 'begin'
|
|
2275
|
-
b_nest += 1
|
|
2276
|
-
when 'end'
|
|
2277
|
-
b_nest -= 1
|
|
2278
|
-
when 'do'
|
|
2279
|
-
break if nest.zero?
|
|
2280
|
-
end
|
|
2281
|
-
when :on_comment, :on_embdoc then
|
|
2282
|
-
if b_nest.zero? and "\n" == tk[:text][-1] then
|
|
2283
|
-
break
|
|
1316
|
+
if @scanner.singleton
|
|
1317
|
+
@scanner.add_extends(names, line_no)
|
|
1318
|
+
else
|
|
1319
|
+
@scanner.add_includes(names, line_no)
|
|
1320
|
+
end
|
|
2284
1321
|
end
|
|
2285
|
-
end
|
|
2286
|
-
tk = get_tk
|
|
2287
|
-
end
|
|
2288
1322
|
|
|
2289
|
-
|
|
1323
|
+
def _visit_call_extend(call_node)
|
|
1324
|
+
return if @scanner.in_proc_block
|
|
2290
1325
|
|
|
2291
|
-
|
|
2292
|
-
|
|
1326
|
+
names = constant_arguments_names(call_node)
|
|
1327
|
+
@scanner.add_extends(names, call_node.location.start_line) if names && !@scanner.singleton
|
|
1328
|
+
end
|
|
2293
1329
|
|
|
2294
|
-
|
|
2295
|
-
|
|
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
|
|
2296
1335
|
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
unget_tk(tk) unless :on_kw == tk[:kind] and 'in' == tk[:text]
|
|
2303
|
-
end
|
|
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
|
|
2304
1341
|
|
|
2305
|
-
|
|
2306
|
-
|
|
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
|
|
1346
|
+
end
|
|
2307
1347
|
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
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
|
|
2313
1358
|
|
|
2314
|
-
|
|
2315
|
-
# Skip spaces until a comment is found
|
|
1359
|
+
attr_reader :params, :yields, :calls_super
|
|
2316
1360
|
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
get_tk
|
|
2323
|
-
end
|
|
2324
|
-
end
|
|
1361
|
+
def initialize
|
|
1362
|
+
@params = nil
|
|
1363
|
+
@calls_super = false
|
|
1364
|
+
@yields = []
|
|
1365
|
+
end
|
|
2325
1366
|
|
|
2326
|
-
|
|
2327
|
-
|
|
1367
|
+
def visit_def_node(node)
|
|
1368
|
+
# stop traverse inside nested def
|
|
1369
|
+
end
|
|
2328
1370
|
|
|
2329
|
-
|
|
2330
|
-
|
|
1371
|
+
def visit_yield_node(node)
|
|
1372
|
+
@yields << (node.arguments&.slice || '')
|
|
1373
|
+
end
|
|
2331
1374
|
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
1375
|
+
def visit_super_node(node)
|
|
1376
|
+
@calls_super = true
|
|
1377
|
+
super
|
|
1378
|
+
end
|
|
2336
1379
|
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
s_m.singleton = true
|
|
2341
|
-
new_methods << s_m
|
|
2342
|
-
end
|
|
2343
|
-
when 'public_class_method', 'private_class_method' then
|
|
2344
|
-
args = parse_symbol_arg
|
|
2345
|
-
|
|
2346
|
-
container.methods_matching args, true do |m|
|
|
2347
|
-
if m.parent != container then
|
|
2348
|
-
m = m.dup
|
|
2349
|
-
record_location m
|
|
2350
|
-
new_methods << m
|
|
1380
|
+
def visit_forwarding_super_node(node)
|
|
1381
|
+
@calls_super = true
|
|
1382
|
+
end
|
|
2351
1383
|
end
|
|
2352
|
-
|
|
2353
|
-
m.visibility = vis
|
|
2354
|
-
end
|
|
2355
|
-
else
|
|
2356
|
-
args = parse_symbol_arg
|
|
2357
|
-
container.set_visibility_for args, vis, singleton
|
|
2358
|
-
end
|
|
2359
|
-
|
|
2360
|
-
new_methods.each do |method|
|
|
2361
|
-
case method
|
|
2362
|
-
when RDoc::AnyMethod then
|
|
2363
|
-
container.add_method method
|
|
2364
|
-
when RDoc::Attr then
|
|
2365
|
-
container.add_attribute method
|
|
2366
1384
|
end
|
|
2367
|
-
method.visibility = vis
|
|
2368
1385
|
end
|
|
2369
1386
|
end
|
|
2370
|
-
|
|
2371
|
-
##
|
|
2372
|
-
# Prints +message+ to +$stderr+ unless we're being quiet
|
|
2373
|
-
|
|
2374
|
-
def warn(message)
|
|
2375
|
-
@options.warn make_message message
|
|
2376
|
-
end
|
|
2377
|
-
|
|
2378
1387
|
end
|