diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index 1fa63dec787e..35848692bd6b 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -2053,6 +2053,24 @@ module Crystal it "gets empty output" do assert_macro %({{x.output}}), "nil", {x: ProcNotation.new([Path.new("SomeType")] of ASTNode)} end + + it "executes resolve" do + assert_macro %({{x.resolve}}), "Proc(Int32, String)", {x: ProcNotation.new(([Path.new("Int32")] of ASTNode), Path.new("String"))} + + assert_macro_error(%({{x.resolve}}), "undefined constant Foo") do + {x: ProcNotation.new(([Path.new("Foo")] of ASTNode))} + end + + assert_macro_error(%({{x.resolve}}), "undefined constant Foo") do + {x: ProcNotation.new(([] of ASTNode), Path.new("Foo"))} + end + end + + it "executes resolve?" do + assert_macro %({{x.resolve?}}), "Proc(Int32, String)", {x: ProcNotation.new(([Path.new("Int32")] of ASTNode), Path.new("String"))} + assert_macro %({{x.resolve?}}), "nil", {x: ProcNotation.new(([Path.new("Foo")] of ASTNode))} + assert_macro %({{x.resolve?}}), "nil", {x: ProcNotation.new(([] of ASTNode), Path.new("Foo"))} + end end describe "proc literal methods" do diff --git a/src/compiler/crystal/macros.cr b/src/compiler/crystal/macros.cr index e2ec95b5c98b..f3f959964384 100644 --- a/src/compiler/crystal/macros.cr +++ b/src/compiler/crystal/macros.cr @@ -1098,6 +1098,16 @@ module Crystal::Macros # Returns the output type, or nil if there is no return type. def output : ASTNode | NilLiteral end + + # Resolves this proc notation to a `TypeNode` if it denotes a type, + # or otherwise gives a compile-time error. + def resolve : ASTNode + end + + # Resolves this proc notation to a `TypeNode` if it denotes a type, + # or otherwise returns a `NilLiteral`. + def resolve? : ASTNode | NilLiteral + end end # A method definition. diff --git a/src/compiler/crystal/macros/interpreter.cr b/src/compiler/crystal/macros/interpreter.cr index 82f2479119e5..223ffe71fd0b 100644 --- a/src/compiler/crystal/macros/interpreter.cr +++ b/src/compiler/crystal/macros/interpreter.cr @@ -485,12 +485,12 @@ module Crystal end end - def resolve(node : Generic) + def resolve(node : Generic | ProcNotation) type = @path_lookup.lookup_type(node, self_type: @scope, free_vars: @free_vars) TypeNode.new(type) end - def resolve?(node : Generic) + def resolve?(node : Generic | ProcNotation) resolve(node) rescue Crystal::CodeError nil diff --git a/src/compiler/crystal/macros/methods.cr b/src/compiler/crystal/macros/methods.cr index 1a217dedb92e..0afe62d2e4dd 100644 --- a/src/compiler/crystal/macros/methods.cr +++ b/src/compiler/crystal/macros/methods.cr @@ -1175,6 +1175,10 @@ module Crystal interpret_check_args { ArrayLiteral.new(@inputs || [] of ASTNode) } when "output" interpret_check_args { @output || NilLiteral.new } + when "resolve" + interpret_check_args { interpreter.resolve(self) } + when "resolve?" + interpret_check_args { interpreter.resolve?(self) || NilLiteral.new } else super end