Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions spec/compiler/macro/macro_methods_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,46 @@ module Crystal
assert_macro "", %({{"spice".includes?("b")}}), [] of ASTNode, %(false)
assert_macro "", %({{"spice".includes?("spice ")}}), [] of ASTNode, %(false)
end

describe "#parse_type_name" do
it "path" do
assert_type(%[class Bar; end; {{ "Bar".parse_type_name.is_a?(Path) ? 1 : 'a'}}]) { int32 }
end

it "generic" do
assert_type(%[class Foo(A, B); end; {{ "Foo(Int32, String)".parse_type_name.resolve.type_vars.size == 2 ? 1 : 'a'}}]) { int32 }
end

it "union - |" do
assert_type(%[class Foo; end; class Bar; end; {{ "Foo|Bar".parse_type_name.resolve.union_types.size == 2 ? 1 : 'a'}}]) { int32 }
end

it "union - Union" do
assert_type(%[class Foo; end; class Bar; end; {{ "Union(Foo,Bar)".parse_type_name.resolve.union_types.size == 2 ? 1 : 'a'}}]) { int32 }
end

it "union - in generic" do
assert_type(%[{{ "Array(Int32 | String)".parse_type_name.resolve.type_vars[0].union_types.size == 2 ? 1 : 'a'}}]) { int32 }
end

it "raises on empty string" do
expect_raises(Crystal::TypeException, "StringLiteral#parse_type_name cannot be called on an empty string") do
assert_macro "", %({{"".parse_type_name}}), [] of ASTNode, %(nil)
end
end

it "raises on extra unparsed tokens before the type" do
expect_raises(Crystal::TypeException, "Invalid type name: 100Foo") do
assert_macro "", %({{"100Foo".parse_type_name}}), [] of ASTNode, %(nil)
end
end

it "raises on extra unparsed tokens after the type" do
expect_raises(Crystal::TypeException, "Invalid type name: Foo(Int32)100") do
assert_macro "", %({{"Foo(Int32)100".parse_type_name}}), [] of ASTNode, %(nil)
end
end
end
end

describe "macro id methods" do
Expand Down
28 changes: 28 additions & 0 deletions src/compiler/crystal/macros.cr
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,34 @@ module Crystal::Macros
def includes?(search : StringLiteral | CharLiteral) : BoolLiteral
end

# Parses `self` into a `Path` or `Generic` (also used for unions).
# The `Path#resolve` or `Generic#resolve` method could then be used,
# with the return value depending on what the string represents.
#
# A compile time error is raised if the type/constant does not actually exist,
# or if a required generic argument was not provided.
#
# ```
# class Foo; end
#
# struct Some::Namespace::Foo; end
#
# module Bar(T); end
#
# MY_CONST = 1234
#
# {{ "Foo".parse_type_name.resolve.class? }} # => true
# {{ "Some::Namespace::Foo".parse_type_name.resolve.struct? }} # => true
# {{ "Foo|Some::Namespace::Foo".parse_type_name.resolve.union_types.size }} # => 2
# {{ "Bar(Int32)|Foo".parse_type_name.resolve.union_types[0].type_vars.size }} # => 1
# {{ "MY_CONST".parse_type_name.resolve }} # => 1234
#
# {{ "MissingType".parse_type_name }} # => Error: undefined constant MissingType
# {{ "Bar".parse_type_name.resolve }} # => Error: undefined constant T
# ```
def parse_type_name : TypeNode
end

# Similar to `String#size`.
def size : NumberLiteral
end
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/crystal/macros/methods.cr
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,18 @@ module Crystal
interpret_argless_method(method, args) { StringLiteral.new(@value.underscore) }
when "upcase"
interpret_argless_method(method, args) { StringLiteral.new(@value.upcase) }
when "parse_type_name"
interpret_argless_method(method, args) do
raise "StringLiteral#parse_type_name cannot be called on an empty string" if @value.blank?

parser = Crystal::Parser.new @value
parser.next_token_skip_statement_end
type = parser.parse_bare_proc_type
parser.check :EOF
type
rescue ex : Crystal::SyntaxException
raise "Invalid type name: #{@value.inspect}"
end
else
super
end
Expand Down