Add rule namespaces: style, lint, layout (#63)

This commit is contained in:
V. Elenhaupt 2018-06-16 14:50:59 +03:00 committed by GitHub
parent d9f04af057
commit 4cb5328513
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
71 changed files with 128 additions and 128 deletions

View file

@ -0,0 +1,113 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
subject = ComparisonToBoolean.new
describe ComparisonToBoolean do
it "passes if there is no comparison to boolean" do
source = Source.new %(
a = true
if a
:ok
end
if true
:ok
end
unless s.empty?
:ok
end
:ok if a
:ok if a != 1
:ok if a == "true"
case a
when true
:ok
when false
:not_ok
end
)
subject.catch(source).should be_valid
end
context "boolean on the right" do
it "fails if there is == comparison to boolean" do
source = Source.new %(
if s.empty? == true
:ok
end
)
subject.catch(source).should_not be_valid
end
it "fails if there is != comparison to boolean" do
source = Source.new %(
if a != false
:ok
end
)
subject.catch(source).should_not be_valid
end
it "fails if there is case comparison to boolean" do
source = Source.new %(
a === true
)
subject.catch(source).should_not be_valid
end
it "reports rule, pos and message" do
source = Source.new "a != true", "source.cr"
subject.catch(source)
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.message.should eq "Comparison to a boolean is pointless"
end
end
context "boolean on the left" do
it "fails if there is == comparison to boolean" do
source = Source.new %(
if true == s.empty?
:ok
end
)
subject.catch(source).should_not be_valid
end
it "fails if there is != comparison to boolean" do
source = Source.new %(
if false != a
:ok
end
)
subject.catch(source).should_not be_valid
end
it "fails if there is case comparison to boolean" do
source = Source.new %(
true === a
)
subject.catch(source).should_not be_valid
end
it "reports rule, pos and message" do
source = Source.new "true != a", "source.cr"
subject.catch(source).should_not be_valid
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.message.should eq "Comparison to a boolean is pointless"
end
end
end
end

View file

@ -0,0 +1,44 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
subject = DebuggerStatement.new
describe DebuggerStatement do
it "passes if there is no debugger statement" do
s = Source.new %(
"this is not a debugger statement"
s = "debugger"
def debugger(program)
end
debugger ""
class A
def debugger
end
end
A.new.debugger
)
subject.catch(s).should be_valid
end
it "fails if there is a debugger statement" do
s = Source.new %(
a = 2
debugger
a = a + 1
)
subject.catch(s).should_not be_valid
end
it "reports rule, pos and message" do
s = Source.new "debugger", "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.message.should eq "Possible forgotten debugger statement detected"
end
end
end

View file

@ -0,0 +1,68 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
describe EmptyEnsure do
subject = EmptyEnsure.new
it "passes if there is no empty ensure blocks" do
s = Source.new %(
def some_method
do_some_stuff
ensure
do_something_else
end
begin
do_some_stuff
ensure
do_something_else
end
def method_with_rescue
rescue
ensure
nil
end
)
subject.catch(s).should be_valid
end
it "fails if there is an empty ensure in method" do
s = Source.new %(
def method
do_some_stuff
ensure
end
)
subject.catch(s).should_not be_valid
end
it "fails if there is an empty ensure in a block" do
s = Source.new %(
begin
do_some_stuff
ensure
# nothing here
end
)
subject.catch(s).should_not be_valid
end
it "reports rule, pos and message" do
s = Source.new %(
begin
do_some_stuff
rescue
do_some_other_stuff
ensure
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:3:11"
issue.message.should eq "Empty `ensure` block detected"
end
end
end

View file

@ -0,0 +1,110 @@
require "../../../spec_helper"
module Ameba
subject = Rule::Lint::EmptyExpression.new
def it_detects_empty_expression(code)
it "detects empty expression" do
s = Source.new code
rule = Rule::Lint::EmptyExpression.new
rule.catch(s).should_not be_valid
end
end
describe Rule::Lint::EmptyExpression do
it "passes if there is no empty expression" do
s = Source.new %(
def method()
end
method()
method(1, 2, 3)
method(nil)
a = nil
a = ""
a = 0
nil
:any.nil?
begin "" end
[nil] << nil
)
subject.catch(s).should be_valid
end
it_detects_empty_expression %(())
it_detects_empty_expression %(((())))
it_detects_empty_expression %(a = ())
it_detects_empty_expression %((();()))
it_detects_empty_expression %(if (); end)
it_detects_empty_expression %(
if foo
1
elsif ()
2
end
)
it_detects_empty_expression %(
case foo
when :foo then ()
end
)
it_detects_empty_expression %(
case foo
when :foo then 1
else
()
end
)
it_detects_empty_expression %(
case foo
when () then 1
end
)
it_detects_empty_expression %(
def method
a = 1
()
end
)
it_detects_empty_expression %(
def method
rescue
()
end
)
it_detects_empty_expression %(
def method
begin
end
end
)
it_detects_empty_expression %(
begin; end
)
it_detects_empty_expression %(
begin
nil
end
)
it_detects_empty_expression %(
begin
()
end
)
it "reports rule, location and message" do
s = Source.new %(
if ()
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:12"
issue.message.should eq "Avoid empty expressions"
end
end
end

View file

@ -0,0 +1,50 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
describe HashDuplicatedKey do
subject = HashDuplicatedKey.new
it "passes if there is no duplicated keys in a hash literals" do
s = Source.new %(
h = {"a" => 1, :a => 2, "b" => 3}
h = {"a" => 1, "b" => 2, "c" => {"a" => 3, "b" => 4}}
h = {} of String => String
)
subject.catch(s).should be_valid
end
it "fails if there is a duplicated key in a hash literal" do
s = Source.new %q(
h = {"a" => 1, "b" => 2, "a" => 3}
)
subject.catch(s).should_not be_valid
end
it "fails if there is a duplicated key in the inner hash literal" do
s = Source.new %q(
h = {"a" => 1, "b" => {"a" => 3, "b" => 4, "a" => 5}}
)
subject.catch(s).should_not be_valid
end
it "reports rule, location and message" do
s = Source.new %q(
h = {"a" => 1, "a" => 2}
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:13"
issue.message.should eq %(Duplicated keys in hash literal: "a")
end
it "reports multiple duplicated keys" do
s = Source.new %q(
h = {"key1" => 1, "key1" => 2, "key2" => 3, "key2" => 4}
)
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.message.should eq %(Duplicated keys in hash literal: "key1", "key2")
end
end
end

View file

@ -0,0 +1,72 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
subject = LiteralInCondition.new
describe LiteralInCondition do
it "passes if there is not literals in conditional" do
s = Source.new %(
if a == 2
:ok
end
:ok unless b
case string
when "a"
:ok
when "b"
:ok
end
unless a.nil?
:ok
end
)
subject.catch(s).should be_valid
end
it "fails if there is a predicate in if conditional" do
s = Source.new %(
if "string"
:ok
end
)
subject.catch(s).should_not be_valid
end
it "fails if there is a predicate in unless conditional" do
s = Source.new %(
unless true
:ok
end
)
subject.catch(s).should_not be_valid
end
it "fails if there is a predicate in case conditional" do
s = Source.new %(
case [1, 2, 3]
when :array
:ok
when :not_array
:also_ok
end
)
subject.catch(s).should_not be_valid
end
it "reports rule, pos and message" do
s = Source.new %(
puts "hello" if true
), "source.cr"
subject.catch(s).should_not be_valid
s.issues.size.should eq 1
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:9"
issue.message.should eq "Literal value found in conditional"
end
end
end

View file

@ -0,0 +1,41 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
subject = LiteralInInterpolation.new
describe LiteralInInterpolation do
it "passes with good interpolation examples" do
s = Source.new %q(
name = "Ary"
"Hello, #{name}"
"#{name}"
"Name size: #{name.size}"
)
subject.catch(s).should be_valid
end
it "fails if there is useless interpolation" do
[
%q("#{:Ary}"),
%q("#{[1, 2, 3]}"),
%q("#{true}"),
%q("#{false}"),
%q("here are #{4} cats"),
].each do |str|
subject.catch(Source.new str).should_not be_valid
end
end
it "reports rule, pos and message" do
s = Source.new %q("#{4}"), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.message.should eq "Literal value found in interpolation"
end
end
end

View file

@ -0,0 +1,85 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
describe PercentArrays do
subject = PercentArrays.new
it "passes if percent arrays are written correctly" do
s = Source.new %q(
%i(one two three)
%w(one two three)
%i(1 2 3)
%w(1 2 3)
%i()
%w()
)
subject.catch(s).should be_valid
end
it "fails if string percent array has commas" do
s = Source.new %( %w(one, two) )
subject.catch(s).should_not be_valid
end
it "fails if string percent array has quotes" do
s = Source.new %( %w("one" "two") )
subject.catch(s).should_not be_valid
end
it "fails if symbols percent array has commas" do
s = Source.new %( %i(one, two) )
subject.catch(s).should_not be_valid
end
it "fails if symbols percent array has a colon" do
s = Source.new %( %i(:one :two) )
subject.catch(s).should_not be_valid
end
it "reports rule, location and message for %i" do
s = Source.new %(
%i(:one)
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:9"
issue.message.should eq(
"Symbols `,:` may be unwanted in %i array literals"
)
end
it "reports rule, location and message for %w" do
s = Source.new %(
%w("one")
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:9"
issue.message.should eq(
"Symbols `,\"` may be unwanted in %w array literals"
)
end
context "properties" do
it "allows to configure string_array_unwanted_symbols" do
rule = PercentArrays.new
rule.string_array_unwanted_symbols = ","
s = Source.new %( %w("one") )
rule.catch(s).should be_valid
end
it "allows to configure symbol_array_unwanted_symbols" do
rule = PercentArrays.new
rule.symbol_array_unwanted_symbols = ","
s = Source.new %( %i(:one) )
rule.catch(s).should be_valid
end
end
end
end

View file

@ -0,0 +1,36 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
describe RandZero do
subject = RandZero.new
it "passes if it is not rand(1) or rand(0)" do
s = Source.new %(
rand(1.0)
rand(0.11)
rand(2)
)
subject.catch(s).should be_valid
end
it "fails if it is rand(0)" do
s = Source.new "rand(0)"
subject.catch(s).should_not be_valid
end
it "fails if it is rand(1)" do
s = Source.new "rand(1)"
subject.catch(s).should_not be_valid
end
it "reports rule, location and a message" do
s = Source.new "rand(1)", "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:1"
issue.message.should eq "rand(1) always returns 0"
end
end
end

View file

@ -0,0 +1,165 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
describe ShadowedArgument do
subject = ShadowedArgument.new
it "doesn't report if there is not a shadowed argument" do
s = Source.new %(
def foo(bar)
baz = 1
end
3.times do |i|
a = 1
end
proc = -> (a : Int32) {
b = 2
}
)
subject.catch(s).should be_valid
end
it "reports if there is a shadowed method argument" do
s = Source.new %(
def foo(bar)
bar = 1
bar
end
)
subject.catch(s).should_not be_valid
end
it "reports if there is a shadowed block argument" do
s = Source.new %(
3.times do |i|
i = 2
end
)
subject.catch(s).should_not be_valid
end
it "reports if there is a shadowed proc argument" do
s = Source.new %(
->(x : Int32) {
x = 20
x
}
)
subject.catch(s).should_not be_valid
end
it "doesn't report if the argument is referenced before the assignment" do
s = Source.new %(
def foo(bar)
bar
bar = 1
end
)
subject.catch(s).should be_valid
end
it "doesn't report if the argument is conditionally reassigned" do
s = Source.new %(
def foo(bar = nil)
bar ||= true
bar
end
)
subject.catch(s).should be_valid
end
it "doesn't report if the op assign is followed by another assignment" do
s = Source.new %(
def foo(bar)
bar ||= 3
bar = 43
bar
end
)
subject.catch(s).should be_valid
end
it "reports if the shadowing assignment is followed by op assign" do
s = Source.new %(
def foo(bar)
bar = 42
bar ||= 43
bar
end
)
subject.catch(s).should_not be_valid
end
it "doesn't report if the argument is unused" do
s = Source.new %(
def foo(bar)
end
)
subject.catch(s).should be_valid
end
it "reports if the argument is shadowed before super" do
s = Source.new %(
def foo(bar)
bar = 1
super
end
)
subject.catch(s).should_not be_valid
end
context "branch" do
it "doesn't report if the argument is not shadowed in a condition" do
s = Source.new %(
def foo(bar, baz)
bar = 1 if baz
bar
end
)
subject.catch(s).should be_valid
end
it "reports if the argument is shadowed after the condition" do
s = Source.new %(
def foo(foo)
if something
foo = 42
end
foo = 43
foo
end
)
subject.catch(s).should_not be_valid
end
it "doesn't report if the argument is conditionally assigned in a branch" do
s = Source.new %(
def foo(bar)
if something
bar ||= 22
end
bar
end
)
subject.catch(s).should be_valid
end
end
it "reports rule, location and message" do
s = Source.new %(
def foo(bar)
bar = 22
bar
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:3:11"
issue.message.should eq "Argument `bar` is assigned before it is used"
end
end
end

View file

@ -0,0 +1,175 @@
require "../../../spec_helper"
private def check_shadowed(source, exceptions)
s = Ameba::Source.new source
Ameba::Rule::Lint::ShadowedException.new.catch(s).should_not be_valid
s.issues.first.message.should contain exceptions.join(", ")
end
module Ameba::Rule::Lint
describe ShadowedException do
subject = ShadowedException.new
it "passes if there isn't shadowed exception" do
s = Source.new %(
def method
do_something
rescue ArgumentError
handle_argument_error_exception
rescue Exception
handle_exception
end
def method
rescue Exception
handle_exception
end
def method
rescue e : ArgumentError
handle_argument_error_exception
rescue e : Exception
handle_exception
end
)
subject.catch(s).should be_valid
end
it "fails if there is a shadowed exception" do
check_shadowed %(
begin
do_something
rescue Exception
handle_exception
rescue ArgumentError
handle_argument_error_exception
end
), %w(ArgumentError)
end
it "fails if there is a custom shadowed exceptions" do
check_shadowed %(
begin
1
rescue Exception
2
rescue MySuperException
3
end
), %w(MySuperException)
end
it "fails if there is a shadowed exception in a type list" do
check_shadowed %(
begin
rescue Exception | IndexError
end
), %w(IndexError)
end
it "fails if there is a first shadowed exception in a type list" do
check_shadowed %(
begin
rescue IndexError | Exception
rescue Exception
rescue
end
), %w(IndexError)
end
it "fails if there is a shadowed duplicated exception" do
check_shadowed %(
begin
rescue IndexError
rescue ArgumentError
rescue IndexError
end
), %w(IndexError)
end
it "fails if there is a shadowed duplicated exception in a type list" do
check_shadowed %(
begin
rescue IndexError
rescue ArgumentError | IndexError
end
), %w(IndexError)
end
it "fails if there is only shadowed duplicated exceptions" do
check_shadowed %(
begin
rescue IndexError
rescue IndexError
end
), %w(IndexError)
end
it "fails if there is only shadowed duplicated exceptions in a type list" do
check_shadowed %(
begin
rescue IndexError | IndexError
end
), %w(IndexError)
end
it "fails if all rescues are shadowed and there is a catch-all rescue" do
check_shadowed %(
begin
rescue Exception
rescue ArgumentError
rescue IndexError
rescue KeyError | IO::Error
rescue
end
), %w(IndexError KeyError IO::Error)
end
it "fails if there are shadowed exception with args" do
check_shadowed %(
begin
rescue Exception
rescue ex : IndexError
rescue
end
), %w(IndexError)
end
it "fails if there are multiple shadowed exceptions" do
check_shadowed %(
begin
rescue Exception
rescue ArgumentError
rescue IndexError
end
), %w(ArgumentError IndexError)
end
it "fails if there are multipe shadowed exceptions in a type list" do
check_shadowed %(
begin
rescue Exception
rescue ArgumentError | IndexError
rescue IO::Error
end
), %w(ArgumentError IndexError IO::Error)
end
it "reports rule, location and a message" do
s = Source.new %q(
begin
do_something
rescue Exception | IndexError
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:3:11"
issue.message.should eq(
"Exception handler has shadowed exceptions: IndexError"
)
end
end
end

View file

@ -0,0 +1,140 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
describe ShadowingOuterLocalVar do
subject = ShadowingOuterLocalVar.new
it "doesn't report if there is no shadowing" do
source = Source.new %(
def some_method
foo = 1
3.times do |bar|
bar
end
-> (baz : Int32) {}
-> (bar : String) {}
end
)
subject.catch(source).should be_valid
end
it "reports if there is a shadowing in a block" do
source = Source.new %(
def some_method
foo = 1
3.times do |foo|
end
end
)
subject.catch(source).should_not be_valid
end
it "reports if there is a shadowing in a proc" do
source = Source.new %(
def some_method
foo = 1
-> (foo : Int32) {}
end
)
subject.catch(source).should_not be_valid
end
it "reports if there is a shadowing in an inner scope" do
source = Source.new %(
def foo
foo = 1
3.times do |i|
3.times { |foo| foo }
end
end
)
subject.catch(source).should_not be_valid
end
it "reports if variable is shadowed twice" do
source = Source.new %(
foo = 1
3.times do |foo|
-> (foo : Int32) { foo + 1 }
end
)
subject.catch(source).should_not be_valid
source.issues.size.should eq 2
end
it "reports if a splat block argument shadows local var" do
source = Source.new %(
foo = 1
3.times do |*foo|
end
)
subject.catch(source).should_not be_valid
end
it "reports if a &block argument is shadowed" do
source = Source.new %(
def method_with_block(a, &block)
3.times do |block|
end
end
)
subject.catch(source).should_not be_valid
source.issues.first.message.should eq "Shadowing outer local variable `block`"
end
it "reports if there are multiple args and one shadows local var" do
source = Source.new %(
foo = 1
[1, 2, 3].each_with_index do |i, foo|
i + foo
end
)
subject.catch(source).should_not be_valid
source.issues.first.message.should eq "Shadowing outer local variable `foo`"
end
it "doesn't report if an outer var is reassigned in a block" do
source = Source.new %(
def foo
foo = 1
3.times do |i|
foo = 2
end
end
)
subject.catch(source).should be_valid
end
it "doesn't report if an argument is a black hole '_'" do
source = Source.new %(
_ = 1
3.times do |_|
end
)
subject.catch(source).should be_valid
end
it "reports rule, location and message" do
source = Source.new %(
foo = 1
3.times { |foo| foo + 1 }
), "source.cr"
subject.catch(source).should_not be_valid
issue = source.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:3:20"
issue.message.should eq "Shadowing outer local variable `foo`"
end
end
end

View file

@ -0,0 +1,37 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
describe Syntax do
subject = Syntax.new
it "passes if there is no invalid syntax" do
s = Source.new %(
def hello
puts "totally valid"
rescue e: Exception
end
)
subject.catch(s).should be_valid
end
it "fails if there is an invalid syntax" do
s = Source.new %(
def hello
puts "invalid"
rescue Exception => e
end
)
subject.catch(s).should_not be_valid
end
it "reports rule, location and message" do
s = Source.new "def hello end", "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:1:11"
issue.message.should eq "unexpected token: end (expected ';' or newline)"
end
end
end

View file

@ -0,0 +1,102 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
describe UnneededDisableDirective do
subject = UnneededDisableDirective.new
it "passes if there are no comments" do
s = Source.new %(
a = 1
)
subject.catch(s).should be_valid
end
it "passes if there is disable directive" do
s = Source.new %(
a = 1 # my super var
)
subject.catch(s).should be_valid
end
it "doesn't report if there is disable directive and it is needed" do
s = Source.new %Q(
# ameba:disable #{NamedRule.name}
a = 1
)
s.add_issue NamedRule.new, location: {3, 9},
message: "Useless assignment", status: :disabled
subject.catch(s).should be_valid
end
it "passes if there is inline disable directive and it is needed" do
s = Source.new %Q(
a = 1 # ameba:disable #{NamedRule.name}
)
s.add_issue NamedRule.new, location: {2, 1},
message: "Alarm!", status: :disabled
subject.catch(s).should be_valid
end
it "ignores commented out disable directive" do
s = Source.new %Q(
# # ameba:disable #{NamedRule.name}
a = 1
)
s.add_issue NamedRule.new, location: {3, 1},
message: "Alarm!", status: :disabled
subject.catch(s).should be_valid
end
it "fails if there is unneeded directive" do
s = Source.new %Q(
# ameba:disable #{NamedRule.name}
a = 1
)
subject.catch(s).should_not be_valid
s.issues.first.message.should eq(
"Unnecessary disabling of #{NamedRule.name}"
)
end
it "fails if there is inline unneeded directive" do
s = Source.new %Q(a = 1 # ameba:disable #{NamedRule.name})
subject.catch(s).should_not be_valid
s.issues.first.message.should eq(
"Unnecessary disabling of #{NamedRule.name}"
)
end
it "detects mixed inline directives" do
s = Source.new %Q(
# ameba:disable Rule1, Rule2
a = 1 # ameba:disable Rule3
), "source.cr"
subject.catch(s).should_not be_valid
s.issues.size.should eq 2
s.issues.first.message.should contain "Rule1, Rule2"
s.issues.last.message.should contain "Rule3"
end
it "fails if there is disabled UnneededDisableDirective" do
s = Source.new %Q(
# ameba:disable #{UnneededDisableDirective.rule_name}
a = 1
), "source.cr"
s.add_issue UnneededDisableDirective.new, location: {3, 1},
message: "Alarm!", status: :disabled
subject.catch(s).should_not be_valid
end
it "reports issue, location and message" do
s = Source.new %Q(
# ameba:disable Rule1, Rule2
a = 1
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:2:9"
issue.message.should eq "Unnecessary disabling of Rule1, Rule2"
end
end
end

View file

@ -0,0 +1,278 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
subject = UnusedArgument.new
subject.ignore_defs = false
describe UnusedArgument do
it "doesn't report if arguments are used" do
s = Source.new %(
def method(a, b, c)
a + b + c
end
3.times do |i|
i + 1
end
->(i : Int32) { i + 1 }
)
subject.catch(s).should be_valid
end
it "reports if method argument is unused" do
s = Source.new %(
def method(a, b, c)
a + b
end
)
subject.catch(s).should_not be_valid
s.issues.first.message.should eq "Unused argument `c`. If it's necessary, use `_c` " \
"as an argument name to indicate that it won't be used."
end
it "reports if block argument is unused" do
s = Source.new %(
[1,2].each_with_index do |a, i|
a
end
)
subject.catch(s).should_not be_valid
s.issues.first.message.should eq "Unused argument `i`. If it's necessary, use `_` " \
"as an argument name to indicate that it won't be used."
end
it "reports if proc argument is unused" do
s = Source.new %(
-> (a : Int32, b : String) do
a = a + 1
end
)
subject.catch(s).should_not be_valid
s.issues.first.message.should eq "Unused argument `b`. If it's necessary, use `_b` " \
"as an argument name to indicate that it won't be used."
end
it "reports multiple unused args" do
s = Source.new %(
def method(a, b, c)
nil
end
)
subject.catch(s).should_not be_valid
s.issues[0].message.should eq "Unused argument `a`. If it's necessary, use `_a` " \
"as an argument name to indicate that it won't be used."
s.issues[1].message.should eq "Unused argument `b`. If it's necessary, use `_b` " \
"as an argument name to indicate that it won't be used."
s.issues[2].message.should eq "Unused argument `c`. If it's necessary, use `_c` " \
"as an argument name to indicate that it won't be used."
end
it "doesn't report if it is an instance var argument" do
s = Source.new %(
class A
def method(@name)
end
end
)
subject.catch(s).should be_valid
end
it "doesn't report if a typed argument is used" do
s = Source.new %(
def method(x : Int32)
3.times do
puts x
end
end
)
subject.catch(s).should be_valid
end
it "doesn't report if an argument with default value is used" do
s = Source.new %(
def method(x = 1)
puts x
end
)
subject.catch(s).should be_valid
end
it "doesn't report if argument starts with a _" do
s = Source.new %(
def method(_x)
end
)
subject.catch(s).should be_valid
end
it "doesn't report if it is a block and used" do
s = Source.new %(
def method(&block)
block.call
end
)
subject.catch(s).should be_valid
end
it "reports if block arg is not used" do
s = Source.new %(
def method(&block)
end
)
subject.catch(s).should_not be_valid
end
it "reports if unused and there is yield" do
s = Source.new %(
def method(&block)
yield 1
end
)
subject.catch(s).should_not be_valid
end
it "doesn't report if variable is referenced implicitly" do
s = Source.new %(
class Bar < Foo
def method(a, b)
super
end
end
)
subject.catch(s).should be_valid
end
it "doesn't report if arg if referenced in case" do
s = Source.new %(
def foo(a)
case a
when /foo/
end
end
)
subject.catch(s).should be_valid
end
context "super" do
it "reports if variable is not referenced implicitly by super" do
s = Source.new %(
class Bar < Foo
def method(a, b)
super a
end
end
)
subject.catch(s).should_not be_valid
s.issues.first.message.should eq "Unused argument `b`. If it's necessary, use `_b` " \
"as an argument name to indicate that it won't be used."
end
it "reports rule, location and message" do
s = Source.new %(
def method(a)
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.message.should eq "Unused argument `a`. If it's necessary, use `_a` " \
"as an argument name to indicate that it won't be used."
issue.location.to_s.should eq "source.cr:2:22"
end
end
context "macro" do
it "doesn't report if it is a used macro argument" do
s = Source.new %(
macro my_macro(arg)
{% arg %}
end
)
subject.catch(s).should be_valid
end
it "doesn't report if it is a used macro block argument" do
s = Source.new %(
macro my_macro(&block)
{% block %}
end
)
subject.catch(s).should be_valid
end
it "doesn't report used macro args with equal names in record" do
s = Source.new %(
record X do
macro foo(a, b)
{{a}} + {{b}}
end
macro bar(a, b, c)
{{a}} + {{b}} + {{c}}
end
end
)
subject.catch(s).should be_valid
end
end
context "properties" do
describe "#ignore_defs" do
it "lets the rule to ignore def scopes if true" do
subject.ignore_defs = true
s = Source.new %(
def method(a)
end
)
subject.catch(s).should be_valid
end
it "lets the rule not to ignore def scopes if false" do
subject.ignore_defs = false
s = Source.new %(
def method(a)
end
)
subject.catch(s).should_not be_valid
end
end
context "#ignore_blocks" do
it "lets the rule to ignore block scopes if true" do
subject.ignore_blocks = true
s = Source.new %(
3.times { |i| puts "yo!" }
)
subject.catch(s).should be_valid
end
it "lets the rule not to ignore block scopes if false" do
subject.ignore_blocks = false
s = Source.new %(
3.times { |i| puts "yo!" }
)
subject.catch(s).should_not be_valid
end
end
context "#ignore_procs" do
it "lets the rule to ignore proc scopes if true" do
subject.ignore_procs = true
s = Source.new %(
->(a : Int32) {}
)
subject.catch(s).should be_valid
end
it "lets the rule not to ignore proc scopes if false" do
subject.ignore_procs = false
s = Source.new %(
->(a : Int32) {}
)
subject.catch(s).should_not be_valid
end
end
end
end
end

View file

@ -0,0 +1,900 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
describe UselessAssign do
subject = UselessAssign.new
it "does not report used assigments" do
s = Source.new %(
def method
a = 2
a
end
)
subject.catch(s).should be_valid
end
it "reports a useless assignment in a method" do
s = Source.new %(
def method
a = 2
end
)
subject.catch(s).should_not be_valid
end
it "reports a useless assignment in a proc" do
s = Source.new %(
->() {
a = 2
}
)
subject.catch(s).should_not be_valid
end
it "reports a useless assignment in a block" do
s = Source.new %(
def method
3.times do
a = 1
end
end
)
subject.catch(s).should_not be_valid
end
it "reports a useless assignment in a proc inside def" do
s = Source.new %(
def method
->() {
a = 2
}
end
)
subject.catch(s).should_not be_valid
end
it "reports a useless assignment in a proc inside a block" do
s = Source.new %(
def method
3.times do
->() {
a = 2
}
end
end
)
subject.catch(s).should_not be_valid
end
it "reports rule, position and a message" do
s = Source.new %(
def method
a = 2
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:3:11"
issue.message.should eq "Useless assignment to variable `a`"
end
it "does not report useless assignment of instance var" do
s = Source.new %(
class Cls
def initialize(@name)
end
end
)
subject.catch(s).should be_valid
end
it "does not report if assignment used in the inner block scope" do
s = Source.new %(
def method
var = true
3.times { var = false }
end
)
subject.catch(s).should be_valid
end
it "reports if assigned is not referenced in the inner block scope" do
s = Source.new %(
def method
var = true
3.times {}
end
)
subject.catch(s).should_not be_valid
end
it "doesn't report if assignment in referenced in inner block" do
s = Source.new %(
def method
two = true
3.times do
mutex.synchronize do
two = 2
end
end
two.should be_true
end
)
subject.catch(s).should be_valid
end
it "reports if first assignment is useless" do
s = Source.new %(
def method
var = true
var = false
var
end
)
subject.catch(s).should_not be_valid
s.issues.first.location.to_s.should eq ":3:11"
end
it "reports if variable reassigned and not used" do
s = Source.new %(
def method
var = true
var = false
end
)
subject.catch(s).should_not be_valid
end
it "does not report if variable used in a condition" do
s = Source.new %(
def method
a = 1
if a
nil
end
end
)
subject.catch(s).should be_valid
end
it "reports second assignment as useless" do
s = Source.new %(
def method
a = 1
a = a + 1
end
)
subject.catch(s).should_not be_valid
end
it "does not report if variable is referenced in other assignment" do
s = Source.new %(
def method
if f = get_something
@f = f
end
end
)
subject.catch(s).should be_valid
end
it "does not report if variable is referenced in a setter" do
s = Source.new %(
def method
foo = 2
table[foo] ||= "bar"
end
)
subject.catch(s).should be_valid
end
it "does not report if variable is reassigned but not referenced" do
s = Source.new %(
def method
foo = 1
puts foo
foo = 2
end
)
subject.catch(s).should_not be_valid
end
it "does not report if variable is referenced in a call" do
s = Source.new %(
def method
if f = FORMATTER
@formatter = f.new
end
end
)
subject.catch(s).should be_valid
end
it "does not report if a setter is invoked with operator assignment" do
s = Source.new %(
def method
obj = {} of Symbol => Int32
obj[:name] = 3
end
)
subject.catch(s).should be_valid
end
it "does not report if global var" do
s = Source.new %(
def method
$? = 3
end
)
subject.catch(s).should be_valid
end
it "does not report if assignment is referenced in a proc" do
s = Source.new %(
def method
called = false
->() { called = true }
called
end
)
subject.catch(s).should be_valid
end
it "reports if variable is shadowed in inner scope" do
s = Source.new %(
def method
i = 1
3.times do |i|
i + 1
end
end
)
subject.catch(s).should_not be_valid
end
it "does not report if parameter is referenced after the branch" do
s = Source.new %(
def method(param)
3.times do
param = 3
end
param
end
)
subject.catch(s).should be_valid
end
context "op assigns" do
it "does not report if variable is referenced below the op assign" do
s = Source.new %(
def method
a = 1
a += 1
a
end
)
subject.catch(s).should be_valid
end
it "does not report if variable is referenced in op assign few times" do
s = Source.new %(
def method
a = 1
a += 1
a += 1
a = a + 1
a
end
)
subject.catch(s).should be_valid
end
it "reports if variable is not referenced below the op assign" do
s = Source.new %(
def method
a = 1
a += 1
end
)
subject.catch(s).should_not be_valid
end
it "reports rule, location and a message" do
s = Source.new %(
def method
b = 2
a = 3
a += 1
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.last
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:5:13"
issue.message.should eq "Useless assignment to variable `a`"
end
end
context "multi assigns" do
it "does not report if all assigns are referenced" do
s = Source.new %(
def method
a, b = {1, 2}
a + b
end
)
subject.catch(s).should be_valid
end
it "reports if one assign is not referenced" do
s = Source.new %(
def method
a, b = {1, 2}
a
end
)
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.location.to_s.should eq ":3:16"
issue.message.should eq "Useless assignment to variable `b`"
end
it "reports if both assigns are reassigned and useless" do
s = Source.new %(
def method
a, b = {1, 2}
a, b = {3, 4}
end
)
subject.catch(s).should_not be_valid
end
it "reports if both assigns are not referenced" do
s = Source.new %(
def method
a, b = {1, 2}
end
)
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.location.to_s.should eq ":3:13"
issue.message.should eq "Useless assignment to variable `a`"
issue = s.issues.last
issue.location.to_s.should eq ":3:16"
issue.message.should eq "Useless assignment to variable `b`"
end
end
context "top level" do
it "reports if assignment is not referenced" do
s = Source.new %(
a = 1
a = 2
)
subject.catch(s).should_not be_valid
s.issues.size.should eq 2
s.issues.first.location.to_s.should eq ":2:11"
s.issues.last.location.to_s.should eq ":3:11"
end
it "doesn't report if assignments are referenced" do
s = Source.new %(
a = 1
a += 1
a
b, c = {1, 2}
b
c
)
subject.catch(s).should be_valid
end
it "doesn't report if assignment is captured by block" do
s = Source.new %(
a = 1
3.times do
a = 2
end
)
subject.catch(s).should be_valid
end
end
context "branching" do
context "if-then-else" do
it "doesn't report if assignment is consumed by branches" do
s = Source.new %(
def method
a = 0
if something
a = 1
else
a = 2
end
a
end
)
subject.catch(s).should be_valid
end
it "doesn't report if assignment is in one branch" do
s = Source.new %(
def method
a = 0
if something
a = 1
else
nil
end
a
end
)
subject.catch(s).should be_valid
end
it "doesn't report if assignment is in one line branch" do
s = Source.new %(
def method
a = 0
a = 1 if something
a
end
)
subject.catch(s).should be_valid
end
it "reports if assignment is useless in the branch" do
s = Source.new %(
def method(a)
if a
a = 2
end
end
)
subject.catch(s).should_not be_valid
end
it "reports if only last assignment is referenced in a branch" do
s = Source.new %(
def method(a)
a = 1
if a
a = 2
a = 3
end
a
end
)
subject.catch(s).should_not be_valid
s.issues.size.should eq 1
s.issues.first.location.to_s.should eq ":5:17"
end
it "does not report of assignments are referenced in all branches" do
s = Source.new %(
def method
if matches
matches = owner.lookup_matches signature
else
matches = owner.lookup_matches signature
end
matches
end
)
subject.catch(s).should be_valid
end
it "does not report referenced assignments in inner branches" do
s = Source.new %(
def method
has_newline = false
if something
do_something unless false
has_newline = false
else
do_something if true
has_newline = true
end
has_newline
end
)
subject.catch(s).should be_valid
end
end
context "unless-then-else" do
it "doesn't report if assignment is consumed by branches" do
s = Source.new %(
def method
a = 0
unless something
a = 1
else
a = 2
end
a
end
)
subject.catch(s).should be_valid
end
it "reports if there is a useless assignment in a branch" do
s = Source.new %(
def method
a = 0
unless something
a = 1
a = 2
else
a = 2
end
a
end
)
subject.catch(s).should_not be_valid
s.issues.size.should eq 1
s.issues.first.location.to_s.should eq ":5:17"
end
end
context "case" do
it "does not report if assignment is referenced" do
s = Source.new %(
def method(a)
case a
when /foo/
a = 1
when /bar/
a = 2
end
puts a
end
)
subject.catch(s).should be_valid
end
it "reports if assignment is useless" do
s = Source.new %(
def method(a)
case a
when /foo/
a = 1
when /bar/
a = 2
end
end
)
subject.catch(s).should_not be_valid
s.issues.size.should eq 2
s.issues.first.location.to_s.should eq ":5:17"
s.issues.last.location.to_s.should eq ":7:17"
end
it "doesn't report if assignment is referenced in cond" do
s = Source.new %(
def method
a = 2
case a
when /foo/
end
end
)
subject.catch(s).should be_valid
end
end
context "binary operator" do
it "does not report if assignment is referenced" do
s = Source.new %(
def method(a)
(a = 1) && (b = 1)
a + b
end
)
subject.catch(s).should be_valid
end
it "reports if assignment is useless" do
s = Source.new %(
def method(a)
(a = 1) || (b = 1)
a
end
)
subject.catch(s).should_not be_valid
s.issues.size.should eq 1
s.issues.first.location.to_s.should eq ":3:27"
end
end
context "while" do
it "does not report if assignment is referenced" do
s = Source.new %(
def method(a)
while a < 10
a = a + 1
end
a
end
)
subject.catch(s).should be_valid
end
it "reports if assignment is useless" do
s = Source.new %(
def method(a)
while a < 10
b = a
end
end
)
subject.catch(s).should_not be_valid
s.issues.size.should eq 1
s.issues.first.location.to_s.should eq ":4:17"
end
it "does not report if assignment is referenced in a loop" do
s = Source.new %(
def method
a = 3
result = 0
while result < 10
result += a
a = a + 1
end
result
end
)
subject.catch(s).should be_valid
end
it "does not report if assignment is referenced as param in a loop" do
s = Source.new %(
def method(a)
result = 0
while result < 10
result += a
a = a + 1
end
result
end
)
subject.catch(s).should be_valid
end
it "does not report if assignment is referenced in loop and inner branch" do
s = Source.new %(
def method(a)
result = 0
while result < 10
result += a
if result > 0
a = a + 1
else
a = 3
end
end
result
end
)
subject.catch(s).should be_valid
end
it "works properly if there is branch with blank node" do
s = Source.new %(
def visit
count = 0
while true
break if count == 1
case something
when :any
else
:anything_else
end
count += 1
end
end
)
subject.catch(s).should be_valid
end
end
context "until" do
it "does not report if assignment is referenced" do
s = Source.new %(
def method(a)
until a > 10
a = a + 1
end
a
end
)
subject.catch(s).should be_valid
end
it "reports if assignment is useless" do
s = Source.new %(
def method(a)
until a > 10
b = a + 1
end
end
)
subject.catch(s).should_not be_valid
s.issues.size.should eq 1
s.issues.first.location.to_s.should eq ":4:17"
end
end
context "exception handler" do
it "does not report if assignment is referenced in body" do
s = Source.new %(
def method(a)
a = 2
rescue
a
end
)
subject.catch(s).should be_valid
end
it "doesn't report if assignment is referenced in ensure" do
s = Source.new %(
def method(a)
a = 2
ensure
a
end
)
subject.catch(s).should be_valid
end
it "doesn't report if assignment is referenced in else" do
s = Source.new %(
def method(a)
a = 2
rescue
else
a
end
)
subject.catch(s).should be_valid
end
it "reports if assignment is useless" do
s = Source.new %(
def method(a)
rescue
a = 2
end
)
subject.catch(s).should_not be_valid
s.issues.size.should eq 1
s.issues.first.location.to_s.should eq ":4:15"
end
end
end
context "macro" do
it "doesn't report if assignment is referenced in macro" do
s = Source.new %(
def method
a = 2
{% if flag?(:bits64) %}
a.to_s
{% else %}
a
{% end %}
end
)
subject.catch(s).should be_valid
end
it "doesn't report referenced assignments in macro literal" do
s = Source.new %(
def method
a = 2
{% if flag?(:bits64) %}
a = 3
{% else %}
a = 4
{% end %}
puts a
end
)
subject.catch(s).should be_valid
end
it "doesn't report if assignment is referenced in macro def" do
s = Source.new %(
macro macro_call
puts x
end
def foo
x = 1
macro_call
end
)
subject.catch(s).should be_valid
end
it "reports if assignment is referenced in macro def in a different scope" do
s = Source.new %(
class Foo
def foo
x = 1
end
end
class Bar
macro macro_call
puts x
end
end
)
subject.catch(s).should_not be_valid
end
it "doesn't report if assignment is referenced in a macro below" do
s = Source.new %(
class Foo
def foo
a = 1
macro_call
end
macro macro_call
puts a
end
end
)
subject.catch(s).should be_valid
end
end
context "uninitialized" do
it "reports if uninitialized assignment is not referenced at a top level" do
s = Source.new %(
a = uninitialized U
)
subject.catch(s).should_not be_valid
end
it "reports if uninitialized assignment is not referenced in a method" do
s = Source.new %(
def foo
a = uninitialized U
end
)
subject.catch(s).should_not be_valid
end
it "doesn't report if uninitialized assignment is referenced" do
s = Source.new %(
def foo
a = uninitialized U
a
end
)
subject.catch(s).should be_valid
end
end
end
end

View file

@ -0,0 +1,45 @@
require "../../../spec_helper"
module Ameba::Rule::Lint
subject = UselessConditionInWhen.new
describe UselessConditionInWhen do
it "passes if there is not useless condition" do
s = Source.new %(
case
when utc?
io << " UTC"
when local?
Format.new(" %:z").format(self, io) if utc?
end
)
subject.catch(s).should be_valid
end
it "fails if there is useless if condition" do
s = Source.new %(
case
when utc?
io << " UTC" if utc?
end
)
subject.catch(s).should_not be_valid
end
it "reports rule, location and message" do
s = Source.new %(
case
when String
puts "hello"
when can_generate?
generate if can_generate?
end
), "source.cr"
subject.catch(s).should_not be_valid
issue = s.issues.first
issue.rule.should_not be_nil
issue.location.to_s.should eq "source.cr:6:23"
issue.message.should eq "Useless condition in when detected"
end
end
end