shard-ameba/spec/ameba/rule/lint/unreachable_code_spec.cr
2021-11-10 09:16:14 +02:00

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