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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions