Merge pull request #425 from crystal-ameba/prepare-release-1.6.0

Prepare release 1.6.0
This commit is contained in:
Sijawusz Pur Rahnama 2023-11-17 18:14:20 +01:00 committed by GitHub
commit 5aac63ea74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 209 additions and 192 deletions

View file

@ -118,7 +118,6 @@ Add this to your application's `shard.yml`:
development_dependencies: development_dependencies:
ameba: ameba:
github: crystal-ameba/ameba github: crystal-ameba/ameba
version: ~> 1.4.0
``` ```
Build `bin/ameba` binary within your project directory while running `shards install`. Build `bin/ameba` binary within your project directory while running `shards install`.
@ -165,7 +164,7 @@ Generate new file by running `ameba --gen-config`.
**List of sources to run Ameba on can be configured globally via:** **List of sources to run Ameba on can be configured globally via:**
- `Globs` section - an array of wildcards (or paths) to include to the - `Globs` section - an array of wildcards (or paths) to include to the
inspection. Defaults to `%w(**/*.cr !lib)`, meaning it includes all project inspection. Defaults to `%w[**/*.cr !lib]`, meaning it includes all project
files with `*.cr` extension except those which exist in `lib` folder. files with `*.cr` extension except those which exist in `lib` folder.
- `Excluded` section - an array of wildcards (or paths) to exclude from the - `Excluded` section - an array of wildcards (or paths) to exclude from the
source list defined by `Globs`. Defaults to an empty array. source list defined by `Globs`. Defaults to an empty array.
@ -240,4 +239,4 @@ time = Time.epoch(1483859302) # ameba:disable Style, Lint
## Contributors ## Contributors
- [veelenga](https://github.com/veelenga) Vitalii Elenhaupt - creator, maintainer - [veelenga](https://github.com/veelenga) Vitalii Elenhaupt - creator, maintainer
- [Sija](https://github.com/Sija) Sijawusz Pur Rahnama - maintainer - [Sija](https://github.com/Sija) Sijawusz Pur Rahnama - contributor, maintainer

View file

@ -1,21 +1,22 @@
name: ameba name: ameba
version: 1.5.0 version: 1.6.0
authors: authors:
- Vitalii Elenhaupt <velenhaupt@gmail.com> - Vitalii Elenhaupt <velenhaupt@gmail.com>
- Sijawusz Pur Rahnama <sija@sija.pl>
targets: targets:
ameba: ameba:
main: src/cli.cr main: src/cli.cr
scripts: scripts:
# TODO: remove pre-compiled executable in future releases
postinstall: shards build -Dpreview_mt postinstall: shards build -Dpreview_mt
# TODO: remove pre-compiled executable in future releases
executables: executables:
- ameba - ameba
- ameba.cr - ameba.cr
crystal: "~> 1.9.0" crystal: ~> 1.10
license: MIT license: MIT

View file

@ -36,6 +36,43 @@ module Ameba::AST
end end
end end
describe "#static/dynamic_literal?" do
[
Crystal::ArrayLiteral.new,
Crystal::ArrayLiteral.new([Crystal::StringLiteral.new("foo")] of Crystal::ASTNode),
Crystal::BoolLiteral.new(false),
Crystal::CharLiteral.new('a'),
Crystal::HashLiteral.new,
Crystal::NamedTupleLiteral.new,
Crystal::NilLiteral.new,
Crystal::NumberLiteral.new(42),
Crystal::RegexLiteral.new(Crystal::StringLiteral.new("")),
Crystal::StringLiteral.new("foo"),
Crystal::SymbolLiteral.new("foo"),
Crystal::TupleLiteral.new([] of Crystal::ASTNode),
Crystal::TupleLiteral.new([Crystal::StringLiteral.new("foo")] of Crystal::ASTNode),
Crystal::RangeLiteral.new(
Crystal::NumberLiteral.new(0),
Crystal::NumberLiteral.new(10),
true),
].each do |literal|
it "properly identifies static node #{literal}" do
subject.static_literal?(literal).should be_true
subject.dynamic_literal?(literal).should be_false
end
end
[
Crystal::ArrayLiteral.new([Crystal::Path.new(%w[IO])] of Crystal::ASTNode),
Crystal::TupleLiteral.new([Crystal::Path.new(%w[IO])] of Crystal::ASTNode),
].each do |literal|
it "properly identifies dynamic node #{literal}" do
subject.dynamic_literal?(literal).should be_true
subject.static_literal?(literal).should be_false
end
end
end
describe "#node_source" do describe "#node_source" do
it "returns original source of the node" do it "returns original source of the node" do
s = <<-CRYSTAL s = <<-CRYSTAL

View file

@ -10,7 +10,7 @@ module Ameba::Rule
end end
it "contains rules across all the available groups" do it "contains rules across all the available groups" do
Rule.rules.map(&.group_name).uniq!.reject!(&.empty?).sort.should eq %w( Rule.rules.map(&.group_name).uniq!.reject!(&.empty?).sort.should eq %w[
Ameba Ameba
Documentation Documentation
Layout Layout
@ -19,7 +19,7 @@ module Ameba::Rule
Naming Naming
Performance Performance
Style Style
) ]
end end
end end
@ -50,25 +50,25 @@ module Ameba::Rule
it "returns false if source is not excluded from this rule" do it "returns false if source is not excluded from this rule" do
rule = DummyRule.new rule = DummyRule.new
rule.excluded = %w(some_source.cr) rule.excluded = %w[some_source.cr]
rule.excluded?(Source.new "", "another_source.cr").should_not be_true rule.excluded?(Source.new "", "another_source.cr").should_not be_true
end end
it "returns true if source is excluded from this rule" do it "returns true if source is excluded from this rule" do
rule = DummyRule.new rule = DummyRule.new
rule.excluded = %w(source.cr) rule.excluded = %w[source.cr]
rule.excluded?(Source.new "", "source.cr").should be_true rule.excluded?(Source.new "", "source.cr").should be_true
end end
it "returns true if source matches the wildcard" do it "returns true if source matches the wildcard" do
rule = DummyRule.new rule = DummyRule.new
rule.excluded = %w(**/*.cr) rule.excluded = %w[**/*.cr]
rule.excluded?(Source.new "", __FILE__).should be_true rule.excluded?(Source.new "", __FILE__).should be_true
end end
it "returns false if source does not match the wildcard" do it "returns false if source does not match the wildcard" do
rule = DummyRule.new rule = DummyRule.new
rule.excluded = %w(*_spec.cr) rule.excluded = %w[*_spec.cr]
rule.excluded?(Source.new "", "source.cr").should be_false rule.excluded?(Source.new "", "source.cr").should be_false
end end
end end

View file

@ -5,27 +5,27 @@ module Ameba::Cli
describe "Cmd" do describe "Cmd" do
describe ".run" do describe ".run" do
it "runs ameba" do it "runs ameba" do
r = Cli.run %w(-f silent -c spec/fixtures/config.yml spec/fixtures/source.cr) r = Cli.run %w[-f silent -c spec/fixtures/config.yml spec/fixtures/source.cr]
r.should be_nil r.should be_nil
end end
end end
describe ".parse_args" do describe ".parse_args" do
%w(-s --silent).each do |flag| %w[-s --silent].each do |flag|
it "accepts #{flag} flag" do it "accepts #{flag} flag" do
c = Cli.parse_args [flag] c = Cli.parse_args [flag]
c.formatter.should eq :silent c.formatter.should eq :silent
end end
end end
%w(-c --config).each do |flag| %w[-c --config].each do |flag|
it "accepts #{flag} flag" do it "accepts #{flag} flag" do
c = Cli.parse_args [flag, "config.yml"] c = Cli.parse_args [flag, "config.yml"]
c.config.should eq Path["config.yml"] c.config.should eq Path["config.yml"]
end end
end end
%w(-f --format).each do |flag| %w[-f --format].each do |flag|
it "accepts #{flag} flag" do it "accepts #{flag} flag" do
c = Cli.parse_args [flag, "my-formatter"] c = Cli.parse_args [flag, "my-formatter"]
c.formatter.should eq "my-formatter" c.formatter.should eq "my-formatter"
@ -34,68 +34,68 @@ module Ameba::Cli
it "accepts --only flag" do it "accepts --only flag" do
c = Cli.parse_args ["--only", "RULE1,RULE2"] c = Cli.parse_args ["--only", "RULE1,RULE2"]
c.only.should eq %w(RULE1 RULE2) c.only.should eq %w[RULE1 RULE2]
end end
it "accepts --except flag" do it "accepts --except flag" do
c = Cli.parse_args ["--except", "RULE1,RULE2"] c = Cli.parse_args ["--except", "RULE1,RULE2"]
c.except.should eq %w(RULE1 RULE2) c.except.should eq %w[RULE1 RULE2]
end end
it "defaults rules? flag to false" do it "defaults rules? flag to false" do
c = Cli.parse_args %w(spec/fixtures/source.cr) c = Cli.parse_args %w[spec/fixtures/source.cr]
c.rules?.should be_false c.rules?.should be_false
end end
it "defaults skip_reading_config? flag to false" do it "defaults skip_reading_config? flag to false" do
c = Cli.parse_args %w(spec/fixtures/source.cr) c = Cli.parse_args %w[spec/fixtures/source.cr]
c.skip_reading_config?.should be_false c.skip_reading_config?.should be_false
end end
it "accepts --rules flag" do it "accepts --rules flag" do
c = Cli.parse_args %w(--rules) c = Cli.parse_args %w[--rules]
c.rules?.should eq true c.rules?.should eq true
end end
it "defaults all? flag to false" do it "defaults all? flag to false" do
c = Cli.parse_args %w(spec/fixtures/source.cr) c = Cli.parse_args %w[spec/fixtures/source.cr]
c.all?.should be_false c.all?.should be_false
end end
it "accepts --all flag" do it "accepts --all flag" do
c = Cli.parse_args %w(--all) c = Cli.parse_args %w[--all]
c.all?.should eq true c.all?.should eq true
end end
it "accepts --gen-config flag" do it "accepts --gen-config flag" do
c = Cli.parse_args %w(--gen-config) c = Cli.parse_args %w[--gen-config]
c.formatter.should eq :todo c.formatter.should eq :todo
end end
it "accepts --no-color flag" do it "accepts --no-color flag" do
c = Cli.parse_args %w(--no-color) c = Cli.parse_args %w[--no-color]
c.colors?.should be_false c.colors?.should be_false
end end
it "accepts --without-affected-code flag" do it "accepts --without-affected-code flag" do
c = Cli.parse_args %w(--without-affected-code) c = Cli.parse_args %w[--without-affected-code]
c.without_affected_code?.should be_true c.without_affected_code?.should be_true
end end
it "doesn't disable colors by default" do it "doesn't disable colors by default" do
c = Cli.parse_args %w(--all) c = Cli.parse_args %w[--all]
c.colors?.should be_true c.colors?.should be_true
end end
it "ignores --config if --gen-config flag passed" do it "ignores --config if --gen-config flag passed" do
c = Cli.parse_args %w(--gen-config --config my_config.yml) c = Cli.parse_args %w[--gen-config --config my_config.yml]
c.formatter.should eq :todo c.formatter.should eq :todo
c.skip_reading_config?.should be_true c.skip_reading_config?.should be_true
end end
describe "-e/--explain" do describe "-e/--explain" do
it "configures file/line/column" do it "configures file/line/column" do
c = Cli.parse_args %w(--explain spec/fixtures/source.cr:3:5) c = Cli.parse_args %w[--explain spec/fixtures/source.cr:3:5]
location_to_explain = c.location_to_explain.should_not be_nil location_to_explain = c.location_to_explain.should_not be_nil
location_to_explain[:file].should eq "spec/fixtures/source.cr" location_to_explain[:file].should eq "spec/fixtures/source.cr"
@ -105,59 +105,59 @@ module Ameba::Cli
it "raises an error if location is not valid" do it "raises an error if location is not valid" do
expect_raises(Exception, "location should have PATH:line:column") do expect_raises(Exception, "location should have PATH:line:column") do
Cli.parse_args %w(--explain spec/fixtures/source.cr:3) Cli.parse_args %w[--explain spec/fixtures/source.cr:3]
end end
end end
it "raises an error if line number is not valid" do it "raises an error if line number is not valid" do
expect_raises(Exception, "location should have PATH:line:column") do expect_raises(Exception, "location should have PATH:line:column") do
Cli.parse_args %w(--explain spec/fixtures/source.cr:a:3) Cli.parse_args %w[--explain spec/fixtures/source.cr:a:3]
end end
end end
it "raises an error if column number is not valid" do it "raises an error if column number is not valid" do
expect_raises(Exception, "location should have PATH:line:column") do expect_raises(Exception, "location should have PATH:line:column") do
Cli.parse_args %w(--explain spec/fixtures/source.cr:3:&) Cli.parse_args %w[--explain spec/fixtures/source.cr:3:&]
end end
end end
it "raises an error if line/column are missing" do it "raises an error if line/column are missing" do
expect_raises(Exception, "location should have PATH:line:column") do expect_raises(Exception, "location should have PATH:line:column") do
Cli.parse_args %w(--explain spec/fixtures/source.cr) Cli.parse_args %w[--explain spec/fixtures/source.cr]
end end
end end
end end
context "--fail-level" do context "--fail-level" do
it "configures fail level Convention" do it "configures fail level Convention" do
c = Cli.parse_args %w(--fail-level convention) c = Cli.parse_args %w[--fail-level convention]
c.fail_level.should eq Severity::Convention c.fail_level.should eq Severity::Convention
end end
it "configures fail level Warning" do it "configures fail level Warning" do
c = Cli.parse_args %w(--fail-level Warning) c = Cli.parse_args %w[--fail-level Warning]
c.fail_level.should eq Severity::Warning c.fail_level.should eq Severity::Warning
end end
it "configures fail level Error" do it "configures fail level Error" do
c = Cli.parse_args %w(--fail-level error) c = Cli.parse_args %w[--fail-level error]
c.fail_level.should eq Severity::Error c.fail_level.should eq Severity::Error
end end
it "raises if fail level is incorrect" do it "raises if fail level is incorrect" do
expect_raises(Exception, "Incorrect severity name JohnDoe") do expect_raises(Exception, "Incorrect severity name JohnDoe") do
Cli.parse_args %w(--fail-level JohnDoe) Cli.parse_args %w[--fail-level JohnDoe]
end end
end end
end end
it "accepts unknown args as globs" do it "accepts unknown args as globs" do
c = Cli.parse_args %w(source1.cr source2.cr) c = Cli.parse_args %w[source1.cr source2.cr]
c.globs.should eq %w(source1.cr source2.cr) c.globs.should eq %w[source1.cr source2.cr]
end end
it "accepts one unknown arg as explain location if it has correct format" do it "accepts one unknown arg as explain location if it has correct format" do
c = Cli.parse_args %w(source.cr:3:22) c = Cli.parse_args %w[source.cr:3:22]
location_to_explain = c.location_to_explain.should_not be_nil location_to_explain = c.location_to_explain.should_not be_nil
location_to_explain[:file].should eq "source.cr" location_to_explain[:file].should eq "source.cr"

View file

@ -32,7 +32,7 @@ module Ameba
Globs: src/*.cr Globs: src/*.cr
CONFIG CONFIG
config = Config.new(yml) config = Config.new(yml)
config.globs.should eq %w(src/*.cr) config.globs.should eq %w[src/*.cr]
end end
it "initializes globs as array" do it "initializes globs as array" do
@ -43,7 +43,7 @@ module Ameba
- "!spec" - "!spec"
CONFIG CONFIG
config = Config.new(yml) config = Config.new(yml)
config.globs.should eq %w(src/*.cr !spec) config.globs.should eq %w[src/*.cr !spec]
end end
it "raises if Globs has a wrong type" do it "raises if Globs has a wrong type" do
@ -62,7 +62,7 @@ module Ameba
Excluded: spec Excluded: spec
CONFIG CONFIG
config = Config.new(yml) config = Config.new(yml)
config.excluded.should eq %w(spec) config.excluded.should eq %w[spec]
end end
it "initializes excluded as array" do it "initializes excluded as array" do
@ -73,7 +73,7 @@ module Ameba
- lib/*.cr - lib/*.cr
CONFIG CONFIG
config = Config.new(yml) config = Config.new(yml)
config.excluded.should eq %w(spec lib/*.cr) config.excluded.should eq %w[spec lib/*.cr]
end end
it "raises if Excluded has a wrong type" do it "raises if Excluded has a wrong type" do
@ -145,12 +145,12 @@ module Ameba
end end
it "returns a list of sources matching globs" do it "returns a list of sources matching globs" do
config.globs = %w(**/config_spec.cr) config.globs = %w[**/config_spec.cr]
config.sources.size.should eq(1) config.sources.size.should eq(1)
end end
it "returns a list of sources excluding 'Excluded'" do it "returns a list of sources excluding 'Excluded'" do
config.excluded = %w(**/config_spec.cr) config.excluded = %w[**/config_spec.cr]
config.sources.any?(&.fullpath.==(__FILE__)).should be_false config.sources.any?(&.fullpath.==(__FILE__)).should be_false
end end
end end
@ -192,7 +192,7 @@ module Ameba
it "updates excluded property" do it "updates excluded property" do
name = DummyRule.rule_name name = DummyRule.rule_name
excluded = %w(spec/source.cr) excluded = %w[spec/source.cr]
config.update_rule name, excluded: excluded config.update_rule name, excluded: excluded
rule = config.rules.find!(&.name.== name) rule = config.rules.find!(&.name.== name)
rule.excluded.should eq excluded rule.excluded.should eq excluded
@ -211,7 +211,7 @@ module Ameba
it "updates multiple rules by excluded property" do it "updates multiple rules by excluded property" do
name = DummyRule.rule_name name = DummyRule.rule_name
excluded = %w(spec/source.cr) excluded = %w[spec/source.cr]
config.update_rules [name], excluded: excluded config.update_rules [name], excluded: excluded
rule = config.rules.find!(&.name.== name) rule = config.rules.find!(&.name.== name)
rule.excluded.should eq excluded rule.excluded.should eq excluded
@ -226,7 +226,7 @@ module Ameba
it "updates a group by excluded property" do it "updates a group by excluded property" do
name = DummyRule.group_name name = DummyRule.group_name
excluded = %w(spec/source.cr) excluded = %w[spec/source.cr]
config.update_rules [name], excluded: excluded config.update_rules [name], excluded: excluded
rule = config.rules.find!(&.name.== DummyRule.rule_name) rule = config.rules.find!(&.name.== DummyRule.rule_name)
rule.excluded.should eq excluded rule.excluded.should eq excluded

View file

@ -4,16 +4,16 @@ module Ameba
subject = Rule::Lint::EmptyExpression.new subject = Rule::Lint::EmptyExpression.new
private def it_detects_empty_expression(code, *, file = __FILE__, line = __LINE__) private def it_detects_empty_expression(code, *, file = __FILE__, line = __LINE__)
it %(detects empty expression "#{code}"), file, line do it "detects empty expression #{code.inspect}", file, line do
s = Source.new code source = Source.new code
rule = Rule::Lint::EmptyExpression.new 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
end end
describe Rule::Lint::EmptyExpression do describe Rule::Lint::EmptyExpression do
it "passes if there is no empty expression" do it "passes if there is no empty expression" do
s = Source.new <<-CRYSTAL expect_no_issues subject, <<-CRYSTAL
def method() def method()
end end
@ -31,7 +31,6 @@ module Ameba
begin "" end begin "" end
[nil] << nil [nil] << nil
CRYSTAL CRYSTAL
subject.catch(s).should be_valid
end end
it_detects_empty_expression %(()) it_detects_empty_expression %(())
@ -91,10 +90,10 @@ module Ameba
) )
it "does not report empty expression in macro" do it "does not report empty expression in macro" do
s = Source.new %q( expect_no_issues subject, <<-CRYSTAL
module MyModule module MyModule
macro conditional_error_for_inline_callbacks macro conditional_error_for_inline_callbacks
\{% \\{%
raise "" raise ""
%} %}
end end
@ -102,8 +101,7 @@ module Ameba
macro before_save(x = nil) macro before_save(x = nil)
end end
end end
) CRYSTAL
subject.catch(s).should be_valid
end end
end end
end end

View file

@ -6,41 +6,41 @@ module Ameba::Rule::Lint
it "passes if percent arrays are written correctly" do it "passes if percent arrays are written correctly" do
s = Source.new %q( s = Source.new %q(
%i(one two three) %i[one two three]
%w(one two three) %w[one two three]
%i(1 2 3) %i[1 2 3]
%w(1 2 3) %w[1 2 3]
%i() %i[]
%w() %w[]
) )
subject.catch(s).should be_valid subject.catch(s).should be_valid
end end
it "fails if string percent array has commas" do it "fails if string percent array has commas" do
s = Source.new %( %w(one, two) ) s = Source.new %( %w[one, two] )
subject.catch(s).should_not be_valid subject.catch(s).should_not be_valid
end end
it "fails if string percent array has quotes" do it "fails if string percent array has quotes" do
s = Source.new %( %w("one" "two") ) s = Source.new %( %w["one" "two"] )
subject.catch(s).should_not be_valid subject.catch(s).should_not be_valid
end end
it "fails if symbols percent array has commas" do it "fails if symbols percent array has commas" do
s = Source.new %( %i(one, two) ) s = Source.new %( %i[one, two] )
subject.catch(s).should_not be_valid subject.catch(s).should_not be_valid
end end
it "fails if symbols percent array has a colon" do it "fails if symbols percent array has a colon" do
s = Source.new %( %i(:one :two) ) s = Source.new %( %i[:one :two] )
subject.catch(s).should_not be_valid subject.catch(s).should_not be_valid
end end
it "reports rule, location and message for %i" do it "reports rule, location and message for %i" do
s = Source.new %( s = Source.new %(
%i(:one) %i[:one]
), "source.cr" ), "source.cr"
subject.catch(s).should_not be_valid subject.catch(s).should_not be_valid
@ -54,7 +54,7 @@ module Ameba::Rule::Lint
it "reports rule, location and message for %w" do it "reports rule, location and message for %w" do
s = Source.new %( s = Source.new %(
%w("one") %w["one"]
), "source.cr" ), "source.cr"
subject.catch(s).should_not be_valid subject.catch(s).should_not be_valid
@ -71,14 +71,14 @@ module Ameba::Rule::Lint
it "#string_array_unwanted_symbols" do it "#string_array_unwanted_symbols" do
rule = PercentArrays.new rule = PercentArrays.new
rule.string_array_unwanted_symbols = "," rule.string_array_unwanted_symbols = ","
s = Source.new %( %w("one") ) s = Source.new %( %w["one"] )
rule.catch(s).should be_valid rule.catch(s).should be_valid
end end
it "#symbol_array_unwanted_symbols" do it "#symbol_array_unwanted_symbols" do
rule = PercentArrays.new rule = PercentArrays.new
rule.symbol_array_unwanted_symbols = "," rule.symbol_array_unwanted_symbols = ","
s = Source.new %( %i(:one) ) s = Source.new %( %i[:one] )
rule.catch(s).should be_valid rule.catch(s).should be_valid
end end
end end

View file

@ -19,12 +19,15 @@ module Ameba::Rule::Lint
# ^^^^^^^^^ error: Typo found: arugments -> arguments # ^^^^^^^^^ error: Typo found: arugments -> arguments
def tpos def tpos
# ^^^^ error: Typo found: tpos -> typos # ^^^^ error: Typo found: tpos -> typos
:otput
# ^^^^^ error: Typo found: otput -> output
end end
CRYSTAL CRYSTAL
expect_correction source, <<-CRYSTAL expect_correction source, <<-CRYSTAL
# method with no arguments # method with no arguments
def typos def typos
:output
end end
CRYSTAL CRYSTAL
end end

View file

@ -977,7 +977,7 @@ module Ameba::Rule::Lint
s = Source.new %( s = Source.new %(
foo = 22 foo = 22
{% for x in %w(foo) %} {% for x in %w[foo] %}
add({{ x.id }}) add({{ x.id }})
{% end %} {% end %}
) )
@ -988,7 +988,7 @@ module Ameba::Rule::Lint
s = Source.new %( s = Source.new %(
foo = 22 foo = 22
{% for x in %w(bar) %} {% for x in %w[bar] %}
puts {{ "foo".id }} puts {{ "foo".id }}
{% end %} {% end %}
) )

View file

@ -21,8 +21,18 @@ module Ameba::Rule::Naming
it "fails if predicate name is wrong" do it "fails if predicate name is wrong" do
expect_issue subject, <<-CRYSTAL expect_issue subject, <<-CRYSTAL
class Image
def self.is_valid?(x)
# ^^^^^^^^^ error: Favour method name 'valid?' over 'is_valid?'
end
end
def is_valid?(x) def is_valid?(x)
# ^^^^^^^^^^^^^^ error: Favour method name 'valid?' over 'is_valid?' # ^^^^^^^^^ error: Favour method name 'valid?' over 'is_valid?'
end
def is_valid(x)
# ^^^^^^^^ error: Favour method name 'valid?' over 'is_valid'
end end
CRYSTAL CRYSTAL
end end

View file

@ -8,7 +8,7 @@ module Ameba
rule = Rule::Naming::TypeNames.new rule = Rule::Naming::TypeNames.new
expect_issue rule, <<-CRYSTAL, type: type, name: name, file: file, line: line expect_issue rule, <<-CRYSTAL, type: type, name: name, file: file, line: line
%{type} %{name}; end %{type} %{name}; end
# ^{type}^{name}^^^^ error: Type name should be camelcased: #{expected}, but it was %{name} _{type} # ^{name} error: Type name should be camelcased: #{expected}, but it was %{name}
CRYSTAL CRYSTAL
end end
end end
@ -46,7 +46,7 @@ module Ameba
it "reports alias name" do it "reports alias name" do
expect_issue subject, <<-CRYSTAL expect_issue subject, <<-CRYSTAL
alias Numeric_value = Int32 alias Numeric_value = Int32
# ^{} error: Type name should be camelcased: NumericValue, but it was Numeric_value # ^^^^^^^^^^^^^ error: Type name should be camelcased: NumericValue, but it was Numeric_value
CRYSTAL CRYSTAL
end end
end end

View file

@ -48,7 +48,7 @@ module Ameba::Rule::Performance
context "properties" do context "properties" do
it "#filter_names" do it "#filter_names" do
rule = AnyAfterFilter.new rule = AnyAfterFilter.new
rule.filter_names = %w(select) rule.filter_names = %w[select]
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
[1, 2, 3].reject { |e| e > 2 }.any? [1, 2, 3].reject { |e| e > 2 }.any?

View file

@ -46,7 +46,7 @@ module Ameba::Rule::Performance
context "properties" do context "properties" do
it "#call_names" do it "#call_names" do
rule = ChainedCallWithNoBang.new rule = ChainedCallWithNoBang.new
rule.call_names = %w(uniq) rule.call_names = %w[uniq]
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
[1, 2, 3].select { |e| e > 2 }.reverse [1, 2, 3].select { |e| e > 2 }.reverse

View file

@ -64,7 +64,7 @@ module Ameba::Rule::Performance
context "properties" do context "properties" do
it "#filter_names" do it "#filter_names" do
rule = FirstLastAfterFilter.new rule = FirstLastAfterFilter.new
rule.filter_names = %w(reject) rule.filter_names = %w[reject]
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
[1, 2, 3].select { |e| e > 2 }.first [1, 2, 3].select { |e| e > 2 }.first

View file

@ -46,7 +46,7 @@ module Ameba::Rule::Performance
context "properties" do context "properties" do
it "#filter_names" do it "#filter_names" do
rule = SizeAfterFilter.new rule = SizeAfterFilter.new
rule.filter_names = %w(select) rule.filter_names = %w[select]
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
[1, 2, 3].reject(&.empty?).size [1, 2, 3].reject(&.empty?).size

View file

@ -44,7 +44,7 @@ module Ameba::Rule::Style
context "properties" do context "properties" do
it "#filter_names" do it "#filter_names" do
rule = IsAFilter.new rule = IsAFilter.new
rule.filter_names = %w(select) rule.filter_names = %w[select]
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
[1, 2, nil].reject(&.nil?) [1, 2, nil].reject(&.nil?)

View file

@ -123,7 +123,7 @@ module Ameba
it "#int_min_digits" do it "#int_min_digits" do
rule = Rule::Style::LargeNumbers.new rule = Rule::Style::LargeNumbers.new
rule.int_min_digits = 10 rule.int_min_digits = 10
expect_no_issues rule, %q(1200000) expect_no_issues rule, "1200000"
end end
end end
end end

View file

@ -4,7 +4,7 @@ module Ameba::Rule::Style
subject = ParenthesesAroundCondition.new subject = ParenthesesAroundCondition.new
describe ParenthesesAroundCondition do describe ParenthesesAroundCondition do
{% for keyword in %w(if unless while until) %} {% for keyword in %w[if unless while until] %}
context "{{ keyword.id }}" do context "{{ keyword.id }}" do
it "reports if redundant parentheses are found" do it "reports if redundant parentheses are found" do
source = expect_issue subject, <<-CRYSTAL, keyword: {{ keyword }} source = expect_issue subject, <<-CRYSTAL, keyword: {{ keyword }}

View file

@ -22,11 +22,11 @@ module Ameba
DELIMITER_START STRING INTERPOLATION_START NUMBER } DELIMITER_END EOF DELIMITER_START STRING INTERPOLATION_START NUMBER } DELIMITER_END EOF
) )
it_tokenizes %(%w(1 2)), it_tokenizes %(%w[1 2]),
%w(STRING_ARRAY_START STRING STRING STRING_ARRAY_END EOF) %w[STRING_ARRAY_START STRING STRING STRING_ARRAY_END EOF]
it_tokenizes %(%i(one two)), it_tokenizes %(%i[one two]),
%w(SYMBOL_ARRAY_START STRING STRING STRING_ARRAY_END EOF) %w[SYMBOL_ARRAY_START STRING STRING STRING_ARRAY_END EOF]
it_tokenizes %( it_tokenizes %(
class A class A
@ -34,11 +34,11 @@ module Ameba
puts "hello" puts "hello"
end end
end end
), %w( ), %w[
NEWLINE SPACE IDENT SPACE CONST NEWLINE SPACE IDENT SPACE IDENT NEWLINE SPACE IDENT SPACE CONST NEWLINE SPACE IDENT SPACE IDENT
NEWLINE SPACE IDENT SPACE DELIMITER_START STRING DELIMITER_END NEWLINE SPACE IDENT SPACE DELIMITER_START STRING DELIMITER_END
NEWLINE SPACE IDENT NEWLINE SPACE IDENT NEWLINE SPACE EOF NEWLINE SPACE IDENT NEWLINE SPACE IDENT NEWLINE SPACE EOF
) ]
end end
end end
end end

View file

@ -75,34 +75,5 @@ module Ameba::AST
node node
end end
end end
# TODO: Remove in a next release. BC for crystal <= 1.9.
# refs https://github.com/crystal-ameba/ameba/pull/407
#
# Indicates whether the node is a transformed assignment by the compiler.
# i.e.
#
# ```
# collection.each do |(a, b)|
# puts b
# end
# ```
#
# is transformed to:
#
# ```
# collection.each do |__arg0|
# a = __arg0[0]
# b = __arg0[1]
# puts(b)
# end
# ```
def transformed?
return false unless (assign = node).is_a?(Crystal::Assign)
return false unless (value = assign.value).is_a?(Crystal::Call)
return false unless (obj = value.obj).is_a?(Crystal::Var)
obj.name.starts_with? "__arg"
end
end end
end end

View file

@ -24,7 +24,7 @@ module Ameba::AST
# Uses the same logic than rubocop. See # Uses the same logic than rubocop. See
# https://github.com/rubocop-hq/rubocop/blob/master/lib/rubocop/cop/metrics/cyclomatic_complexity.rb#L21 # https://github.com/rubocop-hq/rubocop/blob/master/lib/rubocop/cop/metrics/cyclomatic_complexity.rb#L21
# Except "for", because crystal doesn't have a "for" loop. # Except "for", because crystal doesn't have a "for" loop.
{% for node in %i(if while until rescue or and) %} {% for node in %i[if while until rescue or and] %}
# :nodoc: # :nodoc:
def visit(node : Crystal::{{ node.id.capitalize }}) def visit(node : Crystal::{{ node.id.capitalize }})
@complexity += 1 unless macro_condition? @complexity += 1 unless macro_condition?

View file

@ -200,13 +200,13 @@ class Ameba::Config
# #
# ``` # ```
# config = Ameba::Config.load # config = Ameba::Config.load
# config.update_rules %w(Rule1 Rule2), enabled: true # config.update_rules %w[Rule1 Rule2], enabled: true
# ``` # ```
# #
# also it allows to update groups of rules: # also it allows to update groups of rules:
# #
# ``` # ```
# config.update_rules %w(Group1 Group2), enabled: true # config.update_rules %w[Group1 Group2], enabled: true
# ``` # ```
def update_rules(names, enabled = true, excluded = nil) def update_rules(names, enabled = true, excluded = nil)
names.try &.each do |name| names.try &.each do |name|

View file

@ -114,7 +114,7 @@ module Ameba::Rule
# Adds an issue to the *source* # Adds an issue to the *source*
macro issue_for(*args, **kwargs, &block) macro issue_for(*args, **kwargs, &block)
source.add_issue(self, {{ *args }}, {{ **kwargs }}) {{ block }} source.add_issue(self, {{ args.splat }}, {{ kwargs.double_splat }}) {{ block }}
end end
protected def self.rule_name protected def self.rule_name

View file

@ -28,7 +28,7 @@ module Ameba::Rule::Lint
end end
MSG = "Comparison to a boolean is pointless" MSG = "Comparison to a boolean is pointless"
OP_NAMES = %w(== != ===) OP_NAMES = %w[== != ===]
def test(source, node : Crystal::Call) def test(source, node : Crystal::Call)
return unless node.name.in?(OP_NAMES) return unless node.name.in?(OP_NAMES)

View file

@ -18,7 +18,7 @@ module Ameba::Rule::Lint
class DebugCalls < Base class DebugCalls < Base
properties do properties do
description "Disallows debug-related calls" description "Disallows debug-related calls"
method_names %w(p p! pp pp!) method_names %w[p p! pp pp!]
end end
MSG = "Possibly forgotten debug-related `%s` call detected" MSG = "Possibly forgotten debug-related `%s` call detected"

View file

@ -28,8 +28,6 @@ module Ameba::Rule::Lint
# Enabled: true # Enabled: true
# ``` # ```
class EmptyExpression < Base class EmptyExpression < Base
include AST::Util
properties do properties do
description "Disallows empty expressions" description "Disallows empty expressions"
end end

View file

@ -23,7 +23,7 @@ module Ameba::Rule::Lint
description "Identifies comparisons between literals" description "Identifies comparisons between literals"
end end
OP_NAMES = %w(=== == !=) OP_NAMES = %w[=== == !=]
MSG = "Comparison always evaluates to %s" MSG = "Comparison always evaluates to %s"
MSG_LIKELY = "Comparison most likely evaluates to %s" MSG_LIKELY = "Comparison most likely evaluates to %s"

View file

@ -27,11 +27,11 @@ module Ameba::Rule::Lint
description "Identifies usage of `index/rindex/find/match` calls followed by `not_nil!`" description "Identifies usage of `index/rindex/find/match` calls followed by `not_nil!`"
end end
BLOCK_CALL_NAMES = %w(index rindex find)
CALL_NAMES = %w(index rindex match)
MSG = "Use `%s! {...}` instead of `%s {...}.not_nil!`" MSG = "Use `%s! {...}` instead of `%s {...}.not_nil!`"
BLOCK_CALL_NAMES = %w[index rindex find]
CALL_NAMES = %w[index rindex match]
def test(source) def test(source)
AST::NodeVisitor.new self, source, skip: :macro AST::NodeVisitor.new self, source, skip: :macro
end end

View file

@ -4,15 +4,15 @@ module Ameba::Rule::Lint
# For example, this is usually written by mistake: # For example, this is usually written by mistake:
# #
# ``` # ```
# %i(:one, :two) # %i[:one, :two]
# %w("one", "two") # %w["one", "two"]
# ``` # ```
# #
# And the expected example is: # And the expected example is:
# #
# ``` # ```
# %i(one two) # %i[one two]
# %w(one two) # %w[one two]
# ``` # ```
# #
# YAML configuration example: # YAML configuration example:
@ -42,7 +42,7 @@ module Ameba::Rule::Lint
start_token = token.dup start_token = token.dup
when .string? when .string?
if (_start = start_token) && !issue if (_start = start_token) && !issue
issue = array_entry_invalid?(token.value, _start.raw) issue = array_entry_invalid?(token.value.to_s, _start.raw)
end end
when .string_array_end? when .string_array_end?
if (_start = start_token) && (_issue = issue) if (_start = start_token) && (_issue = issue)
@ -63,7 +63,7 @@ module Ameba::Rule::Lint
end end
private def check_array_entry(entry, symbols, literal) 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 end
end end

View file

@ -50,7 +50,8 @@ module Ameba::Rule::Lint
end end
MSG = "Focused spec item detected" MSG = "Focused spec item detected"
SPEC_ITEM_NAMES = %w(describe context it pending)
SPEC_ITEM_NAMES = %w[describe context it pending]
def test(source) def test(source)
return unless source.spec? return unless source.spec?

View file

@ -15,7 +15,7 @@ module Ameba::Rule::Lint
class Typos < Base class Typos < Base
properties do properties do
description "Reports typos found in source files" description "Reports typos found in source files"
bin_path nil.as(String?) bin_path : String? = nil
fail_on_error false fail_on_error false
end end

View file

@ -42,8 +42,6 @@ module Ameba::Rule::Lint
# Enabled: true # Enabled: true
# ``` # ```
class UnreachableCode < Base class UnreachableCode < Base
include AST::Util
properties do properties do
description "Reports unreachable code" description "Reports unreachable code"
end end

View file

@ -31,8 +31,6 @@ module Ameba::Rule::Lint
# Enabled: true # Enabled: true
# ``` # ```
class UnusedBlockArgument < Base class UnusedBlockArgument < Base
include AST::Util
properties do properties do
description "Disallows unused block arguments" description "Disallows unused block arguments"
end end
@ -58,7 +56,8 @@ module Ameba::Rule::Lint
location = block_arg.node.location location = block_arg.node.location
end_location = location.try &.adjust(column_number: block_arg.name.size - 1) end_location = location.try &.adjust(column_number: block_arg.name.size - 1)
if scope.yields? case
when scope.yields?
if location && end_location if location && end_location
issue_for location, end_location, MSG_YIELDED do |corrector| issue_for location, end_location, MSG_YIELDED do |corrector|
corrector.remove(location, end_location) corrector.remove(location, end_location)
@ -66,8 +65,7 @@ module Ameba::Rule::Lint
else else
issue_for block_arg.node, MSG_YIELDED issue_for block_arg.node, MSG_YIELDED
end end
else when !block_arg.ignored?
return if block_arg.ignored?
if location && end_location if location && end_location
issue_for location, end_location, MSG_UNUSED % block_arg.name do |corrector| issue_for location, end_location, MSG_UNUSED % block_arg.name do |corrector|
corrector.insert_before(location, '_') corrector.insert_before(location, '_')

View file

@ -42,7 +42,7 @@ module Ameba::Rule::Lint
next if scope.assigns_type_dec?(var.name) next if scope.assigns_type_dec?(var.name)
var.assignments.each do |assign| var.assignments.each do |assign|
next if assign.referenced? || assign.transformed? next if assign.referenced?
issue_for assign.target_node, MSG % var.name issue_for assign.target_node, MSG % var.name
end end
end end

View file

@ -30,7 +30,8 @@ module Ameba::Rule::Naming
def test(source, node : Crystal::Assign) def test(source, node : Crystal::Assign)
return unless (target = node.target).is_a?(Crystal::Path) return unless (target = node.target).is_a?(Crystal::Path)
name = target.names.first
name = target.to_s
expected = name.upcase expected = name.upcase
return if name.in?(expected, name.camelcase) return if name.in?(expected, name.camelcase)

View file

@ -45,9 +45,11 @@ module Ameba::Rule::Naming
MSG = "Method name should be underscore-cased: %s, not %s" MSG = "Method name should be underscore-cased: %s, not %s"
def test(source, node : Crystal::Def) 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 end
end end

View file

@ -31,10 +31,10 @@ module Ameba::Rule::Naming
MSG = "Favour method name '%s?' over '%s'" MSG = "Favour method name '%s?' over '%s'"
def test(source, node : Crystal::Def) def test(source, node : Crystal::Def)
return unless node.name =~ /^is_([a-z]\w*)\?$/ return unless node.name =~ /^is_([a-z]\w*)\??$/
alternative = $1 alternative = $1
issue_for node, MSG % {alternative, node.name} issue_for node, MSG % {alternative, node.name}, prefer_name_location: true
end end
end end
end end

View file

@ -48,9 +48,7 @@ module Ameba::Rule::Naming
.select!(&.name.in?(CALL_NAMES)) .select!(&.name.in?(CALL_NAMES))
end end
return unless calls calls.try &.each do |exp|
calls.each do |exp|
exp.args.each do |arg| exp.args.each do |arg|
name_node, is_bool = name_node, is_bool =
case arg case arg

View file

@ -60,10 +60,10 @@ module Ameba::Rule::Naming
def test(source, node : Crystal::Alias | Crystal::ClassDef | Crystal::ModuleDef | Crystal::LibDef | Crystal::EnumDef) def test(source, node : Crystal::Alias | Crystal::ClassDef | Crystal::ModuleDef | Crystal::LibDef | Crystal::EnumDef)
name = node.name.to_s name = node.name.to_s
expected = name.camelcase
return if name == expected
issue_for node, MSG % {expected, name} return if (expected = name.camelcase) == name
issue_for node.name, MSG % {expected, name}
end end
end end
end end

View file

@ -35,8 +35,8 @@ module Ameba::Rule::Naming
def test(source, node : Crystal::Var | Crystal::InstanceVar | Crystal::ClassVar) def test(source, node : Crystal::Var | Crystal::InstanceVar | Crystal::ClassVar)
name = node.name.to_s name = node.name.to_s
expected = name.underscore
return if name == expected return if (expected = name.underscore) == name
issue_for node, MSG % {expected, name} issue_for node, MSG % {expected, name}
end end

View file

@ -31,7 +31,7 @@ module Ameba::Rule::Performance
properties do properties do
description "Identifies usage of `any?` calls that follow filters" description "Identifies usage of `any?` calls that follow filters"
filter_names %w(select reject) filter_names %w[select reject]
end end
MSG = "Use `any? {...}` instead of `%s {...}.any?`" MSG = "Use `any? {...}` instead of `%s {...}.any?`"

View file

@ -45,18 +45,18 @@ module Ameba::Rule::Performance
# All of those have bang method variants returning `self` # All of those have bang method variants returning `self`
# and are not modifying the receiver type (like `compact` does), # and are not modifying the receiver type (like `compact` does),
# thus are safe to switch to the bang variant. # thus are safe to switch to the bang variant.
call_names %w(uniq sort sort_by shuffle reverse) call_names %w[uniq sort sort_by shuffle reverse]
end end
# All these methods are allocating a new object MSG = "Use bang method variant `%s!` after chained `%s` call"
ALLOCATING_METHOD_NAMES = %w(
# All these methods allocate a new object
ALLOCATING_METHOD_NAMES = %w[
keys values values_at map map_with_index flat_map compact_map keys values values_at map map_with_index flat_map compact_map
flatten compact select reject sample group_by chunks tally merge flatten compact select reject sample group_by chunks tally merge
combinations repeated_combinations permutations repeated_permutations combinations repeated_combinations permutations repeated_permutations
transpose invert chars captures named_captures clone transpose invert chars captures named_captures clone
) ]
MSG = "Use bang method variant `%s!` after chained `%s` call"
def test(source) def test(source)
AST::NodeVisitor.new self, source, skip: :macro AST::NodeVisitor.new self, source, skip: :macro

View file

@ -30,13 +30,14 @@ module Ameba::Rule::Performance
properties do properties do
description "Identifies usage of `first/last/first?/last?` calls that follow filters" description "Identifies usage of `first/last/first?/last?` calls that follow filters"
filter_names %w(select) filter_names %w[select]
end end
CALL_NAMES = %w(first last first? last?)
MSG = "Use `find {...}` instead of `%s {...}.%s`" MSG = "Use `find {...}` instead of `%s {...}.%s`"
MSG_REVERSE = "Use `reverse_each.find {...}` instead of `%s {...}.%s`" MSG_REVERSE = "Use `reverse_each.find {...}` instead of `%s {...}.%s`"
CALL_NAMES = %w[first last first? last?]
def test(source) def test(source)
AST::NodeVisitor.new self, source, skip: :macro AST::NodeVisitor.new self, source, skip: :macro
end end

View file

@ -29,10 +29,10 @@ module Ameba::Rule::Performance
description "Identifies usage of `sum/product` calls that follow `map`" description "Identifies usage of `sum/product` calls that follow `map`"
end end
CALL_NAMES = %w(sum product)
MAP_NAME = "map"
MSG = "Use `%s {...}` instead of `map {...}.%s`" MSG = "Use `%s {...}` instead of `map {...}.%s`"
CALL_NAMES = %w[sum product]
def test(source) def test(source)
AST::NodeVisitor.new self, source, skip: :macro AST::NodeVisitor.new self, source, skip: :macro
end end
@ -40,7 +40,7 @@ module Ameba::Rule::Performance
def test(source, node : Crystal::Call) def test(source, node : Crystal::Call)
return unless node.name.in?(CALL_NAMES) && (obj = node.obj) return unless node.name.in?(CALL_NAMES) && (obj = node.obj)
return unless obj.is_a?(Crystal::Call) && obj.block 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), issue_for name_location(obj), name_end_location(node),
MSG % {node.name, node.name} MSG % {node.name, node.name}

View file

@ -37,7 +37,7 @@ module Ameba::Rule::Performance
properties do properties do
description "Identifies usage of `size` calls that follow filter" description "Identifies usage of `size` calls that follow filter"
filter_names %w(select reject) filter_names %w[select reject]
end end
MSG = "Use `count {...}` instead of `%s {...}.size`." MSG = "Use `count {...}` instead of `%s {...}.size`."

View file

@ -43,12 +43,13 @@ module Ameba::Rule::Style
properties do properties do
description "Identifies usage of `is_a?/nil?` calls within filters" description "Identifies usage of `is_a?/nil?` calls within filters"
filter_names %w(select reject any? all? none? one?) filter_names %w[select reject any? all? none? one?]
end end
MSG = "Use `%s` instead of `%s`" MSG = "Use `%s` instead of `%s`"
NEW = "%s(%s)"
OLD = "%s {...}" OLD = "%s {...}"
NEW = "%s(%s)"
def test(source) def test(source)
AST::NodeVisitor.new self, source, skip: :macro AST::NodeVisitor.new self, source, skip: :macro

View file

@ -227,9 +227,6 @@ module Ameba::Rule::Style
arg = block.args.first 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?` # we filter out the blocks that are of call type - `i.to_i64.odd?`
return unless (body = block.body).is_a?(Crystal::Call) return unless (body = block.body).is_a?(Crystal::Call)

View file

@ -67,7 +67,7 @@ module Ameba
@unneeded_disable_directive_rule = @unneeded_disable_directive_rule =
config.rules config.rules
.find &.name.==(Rule::Lint::UnneededDisableDirective.rule_name) .find &.class.==(Rule::Lint::UnneededDisableDirective)
end end
protected def initialize(@rules, @sources, @formatter, @severity, @autocorrect = false) protected def initialize(@rules, @sources, @formatter, @severity, @autocorrect = false)

View file

@ -74,7 +74,7 @@ module Ameba
# Returns `true` if *filepath* matches the source's path, `false` otherwise. # Returns `true` if *filepath* matches the source's path, `false` otherwise.
def matches_path?(filepath) def matches_path?(filepath)
path.in?(filepath, File.expand_path(filepath)) fullpath == File.expand_path(filepath)
end end
# Converts an AST location to a string position. # Converts an AST location to a string position.

View file

@ -72,12 +72,15 @@ class Ameba::Source
# Replaces the code of the given range with *content*. # Replaces the code of the given range with *content*.
def replace(begin_pos, end_pos, 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 end
# Inserts the given strings before and after the given range. # Inserts the given strings before and after the given range.
def wrap(begin_pos, end_pos, insert_before, insert_after) 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 end
# Shortcut for `replace(begin_pos, end_pos, "")` # Shortcut for `replace(begin_pos, end_pos, "")`