2017-11-01 11:10:34 +00:00
|
|
|
require "../../spec_helper"
|
|
|
|
|
|
|
|
module Ameba::AST
|
|
|
|
struct Test
|
|
|
|
include Util
|
|
|
|
end
|
|
|
|
|
|
|
|
subject = Test.new
|
|
|
|
|
|
|
|
describe Util do
|
|
|
|
describe "#literal?" do
|
|
|
|
[
|
|
|
|
Crystal::ArrayLiteral.new,
|
|
|
|
Crystal::BoolLiteral.new(false),
|
|
|
|
Crystal::CharLiteral.new('a'),
|
|
|
|
Crystal::HashLiteral.new,
|
|
|
|
Crystal::NamedTupleLiteral.new,
|
|
|
|
Crystal::NilLiteral.new,
|
|
|
|
Crystal::NumberLiteral.new(42),
|
2020-03-25 17:21:06 +00:00
|
|
|
Crystal::RegexLiteral.new(Crystal::StringLiteral.new("")),
|
2017-11-01 11:10:34 +00:00
|
|
|
Crystal::StringLiteral.new(""),
|
|
|
|
Crystal::SymbolLiteral.new(""),
|
|
|
|
Crystal::TupleLiteral.new([] of Crystal::ASTNode),
|
|
|
|
Crystal::RangeLiteral.new(
|
|
|
|
Crystal::NilLiteral.new,
|
|
|
|
Crystal::NilLiteral.new,
|
|
|
|
true),
|
|
|
|
].each do |literal|
|
|
|
|
it "returns true if node is #{literal}" do
|
|
|
|
subject.literal?(literal).should be_true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if node is not a literal" do
|
|
|
|
subject.literal?(Crystal::Nop).should be_false
|
|
|
|
end
|
|
|
|
end
|
2017-11-01 12:18:28 +00:00
|
|
|
|
2017-11-07 11:28:33 +00:00
|
|
|
describe "#node_source" do
|
|
|
|
it "returns original source of the node" do
|
2021-11-09 19:45:38 +00:00
|
|
|
s = <<-CRYSTAL
|
2017-11-07 11:28:33 +00:00
|
|
|
a = 1
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2017-11-07 11:28:33 +00:00
|
|
|
node = Crystal::Parser.new(s).parse
|
|
|
|
source = subject.node_source node, s.split("\n")
|
2021-10-19 09:44:41 +00:00
|
|
|
source.should eq "a = 1"
|
2017-11-07 11:28:33 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns original source of multiline node" do
|
2021-11-09 19:45:38 +00:00
|
|
|
s = <<-CRYSTAL
|
2017-11-07 11:28:33 +00:00
|
|
|
if ()
|
|
|
|
:ok
|
|
|
|
end
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2017-11-07 11:28:33 +00:00
|
|
|
node = Crystal::Parser.new(s).parse
|
|
|
|
source = subject.node_source node, s.split("\n")
|
2021-10-19 09:44:41 +00:00
|
|
|
source.should eq <<-CRYSTAL
|
|
|
|
if ()
|
2021-11-09 19:45:38 +00:00
|
|
|
:ok
|
|
|
|
end
|
2021-10-19 09:44:41 +00:00
|
|
|
CRYSTAL
|
2017-11-07 11:28:33 +00:00
|
|
|
end
|
2020-10-23 07:41:17 +00:00
|
|
|
|
|
|
|
it "does not report source of node which has incorrect location" do
|
2021-11-09 19:45:38 +00:00
|
|
|
s = <<-'CRYSTAL'
|
2020-10-23 07:41:17 +00:00
|
|
|
module MyModule
|
|
|
|
macro conditional_error_for_inline_callbacks
|
|
|
|
\{%
|
|
|
|
raise ""
|
|
|
|
%}
|
|
|
|
end
|
|
|
|
|
|
|
|
macro before_save(x = nil)
|
|
|
|
end
|
|
|
|
end
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2020-10-23 07:41:17 +00:00
|
|
|
node = as_nodes(s).nil_literal_nodes.first
|
|
|
|
source = subject.node_source node, s.split("\n")
|
2022-04-03 16:17:47 +00:00
|
|
|
source.should eq "nil"
|
2020-10-23 07:41:17 +00:00
|
|
|
end
|
2017-11-07 11:28:33 +00:00
|
|
|
end
|
2018-11-22 08:38:32 +00:00
|
|
|
|
|
|
|
describe "#flow_command?" do
|
|
|
|
it "returns true if this is return" do
|
|
|
|
node = as_node("return 22")
|
|
|
|
subject.flow_command?(node, false).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is a break in a loop" do
|
|
|
|
node = as_node("break")
|
|
|
|
subject.flow_command?(node, true).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if this is a break out of loop" do
|
|
|
|
node = as_node("break")
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.flow_command?(node, false).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is a next in a loop" do
|
|
|
|
node = as_node("next")
|
|
|
|
subject.flow_command?(node, true).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if this is a next out of loop" do
|
|
|
|
node = as_node("next")
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.flow_command?(node, false).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is raise" do
|
|
|
|
node = as_node("raise e")
|
|
|
|
subject.flow_command?(node, false).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is exit" do
|
|
|
|
node = as_node("exit")
|
|
|
|
subject.flow_command?(node, false).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is abort" do
|
|
|
|
node = as_node("abort")
|
|
|
|
subject.flow_command?(node, false).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false otherwise" do
|
|
|
|
node = as_node("foobar")
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.flow_command?(node, false).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#flow_expression?" do
|
|
|
|
it "returns true if this is a flow command" do
|
|
|
|
node = as_node("return")
|
|
|
|
subject.flow_expression?(node, true).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is if-else consumed by flow expressions" do
|
2021-11-09 19:45:38 +00:00
|
|
|
node = as_node <<-CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
if foo
|
|
|
|
return :foo
|
|
|
|
else
|
|
|
|
return :bar
|
|
|
|
end
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
subject.flow_expression?(node, false).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is unless-else consumed by flow expressions" do
|
2021-11-09 19:45:38 +00:00
|
|
|
node = as_node <<-CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
unless foo
|
|
|
|
return :foo
|
|
|
|
else
|
|
|
|
return :bar
|
|
|
|
end
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
subject.flow_expression?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is case consumed by flow expressions" do
|
2021-11-09 19:45:38 +00:00
|
|
|
node = as_node <<-CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
case
|
|
|
|
when 1
|
|
|
|
return 1
|
|
|
|
when 2
|
|
|
|
return 2
|
|
|
|
else
|
|
|
|
return 3
|
|
|
|
end
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
subject.flow_expression?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is exception handler consumed by flow expressions" do
|
2021-11-09 19:45:38 +00:00
|
|
|
node = as_node <<-CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
begin
|
|
|
|
raise "exp"
|
|
|
|
rescue e
|
|
|
|
return e
|
|
|
|
end
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
subject.flow_expression?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this while consumed by flow expressions" do
|
2021-11-09 19:45:38 +00:00
|
|
|
node = as_node <<-CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
while true
|
|
|
|
return
|
|
|
|
end
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
subject.flow_expression?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if this while with break" do
|
2021-11-09 19:45:38 +00:00
|
|
|
node = as_node <<-CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
while true
|
|
|
|
break
|
|
|
|
end
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.flow_expression?(node).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this until consumed by flow expressions" do
|
2021-11-09 19:45:38 +00:00
|
|
|
node = as_node <<-CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
until false
|
|
|
|
return
|
|
|
|
end
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
subject.flow_expression?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if this until with break" do
|
2021-11-09 19:45:38 +00:00
|
|
|
node = as_node <<-CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
until false
|
|
|
|
break
|
|
|
|
end
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.flow_expression?(node).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this expressions consumed by flow expressions" do
|
2021-11-09 19:45:38 +00:00
|
|
|
node = as_node <<-CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
exp1
|
|
|
|
exp2
|
|
|
|
return
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
subject.flow_expression?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false otherwise" do
|
2021-11-09 19:45:38 +00:00
|
|
|
node = as_node <<-CRYSTAL
|
2018-11-22 08:38:32 +00:00
|
|
|
exp1
|
|
|
|
exp2
|
2021-11-09 19:45:38 +00:00
|
|
|
CRYSTAL
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.flow_expression?(node).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#raise?" do
|
|
|
|
it "returns true if this is a raise method call" do
|
|
|
|
node = as_node "raise e"
|
|
|
|
subject.raise?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if it has a receiver" do
|
|
|
|
node = as_node "obj.raise e"
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.raise?(node).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if size of the arguments doesn't match" do
|
|
|
|
node = as_node "raise"
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.raise?(node).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#exit?" do
|
|
|
|
it "returns true if this is a exit method call" do
|
|
|
|
node = as_node "exit"
|
|
|
|
subject.exit?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is a exit method call with one argument" do
|
|
|
|
node = as_node "exit 1"
|
|
|
|
subject.exit?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if it has a receiver" do
|
|
|
|
node = as_node "obj.exit"
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.exit?(node).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if size of the arguments doesn't match" do
|
|
|
|
node = as_node "exit 1, 1"
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.exit?(node).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#abort?" do
|
|
|
|
it "returns true if this is an abort method call" do
|
|
|
|
node = as_node "abort"
|
|
|
|
subject.abort?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is an abort method call with one argument" do
|
|
|
|
node = as_node "abort \"message\""
|
|
|
|
subject.abort?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if this is an abort method call with two arguments" do
|
|
|
|
node = as_node "abort \"message\", 1"
|
|
|
|
subject.abort?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if it has a receiver" do
|
|
|
|
node = as_node "obj.abort"
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.abort?(node).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if size of the arguments doesn't match" do
|
|
|
|
node = as_node "abort 1, 1, 1"
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.abort?(node).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "#loop?" do
|
|
|
|
it "returns true if this is a loop method call" do
|
|
|
|
node = as_node "loop"
|
|
|
|
subject.loop?(node).should eq true
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if it has a receiver" do
|
|
|
|
node = as_node "obj.loop"
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.loop?(node).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if size of the arguments doesn't match" do
|
|
|
|
node = as_node "loop 1"
|
2023-03-09 04:39:25 +00:00
|
|
|
subject.loop?(node).should be_false
|
2018-11-22 08:38:32 +00:00
|
|
|
end
|
|
|
|
end
|
2021-11-09 19:45:38 +00:00
|
|
|
|
|
|
|
describe "#control_exp_code" do
|
|
|
|
it "returns the exp code of a control expression" do
|
|
|
|
s = "return 1"
|
|
|
|
node = as_node(s).as Crystal::ControlExpression
|
|
|
|
exp_code = subject.control_exp_code node, [s]
|
|
|
|
exp_code.should eq "1"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "wraps implicit tuple literal with curly brackets" do
|
|
|
|
s = "return 1, 2"
|
|
|
|
node = as_node(s).as Crystal::ControlExpression
|
|
|
|
exp_code = subject.control_exp_code node, [s]
|
|
|
|
exp_code.should eq "{1, 2}"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "accepts explicit tuple literal" do
|
|
|
|
s = "return {1, 2}"
|
|
|
|
node = as_node(s).as Crystal::ControlExpression
|
|
|
|
exp_code = subject.control_exp_code node, [s]
|
|
|
|
exp_code.should eq "{1, 2}"
|
|
|
|
end
|
|
|
|
end
|
2021-11-16 21:30:33 +00:00
|
|
|
|
|
|
|
describe "#name_end_location" do
|
|
|
|
it "works on method call" do
|
|
|
|
node = as_node("name(foo)").as Crystal::Call
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:4"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on method definition" do
|
|
|
|
node = as_node("def name; end").as Crystal::Def
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:8"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on macro definition" do
|
|
|
|
node = as_node("macro name; end").as Crystal::Macro
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:10"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on class definition" do
|
|
|
|
node = as_node("class Name; end").as Crystal::ClassDef
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:10"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on module definition" do
|
|
|
|
node = as_node("module Name; end").as Crystal::ModuleDef
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:11"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on annotation definition" do
|
|
|
|
node = as_node("annotation Name; end").as Crystal::AnnotationDef
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:15"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on enum definition" do
|
|
|
|
node = as_node("enum Name; end").as Crystal::EnumDef
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:9"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on alias definition" do
|
|
|
|
node = as_node("alias Name = Foo").as Crystal::Alias
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:10"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on generic" do
|
|
|
|
node = as_node("Name(Foo)").as Crystal::Generic
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:4"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on include" do
|
|
|
|
node = as_node("include Name").as Crystal::Include
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:12"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on extend" do
|
|
|
|
node = as_node("extend Name").as Crystal::Extend
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:11"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on variable type declaration" do
|
|
|
|
node = as_node("name : Foo").as Crystal::TypeDeclaration
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:4"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on uninitialized variable" do
|
|
|
|
node = as_node("name = uninitialized Foo").as Crystal::UninitializedVar
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:4"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on lib definition" do
|
|
|
|
node = as_node("lib Name; end").as Crystal::LibDef
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:8"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on lib type definition" do
|
|
|
|
node = as_node("lib Foo; type Name = Bar; end").as(Crystal::LibDef).body
|
|
|
|
node.class.should eq Crystal::TypeDef
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:18"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "works on metaclass" do
|
|
|
|
node = as_node("foo : Name.class").as(Crystal::TypeDeclaration).declared_type
|
|
|
|
node.class.should eq Crystal::Metaclass
|
|
|
|
subject.name_end_location(node).to_s.should eq ":1:10"
|
|
|
|
end
|
|
|
|
end
|
2017-11-01 11:10:34 +00:00
|
|
|
end
|
|
|
|
end
|