PageRenderTime 51ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/rdoc/parser/c.rb

https://github.com/EarthJem/ruby
Ruby | 1181 lines | 734 code | 190 blank | 257 comment | 59 complexity | 6283d0c98457daec8718781f9b1f10bb MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. require 'tsort'
  2. ##
  3. # RDoc::Parser::C attempts to parse C extension files. It looks for
  4. # the standard patterns that you find in extensions: <tt>rb_define_class,
  5. # rb_define_method</tt> and so on. It tries to find the corresponding
  6. # C source for the methods and extract comments, but if we fail
  7. # we don't worry too much.
  8. #
  9. # The comments associated with a Ruby method are extracted from the C
  10. # comment block associated with the routine that _implements_ that
  11. # method, that is to say the method whose name is given in the
  12. # <tt>rb_define_method</tt> call. For example, you might write:
  13. #
  14. # /*
  15. # * Returns a new array that is a one-dimensional flattening of this
  16. # * array (recursively). That is, for every element that is an array,
  17. # * extract its elements into the new array.
  18. # *
  19. # * s = [ 1, 2, 3 ] #=> [1, 2, 3]
  20. # * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
  21. # * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
  22. # * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  23. # */
  24. # static VALUE
  25. # rb_ary_flatten(ary)
  26. # VALUE ary;
  27. # {
  28. # ary = rb_obj_dup(ary);
  29. # rb_ary_flatten_bang(ary);
  30. # return ary;
  31. # }
  32. #
  33. # ...
  34. #
  35. # void
  36. # Init_Array()
  37. # {
  38. # ...
  39. # rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
  40. #
  41. # Here RDoc will determine from the rb_define_method line that there's a
  42. # method called "flatten" in class Array, and will look for the implementation
  43. # in the method rb_ary_flatten. It will then use the comment from that
  44. # method in the HTML output. This method must be in the same source file
  45. # as the rb_define_method.
  46. #
  47. # The comment blocks may include special directives:
  48. #
  49. # [Document-class: +name+]
  50. # Documentation for the named class.
  51. #
  52. # [Document-module: +name+]
  53. # Documentation for the named module.
  54. #
  55. # [Document-const: +name+]
  56. # Documentation for the named +rb_define_const+.
  57. #
  58. # Constant values can be supplied on the first line of the comment like so:
  59. #
  60. # /* 300: The highest possible score in bowling */
  61. # rb_define_const(cFoo, "PERFECT", INT2FIX(300));
  62. #
  63. # The value can contain internal colons so long as they are escaped with a \
  64. #
  65. # [Document-global: +name+]
  66. # Documentation for the named +rb_define_global_const+
  67. #
  68. # [Document-variable: +name+]
  69. # Documentation for the named +rb_define_variable+
  70. #
  71. # [Document-method: +method_name+]
  72. # Documentation for the named method. Use this when the method name is
  73. # unambiguous.
  74. #
  75. # [Document-method: <tt>ClassName::method_name<tt>]
  76. # Documentation for a singleton method in the given class. Use this when
  77. # the method name alone is ambiguous.
  78. #
  79. # [Document-method: <tt>ClassName#method_name<tt>]
  80. # Documentation for a instance method in the given class. Use this when the
  81. # method name alone is ambiguous.
  82. #
  83. # [Document-attr: +name+]
  84. # Documentation for the named attribute.
  85. #
  86. # [call-seq: <i>text up to an empty line</i>]
  87. # Because C source doesn't give descriptive names to Ruby-level parameters,
  88. # you need to document the calling sequence explicitly
  89. #
  90. # In addition, RDoc assumes by default that the C method implementing a
  91. # Ruby function is in the same source file as the rb_define_method call.
  92. # If this isn't the case, add the comment:
  93. #
  94. # rb_define_method(....); // in filename
  95. #
  96. # As an example, we might have an extension that defines multiple classes
  97. # in its Init_xxx method. We could document them using
  98. #
  99. # /*
  100. # * Document-class: MyClass
  101. # *
  102. # * Encapsulate the writing and reading of the configuration
  103. # * file. ...
  104. # */
  105. #
  106. # /*
  107. # * Document-method: read_value
  108. # *
  109. # * call-seq:
  110. # * cfg.read_value(key) -> value
  111. # * cfg.read_value(key} { |key| } -> value
  112. # *
  113. # * Return the value corresponding to +key+ from the configuration.
  114. # * In the second form, if the key isn't found, invoke the
  115. # * block and return its value.
  116. # */
  117. class RDoc::Parser::C < RDoc::Parser
  118. parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/)
  119. include RDoc::Text
  120. ##
  121. # Maps C variable names to names of ruby classes or modules
  122. attr_reader :classes
  123. ##
  124. # C file the parser is parsing
  125. attr_accessor :content
  126. ##
  127. # Dependencies from a missing enclosing class to the classes in
  128. # missing_dependencies that depend upon it.
  129. attr_reader :enclosure_dependencies
  130. ##
  131. # Maps C variable names to names of ruby classes (and singleton classes)
  132. attr_reader :known_classes
  133. ##
  134. # Classes found while parsing the C file that were not yet registered due to
  135. # a missing enclosing class. These are processed by do_missing
  136. attr_reader :missing_dependencies
  137. ##
  138. # Maps C variable names to names of ruby singleton classes
  139. attr_reader :singleton_classes
  140. ##
  141. # The TopLevel items in the parsed file belong to
  142. attr_reader :top_level
  143. ##
  144. # Prepares for parsing a C file. See RDoc::Parser#initialize for details on
  145. # the arguments.
  146. def initialize top_level, file_name, content, options, stats
  147. super
  148. @known_classes = RDoc::KNOWN_CLASSES.dup
  149. @content = handle_tab_width handle_ifdefs_in @content
  150. @file_dir = File.dirname @file_name
  151. @classes = load_variable_map :c_class_variables
  152. @singleton_classes = load_variable_map :c_singleton_class_variables
  153. # missing variable => [handle_class_module arguments]
  154. @missing_dependencies = {}
  155. # missing enclosure variable => [dependent handle_class_module arguments]
  156. @enclosure_dependencies = Hash.new { |h, k| h[k] = [] }
  157. @enclosure_dependencies.instance_variable_set :@missing_dependencies,
  158. @missing_dependencies
  159. @enclosure_dependencies.extend TSort
  160. def @enclosure_dependencies.tsort_each_node &block
  161. each_key(&block)
  162. rescue TSort::Cyclic => e
  163. cycle_vars = e.message.scan(/"(.*?)"/).flatten
  164. cycle = cycle_vars.sort.map do |var_name|
  165. delete var_name
  166. var_name, type, mod_name, = @missing_dependencies[var_name]
  167. "#{type} #{mod_name} (#{var_name})"
  168. end.join ', '
  169. warn "Unable to create #{cycle} due to a cyclic class or module creation"
  170. retry
  171. end
  172. def @enclosure_dependencies.tsort_each_child node, &block
  173. fetch(node, []).each(&block)
  174. end
  175. end
  176. ##
  177. # Scans #content for rb_define_alias
  178. def do_aliases
  179. @content.scan(/rb_define_alias\s*\(
  180. \s*(\w+),
  181. \s*"(.+?)",
  182. \s*"(.+?)"
  183. \s*\)/xm) do |var_name, new_name, old_name|
  184. class_name = @known_classes[var_name]
  185. unless class_name then
  186. @options.warn "Enclosing class or module %p for alias %s %s is not known" % [
  187. var_name, new_name, old_name]
  188. next
  189. end
  190. class_obj = find_class var_name, class_name
  191. al = RDoc::Alias.new '', old_name, new_name, ''
  192. al.singleton = @singleton_classes.key? var_name
  193. comment = find_alias_comment var_name, new_name, old_name
  194. comment.normalize
  195. al.comment = comment
  196. al.record_location @top_level
  197. class_obj.add_alias al
  198. @stats.add_alias al
  199. end
  200. end
  201. ##
  202. # Scans #content for rb_attr and rb_define_attr
  203. def do_attrs
  204. @content.scan(/rb_attr\s*\(
  205. \s*(\w+),
  206. \s*([\w"()]+),
  207. \s*([01]),
  208. \s*([01]),
  209. \s*\w+\);/xm) do |var_name, attr_name, read, write|
  210. handle_attr var_name, attr_name, read, write
  211. end
  212. @content.scan(%r%rb_define_attr\(
  213. \s*([\w\.]+),
  214. \s*"([^"]+)",
  215. \s*(\d+),
  216. \s*(\d+)\s*\);
  217. %xm) do |var_name, attr_name, read, write|
  218. handle_attr var_name, attr_name, read, write
  219. end
  220. end
  221. ##
  222. # Scans #content for boot_defclass
  223. def do_boot_defclass
  224. @content.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do
  225. |var_name, class_name, parent|
  226. parent = nil if parent == "0"
  227. handle_class_module(var_name, :class, class_name, parent, nil)
  228. end
  229. end
  230. ##
  231. # Scans #content for rb_define_class, boot_defclass, rb_define_class_under
  232. # and rb_singleton_class
  233. def do_classes
  234. do_boot_defclass
  235. do_define_class
  236. do_define_class_under
  237. do_singleton_class
  238. do_struct_define_without_accessor
  239. end
  240. ##
  241. # Scans #content for rb_define_variable, rb_define_readonly_variable,
  242. # rb_define_const and rb_define_global_const
  243. def do_constants
  244. @content.scan(%r%\Wrb_define_
  245. ( variable |
  246. readonly_variable |
  247. const |
  248. global_const )
  249. \s*\(
  250. (?:\s*(\w+),)?
  251. \s*"(\w+)",
  252. \s*(.*?)\s*\)\s*;
  253. %xm) do |type, var_name, const_name, definition|
  254. var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
  255. handle_constants type, var_name, const_name, definition
  256. end
  257. @content.scan(%r%
  258. \Wrb_curses_define_const
  259. \s*\(
  260. \s*
  261. (\w+)
  262. \s*
  263. \)
  264. \s*;%xm) do |consts|
  265. const = consts.first
  266. handle_constants 'const', 'mCurses', const, "UINT2NUM(#{const})"
  267. end
  268. @content.scan(%r%
  269. \Wrb_file_const
  270. \s*\(
  271. \s*
  272. "([^"]+)",
  273. \s*
  274. (.*?)
  275. \s*
  276. \)
  277. \s*;%xm) do |name, value|
  278. handle_constants 'const', 'rb_mFConst', name, value
  279. end
  280. end
  281. ##
  282. # Scans #content for rb_define_class
  283. def do_define_class
  284. # The '.' lets us handle SWIG-generated files
  285. @content.scan(/([\w\.]+)\s* = \s*rb_define_class\s*
  286. \(
  287. \s*"(\w+)",
  288. \s*(\w+)\s*
  289. \)/mx) do |var_name, class_name, parent|
  290. handle_class_module(var_name, :class, class_name, parent, nil)
  291. end
  292. end
  293. ##
  294. # Scans #content for rb_define_class_under
  295. def do_define_class_under
  296. @content.scan(/([\w\.]+)\s* = # var_name
  297. \s*rb_define_class_under\s*
  298. \(
  299. \s* (\w+), # under
  300. \s* "(\w+)", # class_name
  301. \s*
  302. (?:
  303. ([\w\*\s\(\)\.\->]+) | # parent_name
  304. rb_path2class\("([\w:]+)"\) # path
  305. )
  306. \s*
  307. \)
  308. /mx) do |var_name, under, class_name, parent_name, path|
  309. parent = path || parent_name
  310. handle_class_module var_name, :class, class_name, parent, under
  311. end
  312. end
  313. ##
  314. # Scans #content for rb_define_module
  315. def do_define_module
  316. @content.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do
  317. |var_name, class_name|
  318. handle_class_module(var_name, :module, class_name, nil, nil)
  319. end
  320. end
  321. ##
  322. # Scans #content for rb_define_module_under
  323. def do_define_module_under
  324. @content.scan(/(\w+)\s* = \s*rb_define_module_under\s*
  325. \(
  326. \s*(\w+),
  327. \s*"(\w+)"
  328. \s*\)/mx) do |var_name, in_module, class_name|
  329. handle_class_module(var_name, :module, class_name, nil, in_module)
  330. end
  331. end
  332. ##
  333. # Scans #content for rb_include_module
  334. def do_includes
  335. @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m|
  336. next unless cls = @classes[c]
  337. m = @known_classes[m] || m
  338. comment = RDoc::Comment.new '', @top_level
  339. incl = cls.add_include RDoc::Include.new(m, comment)
  340. incl.record_location @top_level
  341. end
  342. end
  343. ##
  344. # Scans #content for rb_define_method, rb_define_singleton_method,
  345. # rb_define_module_function, rb_define_private_method,
  346. # rb_define_global_function and define_filetest_function
  347. def do_methods
  348. @content.scan(%r%rb_define_
  349. (
  350. singleton_method |
  351. method |
  352. module_function |
  353. private_method
  354. )
  355. \s*\(\s*([\w\.]+),
  356. \s*"([^"]+)",
  357. \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
  358. \s*(-?\w+)\s*\)
  359. (?:;\s*/[*/]\s+in\s+(\w+?\.(?:cpp|c|y)))?
  360. %xm) do |type, var_name, meth_name, function, param_count, source_file|
  361. # Ignore top-object and weird struct.c dynamic stuff
  362. next if var_name == "ruby_top_self"
  363. next if var_name == "nstr"
  364. var_name = "rb_cObject" if var_name == "rb_mKernel"
  365. handle_method(type, var_name, meth_name, function, param_count,
  366. source_file)
  367. end
  368. @content.scan(%r%rb_define_global_function\s*\(
  369. \s*"([^"]+)",
  370. \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
  371. \s*(-?\w+)\s*\)
  372. (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
  373. %xm) do |meth_name, function, param_count, source_file|
  374. handle_method("method", "rb_mKernel", meth_name, function, param_count,
  375. source_file)
  376. end
  377. @content.scan(/define_filetest_function\s*\(
  378. \s*"([^"]+)",
  379. \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
  380. \s*(-?\w+)\s*\)/xm) do |meth_name, function, param_count|
  381. handle_method("method", "rb_mFileTest", meth_name, function, param_count)
  382. handle_method("singleton_method", "rb_cFile", meth_name, function,
  383. param_count)
  384. end
  385. end
  386. ##
  387. # Creates classes and module that were missing were defined due to the file
  388. # order being different than the declaration order.
  389. def do_missing
  390. return if @missing_dependencies.empty?
  391. @enclosure_dependencies.tsort.each do |in_module|
  392. arguments = @missing_dependencies.delete in_module
  393. next unless arguments # dependency on existing class
  394. handle_class_module(*arguments)
  395. end
  396. end
  397. ##
  398. # Scans #content for rb_define_module and rb_define_module_under
  399. def do_modules
  400. do_define_module
  401. do_define_module_under
  402. end
  403. ##
  404. # Scans #content for rb_singleton_class
  405. def do_singleton_class
  406. @content.scan(/([\w\.]+)\s* = \s*rb_singleton_class\s*
  407. \(
  408. \s*(\w+)
  409. \s*\)/mx) do |sclass_var, class_var|
  410. handle_singleton sclass_var, class_var
  411. end
  412. end
  413. ##
  414. # Scans #content for struct_define_without_accessor
  415. def do_struct_define_without_accessor
  416. @content.scan(/([\w\.]+)\s* = \s*rb_struct_define_without_accessor\s*
  417. \(
  418. \s*"(\w+)", # Class name
  419. \s*(\w+), # Parent class
  420. \s*\w+, # Allocation function
  421. (\s*"\w+",)* # Attributes
  422. \s*NULL
  423. \)/mx) do |var_name, class_name, parent|
  424. handle_class_module(var_name, :class, class_name, parent, nil)
  425. end
  426. end
  427. ##
  428. # Finds the comment for an alias on +class_name+ from +new_name+ to
  429. # +old_name+
  430. def find_alias_comment class_name, new_name, old_name
  431. content =~ %r%((?>/\*.*?\*/\s+))
  432. rb_define_alias\(\s*#{Regexp.escape class_name}\s*,
  433. \s*"#{Regexp.escape new_name}"\s*,
  434. \s*"#{Regexp.escape old_name}"\s*\);%xm
  435. RDoc::Comment.new($1 || '', @top_level)
  436. end
  437. ##
  438. # Finds a comment for rb_define_attr, rb_attr or Document-attr.
  439. #
  440. # +var_name+ is the C class variable the attribute is defined on.
  441. # +attr_name+ is the attribute's name.
  442. #
  443. # +read+ and +write+ are the read/write flags ('1' or '0'). Either both or
  444. # neither must be provided.
  445. def find_attr_comment var_name, attr_name, read = nil, write = nil
  446. attr_name = Regexp.escape attr_name
  447. rw = if read and write then
  448. /\s*#{read}\s*,\s*#{write}\s*/xm
  449. else
  450. /.*?/m
  451. end
  452. comment = if @content =~ %r%((?>/\*.*?\*/\s+))
  453. rb_define_attr\((?:\s*#{var_name},)?\s*
  454. "#{attr_name}"\s*,
  455. #{rw}\)\s*;%xm then
  456. $1
  457. elsif @content =~ %r%((?>/\*.*?\*/\s+))
  458. rb_attr\(\s*#{var_name}\s*,
  459. \s*#{attr_name}\s*,
  460. #{rw},.*?\)\s*;%xm then
  461. $1
  462. elsif @content =~ %r%Document-attr:\s#{attr_name}\s*?\n
  463. ((?>.*?\*/))%xm then
  464. $1
  465. else
  466. ''
  467. end
  468. RDoc::Comment.new comment, @top_level
  469. end
  470. ##
  471. # Find the C code corresponding to a Ruby method
  472. def find_body class_name, meth_name, meth_obj, file_content, quiet = false
  473. case file_content
  474. when %r%((?>/\*.*?\*/\s*)?)
  475. ((?:(?:static|SWIGINTERN)\s+)?
  476. (?:intern\s+)?VALUE\s+#{meth_name}
  477. \s*(\([^)]*\))([^;]|$))%xm then
  478. comment = RDoc::Comment.new $1, @top_level
  479. body = $2
  480. offset, = $~.offset(2)
  481. comment.remove_private if comment
  482. # try to find the whole body
  483. body = $& if /#{Regexp.escape body}[^(]*?\{.*?^\}/m =~ file_content
  484. # The comment block may have been overridden with a 'Document-method'
  485. # block. This happens in the interpreter when multiple methods are
  486. # vectored through to the same C method but those methods are logically
  487. # distinct (for example Kernel.hash and Kernel.object_id share the same
  488. # implementation
  489. override_comment = find_override_comment class_name, meth_obj
  490. comment = override_comment if override_comment
  491. comment.normalize
  492. find_modifiers comment, meth_obj if comment
  493. #meth_obj.params = params
  494. meth_obj.start_collecting_tokens
  495. tk = RDoc::RubyToken::Token.new nil, 1, 1
  496. tk.set_text body
  497. meth_obj.add_token tk
  498. meth_obj.comment = comment
  499. meth_obj.offset = offset
  500. meth_obj.line = file_content[0, offset].count("\n") + 1
  501. body
  502. when %r%((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+#{meth_name}\s+(\w+))%m then
  503. comment = RDoc::Comment.new $1, @top_level
  504. body = $2
  505. offset = $~.offset(2).first
  506. find_body class_name, $3, meth_obj, file_content, true
  507. comment.normalize
  508. find_modifiers comment, meth_obj
  509. meth_obj.start_collecting_tokens
  510. tk = RDoc::RubyToken::Token.new nil, 1, 1
  511. tk.set_text body
  512. meth_obj.add_token tk
  513. meth_obj.comment = comment
  514. meth_obj.offset = offset
  515. meth_obj.line = file_content[0, offset].count("\n") + 1
  516. body
  517. when %r%^\s*\#\s*define\s+#{meth_name}\s+(\w+)%m then
  518. # with no comment we hope the aliased definition has it and use it's
  519. # definition
  520. body = find_body(class_name, $1, meth_obj, file_content, true)
  521. return body if body
  522. @options.warn "No definition for #{meth_name}"
  523. false
  524. else # No body, but might still have an override comment
  525. comment = find_override_comment class_name, meth_obj
  526. if comment then
  527. comment.normalize
  528. find_modifiers comment, meth_obj
  529. meth_obj.comment = comment
  530. ''
  531. else
  532. @options.warn "No definition for #{meth_name}"
  533. false
  534. end
  535. end
  536. end
  537. ##
  538. # Finds a RDoc::NormalClass or RDoc::NormalModule for +raw_name+
  539. def find_class(raw_name, name)
  540. unless @classes[raw_name]
  541. if raw_name =~ /^rb_m/
  542. container = @top_level.add_module RDoc::NormalModule, name
  543. else
  544. container = @top_level.add_class RDoc::NormalClass, name
  545. end
  546. container.record_location @top_level
  547. @classes[raw_name] = container
  548. end
  549. @classes[raw_name]
  550. end
  551. ##
  552. # Look for class or module documentation above Init_+class_name+(void),
  553. # in a Document-class +class_name+ (or module) comment or above an
  554. # rb_define_class (or module). If a comment is supplied above a matching
  555. # Init_ and a rb_define_class the Init_ comment is used.
  556. #
  557. # /*
  558. # * This is a comment for Foo
  559. # */
  560. # Init_Foo(void) {
  561. # VALUE cFoo = rb_define_class("Foo", rb_cObject);
  562. # }
  563. #
  564. # /*
  565. # * Document-class: Foo
  566. # * This is a comment for Foo
  567. # */
  568. # Init_foo(void) {
  569. # VALUE cFoo = rb_define_class("Foo", rb_cObject);
  570. # }
  571. #
  572. # /*
  573. # * This is a comment for Foo
  574. # */
  575. # VALUE cFoo = rb_define_class("Foo", rb_cObject);
  576. def find_class_comment class_name, class_mod
  577. comment = nil
  578. if @content =~ %r%
  579. ((?>/\*.*?\*/\s+))
  580. (static\s+)?
  581. void\s+
  582. Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)%xmi then
  583. comment = $1.sub(%r%Document-(?:class|module):\s+#{class_name}%, '')
  584. elsif @content =~ %r%Document-(?:class|module):\s+#{class_name}\s*?
  585. (?:<\s+[:,\w]+)?\n((?>.*?\*/))%xm then
  586. comment = "/*\n#{$1}"
  587. elsif @content =~ %r%.*((?>/\*.*?\*/\s+))
  588. ([\w\.\s]+\s* = \s+)?rb_define_(class|module).*?"(#{class_name})"%xm then
  589. comment = $1
  590. elsif @content =~ %r%.*((?>/\*.*?\*/\s+))
  591. ([\w\.\s]+\s* = \s+)?rb_define_(class|module)_under.*?"(#{class_name.split('::').last})"%xm then
  592. comment = $1
  593. else
  594. comment = ''
  595. end
  596. comment = RDoc::Comment.new comment, @top_level
  597. comment.normalize
  598. look_for_directives_in class_mod, comment
  599. class_mod.add_comment comment, @top_level
  600. end
  601. ##
  602. # Finds a comment matching +type+ and +const_name+ either above the
  603. # comment or in the matching Document- section.
  604. def find_const_comment(type, const_name, class_name = nil)
  605. comment = if @content =~ %r%((?>^\s*/\*.*?\*/\s+))
  606. rb_define_#{type}\((?:\s*(\w+),)?\s*
  607. "#{const_name}"\s*,
  608. .*?\)\s*;%xmi then
  609. $1
  610. elsif class_name and
  611. @content =~ %r%Document-(?:const|global|variable):\s
  612. #{class_name}::#{const_name}
  613. \s*?\n((?>.*?\*/))%xm then
  614. "/*\n#{$1}"
  615. elsif @content =~ %r%Document-(?:const|global|variable):
  616. \s#{const_name}
  617. \s*?\n((?>.*?\*/))%xm then
  618. "/*\n#{$1}"
  619. else
  620. ''
  621. end
  622. RDoc::Comment.new comment, @top_level
  623. end
  624. ##
  625. # Handles modifiers in +comment+ and updates +meth_obj+ as appropriate.
  626. def find_modifiers comment, meth_obj
  627. comment.normalize
  628. comment.extract_call_seq meth_obj
  629. look_for_directives_in meth_obj, comment
  630. end
  631. ##
  632. # Finds a <tt>Document-method</tt> override for +meth_obj+ on +class_name+
  633. def find_override_comment class_name, meth_obj
  634. name = Regexp.escape meth_obj.name
  635. prefix = Regexp.escape meth_obj.name_prefix
  636. comment = if @content =~ %r%Document-method:
  637. \s+#{class_name}#{prefix}#{name}
  638. \s*?\n((?>.*?\*/))%xm then
  639. "/*#{$1}"
  640. elsif @content =~ %r%Document-method:
  641. \s#{name}\s*?\n((?>.*?\*/))%xm then
  642. "/*#{$1}"
  643. end
  644. return unless comment
  645. RDoc::Comment.new comment, @top_level
  646. end
  647. ##
  648. # Creates a new RDoc::Attr +attr_name+ on class +var_name+ that is either
  649. # +read+, +write+ or both
  650. def handle_attr(var_name, attr_name, read, write)
  651. rw = ''
  652. rw << 'R' if '1' == read
  653. rw << 'W' if '1' == write
  654. class_name = @known_classes[var_name]
  655. return unless class_name
  656. class_obj = find_class var_name, class_name
  657. return unless class_obj
  658. comment = find_attr_comment var_name, attr_name
  659. comment.normalize
  660. name = attr_name.gsub(/rb_intern\("([^"]+)"\)/, '\1')
  661. attr = RDoc::Attr.new '', name, rw, comment
  662. attr.record_location @top_level
  663. class_obj.add_attribute attr
  664. @stats.add_attribute attr
  665. end
  666. ##
  667. # Creates a new RDoc::NormalClass or RDoc::NormalModule based on +type+
  668. # named +class_name+ in +parent+ which was assigned to the C +var_name+.
  669. def handle_class_module(var_name, type, class_name, parent, in_module)
  670. parent_name = @known_classes[parent] || parent
  671. if in_module then
  672. enclosure = @classes[in_module] || @store.find_c_enclosure(in_module)
  673. if enclosure.nil? and enclosure = @known_classes[in_module] then
  674. enc_type = /^rb_m/ =~ in_module ? :module : :class
  675. handle_class_module in_module, enc_type, enclosure, nil, nil
  676. enclosure = @classes[in_module]
  677. end
  678. unless enclosure then
  679. @enclosure_dependencies[in_module] << var_name
  680. @missing_dependencies[var_name] =
  681. [var_name, type, class_name, parent, in_module]
  682. return
  683. end
  684. else
  685. enclosure = @top_level
  686. end
  687. if type == :class then
  688. full_name = if RDoc::ClassModule === enclosure then
  689. enclosure.full_name + "::#{class_name}"
  690. else
  691. class_name
  692. end
  693. if @content =~ %r%Document-class:\s+#{full_name}\s*<\s+([:,\w]+)% then
  694. parent_name = $1
  695. end
  696. cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name
  697. else
  698. cm = enclosure.add_module RDoc::NormalModule, class_name
  699. end
  700. cm.record_location enclosure.top_level
  701. find_class_comment cm.full_name, cm
  702. case cm
  703. when RDoc::NormalClass
  704. @stats.add_class cm
  705. when RDoc::NormalModule
  706. @stats.add_module cm
  707. end
  708. @classes[var_name] = cm
  709. @known_classes[var_name] = cm.full_name
  710. @store.add_c_enclosure var_name, cm
  711. end
  712. ##
  713. # Adds constants. By providing some_value: at the start of the comment you
  714. # can override the C value of the comment to give a friendly definition.
  715. #
  716. # /* 300: The perfect score in bowling */
  717. # rb_define_const(cFoo, "PERFECT", INT2FIX(300);
  718. #
  719. # Will override <tt>INT2FIX(300)</tt> with the value +300+ in the output
  720. # RDoc. Values may include quotes and escaped colons (\:).
  721. def handle_constants(type, var_name, const_name, definition)
  722. class_name = @known_classes[var_name]
  723. return unless class_name
  724. class_obj = find_class var_name, class_name
  725. unless class_obj then
  726. @options.warn 'Enclosing class or module %p is not known' % [const_name]
  727. return
  728. end
  729. comment = find_const_comment type, const_name, class_name
  730. comment.normalize
  731. # In the case of rb_define_const, the definition and comment are in
  732. # "/* definition: comment */" form. The literal ':' and '\' characters
  733. # can be escaped with a backslash.
  734. if type.downcase == 'const' then
  735. no_match, new_definition, new_comment = comment.text.split(/(\A.*):/)
  736. if no_match and no_match.empty? then
  737. if new_definition.empty? then # Default to literal C definition
  738. new_definition = definition
  739. else
  740. new_definition.gsub!("\:", ":")
  741. new_definition.gsub!("\\", '\\')
  742. end
  743. new_definition.sub!(/\A(\s+)/, '')
  744. new_comment = "#{$1}#{new_comment.lstrip}"
  745. new_comment = RDoc::Comment.new new_comment, @top_level
  746. con = RDoc::Constant.new const_name, new_definition, new_comment
  747. else
  748. con = RDoc::Constant.new const_name, definition, comment
  749. end
  750. else
  751. con = RDoc::Constant.new const_name, definition, comment
  752. end
  753. con.record_location @top_level
  754. @stats.add_constant con
  755. class_obj.add_constant con
  756. end
  757. ##
  758. # Removes #ifdefs that would otherwise confuse us
  759. def handle_ifdefs_in(body)
  760. body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1')
  761. end
  762. ##
  763. # Adds an RDoc::AnyMethod +meth_name+ defined on a class or module assigned
  764. # to +var_name+. +type+ is the type of method definition function used.
  765. # +singleton_method+ and +module_function+ create a singleton method.
  766. def handle_method(type, var_name, meth_name, function, param_count,
  767. source_file = nil)
  768. class_name = @known_classes[var_name]
  769. singleton = @singleton_classes.key? var_name
  770. return unless class_name
  771. class_obj = find_class var_name, class_name
  772. if class_obj then
  773. if meth_name == 'initialize' then
  774. meth_name = 'new'
  775. singleton = true
  776. type = 'method' # force public
  777. end
  778. meth_obj = RDoc::AnyMethod.new '', meth_name
  779. meth_obj.c_function = function
  780. meth_obj.singleton =
  781. singleton || %w[singleton_method module_function].include?(type)
  782. p_count = Integer(param_count) rescue -1
  783. if source_file then
  784. file_name = File.join @file_dir, source_file
  785. if File.exist? file_name then
  786. file_content = File.read file_name
  787. else
  788. @options.warn "unknown source #{source_file} for #{meth_name} in #{@file_name}"
  789. end
  790. else
  791. file_content = @content
  792. end
  793. body = find_body class_name, function, meth_obj, file_content
  794. if body and meth_obj.document_self then
  795. meth_obj.params = if p_count < -1 then # -2 is Array
  796. '(*args)'
  797. elsif p_count == -1 then # argc, argv
  798. rb_scan_args body
  799. else
  800. "(#{(1..p_count).map { |i| "p#{i}" }.join ', '})"
  801. end
  802. meth_obj.record_location @top_level
  803. class_obj.add_method meth_obj
  804. @stats.add_method meth_obj
  805. meth_obj.visibility = :private if 'private_method' == type
  806. end
  807. end
  808. end
  809. ##
  810. # Registers a singleton class +sclass_var+ as a singleton of +class_var+
  811. def handle_singleton sclass_var, class_var
  812. class_name = @known_classes[class_var]
  813. @known_classes[sclass_var] = class_name
  814. @singleton_classes[sclass_var] = class_name
  815. end
  816. ##
  817. # Normalizes tabs in +body+
  818. def handle_tab_width(body)
  819. if /\t/ =~ body
  820. tab_width = @options.tab_width
  821. body.split(/\n/).map do |line|
  822. 1 while line.gsub!(/\t+/) do
  823. ' ' * (tab_width * $&.length - $`.length % tab_width)
  824. end && $~
  825. line
  826. end.join "\n"
  827. else
  828. body
  829. end
  830. end
  831. ##
  832. # Loads the variable map with the given +name+ from the RDoc::Store, if
  833. # present.
  834. def load_variable_map map_name
  835. return {} unless files = @store.cache[map_name]
  836. return {} unless name_map = files[@file_name]
  837. class_map = {}
  838. name_map.each do |variable, name|
  839. next unless mod = @store.find_class_or_module(name)
  840. class_map[variable] = if map_name == :c_class_variables then
  841. mod
  842. else
  843. name
  844. end
  845. @known_classes[variable] = name
  846. end
  847. class_map
  848. end
  849. ##
  850. # Look for directives in a normal comment block:
  851. #
  852. # /*
  853. # * :title: My Awesome Project
  854. # */
  855. #
  856. # This method modifies the +comment+
  857. def look_for_directives_in context, comment
  858. @preprocess.handle comment, context do |directive, param|
  859. case directive
  860. when 'main' then
  861. @options.main_page = param
  862. ''
  863. when 'title' then
  864. @options.default_title = param if @options.respond_to? :default_title=
  865. ''
  866. end
  867. end
  868. comment
  869. end
  870. ##
  871. # Extracts parameters from the +method_body+ and returns a method
  872. # parameter string. Follows 1.9.3dev's scan-arg-spec, see README.EXT
  873. def rb_scan_args method_body
  874. method_body =~ /rb_scan_args\((.*?)\)/m
  875. return '(*args)' unless $1
  876. $1.split(/,/)[2] =~ /"(.*?)"/ # format argument
  877. format = $1.split(//)
  878. lead = opt = trail = 0
  879. if format.first =~ /\d/ then
  880. lead = $&.to_i
  881. format.shift
  882. if format.first =~ /\d/ then
  883. opt = $&.to_i
  884. format.shift
  885. if format.first =~ /\d/ then
  886. trail = $&.to_i
  887. format.shift
  888. block_arg = true
  889. end
  890. end
  891. end
  892. if format.first == '*' and not block_arg then
  893. var = true
  894. format.shift
  895. if format.first =~ /\d/ then
  896. trail = $&.to_i
  897. format.shift
  898. end
  899. end
  900. if format.first == ':' then
  901. hash = true
  902. format.shift
  903. end
  904. if format.first == '&' then
  905. block = true
  906. format.shift
  907. end
  908. # if the format string is not empty there's a bug in the C code, ignore it
  909. args = []
  910. position = 1
  911. (1...(position + lead)).each do |index|
  912. args << "p#{index}"
  913. end
  914. position += lead
  915. (position...(position + opt)).each do |index|
  916. args << "p#{index} = v#{index}"
  917. end
  918. position += opt
  919. if var then
  920. args << '*args'
  921. position += 1
  922. end
  923. (position...(position + trail)).each do |index|
  924. args << "p#{index}"
  925. end
  926. position += trail
  927. if hash then
  928. args << "p#{position} = {}"
  929. position += 1
  930. end
  931. args << '&block' if block
  932. "(#{args.join ', '})"
  933. end
  934. ##
  935. # Removes lines that are commented out that might otherwise get picked up
  936. # when scanning for classes and methods
  937. def remove_commented_out_lines
  938. @content.gsub!(%r%//.*rb_define_%, '//')
  939. end
  940. ##
  941. # Extracts the classes, modules, methods, attributes, constants and aliases
  942. # from a C file and returns an RDoc::TopLevel for this file
  943. def scan
  944. remove_commented_out_lines
  945. do_modules
  946. do_classes
  947. do_missing
  948. do_constants
  949. do_methods
  950. do_includes
  951. do_aliases
  952. do_attrs
  953. @store.add_c_variables self
  954. @top_level
  955. end
  956. end