mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
Handle raise, exit, abort in unreachable code
This commit is contained in:
parent
67d76116f7
commit
eca0f3f350
2 changed files with 160 additions and 7 deletions
|
@ -33,13 +33,46 @@ module Ameba::Rule::Lint
|
||||||
it "doesn't report if there are returns in if-then-else" do
|
it "doesn't report if there are returns in if-then-else" do
|
||||||
s = Source.new %(
|
s = Source.new %(
|
||||||
if a > 0
|
if a > 0
|
||||||
return :positivie
|
return :positive
|
||||||
else
|
else
|
||||||
return :negative
|
return :negative
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
subject.catch(s).should be_valid
|
subject.catch(s).should be_valid
|
||||||
end
|
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
|
||||||
|
|
||||||
|
pending "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:4"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "break" do
|
context "break" do
|
||||||
|
@ -98,5 +131,98 @@ module Ameba::Rule::Lint
|
||||||
subject.catch(s).should be_valid
|
subject.catch(s).should be_valid
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,7 @@ module Ameba::AST
|
||||||
# AST Visitor that traverses all the flow expressions.
|
# AST Visitor that traverses all the flow expressions.
|
||||||
class FlowExpressionVisitor < BaseVisitor
|
class FlowExpressionVisitor < BaseVisitor
|
||||||
@node_stack = Array(Crystal::ASTNode).new
|
@node_stack = Array(Crystal::ASTNode).new
|
||||||
|
@flow_expression : FlowExpression?
|
||||||
|
|
||||||
def initialize(@rule, @source)
|
def initialize(@rule, @source)
|
||||||
@source.ast.accept self
|
@source.ast.accept self
|
||||||
|
@ -14,20 +15,46 @@ module Ameba::AST
|
||||||
end
|
end
|
||||||
|
|
||||||
def end_visit(node)
|
def end_visit(node)
|
||||||
@node_stack.pop
|
if @flow_expression.nil?
|
||||||
|
@node_stack.pop unless @node_stack.empty?
|
||||||
|
else
|
||||||
|
@flow_expression = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def visit(node : Crystal::ControlExpression)
|
def visit(node : Crystal::ControlExpression)
|
||||||
if parent_node = @node_stack.last?
|
on_flow_expression_start(node)
|
||||||
flow_expression = FlowExpression.new(node, parent_node)
|
|
||||||
@rule.test @source, node, flow_expression
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def visit(node : Crystal::Call)
|
||||||
|
if raise?(node) || exit?(node) || abort?(node)
|
||||||
|
on_flow_expression_start(node)
|
||||||
|
else
|
||||||
|
@node_stack.push node
|
||||||
end
|
end
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def end_visit(node : Crystal::ControlExpression)
|
private def on_flow_expression_start(node)
|
||||||
#
|
if parent_node = @node_stack.last?
|
||||||
|
@flow_expression = FlowExpression.new(node, parent_node)
|
||||||
|
@rule.test @source, node, @flow_expression
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def raise?(node)
|
||||||
|
node.name == "raise" && node.args.size == 1 && node.obj.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
private def exit?(node)
|
||||||
|
node.name == "exit" && node.args.size <= 1 && node.obj.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
private def abort?(node)
|
||||||
|
node.name == "abort" && node.args.size <= 2 && node.obj.nil?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue