diff --git a/spec/ameba/ast/util_spec.cr b/spec/ameba/ast/util_spec.cr index 447b1fb36..6e559ab55 100644 --- a/spec/ameba/ast/util_spec.cr +++ b/spec/ameba/ast/util_spec.cr @@ -354,6 +354,39 @@ module Ameba::AST end end + describe "#nodoc?" do + it "returns true if a node has a single `:nodoc:` annotation" do + node = as_node <<-CRYSTAL, wants_doc: true + # :nodoc: + def foo; end + CRYSTAL + + subject.nodoc?(node).should be_true + end + + it "returns true if a node has a `:nodoc:` annotation in the first line" do + node = as_node <<-CRYSTAL, wants_doc: true + # :nodoc: + # + # foo + def foo; end + CRYSTAL + + subject.nodoc?(node).should be_true + end + + it "returns false if a node has a `:nodoc:` annotation in the middle" do + node = as_node <<-CRYSTAL, wants_doc: true + # foo + # :nodoc: + # bar + def foo; end + CRYSTAL + + subject.nodoc?(node).should be_false + end + end + describe "#control_exp_code" do it "returns the exp code of a control expression" do s = "return 1" diff --git a/spec/ameba/rule/typing/method_parameter_type_restriction_spec.cr b/spec/ameba/rule/typing/method_parameter_type_restriction_spec.cr index 070ea4c24..ec363796a 100644 --- a/spec/ameba/rule/typing/method_parameter_type_restriction_spec.cr +++ b/spec/ameba/rule/typing/method_parameter_type_restriction_spec.cr @@ -57,6 +57,13 @@ module Ameba::Rule::Typing CRYSTAL end + it "passes if a method has a `:nodoc:` annotation" do + expect_no_issues subject, <<-CRYSTAL + # :nodoc: + def foo(bar); end + CRYSTAL + end + it "fails if a public method parameter doesn't have a type restriction" do expect_issue subject, <<-CRYSTAL def hello(a) @@ -195,6 +202,20 @@ module Ameba::Rule::Typing CRYSTAL end end + + context "#nodoc_methods" do + rule = MethodParameterTypeRestriction.new + rule.nodoc_methods = true + + it "fails if a public method parameter doesn't have a type restriction" do + expect_issue rule, <<-CRYSTAL + # :nodoc: + def foo(bar) + # ^ error: Method parameter should have a type restriction + end + CRYSTAL + end + end end end end diff --git a/spec/ameba/rule/typing/method_return_type_restriction_spec.cr b/spec/ameba/rule/typing/method_return_type_restriction_spec.cr index 08cd8622f..b85c34bf9 100644 --- a/spec/ameba/rule/typing/method_return_type_restriction_spec.cr +++ b/spec/ameba/rule/typing/method_return_type_restriction_spec.cr @@ -32,6 +32,13 @@ module Ameba::Rule::Typing CRYSTAL end + it "passes if a method has a `:nodoc:` annotation" do + expect_no_issues subject, <<-CRYSTAL + # :nodoc: + def foo; end + CRYSTAL + end + it "fails if a public method doesn't have a return type restriction" do expect_issue subject, <<-CRYSTAL def hello @@ -119,6 +126,20 @@ module Ameba::Rule::Typing CRYSTAL end end + + context "#nodoc_methods" do + rule = MethodReturnTypeRestriction.new + rule.nodoc_methods = true + + it "fails if a public method doesn't have a return type restriction" do + expect_issue rule, <<-CRYSTAL + # :nodoc: + def foo + # ^^^^^ error: Method should have a return type restriction + end + CRYSTAL + end + end end end end diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index 5573db455..2e948660b 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -320,12 +320,14 @@ def with_presenter(klass, *args, deansify = true, **kwargs, &) yield presenter, output end -def as_node(source) - Crystal::Parser.new(source).parse +def as_node(source, *, wants_doc = false) + Crystal::Parser.new(source) + .tap(&.wants_doc = wants_doc) + .parse end -def as_nodes(source) - Ameba::TestNodeVisitor.new(as_node source) +def as_nodes(source, *, wants_doc = false) + Ameba::TestNodeVisitor.new(as_node(source, wants_doc: wants_doc)) end def trailing_whitespace diff --git a/src/ameba/ast/util.cr b/src/ameba/ast/util.cr index ce71fc9ca..065439fe6 100644 --- a/src/ameba/ast/util.cr +++ b/src/ameba/ast/util.cr @@ -194,6 +194,14 @@ module Ameba::AST::Util end end + # Returns `true` if node has a `:nodoc:` annotation as the first line. + def nodoc?(node) + return false unless node.responds_to?(:doc) + return false unless doc = node.doc.presence + + doc.lines.first?.try(&.strip) == ":nodoc:" + end + # Returns the exp code of a control expression. # Wraps implicit tuple literal with curly brackets (e.g. multi-return). def control_exp_code(node : Crystal::ControlExpression, code_lines) diff --git a/src/ameba/rule/typing/method_parameter_type_restriction.cr b/src/ameba/rule/typing/method_parameter_type_restriction.cr index f81f0f0a1..8be83c452 100644 --- a/src/ameba/rule/typing/method_parameter_type_restriction.cr +++ b/src/ameba/rule/typing/method_parameter_type_restriction.cr @@ -20,6 +20,9 @@ module Ameba::Rule::Typing # When the config options `PrivateMethods` and `ProtectedMethods` # are true, this rule is also applied to private and protected methods, respectively. # + # The `NodocMethods` configuration option controls whether this rule applies to + # methods with a `:nodoc:` directive. + # # The `BlockParameters` configuration option will extend this to block parameters, where these are invalid: # # ``` @@ -49,8 +52,11 @@ module Ameba::Rule::Typing # BlockParameters: false # PrivateMethods: false # ProtectedMethods: false + # NodocMethods: false # ``` class MethodParameterTypeRestriction < Base + include AST::Util + properties do since_version "1.7.0" description "Recommends that method parameters have type restrictions" @@ -59,6 +65,7 @@ module Ameba::Rule::Typing block_parameters false private_methods false protected_methods false + nodoc_methods false end MSG = "Method parameter should have a type restriction" @@ -80,7 +87,8 @@ module Ameba::Rule::Typing private def valid_visibility?(node : Crystal::ASTNode) : Bool (!private_methods? && node.visibility.private?) || - (!protected_methods? && node.visibility.protected?) + (!protected_methods? && node.visibility.protected?) || + (!nodoc_methods? && nodoc?(node)) end end end diff --git a/src/ameba/rule/typing/method_return_type_restriction.cr b/src/ameba/rule/typing/method_return_type_restriction.cr index 43d7b8bed..5f7eacffa 100644 --- a/src/ameba/rule/typing/method_return_type_restriction.cr +++ b/src/ameba/rule/typing/method_return_type_restriction.cr @@ -20,6 +20,9 @@ module Ameba::Rule::Typing # When the config options `PrivateMethods` and `ProtectedMethods` # are true, this rule is also applied to private and protected methods, respectively. # + # The `NodocMethods` configuration option controls whether this rule applies to + # methods with a `:nodoc:` directive. + # # YAML configuration example: # # ``` @@ -27,14 +30,18 @@ module Ameba::Rule::Typing # Enabled: true # PrivateMethods: false # ProtectedMethods: false + # NodocMethods: false # ``` class MethodReturnTypeRestriction < Base + include AST::Util + properties do since_version "1.7.0" description "Recommends that methods have a return type restriction" enabled false private_methods false protected_methods false + nodoc_methods false end MSG = "Method should have a return type restriction" @@ -46,7 +53,8 @@ module Ameba::Rule::Typing private def valid_return_type?(node : Crystal::ASTNode) : Bool !!node.return_type || (node.visibility.private? && !private_methods?) || - (node.visibility.protected? && !protected_methods?) + (node.visibility.protected? && !protected_methods?) || + (!nodoc_methods? && nodoc?(node)) end end end