mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
Merge pull request #379 from crystal-ameba/several-tweaks-and-refactors
Several tweaks and refactors
This commit is contained in:
commit
e1f5c81804
20 changed files with 113 additions and 98 deletions
|
@ -62,7 +62,7 @@ module Ameba::AST
|
|||
# Branch.of(assign_node, def_node)
|
||||
# ```
|
||||
def self.of(node : Crystal::ASTNode, parent_node : Crystal::ASTNode)
|
||||
BranchVisitor.new(node).tap(&.accept parent_node).branch
|
||||
BranchVisitor.new(node).tap(&.accept(parent_node)).branch
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
|
@ -84,6 +84,7 @@ module Ameba::AST
|
|||
|
||||
branches.each do |branch_node|
|
||||
break if branch # branch found
|
||||
|
||||
@current_branch = branch_node if branch_node && !branch_node.nop?
|
||||
branch_node.try &.accept(self)
|
||||
end
|
||||
|
@ -108,19 +109,11 @@ module Ameba::AST
|
|||
true
|
||||
end
|
||||
|
||||
def visit(node : Crystal::If)
|
||||
def visit(node : Crystal::If | Crystal::Unless)
|
||||
on_branchable_start node, node.cond, node.then, node.else
|
||||
end
|
||||
|
||||
def end_visit(node : Crystal::If)
|
||||
on_branchable_end node
|
||||
end
|
||||
|
||||
def visit(node : Crystal::Unless)
|
||||
on_branchable_start node, node.cond, node.then, node.else
|
||||
end
|
||||
|
||||
def end_visit(node : Crystal::Unless)
|
||||
def end_visit(node : Crystal::If | Crystal::Unless)
|
||||
on_branchable_end node
|
||||
end
|
||||
|
||||
|
@ -140,19 +133,11 @@ module Ameba::AST
|
|||
on_branchable_end node
|
||||
end
|
||||
|
||||
def visit(node : Crystal::While)
|
||||
def visit(node : Crystal::While | Crystal::Until)
|
||||
on_branchable_start node, node.cond, node.body
|
||||
end
|
||||
|
||||
def end_visit(node : Crystal::While)
|
||||
on_branchable_end node
|
||||
end
|
||||
|
||||
def visit(node : Crystal::Until)
|
||||
on_branchable_start node, node.cond, node.body
|
||||
end
|
||||
|
||||
def end_visit(node : Crystal::Until)
|
||||
def end_visit(node : Crystal::While | Crystal::Until)
|
||||
on_branchable_end node
|
||||
end
|
||||
|
||||
|
|
|
@ -15,14 +15,15 @@ module Ameba::AST
|
|||
class Branchable
|
||||
include Util
|
||||
|
||||
getter branches = [] of Crystal::ASTNode
|
||||
|
||||
# The actual Crystal node.
|
||||
getter node : Crystal::ASTNode
|
||||
|
||||
# Parent branchable (if any)
|
||||
getter parent : Branchable?
|
||||
|
||||
# Array of branches
|
||||
getter branches = [] of Crystal::ASTNode
|
||||
|
||||
# The actual Crystal node
|
||||
getter node : Crystal::ASTNode
|
||||
|
||||
delegate to_s, to: @node
|
||||
delegate location, to: @node
|
||||
delegate end_location, to: @node
|
||||
|
|
|
@ -53,8 +53,11 @@ module Ameba::AST
|
|||
case current_node = node
|
||||
when Crystal::Expressions
|
||||
control_flow_found = false
|
||||
|
||||
current_node.expressions.each do |exp|
|
||||
unreachable_nodes << exp if control_flow_found
|
||||
if control_flow_found
|
||||
unreachable_nodes << exp
|
||||
end
|
||||
control_flow_found ||= !loop?(exp) && flow_expression?(exp, in_loop?)
|
||||
end
|
||||
when Crystal::BinaryOp
|
||||
|
|
|
@ -46,7 +46,7 @@ module Ameba::AST
|
|||
# scope = Scope.new(class_node, nil)
|
||||
# ```
|
||||
def initialize(@node, @outer_scope = nil)
|
||||
@outer_scope.try &.inner_scopes.<<(self)
|
||||
@outer_scope.try &.inner_scopes.<< self
|
||||
end
|
||||
|
||||
# Creates a new variable in the current scope.
|
||||
|
@ -97,7 +97,8 @@ module Ameba::AST
|
|||
# scope.find_variable("foo")
|
||||
# ```
|
||||
def find_variable(name : String)
|
||||
variables.find(&.name.==(name)) || outer_scope.try &.find_variable(name)
|
||||
variables.find(&.name.==(name)) ||
|
||||
outer_scope.try &.find_variable(name)
|
||||
end
|
||||
|
||||
# Creates a new assignment for the variable.
|
||||
|
@ -113,7 +114,8 @@ module Ameba::AST
|
|||
# Returns `true` if current scope represents a block (or proc),
|
||||
# `false` otherwise.
|
||||
def block?
|
||||
node.is_a?(Crystal::Block) || node.is_a?(Crystal::ProcLiteral)
|
||||
node.is_a?(Crystal::Block) ||
|
||||
node.is_a?(Crystal::ProcLiteral)
|
||||
end
|
||||
|
||||
# Returns `true` if current scope represents a spawn block, e. g.
|
||||
|
@ -129,7 +131,9 @@ module Ameba::AST
|
|||
|
||||
# Returns `true` if current scope sits inside a macro.
|
||||
def in_macro?
|
||||
(node.is_a?(Crystal::Macro) || node.is_a?(Crystal::MacroFor)) ||
|
||||
(node.is_a?(Crystal::Macro) ||
|
||||
node.is_a?(Crystal::MacroIf) ||
|
||||
node.is_a?(Crystal::MacroFor)) ||
|
||||
!!outer_scope.try(&.in_macro?)
|
||||
end
|
||||
|
||||
|
@ -141,7 +145,8 @@ module Ameba::AST
|
|||
|
||||
# Returns `true` if type declaration variable is assigned in this scope.
|
||||
def assigns_type_dec?(name)
|
||||
type_dec_variables.any?(&.name.== name) || !!outer_scope.try(&.assigns_type_dec?(name))
|
||||
type_dec_variables.any?(&.name.== name) ||
|
||||
!!outer_scope.try(&.assigns_type_dec?(name))
|
||||
end
|
||||
|
||||
# Returns `true` if and only if current scope represents some
|
||||
|
@ -149,6 +154,7 @@ module Ameba::AST
|
|||
def type_definition?
|
||||
node.is_a?(Crystal::ClassDef) ||
|
||||
node.is_a?(Crystal::ModuleDef) ||
|
||||
node.is_a?(Crystal::EnumDef) ||
|
||||
node.is_a?(Crystal::LibDef) ||
|
||||
node.is_a?(Crystal::FunDef) ||
|
||||
node.is_a?(Crystal::TypeDef) ||
|
||||
|
@ -159,8 +165,8 @@ module Ameba::AST
|
|||
# `false` otherwise.
|
||||
def references?(variable : Variable, check_inner_scopes = true)
|
||||
variable.references.any? do |reference|
|
||||
return true if reference.scope == self
|
||||
check_inner_scopes && inner_scopes.any?(&.references?(variable))
|
||||
(reference.scope == self) ||
|
||||
(check_inner_scopes && inner_scopes.any?(&.references?(variable)))
|
||||
end || variable.used_in_macro?
|
||||
end
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ module Ameba::AST
|
|||
private class MacroReferenceFinder < Crystal::Visitor
|
||||
property? references = false
|
||||
|
||||
def initialize(node, @reference : String = reference)
|
||||
def initialize(node, @reference : String)
|
||||
node.accept self
|
||||
end
|
||||
|
||||
|
@ -179,10 +179,6 @@ module Ameba::AST
|
|||
val.to_s.includes?(@reference)
|
||||
end
|
||||
|
||||
def visit(node : Crystal::ASTNode)
|
||||
true
|
||||
end
|
||||
|
||||
def visit(node : Crystal::MacroLiteral)
|
||||
!(@references ||= includes_reference?(node.value))
|
||||
end
|
||||
|
@ -201,14 +197,20 @@ module Ameba::AST
|
|||
includes_reference?(node.then) ||
|
||||
includes_reference?(node.else))
|
||||
end
|
||||
|
||||
def visit(node : Crystal::ASTNode)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
private def update_assign_reference!
|
||||
if @assign_before_reference.nil? &&
|
||||
references.size <= assignments.size &&
|
||||
assignments.none?(&.op_assign?)
|
||||
@assign_before_reference = assignments.find { |ass| !ass.in_branch? }.try &.node
|
||||
end
|
||||
return if @assign_before_reference
|
||||
return if references.size > assignments.size
|
||||
return if assignments.any?(&.op_assign?)
|
||||
|
||||
@assign_before_reference = assignments.find { |ass|
|
||||
!ass.in_branch?
|
||||
}.try &.node
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,11 +8,6 @@ module Ameba::AST
|
|||
|
||||
@loop_stack = [] of Crystal::ASTNode
|
||||
|
||||
# Creates a new flow expression visitor.
|
||||
def initialize(@rule, @source)
|
||||
@source.ast.accept self
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
def visit(node)
|
||||
if flow_expression?(node, in_loop?)
|
||||
|
@ -22,12 +17,7 @@ module Ameba::AST
|
|||
end
|
||||
|
||||
# :nodoc:
|
||||
def visit(node : Crystal::While)
|
||||
on_loop_started(node)
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
def visit(node : Crystal::Until)
|
||||
def visit(node : Crystal::While | Crystal::Until)
|
||||
on_loop_started(node)
|
||||
end
|
||||
|
||||
|
@ -37,12 +27,7 @@ module Ameba::AST
|
|||
end
|
||||
|
||||
# :nodoc:
|
||||
def end_visit(node : Crystal::While)
|
||||
on_loop_ended(node)
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
def end_visit(node : Crystal::Until)
|
||||
def end_visit(node : Crystal::While | Crystal::Until)
|
||||
on_loop_ended(node)
|
||||
end
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ module Ameba::AST
|
|||
ProcLiteral,
|
||||
Block,
|
||||
Macro,
|
||||
MacroIf,
|
||||
MacroFor,
|
||||
}
|
||||
|
||||
|
@ -25,21 +26,25 @@ module Ameba::AST
|
|||
@scope_queue = [] of Scope
|
||||
@current_scope : Scope
|
||||
@current_assign : Crystal::ASTNode?
|
||||
@visibility_modifier : Crystal::Visibility?
|
||||
@current_visibility : Crystal::Visibility?
|
||||
@skip : Array(Crystal::ASTNode.class)?
|
||||
|
||||
def initialize(@rule, @source, skip = nil)
|
||||
@skip = skip.try &.map(&.as(Crystal::ASTNode.class))
|
||||
@current_scope = Scope.new(@source.ast) # top level scope
|
||||
@source.ast.accept self
|
||||
@scope_queue.each { |scope| @rule.test @source, scope.node, scope }
|
||||
@skip = skip.try &.map(&.as(Crystal::ASTNode.class))
|
||||
|
||||
super @rule, @source
|
||||
|
||||
@scope_queue.each do |scope|
|
||||
@rule.test @source, scope.node, scope
|
||||
end
|
||||
end
|
||||
|
||||
private def on_scope_enter(node)
|
||||
return if skip?(node)
|
||||
|
||||
scope = Scope.new(node, @current_scope)
|
||||
scope.visibility = @visibility_modifier
|
||||
scope.visibility = @current_visibility
|
||||
|
||||
@current_scope = scope
|
||||
end
|
||||
|
@ -47,11 +52,12 @@ module Ameba::AST
|
|||
private def on_scope_end(node)
|
||||
@scope_queue << @current_scope
|
||||
|
||||
@visibility_modifier = nil
|
||||
@current_visibility = nil
|
||||
|
||||
# go up if this is not a top level scope
|
||||
return unless outer_scope = @current_scope.outer_scope
|
||||
@current_scope = outer_scope
|
||||
if outer_scope = @current_scope.outer_scope
|
||||
@current_scope = outer_scope
|
||||
end
|
||||
end
|
||||
|
||||
private def on_assign_end(target, node)
|
||||
|
@ -73,7 +79,7 @@ module Ameba::AST
|
|||
|
||||
# :nodoc:
|
||||
def visit(node : Crystal::VisibilityModifier)
|
||||
@visibility_modifier = node.exp.visibility = node.modifier
|
||||
@current_visibility = node.exp.visibility = node.modifier
|
||||
true
|
||||
end
|
||||
|
||||
|
@ -96,6 +102,7 @@ module Ameba::AST
|
|||
def end_visit(node : Crystal::Assign | Crystal::OpAssign)
|
||||
on_assign_end(node.target, node)
|
||||
@current_assign = nil
|
||||
|
||||
on_scope_end(node) if @current_scope.eql?(node)
|
||||
end
|
||||
|
||||
|
@ -103,6 +110,7 @@ module Ameba::AST
|
|||
def end_visit(node : Crystal::MultiAssign)
|
||||
node.targets.each { |target| on_assign_end(target, node) }
|
||||
@current_assign = nil
|
||||
|
||||
on_scope_end(node) if @current_scope.eql?(node)
|
||||
end
|
||||
|
||||
|
@ -110,6 +118,7 @@ module Ameba::AST
|
|||
def end_visit(node : Crystal::UninitializedVar)
|
||||
on_assign_end(node.var, node)
|
||||
@current_assign = nil
|
||||
|
||||
on_scope_end(node) if @current_scope.eql?(node)
|
||||
end
|
||||
|
||||
|
@ -119,7 +128,8 @@ module Ameba::AST
|
|||
|
||||
@current_scope.add_variable(var)
|
||||
@current_scope.add_type_dec_variable(node)
|
||||
@current_assign = node.value unless node.value.nil?
|
||||
|
||||
@current_assign = node.value if node.value
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
|
@ -128,6 +138,7 @@ module Ameba::AST
|
|||
|
||||
on_assign_end(var, node)
|
||||
@current_assign = nil
|
||||
|
||||
on_scope_end(node) if @current_scope.eql?(node)
|
||||
end
|
||||
|
||||
|
@ -165,7 +176,9 @@ module Ameba::AST
|
|||
if node.name.in?(SPECIAL_NODE_NAMES) && node.args.empty?
|
||||
@current_scope.arguments.each do |arg|
|
||||
variable = arg.variable
|
||||
variable.reference(variable.node, @current_scope).explicit = false
|
||||
|
||||
ref = variable.reference(variable.node, @current_scope)
|
||||
ref.explicit = false
|
||||
end
|
||||
end
|
||||
true
|
||||
|
|
|
@ -246,7 +246,7 @@ class Ameba::Config
|
|||
{% if block.body.is_a? Assign %}
|
||||
{% definitions << {var: block.body.target, value: block.body.value} %}
|
||||
{% elsif block.body.is_a? Call %}
|
||||
{% definitions << {var: block.body.name, value: block.body.args.first} %}
|
||||
{% definitions << {var: block.body.name, value: block.body.args.first} %}
|
||||
{% elsif block.body.is_a? TypeDeclaration %}
|
||||
{% definitions << {var: block.body.var, value: block.body.value, type: block.body.type} %}
|
||||
{% elsif block.body.is_a? Expressions %}
|
||||
|
@ -274,7 +274,7 @@ class Ameba::Config
|
|||
{% converter = SeverityYamlConverter %}
|
||||
{% end %}
|
||||
|
||||
{% if type == nil %}
|
||||
{% unless type %}
|
||||
{% if value.is_a? BoolLiteral %}
|
||||
{% type = Bool %}
|
||||
{% elsif value.is_a? StringLiteral %}
|
||||
|
@ -284,23 +284,23 @@ class Ameba::Config
|
|||
{% type = Int32 %}
|
||||
{% elsif value.kind == :i64 %}
|
||||
{% type = Int64 %}
|
||||
{% elsif value.kind == :i128 %}
|
||||
{% type = Int128 %}
|
||||
{% elsif value.kind == :f32 %}
|
||||
{% type = Float32 %}
|
||||
{% elsif value.kind == :f64 %}
|
||||
{% type = Float64 %}
|
||||
{% end %}
|
||||
{% end %}
|
||||
|
||||
{% type = Nil if type == nil %}
|
||||
{% end %}
|
||||
|
||||
{% properties[name] = {key: key, default: value, type: type, converter: converter} %}
|
||||
|
||||
@[YAML::Field(key: {{ key }}, converter: {{ converter }}, type: {{ type }})]
|
||||
@[YAML::Field(key: {{ key }}, converter: {{ converter }})]
|
||||
{% if type == Bool %}
|
||||
property? {{ name }} : {{ type }} = {{ value }}
|
||||
property? {{ name }}{{ " : #{type}".id if type }} = {{ value }}
|
||||
{% else %}
|
||||
property {{ name }} : {{ type }} = {{ value }}
|
||||
property {{ name }}{{ " : #{type}".id if type }} = {{ value }}
|
||||
{% end %}
|
||||
{% end %}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ module Ameba::Rule::Lint
|
|||
class DebugCalls < Base
|
||||
properties do
|
||||
description "Disallows debug-related calls"
|
||||
method_names : Array(String) = %w(p p! pp pp!)
|
||||
method_names %w(p p! pp pp!)
|
||||
end
|
||||
|
||||
MSG = "Possibly forgotten debug-related `%s` call detected"
|
||||
|
|
|
@ -7,6 +7,12 @@ module Ameba::Rule::Lint
|
|||
# ```
|
||||
# Lint/Documentation:
|
||||
# Enabled: true
|
||||
# IgnoreClasses: false
|
||||
# IgnoreModules: true
|
||||
# IgnoreEnums: false
|
||||
# IgnoreDefs: true
|
||||
# IgnoreMacros: false
|
||||
# IgnoreMacroHooks: true
|
||||
# ```
|
||||
class Documentation < Base
|
||||
properties do
|
||||
|
@ -18,6 +24,7 @@ module Ameba::Rule::Lint
|
|||
ignore_enums false
|
||||
ignore_defs true
|
||||
ignore_macros false
|
||||
ignore_macro_hooks true
|
||||
end
|
||||
|
||||
MSG = "Missing documentation"
|
||||
|
@ -50,14 +57,21 @@ module Ameba::Rule::Lint
|
|||
end
|
||||
|
||||
def test(source, node : Crystal::Macro, scope : AST::Scope)
|
||||
node.name.in?(MACRO_HOOK_NAMES) ||
|
||||
ignore_macros? || check_missing_doc(source, node, scope)
|
||||
return if ignore_macro_hooks? && node.name.in?(MACRO_HOOK_NAMES)
|
||||
|
||||
ignore_macros? || check_missing_doc(source, node, scope)
|
||||
end
|
||||
|
||||
private def check_missing_doc(source, node, scope)
|
||||
visibility = scope.visibility
|
||||
# bail out if the node is not public,
|
||||
# i.e. `private def foo`
|
||||
return if !node.visibility.public?
|
||||
|
||||
return if visibility && !visibility.public?
|
||||
# bail out if the scope is not public,
|
||||
# i.e. `def bar` inside `private class Foo`
|
||||
return if (visibility = scope.visibility) && !visibility.public?
|
||||
|
||||
# bail out if the node has the documentation present
|
||||
return if node.doc.presence
|
||||
|
||||
issue_for(node, MSG)
|
||||
|
|
|
@ -42,23 +42,26 @@ module Ameba::Rule::Lint
|
|||
end
|
||||
|
||||
def test(source, node : Crystal::ProcLiteral, scope : AST::Scope)
|
||||
ignore_procs? || find_unused_arguments source, scope
|
||||
ignore_procs? || find_unused_arguments(source, scope)
|
||||
end
|
||||
|
||||
def test(source, node : Crystal::Block, scope : AST::Scope)
|
||||
ignore_blocks? || find_unused_arguments source, scope
|
||||
ignore_blocks? || find_unused_arguments(source, scope)
|
||||
end
|
||||
|
||||
def test(source, node : Crystal::Def, scope : AST::Scope)
|
||||
arguments = scope.arguments.dup
|
||||
|
||||
# `Lint/UnusedBlockArgument` rule covers this case explicitly
|
||||
if block_arg = node.block_arg
|
||||
scope.arguments.reject!(&.node.== block_arg)
|
||||
arguments.reject!(&.node.== block_arg)
|
||||
end
|
||||
ignore_defs? || find_unused_arguments source, scope
|
||||
|
||||
ignore_defs? || find_unused_arguments(source, scope, arguments)
|
||||
end
|
||||
|
||||
private def find_unused_arguments(source, scope)
|
||||
scope.arguments.each do |argument|
|
||||
private def find_unused_arguments(source, scope, arguments = scope.arguments)
|
||||
arguments.each do |argument|
|
||||
next if argument.anonymous? || argument.ignored?
|
||||
next if scope.references?(argument.variable)
|
||||
|
||||
|
|
|
@ -37,11 +37,11 @@ module Ameba::Rule::Lint
|
|||
|
||||
MSG = "Useless condition in when detected"
|
||||
|
||||
# TODO: condition.cond may be a complex ASTNode with
|
||||
# TODO: condition *cond* may be a complex ASTNode with
|
||||
# useless inner conditions. We might need to improve this
|
||||
# simple implementation in future.
|
||||
protected def check_node(source, when_node, cond)
|
||||
cond_s = cond.to_s
|
||||
return unless cond_s = cond.to_s.presence
|
||||
return if when_node.conds.none?(&.to_s.==(cond_s))
|
||||
|
||||
issue_for cond, MSG
|
||||
|
|
|
@ -29,7 +29,7 @@ module Ameba::Rule::Performance
|
|||
class AnyAfterFilter < Base
|
||||
properties do
|
||||
description "Identifies usage of `any?` calls that follow filters"
|
||||
filter_names : Array(String) = %w(select reject)
|
||||
filter_names %w(select reject)
|
||||
end
|
||||
|
||||
ANY_NAME = "any?"
|
||||
|
|
|
@ -45,7 +45,7 @@ module Ameba::Rule::Performance
|
|||
# All of those have bang method variants returning `self`
|
||||
# and are not modifying the receiver type (like `compact` does),
|
||||
# thus are safe to switch to the bang variant.
|
||||
call_names : Array(String) = %w(uniq sort sort_by shuffle reverse)
|
||||
call_names %w(uniq sort sort_by shuffle reverse)
|
||||
end
|
||||
|
||||
# All these methods are allocating a new object
|
||||
|
|
|
@ -28,7 +28,7 @@ module Ameba::Rule::Performance
|
|||
class FirstLastAfterFilter < Base
|
||||
properties do
|
||||
description "Identifies usage of `first/last/first?/last?` calls that follow filters"
|
||||
filter_names : Array(String) = %w(select)
|
||||
filter_names %w(select)
|
||||
end
|
||||
|
||||
CALL_NAMES = %w(first last first? last?)
|
||||
|
|
|
@ -35,7 +35,7 @@ module Ameba::Rule::Performance
|
|||
class SizeAfterFilter < Base
|
||||
properties do
|
||||
description "Identifies usage of `size` calls that follow filter"
|
||||
filter_names : Array(String) = %w(select reject)
|
||||
filter_names %w(select reject)
|
||||
end
|
||||
|
||||
SIZE_NAME = "size"
|
||||
|
|
|
@ -63,7 +63,9 @@ module Ameba::Rule::Style
|
|||
"code inside a conditional expression."
|
||||
|
||||
def test(source)
|
||||
AST::NodeVisitor.new self, source, skip: [Crystal::Assign]
|
||||
AST::NodeVisitor.new self, source, skip: [
|
||||
Crystal::Assign,
|
||||
]
|
||||
end
|
||||
|
||||
def test(source, node : Crystal::Def)
|
||||
|
@ -118,6 +120,7 @@ module Ameba::Rule::Style
|
|||
|
||||
issue_for keyword_loc, keyword_end_loc, MSG % example do |corrector|
|
||||
replacement = "#{scope_exiting_keyword} #{conditional_keyword}"
|
||||
|
||||
corrector.replace(keyword_loc, keyword_end_loc, replacement)
|
||||
corrector.remove(end_loc, end_end_loc)
|
||||
end
|
||||
|
|
|
@ -41,7 +41,7 @@ module Ameba::Rule::Style
|
|||
class IsAFilter < Base
|
||||
properties do
|
||||
description "Identifies usage of `is_a?/nil?` calls within filters"
|
||||
filter_names : Array(String) = %w(select reject any? all? none? one?)
|
||||
filter_names %w(select reject any? all? none? one?)
|
||||
end
|
||||
|
||||
MSG = "Use `%s` instead of `%s`"
|
||||
|
|
Loading…
Reference in a new issue