shard-ameba/spec/ameba/ast/variabling/variable_spec.cr
Sijawusz Pur Rahnama e99a69765f Few refactors
2024-01-01 14:46:32 +01:00

223 lines
7.4 KiB
Crystal

require "../../../spec_helper"
module Ameba::AST
describe Variable do
var_node = Crystal::Var.new("foo")
scope = Scope.new as_node "foo = 1"
describe "#initialize" do
it "creates a new variable" do
variable = Variable.new(var_node, scope)
variable.node.should_not be_nil
end
end
describe "delegation" do
it "delegates locations" do
variable = Variable.new(var_node, scope)
variable.location.should eq var_node.location
variable.end_location.should eq var_node.end_location
end
it "delegates name" do
variable = Variable.new(var_node, scope)
variable.name.should eq var_node.name
end
it "delegates to_s" do
variable = Variable.new(var_node, scope)
variable.to_s.should eq var_node.to_s
end
end
describe "#special?" do
it "returns truthy if it is a special `$?` var" do
variable = Variable.new Crystal::Var.new("$?"), scope
variable.special?.should be_truthy
end
it "returns falsey otherwise" do
variable = Variable.new Crystal::Var.new("a"), scope
variable.special?.should be_falsey
end
end
describe "#assign" do
assign_node = as_node("foo=1")
it "assigns the variable (creates a new assignment)" do
variable = Variable.new(var_node, scope)
variable.assign(assign_node, scope)
variable.assignments.empty?.should be_false
end
it "can create multiple assignments" do
variable = Variable.new(var_node, scope)
variable.assign(assign_node, scope)
variable.assign(assign_node, scope)
variable.assignments.size.should eq 2
end
end
describe "#reference" do
it "references the existed assignment" do
variable = Variable.new(var_node, scope)
variable.assign(as_node("foo=1"), scope)
variable.reference(var_node, scope)
variable.references.empty?.should be_false
end
it "adds a reference to the scope" do
scope = Scope.new as_node "foo = 1"
variable = Variable.new(var_node, scope)
variable.assign(as_node("foo=1"), scope)
variable.reference(var_node, scope)
scope.references.size.should eq 1
scope.references.first.node.to_s.should eq "foo"
end
end
describe "#captured_by_block?" do
it "returns truthy if the variable is captured by block" do
nodes = as_nodes <<-CRYSTAL
def method
a = 2
3.times { |i| a = a + i }
end
CRYSTAL
var_node = nodes.var_nodes.first
scope = Scope.new(nodes.def_nodes.first)
scope.add_variable(var_node)
scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope)
variable = Variable.new(var_node, scope)
variable.reference(nodes.var_nodes.last, scope.inner_scopes.last)
variable.captured_by_block?.should be_truthy
end
it "returns falsy if the variable is not captured by the block" do
scope = Scope.new as_node <<-CRYSTAL
def method
a = 1
end
CRYSTAL
scope.add_variable(Crystal::Var.new "a")
variable = scope.variables.first
variable.captured_by_block?.should be_falsey
end
end
describe "#target_of?" do
it "returns true if the variable is a target of Crystal::Assign node" do
assign_node = as_nodes("foo=1").assign_nodes.last
variable = Variable.new assign_node.target.as(Crystal::Var), scope
variable.target_of?(assign_node).should be_true
end
it "returns true if the variable is a target of Crystal::OpAssign node" do
assign_node = as_nodes("foo=1;foo+=1").op_assign_nodes.last
variable = Variable.new assign_node.target.as(Crystal::Var), scope
variable.target_of?(assign_node).should be_true
end
it "returns true if the variable is a target of Crystal::MultiAssign node" do
assign_node = as_nodes("a,b,c={1,2,3}").multi_assign_nodes.last
assign_node.targets.size.should_not eq 0
assign_node.targets.each do |target|
variable = Variable.new target.as(Crystal::Var), scope
variable.target_of?(assign_node).should be_true
end
end
it "returns false if the node is not assign" do
variable = Variable.new(Crystal::Var.new("v"), scope)
variable.target_of?(as_node "nil").should be_false
end
it "returns false if the variable is not a target of the assign" do
variable = Variable.new(Crystal::Var.new("foo"), scope)
variable.target_of?(as_node("bar = 1")).should be_false
end
end
describe "#ignored?" do
it "is true if variable is ignored" do
variable = Variable.new(Crystal::Var.new("_var"), scope)
variable.ignored?.should be_true
end
it "is false if variable is not ignored" do
variable = Variable.new(Crystal::Var.new("v_ar"), scope)
variable.ignored?.should be_false
end
it "is true if variable is a black hole" do
variable = Variable.new(Crystal::Var.new("_"), scope)
variable.ignored?.should be_true
end
end
describe "#eql?" do
var = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2))
variable = Variable.new var, scope
it "is false if node is not a Crystal::Var" do
variable.eql?(as_node("nil")).should be_false
end
it "is false if node name is different" do
variable.eql?(Crystal::Var.new "bar").should be_false
end
it "is false if node has a different location" do
variable.eql?(Crystal::Var.new "foo").should be_false
end
it "is true otherwise" do
variable.eql?(variable.node).should be_true
end
end
describe "#declared_before?" do
it "is falsey if variable doesn't have location" do
var1 = Crystal::Var.new("foo")
var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 2))
Variable.new(var1, scope).declared_before?(var2).should be_falsey
end
it "is falsey if node doesn't have location" do
var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2))
var2 = Crystal::Var.new("bar")
Variable.new(var1, scope).declared_before?(var2).should be_falsey
end
it "is true if var's line_number below the node" do
var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2))
var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 2, 2))
Variable.new(var1, scope).declared_before?(var2).should be_true
end
it "is true if var's column_number is after the node" do
var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 1, 2))
var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 3))
Variable.new(var1, scope).declared_before?(var2).should be_true
end
it "is false if var's location is before the node" do
var1 = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 2, 2))
var2 = Crystal::Var.new("bar").at(Crystal::Location.new(nil, 1, 3))
Variable.new(var1, scope).declared_before?(var2).should be_false
end
it "is false is the node is the same var" do
var = Crystal::Var.new("foo").at(Crystal::Location.new(nil, 2, 2))
Variable.new(var, scope).declared_before?(var).should be_false
end
end
end
end