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

View file

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