From e9c9a5a871a0d4b67113c53a14a2ab726fdb4c10 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Wed, 27 Oct 2021 17:34:49 +0800 Subject: [PATCH 1/2] Add `Crystal::Macros::ProcNotation#resolve` and `#resolve?` --- spec/compiler/macro/macro_methods_spec.cr | 18 ++++++++++++++++++ src/compiler/crystal/macros.cr | 10 ++++++++++ src/compiler/crystal/macros/interpreter.cr | 4 ++-- src/compiler/crystal/macros/methods.cr | 4 ++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index 64a22055b56f..cf31f4199adb 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -2077,6 +2077,24 @@ module Crystal it "gets empty output" do assert_macro "x", %({{x.output}}), [ProcNotation.new([Path.new("SomeType")] of ASTNode)] of ASTNode, "nil" end + + it "executes resolve" do + assert_macro "x", %({{x.resolve}}), [ProcNotation.new(([Path.new("Int32")] of ASTNode), Path.new("String"))] of ASTNode, %(Proc(Int32, String)) + + expect_raises(Crystal::TypeException, "undefined constant Foo") do + assert_macro "x", %({{x.resolve}}), [ProcNotation.new(([Path.new("Foo")] of ASTNode))] of ASTNode, %() + end + + expect_raises(Crystal::TypeException, "undefined constant Foo") do + assert_macro "x", %({{x.resolve}}), [ProcNotation.new(([] of ASTNode), Path.new("Foo"))] of ASTNode, %() + end + end + + it "executes resolve?" do + assert_macro "x", %({{x.resolve?}}), [ProcNotation.new(([Path.new("Int32")] of ASTNode), Path.new("String"))] of ASTNode, %(Proc(Int32, String)) + assert_macro "x", %({{x.resolve?}}), [ProcNotation.new(([Path.new("Foo")] of ASTNode))] of ASTNode, %(nil) + assert_macro "x", %({{x.resolve?}}), [ProcNotation.new(([] of ASTNode), Path.new("Foo"))] of ASTNode, %(nil) + 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 From 64e6e3e18a11b616e460224142b5cd8fdaa03681 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Fri, 29 Oct 2021 19:21:04 +0800 Subject: [PATCH 2/2] use new `assert_macro` --- spec/compiler/macro/macro_methods_spec.cr | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index eefc08a09032..35848692bd6b 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -2055,21 +2055,21 @@ module Crystal end it "executes resolve" do - assert_macro "x", %({{x.resolve}}), [ProcNotation.new(([Path.new("Int32")] of ASTNode), Path.new("String"))] of ASTNode, %(Proc(Int32, String)) + assert_macro %({{x.resolve}}), "Proc(Int32, String)", {x: ProcNotation.new(([Path.new("Int32")] of ASTNode), Path.new("String"))} - expect_raises(Crystal::TypeException, "undefined constant Foo") do - assert_macro "x", %({{x.resolve}}), [ProcNotation.new(([Path.new("Foo")] of ASTNode))] of ASTNode, %() + assert_macro_error(%({{x.resolve}}), "undefined constant Foo") do + {x: ProcNotation.new(([Path.new("Foo")] of ASTNode))} end - expect_raises(Crystal::TypeException, "undefined constant Foo") do - assert_macro "x", %({{x.resolve}}), [ProcNotation.new(([] of ASTNode), Path.new("Foo"))] of ASTNode, %() + 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", %({{x.resolve?}}), [ProcNotation.new(([Path.new("Int32")] of ASTNode), Path.new("String"))] of ASTNode, %(Proc(Int32, String)) - assert_macro "x", %({{x.resolve?}}), [ProcNotation.new(([Path.new("Foo")] of ASTNode))] of ASTNode, %(nil) - assert_macro "x", %({{x.resolve?}}), [ProcNotation.new(([] of ASTNode), Path.new("Foo"))] of ASTNode, %(nil) + 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