Skip to content

Look into adopting yard-lint #913

@myronmarston

Description

@myronmarston

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions