Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/rdoc/rdoc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ def document(options)
if @options.coverage_report then
puts

puts @stats.report.accept RDoc::Markup::ToRdoc.new
puts @stats.report
elsif file_info.empty? then
$stderr.puts "\nNo newer files." unless @options.quiet
else
Expand All @@ -510,7 +510,7 @@ def document(options)

if @stats and (@options.coverage_report or not @options.quiet) then
puts
puts @stats.summary.accept RDoc::Markup::ToRdoc.new
puts @stats.summary
end

exit @stats.fully_documented? if @options.coverage_report
Expand Down
235 changes: 114 additions & 121 deletions lib/rdoc/stats.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ class RDoc::Stats

include RDoc::Text

TYPE_ORDER = %w[Class Module Constant Attribute Method].freeze
GREAT_JOB_MESSAGE = <<~MSG
100% documentation!
Great Job!
MSG

##
# Output level for the coverage report

Expand Down Expand Up @@ -193,17 +199,6 @@ def fully_documented?
@fully_documented
end

##
# A report that says you did a great job!

def great_job
report = RDoc::Markup::Document.new

report << RDoc::Markup::Paragraph.new('100% documentation!')
report << RDoc::Markup::Paragraph.new('Great Job!')

report
end

##
# Calculates the percentage of items documented.
Expand All @@ -230,164 +225,167 @@ def report
if @coverage_level.zero? then
calculate

return great_job if @num_items == @doc_items
return GREAT_JOB_MESSAGE if @num_items == @doc_items
end

ucm = @store.unique_classes_and_modules
items, empty_classes = collect_undocumented_items

report = RDoc::Markup::Document.new
report << RDoc::Markup::Paragraph.new('The following items are not documented:')
report << RDoc::Markup::BlankLine.new

ucm.sort.each do |cm|
body = report_class_module(cm) {
[
report_constants(cm),
report_attributes(cm),
report_methods(cm),
].compact
}
if @coverage_level > 0 then
calculate

report << body if body
return GREAT_JOB_MESSAGE if @num_items == @doc_items
end

if @coverage_level > 0 then
calculate
report = +""
report << "The following items are not documented:\n\n"

return great_job if @num_items == @doc_items
# Referenced-but-empty classes
empty_classes.each do |cm|
report << "#{cm.full_name} is referenced but empty.\n"
report << "It probably came from another project. I'm sorry I'm holding it against you.\n\n"
end

report
end
# Group items by file, then by type
by_file = items.group_by { |item| item[:file] }

##
# Returns a report on undocumented attributes in ClassModule +cm+
by_file.sort_by { |file, _| file }.each do |file, file_items|
report << "#{file}:\n"

def report_attributes(cm)
return if cm.attributes.empty?
by_type = file_items.group_by { |item| item[:type] }

report = []
TYPE_ORDER.each do |type|
next unless by_type[type]

report << " #{type}:\n"

sorted = by_type[type].sort_by { |item| [item[:line] || 0, item[:name]] }
name_width = sorted.reduce(0) { |max, item| item[:line] && item[:name].length > max ? item[:name].length : max }

sorted.each do |item|
if item[:line]
report << " %-*s %s:%d\n" % [name_width, item[:name], item[:file], item[:line]]
else
report << " #{item[:name]}\n"
end

if item[:undoc_params]
report << " Undocumented params: #{item[:undoc_params].join(', ')}\n"
end
end
end

cm.attributes.each do |attr|
next if attr.documented?
line = attr.line ? ":#{attr.line}" : nil
report << " #{attr.definition} :#{attr.name} # in file #{attr.file.full_name}#{line}\n"
report << "\n"
end

report
end

##
# Returns a report on undocumented items in ClassModule +cm+
# Collects all undocumented items across all classes and modules.
# Returns [items, empty_classes] where items is an Array of Hashes
# with keys :type, :name, :file, :line, and empty_classes is an
# Array of ClassModule objects that are referenced but have no files.

def report_class_module(cm)
return if cm.fully_documented? and @coverage_level.zero?
return unless cm.display?
def collect_undocumented_items
empty_classes = []
items = []

report = RDoc::Markup::Document.new
@store.unique_classes_and_modules.each do |cm|
next unless cm.display?

if cm.in_files.empty? then
report << RDoc::Markup::Paragraph.new("#{cm.definition} is referenced but empty.")
report << RDoc::Markup::Paragraph.new("It probably came from another project. I'm sorry I'm holding it against you.")

return report
elsif cm.documented? then
documented = true
klass = RDoc::Markup::Verbatim.new("#{cm.definition} # is documented\n")
else
report << RDoc::Markup::Paragraph.new('In files:')

list = RDoc::Markup::List.new :BULLET

cm.in_files.each do |file|
para = RDoc::Markup::Paragraph.new file.full_name
list << RDoc::Markup::ListItem.new(nil, para)
if cm.in_files.empty?
empty_classes << cm
next
end

report << list
report << RDoc::Markup::BlankLine.new
unless cm.documented? || cm.full_name == 'Object'
file = cm.in_files.first&.full_name
items << { type: cm.type.capitalize, name: cm.full_name, file: file, line: nil } if file
end

klass = RDoc::Markup::Verbatim.new("#{cm.definition}\n")
collect_undocumented_constants(cm, items)
collect_undocumented_attributes(cm, items)
collect_undocumented_methods(cm, items)
end

klass << "\n"

body = yield.flatten # HACK remove #flatten

if body.empty? then
return if documented
[items, empty_classes]
end

klass.parts.pop
else
klass.parts.concat body
end
##
# Collects undocumented constants from +cm+ into +items+.

klass << "end\n"
def collect_undocumented_constants(cm, items)
cm.constants.each do |constant|
next if constant.documented? || constant.is_alias_for

report << klass
file = constant.file&.full_name
next unless file

report
items << {
type: "Constant",
name: constant.full_name,
file: file,
line: constant.line,
}
end
end

##
# Returns a report on undocumented constants in ClassModule +cm+
# Collects undocumented attributes from +cm+ into +items+.

def report_constants(cm)
return if cm.constants.empty?

report = []
def collect_undocumented_attributes(cm, items)
cm.attributes.each do |attr|
next if attr.documented?

cm.constants.each do |constant|
# TODO constant aliases are listed in the summary but not reported
# figure out what to do here
next if constant.documented? || constant.is_alias_for
file = attr.file&.full_name
next unless file

line = constant.line ? ":#{constant.line}" : line
report << " # in file #{constant.file.full_name}#{line}\n"
report << " #{constant.name} = nil\n"
report << "\n"
scope = attr.singleton ? "." : "#"
items << {
type: "Attribute",
name: "#{cm.full_name}#{scope}#{attr.name}",
file: file,
line: attr.line,
}
end

report
end

##
# Returns a report on undocumented methods in ClassModule +cm+

def report_methods(cm)
return if cm.method_list.empty?

report = []
# Collects undocumented methods from +cm+ into +items+.
# At coverage level > 0, also counts undocumented parameters.

def collect_undocumented_methods(cm, items)
cm.each_method do |method|
next if method.documented? and @coverage_level.zero?
next if method.documented? && @coverage_level.zero?

if @coverage_level > 0 then
params, undoc = undoc_params method
undoc_param_names = nil

if @coverage_level > 0
params, undoc = undoc_params method
@num_params += params

unless undoc.empty? then
unless undoc.empty?
@undoc_params += undoc.length

undoc = undoc.map do |param| "+#{param}+" end
param_report = " # #{undoc.join ', '} is not documented\n"
undoc_param_names = undoc
end
end

next if method.documented? and not param_report
next if method.documented? && !undoc_param_names

line = method.line ? ":#{method.line}" : nil
scope = method.singleton ? 'self.' : nil
file = method.file&.full_name
next unless file

report << " # in file #{method.file.full_name}#{line}\n"
report << param_report if param_report
report << " def #{scope}#{method.name}#{method.params}; end\n"
report << "\n"
end
scope = method.singleton ? "." : "#"
item = {
type: "Method",
name: "#{cm.full_name}#{scope}#{method.name}",
file: file,
line: method.line,
}
item[:undoc_params] = undoc_param_names if undoc_param_names

report
items << item
end
end

##
Expand All @@ -407,12 +405,10 @@ def summary
@undoc_params,
].max.to_s.length

report = RDoc::Markup::Verbatim.new
report = +""

report << "Files: %*d\n" % [num_width, @num_files]

report << "\n"

report << "Classes: %*d (%*d undocumented)\n" % [
num_width, @num_classes, undoc_width, @undoc_classes]
report << "Modules: %*d (%*d undocumented)\n" % [
Expand All @@ -426,17 +422,14 @@ def summary
report << "Parameters: %*d (%*d undocumented)\n" % [
num_width, @num_params, undoc_width, @undoc_params] if
@coverage_level > 0

report << "\n"

report << "Total: %*d (%*d undocumented)\n" % [
num_width, @num_items, undoc_width, @undoc_items]

report << "%6.2f%% documented\n" % percent_doc
report << "\n"
report << "Elapsed: %0.1fs\n" % (Time.now - @start)

RDoc::Markup::Document.new report
report
end

##
Expand Down
Loading
Loading