Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it easier to add issues for nodes with name location preference #422

Merged
merged 4 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/ameba/reportable.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
require "./ast/util"

module Ameba
# Represents a module used to report issues.
module Reportable
include AST::Util

# List of reported issues.
getter issues = [] of Issue

Expand Down Expand Up @@ -30,13 +34,19 @@ module Ameba
end

# Adds a new issue for Crystal AST *node*.
def add_issue(rule, node : Crystal::ASTNode, message, status : Issue::Status? = nil, block : (Source::Corrector ->)? = nil) : Issue
add_issue rule, node.location, node.end_location, message, status, block
def add_issue(rule, node : Crystal::ASTNode, message, status : Issue::Status? = nil, block : (Source::Corrector ->)? = nil, *, prefer_name_location = false) : Issue
location = name_location(node) if prefer_name_location
location ||= node.location

end_location = name_end_location(node) if prefer_name_location
end_location ||= node.end_location

add_issue rule, location, end_location, message, status, block
end

# :ditto:
def add_issue(rule, node : Crystal::ASTNode, message, status : Issue::Status? = nil, &block : Source::Corrector ->) : Issue
add_issue rule, node, message, status, block
def add_issue(rule, node : Crystal::ASTNode, message, status : Issue::Status? = nil, *, prefer_name_location = false, &block : Source::Corrector ->) : Issue
add_issue rule, node, message, status, block, prefer_name_location: prefer_name_location
end

# Adds a new issue for Crystal *token*.
Expand Down
7 changes: 1 addition & 6 deletions src/ameba/rule/lint/missing_block_argument.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ module Ameba::Rule::Lint
# Enabled: true
# ```
class MissingBlockArgument < Base
include AST::Util

properties do
description "Disallows yielding method definitions without block argument"
end
Expand All @@ -36,10 +34,7 @@ module Ameba::Rule::Lint
def test(source, node : Crystal::Def, scope : AST::Scope)
return if !scope.yields? || node.block_arg

return unless location = node.name_location
end_location = name_end_location(node)

issue_for location, end_location, MSG
issue_for node, MSG, prefer_name_location: true
veelenga marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
7 changes: 1 addition & 6 deletions src/ameba/rule/lint/not_nil.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ module Ameba::Rule::Lint
# Enabled: true
# ```
class NotNil < Base
include AST::Util

properties do
description "Identifies usage of `not_nil!` calls"
end
Expand All @@ -42,10 +40,7 @@ module Ameba::Rule::Lint
return unless node.name == "not_nil!"
return unless node.obj && node.args.empty?

return unless name_location = node.name_location
return unless end_location = name_end_location(node)

issue_for name_location, end_location, MSG
issue_for node, MSG, prefer_name_location: true
end
end
end
2 changes: 1 addition & 1 deletion src/ameba/rule/lint/not_nil_after_no_bang.cr
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ module Ameba::Rule::Lint
return unless (obj = node.obj).is_a?(Crystal::Call)
return unless obj.name.in?(obj.block ? BLOCK_CALL_NAMES : CALL_NAMES)

return unless name_location = obj.name_location
return unless name_location = name_location(obj)
return unless name_location_end = name_end_location(obj)
return unless end_location = name_end_location(node)

Expand Down
2 changes: 1 addition & 1 deletion src/ameba/rule/lint/redundant_string_coercion.cr
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ module Ameba::Rule::Lint

def test(source, node : Crystal::StringInterpolation)
string_coercion_nodes(node).each do |expr|
issue_for expr.name_location, expr.end_location, MSG
issue_for name_location(expr), expr.end_location, MSG
end
end

Expand Down
2 changes: 1 addition & 1 deletion src/ameba/rule/lint/redundant_with_index.cr
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ module Ameba::Rule::Lint
end

private def report(source, node, msg)
issue_for node.name_location, node.name_end_location, msg
issue_for node, msg, prefer_name_location: true
end
end
end
2 changes: 1 addition & 1 deletion src/ameba/rule/lint/redundant_with_object.cr
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module Ameba::Rule::Lint
!(block = node.block) ||
with_index_arg?(block)

issue_for node.name_location, node.name_end_location, MSG
issue_for node, MSG, prefer_name_location: true
end

private def with_index_arg?(block : Crystal::Block)
Expand Down
7 changes: 1 addition & 6 deletions src/ameba/rule/metrics/cyclomatic_complexity.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ module Ameba::Rule::Metrics
# MaxComplexity: 10
# ```
class CyclomaticComplexity < Base
include AST::Util

properties do
description "Disallows methods with a cyclomatic complexity higher than `MaxComplexity`"
max_complexity 10
Expand All @@ -22,10 +20,7 @@ module Ameba::Rule::Metrics
complexity = AST::CountingVisitor.new(node).count
return unless complexity > max_complexity

return unless location = node.name_location
end_location = name_end_location(node)

issue_for location, end_location, MSG % {complexity, max_complexity}
issue_for node, MSG % {complexity, max_complexity}, prefer_name_location: true
end
end
end
17 changes: 2 additions & 15 deletions src/ameba/rule/naming/accessor_method_name.cr
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ module Ameba::Rule::Naming
# Enabled: true
# ```
class AccessorMethodName < Base
include AST::Util

properties do
description "Makes sure that accessor methods are named properly"
end
Expand Down Expand Up @@ -66,24 +64,13 @@ module Ameba::Rule::Naming
end

private def check_issue(source, node : Crystal::Def)
location = name_location(node)
end_location = name_end_location(node)

case node.name
when /^get_([a-z]\w*)$/
return unless node.args.empty?
if location && end_location
issue_for location, end_location, MSG % {$1, node.name}
else
issue_for node, MSG % {$1, node.name}
end
issue_for node, MSG % {$1, node.name}, prefer_name_location: true
when /^set_([a-z]\w*)$/
return unless node.args.size == 1
if location && end_location
issue_for location, end_location, MSG % {"#{$1}=", node.name}
else
issue_for node, MSG % {"#{$1}=", node.name}
end
issue_for node, MSG % {"#{$1}=", node.name}, prefer_name_location: true
end
end
end
Expand Down
23 changes: 5 additions & 18 deletions src/ameba/rule/naming/ascii_identifiers.cr
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ module Ameba::Rule::Naming
# Enabled: true
# ```
class AsciiIdentifiers < Base
include AST::Util

properties do
description "Disallows non-ascii characters in identifiers"
end
Expand All @@ -43,38 +41,27 @@ module Ameba::Rule::Naming
end

def test(source, node : Crystal::Def)
check_issue_with_location(source, node)
check_issue(source, node, prefer_name_location: true)

node.args.each do |arg|
check_issue_with_location(source, arg)
check_issue(source, arg, prefer_name_location: true)
end
end

def test(source, node : Crystal::ClassVar | Crystal::InstanceVar | Crystal::Var | Crystal::Alias)
check_issue_with_location(source, node)
check_issue(source, node, prefer_name_location: true)
end

def test(source, node : Crystal::ClassDef | Crystal::ModuleDef | Crystal::EnumDef | Crystal::LibDef)
check_issue(source, node.name, node.name)
end

private def check_issue_with_location(source, node)
location = name_location(node)
end_location = name_end_location(node)

if location && end_location
check_issue(source, location, end_location, node.name)
else
check_issue(source, node, node.name)
end
end

private def check_issue(source, location, end_location, name)
issue_for location, end_location, MSG unless name.to_s.ascii_only?
end

private def check_issue(source, node, name)
issue_for node, MSG unless name.to_s.ascii_only?
private def check_issue(source, node, name = node.name, *, prefer_name_location = false)
issue_for node, MSG, prefer_name_location: prefer_name_location unless name.to_s.ascii_only?
end
end
end
4 changes: 2 additions & 2 deletions src/ameba/rule/naming/block_parameter_name.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ module Ameba::Rule::Naming
# Enabled: true
# MinNameLength: 3
# AllowNamesEndingInNumbers: true
# AllowedNames: [_, e, i, j, k, v, x, y, ex, io, ws, tx, id, k1, k2, v1, v2]
# AllowedNames: [_, e, i, j, k, v, x, y, ex, io, ws, op, tx, id, ip, k1, k2, v1, v2]
# ForbiddenNames: []
# ```
class BlockParameterName < Base
properties do
description "Disallows non-descriptive block parameter names"
min_name_length 3
allow_names_ending_in_numbers true
allowed_names %w[_ e i j k v x y ex io ws tx id k1 k2 v1 v2]
allowed_names %w[_ e i j k v x y ex io ws op tx id ip k1 k2 v1 v2]
forbidden_names %w[]
end

Expand Down
7 changes: 1 addition & 6 deletions src/ameba/rule/naming/method_names.cr
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ module Ameba::Rule::Naming
# Enabled: true
# ```
class MethodNames < Base
include AST::Util

properties do
description "Enforces method names to be in underscored case"
end
Expand All @@ -49,10 +47,7 @@ module Ameba::Rule::Naming
def test(source, node : Crystal::Def)
return if (expected = node.name.underscore) == node.name

return unless location = name_location(node)
return unless end_location = name_end_location(node)

issue_for location, end_location, MSG % {expected, node.name}
issue_for node, MSG % {expected, node.name}, prefer_name_location: true
end
end
end
4 changes: 3 additions & 1 deletion src/ameba/rule/performance/any_after_filter.cr
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ module Ameba::Rule::Performance
# - reject
# ```
class AnyAfterFilter < Base
include AST::Util

properties do
description "Identifies usage of `any?` calls that follow filters"
filter_names %w(select reject)
Expand All @@ -39,7 +41,7 @@ module Ameba::Rule::Performance
return unless obj.is_a?(Crystal::Call) && obj.block && node.block.nil?
return unless obj.name.in?(filter_names)

issue_for obj.name_location, node.name_end_location, MSG % obj.name
issue_for name_location(obj), name_end_location(node), MSG % obj.name
end
end
end
7 changes: 1 addition & 6 deletions src/ameba/rule/performance/any_instead_of_empty.cr
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ module Ameba::Rule::Performance
# Enabled: true
# ```
class AnyInsteadOfEmpty < Base
include AST::Util

properties do
description "Identifies usage of arg-less `any?` calls"
end
Expand All @@ -41,10 +39,7 @@ module Ameba::Rule::Performance
return unless node.block.nil? && node.args.empty?
return unless node.obj

return unless name_location = node.name_location
return unless end_location = name_end_location(node)

issue_for name_location, end_location, MSG
issue_for node, MSG, prefer_name_location: true
end
end
end
11 changes: 6 additions & 5 deletions src/ameba/rule/performance/chained_call_with_no_bang.cr
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,12 @@ module Ameba::Rule::Performance
return unless node.name.in?(call_names)
return unless obj.name.in?(call_names) || obj.name.in?(ALLOCATING_METHOD_NAMES)

return unless location = node.name_location
return unless end_location = name_end_location(node)

issue_for location, end_location, MSG % {node.name, obj.name} do |corrector|
corrector.insert_after(end_location, '!')
if end_location = name_end_location(node)
issue_for node, MSG % {node.name, obj.name}, prefer_name_location: true do |corrector|
corrector.insert_after(end_location, '!')
end
else
issue_for node, MSG % {node.name, obj.name}, prefer_name_location: true
end
end
end
Expand Down
4 changes: 3 additions & 1 deletion src/ameba/rule/performance/compact_after_map.cr
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ module Ameba::Rule::Performance
# Enabled: true
# ```
class CompactAfterMap < Base
include AST::Util

properties do
description "Identifies usage of `compact` calls that follow `map`"
end
Expand All @@ -37,7 +39,7 @@ module Ameba::Rule::Performance
return unless obj.is_a?(Crystal::Call) && obj.block
return unless obj.name == "map"

issue_for obj.name_location, node.name_end_location, MSG
issue_for name_location(obj), name_end_location(node), MSG
end
end
end
2 changes: 1 addition & 1 deletion src/ameba/rule/performance/excessive_allocations.cr
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ module Ameba::Rule::Performance
return unless obj.args.empty? && obj.block.nil?
return unless method = call_names[obj.name]?

return unless name_location = obj.name_location
return unless name_location = name_location(obj)
return unless end_location = name_end_location(node)

msg = MSG % {method, obj.name}
Expand Down
4 changes: 3 additions & 1 deletion src/ameba/rule/performance/first_last_after_filter.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ module Ameba::Rule::Performance
# - select
# ```
class FirstLastAfterFilter < Base
include AST::Util

properties do
description "Identifies usage of `first/last/first?/last?` calls that follow filters"
filter_names %w(select)
Expand All @@ -47,7 +49,7 @@ module Ameba::Rule::Performance

message = node.name.includes?(CALL_NAMES.first) ? MSG : MSG_REVERSE

issue_for obj.name_location, node.name_end_location,
issue_for name_location(obj), name_end_location(node),
message % {obj.name, node.name}
end
end
Expand Down
4 changes: 3 additions & 1 deletion src/ameba/rule/performance/flatten_after_map.cr
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ module Ameba::Rule::Performance
# Enabled: true
# ```
class FlattenAfterMap < Base
include AST::Util

properties do
description "Identifies usage of `flatten` calls that follow `map`"
end
Expand All @@ -37,7 +39,7 @@ module Ameba::Rule::Performance
return unless obj.is_a?(Crystal::Call) && obj.block
return unless obj.name == "map"

issue_for obj.name_location, node.name_end_location, MSG
issue_for name_location(obj), name_end_location(node), MSG
end
end
end
4 changes: 3 additions & 1 deletion src/ameba/rule/performance/map_instead_of_block.cr
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ module Ameba::Rule::Performance
# Enabled: true
# ```
class MapInsteadOfBlock < Base
include AST::Util

properties do
description "Identifies usage of `sum/product` calls that follow `map`"
end
Expand All @@ -40,7 +42,7 @@ module Ameba::Rule::Performance
return unless obj.is_a?(Crystal::Call) && obj.block
return unless obj.name == MAP_NAME

issue_for obj.name_location, node.name_end_location,
issue_for name_location(obj), name_end_location(node),
MSG % {node.name, node.name}
end
end
Expand Down
Loading
Loading