https://mensfeld.pl/2025/11/yard-lint-ruby-documentation-linter/
https://github.com/mensfeld/yard-lint
We already attempt to lint some of this in a janky way:
|
desc "Check documentation coverage" |
|
task :docs_coverage do |
|
doc_output = run_yard_doc_ignoring_expected_warnings |
|
|
|
coverage = doc_output[/([\d.]+)% documented/, 1] |
|
warning_count = doc_output.scan("[warn]:").count |
|
error_count = doc_output.scan("[error]:").count |
|
|
|
if coverage.to_f < 100 |
|
# Since we do not have 100% coverage, we want to list what is undocumented. |
|
# |
|
# Note: we don't use this as the main command above because we've observed that |
|
# `stats` does not produce as many warnings as `doc`--so we'd rather run `doc` |
|
# when detecting warnings, and use `stats --list-undoc` for supplemental output. |
|
undoc_output = IO.popen(yard_cmd("stats --list-undoc")).read |
|
|
|
# Just print the output starting with `Undocumented Objects" |
|
puts "\n#{undoc_output[/^Undocumented .*/m]}" |
|
end |
|
|
|
issues = [] |
|
issues << "Missing documentation coverage (currently at #{coverage}%)." if coverage.to_f < 100 |
|
issues << "YARD emitted #{warning_count} documentation warning(s)." if warning_count > 0 |
|
issues << "YARD emitted #{error_count} documentation error(s)." if error_count > 0 |
|
|
|
unless issues.empty? |
|
abort <<~EOS |
|
|
|
Documentation has #{issues.size} issues: |
|
|
|
#{issues.map { |i| " - #{i}" }.join("\n")} |
|
EOS |
|
end |
|
end |
|
# Yard doesn't allow us to suppress warnings. Warnings are for code constructs its not able to understand, and sometimes we're ok |
|
# with that (e.g. when the alternative is making the code worse or more verbose). Here we suppress warnings using custom logic: |
|
# we filter out specific warnings that we don't want to be notified about. |
|
YARD_WARNINGS_TO_IGNORE = { |
|
# `Data.define` metaprograms a superclass and there's not a way to avoid the warning: |
|
# https://github.com/lsegal/yard/issues/1533 |
|
# https://github.com/lsegal/yard/issues/1477#issuecomment-1399339983 |
|
"Undocumentable superclass" => |
|
# https://rubular.com/r/lecYmbp981T4LY |
|
/^\[warn\]: in YARD::Handlers::Ruby::ClassHandler: Undocumentable superclass \(class was added without superclass\)\n\s+in file[^\n]+\n\n\s+\d+:[^\n]*?(Data\.define|Struct\.new|Support::Config\.define)[^\n]*\n\n/m, |
|
|
|
# We sometimes include/extend/prepend a mixin that is a dynamic module |
|
# (e.g. `include Mixins::HasReadableToSAndInspect.new`) and YARD isn't able to understand this. |
|
# That's fine, and we don't want a warning for this. |
|
"Undocumentable mixin" => |
|
# https://rubular.com/r/o0Daj0rKgNLes0 |
|
/^\[warn\]: in YARD::Handlers::Ruby::(Extend|Mixin)Handler: Undocumentable mixin: YARD::Parser::UndocumentableError[^\n]*\n\s+in file[^\n]+\n\n\s+\d+: (include|extend|prepend) [A-Za-z:]+\.new[^\n]*\n\n/m |
|
ignored_warnings_output = YARD_WARNINGS_TO_IGNORE.filter_map do |warning, regex| |
|
if (count = doc_output.scan(regex).count) > 0 |
|
"Ignored #{count} #{warning} warning(s)." |
|
end |
|
end.join("\n") |
This looks way better, though.
https://mensfeld.pl/2025/11/yard-lint-ruby-documentation-linter/
https://github.com/mensfeld/yard-lint
We already attempt to lint some of this in a janky way:
elasticgraph/config/site/Rakefile
Lines 70 to 103 in 8820a5a
elasticgraph/config/site/Rakefile
Lines 565 to 581 in 8820a5a
elasticgraph/config/site/Rakefile
Lines 606 to 610 in 8820a5a
This looks way better, though.