lib/rdoc/parser/c.rb in rdoc-3.4 vs lib/rdoc/parser/c.rb in rdoc-3.5

- old
+ new

@@ -283,40 +283,41 @@ \s*\(\s*([\w\.]+), \s*"([^"]+)", \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, \s*(-?\w+)\s*\) (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))? - %xm) do |type, var_name, meth_name, meth_body, param_count, source_file| + %xm) do |type, var_name, meth_name, function, param_count, source_file| # Ignore top-object and weird struct.c dynamic stuff next if var_name == "ruby_top_self" next if var_name == "nstr" next if var_name == "envtbl" next if var_name == "argf" # it'd be nice to handle this one var_name = "rb_cObject" if var_name == "rb_mKernel" - handle_method(type, var_name, meth_name, meth_body, param_count, + handle_method(type, var_name, meth_name, function, param_count, source_file) end @content.scan(%r%rb_define_global_function\s*\( \s*"([^"]+)", \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, \s*(-?\w+)\s*\) (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))? - %xm) do |meth_name, meth_body, param_count, source_file| - handle_method("method", "rb_mKernel", meth_name, - meth_body, param_count, source_file) + %xm) do |meth_name, function, param_count, source_file| + handle_method("method", "rb_mKernel", meth_name, function, param_count, + source_file) end @content.scan(/define_filetest_function\s*\( \s*"([^"]+)", \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, - \s*(-?\w+)\s*\)/xm) do |meth_name, meth_body, param_count| + \s*(-?\w+)\s*\)/xm) do |meth_name, function, param_count| - handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count) - handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count) + handle_method("method", "rb_mFileTest", meth_name, function, param_count) + handle_method("singleton_method", "rb_cFile", meth_name, function, + param_count) end end ## # Finds the comment for an alias on +class_name+ from +new_name+ to @@ -368,26 +369,25 @@ end ## # Find the C code corresponding to a Ruby method - def find_body(class_name, meth_name, meth_obj, body, quiet = false) - case body + def find_body class_name, meth_name, meth_obj, file_content, quiet = false + case file_content when %r%((?>/\*.*?\*/\s*)?) ((?:(?:static|SWIGINTERN)\s+)? (?:intern\s+)?VALUE\s+#{meth_name} - \s*(\([^)]*\))([^;]|$))%xm then + \s*(\(.*?\))([^;]|$))%xm then comment = $1 - body_text = $2 + body = $2 + offset = $~.offset(2).first remove_private_comments comment if comment - # see if we can find the whole body + # try to find the whole body + body = $& if /#{Regexp.escape body}[^(]*?\{.*?^\}/m =~ file_content - re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}' - body_text = $& if /#{re}/m =~ body - # The comment block may have been overridden with a 'Document-method' # block. This happens in the interpreter when multiple methods are # vectored through to the same C method but those methods are logically # distinct (for example Kernel.hash and Kernel.object_id share the same # implementation @@ -398,42 +398,57 @@ find_modifiers comment, meth_obj if comment #meth_obj.params = params meth_obj.start_collecting_tokens tk = RDoc::RubyToken::Token.new nil, 1, 1 - tk.set_text body_text + tk.set_text body meth_obj.add_token tk meth_obj.comment = strip_stars comment - when %r%((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+#{meth_name}\s+(\w+))%m + meth_obj.offset = offset + meth_obj.line = file_content[0, offset].count("\n") + 1 + + body + when %r%((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+#{meth_name}\s+(\w+))%m then comment = $1 - body_text = $2 - find_body class_name, $3, meth_obj, body, true + body = $2 + offset = $~.offset(2).first + + find_body class_name, $3, meth_obj, file_content, true find_modifiers comment, meth_obj meth_obj.start_collecting_tokens tk = RDoc::RubyToken::Token.new nil, 1, 1 - tk.set_text body_text + tk.set_text body meth_obj.add_token tk meth_obj.comment = strip_stars(comment) + meth_obj.comment.to_s - when %r%^\s*\#\s*define\s+#{meth_name}\s+(\w+)%m - unless find_body(class_name, $1, meth_obj, body, true) - warn "No definition for #{meth_name}" if @options.verbosity > 1 - return false - end + meth_obj.offset = offset + meth_obj.line = file_content[0, offset].count("\n") + 1 + + body + when %r%^\s*\#\s*define\s+#{meth_name}\s+(\w+)%m then + # with no comment we hope the aliased definition has it and use it's + # definition + + body = find_body(class_name, $1, meth_obj, file_content, true) + + return body if body + + warn "No definition for #{meth_name}" if @options.verbosity > 1 + false else # No body, but might still have an override comment comment = find_override_comment class_name, meth_obj.name - if comment + if comment then find_modifiers comment, meth_obj meth_obj.comment = strip_stars comment + + '' else warn "No definition for #{meth_name}" if @options.verbosity > 1 - return false + false end end - - true end ## # Finds a RDoc::NormalClass or RDoc::NormalModule for +raw_name+ @@ -749,11 +764,11 @@ ## # Adds an RDoc::AnyMethod +meth_name+ defined on a class or module assigned # to +var_name+. +type+ is the type of method definition function used. # +singleton_method+ and +module_function+ create a singleton method. - def handle_method(type, var_name, meth_name, meth_body, param_count, + def handle_method(type, var_name, meth_name, function, param_count, source_file = nil) singleton = false class_name = @known_classes[var_name] unless class_name then @@ -771,38 +786,40 @@ singleton = true type = 'method' # force public end meth_obj = RDoc::AnyMethod.new '', meth_name + meth_obj.c_function = function meth_obj.singleton = singleton || %w[singleton_method module_function].include?(type) p_count = Integer(param_count) rescue -1 - if p_count < 0 then - meth_obj.params = "(*args)" - elsif p_count == 0 - meth_obj.params = "()" - else - params = (1..p_count).map { |i| "p#{i}" }.join(', ') - meth_obj.params = "(#{params})" - end - if source_file then file_name = File.join @file_dir, source_file if File.exist? file_name then - body = (@@known_bodies[file_name] ||= File.read(file_name)) + file_content = (@@known_bodies[file_name] ||= File.read(file_name)) else warn "unknown source #{source_file} for #{meth_name} in #{@file_name}" end else - body = @content + file_content = @content end - if find_body(class_name, meth_body, meth_obj, body) and - meth_obj.document_self then + body = find_body class_name, function, meth_obj, file_content + + if body and meth_obj.document_self then + meth_obj.params = if p_count < -1 then # -2 is Array + '(*args)' + elsif p_count == -1 then # argc, argv + rb_scan_args body + else + "(#{(1..p_count).map { |i| "p#{i}" }.join ', '})" + end + + meth_obj.record_location @top_level class_obj.add_method meth_obj @stats.add_method meth_obj meth_obj.visibility = :private if 'private_method' == type end @@ -857,9 +874,93 @@ '' end end comment + end + + ## + # Extracts parameters from the +method_body+ and returns a method + # parameter string. Follows 1.9.3dev's scan-arg-spec, see README.EXT + + def rb_scan_args method_body + method_body =~ /rb_scan_args\((.*?)\)/m + return '(*args)' unless $1 + + $1.split(/,/)[2] =~ /"(.*?)"/ # format argument + format = $1.split(//) + + lead = opt = trail = 0 + + if format.first =~ /\d/ then + lead = $&.to_i + format.shift + if format.first =~ /\d/ then + opt = $&.to_i + format.shift + if format.first =~ /\d/ then + trail = $&.to_i + format.shift + block_arg = true + end + end + end + + if format.first == '*' and not block_arg then + var = true + format.shift + if format.first =~ /\d/ then + trail = $&.to_i + format.shift + end + end + + if format.first == ':' then + hash = true + format.shift + end + + if format.first == '&' then + block = true + format.shift + end + + # if the format string is not empty there's a bug in the C code, ignore it + + args = [] + position = 1 + + (1...(position + lead)).each do |index| + args << "p#{index}" + end + + position += lead + + (position...(position + opt)).each do |index| + args << "p#{index} = v#{index}" + end + + position += opt + + if var then + args << '*args' + position += 1 + end + + (position...(position + trail)).each do |index| + args << "p#{index}" + end + + position += trail + + if hash then + args << "p#{position} = {}" + position += 1 + end + + args << '&block' if block + + "(#{args.join ', '})" end ## # Removes lines that are commented out that might otherwise get picked up # when scanning for classes and methods