rdoc 7.1.0 → 8.0.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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +3 -4
  3. data/LICENSE.rdoc +4 -0
  4. data/README.md +43 -2
  5. data/doc/markup_reference/markdown.md +104 -3
  6. data/lib/rdoc/code_object/alias.rb +2 -8
  7. data/lib/rdoc/code_object/any_method.rb +11 -6
  8. data/lib/rdoc/code_object/attr.rb +12 -6
  9. data/lib/rdoc/code_object/class_module.rb +62 -32
  10. data/lib/rdoc/code_object/constant.rb +29 -3
  11. data/lib/rdoc/code_object/context/section.rb +30 -43
  12. data/lib/rdoc/code_object/context.rb +53 -37
  13. data/lib/rdoc/code_object/method_attr.rb +9 -15
  14. data/lib/rdoc/code_object/mixin.rb +3 -0
  15. data/lib/rdoc/code_object/top_level.rb +11 -3
  16. data/lib/rdoc/code_object.rb +2 -4
  17. data/lib/rdoc/comment.rb +1 -66
  18. data/lib/rdoc/cross_reference.rb +8 -30
  19. data/lib/rdoc/encoding.rb +3 -3
  20. data/lib/rdoc/generator/aliki.rb +17 -0
  21. data/lib/rdoc/generator/darkfish.rb +12 -6
  22. data/lib/rdoc/generator/json_index.rb +2 -2
  23. data/lib/rdoc/generator/markup.rb +56 -31
  24. data/lib/rdoc/generator/template/aliki/DESIGN.md +536 -0
  25. data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +1 -1
  26. data/lib/rdoc/generator/template/aliki/_head.rhtml +6 -1
  27. data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +8 -6
  28. data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +8 -6
  29. data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +1 -1
  30. data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +2 -2
  31. data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +1 -1
  32. data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +1 -1
  33. data/lib/rdoc/generator/template/aliki/class.rhtml +61 -51
  34. data/lib/rdoc/generator/template/aliki/css/rdoc.css +365 -147
  35. data/lib/rdoc/generator/template/aliki/index.rhtml +1 -1
  36. data/lib/rdoc/generator/template/aliki/js/aliki.js +28 -20
  37. data/lib/rdoc/generator/template/aliki/js/bash_highlighter.js +167 -0
  38. data/lib/rdoc/generator/template/aliki/js/search_controller.js +1 -1
  39. data/lib/rdoc/generator/template/aliki/page.rhtml +1 -1
  40. data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +1 -1
  41. data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +2 -2
  42. data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +8 -6
  43. data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +8 -6
  44. data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +1 -1
  45. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +1 -1
  46. data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +1 -1
  47. data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +5 -5
  48. data/lib/rdoc/generator/template/darkfish/class.rhtml +18 -21
  49. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +0 -1
  50. data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +3 -3
  51. data/lib/rdoc/i18n/text.rb +3 -3
  52. data/lib/rdoc/markdown.kpeg +36 -17
  53. data/lib/rdoc/markdown.rb +324 -125
  54. data/lib/rdoc/markup/document.rb +2 -2
  55. data/lib/rdoc/markup/formatter.rb +128 -115
  56. data/lib/rdoc/markup/heading.rb +3 -6
  57. data/lib/rdoc/markup/indented_paragraph.rb +1 -1
  58. data/lib/rdoc/markup/inline_parser.rb +312 -0
  59. data/lib/rdoc/markup/list.rb +2 -2
  60. data/lib/rdoc/markup/list_item.rb +2 -2
  61. data/lib/rdoc/markup/parser.rb +1 -1
  62. data/lib/rdoc/markup/pre_process.rb +0 -25
  63. data/lib/rdoc/markup/to_ansi.rb +52 -5
  64. data/lib/rdoc/markup/to_bs.rb +23 -43
  65. data/lib/rdoc/markup/to_html.rb +279 -226
  66. data/lib/rdoc/markup/to_html_crossref.rb +122 -136
  67. data/lib/rdoc/markup/to_html_snippet.rb +65 -65
  68. data/lib/rdoc/markup/to_joined_paragraph.rb +0 -5
  69. data/lib/rdoc/markup/to_label.rb +22 -23
  70. data/lib/rdoc/markup/to_markdown.rb +62 -38
  71. data/lib/rdoc/markup/to_rdoc.rb +88 -28
  72. data/lib/rdoc/markup/to_table_of_contents.rb +1 -1
  73. data/lib/rdoc/markup/to_test.rb +9 -1
  74. data/lib/rdoc/markup/to_tt_only.rb +10 -23
  75. data/lib/rdoc/markup/verbatim.rb +1 -1
  76. data/lib/rdoc/markup.rb +8 -30
  77. data/lib/rdoc/options.rb +36 -51
  78. data/lib/rdoc/parser/c.rb +7 -6
  79. data/lib/rdoc/parser/changelog.rb +21 -0
  80. data/lib/rdoc/parser/rbs.rb +275 -0
  81. data/lib/rdoc/parser/ruby.rb +955 -2067
  82. data/lib/rdoc/parser/ruby_colorizer.rb +253 -0
  83. data/lib/rdoc/parser.rb +3 -2
  84. data/lib/rdoc/rbs_helper.rb +186 -0
  85. data/lib/rdoc/rdoc.rb +196 -24
  86. data/lib/rdoc/ri/driver.rb +8 -2
  87. data/lib/rdoc/ri/paths.rb +1 -1
  88. data/lib/rdoc/{servlet.rb → ri/servlet.rb} +5 -5
  89. data/lib/rdoc/ri.rb +4 -3
  90. data/lib/rdoc/rubygems_hook.rb +11 -11
  91. data/lib/rdoc/server.rb +460 -0
  92. data/lib/rdoc/stats.rb +147 -124
  93. data/lib/rdoc/store.rb +212 -4
  94. data/lib/rdoc/task.rb +16 -15
  95. data/lib/rdoc/text.rb +24 -117
  96. data/lib/rdoc/token_stream.rb +11 -33
  97. data/lib/rdoc/version.rb +1 -1
  98. data/lib/rdoc.rb +35 -7
  99. data/lib/rubygems_plugin.rb +2 -11
  100. data/rdoc-logo.svg +43 -0
  101. data/rdoc.gemspec +6 -4
  102. metadata +37 -23
  103. data/lib/rdoc/code_object/anon_class.rb +0 -10
  104. data/lib/rdoc/code_object/ghost_method.rb +0 -6
  105. data/lib/rdoc/code_object/meta_method.rb +0 -6
  106. data/lib/rdoc/markup/attr_changer.rb +0 -22
  107. data/lib/rdoc/markup/attr_span.rb +0 -35
  108. data/lib/rdoc/markup/attribute_manager.rb +0 -432
  109. data/lib/rdoc/markup/attributes.rb +0 -70
  110. data/lib/rdoc/markup/regexp_handling.rb +0 -40
  111. data/lib/rdoc/parser/prism_ruby.rb +0 -1100
  112. data/lib/rdoc/parser/ripper_state_lex.rb +0 -302
  113. data/lib/rdoc/parser/ruby_tools.rb +0 -163
@@ -1,1100 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'prism'
4
- require_relative 'ripper_state_lex'
5
-
6
- # Unlike lib/rdoc/parser/ruby.rb, this file is not based on rtags and does not contain code from
7
- # rtags.rb -
8
- # ruby-lex.rb - ruby lexcal analyzer
9
- # ruby-token.rb - ruby tokens
10
-
11
- # Parse and collect document from Ruby source code.
12
- # RDoc::Parser::PrismRuby is compatible with RDoc::Parser::Ruby and aims to replace it.
13
-
14
- class RDoc::Parser::PrismRuby < RDoc::Parser
15
-
16
- parse_files_matching(/\.rbw?$/) if ENV['RDOC_USE_PRISM_PARSER']
17
-
18
- attr_accessor :visibility
19
- attr_reader :container, :singleton
20
-
21
- def initialize(top_level, content, options, stats)
22
- super
23
-
24
- content = handle_tab_width(content)
25
-
26
- @size = 0
27
- @token_listeners = nil
28
- content = RDoc::Encoding.remove_magic_comment content
29
- @content = content
30
- @markup = @options.markup
31
- @track_visibility = :nodoc != @options.visibility
32
- @encoding = @options.encoding
33
-
34
- @module_nesting = [[top_level, false]]
35
- @container = top_level
36
- @visibility = :public
37
- @singleton = false
38
- @in_proc_block = false
39
- end
40
-
41
- # Suppress `extend` and `include` within block
42
- # because they might be a metaprogramming block
43
- # example: `Module.new { include M }` `M.module_eval { include N }`
44
-
45
- def with_in_proc_block
46
- @in_proc_block = true
47
- yield
48
- @in_proc_block = false
49
- end
50
-
51
- # Dive into another container
52
-
53
- def with_container(container, singleton: false)
54
- old_container = @container
55
- old_visibility = @visibility
56
- old_singleton = @singleton
57
- old_in_proc_block = @in_proc_block
58
- @visibility = :public
59
- @container = container
60
- @singleton = singleton
61
- @in_proc_block = false
62
- unless singleton
63
- # Need to update module parent chain to emulate Module.nesting.
64
- # This mechanism is inaccurate and needs to be fixed.
65
- container.parent = old_container
66
- end
67
- @module_nesting.push([container, singleton])
68
- yield container
69
- ensure
70
- @container = old_container
71
- @visibility = old_visibility
72
- @singleton = old_singleton
73
- @in_proc_block = old_in_proc_block
74
- @module_nesting.pop
75
- end
76
-
77
- # Records the location of this +container+ in the file for this parser and
78
- # adds it to the list of classes and modules in the file.
79
-
80
- def record_location(container) # :nodoc:
81
- case container
82
- when RDoc::ClassModule then
83
- @top_level.add_to_classes_or_modules container
84
- end
85
-
86
- container.record_location @top_level
87
- end
88
-
89
- # Scans this Ruby file for Ruby constructs
90
-
91
- def scan
92
- @tokens = RDoc::Parser::RipperStateLex.parse(@content)
93
- @lines = @content.lines
94
- result = Prism.parse(@content)
95
- @program_node = result.value
96
- @line_nodes = {}
97
- prepare_line_nodes(@program_node)
98
- prepare_comments(result.comments)
99
- return if @top_level.done_documenting
100
-
101
- @first_non_meta_comment_start_line = nil
102
- if (_line_no, start_line = @unprocessed_comments.first)
103
- @first_non_meta_comment_start_line = start_line if start_line < @program_node.location.start_line
104
- end
105
-
106
- @program_node.accept(RDocVisitor.new(self, @top_level, @store))
107
- process_comments_until(@lines.size + 1)
108
- end
109
-
110
- def should_document?(code_object) # :nodoc:
111
- return true unless @track_visibility
112
- return false if code_object.parent&.document_children == false
113
- code_object.document_self
114
- end
115
-
116
- # Assign AST node to a line.
117
- # This is used to show meta-method source code in the documentation.
118
-
119
- def prepare_line_nodes(node) # :nodoc:
120
- case node
121
- when Prism::CallNode, Prism::DefNode
122
- @line_nodes[node.location.start_line] ||= node
123
- end
124
- node.compact_child_nodes.each do |child|
125
- prepare_line_nodes(child)
126
- end
127
- end
128
-
129
- # Prepares comments for processing. Comments are grouped into consecutive.
130
- # Consecutive comment is linked to the next non-blank line.
131
- #
132
- # Example:
133
- # 01| class A # modifier comment 1
134
- # 02| def foo; end # modifier comment 2
135
- # 03|
136
- # 04| # consecutive comment 1 start_line: 4
137
- # 05| # consecutive comment 1 linked to line: 7
138
- # 06|
139
- # 07| # consecutive comment 2 start_line: 7
140
- # 08| # consecutive comment 2 linked to line: 10
141
- # 09|
142
- # 10| def bar; end # consecutive comment 2 linked to this line
143
- # 11| end
144
-
145
- def prepare_comments(comments)
146
- current = []
147
- consecutive_comments = [current]
148
- @modifier_comments = {}
149
- comments.each do |comment|
150
- if comment.is_a? Prism::EmbDocComment
151
- consecutive_comments << [comment] << (current = [])
152
- elsif comment.location.start_line_slice.match?(/\S/)
153
- text = comment.slice
154
- text = RDoc::Encoding.change_encoding(text, @encoding) if @encoding
155
- @modifier_comments[comment.location.start_line] = text
156
- elsif current.empty? || current.last.location.end_line + 1 == comment.location.start_line
157
- current << comment
158
- else
159
- consecutive_comments << (current = [comment])
160
- end
161
- end
162
- consecutive_comments.reject!(&:empty?)
163
-
164
- # Example: line_no = 5, start_line = 2, comment_text = "# comment_start_line\n# comment\n"
165
- # 1| class A
166
- # 2| # comment_start_line
167
- # 3| # comment
168
- # 4|
169
- # 5| def f; end # comment linked to this line
170
- # 6| end
171
- @unprocessed_comments = consecutive_comments.map! do |comments|
172
- start_line = comments.first.location.start_line
173
- line_no = comments.last.location.end_line + (comments.last.location.end_column == 0 ? 0 : 1)
174
- texts = comments.map do |c|
175
- c.is_a?(Prism::EmbDocComment) ? c.slice.lines[1...-1].join : c.slice
176
- end
177
- text = texts.join("\n")
178
- text = RDoc::Encoding.change_encoding(text, @encoding) if @encoding
179
- line_no += 1 while @lines[line_no - 1]&.match?(/\A\s*$/)
180
- [line_no, start_line, text]
181
- end
182
-
183
- # The first comment is special. It defines markup for the rest of the comments.
184
- _, first_comment_start_line, first_comment_text = @unprocessed_comments.first
185
- if first_comment_text && @lines[0...first_comment_start_line - 1].all? { |l| l.match?(/\A\s*$/) }
186
- _text, directives = @preprocess.parse_comment(first_comment_text, first_comment_start_line, :ruby)
187
- markup, = directives['markup']
188
- @markup = markup.downcase if markup
189
- end
190
- end
191
-
192
- # Creates an RDoc::Method on +container+ from +comment+ if there is a
193
- # Signature section in the comment
194
-
195
- def parse_comment_tomdoc(container, comment, line_no, start_line)
196
- return unless signature = RDoc::TomDoc.signature(comment)
197
-
198
- name, = signature.split %r%[ \(]%, 2
199
-
200
- meth = RDoc::GhostMethod.new comment.text, name
201
- record_location(meth)
202
- meth.line = start_line
203
- meth.call_seq = signature
204
- return unless meth.name
205
-
206
- meth.start_collecting_tokens(:ruby)
207
- node = @line_nodes[line_no]
208
- tokens = node ? visible_tokens_from_location(node.location) : [file_line_comment_token(start_line)]
209
- tokens.each { |token| meth.token_stream << token }
210
-
211
- container.add_method meth
212
- meth.comment = comment
213
- @stats.add_method meth
214
- end
215
-
216
- def has_modifier_nodoc?(line_no) # :nodoc:
217
- @modifier_comments[line_no]&.match?(/\A#\s*:nodoc:/)
218
- end
219
-
220
- def handle_modifier_directive(code_object, line_no) # :nodoc:
221
- if (comment_text = @modifier_comments[line_no])
222
- _text, directives = @preprocess.parse_comment(comment_text, line_no, :ruby)
223
- handle_code_object_directives(code_object, directives)
224
- end
225
- end
226
-
227
- def call_node_name_arguments(call_node) # :nodoc:
228
- return [] unless call_node.arguments
229
- call_node.arguments.arguments.map do |arg|
230
- case arg
231
- when Prism::SymbolNode
232
- arg.value
233
- when Prism::StringNode
234
- arg.unescaped
235
- end
236
- end || []
237
- end
238
-
239
- # Handles meta method comments
240
-
241
- def handle_meta_method_comment(comment, directives, node)
242
- handle_code_object_directives(@container, directives)
243
- is_call_node = node.is_a?(Prism::CallNode)
244
- singleton_method = false
245
- visibility = @visibility
246
- attributes = rw = line_no = method_name = nil
247
- directives.each do |directive, (param, line)|
248
- case directive
249
- when 'attr', 'attr_reader', 'attr_writer', 'attr_accessor'
250
- attributes = [param] if param
251
- attributes ||= call_node_name_arguments(node) if is_call_node
252
- rw = directive == 'attr_writer' ? 'W' : directive == 'attr_accessor' ? 'RW' : 'R'
253
- when 'method'
254
- method_name = param if param
255
- line_no = line
256
- when 'singleton-method'
257
- method_name = param if param
258
- line_no = line
259
- singleton_method = true
260
- visibility = :public
261
- end
262
- end
263
-
264
- if attributes
265
- attributes.each do |attr|
266
- a = RDoc::Attr.new(@container, attr, rw, comment, singleton: @singleton)
267
- a.store = @store
268
- a.line = line_no
269
- record_location(a)
270
- @container.add_attribute(a)
271
- a.visibility = visibility
272
- end
273
- elsif line_no || node
274
- method_name ||= call_node_name_arguments(node).first if is_call_node
275
- if node
276
- tokens = visible_tokens_from_location(node.location)
277
- line_no = node.location.start_line
278
- else
279
- tokens = [file_line_comment_token(line_no)]
280
- end
281
- internal_add_method(
282
- method_name,
283
- @container,
284
- comment: comment,
285
- directives: directives,
286
- dont_rename_initialize: false,
287
- line_no: line_no,
288
- visibility: visibility,
289
- singleton: @singleton || singleton_method,
290
- params: nil,
291
- calls_super: false,
292
- block_params: nil,
293
- tokens: tokens,
294
- )
295
- end
296
- end
297
-
298
- INVALID_GHOST_METHOD_ACCEPT_DIRECTIVE_LIST = %w[
299
- method singleton-method attr attr_reader attr_writer attr_accessor
300
- ].freeze
301
- private_constant :INVALID_GHOST_METHOD_ACCEPT_DIRECTIVE_LIST
302
-
303
- def normal_comment_treat_as_ghost_method_for_now?(directives, line_no) # :nodoc:
304
- # Meta method comment should start with `##` but some comments does not follow this rule.
305
- # For now, RDoc accepts them as a meta method comment if there is no node linked to it.
306
- !@line_nodes[line_no] && INVALID_GHOST_METHOD_ACCEPT_DIRECTIVE_LIST.any? { |directive| directives.has_key?(directive) }
307
- end
308
-
309
- def handle_standalone_consecutive_comment_directive(comment, directives, start_with_sharp_sharp, line_no, start_line) # :nodoc:
310
- if start_with_sharp_sharp && start_line != @first_non_meta_comment_start_line
311
- node = @line_nodes[line_no]
312
- handle_meta_method_comment(comment, directives, node)
313
- elsif normal_comment_treat_as_ghost_method_for_now?(directives, line_no) && start_line != @first_non_meta_comment_start_line
314
- handle_meta_method_comment(comment, directives, nil)
315
- else
316
- handle_code_object_directives(@container, directives)
317
- end
318
- end
319
-
320
- # Processes consecutive comments that were not linked to any documentable code until the given line number
321
-
322
- def process_comments_until(line_no_until)
323
- while !@unprocessed_comments.empty? && @unprocessed_comments.first[0] <= line_no_until
324
- line_no, start_line, text = @unprocessed_comments.shift
325
- if @markup == 'tomdoc'
326
- comment = RDoc::Comment.new(text, @top_level, :ruby)
327
- comment.format = 'tomdoc'
328
- parse_comment_tomdoc(@container, comment, line_no, start_line)
329
- @preprocess.run_post_processes(comment, @container)
330
- elsif (comment_text, directives = parse_comment_text_to_directives(text, start_line))
331
- handle_standalone_consecutive_comment_directive(comment_text, directives, text.start_with?(/#\#$/), line_no, start_line)
332
- end
333
- end
334
- end
335
-
336
- # Skips all undocumentable consecutive comments until the given line number.
337
- # Undocumentable comments are comments written inside `def` or inside undocumentable class/module
338
-
339
- def skip_comments_until(line_no_until)
340
- while !@unprocessed_comments.empty? && @unprocessed_comments.first[0] <= line_no_until
341
- @unprocessed_comments.shift
342
- end
343
- end
344
-
345
- # Returns consecutive comment linked to the given line number
346
-
347
- def consecutive_comment(line_no)
348
- return unless @unprocessed_comments.first&.first == line_no
349
- _line_no, start_line, text = @unprocessed_comments.shift
350
- parse_comment_text_to_directives(text, start_line)
351
- end
352
-
353
- # Parses comment text and retuns a pair of RDoc::Comment and directives
354
-
355
- def parse_comment_text_to_directives(comment_text, start_line) # :nodoc:
356
- comment_text, directives = @preprocess.parse_comment(comment_text, start_line, :ruby)
357
- comment = RDoc::Comment.new(comment_text, @top_level, :ruby)
358
- comment.normalized = true
359
- comment.line = start_line
360
- markup, = directives['markup']
361
- comment.format = markup&.downcase || @markup
362
- if (section, = directives['section'])
363
- # If comment has :section:, it is not a documentable comment for a code object
364
- @container.set_current_section(section, comment.dup)
365
- return
366
- end
367
- @preprocess.run_post_processes(comment, @container)
368
- [comment, directives]
369
- end
370
-
371
- def slice_tokens(start_pos, end_pos) # :nodoc:
372
- start_index = @tokens.bsearch_index { |t| ([t.line_no, t.char_no] <=> start_pos) >= 0 }
373
- end_index = @tokens.bsearch_index { |t| ([t.line_no, t.char_no] <=> end_pos) >= 0 }
374
- tokens = @tokens[start_index...end_index]
375
- tokens.pop if tokens.last&.kind == :on_nl
376
- tokens
377
- end
378
-
379
- def file_line_comment_token(line_no) # :nodoc:
380
- position_comment = RDoc::Parser::RipperStateLex::Token.new(line_no - 1, 0, :on_comment)
381
- position_comment[:text] = "# File #{@top_level.relative_name}, line #{line_no}"
382
- position_comment
383
- end
384
-
385
- # Returns tokens from the given location
386
-
387
- def visible_tokens_from_location(location)
388
- position_comment = file_line_comment_token(location.start_line)
389
- newline_token = RDoc::Parser::RipperStateLex::Token.new(0, 0, :on_nl, "\n")
390
- indent_token = RDoc::Parser::RipperStateLex::Token.new(location.start_line, 0, :on_sp, ' ' * location.start_character_column)
391
- tokens = slice_tokens(
392
- [location.start_line, location.start_character_column],
393
- [location.end_line, location.end_character_column]
394
- )
395
- [position_comment, newline_token, indent_token, *tokens]
396
- end
397
-
398
- # Handles `public :foo, :bar` `private :foo, :bar` and `protected :foo, :bar`
399
-
400
- def change_method_visibility(names, visibility, singleton: @singleton)
401
- new_methods = []
402
- @container.methods_matching(names, singleton) do |m|
403
- if m.parent != @container
404
- m = m.dup
405
- record_location(m)
406
- new_methods << m
407
- else
408
- m.visibility = visibility
409
- end
410
- end
411
- new_methods.each do |method|
412
- case method
413
- when RDoc::AnyMethod then
414
- @container.add_method(method)
415
- when RDoc::Attr then
416
- @container.add_attribute(method)
417
- end
418
- method.visibility = visibility
419
- end
420
- end
421
-
422
- # Handles `module_function :foo, :bar`
423
-
424
- def change_method_to_module_function(names)
425
- @container.set_visibility_for(names, :private, false)
426
- new_methods = []
427
- @container.methods_matching(names) do |m|
428
- s_m = m.dup
429
- record_location(s_m)
430
- s_m.singleton = true
431
- new_methods << s_m
432
- end
433
- new_methods.each do |method|
434
- case method
435
- when RDoc::AnyMethod then
436
- @container.add_method(method)
437
- when RDoc::Attr then
438
- @container.add_attribute(method)
439
- end
440
- method.visibility = :public
441
- end
442
- end
443
-
444
- def handle_code_object_directives(code_object, directives) # :nodoc:
445
- directives.each do |directive, (param)|
446
- @preprocess.handle_directive('', directive, param, code_object)
447
- end
448
- end
449
-
450
- # Handles `alias foo bar` and `alias_method :foo, :bar`
451
-
452
- def add_alias_method(old_name, new_name, line_no)
453
- comment, directives = consecutive_comment(line_no)
454
- handle_code_object_directives(@container, directives) if directives
455
- visibility = @container.find_method(old_name, @singleton)&.visibility || :public
456
- a = RDoc::Alias.new(nil, old_name, new_name, comment, singleton: @singleton)
457
- handle_modifier_directive(a, line_no)
458
- a.store = @store
459
- a.line = line_no
460
- record_location(a)
461
- if should_document?(a)
462
- @container.add_alias(a)
463
- @container.find_method(new_name, @singleton)&.visibility = visibility
464
- end
465
- end
466
-
467
- # Handles `attr :a, :b`, `attr_reader :a, :b`, `attr_writer :a, :b` and `attr_accessor :a, :b`
468
-
469
- def add_attributes(names, rw, line_no)
470
- comment, directives = consecutive_comment(line_no)
471
- handle_code_object_directives(@container, directives) if directives
472
- return unless @container.document_children
473
-
474
- names.each do |symbol|
475
- a = RDoc::Attr.new(nil, symbol.to_s, rw, comment, singleton: @singleton)
476
- a.store = @store
477
- a.line = line_no
478
- record_location(a)
479
- handle_modifier_directive(a, line_no)
480
- @container.add_attribute(a) if should_document?(a)
481
- a.visibility = visibility # should set after adding to container
482
- end
483
- end
484
-
485
- def add_includes_extends(names, rdoc_class, line_no) # :nodoc:
486
- return if @in_proc_block
487
- comment, directives = consecutive_comment(line_no)
488
- handle_code_object_directives(@container, directives) if directives
489
- names.each do |name|
490
- ie = @container.add(rdoc_class, name, '')
491
- ie.store = @store
492
- ie.line = line_no
493
- ie.comment = comment
494
- record_location(ie)
495
- end
496
- end
497
-
498
- # Handle `include Foo, Bar`
499
-
500
- def add_includes(names, line_no) # :nodoc:
501
- add_includes_extends(names, RDoc::Include, line_no)
502
- end
503
-
504
- # Handle `extend Foo, Bar`
505
-
506
- def add_extends(names, line_no) # :nodoc:
507
- add_includes_extends(names, RDoc::Extend, line_no)
508
- end
509
-
510
- # Adds a method defined by `def` syntax
511
-
512
- def add_method(method_name, receiver_name:, receiver_fallback_type:, visibility:, singleton:, params:, calls_super:, block_params:, tokens:, start_line:, args_end_line:, end_line:)
513
- return if @in_proc_block
514
-
515
- receiver = receiver_name ? find_or_create_module_path(receiver_name, receiver_fallback_type) : @container
516
- comment, directives = consecutive_comment(start_line)
517
- handle_code_object_directives(@container, directives) if directives
518
-
519
- internal_add_method(
520
- method_name,
521
- receiver,
522
- comment: comment,
523
- directives: directives,
524
- modifier_comment_lines: [start_line, args_end_line, end_line].uniq,
525
- line_no: start_line,
526
- visibility: visibility,
527
- singleton: singleton,
528
- params: params,
529
- calls_super: calls_super,
530
- block_params: block_params,
531
- tokens: tokens
532
- )
533
- end
534
-
535
- private def internal_add_method(method_name, container, comment:, dont_rename_initialize: false, directives:, modifier_comment_lines: nil, line_no:, visibility:, singleton:, params:, calls_super:, block_params:, tokens:) # :nodoc:
536
- meth = RDoc::AnyMethod.new(nil, method_name, singleton: singleton)
537
- meth.comment = comment
538
- handle_code_object_directives(meth, directives) if directives
539
- modifier_comment_lines&.each do |line|
540
- handle_modifier_directive(meth, line)
541
- end
542
- return unless should_document?(meth)
543
-
544
- if directives && (call_seq, = directives['call-seq'])
545
- meth.call_seq = call_seq.lines.map(&:chomp).reject(&:empty?).join("\n") if call_seq
546
- end
547
- meth.name ||= meth.call_seq[/\A[^()\s]+/] if meth.call_seq
548
- meth.name ||= 'unknown'
549
- meth.store = @store
550
- meth.line = line_no
551
- container.add_method(meth) # should add after setting singleton and before setting visibility
552
- meth.visibility = visibility
553
- meth.params ||= params || '()'
554
- meth.calls_super = calls_super
555
- meth.block_params ||= block_params if block_params
556
- record_location(meth)
557
- meth.start_collecting_tokens(:ruby)
558
- tokens.each do |token|
559
- meth.token_stream << token
560
- end
561
-
562
- # Rename after add_method to register duplicated 'new' and 'initialize'
563
- # defined in c and ruby just like the old parser did.
564
- if !dont_rename_initialize && method_name == 'initialize' && !singleton
565
- if meth.dont_rename_initialize
566
- meth.visibility = :protected
567
- else
568
- meth.name = 'new'
569
- meth.singleton = true
570
- meth.visibility = :public
571
- end
572
- end
573
- end
574
-
575
- # Find or create module or class from a given module name.
576
- # If module or class does not exist, creates a module or a class according to `create_mode` argument.
577
-
578
- def find_or_create_module_path(module_name, create_mode)
579
- root_name, *path, name = module_name.split('::')
580
- add_module = ->(mod, name, mode) {
581
- case mode
582
- when :class
583
- mod.add_class(RDoc::NormalClass, name, 'Object').tap { |m| m.store = @store }
584
- when :module
585
- mod.add_module(RDoc::NormalModule, name).tap { |m| m.store = @store }
586
- end
587
- }
588
- if root_name.empty?
589
- mod = @top_level
590
- else
591
- @module_nesting.reverse_each do |nesting, singleton|
592
- next if singleton
593
- mod = nesting.find_module_named(root_name)
594
- break if mod
595
- # If a constant is found and it is not a module or class, RDoc can't document about it.
596
- # Return an anonymous module to avoid wrong document creation.
597
- return RDoc::NormalModule.new(nil) if nesting.find_constant_named(root_name)
598
- end
599
- last_nesting, = @module_nesting.reverse_each.find { |_, singleton| !singleton }
600
- return mod || add_module.call(last_nesting, root_name, create_mode) unless name
601
- mod ||= add_module.call(last_nesting, root_name, :module)
602
- end
603
- path.each do |name|
604
- mod = mod.find_module_named(name) || add_module.call(mod, name, :module)
605
- end
606
- mod.find_module_named(name) || add_module.call(mod, name, create_mode)
607
- end
608
-
609
- # Resolves constant path to a full path by searching module nesting
610
-
611
- def resolve_constant_path(constant_path)
612
- owner_name, path = constant_path.split('::', 2)
613
- return constant_path if owner_name.empty? # ::Foo, ::Foo::Bar
614
- mod = nil
615
- @module_nesting.reverse_each do |nesting, singleton|
616
- next if singleton
617
- mod = nesting.find_module_named(owner_name)
618
- break if mod
619
- end
620
- mod ||= @top_level.find_module_named(owner_name)
621
- [mod.full_name, path].compact.join('::') if mod
622
- end
623
-
624
- # Returns a pair of owner module and constant name from a given constant path.
625
- # Creates owner module if it does not exist.
626
-
627
- def find_or_create_constant_owner_name(constant_path)
628
- const_path, colon, name = constant_path.rpartition('::')
629
- if colon.empty? # class Foo
630
- # Within `class C` or `module C`, owner is C(== current container)
631
- # Within `class <<C`, owner is C.singleton_class
632
- # but RDoc don't track constants of a singleton class of module
633
- [(@singleton ? nil : @container), name]
634
- elsif const_path.empty? # class ::Foo
635
- [@top_level, name]
636
- else # `class Foo::Bar` or `class ::Foo::Bar`
637
- [find_or_create_module_path(const_path, :module), name]
638
- end
639
- end
640
-
641
- # Adds a constant
642
-
643
- def add_constant(constant_name, rhs_name, start_line, end_line)
644
- comment, directives = consecutive_comment(start_line)
645
- handle_code_object_directives(@container, directives) if directives
646
- owner, name = find_or_create_constant_owner_name(constant_name)
647
- return unless owner
648
-
649
- constant = RDoc::Constant.new(name, rhs_name, comment)
650
- constant.store = @store
651
- constant.line = start_line
652
- record_location(constant)
653
- handle_modifier_directive(constant, start_line)
654
- handle_modifier_directive(constant, end_line)
655
- owner.add_constant(constant)
656
- mod =
657
- if rhs_name =~ /^::/
658
- @store.find_class_or_module(rhs_name)
659
- else
660
- @container.find_module_named(rhs_name)
661
- end
662
- if mod && constant.document_self
663
- a = @container.add_module_alias(mod, rhs_name, constant, @top_level)
664
- a.store = @store
665
- a.line = start_line
666
- record_location(a)
667
- end
668
- end
669
-
670
- # Adds module or class
671
-
672
- def add_module_or_class(module_name, start_line, end_line, is_class: false, superclass_name: nil, superclass_expr: nil)
673
- comment, directives = consecutive_comment(start_line)
674
- handle_code_object_directives(@container, directives) if directives
675
- return unless @container.document_children
676
-
677
- owner, name = find_or_create_constant_owner_name(module_name)
678
- return unless owner
679
-
680
- if is_class
681
- # RDoc::NormalClass resolves superclass name despite of the lack of module nesting information.
682
- # We need to fix it when RDoc::NormalClass resolved to a wrong constant name
683
- if superclass_name
684
- superclass_full_path = resolve_constant_path(superclass_name)
685
- superclass = @store.find_class_or_module(superclass_full_path) if superclass_full_path
686
- superclass_full_path ||= superclass_name
687
- superclass_full_path = superclass_full_path.sub(/^::/, '')
688
- end
689
- # add_class should be done after resolving superclass
690
- mod = owner.classes_hash[name] || owner.add_class(RDoc::NormalClass, name, superclass_name || superclass_expr || '::Object')
691
- if superclass_name
692
- if superclass
693
- mod.superclass = superclass
694
- elsif (mod.superclass.is_a?(String) || mod.superclass.name == 'Object') && mod.superclass != superclass_full_path
695
- mod.superclass = superclass_full_path
696
- end
697
- end
698
- else
699
- mod = owner.modules_hash[name] || owner.add_module(RDoc::NormalModule, name)
700
- end
701
-
702
- mod.store = @store
703
- mod.line = start_line
704
- record_location(mod)
705
- handle_modifier_directive(mod, start_line)
706
- handle_modifier_directive(mod, end_line)
707
- mod.add_comment(comment, @top_level) if comment
708
- mod
709
- end
710
-
711
- class RDocVisitor < Prism::Visitor # :nodoc:
712
- def initialize(scanner, top_level, store)
713
- @scanner = scanner
714
- @top_level = top_level
715
- @store = store
716
- end
717
-
718
- def visit_if_node(node)
719
- if node.end_keyword
720
- super
721
- else
722
- # Visit with the order in text representation to handle this method comment
723
- # # comment
724
- # def f
725
- # end if call_node
726
- node.statements.accept(self)
727
- node.predicate.accept(self)
728
- end
729
- end
730
- alias visit_unless_node visit_if_node
731
-
732
- def visit_call_node(node)
733
- @scanner.process_comments_until(node.location.start_line - 1)
734
- if node.receiver.nil?
735
- case node.name
736
- when :attr
737
- _visit_call_attr_reader_writer_accessor(node, 'R')
738
- when :attr_reader
739
- _visit_call_attr_reader_writer_accessor(node, 'R')
740
- when :attr_writer
741
- _visit_call_attr_reader_writer_accessor(node, 'W')
742
- when :attr_accessor
743
- _visit_call_attr_reader_writer_accessor(node, 'RW')
744
- when :include
745
- _visit_call_include(node)
746
- when :extend
747
- _visit_call_extend(node)
748
- when :public
749
- _visit_call_public_private_protected(node, :public) { super }
750
- when :private
751
- _visit_call_public_private_protected(node, :private) { super }
752
- when :protected
753
- _visit_call_public_private_protected(node, :protected) { super }
754
- when :private_constant
755
- _visit_call_private_constant(node)
756
- when :public_constant
757
- _visit_call_public_constant(node)
758
- when :require
759
- _visit_call_require(node)
760
- when :alias_method
761
- _visit_call_alias_method(node)
762
- when :module_function
763
- _visit_call_module_function(node) { super }
764
- when :public_class_method
765
- _visit_call_public_private_class_method(node, :public) { super }
766
- when :private_class_method
767
- _visit_call_public_private_class_method(node, :private) { super }
768
- else
769
- node.arguments&.accept(self)
770
- super
771
- end
772
- else
773
- super
774
- end
775
- end
776
-
777
- def visit_block_node(node)
778
- @scanner.with_in_proc_block do
779
- # include, extend and method definition inside block are not documentable
780
- super
781
- end
782
- end
783
-
784
- def visit_alias_method_node(node)
785
- @scanner.process_comments_until(node.location.start_line - 1)
786
- return unless node.old_name.is_a?(Prism::SymbolNode) && node.new_name.is_a?(Prism::SymbolNode)
787
- @scanner.add_alias_method(node.old_name.value.to_s, node.new_name.value.to_s, node.location.start_line)
788
- end
789
-
790
- def visit_module_node(node)
791
- node.constant_path.accept(self)
792
- @scanner.process_comments_until(node.location.start_line - 1)
793
- module_name = constant_path_string(node.constant_path)
794
- mod = @scanner.add_module_or_class(module_name, node.location.start_line, node.location.end_line) if module_name
795
- if mod
796
- @scanner.with_container(mod) do
797
- node.body&.accept(self)
798
- @scanner.process_comments_until(node.location.end_line)
799
- end
800
- else
801
- @scanner.skip_comments_until(node.location.end_line)
802
- end
803
- end
804
-
805
- def visit_class_node(node)
806
- node.constant_path.accept(self)
807
- node.superclass&.accept(self)
808
- @scanner.process_comments_until(node.location.start_line - 1)
809
- superclass_name = constant_path_string(node.superclass) if node.superclass
810
- superclass_expr = node.superclass.slice if node.superclass && !superclass_name
811
- class_name = constant_path_string(node.constant_path)
812
- 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
813
- if klass
814
- @scanner.with_container(klass) do
815
- node.body&.accept(self)
816
- @scanner.process_comments_until(node.location.end_line)
817
- end
818
- else
819
- @scanner.skip_comments_until(node.location.end_line)
820
- end
821
- end
822
-
823
- def visit_singleton_class_node(node)
824
- @scanner.process_comments_until(node.location.start_line - 1)
825
-
826
- if @scanner.has_modifier_nodoc?(node.location.start_line)
827
- # Skip visiting inside the singleton class. Also skips creation of node.expression as a module
828
- @scanner.skip_comments_until(node.location.end_line)
829
- return
830
- end
831
-
832
- expression = node.expression
833
- expression = expression.body.body.first if expression.is_a?(Prism::ParenthesesNode) && expression.body&.body&.size == 1
834
-
835
- case expression
836
- when Prism::ConstantWriteNode
837
- # Accept `class << (NameErrorCheckers = Object.new)` as a module which is not actually a module
838
- mod = @scanner.container.add_module(RDoc::NormalModule, expression.name.to_s)
839
- when Prism::ConstantPathNode, Prism::ConstantReadNode
840
- expression_name = constant_path_string(expression)
841
- # If a constant_path does not exist, RDoc creates a module
842
- mod = @scanner.find_or_create_module_path(expression_name, :module) if expression_name
843
- when Prism::SelfNode
844
- mod = @scanner.container if @scanner.container != @top_level
845
- end
846
- expression.accept(self)
847
- if mod
848
- @scanner.with_container(mod, singleton: true) do
849
- node.body&.accept(self)
850
- @scanner.process_comments_until(node.location.end_line)
851
- end
852
- else
853
- @scanner.skip_comments_until(node.location.end_line)
854
- end
855
- end
856
-
857
- def visit_def_node(node)
858
- start_line = node.location.start_line
859
- args_end_line = node.parameters&.location&.end_line || start_line
860
- end_line = node.location.end_line
861
- @scanner.process_comments_until(start_line - 1)
862
-
863
- case node.receiver
864
- when Prism::NilNode, Prism::TrueNode, Prism::FalseNode
865
- visibility = :public
866
- singleton = false
867
- receiver_name =
868
- case node.receiver
869
- when Prism::NilNode
870
- 'NilClass'
871
- when Prism::TrueNode
872
- 'TrueClass'
873
- when Prism::FalseNode
874
- 'FalseClass'
875
- end
876
- receiver_fallback_type = :class
877
- when Prism::SelfNode
878
- # singleton method of a singleton class is not documentable
879
- return if @scanner.singleton
880
- visibility = :public
881
- singleton = true
882
- when Prism::ConstantReadNode, Prism::ConstantPathNode
883
- visibility = :public
884
- singleton = true
885
- receiver_name = constant_path_string(node.receiver)
886
- receiver_fallback_type = :module
887
- return unless receiver_name
888
- when nil
889
- visibility = @scanner.visibility
890
- singleton = @scanner.singleton
891
- else
892
- # `def (unknown expression).method_name` is not documentable
893
- return
894
- end
895
- name = node.name.to_s
896
- params, block_params, calls_super = MethodSignatureVisitor.scan_signature(node)
897
- tokens = @scanner.visible_tokens_from_location(node.location)
898
-
899
- @scanner.add_method(
900
- name,
901
- receiver_name: receiver_name,
902
- receiver_fallback_type: receiver_fallback_type,
903
- visibility: visibility,
904
- singleton: singleton,
905
- params: params,
906
- block_params: block_params,
907
- calls_super: calls_super,
908
- tokens: tokens,
909
- start_line: start_line,
910
- args_end_line: args_end_line,
911
- end_line: end_line
912
- )
913
- ensure
914
- @scanner.skip_comments_until(end_line)
915
- end
916
-
917
- def visit_constant_path_write_node(node)
918
- @scanner.process_comments_until(node.location.start_line - 1)
919
- path = constant_path_string(node.target)
920
- return unless path
921
-
922
- @scanner.add_constant(
923
- path,
924
- constant_path_string(node.value) || node.value.slice,
925
- node.location.start_line,
926
- node.location.end_line
927
- )
928
- @scanner.skip_comments_until(node.location.end_line)
929
- # Do not traverse rhs not to document `A::B = Struct.new{def undocumentable_method; end}`
930
- end
931
-
932
- def visit_constant_write_node(node)
933
- @scanner.process_comments_until(node.location.start_line - 1)
934
- @scanner.add_constant(
935
- node.name.to_s,
936
- constant_path_string(node.value) || node.value.slice,
937
- node.location.start_line,
938
- node.location.end_line
939
- )
940
- @scanner.skip_comments_until(node.location.end_line)
941
- # Do not traverse rhs not to document `A = Struct.new{def undocumentable_method; end}`
942
- end
943
-
944
- private
945
-
946
- def constant_arguments_names(call_node)
947
- return unless call_node.arguments
948
- names = call_node.arguments.arguments.map { |arg| constant_path_string(arg) }
949
- names.all? ? names : nil
950
- end
951
-
952
- def symbol_arguments(call_node)
953
- arguments_node = call_node.arguments
954
- return unless arguments_node && arguments_node.arguments.all? { |arg| arg.is_a?(Prism::SymbolNode)}
955
- arguments_node.arguments.map { |arg| arg.value.to_sym }
956
- end
957
-
958
- def visibility_method_arguments(call_node, singleton:)
959
- arguments_node = call_node.arguments
960
- return unless arguments_node
961
- symbols = symbol_arguments(call_node)
962
- if symbols
963
- # module_function :foo, :bar
964
- return symbols.map(&:to_s)
965
- else
966
- return unless arguments_node.arguments.size == 1
967
- arg = arguments_node.arguments.first
968
- return unless arg.is_a?(Prism::DefNode)
969
-
970
- if singleton
971
- # `private_class_method def foo; end` `private_class_method def not_self.foo; end` should be ignored
972
- return unless arg.receiver.is_a?(Prism::SelfNode)
973
- else
974
- # `module_function def something.foo` should be ignored
975
- return if arg.receiver
976
- end
977
- # `module_function def foo; end` or `private_class_method def self.foo; end`
978
- [arg.name.to_s]
979
- end
980
- end
981
-
982
- def constant_path_string(node)
983
- case node
984
- when Prism::ConstantReadNode
985
- node.name.to_s
986
- when Prism::ConstantPathNode
987
- parent_name = node.parent ? constant_path_string(node.parent) : ''
988
- "#{parent_name}::#{node.name}" if parent_name
989
- end
990
- end
991
-
992
- def _visit_call_require(call_node)
993
- return unless call_node.arguments&.arguments&.size == 1
994
- arg = call_node.arguments.arguments.first
995
- return unless arg.is_a?(Prism::StringNode)
996
- @scanner.container.add_require(RDoc::Require.new(arg.unescaped, nil))
997
- end
998
-
999
- def _visit_call_module_function(call_node)
1000
- yield
1001
- return if @scanner.singleton
1002
- names = visibility_method_arguments(call_node, singleton: false)&.map(&:to_s)
1003
- @scanner.change_method_to_module_function(names) if names
1004
- end
1005
-
1006
- def _visit_call_public_private_class_method(call_node, visibility)
1007
- yield
1008
- return if @scanner.singleton
1009
- names = visibility_method_arguments(call_node, singleton: true)
1010
- @scanner.change_method_visibility(names, visibility, singleton: true) if names
1011
- end
1012
-
1013
- def _visit_call_public_private_protected(call_node, visibility)
1014
- arguments_node = call_node.arguments
1015
- if arguments_node.nil? # `public` `private`
1016
- @scanner.visibility = visibility
1017
- else # `public :foo, :bar`, `private def foo; end`
1018
- yield
1019
- names = visibility_method_arguments(call_node, singleton: false)
1020
- @scanner.change_method_visibility(names, visibility) if names
1021
- end
1022
- end
1023
-
1024
- def _visit_call_alias_method(call_node)
1025
- new_name, old_name, *rest = symbol_arguments(call_node)
1026
- return unless old_name && new_name && rest.empty?
1027
- @scanner.add_alias_method(old_name.to_s, new_name.to_s, call_node.location.start_line)
1028
- end
1029
-
1030
- def _visit_call_include(call_node)
1031
- names = constant_arguments_names(call_node)
1032
- line_no = call_node.location.start_line
1033
- return unless names
1034
-
1035
- if @scanner.singleton
1036
- @scanner.add_extends(names, line_no)
1037
- else
1038
- @scanner.add_includes(names, line_no)
1039
- end
1040
- end
1041
-
1042
- def _visit_call_extend(call_node)
1043
- names = constant_arguments_names(call_node)
1044
- @scanner.add_extends(names, call_node.location.start_line) if names && !@scanner.singleton
1045
- end
1046
-
1047
- def _visit_call_public_constant(call_node)
1048
- return if @scanner.singleton
1049
- names = symbol_arguments(call_node)
1050
- @scanner.container.set_constant_visibility_for(names.map(&:to_s), :public) if names
1051
- end
1052
-
1053
- def _visit_call_private_constant(call_node)
1054
- return if @scanner.singleton
1055
- names = symbol_arguments(call_node)
1056
- @scanner.container.set_constant_visibility_for(names.map(&:to_s), :private) if names
1057
- end
1058
-
1059
- def _visit_call_attr_reader_writer_accessor(call_node, rw)
1060
- names = symbol_arguments(call_node)
1061
- @scanner.add_attributes(names.map(&:to_s), rw, call_node.location.start_line) if names
1062
- end
1063
- class MethodSignatureVisitor < Prism::Visitor # :nodoc:
1064
- class << self
1065
- def scan_signature(def_node)
1066
- visitor = new
1067
- def_node.body&.accept(visitor)
1068
- params = "(#{def_node.parameters&.slice})"
1069
- block_params = visitor.yields.first
1070
- [params, block_params, visitor.calls_super]
1071
- end
1072
- end
1073
-
1074
- attr_reader :params, :yields, :calls_super
1075
-
1076
- def initialize
1077
- @params = nil
1078
- @calls_super = false
1079
- @yields = []
1080
- end
1081
-
1082
- def visit_def_node(node)
1083
- # stop traverse inside nested def
1084
- end
1085
-
1086
- def visit_yield_node(node)
1087
- @yields << (node.arguments&.slice || '')
1088
- end
1089
-
1090
- def visit_super_node(node)
1091
- @calls_super = true
1092
- super
1093
- end
1094
-
1095
- def visit_forwarding_super_node(node)
1096
- @calls_super = true
1097
- end
1098
- end
1099
- end
1100
- end