Misc cleanups/refactors
This commit is contained in:
parent
547fec5a94
commit
f3f1f3a2ab
|
@ -4,4 +4,4 @@ Documentation/DocumentationAdmonition:
|
|||
|
||||
Lint/Typos:
|
||||
Excluded:
|
||||
- spec/ameba/rule/lint/typos_spec.cr
|
||||
- spec/ameba/rule/lint/typos_spec.cr
|
||||
|
|
|
@ -186,8 +186,8 @@ Excluded:
|
|||
``` yaml
|
||||
Style/RedundantBegin:
|
||||
Excluded:
|
||||
- src/server/processor.cr
|
||||
- src/server/api.cr
|
||||
- src/server/processor.cr
|
||||
- src/server/api.cr
|
||||
```
|
||||
|
||||
### Rules
|
||||
|
|
|
@ -9,9 +9,9 @@ targets:
|
|||
main: src/cli.cr
|
||||
|
||||
scripts:
|
||||
# TODO: remove pre-compiled executable in future releases
|
||||
postinstall: shards build -Dpreview_mt
|
||||
|
||||
# TODO: remove pre-compiled executable in future releases
|
||||
executables:
|
||||
- ameba
|
||||
- ameba.cr
|
||||
|
|
|
@ -4,16 +4,16 @@ module Ameba
|
|||
subject = Rule::Lint::EmptyExpression.new
|
||||
|
||||
private def it_detects_empty_expression(code, *, file = __FILE__, line = __LINE__)
|
||||
it %(detects empty expression "#{code}"), file, line do
|
||||
s = Source.new code
|
||||
it "detects empty expression #{code.inspect}", file, line do
|
||||
source = Source.new code
|
||||
rule = Rule::Lint::EmptyExpression.new
|
||||
rule.catch(s).should_not be_valid, file: file, line: line
|
||||
rule.catch(source).should_not be_valid, file: file, line: line
|
||||
end
|
||||
end
|
||||
|
||||
describe Rule::Lint::EmptyExpression do
|
||||
it "passes if there is no empty expression" do
|
||||
s = Source.new <<-CRYSTAL
|
||||
expect_no_issues subject, <<-CRYSTAL
|
||||
def method()
|
||||
end
|
||||
|
||||
|
@ -31,7 +31,6 @@ module Ameba
|
|||
begin "" end
|
||||
[nil] << nil
|
||||
CRYSTAL
|
||||
subject.catch(s).should be_valid
|
||||
end
|
||||
|
||||
it_detects_empty_expression %(())
|
||||
|
@ -91,10 +90,10 @@ module Ameba
|
|||
)
|
||||
|
||||
it "does not report empty expression in macro" do
|
||||
s = Source.new %q(
|
||||
expect_no_issues subject, <<-CRYSTAL
|
||||
module MyModule
|
||||
macro conditional_error_for_inline_callbacks
|
||||
\{%
|
||||
\\{%
|
||||
raise ""
|
||||
%}
|
||||
end
|
||||
|
@ -102,8 +101,7 @@ module Ameba
|
|||
macro before_save(x = nil)
|
||||
end
|
||||
end
|
||||
)
|
||||
subject.catch(s).should be_valid
|
||||
CRYSTAL
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,12 +19,15 @@ module Ameba::Rule::Lint
|
|||
# ^^^^^^^^^ error: Typo found: arugments -> arguments
|
||||
def tpos
|
||||
# ^^^^ error: Typo found: tpos -> typos
|
||||
:otput
|
||||
# ^^^^^ error: Typo found: otput -> output
|
||||
end
|
||||
CRYSTAL
|
||||
|
||||
expect_correction source, <<-CRYSTAL
|
||||
# method with no arguments
|
||||
def typos
|
||||
:output
|
||||
end
|
||||
CRYSTAL
|
||||
end
|
||||
|
|
|
@ -123,7 +123,7 @@ module Ameba
|
|||
it "#int_min_digits" do
|
||||
rule = Rule::Style::LargeNumbers.new
|
||||
rule.int_min_digits = 10
|
||||
expect_no_issues rule, %q(1200000)
|
||||
expect_no_issues rule, "1200000"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,11 +27,11 @@ module Ameba::Rule::Lint
|
|||
description "Identifies usage of `index/rindex/find/match` calls followed by `not_nil!`"
|
||||
end
|
||||
|
||||
MSG = "Use `%s! {...}` instead of `%s {...}.not_nil!`"
|
||||
|
||||
BLOCK_CALL_NAMES = %w(index rindex find)
|
||||
CALL_NAMES = %w(index rindex match)
|
||||
|
||||
MSG = "Use `%s! {...}` instead of `%s {...}.not_nil!`"
|
||||
|
||||
def test(source)
|
||||
AST::NodeVisitor.new self, source, skip: :macro
|
||||
end
|
||||
|
|
|
@ -42,7 +42,7 @@ module Ameba::Rule::Lint
|
|||
start_token = token.dup
|
||||
when .string?
|
||||
if (_start = start_token) && !issue
|
||||
issue = array_entry_invalid?(token.value, _start.raw)
|
||||
issue = array_entry_invalid?(token.value.to_s, _start.raw)
|
||||
end
|
||||
when .string_array_end?
|
||||
if (_start = start_token) && (_issue = issue)
|
||||
|
@ -63,7 +63,7 @@ module Ameba::Rule::Lint
|
|||
end
|
||||
|
||||
private def check_array_entry(entry, symbols, literal)
|
||||
MSG % {symbols, literal} if entry =~ /[#{symbols}]/
|
||||
MSG % {symbols, literal} if entry.matches?(/[#{Regex.escape(symbols)}]/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,7 +15,7 @@ module Ameba::Rule::Lint
|
|||
class Typos < Base
|
||||
properties do
|
||||
description "Reports typos found in source files"
|
||||
bin_path nil.as(String?)
|
||||
bin_path : String? = nil
|
||||
fail_on_error false
|
||||
end
|
||||
|
||||
|
|
|
@ -56,7 +56,8 @@ module Ameba::Rule::Lint
|
|||
location = block_arg.node.location
|
||||
end_location = location.try &.adjust(column_number: block_arg.name.size - 1)
|
||||
|
||||
if scope.yields?
|
||||
case
|
||||
when scope.yields?
|
||||
if location && end_location
|
||||
issue_for location, end_location, MSG_YIELDED do |corrector|
|
||||
corrector.remove(location, end_location)
|
||||
|
@ -64,8 +65,7 @@ module Ameba::Rule::Lint
|
|||
else
|
||||
issue_for block_arg.node, MSG_YIELDED
|
||||
end
|
||||
else
|
||||
return if block_arg.ignored?
|
||||
when !block_arg.ignored?
|
||||
if location && end_location
|
||||
issue_for location, end_location, MSG_UNUSED % block_arg.name do |corrector|
|
||||
corrector.insert_before(location, '_')
|
||||
|
|
|
@ -30,7 +30,8 @@ module Ameba::Rule::Naming
|
|||
|
||||
def test(source, node : Crystal::Assign)
|
||||
return unless (target = node.target).is_a?(Crystal::Path)
|
||||
name = target.names.first
|
||||
|
||||
name = target.to_s
|
||||
expected = name.upcase
|
||||
|
||||
return if name.in?(expected, name.camelcase)
|
||||
|
|
|
@ -45,9 +45,11 @@ module Ameba::Rule::Naming
|
|||
MSG = "Method name should be underscore-cased: %s, not %s"
|
||||
|
||||
def test(source, node : Crystal::Def)
|
||||
return if (expected = node.name.underscore) == node.name
|
||||
name = node.name.to_s
|
||||
|
||||
issue_for node, MSG % {expected, node.name}, prefer_name_location: true
|
||||
return if (expected = name.underscore) == name
|
||||
|
||||
issue_for node, MSG % {expected, name}, prefer_name_location: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,9 +48,7 @@ module Ameba::Rule::Naming
|
|||
.select!(&.name.in?(CALL_NAMES))
|
||||
end
|
||||
|
||||
return unless calls
|
||||
|
||||
calls.each do |exp|
|
||||
calls.try &.each do |exp|
|
||||
exp.args.each do |arg|
|
||||
name_node, is_bool =
|
||||
case arg
|
||||
|
|
|
@ -60,8 +60,8 @@ module Ameba::Rule::Naming
|
|||
|
||||
def test(source, node : Crystal::Alias | Crystal::ClassDef | Crystal::ModuleDef | Crystal::LibDef | Crystal::EnumDef)
|
||||
name = node.name.to_s
|
||||
expected = name.camelcase
|
||||
return if name == expected
|
||||
|
||||
return if (expected = name.camelcase) == name
|
||||
|
||||
issue_for node.name, MSG % {expected, name}
|
||||
end
|
||||
|
|
|
@ -35,8 +35,8 @@ module Ameba::Rule::Naming
|
|||
|
||||
def test(source, node : Crystal::Var | Crystal::InstanceVar | Crystal::ClassVar)
|
||||
name = node.name.to_s
|
||||
expected = name.underscore
|
||||
return if name == expected
|
||||
|
||||
return if (expected = name.underscore) == name
|
||||
|
||||
issue_for node, MSG % {expected, name}
|
||||
end
|
||||
|
|
|
@ -48,7 +48,9 @@ module Ameba::Rule::Performance
|
|||
call_names %w(uniq sort sort_by shuffle reverse)
|
||||
end
|
||||
|
||||
# All these methods are allocating a new object
|
||||
MSG = "Use bang method variant `%s!` after chained `%s` call"
|
||||
|
||||
# All these methods allocate a new object
|
||||
ALLOCATING_METHOD_NAMES = %w(
|
||||
keys values values_at map map_with_index flat_map compact_map
|
||||
flatten compact select reject sample group_by chunks tally merge
|
||||
|
@ -56,8 +58,6 @@ module Ameba::Rule::Performance
|
|||
transpose invert chars captures named_captures clone
|
||||
)
|
||||
|
||||
MSG = "Use bang method variant `%s!` after chained `%s` call"
|
||||
|
||||
def test(source)
|
||||
AST::NodeVisitor.new self, source, skip: :macro
|
||||
end
|
||||
|
|
|
@ -29,9 +29,9 @@ module Ameba::Rule::Performance
|
|||
description "Identifies usage of `sum/product` calls that follow `map`"
|
||||
end
|
||||
|
||||
MSG = "Use `%s {...}` instead of `map {...}.%s`"
|
||||
|
||||
CALL_NAMES = %w(sum product)
|
||||
MAP_NAME = "map"
|
||||
MSG = "Use `%s {...}` instead of `map {...}.%s`"
|
||||
|
||||
def test(source)
|
||||
AST::NodeVisitor.new self, source, skip: :macro
|
||||
|
@ -40,7 +40,7 @@ module Ameba::Rule::Performance
|
|||
def test(source, node : Crystal::Call)
|
||||
return unless node.name.in?(CALL_NAMES) && (obj = node.obj)
|
||||
return unless obj.is_a?(Crystal::Call) && obj.block
|
||||
return unless obj.name == MAP_NAME
|
||||
return unless obj.name == "map"
|
||||
|
||||
issue_for name_location(obj), name_end_location(node),
|
||||
MSG % {node.name, node.name}
|
||||
|
|
|
@ -47,8 +47,9 @@ module Ameba::Rule::Style
|
|||
end
|
||||
|
||||
MSG = "Use `%s` instead of `%s`"
|
||||
NEW = "%s(%s)"
|
||||
|
||||
OLD = "%s {...}"
|
||||
NEW = "%s(%s)"
|
||||
|
||||
def test(source)
|
||||
AST::NodeVisitor.new self, source, skip: :macro
|
||||
|
|
|
@ -227,9 +227,6 @@ module Ameba::Rule::Style
|
|||
|
||||
arg = block.args.first
|
||||
|
||||
# we skip auto-generated blocks - `(1..3).any?(&.odd?)`
|
||||
return if arg.name.starts_with?("__arg")
|
||||
|
||||
# we filter out the blocks that are of call type - `i.to_i64.odd?`
|
||||
return unless (body = block.body).is_a?(Crystal::Call)
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ module Ameba
|
|||
|
||||
@unneeded_disable_directive_rule =
|
||||
config.rules
|
||||
.find &.name.==(Rule::Lint::UnneededDisableDirective.rule_name)
|
||||
.find &.class.==(Rule::Lint::UnneededDisableDirective)
|
||||
end
|
||||
|
||||
protected def initialize(@rules, @sources, @formatter, @severity, @autocorrect = false)
|
||||
|
|
|
@ -74,7 +74,7 @@ module Ameba
|
|||
|
||||
# Returns `true` if *filepath* matches the source's path, `false` otherwise.
|
||||
def matches_path?(filepath)
|
||||
path.in?(filepath, File.expand_path(filepath))
|
||||
fullpath == File.expand_path(filepath)
|
||||
end
|
||||
|
||||
# Converts an AST location to a string position.
|
||||
|
|
|
@ -72,12 +72,15 @@ class Ameba::Source
|
|||
|
||||
# Replaces the code of the given range with *content*.
|
||||
def replace(begin_pos, end_pos, content)
|
||||
combine(begin_pos, end_pos, replacement: content.to_s)
|
||||
combine begin_pos, end_pos,
|
||||
replacement: content.to_s
|
||||
end
|
||||
|
||||
# Inserts the given strings before and after the given range.
|
||||
def wrap(begin_pos, end_pos, insert_before, insert_after)
|
||||
combine(begin_pos, end_pos, insert_before: insert_before.to_s, insert_after: insert_after.to_s)
|
||||
combine begin_pos, end_pos,
|
||||
insert_before: insert_before.to_s,
|
||||
insert_after: insert_after.to_s
|
||||
end
|
||||
|
||||
# Shortcut for `replace(begin_pos, end_pos, "")`
|
||||
|
|
Loading…
Reference in New Issue