shard-ameba/spec/ameba/ast/branch_spec.cr
V. Elenhaupt 6475c2bb25
Variable scope & useless assignments (#41)
* AST::Visitor -> AST::NodeVisitor

* Scope & ScopeVisitor

* Useless assignment rule

* Instance vars and useless assignments

* Multiple assigns one by one

* Support outer scope

* Variable used in the useless assignment

* Support OpAssign & MultiAssign

* Captured by block

* Variable, Assignment, Reference & Refactoring

* Variable has references, Assignment can be referenced

* Branch entity

* Handle useless assignments in branches

* Handle assignments in a loop

* Handle branch equality

* Handle special var `$?` assignment

* Improve captured by block stuff

* Avoid assignments in property definitions

(UselessAssign rule reports an warning)

* Support MacroIf and MacroFor branches

* Handle assignments with shadowed vars in inner scopes

* Add method arguments as scope variables

* Handle case if branch is blank

* Top level scope

* Handle case when branch is nop?
2018-05-03 18:57:47 +03:00

364 lines
9.2 KiB
Crystal

require "../../spec_helper"
private def branch_of_assign_in_def(source)
nodes = as_nodes source
Ameba::AST::Branch.of(nodes.assign_nodes.first, nodes.def_nodes.first)
end
module Ameba::AST
describe Branch do
describe ".of" do
context "Crystal::If" do
it "constructs a branch in If.cond" do
branch = branch_of_assign_in_def %(
def method
if a = get_something # --> Crystal::Assign
puts a
end
end
)
branch.to_s.should eq "a = get_something"
end
it "constructs a branch in If.then" do
branch = branch_of_assign_in_def %(
def method
if true
a = 2 # --> Crystal::Assign
end
end
)
branch.to_s.should eq "a = 2"
end
it "constructs a branch in If.else" do
branch = branch_of_assign_in_def %(
def method
if true
nil
else
a = 2 # --> Crystal::Assign
end
end
)
branch.to_s.should eq "a = 2"
end
it "constructs a branch in inline If" do
branch = branch_of_assign_in_def %(
def method(a)
a = 0 if a == 2 # --> Crystal::Assign
end
)
branch.to_s.should eq "a = 0"
end
end
context "Crystal::Unless" do
it "constructs a branch in Unless.cond" do
branch = branch_of_assign_in_def %(
def method
unless a = get_something # --> Crystal::Assign
puts a
end
end
)
branch.to_s.should eq "a = get_something"
end
it "constructs a branch in Unless.then" do
branch = branch_of_assign_in_def %(
def method
unless true
a = 2 # --> Crystal::Assign
end
end
)
branch.to_s.should eq "a = 2"
end
it "constructs a new branch in Unless.else" do
branch = branch_of_assign_in_def %(
def method
unless true
nil
else
a = 2 # --> Crystal::Assign
end
end
)
branch.to_s.should eq "a = 2"
end
it "constructs a branch in inline Unless" do
branch = branch_of_assign_in_def %(
def method(a)
(a = 0; b = 3) unless a == 2 # --> Crystal::Expressions
end
)
branch.to_s.should eq "(a = 0\nb = 3)"
end
end
context "Crystal::BinaryOp" do
it "constructs a branch in left node" do
branch = branch_of_assign_in_def %(
def method(a)
(a = 2) && do_something
end
)
branch.to_s.should eq "(a = 2)"
end
it "constructs a branch in right node" do
branch = branch_of_assign_in_def %(
def method(a)
do_something || (a = 0)
end
)
branch.to_s.should eq "(a = 0)"
end
end
context "Crystal::Case" do
# FIXME:
# https://github.com/crystal-lang/crystal/pull/6032
# it "constructs a branch in cond" do
# branch = branch_of_assign_in_def %(
# def method(a)
# case (a = 2)
# when true then nil
# end
# end
# )
# branch.to_s.should eq "(a = 2)"
# end
it "constructs a branch in when" do
branch = branch_of_assign_in_def %(
def method(a)
case a
when a = 3 then nil
end
end
)
branch.to_s.should eq "when a = 3\n nil\n"
end
it "constructs a branch in else" do
branch = branch_of_assign_in_def %(
def method(a)
case a
when true then nil
else a = 4
end
end
)
branch.to_s.should eq "a = 4"
end
end
context "Crystal::While" do
it "constructs a branch in cond" do
branch = branch_of_assign_in_def %(
def method(a)
while a = 1
nil
end
end
)
branch.to_s.should eq "a = 1"
end
it "constructs a branch in body" do
branch = branch_of_assign_in_def %(
def method(a)
while true
b = (a = 1)
end
end
)
branch.to_s.should eq "b = (a = 1)"
end
end
context "Crystal::Until" do
it "constructs a branch in cond" do
branch = branch_of_assign_in_def %(
def method(a)
until a = 1
nil
end
end
)
branch.to_s.should eq "a = 1"
end
it "constructs a branch in body" do
branch = branch_of_assign_in_def %(
def method(a)
until false
b = (a = 1)
end
end
)
branch.to_s.should eq "b = (a = 1)"
end
end
context "Crystal::ExceptionHandler" do
it "constructs a branch in body" do
branch = branch_of_assign_in_def %(
def method(a)
a = 1
rescue
nil
end
)
branch.to_s.should eq "a = 1"
end
it "constructs a branch in a rescue" do
branch = branch_of_assign_in_def %(
def method(a)
rescue
a = 1
end
)
branch.to_s.should eq "a = 1"
end
it "constructs a branch in else" do
branch = branch_of_assign_in_def %(
def method(a)
rescue
else
a = 1
end
)
branch.to_s.should eq "a = 1"
end
it "constructs a branch in ensure" do
branch = branch_of_assign_in_def %(
def method(a)
rescue
ensure
a = 1
end
)
branch.to_s.should eq "a = 1"
end
end
context "Crystal::MacroIf" do
it "constructs a branch in cond" do
branch = branch_of_assign_in_def %(
def method(a)
{% if a = 2 %}
{% end %}
end
)
branch.to_s.should eq "a = 2"
end
it "constructs a branch in then" do
nodes = as_nodes %(
def method(a)
{% if true %}
a = 2
{% end %}
end
)
branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first)
branch.to_s.strip.should eq "a = 2"
end
end
context "Crystal::MacroFor" do
it "constructs a branch in body" do
nodes = as_nodes %(
def method(a)
{% for x in [1, 2, 3] %}
a = 2
{% end %}
end
)
branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first)
branch.to_s.strip.should eq "a = 2"
end
end
it "returns nil if branch does not exist" do
nodes = as_nodes %(
def method
a = 2
end
)
branch = Branch.of(nodes.assign_nodes.first, nodes.def_nodes.first)
branch.should be_nil
end
end
describe "#initialize" do
it "creates new branch" do
nodes = as_nodes %(
if true
a = 2
end
)
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.node.should_not be_nil
end
end
describe "delegation" do
it "delegates to_s to node" do
nodes = as_nodes %(
if true
a = 2
end
)
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.to_s.should eq branch.node.to_s
end
it "delegates location to node" do
nodes = as_nodes %(
if true
a = 2
end
)
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.location.should eq branch.node.location
end
end
describe "#in_loop?" do
it "returns true if branch is in a loop" do
nodes = as_nodes %(
while true
a = 1
end
)
branchable = Branchable.new nodes.while_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.in_loop?.should be_true
end
it "returns false if branch is not in a loop" do
nodes = as_nodes %(
if a > 2
a = 1
end
)
branchable = Branchable.new nodes.if_nodes.first
branch = Branch.new nodes.assign_nodes.first, branchable
branch.in_loop?.should be_false
end
end
end
end