mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
695 lines
16 KiB
Crystal
695 lines
16 KiB
Crystal
require "../../../spec_helper"
|
|
|
|
module Ameba::Rule::Lint
|
|
subject = UnreachableCode.new
|
|
|
|
describe UnreachableCode do
|
|
context "return" do
|
|
it "reports if there is unreachable code after return" do
|
|
s = Source.new %(
|
|
def foo
|
|
a = 1
|
|
return false
|
|
b = 2
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":4:3"
|
|
end
|
|
|
|
it "doesn't report if there is return in if" do
|
|
s = Source.new %(
|
|
def foo
|
|
a = 1
|
|
return false if bar
|
|
b = 2
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if there are returns in if-then-else" do
|
|
s = Source.new %(
|
|
if a > 0
|
|
return :positive
|
|
else
|
|
return :negative
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if there is no else in if" do
|
|
s = Source.new %(
|
|
if a > 0
|
|
return :positive
|
|
end
|
|
:reachable
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report return in on-line if" do
|
|
s = Source.new %(
|
|
return :positive if a > 0
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if return is used in a block" do
|
|
s = Source.new %(
|
|
def foo
|
|
bar = obj.try do
|
|
if something
|
|
a = 1
|
|
end
|
|
return nil
|
|
end
|
|
|
|
bar
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if there is unreachable code after if-then-else" do
|
|
s = Source.new %(
|
|
def foo
|
|
if a > 0
|
|
return :positive
|
|
else
|
|
return :negative
|
|
end
|
|
|
|
:unreachable
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":8:3"
|
|
end
|
|
|
|
it "reports if there is unreachable code after if-then-else-if" do
|
|
s = Source.new %(
|
|
def foo
|
|
if a > 0
|
|
return :positive
|
|
elsif a != 0
|
|
return :negative
|
|
else
|
|
return :zero
|
|
end
|
|
|
|
:unreachable
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":10:3"
|
|
end
|
|
|
|
it "doesn't report if there is no unreachable code after if-then-else" do
|
|
s = Source.new %(
|
|
def foo
|
|
if a > 0
|
|
return :positive
|
|
else
|
|
return :negative
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if there is no unreachable in inner branch" do
|
|
s = Source.new %(
|
|
def foo
|
|
if a > 0
|
|
return :positive if a != 1
|
|
else
|
|
return :negative
|
|
end
|
|
|
|
:not_unreachable
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if there is no unreachable in exception handler" do
|
|
s = Source.new %(
|
|
def foo
|
|
puts :bar
|
|
rescue Exception
|
|
raise "Error!"
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if there is multiple conditions with return" do
|
|
s = Source.new %(
|
|
if :foo
|
|
if :bar
|
|
return :foobar
|
|
else
|
|
return :foobaz
|
|
end
|
|
elsif :fox
|
|
return :foofox
|
|
end
|
|
|
|
return :reachable
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if there is unreachable code after unless" do
|
|
s = Source.new %(
|
|
unless :foo
|
|
return :bar
|
|
else
|
|
return :foo
|
|
end
|
|
|
|
:unreachable
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":7:1"
|
|
end
|
|
|
|
it "doesn't report if there is no unreachable code after unless" do
|
|
s = Source.new %(
|
|
unless :foo
|
|
return :bar
|
|
end
|
|
|
|
:reachable
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "binary op" do
|
|
it "reports unreachable code in a binary operator" do
|
|
s = Source.new %(
|
|
(return 22) && puts "a"
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":1:16"
|
|
end
|
|
|
|
it "reports unreachable code in inner binary operator" do
|
|
s = Source.new %(
|
|
do_something || (return 22) && puts "a"
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":1:32"
|
|
end
|
|
|
|
it "reports unreachable code after the binary op" do
|
|
s = Source.new %(
|
|
(return 22) && break
|
|
:unreachable
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":2:1"
|
|
end
|
|
|
|
it "doesn't report if return is not the right" do
|
|
s = Source.new %(
|
|
puts "a" && return
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report unreachable code in multiple binary expressions" do
|
|
s = Source.new %(
|
|
foo || bar || baz
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "case" do
|
|
it "reports if there is unreachable code after case" do
|
|
s = Source.new %(
|
|
def foo
|
|
case cond
|
|
when 1
|
|
something
|
|
return
|
|
when 2
|
|
something2
|
|
return
|
|
else
|
|
something3
|
|
return
|
|
end
|
|
:unreachable
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":13:3"
|
|
end
|
|
|
|
it "doesn't report if case does not have else" do
|
|
s = Source.new %(
|
|
def foo
|
|
case cond
|
|
when 1
|
|
something
|
|
return
|
|
when 2
|
|
something2
|
|
return
|
|
end
|
|
:reachable
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if one when does not return" do
|
|
s = Source.new %(
|
|
def foo
|
|
case cond
|
|
when 1
|
|
something
|
|
return
|
|
when 2
|
|
something2
|
|
else
|
|
something3
|
|
return
|
|
end
|
|
:reachable
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "exception handler" do
|
|
it "reports unreachable code if it returns in body and rescues" do
|
|
s = Source.new %(
|
|
def foo
|
|
begin
|
|
return false
|
|
rescue Error
|
|
return false
|
|
rescue Exception
|
|
return false
|
|
end
|
|
:unreachable
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":9:3"
|
|
end
|
|
|
|
it "reports unreachable code if it returns in rescues and else" do
|
|
s = Source.new %(
|
|
def foo
|
|
begin
|
|
do_something
|
|
rescue Error
|
|
return :error
|
|
else
|
|
return true
|
|
end
|
|
:unreachable
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":9:3"
|
|
end
|
|
|
|
it "doesn't report if there is no else and ensure doesn't return" do
|
|
s = Source.new %(
|
|
def foo
|
|
begin
|
|
return false
|
|
rescue Error
|
|
puts "error"
|
|
rescue Exception
|
|
return false
|
|
end
|
|
:reachable
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if there is no else and body doesn't return" do
|
|
s = Source.new %(
|
|
def foo
|
|
begin
|
|
do_something
|
|
rescue Error
|
|
return true
|
|
rescue Exception
|
|
return false
|
|
end
|
|
:reachable
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if there is else and ensure doesn't return" do
|
|
s = Source.new %(
|
|
def foo
|
|
begin
|
|
do_something
|
|
rescue Error
|
|
puts "yo"
|
|
else
|
|
return true
|
|
end
|
|
:reachable
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if there is else and it doesn't return" do
|
|
s = Source.new %(
|
|
def foo
|
|
begin
|
|
do_something
|
|
rescue Error
|
|
return false
|
|
else
|
|
puts "yo"
|
|
end
|
|
:reachable
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "reports if there is unreachable code in rescue" do
|
|
s = Source.new %(
|
|
def method
|
|
rescue
|
|
return 22
|
|
:unreachable
|
|
end
|
|
)
|
|
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":4:3"
|
|
end
|
|
end
|
|
|
|
context "while/until" do
|
|
it "does not report if there is no unreachable code after while" do
|
|
s = Source.new %(
|
|
def method
|
|
while something
|
|
if :foo
|
|
return :foo
|
|
else
|
|
return :foobar
|
|
end
|
|
end
|
|
:unreachable
|
|
end
|
|
)
|
|
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "does not report if there is no unreachable code after until" do
|
|
s = Source.new %(
|
|
def method
|
|
until something
|
|
if :foo
|
|
return :foo
|
|
else
|
|
return :foobar
|
|
end
|
|
end
|
|
:unreachable
|
|
end
|
|
)
|
|
|
|
subject.catch(s).should be_valid
|
|
end
|
|
|
|
it "doesn't report if there is reachable code after while with break" do
|
|
s = Source.new %(
|
|
while something
|
|
break
|
|
end
|
|
:reachable
|
|
)
|
|
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "rescue" do
|
|
it "reports unreachable code in rescue" do
|
|
s = Source.new %(
|
|
begin
|
|
|
|
rescue e
|
|
raise e
|
|
:unreachable
|
|
end
|
|
)
|
|
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":5:3"
|
|
end
|
|
|
|
it "doesn't report if there is no unreachable code in rescue" do
|
|
s = Source.new %(
|
|
begin
|
|
|
|
rescue e
|
|
raise e
|
|
end
|
|
)
|
|
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "when" do
|
|
it "reports unreachable code in when" do
|
|
s = Source.new %(
|
|
case
|
|
when valid?
|
|
return 22
|
|
:unreachable
|
|
else
|
|
|
|
end
|
|
)
|
|
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":4:3"
|
|
end
|
|
|
|
it "doesn't report if there is no unreachable code in when" do
|
|
s = Source.new %(
|
|
case
|
|
when valid?
|
|
return 22
|
|
else
|
|
end
|
|
)
|
|
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "break" do
|
|
it "reports if there is unreachable code after break" do
|
|
s = Source.new %(
|
|
def foo
|
|
loop do
|
|
break
|
|
a = 1
|
|
end
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":4:5"
|
|
end
|
|
|
|
it "doesn't report if break is in a condition" do
|
|
s = Source.new %(
|
|
a = -100
|
|
while true
|
|
break if a > 0
|
|
a += 1
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "next" do
|
|
it "reports if there is unreachable code after next" do
|
|
s = Source.new %(
|
|
a = 1
|
|
while a < 5
|
|
next
|
|
puts a
|
|
end
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":4:3"
|
|
end
|
|
|
|
it "doesn't report if next is in a condition" do
|
|
s = Source.new %(
|
|
a = 1
|
|
while a < 5
|
|
if a == 3
|
|
next
|
|
end
|
|
puts a
|
|
end
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "raise" do
|
|
it "reports if there is unreachable code after raise" do
|
|
s = Source.new %(
|
|
a = 1
|
|
raise "exception"
|
|
b = 2
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":3:1"
|
|
end
|
|
|
|
it "doesn't report if raise is in a condition" do
|
|
s = Source.new %(
|
|
a = 1
|
|
raise "exception" if a > 0
|
|
b = 2
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "exit" do
|
|
it "reports if there is unreachable code after exit without args" do
|
|
s = Source.new %(
|
|
a = 1
|
|
exit
|
|
b = 2
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":3:1"
|
|
end
|
|
|
|
it "reports if there is unreachable code after exit with exit code" do
|
|
s = Source.new %(
|
|
a = 1
|
|
exit 1
|
|
b = 2
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":3:1"
|
|
end
|
|
|
|
it "doesn't report if exit is in a condition" do
|
|
s = Source.new %(
|
|
a = 1
|
|
exit if a > 0
|
|
b = 2
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
context "abort" do
|
|
it "reports if there is unreachable code after abort with one argument" do
|
|
s = Source.new %(
|
|
a = 1
|
|
abort "abort"
|
|
b = 2
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":3:1"
|
|
end
|
|
|
|
it "reports if there is unreachable code after abort with two args" do
|
|
s = Source.new %(
|
|
a = 1
|
|
abort "abort", 1
|
|
b = 2
|
|
)
|
|
subject.catch(s).should_not be_valid
|
|
|
|
issue = s.issues.first
|
|
issue.location.to_s.should eq ":3:1"
|
|
end
|
|
|
|
it "doesn't report if abort is in a condition" do
|
|
s = Source.new %(
|
|
a = 1
|
|
abort "abort" if a > 0
|
|
b = 2
|
|
)
|
|
subject.catch(s).should be_valid
|
|
end
|
|
end
|
|
|
|
it "reports message, rule, location" do
|
|
s = Source.new %(
|
|
return
|
|
:unreachable
|
|
), "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:1"
|
|
issue.end_location.to_s.should eq "source.cr:2:12"
|
|
issue.message.should eq "Unreachable code detected"
|
|
end
|
|
end
|
|
end
|