mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
Merge pull request #312 from crystal-ameba/Sija/refactors-round-3
This commit is contained in:
commit
be65ba2a92
26 changed files with 114 additions and 127 deletions
|
@ -59,7 +59,7 @@ module Ameba
|
||||||
source = Source.new "a = 42", "source.cr"
|
source = Source.new "a = 42", "source.cr"
|
||||||
output = explanation(source)
|
output = explanation(source)
|
||||||
output.should contain "DETAILED DESCRIPTION"
|
output.should contain "DETAILED DESCRIPTION"
|
||||||
output.should contain "TO BE DONE..."
|
output.should contain "Rule extended description"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "writes nothing if location not found" do
|
it "writes nothing if location not found" do
|
||||||
|
|
|
@ -24,6 +24,7 @@ module Ameba
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Rule extended description
|
||||||
class ErrorRule < Rule::Base
|
class ErrorRule < Rule::Base
|
||||||
properties do
|
properties do
|
||||||
description "Always adds an error at 1:1"
|
description "Always adds an error at 1:1"
|
||||||
|
|
|
@ -175,8 +175,9 @@ module Ameba::AST
|
||||||
node.accept self
|
node.accept self
|
||||||
end
|
end
|
||||||
|
|
||||||
def references?(node : Crystal::Var)
|
# @[AlwaysInline]
|
||||||
@macro_literals.any?(&.value.includes?(node.name))
|
private def includes_reference?(val)
|
||||||
|
val.to_s.includes?(@reference)
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit(node : Crystal::ASTNode)
|
def visit(node : Crystal::ASTNode)
|
||||||
|
@ -184,23 +185,22 @@ module Ameba::AST
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit(node : Crystal::MacroLiteral)
|
def visit(node : Crystal::MacroLiteral)
|
||||||
!(self.references ||= node.value.includes?(@reference))
|
!(@references ||= includes_reference?(node.value))
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit(node : Crystal::MacroExpression)
|
def visit(node : Crystal::MacroExpression)
|
||||||
!(self.references ||= node.exp.to_s.includes?(@reference))
|
!(@references ||= includes_reference?(node.exp))
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit(node : Crystal::MacroFor)
|
def visit(node : Crystal::MacroFor)
|
||||||
exp, body = node.exp, node.body
|
!(@references ||= includes_reference?(node.exp) ||
|
||||||
!(self.references ||= exp.to_s.includes?(@reference) ||
|
includes_reference?(node.body))
|
||||||
body.to_s.includes?(@reference))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit(node : Crystal::MacroIf)
|
def visit(node : Crystal::MacroIf)
|
||||||
!(self.references ||= node.cond.to_s.includes?(@reference) ||
|
!(@references ||= includes_reference?(node.cond) ||
|
||||||
node.then.to_s.includes?(@reference) ||
|
includes_reference?(node.then) ||
|
||||||
node.else.to_s.includes?(@reference))
|
includes_reference?(node.else))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -12,15 +12,17 @@ module Ameba::AST
|
||||||
# :nodoc:
|
# :nodoc:
|
||||||
def visit(node : Crystal::Require)
|
def visit(node : Crystal::Require)
|
||||||
require_nodes << node
|
require_nodes << node
|
||||||
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# If a top level node is Crystal::Expressions traverse the children.
|
# If a top level node is `Crystal::Expressions`,
|
||||||
|
# then always traverse the children.
|
||||||
def visit(node : Crystal::Expressions)
|
def visit(node : Crystal::Expressions)
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# A general visit method for rest of the nodes.
|
# A general visit method for rest of the nodes.
|
||||||
# Returns false meaning all child nodes will not be traversed.
|
# Returns `false`, meaning all child nodes will not be traversed.
|
||||||
def visit(node : Crystal::ASTNode)
|
def visit(node : Crystal::ASTNode)
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
|
@ -111,7 +111,6 @@ class Ameba::Config
|
||||||
# config.formatter = custom_formatter
|
# config.formatter = custom_formatter
|
||||||
# config.formatter
|
# config.formatter
|
||||||
# ```
|
# ```
|
||||||
#
|
|
||||||
property formatter : Formatter::BaseFormatter do
|
property formatter : Formatter::BaseFormatter do
|
||||||
Formatter::DotFormatter.new
|
Formatter::DotFormatter.new
|
||||||
end
|
end
|
||||||
|
@ -249,17 +248,17 @@ class Ameba::Config
|
||||||
{% end %}
|
{% end %}
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
{% if properties["enabled".id] == nil %}
|
{% unless properties["enabled".id] %}
|
||||||
@[YAML::Field(key: "Enabled")]
|
@[YAML::Field(key: "Enabled")]
|
||||||
property? enabled = true
|
property? enabled = true
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
{% if properties["severity".id] == nil %}
|
{% unless properties["severity".id] %}
|
||||||
@[YAML::Field(key: "Severity", converter: Ameba::SeverityYamlConverter)]
|
@[YAML::Field(key: "Severity", converter: Ameba::SeverityYamlConverter)]
|
||||||
property severity = {{ @type }}.default_severity
|
property severity = {{ @type }}.default_severity
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
{% if properties["excluded".id] == nil %}
|
{% unless properties["excluded".id] %}
|
||||||
@[YAML::Field(key: "Excluded")]
|
@[YAML::Field(key: "Excluded")]
|
||||||
property excluded : Array(String)?
|
property excluded : Array(String)?
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
|
@ -5,11 +5,11 @@ module Ameba::Formatter
|
||||||
output << "Disabled rules using inline directives:\n\n"
|
output << "Disabled rules using inline directives:\n\n"
|
||||||
|
|
||||||
sources.each do |source|
|
sources.each do |source|
|
||||||
source.issues.select(&.disabled?).each do |e|
|
source.issues.select(&.disabled?).each do |issue|
|
||||||
next unless loc = e.location
|
next unless loc = issue.location
|
||||||
|
|
||||||
output << "#{source.path}:#{loc.line_number}".colorize(:cyan)
|
output << "#{source.path}:#{loc.line_number}".colorize(:cyan)
|
||||||
output << " #{e.rule.name}\n"
|
output << " #{issue.rule.name}\n"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,8 +4,7 @@ module Ameba::Formatter
|
||||||
# A formatter that shows the detailed explanation of the issue at
|
# A formatter that shows the detailed explanation of the issue at
|
||||||
# a specific location.
|
# a specific location.
|
||||||
class ExplainFormatter
|
class ExplainFormatter
|
||||||
HEADING = "## "
|
HEADING_MARKER = "## "
|
||||||
PREFIX = " "
|
|
||||||
|
|
||||||
include Util
|
include Util
|
||||||
|
|
||||||
|
@ -18,13 +17,18 @@ module Ameba::Formatter
|
||||||
# Second argument is *location* which indicates the location to explain.
|
# Second argument is *location* which indicates the location to explain.
|
||||||
#
|
#
|
||||||
# ```
|
# ```
|
||||||
# ExplainFormatter.new output,
|
# ExplainFormatter.new output, {
|
||||||
# file: path,
|
# file: path,
|
||||||
# line: line_number,
|
# line: line_number,
|
||||||
# column: column_number
|
# column: column_number,
|
||||||
|
# }
|
||||||
# ```
|
# ```
|
||||||
def initialize(@output, location)
|
def initialize(@output, location)
|
||||||
@location = Crystal::Location.new(location[:file], location[:line], location[:column])
|
@location = Crystal::Location.new(
|
||||||
|
location[:file],
|
||||||
|
location[:line],
|
||||||
|
location[:column]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reports the explanations at the *@location*.
|
# Reports the explanations at the *@location*.
|
||||||
|
@ -39,42 +43,59 @@ module Ameba::Formatter
|
||||||
end
|
end
|
||||||
|
|
||||||
private def explain(source, issue)
|
private def explain(source, issue)
|
||||||
rule = issue.rule
|
|
||||||
|
|
||||||
return unless location = issue.location
|
return unless location = issue.location
|
||||||
|
|
||||||
output_title "ISSUE INFO"
|
output << '\n'
|
||||||
|
output_title "Issue info"
|
||||||
output_paragraph [
|
output_paragraph [
|
||||||
issue.message.colorize(:red).to_s,
|
issue.message.colorize(:red),
|
||||||
location.to_s.colorize(:cyan).to_s,
|
location.to_s.colorize(:cyan),
|
||||||
]
|
]
|
||||||
|
|
||||||
if affected_code = affected_code(issue, context_lines: 3)
|
if affected_code = affected_code(issue, context_lines: 3)
|
||||||
output_title "AFFECTED CODE"
|
output_title "Affected code"
|
||||||
output_paragraph affected_code
|
output_paragraph affected_code
|
||||||
end
|
end
|
||||||
|
|
||||||
|
rule = issue.rule
|
||||||
|
|
||||||
|
output_title "Rule info"
|
||||||
|
output_paragraph "%s of a %s severity" % {
|
||||||
|
rule.name.colorize(:magenta),
|
||||||
|
rule.severity.to_s.colorize(rule.severity.color),
|
||||||
|
}
|
||||||
|
|
||||||
if rule.responds_to?(:description)
|
if rule.responds_to?(:description)
|
||||||
output_title "RULE INFO"
|
output_paragraph rule.description
|
||||||
output_paragraph [rule.severity.to_s, rule.name, rule.description]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
output_title "DETAILED DESCRIPTION"
|
rule_doc = colorize_code_fences(rule.class.parsed_doc)
|
||||||
output_paragraph(rule.class.parsed_doc || "TO BE DONE...")
|
return unless rule_doc
|
||||||
|
|
||||||
|
output_title "Detailed description"
|
||||||
|
output_paragraph rule_doc
|
||||||
|
end
|
||||||
|
|
||||||
|
private def colorize_code_fences(string)
|
||||||
|
return unless string
|
||||||
|
string
|
||||||
|
.gsub(/```(.+?)```/m, &.colorize(:dark_gray))
|
||||||
|
.gsub(/`(?!`)(.+?)`/, &.colorize(:dark_gray))
|
||||||
end
|
end
|
||||||
|
|
||||||
private def output_title(title)
|
private def output_title(title)
|
||||||
output << HEADING.colorize(:yellow) << title.colorize(:yellow) << '\n'
|
output << HEADING_MARKER.colorize(:yellow)
|
||||||
output << '\n'
|
output << title.upcase.colorize(:yellow)
|
||||||
|
output << "\n\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
private def output_paragraph(paragraph : String)
|
private def output_paragraph(paragraph : String)
|
||||||
output_paragraph(paragraph.lines)
|
output_paragraph(paragraph.lines)
|
||||||
end
|
end
|
||||||
|
|
||||||
private def output_paragraph(paragraph : Array(String))
|
private def output_paragraph(paragraph : Array)
|
||||||
paragraph.each do |line|
|
paragraph.each do |line|
|
||||||
output << PREFIX << line << '\n'
|
output << ' ' << line << '\n'
|
||||||
end
|
end
|
||||||
output << '\n'
|
output << '\n'
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,16 +3,16 @@ module Ameba::Formatter
|
||||||
@mutex = Mutex.new
|
@mutex = Mutex.new
|
||||||
|
|
||||||
def source_finished(source : Source)
|
def source_finished(source : Source)
|
||||||
source.issues.each do |e|
|
source.issues.each do |issue|
|
||||||
next if e.disabled?
|
next if issue.disabled?
|
||||||
next if e.correctable? && config[:autocorrect]?
|
next if issue.correctable? && config[:autocorrect]?
|
||||||
|
|
||||||
next unless loc = e.location
|
next unless loc = issue.location
|
||||||
|
|
||||||
@mutex.synchronize do
|
@mutex.synchronize do
|
||||||
output.printf "%s:%d:%d: %s: [%s] %s\n",
|
output.printf "%s:%d:%d: %s: [%s] %s\n",
|
||||||
source.path, loc.line_number, loc.column_number, e.rule.severity.symbol,
|
source.path, loc.line_number, loc.column_number, issue.rule.severity.symbol,
|
||||||
e.rule.name, e.message.gsub('\n', " ")
|
issue.rule.name, issue.message.gsub('\n', " ")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,7 +24,9 @@ module Ameba::Rule::Layout
|
||||||
return if source_lines_size == 1 && last_source_line.empty?
|
return if source_lines_size == 1 && last_source_line.empty?
|
||||||
|
|
||||||
last_line_empty = last_source_line.empty?
|
last_line_empty = last_source_line.empty?
|
||||||
return if source_lines_size.zero? || (source_lines.last(2).join.presence && last_line_empty)
|
return if source_lines_size.zero? ||
|
||||||
|
(source_lines.last(2).join.presence && last_line_empty)
|
||||||
|
|
||||||
if last_line_empty
|
if last_line_empty
|
||||||
issue_for({source_lines_size, 1}, MSG)
|
issue_for({source_lines_size, 1}, MSG)
|
||||||
else
|
else
|
||||||
|
|
|
@ -41,6 +41,7 @@ module Ameba::Rule::Lint
|
||||||
op_end_location.column_number - 1
|
op_end_location.column_number - 1
|
||||||
)
|
)
|
||||||
op_text = source_between(op_location, op_end_location, source.lines)
|
op_text = source_between(op_location, op_end_location, source.lines)
|
||||||
|
|
||||||
return unless op_text
|
return unless op_text
|
||||||
return unless MISTAKES.has_key?(op_text)
|
return unless MISTAKES.has_key?(op_text)
|
||||||
|
|
||||||
|
|
|
@ -52,11 +52,7 @@ module Ameba::Rule::Lint
|
||||||
check_node(source, node, node.block)
|
check_node(source, node, node.block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test(source, node : Crystal::While)
|
def test(source, node : Crystal::While | Crystal::Until)
|
||||||
check_node(source, node, node.body) if literal?(node.cond)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test(source, node : Crystal::Until)
|
|
||||||
check_node(source, node, node.body) if literal?(node.cond)
|
check_node(source, node, node.body) if literal?(node.cond)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ module Ameba::Rule::Lint
|
||||||
private def duplicated_keys(entries)
|
private def duplicated_keys(entries)
|
||||||
entries.map(&.key)
|
entries.map(&.key)
|
||||||
.group_by(&.itself)
|
.group_by(&.itself)
|
||||||
.tap(&.select! { |_, v| v.size > 1 })
|
.select! { |_, v| v.size > 1 }
|
||||||
.map { |k, _| k }
|
.map { |k, _| k }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,15 +33,7 @@ module Ameba::Rule::Lint
|
||||||
issue_for node, MSG if static_literal?(node.cond)
|
issue_for node, MSG if static_literal?(node.cond)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test(source, node : Crystal::If)
|
def test(source, node : Crystal::If | Crystal::Unless | Crystal::Case)
|
||||||
check_node source, node
|
|
||||||
end
|
|
||||||
|
|
||||||
def test(source, node : Crystal::Unless)
|
|
||||||
check_node source, node
|
|
||||||
end
|
|
||||||
|
|
||||||
def test(source, node : Crystal::Case)
|
|
||||||
check_node source, node
|
check_node source, node
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,8 +26,8 @@ module Ameba::Rule::Lint
|
||||||
|
|
||||||
def test(source, node : Crystal::StringInterpolation)
|
def test(source, node : Crystal::StringInterpolation)
|
||||||
node.expressions
|
node.expressions
|
||||||
.select { |e| !e.is_a?(Crystal::StringLiteral) && literal?(e) }
|
.select { |exp| !exp.is_a?(Crystal::StringLiteral) && literal?(exp) }
|
||||||
.each { |n| issue_for n, MSG }
|
.each { |exp| issue_for exp, MSG }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -36,12 +36,12 @@ module Ameba::Rule::Lint
|
||||||
end
|
end
|
||||||
|
|
||||||
private def string_coercion_nodes(node)
|
private def string_coercion_nodes(node)
|
||||||
node.expressions.select do |e|
|
node.expressions.select do |exp|
|
||||||
e.is_a?(Crystal::Call) &&
|
exp.is_a?(Crystal::Call) &&
|
||||||
e.name == "to_s" &&
|
exp.name == "to_s" &&
|
||||||
e.args.size.zero? &&
|
exp.args.size.zero? &&
|
||||||
e.named_args.nil? &&
|
exp.named_args.nil? &&
|
||||||
e.obj
|
exp.obj
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,13 +45,16 @@ module Ameba::Rule::Lint
|
||||||
|
|
||||||
return if rescues.nil?
|
return if rescues.nil?
|
||||||
|
|
||||||
shadowed(rescues).each { |tp| issue_for tp, MSG % tp.names.join("::") }
|
shadowed(rescues).each do |path|
|
||||||
|
issue_for path, MSG % path.names.join("::")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private def shadowed(rescues, catch_all = false)
|
private def shadowed(rescues, catch_all = false)
|
||||||
traversed_types = Set(String).new
|
traversed_types = Set(String).new
|
||||||
|
|
||||||
filter_rescues(rescues).each_with_object([] of Crystal::Path) do |types, shadowed|
|
rescues = filter_rescues(rescues)
|
||||||
|
rescues.each_with_object([] of Crystal::Path) do |types, shadowed|
|
||||||
case
|
case
|
||||||
when catch_all
|
when catch_all
|
||||||
shadowed.concat(types)
|
shadowed.concat(types)
|
||||||
|
@ -62,7 +65,7 @@ module Ameba::Rule::Lint
|
||||||
catch_all = true
|
catch_all = true
|
||||||
next
|
next
|
||||||
else
|
else
|
||||||
nodes = types.select { |tp| traverse(tp.to_s, traversed_types) }
|
nodes = types.select { |path| traverse(path.to_s, traversed_types) }
|
||||||
shadowed.concat(nodes) unless nodes.empty?
|
shadowed.concat(nodes) unless nodes.empty?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,11 +45,7 @@ module Ameba::Rule::Lint
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def test(source, node : Crystal::ProcLiteral, scope : AST::Scope)
|
def test(source, node : Crystal::ProcLiteral | Crystal::Block, scope : AST::Scope)
|
||||||
find_shadowing source, scope
|
|
||||||
end
|
|
||||||
|
|
||||||
def test(source, node : Crystal::Block, scope : AST::Scope)
|
|
||||||
find_shadowing source, scope
|
find_shadowing source, scope
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ module Ameba::Rule::Lint
|
||||||
class Syntax < Base
|
class Syntax < Base
|
||||||
properties do
|
properties do
|
||||||
description "Reports invalid Crystal syntax"
|
description "Reports invalid Crystal syntax"
|
||||||
severity Ameba::Severity::Error
|
severity :error
|
||||||
end
|
end
|
||||||
|
|
||||||
def test(source)
|
def test(source)
|
||||||
|
|
|
@ -61,12 +61,7 @@ module Ameba::Rule::Lint
|
||||||
@parent.accept self
|
@parent.accept self
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit(node : Crystal::If)
|
def visit(node : Crystal::If | Crystal::Unless)
|
||||||
@rule.check_node(@source, @parent, node.cond)
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
def visit(node : Crystal::Unless)
|
|
||||||
@rule.check_node(@source, @parent, node.cond)
|
@rule.check_node(@source, @parent, node.cond)
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,10 +20,12 @@ module Ameba::Rule::Metrics
|
||||||
|
|
||||||
def test(source, node : Crystal::Def)
|
def test(source, node : Crystal::Def)
|
||||||
complexity = AST::CountingVisitor.new(node).count
|
complexity = AST::CountingVisitor.new(node).count
|
||||||
|
return unless complexity > max_complexity
|
||||||
|
|
||||||
return unless complexity > max_complexity && (location = node.name_location)
|
return unless location = node.name_location
|
||||||
issue_for location, name_end_location(node),
|
end_location = name_end_location(node)
|
||||||
MSG % {complexity, max_complexity}
|
|
||||||
|
issue_for location, end_location, MSG % {complexity, max_complexity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -79,6 +79,7 @@ module Ameba::Rule::Style
|
||||||
old = OLD % node.name
|
old = OLD % node.name
|
||||||
new = NEW % {node.name, name}
|
new = NEW % {node.name, name}
|
||||||
msg = MSG % {new, old}
|
msg = MSG % {new, old}
|
||||||
|
|
||||||
if end_location
|
if end_location
|
||||||
issue_for(filter_location, end_location, msg) do |corrector|
|
issue_for(filter_location, end_location, msg) do |corrector|
|
||||||
corrector.replace(filter_location, end_location, new)
|
corrector.replace(filter_location, end_location, new)
|
||||||
|
|
|
@ -39,11 +39,12 @@ module Ameba::Rule::Style
|
||||||
Tokenizer.new(source).run do |token|
|
Tokenizer.new(source).run do |token|
|
||||||
next unless token.type.number? && decimal?(token.raw)
|
next unless token.type.number? && decimal?(token.raw)
|
||||||
|
|
||||||
parsed = parse_number token.raw
|
parsed = parse_number(token.raw)
|
||||||
|
|
||||||
if allowed?(*parsed) && (expected = underscored *parsed) != token.raw
|
if allowed?(*parsed) && (expected = underscored *parsed) != token.raw
|
||||||
location = token.location
|
location = token.location
|
||||||
end_location = location.adjust(column_number: token.raw.size - 1)
|
end_location = location.adjust(column_number: token.raw.size - 1)
|
||||||
|
|
||||||
issue_for location, end_location, MSG % expected do |corrector|
|
issue_for location, end_location, MSG % expected do |corrector|
|
||||||
corrector.replace(location, end_location, expected)
|
corrector.replace(location, end_location, expected)
|
||||||
end
|
end
|
||||||
|
@ -58,21 +59,21 @@ module Ameba::Rule::Style
|
||||||
private def allowed?(_sign, value, fraction, _suffix)
|
private def allowed?(_sign, value, fraction, _suffix)
|
||||||
return true if fraction && fraction.size > 3
|
return true if fraction && fraction.size > 3
|
||||||
|
|
||||||
digits = value.chars.select &.to_s.=~ /[0-9]/
|
digits = value.chars.select(&.number?)
|
||||||
digits.size >= int_min_digits
|
digits.size >= int_min_digits
|
||||||
end
|
end
|
||||||
|
|
||||||
private def underscored(sign, value, fraction, suffix)
|
private def underscored(sign, value, fraction, suffix)
|
||||||
value = slice_digits(value.reverse) { |slice| slice }.reverse
|
value = slice_digits(value.reverse).reverse
|
||||||
fraction = "." + slice_digits(fraction) { |slice| slice } if fraction
|
fraction = "." + slice_digits(fraction) if fraction
|
||||||
|
|
||||||
"#{sign}#{value}#{fraction}#{suffix}"
|
"#{sign}#{value}#{fraction}#{suffix}"
|
||||||
end
|
end
|
||||||
|
|
||||||
private def slice_digits(value, by = 3)
|
private def slice_digits(value, by = 3)
|
||||||
([] of String).tap do |slices|
|
%w[].tap do |slices|
|
||||||
value.chars.reject(&.== '_').each_slice(by) do |slice|
|
value.chars.reject(&.== '_').each_slice(by) do |slice|
|
||||||
slices << (yield slice).join
|
slices << slice.join
|
||||||
end
|
end
|
||||||
end.join('_')
|
end.join('_')
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,7 +42,7 @@ module Ameba::Rule::Style
|
||||||
when Crystal::BinaryOp
|
when Crystal::BinaryOp
|
||||||
negated_condition?(node.left) || negated_condition?(node.right)
|
negated_condition?(node.left) || negated_condition?(node.right)
|
||||||
when Crystal::Expressions
|
when Crystal::Expressions
|
||||||
node.expressions.any? { |e| negated_condition?(e) }
|
node.expressions.any? { |exp| negated_condition?(exp) }
|
||||||
when Crystal::Not
|
when Crystal::Not
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
|
|
|
@ -66,23 +66,7 @@ module Ameba::Rule::Style
|
||||||
issue_for node, MSG % {expected, name}
|
issue_for node, MSG % {expected, name}
|
||||||
end
|
end
|
||||||
|
|
||||||
def test(source, node : Crystal::ClassDef)
|
def test(source, node : Crystal::Alias | Crystal::ClassDef | Crystal::ModuleDef | Crystal::LibDef | Crystal::EnumDef)
|
||||||
check_node(source, node)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test(source, node : Crystal::Alias)
|
|
||||||
check_node(source, node)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test(source, node : Crystal::LibDef)
|
|
||||||
check_node(source, node)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test(source, node : Crystal::EnumDef)
|
|
||||||
check_node(source, node)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test(source, node : Crystal::ModuleDef)
|
|
||||||
check_node(source, node)
|
check_node(source, node)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,15 +39,7 @@ module Ameba::Rule::Style
|
||||||
VarVisitor.new self, source
|
VarVisitor.new self, source
|
||||||
end
|
end
|
||||||
|
|
||||||
def test(source, node : Crystal::Var)
|
def test(source, node : Crystal::Var | Crystal::InstanceVar | Crystal::ClassVar)
|
||||||
check_node source, node
|
|
||||||
end
|
|
||||||
|
|
||||||
def test(source, node : Crystal::InstanceVar)
|
|
||||||
check_node source, node
|
|
||||||
end
|
|
||||||
|
|
||||||
def test(source, node : Crystal::ClassVar)
|
|
||||||
check_node source, node
|
check_node source, node
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ module Ameba
|
||||||
@code = corrected_code
|
@code = corrected_code
|
||||||
@lines = nil
|
@lines = nil
|
||||||
@ast = nil
|
@ast = nil
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -46,7 +47,6 @@ module Ameba
|
||||||
# source = Ameba::Source.new "a = 1\nb = 2", path
|
# source = Ameba::Source.new "a = 1\nb = 2", path
|
||||||
# source.lines # => ["a = 1", "b = 2"]
|
# source.lines # => ["a = 1", "b = 2"]
|
||||||
# ```
|
# ```
|
||||||
#
|
|
||||||
getter lines : Array(String) { code.split('\n') }
|
getter lines : Array(String) { code.split('\n') }
|
||||||
|
|
||||||
# Returns AST nodes constructed by `Crystal::Parser`.
|
# Returns AST nodes constructed by `Crystal::Parser`.
|
||||||
|
@ -55,7 +55,6 @@ module Ameba
|
||||||
# source = Ameba::Source.new code, path
|
# source = Ameba::Source.new code, path
|
||||||
# source.ast
|
# source.ast
|
||||||
# ```
|
# ```
|
||||||
#
|
|
||||||
getter ast : Crystal::ASTNode do
|
getter ast : Crystal::ASTNode do
|
||||||
Crystal::Parser.new(code)
|
Crystal::Parser.new(code)
|
||||||
.tap(&.wants_doc = true)
|
.tap(&.wants_doc = true)
|
||||||
|
@ -72,9 +71,9 @@ module Ameba
|
||||||
path.ends_with?("_spec.cr")
|
path.ends_with?("_spec.cr")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns `true` if *filepath* matches the source's path, `false` if it does not.
|
# Returns `true` if *filepath* matches the source's path, `false` otherwise.
|
||||||
def matches_path?(filepath)
|
def matches_path?(filepath)
|
||||||
path == filepath || path == File.expand_path(filepath)
|
path.in?(filepath, File.expand_path(filepath))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue