Specs cleanup

This commit is contained in:
Sijawusz Pur Rahnama 2022-12-19 18:03:11 +01:00
parent e6ebca7a5b
commit 8112dddc8f
24 changed files with 267 additions and 251 deletions

View file

@ -10,29 +10,29 @@ module Ameba::AST
describe ".of" do describe ".of" do
context "Crystal::If" do context "Crystal::If" do
it "constructs a branch in If.cond" do it "constructs a branch in If.cond" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method def method
if a = get_something # --> Crystal::Assign if a = get_something # --> Crystal::Assign
puts a puts a
end end
end end
) CRYSTAL
branch.to_s.should eq "a = get_something" branch.to_s.should eq "a = get_something"
end end
it "constructs a branch in If.then" do it "constructs a branch in If.then" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method def method
if true if true
a = 2 # --> Crystal::Assign a = 2 # --> Crystal::Assign
end end
end end
) CRYSTAL
branch.to_s.should eq "a = 2" branch.to_s.should eq "a = 2"
end end
it "constructs a branch in If.else" do it "constructs a branch in If.else" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method def method
if true if true
nil nil
@ -40,45 +40,45 @@ module Ameba::AST
a = 2 # --> Crystal::Assign a = 2 # --> Crystal::Assign
end end
end end
) CRYSTAL
branch.to_s.should eq "a = 2" branch.to_s.should eq "a = 2"
end end
it "constructs a branch in inline If" do it "constructs a branch in inline If" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
a = 0 if a == 2 # --> Crystal::Assign a = 0 if a == 2 # --> Crystal::Assign
end end
) CRYSTAL
branch.to_s.should eq "a = 0" branch.to_s.should eq "a = 0"
end end
end end
context "Crystal::Unless" do context "Crystal::Unless" do
it "constructs a branch in Unless.cond" do it "constructs a branch in Unless.cond" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method def method
unless a = get_something # --> Crystal::Assign unless a = get_something # --> Crystal::Assign
puts a puts a
end end
end end
) CRYSTAL
branch.to_s.should eq "a = get_something" branch.to_s.should eq "a = get_something"
end end
it "constructs a branch in Unless.then" do it "constructs a branch in Unless.then" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method def method
unless true unless true
a = 2 # --> Crystal::Assign a = 2 # --> Crystal::Assign
end end
end end
) CRYSTAL
branch.to_s.should eq "a = 2" branch.to_s.should eq "a = 2"
end end
it "constructs a new branch in Unless.else" do it "constructs a new branch in Unless.else" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method def method
unless true unless true
nil nil
@ -86,188 +86,188 @@ module Ameba::AST
a = 2 # --> Crystal::Assign a = 2 # --> Crystal::Assign
end end
end end
) CRYSTAL
branch.to_s.should eq "a = 2" branch.to_s.should eq "a = 2"
end end
it "constructs a branch in inline Unless" do it "constructs a branch in inline Unless" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
(a = 0; b = 3) unless a == 2 # --> Crystal::Expressions (a = 0; b = 3) unless a == 2 # --> Crystal::Expressions
end end
) CRYSTAL
branch.to_s.should eq "(a = 0\nb = 3)" branch.to_s.should eq "(a = 0\nb = 3)"
end end
end end
context "Crystal::BinaryOp" do context "Crystal::BinaryOp" do
it "constructs a branch in left node" do it "constructs a branch in left node" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
(a = 2) && do_something (a = 2) && do_something
end end
) CRYSTAL
branch.to_s.should eq "(a = 2)" branch.to_s.should eq "(a = 2)"
end end
it "constructs a branch in right node" do it "constructs a branch in right node" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
do_something || (a = 0) do_something || (a = 0)
end end
) CRYSTAL
branch.to_s.should eq "(a = 0)" branch.to_s.should eq "(a = 0)"
end end
end end
context "Crystal::Case" do context "Crystal::Case" do
it "constructs a branch in cond" do it "constructs a branch in cond" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
case (a = 2) case (a = 2)
when true then nil when true then nil
end end
end end
) CRYSTAL
branch.to_s.should eq "(a = 2)" branch.to_s.should eq "(a = 2)"
end end
it "constructs a branch in when" do it "constructs a branch in when" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
case a case a
when a = 3 then nil when a = 3 then nil
end end
end end
) CRYSTAL
branch.to_s.should eq "when a = 3\n nil\n" branch.to_s.should eq "when a = 3\n nil\n"
end end
it "constructs a branch in else" do it "constructs a branch in else" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
case a case a
when true then nil when true then nil
else a = 4 else a = 4
end end
end end
) CRYSTAL
branch.to_s.should eq "a = 4" branch.to_s.should eq "a = 4"
end end
end end
context "Crystal::While" do context "Crystal::While" do
it "constructs a branch in cond" do it "constructs a branch in cond" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
while a = 1 while a = 1
nil nil
end end
end end
) CRYSTAL
branch.to_s.should eq "a = 1" branch.to_s.should eq "a = 1"
end end
it "constructs a branch in body" do it "constructs a branch in body" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
while true while true
b = (a = 1) b = (a = 1)
end end
end end
) CRYSTAL
branch.to_s.should eq "b = (a = 1)" branch.to_s.should eq "b = (a = 1)"
end end
end end
context "Crystal::Until" do context "Crystal::Until" do
it "constructs a branch in cond" do it "constructs a branch in cond" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
until a = 1 until a = 1
nil nil
end end
end end
) CRYSTAL
branch.to_s.should eq "a = 1" branch.to_s.should eq "a = 1"
end end
it "constructs a branch in body" do it "constructs a branch in body" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
until false until false
b = (a = 1) b = (a = 1)
end end
end end
) CRYSTAL
branch.to_s.should eq "b = (a = 1)" branch.to_s.should eq "b = (a = 1)"
end end
end end
context "Crystal::ExceptionHandler" do context "Crystal::ExceptionHandler" do
it "constructs a branch in body" do it "constructs a branch in body" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
a = 1 a = 1
rescue rescue
nil nil
end end
) CRYSTAL
branch.to_s.should eq "a = 1" branch.to_s.should eq "a = 1"
end end
it "constructs a branch in a rescue" do it "constructs a branch in a rescue" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
rescue rescue
a = 1 a = 1
end end
) CRYSTAL
branch.to_s.should eq "a = 1" branch.to_s.should eq "a = 1"
end end
it "constructs a branch in else" do it "constructs a branch in else" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
rescue rescue
else else
a = 1 a = 1
end end
) CRYSTAL
branch.to_s.should eq "a = 1" branch.to_s.should eq "a = 1"
end end
it "constructs a branch in ensure" do it "constructs a branch in ensure" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
rescue rescue
ensure ensure
a = 1 a = 1
end end
) CRYSTAL
branch.to_s.should eq "a = 1" branch.to_s.should eq "a = 1"
end end
end end
context "Crystal::MacroIf" do context "Crystal::MacroIf" do
it "constructs a branch in cond" do it "constructs a branch in cond" do
branch = branch_of_assign_in_def %( branch = branch_of_assign_in_def <<-CRYSTAL
def method(a) def method(a)
{% if a = 2 %} {% if a = 2 %}
{% end %} {% end %}
end end
) CRYSTAL
branch.to_s.should eq "a = 2" branch.to_s.should eq "a = 2"
end end
it "constructs a branch in then" do it "constructs a branch in then" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def method(a) def method(a)
{% if true %} {% if true %}
a = 2 a = 2
{% end %} {% end %}
end end
) CRYSTAL
branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first) branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first)
branch.to_s.strip.should eq "a = 2" branch.to_s.strip.should eq "a = 2"
end end
@ -275,24 +275,24 @@ module Ameba::AST
context "Crystal::MacroFor" do context "Crystal::MacroFor" do
it "constructs a branch in body" do it "constructs a branch in body" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def method(a) def method(a)
{% for x in [1, 2, 3] %} {% for x in [1, 2, 3] %}
a = 2 a = 2
{% end %} {% end %}
end end
) CRYSTAL
branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first) branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first)
branch.to_s.strip.should eq "a = 2" branch.to_s.strip.should eq "a = 2"
end end
end end
it "returns nil if branch does not exist" do it "returns nil if branch does not exist" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def method def method
a = 2 a = 2
end end
) CRYSTAL
branch = Branch.of(nodes.assign_nodes.first, nodes.def_nodes.first) branch = Branch.of(nodes.assign_nodes.first, nodes.def_nodes.first)
branch.should be_nil branch.should be_nil
end end
@ -300,11 +300,11 @@ module Ameba::AST
describe "#initialize" do describe "#initialize" do
it "creates new branch" do it "creates new branch" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
if true if true
a = 2 a = 2
end end
) CRYSTAL
branchable = Branchable.new nodes.if_nodes.first branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable branch = Branch.new nodes.assign_nodes.first, branchable
branch.node.should_not be_nil branch.node.should_not be_nil
@ -313,22 +313,22 @@ module Ameba::AST
describe "delegation" do describe "delegation" do
it "delegates to_s to node" do it "delegates to_s to node" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
if true if true
a = 2 a = 2
end end
) CRYSTAL
branchable = Branchable.new nodes.if_nodes.first branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable branch = Branch.new nodes.assign_nodes.first, branchable
branch.to_s.should eq branch.node.to_s branch.to_s.should eq branch.node.to_s
end end
it "delegates locations to node" do it "delegates locations to node" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
if true if true
a = 2 a = 2
end end
) CRYSTAL
branchable = Branchable.new nodes.if_nodes.first branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable branch = Branch.new nodes.assign_nodes.first, branchable
branch.location.should eq branch.node.location branch.location.should eq branch.node.location
@ -338,22 +338,22 @@ module Ameba::AST
describe "#in_loop?" do describe "#in_loop?" do
it "returns true if branch is in a loop" do it "returns true if branch is in a loop" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
while true while true
a = 1 a = 1
end end
) CRYSTAL
branchable = Branchable.new nodes.while_nodes.first branchable = Branchable.new nodes.while_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable branch = Branch.new nodes.assign_nodes.first, branchable
branch.in_loop?.should be_true branch.in_loop?.should be_true
end end
it "returns false if branch is not in a loop" do it "returns false if branch is not in a loop" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
if a > 2 if a > 2
a = 1 a = 1
end end
) CRYSTAL
branchable = Branchable.new nodes.if_nodes.first branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable branch = Branch.new nodes.assign_nodes.first, branchable
branch.in_loop?.should be_false branch.in_loop?.should be_false

View file

@ -4,20 +4,20 @@ module Ameba::AST
describe Branchable do describe Branchable do
describe "#initialize" do describe "#initialize" do
it "creates a new branchable" do it "creates a new branchable" do
branchable = Branchable.new as_node %(a = 2 if true) branchable = Branchable.new as_node "a = 2 if true"
branchable.node.should_not be_nil branchable.node.should_not be_nil
end end
end end
describe "delegation" do describe "delegation" do
it "delegates to_s to @node" do it "delegates to_s to @node" do
node = as_node %(a = 2 if true) node = as_node "a = 2 if true"
branchable = Branchable.new node branchable = Branchable.new node
branchable.to_s.should eq node.to_s branchable.to_s.should eq node.to_s
end end
it "delegates locations to @node" do it "delegates locations to @node" do
node = as_node %(a = 2 if true) node = as_node "a = 2 if true"
branchable = Branchable.new node branchable = Branchable.new node
branchable.location.should eq node.location branchable.location.should eq node.location
branchable.end_location.should eq node.end_location branchable.end_location.should eq node.end_location
@ -26,22 +26,22 @@ module Ameba::AST
describe "#loop?" do describe "#loop?" do
it "returns true if it is a while loop" do it "returns true if it is a while loop" do
branchable = Branchable.new as_node %(while true; a = 2; end) branchable = Branchable.new as_node "while true; a = 2; end"
branchable.loop?.should be_true branchable.loop?.should be_true
end end
it "returns true if it is the until loop" do it "returns true if it is the until loop" do
branchable = Branchable.new as_node %(until false; a = 2; end) branchable = Branchable.new as_node "until false; a = 2; end"
branchable.loop?.should be_true branchable.loop?.should be_true
end end
it "returns true if it is loop" do it "returns true if it is loop" do
branchable = Branchable.new as_node %(loop {}) branchable = Branchable.new as_node "loop {}"
branchable.loop?.should be_true branchable.loop?.should be_true
end end
it "returns false otherwise" do it "returns false otherwise" do
branchable = Branchable.new as_node %(a = 2 if true) branchable = Branchable.new as_node "a = 2 if true"
branchable.loop?.should be_false branchable.loop?.should be_false
end end
end end

View file

@ -18,7 +18,7 @@ module Ameba::AST
end end
it "delegates locations to @node" do it "delegates locations to @node" do
node = as_node %(break if true) node = as_node("break if true")
flow_expression = FlowExpression.new node, false flow_expression = FlowExpression.new node, false
flow_expression.location.should eq node.location flow_expression.location.should eq node.location
flow_expression.end_location.should eq node.end_location flow_expression.end_location.should eq node.end_location
@ -27,20 +27,20 @@ module Ameba::AST
describe "#unreachable_nodes" do describe "#unreachable_nodes" do
it "returns unreachable nodes" do it "returns unreachable nodes" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def foobar def foobar
return return
a = 1 a = 1
a = 2 a = 2
end end
) CRYSTAL
node = nodes.expressions_nodes.first node = nodes.expressions_nodes.first
flow_expression = FlowExpression.new node, false flow_expression = FlowExpression.new node, false
flow_expression.unreachable_nodes.should eq nodes.assign_nodes flow_expression.unreachable_nodes.should eq nodes.assign_nodes
end end
it "returns nil if there is no unreachable node after loop" do it "returns nil if there is no unreachable node after loop" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def run def run
idx = items.size - 1 idx = items.size - 1
while 0 <= idx while 0 <= idx
@ -49,19 +49,19 @@ module Ameba::AST
puts "foo" puts "foo"
end end
) CRYSTAL
node = nodes.expressions_nodes.first node = nodes.expressions_nodes.first
flow_expression = FlowExpression.new node, false flow_expression = FlowExpression.new node, false
flow_expression.unreachable_nodes.empty?.should eq true flow_expression.unreachable_nodes.empty?.should eq true
end end
it "returns nil if there is no unreachable node" do it "returns nil if there is no unreachable node" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def foobar def foobar
a = 1 a = 1
return a return a
end end
) CRYSTAL
node = nodes.expressions_nodes.first node = nodes.expressions_nodes.first
flow_expression = FlowExpression.new node, false flow_expression = FlowExpression.new node, false
flow_expression.unreachable_nodes.empty?.should eq true flow_expression.unreachable_nodes.empty?.should eq true

View file

@ -49,14 +49,14 @@ module Ameba::AST
describe "#references?" do describe "#references?" do
it "returns true if current scope references variable" do it "returns true if current scope references variable" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def method def method
a = 2 a = 2
block do block do
3.times { |i| a = a + i } 3.times { |i| a = a + i }
end end
end end
) CRYSTAL
scope = Scope.new nodes.def_nodes.first scope = Scope.new nodes.def_nodes.first
var_node = nodes.var_nodes.first var_node = nodes.var_nodes.first
scope.add_variable var_node scope.add_variable var_node
@ -69,14 +69,14 @@ module Ameba::AST
end end
it "returns false if inner scopes are not checked" do it "returns false if inner scopes are not checked" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def method def method
a = 2 a = 2
block do block do
3.times { |i| a = a + i } 3.times { |i| a = a + i }
end end
end end
) CRYSTAL
scope = Scope.new nodes.def_nodes.first scope = Scope.new nodes.def_nodes.first
var_node = nodes.var_nodes.first var_node = nodes.var_nodes.first
scope.add_variable var_node scope.add_variable var_node
@ -89,7 +89,7 @@ module Ameba::AST
end end
it "returns false if current scope does not reference variable" do it "returns false if current scope does not reference variable" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def method def method
a = 2 a = 2
block do block do
@ -97,7 +97,7 @@ module Ameba::AST
3.times { |i| b = b + i } 3.times { |i| b = b + i }
end end
end end
) CRYSTAL
scope = Scope.new nodes.def_nodes.first scope = Scope.new nodes.def_nodes.first
var_node = nodes.var_nodes.first var_node = nodes.var_nodes.first
scope.add_variable var_node scope.add_variable var_node
@ -150,57 +150,53 @@ module Ameba::AST
describe "#block?" do describe "#block?" do
it "returns true if Crystal::Block" do it "returns true if Crystal::Block" do
nodes = as_nodes %( nodes = as_nodes("3.times {}")
3.times {}
)
scope = Scope.new nodes.block_nodes.first scope = Scope.new nodes.block_nodes.first
scope.block?.should be_true scope.block?.should be_true
end end
it "returns false otherwise" do it "returns false otherwise" do
scope = Scope.new as_node "a = 1" scope = Scope.new as_node("a = 1")
scope.block?.should be_false scope.block?.should be_false
end end
end end
describe "#spawn_block?" do describe "#spawn_block?" do
it "returns true if a node is a spawn block" do it "returns true if a node is a spawn block" do
nodes = as_nodes %( nodes = as_nodes("spawn {}")
spawn {}
)
scope = Scope.new nodes.block_nodes.first scope = Scope.new nodes.block_nodes.first
scope.spawn_block?.should be_true scope.spawn_block?.should be_true
end end
it "returns false otherwise" do it "returns false otherwise" do
scope = Scope.new as_node "a = 1" scope = Scope.new as_node("a = 1")
scope.spawn_block?.should be_false scope.spawn_block?.should be_false
end end
end end
describe "#in_macro?" do describe "#in_macro?" do
it "returns true if Crystal::Macro" do it "returns true if Crystal::Macro" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
macro included macro included
end end
) CRYSTAL
scope = Scope.new nodes.macro_nodes.first scope = Scope.new nodes.macro_nodes.first
scope.in_macro?.should be_true scope.in_macro?.should be_true
end end
it "returns true if node is nested to Crystal::Macro" do it "returns true if node is nested to Crystal::Macro" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
macro included macro included
{{ @type.each do |type| a = type end }} {{ @type.each do |type| a = type end }}
end end
) CRYSTAL
outer_scope = Scope.new nodes.macro_nodes.first outer_scope = Scope.new nodes.macro_nodes.first
scope = Scope.new nodes.block_nodes.first, outer_scope scope = Scope.new nodes.block_nodes.first, outer_scope
scope.in_macro?.should be_true scope.in_macro?.should be_true
end end
it "returns false otherwise" do it "returns false otherwise" do
scope = Scope.new as_node "a = 1" scope = Scope.new as_node("a = 1")
scope.in_macro?.should be_false scope.in_macro?.should be_false
end end
end end

View file

@ -41,15 +41,14 @@ module Ameba::AST
describe "#branch" do describe "#branch" do
it "returns the branch of the assignment" do it "returns the branch of the assignment" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def method(a) def method(a)
if a if a
a = 3 # --> Crystal::Expressions a = 3 # --> Crystal::Expressions
puts a puts a
end end
end end
) CRYSTAL
scope = Scope.new nodes.def_nodes.first scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope) variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
@ -58,7 +57,7 @@ module Ameba::AST
end end
it "returns inner branch" do it "returns inner branch" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def method(a, b) def method(a, b)
if a if a
if b if b
@ -66,7 +65,7 @@ module Ameba::AST
end end
end end
end end
) CRYSTAL
scope = Scope.new nodes.def_nodes.first scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope) variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
@ -75,12 +74,11 @@ module Ameba::AST
end end
it "returns nil if assignment does not have a branch" do it "returns nil if assignment does not have a branch" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def method(a) def method(a)
a = 2 a = 2
end end
) CRYSTAL
scope = Scope.new nodes.def_nodes.first scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope) variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
@ -90,12 +88,11 @@ module Ameba::AST
describe "#transformed?" do describe "#transformed?" do
it "returns false if the assignment is not transformed by the compiler" do it "returns false if the assignment is not transformed by the compiler" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def method(a) def method(a)
a = 2 a = 2
end end
) CRYSTAL
scope = Scope.new nodes.def_nodes.first scope = Scope.new nodes.def_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope) variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)
@ -103,11 +100,10 @@ module Ameba::AST
end end
it "returns true if the assignment is transformed by the compiler" do it "returns true if the assignment is transformed by the compiler" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
array.each do |(a, b)| array.each do |(a, b)|
end end
) CRYSTAL
scope = Scope.new nodes.block_nodes.first scope = Scope.new nodes.block_nodes.first
variable = Variable.new(nodes.var_nodes.first, scope) variable = Variable.new(nodes.var_nodes.first, scope)
assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) assignment = Assignment.new(nodes.assign_nodes.first, variable, scope)

View file

@ -79,12 +79,12 @@ module Ameba::AST
describe "#captured_by_block?" do describe "#captured_by_block?" do
it "returns truthy if the variable is captured by block" do it "returns truthy if the variable is captured by block" do
nodes = as_nodes %( nodes = as_nodes <<-CRYSTAL
def method def method
a = 2 a = 2
3.times { |i| a = a + i } 3.times { |i| a = a + i }
end end
) CRYSTAL
scope = Scope.new nodes.def_nodes.first scope = Scope.new nodes.def_nodes.first
var_node = nodes.var_nodes.first var_node = nodes.var_nodes.first
scope.add_variable var_node scope.add_variable var_node
@ -95,12 +95,12 @@ module Ameba::AST
variable.captured_by_block?.should be_truthy variable.captured_by_block?.should be_truthy
end end
it "returns falsey if the variable is not captured by the block" do it "returns falsy if the variable is not captured by the block" do
scope = Scope.new as_node %( scope = Scope.new as_node <<-CRYSTAL
def method def method
a = 1 a = 1
end end
) CRYSTAL
scope.add_variable Crystal::Var.new "a" scope.add_variable Crystal::Var.new "a"
variable = scope.variables.first variable = scope.variables.first
variable.captured_by_block?.should be_falsey variable.captured_by_block?.should be_falsey

View file

@ -19,33 +19,33 @@ module Ameba::AST
end end
it "is 1 if there is Macro::For" do it "is 1 if there is Macro::For" do
code = %( code = <<-CRYSTAL
def initialize() def initialize
{% for c in ALL_NODES %} {% for c in ALL_NODES %}
true || false true || false
{% end %} {% end %}
end end
) CRYSTAL
node = Crystal::Parser.new(code).parse node = Crystal::Parser.new(code).parse
visitor = CountingVisitor.new node visitor = CountingVisitor.new node
visitor.count.should eq 1 visitor.count.should eq 1
end end
it "is 1 if there is Macro::If" do it "is 1 if there is Macro::If" do
code = %( code = <<-CRYSTAL
def initialize() def initialize
{% if foo.bar? %} {% if foo.bar? %}
true || false true || false
{% end %} {% end %}
end end
) CRYSTAL
node = Crystal::Parser.new(code).parse node = Crystal::Parser.new(code).parse
visitor = CountingVisitor.new node visitor = CountingVisitor.new node
visitor.count.should eq 1 visitor.count.should eq 1
end end
it "increases count for every exhaustive case" do it "increases count for every exhaustive case" do
code = %( code = <<-CRYSTAL
def hello(a : Int32 | Int64 | Float32 | Float64) def hello(a : Int32 | Int64 | Float32 | Float64)
case a case a
in Int32 then "int32" in Int32 then "int32"
@ -54,7 +54,7 @@ module Ameba::AST
in Float64 then "float64" in Float64 then "float64"
end end
end end
) CRYSTAL
node = Crystal::Parser.new(code).parse node = Crystal::Parser.new(code).parse
visitor = CountingVisitor.new node visitor = CountingVisitor.new node
visitor.count.should eq 2 visitor.count.should eq 2

View file

@ -6,17 +6,17 @@ module Ameba::AST
describe FlowExpressionVisitor do describe FlowExpressionVisitor do
it "creates an expression for return" do it "creates an expression for return" do
rule = FlowExpressionRule.new rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %( FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
def foo def foo
return :bar return :bar
end end
) CRYSTAL
rule.expressions.size.should eq 1 rule.expressions.size.should eq 1
end end
it "can create multiple expressions" do it "can create multiple expressions" do
rule = FlowExpressionRule.new rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %( FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
def foo def foo
if bar if bar
return :baz return :baz
@ -24,42 +24,42 @@ module Ameba::AST
return :foobar return :foobar
end end
end end
) CRYSTAL
rule.expressions.size.should eq 3 rule.expressions.size.should eq 3
end end
it "properly creates nested flow expressions" do it "properly creates nested flow expressions" do
rule = FlowExpressionRule.new rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %( FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
def foo def foo
return( return (
loop do loop do
break if a > 1 break if a > 1
return a return a
end end
) )
end end
) CRYSTAL
rule.expressions.size.should eq 4 rule.expressions.size.should eq 4
end end
it "creates an expression for break" do it "creates an expression for break" do
rule = FlowExpressionRule.new rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %( FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
while true while true
break break
end end
) CRYSTAL
rule.expressions.size.should eq 1 rule.expressions.size.should eq 1
end end
it "creates an expression for next" do it "creates an expression for next" do
rule = FlowExpressionRule.new rule = FlowExpressionRule.new
FlowExpressionVisitor.new rule, Source.new %( FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL
while true while true
next if something next if something
end end
) CRYSTAL
rule.expressions.size.should eq 1 rule.expressions.size.should eq 1
end end
end end

View file

@ -5,11 +5,11 @@ module Ameba::AST
rule = RedundantControlExpressionRule.new rule = RedundantControlExpressionRule.new
describe RedundantControlExpressionVisitor do describe RedundantControlExpressionVisitor do
node = as_node %( node = as_node <<-CRYSTAL
a = 1 a = 1
b = 2 b = 2
return a + b return a + b
) CRYSTAL
subject = RedundantControlExpressionVisitor.new(rule, source, node) subject = RedundantControlExpressionVisitor.new(rule, source, node)
it "assigns valid attributes" do it "assigns valid attributes" do

View file

@ -4,37 +4,37 @@ module Ameba::AST
describe ScopeVisitor do describe ScopeVisitor do
it "creates a scope for the def" do it "creates a scope for the def" do
rule = ScopeRule.new rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %( ScopeVisitor.new rule, Source.new <<-CRYSTAL
def method def method
end end
) CRYSTAL
rule.scopes.size.should eq 1 rule.scopes.size.should eq 1
end end
it "creates a scope for the proc" do it "creates a scope for the proc" do
rule = ScopeRule.new rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %( ScopeVisitor.new rule, Source.new <<-CRYSTAL
-> {} -> {}
) CRYSTAL
rule.scopes.size.should eq 1 rule.scopes.size.should eq 1
end end
it "creates a scope for the block" do it "creates a scope for the block" do
rule = ScopeRule.new rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %( ScopeVisitor.new rule, Source.new <<-CRYSTAL
3.times {} 3.times {}
) CRYSTAL
rule.scopes.size.should eq 2 rule.scopes.size.should eq 2
end end
context "inner scopes" do context "inner scopes" do
it "creates scope for block inside def" do it "creates scope for block inside def" do
rule = ScopeRule.new rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %( ScopeVisitor.new rule, Source.new <<-CRYSTAL
def method def method
3.times {} 3.times {}
end end
) CRYSTAL
rule.scopes.size.should eq 2 rule.scopes.size.should eq 2
rule.scopes.last.outer_scope.should_not be_nil rule.scopes.last.outer_scope.should_not be_nil
rule.scopes.first.outer_scope.should eq rule.scopes.last rule.scopes.first.outer_scope.should eq rule.scopes.last
@ -42,11 +42,11 @@ module Ameba::AST
it "creates scope for block inside block" do it "creates scope for block inside block" do
rule = ScopeRule.new rule = ScopeRule.new
ScopeVisitor.new rule, Source.new %( ScopeVisitor.new rule, Source.new <<-CRYSTAL
3.times do 3.times do
2.times {} 2.times {}
end end
) CRYSTAL
rule.scopes.size.should eq 3 rule.scopes.size.should eq 3
inner_block = rule.scopes.first inner_block = rule.scopes.first
outer_block = rule.scopes.last outer_block = rule.scopes.last

View file

@ -4,10 +4,12 @@ module Ameba::AST
describe TopLevelNodesVisitor do describe TopLevelNodesVisitor do
describe "#require_nodes" do describe "#require_nodes" do
it "returns require node" do it "returns require node" do
source = Source.new %( source = Source.new <<-CRYSTAL
require "foo" require "foo"
def bar; end
) def bar
end
CRYSTAL
visitor = TopLevelNodesVisitor.new(source.ast) visitor = TopLevelNodesVisitor.new(source.ast)
visitor.require_nodes.size.should eq 1 visitor.require_nodes.size.should eq 1
visitor.require_nodes.first.to_s.should eq %q(require "foo") visitor.require_nodes.first.to_s.should eq %q(require "foo")

View file

@ -26,135 +26,135 @@ module Ameba
end end
it "disables a rule with a comment directive" do it "disables a rule with a comment directive" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
# ameba:disable #{NamedRule.name} # ameba:disable #{NamedRule.name}
Time.epoch(1483859302) Time.epoch(1483859302)
) CRYSTAL
s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!") source.add_issue(NamedRule.new, location: {1, 12}, message: "Error!")
s.should be_valid source.should be_valid
end end
it "disables a rule with a line that ends with a comment directive" do it "disables a rule with a line that ends with a comment directive" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
Time.epoch(1483859302) # ameba:disable #{NamedRule.name} Time.epoch(1483859302) # ameba:disable #{NamedRule.name}
) CRYSTAL
s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!") source.add_issue(NamedRule.new, location: {1, 12}, message: "Error!")
s.should be_valid source.should be_valid
end end
it "does not disable a rule of a different name" do it "does not disable a rule of a different name" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
# ameba:disable WrongName # ameba:disable WrongName
Time.epoch(1483859302) Time.epoch(1483859302)
) CRYSTAL
s.add_issue(NamedRule.new, location: {2, 12}, message: "Error!") source.add_issue(NamedRule.new, location: {2, 12}, message: "Error!")
s.should_not be_valid source.should_not be_valid
end end
it "disables a rule if multiple rule names provided" do it "disables a rule if multiple rule names provided" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
# ameba:disable SomeRule LargeNumbers #{NamedRule.name} SomeOtherRule # ameba:disable SomeRule LargeNumbers #{NamedRule.name} SomeOtherRule
Time.epoch(1483859302) Time.epoch(1483859302)
) CRYSTAL
s.add_issue(NamedRule.new, location: {2, 12}, message: "") source.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should be_valid source.should be_valid
end end
it "disables a rule if multiple rule names are separated by comma" do it "disables a rule if multiple rule names are separated by comma" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
# ameba:disable SomeRule, LargeNumbers, #{NamedRule.name}, SomeOtherRule # ameba:disable SomeRule, LargeNumbers, #{NamedRule.name}, SomeOtherRule
Time.epoch(1483859302) Time.epoch(1483859302)
) CRYSTAL
s.add_issue(NamedRule.new, location: {2, 12}, message: "") source.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should be_valid source.should be_valid
end end
it "does not disable if multiple rule names used without required one" do it "does not disable if multiple rule names used without required one" do
s = Source.new %( source = Source.new <<-CRYSTAL
# ameba:disable SomeRule, SomeOtherRule LargeNumbers # ameba:disable SomeRule, SomeOtherRule LargeNumbers
Time.epoch(1483859302) Time.epoch(1483859302)
) CRYSTAL
s.add_issue(NamedRule.new, location: {2, 12}, message: "") source.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should_not be_valid source.should_not be_valid
end end
it "does not disable if comment directive has wrong place" do it "does not disable if comment directive has wrong place" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
# ameba:disable #{NamedRule.name} # ameba:disable #{NamedRule.name}
# #
Time.epoch(1483859302) Time.epoch(1483859302)
) CRYSTAL
s.add_issue(NamedRule.new, location: {3, 12}, message: "") source.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid source.should_not be_valid
end end
it "does not disable if comment directive added to the wrong line" do it "does not disable if comment directive added to the wrong line" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
if use_epoch? # ameba:disable #{NamedRule.name} if use_epoch? # ameba:disable #{NamedRule.name}
Time.epoch(1483859302) Time.epoch(1483859302)
end end
) CRYSTAL
s.add_issue(NamedRule.new, location: {3, 12}, message: "") source.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid source.should_not be_valid
end end
it "does not disable if that is not a comment directive" do it "does not disable if that is not a comment directive" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
"ameba:disable #{NamedRule.name}" "ameba:disable #{NamedRule.name}"
Time.epoch(1483859302) Time.epoch(1483859302)
) CRYSTAL
s.add_issue(NamedRule.new, location: {3, 12}, message: "") source.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid source.should_not be_valid
end end
it "does not disable if that is a commented out directive" do it "does not disable if that is a commented out directive" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
# # ameba:disable #{NamedRule.name} # # ameba:disable #{NamedRule.name}
Time.epoch(1483859302) Time.epoch(1483859302)
) CRYSTAL
s.add_issue(NamedRule.new, location: {3, 12}, message: "") source.add_issue(NamedRule.new, location: {3, 12}, message: "")
s.should_not be_valid source.should_not be_valid
end end
it "does not disable if that is an inline commented out directive" do it "does not disable if that is an inline commented out directive" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
a = 1 # Disable it: # ameba:disable #{NamedRule.name} a = 1 # Disable it: # ameba:disable #{NamedRule.name}
) CRYSTAL
s.add_issue(NamedRule.new, location: {2, 12}, message: "") source.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should_not be_valid source.should_not be_valid
end end
context "with group name" do context "with group name" do
it "disables one rule with a group" do it "disables one rule with a group" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
a = 1 # ameba:disable #{DummyRule.rule_name} a = 1 # ameba:disable #{DummyRule.rule_name}
) CRYSTAL
s.add_issue(DummyRule.new, location: {1, 12}, message: "") source.add_issue(DummyRule.new, location: {1, 12}, message: "")
s.should be_valid source.should be_valid
end end
it "doesn't disable others rules" do it "doesn't disable others rules" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
a = 1 # ameba:disable #{DummyRule.rule_name} a = 1 # ameba:disable #{DummyRule.rule_name}
) CRYSTAL
s.add_issue(NamedRule.new, location: {2, 12}, message: "") source.add_issue(NamedRule.new, location: {2, 12}, message: "")
s.should_not be_valid source.should_not be_valid
end end
it "disables a hole group of rules" do it "disables a hole group of rules" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
a = 1 # ameba:disable #{DummyRule.group_name} a = 1 # ameba:disable #{DummyRule.group_name}
) CRYSTAL
s.add_issue(DummyRule.new, location: {1, 12}, message: "") source.add_issue(DummyRule.new, location: {1, 12}, message: "")
s.should be_valid source.should be_valid
end end
it "does not disable rules which do not belong to the group" do it "does not disable rules which do not belong to the group" do
s = Source.new %Q( source = Source.new <<-CRYSTAL
a = 1 # ameba:disable Lint a = 1 # ameba:disable Lint
) CRYSTAL
s.add_issue(DummyRule.new, location: {2, 12}, message: "") source.add_issue(DummyRule.new, location: {2, 12}, message: "")
s.should_not be_valid source.should_not be_valid
end end
end end
end end

View file

@ -24,12 +24,12 @@ module Ameba::Rule::Lint
end end
it "reports rule, pos and message" do it "reports rule, pos and message" do
source = Source.new %( source = Source.new <<-CRYSTAL, "source.cr"
require "./thing" require "./thing"
require "./thing" require "./thing"
require "./another_thing" require "./another_thing"
require "./another_thing" require "./another_thing"
), "source.cr" CRYSTAL
subject.catch(source).should_not be_valid subject.catch(source).should_not be_valid

View file

@ -13,7 +13,7 @@ module Ameba
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 %( s = Source.new <<-CRYSTAL
def method() def method()
end end
@ -30,7 +30,7 @@ module Ameba
begin "" end begin "" end
[nil] << nil [nil] << nil
) CRYSTAL
subject.catch(s).should be_valid subject.catch(s).should be_valid
end end

View file

@ -47,7 +47,7 @@ module Ameba::Rule::Performance
context "properties" do context "properties" do
it "allows to configure object_call_names" do it "allows to configure object_call_names" do
rule = Rule::Performance::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

View file

@ -63,7 +63,7 @@ module Ameba::Rule::Performance
context "properties" do context "properties" do
it "allows to configure object_call_names" do it "allows to configure object_call_names" do
rule = Rule::Performance::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

View file

@ -45,7 +45,7 @@ module Ameba::Rule::Performance
context "properties" do context "properties" do
it "allows to configure object caller names" do it "allows to configure object caller names" do
rule = Rule::Performance::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

View file

@ -45,6 +45,7 @@ module Ameba::Rule::Style
it "allows to configure filter_names" do it "allows to configure 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?)
CRYSTAL CRYSTAL

View file

@ -51,7 +51,7 @@ module Ameba::Rule::Style
end end
it "allows to configure assignments" do it "allows to configure assignments" do
rule = Rule::Style::ParenthesesAroundCondition.new rule = ParenthesesAroundCondition.new
rule.exclude_ternary = false rule.exclude_ternary = false
expect_issue rule, <<-CRYSTAL expect_issue rule, <<-CRYSTAL
@ -98,7 +98,7 @@ module Ameba::Rule::Style
end end
it "allows to configure assignments" do it "allows to configure assignments" do
rule = Rule::Style::ParenthesesAroundCondition.new rule = ParenthesesAroundCondition.new
rule.allow_safe_assignment = true rule.allow_safe_assignment = true
expect_issue rule, <<-CRYSTAL expect_issue rule, <<-CRYSTAL

View file

@ -211,7 +211,7 @@ module Ameba::Rule::Style
end end
it "allows to configure multi next statements" do it "allows to configure multi next statements" do
rule = Rule::Style::RedundantNext.new rule = RedundantNext.new
rule.allow_multi_next = false rule.allow_multi_next = false
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
block do |a, b| block do |a, b|
@ -238,7 +238,7 @@ module Ameba::Rule::Style
end end
it "allows to configure empty next statements" do it "allows to configure empty next statements" do
rule = Rule::Style::RedundantNext.new rule = RedundantNext.new
rule.allow_empty_next = false rule.allow_empty_next = false
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
block do block do

View file

@ -294,7 +294,7 @@ module Ameba::Rule::Style
end end
it "allows to configure multi returns" do it "allows to configure multi returns" do
rule = Rule::Style::RedundantReturn.new rule = RedundantReturn.new
rule.allow_multi_return = false rule.allow_multi_return = false
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
def method(a, b) def method(a, b)
@ -321,7 +321,7 @@ module Ameba::Rule::Style
end end
it "allows to configure empty returns" do it "allows to configure empty returns" do
rule = Rule::Style::RedundantReturn.new rule = RedundantReturn.new
rule.allow_empty_return = false rule.allow_empty_return = false
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
def method def method

View file

@ -64,10 +64,12 @@ module Ameba::Rule::Style
context "properties" do context "properties" do
it "#exclude_calls_with_block" do it "#exclude_calls_with_block" do
rule = VerboseBlock.new rule = VerboseBlock.new
rule.exclude_calls_with_block = true rule.exclude_calls_with_block = true
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
(1..3).in_groups_of(1) { |i| i.map(&.to_s) } (1..3).in_groups_of(1) { |i| i.map(&.to_s) }
CRYSTAL CRYSTAL
rule.exclude_calls_with_block = false rule.exclude_calls_with_block = false
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
(1..3).in_groups_of(1) { |i| i.map(&.to_s) } (1..3).in_groups_of(1) { |i| i.map(&.to_s) }
@ -81,12 +83,14 @@ module Ameba::Rule::Style
it "#exclude_multiple_line_blocks" do it "#exclude_multiple_line_blocks" do
rule = VerboseBlock.new rule = VerboseBlock.new
rule.exclude_multiple_line_blocks = true rule.exclude_multiple_line_blocks = true
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
(1..3).any? do |i| (1..3).any? do |i|
i.odd? i.odd?
end end
CRYSTAL CRYSTAL
rule.exclude_multiple_line_blocks = false rule.exclude_multiple_line_blocks = false
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
(1..3).any? do |i| (1..3).any? do |i|
@ -102,12 +106,14 @@ module Ameba::Rule::Style
it "#exclude_prefix_operators" do it "#exclude_prefix_operators" do
rule = VerboseBlock.new rule = VerboseBlock.new
rule.exclude_prefix_operators = true rule.exclude_prefix_operators = true
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
(1..3).sum { |i| +i } (1..3).sum { |i| +i }
(1..3).sum { |i| -i } (1..3).sum { |i| -i }
(1..3).sum { |i| ~i } (1..3).sum { |i| ~i }
CRYSTAL CRYSTAL
rule.exclude_prefix_operators = false rule.exclude_prefix_operators = false
rule.exclude_operators = false rule.exclude_operators = false
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
@ -128,10 +134,12 @@ module Ameba::Rule::Style
it "#exclude_operators" do it "#exclude_operators" do
rule = VerboseBlock.new rule = VerboseBlock.new
rule.exclude_operators = true rule.exclude_operators = true
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
(1..3).sum { |i| i * 2 } (1..3).sum { |i| i * 2 }
CRYSTAL CRYSTAL
rule.exclude_operators = false rule.exclude_operators = false
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
(1..3).sum { |i| i * 2 } (1..3).sum { |i| i * 2 }
@ -145,10 +153,12 @@ module Ameba::Rule::Style
it "#exclude_setters" do it "#exclude_setters" do
rule = VerboseBlock.new rule = VerboseBlock.new
rule.exclude_setters = true rule.exclude_setters = true
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
Char::Reader.new("abc").tap { |reader| reader.pos = 0 } Char::Reader.new("abc").tap { |reader| reader.pos = 0 }
CRYSTAL CRYSTAL
rule.exclude_setters = false rule.exclude_setters = false
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
Char::Reader.new("abc").tap { |reader| reader.pos = 0 } Char::Reader.new("abc").tap { |reader| reader.pos = 0 }
@ -163,12 +173,14 @@ module Ameba::Rule::Style
it "#max_line_length" do it "#max_line_length" do
rule = VerboseBlock.new rule = VerboseBlock.new
rule.exclude_multiple_line_blocks = false rule.exclude_multiple_line_blocks = false
rule.max_line_length = 60 rule.max_line_length = 60
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
(1..3).tap &.tap &.tap &.tap &.tap &.tap &.tap do |i| (1..3).tap &.tap &.tap &.tap &.tap &.tap &.tap do |i|
i.to_s.reverse.strip.blank? i.to_s.reverse.strip.blank?
end end
CRYSTAL CRYSTAL
rule.max_line_length = nil rule.max_line_length = nil
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
(1..3).tap &.tap &.tap &.tap &.tap &.tap &.tap do |i| (1..3).tap &.tap &.tap &.tap &.tap &.tap &.tap do |i|
@ -184,10 +196,12 @@ module Ameba::Rule::Style
it "#max_length" do it "#max_length" do
rule = VerboseBlock.new rule = VerboseBlock.new
rule.max_length = 30 rule.max_length = 30
expect_no_issues rule, <<-CRYSTAL expect_no_issues rule, <<-CRYSTAL
(1..3).tap { |i| i.to_s.split.reverse.join.strip.blank? } (1..3).tap { |i| i.to_s.split.reverse.join.strip.blank? }
CRYSTAL CRYSTAL
rule.max_length = nil rule.max_length = nil
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
(1..3).tap { |i| i.to_s.split.reverse.join.strip.blank? } (1..3).tap { |i| i.to_s.split.reverse.join.strip.blank? }
@ -216,6 +230,7 @@ module Ameba::Rule::Style
it "reports call args and named_args" do it "reports call args and named_args" do
rule = VerboseBlock.new rule = VerboseBlock.new
rule.exclude_operators = false rule.exclude_operators = false
source = expect_issue rule, <<-CRYSTAL source = expect_issue rule, <<-CRYSTAL
(1..3).map { |i| i.to_s[start: 0.to_i64, count: 3]? } (1..3).map { |i| i.to_s[start: 0.to_i64, count: 3]? }
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: [...] `map(&.to_s.[start: 0.to_i64, count: 3]?)` # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: [...] `map(&.to_s.[start: 0.to_i64, count: 3]?)`
@ -260,9 +275,9 @@ module Ameba::Rule::Style
end end
it "reports rule, pos and message" do it "reports rule, pos and message" do
source = Source.new path: "source.cr", code: %( source = Source.new path: "source.cr", code: <<-CRYSTAL
(1..3).any? { |i| i.odd? } (1..3).any? { |i| i.odd? }
) CRYSTAL
subject.catch(source).should_not be_valid subject.catch(source).should_not be_valid
source.issues.size.should eq 1 source.issues.size.should eq 1

View file

@ -65,6 +65,7 @@ module Ameba
rules = [AtoAA.new] of Rule::Base rules = [AtoAA.new] of Rule::Base
source = Source.new "class A; end", "source.cr" source = Source.new "class A; end", "source.cr"
message = "Infinite loop in source.cr caused by Ameba/AtoAA" message = "Infinite loop in source.cr caused by Ameba/AtoAA"
expect_raises(Runner::InfiniteCorrectionLoopError, message) do expect_raises(Runner::InfiniteCorrectionLoopError, message) do
Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run
end end
@ -95,11 +96,11 @@ module Ameba
it "does not run other rules" do it "does not run other rules" do
rules = [Rule::Lint::Syntax.new, Rule::Style::ConstantNames.new] of Rule::Base rules = [Rule::Lint::Syntax.new, Rule::Style::ConstantNames.new] of Rule::Base
source = Source.new %q( source = Source.new <<-CRYSTAL
MyBadConstant = 1 MyBadConstant = 1
when my_bad_syntax when my_bad_syntax
) CRYSTAL
Runner.new(rules, [source], formatter, default_severity).run Runner.new(rules, [source], formatter, default_severity).run
source.should_not be_valid source.should_not be_valid
@ -110,9 +111,9 @@ module Ameba
context "unneeded disables" do context "unneeded disables" do
it "reports an issue if such disable exists" do it "reports an issue if such disable exists" do
rules = [Rule::Lint::UnneededDisableDirective.new] of Rule::Base rules = [Rule::Lint::UnneededDisableDirective.new] of Rule::Base
source = Source.new %( source = Source.new <<-CRYSTAL
a = 1 # ameba:disable LineLength a = 1 # ameba:disable LineLength
) CRYSTAL
Runner.new(rules, [source], formatter, default_severity).run Runner.new(rules, [source], formatter, default_severity).run
source.should_not be_valid source.should_not be_valid
@ -134,9 +135,7 @@ module Ameba
it "writes the explanation if sources are not valid and location found" do it "writes the explanation if sources are not valid and location found" do
io.clear io.clear
rules = [ErrorRule.new] of Rule::Base rules = [ErrorRule.new] of Rule::Base
source = Source.new %( source = Source.new "a = 1", "source.cr"
a = 1
), "source.cr"
runner = Runner.new(rules, [source], formatter, default_severity).run runner = Runner.new(rules, [source], formatter, default_severity).run
runner.explain({file: "source.cr", line: 1, column: 1}, io) runner.explain({file: "source.cr", line: 1, column: 1}, io)
@ -146,9 +145,7 @@ module Ameba
it "writes nothing if sources are not valid and location is not found" do it "writes nothing if sources are not valid and location is not found" do
io.clear io.clear
rules = [ErrorRule.new] of Rule::Base rules = [ErrorRule.new] of Rule::Base
source = Source.new %( source = Source.new "a = 1", "source.cr"
a = 1
), "source.cr"
runner = Runner.new(rules, [source], formatter, default_severity).run runner = Runner.new(rules, [source], formatter, default_severity).run
runner.explain({file: "source.cr", line: 1, column: 2}, io) runner.explain({file: "source.cr", line: 1, column: 2}, io)
@ -167,10 +164,9 @@ module Ameba
it "returns false if there are invalid sources" do it "returns false if there are invalid sources" do
rules = Rule.rules.map &.new.as(Rule::Base) rules = Rule.rules.map &.new.as(Rule::Base)
s = Source.new %q( source = Source.new "WrongConstant = 5"
WrongConstant = 5
) Runner.new(rules, [source], formatter, default_severity).run.success?.should be_false
Runner.new(rules, [s], formatter, default_severity).run.success?.should be_false
end end
it "depends on the level of severity" do it "depends on the level of severity" do
@ -183,11 +179,11 @@ module Ameba
it "returns false if issue is disabled" do it "returns false if issue is disabled" do
rules = [NamedRule.new] of Rule::Base rules = [NamedRule.new] of Rule::Base
source = Source.new %( source = Source.new <<-CRYSTAL
def foo def foo
bar = 1 # ameba:disable #{NamedRule.name} bar = 1 # ameba:disable #{NamedRule.name}
end end
) CRYSTAL
source.add_issue NamedRule.new, location: {2, 1}, source.add_issue NamedRule.new, location: {2, 1},
message: "Useless assignment" message: "Useless assignment"
@ -204,6 +200,7 @@ module Ameba
rules = [AtoB.new, BtoA.new] rules = [AtoB.new, BtoA.new]
source = Source.new "class A; end", "source.cr" source = Source.new "class A; end", "source.cr"
message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoA" message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoA"
expect_raises(Runner::InfiniteCorrectionLoopError, message) do expect_raises(Runner::InfiniteCorrectionLoopError, message) do
Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run
end end
@ -213,11 +210,12 @@ module Ameba
context "if there are multiple offenses in an inspected file" do context "if there are multiple offenses in an inspected file" do
it "aborts because of an infinite loop" do it "aborts because of an infinite loop" do
rules = [AtoB.new, BtoA.new] rules = [AtoB.new, BtoA.new]
source = Source.new %( source = Source.new <<-CRYSTAL, "source.cr"
class A; end class A; end
class A_A; end class A_A; end
), "source.cr" CRYSTAL
message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoA" message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoA"
expect_raises(Runner::InfiniteCorrectionLoopError, message) do expect_raises(Runner::InfiniteCorrectionLoopError, message) do
Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run
end end
@ -230,6 +228,7 @@ module Ameba
rules = [ClassToModule.new, ModuleToClass.new, AtoB.new, BtoA.new] rules = [ClassToModule.new, ModuleToClass.new, AtoB.new, BtoA.new]
source = Source.new "class A_A; end", "source.cr" source = Source.new "class A_A; end", "source.cr"
message = "Infinite loop in source.cr caused by Ameba/ClassToModule, Ameba/AtoB -> Ameba/ModuleToClass, Ameba/BtoA" message = "Infinite loop in source.cr caused by Ameba/ClassToModule, Ameba/AtoB -> Ameba/ModuleToClass, Ameba/BtoA"
expect_raises(Runner::InfiniteCorrectionLoopError, message) do expect_raises(Runner::InfiniteCorrectionLoopError, message) do
Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run
end end
@ -241,6 +240,7 @@ module Ameba
rules = [AtoB.new, BtoC.new, CtoA.new] rules = [AtoB.new, BtoC.new, CtoA.new]
source = Source.new "class A; end", "source.cr" source = Source.new "class A; end", "source.cr"
message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoC -> Ameba/CtoA" message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoC -> Ameba/CtoA"
expect_raises(Runner::InfiniteCorrectionLoopError, message) do expect_raises(Runner::InfiniteCorrectionLoopError, message) do
Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run
end end

View file

@ -1,15 +1,21 @@
module Ameba::Rule::Lint module Ameba::Rule::Lint
# This rule checks for mistyped shorthand assignments. # This rule checks for mistyped shorthand assignments.
# #
# # bad # This is considered invalid:
# x =- y
# x =+ y
# x =! y
# #
# # good # ```
# x = -y
# x = +y
# x = !y
# ```
#
# And this is valid:
#
# ```
# x -= y # or x = -y # x -= y # or x = -y
# x += y # or x = +y # x += y # or x = +y
# x != y # or x = !y # x != y # or x = !y
# ```
# #
# YAML configuration example: # YAML configuration example:
# #
@ -43,9 +49,9 @@ module Ameba::Rule::Lint
op_text = source_between(op_location, op_end_location, source.lines) op_text = source_between(op_location, op_end_location, source.lines)
return unless op_text return unless op_text
return unless MISTAKES.has_key?(op_text) return unless suggestion = MISTAKES[op_text]?
issue_for op_location, op_end_location, MSG % MISTAKES[op_text] issue_for op_location, op_end_location, MSG % suggestion
end end
end end
end end