Implement `Documentation` rule on top of the `ScopeVisitor`

This commit is contained in:
Sijawusz Pur Rahnama 2023-06-08 02:04:06 +02:00
parent 4c740f394a
commit 4d8346509e
2 changed files with 31 additions and 45 deletions

View File

@ -4,15 +4,21 @@ module Ameba::Rule::Lint
subject = Documentation.new
.tap(&.ignore_classes = false)
.tap(&.ignore_modules = false)
.tap(&.ignore_enums = false)
.tap(&.ignore_defs = false)
.tap(&.ignore_macros = false)
describe Documentation do
it "passes for undocumented private types" do
expect_no_issues subject, <<-CRYSTAL
private class Foo
def foo
end
end
private module Bar
def bar
end
end
private enum Baz
@ -30,10 +36,16 @@ module Ameba::Rule::Lint
expect_no_issues subject, <<-CRYSTAL
# Foo
class Foo
# foo
def foo
end
end
# Bar
module Bar
# bar
def bar
end
end
# Baz

View File

@ -10,9 +10,10 @@ module Ameba::Rule::Lint
# ```
class Documentation < Base
properties do
enabled false
description "Enforces public types to be documented"
ignore_classes true
ignore_classes false
ignore_modules true
ignore_enums false
ignore_defs true
@ -21,7 +22,7 @@ module Ameba::Rule::Lint
MSG = "Missing documentation"
HOOK_NAMES = %w[
MACRO_HOOK_NAMES = %w[
inherited
included extended
method_missing method_added
@ -29,64 +30,37 @@ module Ameba::Rule::Lint
]
def test(source)
DocumentationVisitor.new self, source
AST::ScopeVisitor.new self, source
end
def test(source, node : Crystal::ClassDef)
ignore_classes? || check_missing_doc(source, node)
def test(source, node : Crystal::ClassDef, scope : AST::Scope)
ignore_classes? || check_missing_doc(source, node, scope)
end
def test(source, node : Crystal::ModuleDef)
ignore_modules? || check_missing_doc(source, node)
def test(source, node : Crystal::ModuleDef, scope : AST::Scope)
ignore_modules? || check_missing_doc(source, node, scope)
end
def test(source, node : Crystal::EnumDef)
ignore_enums? || check_missing_doc(source, node)
def test(source, node : Crystal::EnumDef, scope : AST::Scope)
ignore_enums? || check_missing_doc(source, node, scope)
end
def test(source, node : Crystal::Def)
ignore_defs? || check_missing_doc(source, node)
def test(source, node : Crystal::Def, scope : AST::Scope)
ignore_defs? || check_missing_doc(source, node, scope)
end
def test(source, node : Crystal::Macro)
return if node.name.in?(HOOK_NAMES)
ignore_macros? || check_missing_doc(source, node)
def test(source, node : Crystal::Macro, scope : AST::Scope)
node.name.in?(MACRO_HOOK_NAMES) ||
ignore_macros? || check_missing_doc(source, node, scope)
end
private def check_missing_doc(source, node)
return unless node.visibility.public?
private def check_missing_doc(source, node, scope)
visibility = scope.visibility
return if visibility && !visibility.public?
return if node.doc.presence
issue_for(node, MSG)
end
# :nodoc:
private class DocumentationVisitor < AST::BaseVisitor
NODES = {
ClassDef,
ModuleDef,
EnumDef,
Def,
Macro,
}
@visibility : Crystal::Visibility = :public
def visit(node : Crystal::VisibilityModifier)
@visibility = node.modifier
true
end
{% for name in NODES %}
def visit(node : Crystal::{{ name }})
node.visibility = @visibility
@visibility = :public
@rule.test @source, node
true
end
{% end %}
end
end
end