super-quick-rb 0.0.1

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