Skip to content

Commit

Permalink
Avoid raising a new exception when processing macro hooks to retain o…
Browse files Browse the repository at this point in the history
…riginal location
  • Loading branch information
Blacksmoke16 committed Dec 11, 2023
1 parent 19ffbc7 commit 5d4d93e
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 0 deletions.
58 changes: 58 additions & 0 deletions spec/compiler/semantic/macro_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,64 @@ describe "Semantic: macro" do
CRYSTAL
end

it "error raised within complex macro included hook (#7394)" do
ex = assert_error(<<-'CRYSTAL', "Value method must be an instance method")
module ExampleModule
macro included
{% verbatim do %}
{%
if method = @type.class.methods.find &.name.stringify.==("value")
method.raise "Value method must be an instance method."
else
raise "BUG: Didn't find value method."
end
%}
{% end %}
end
end

class ExampleClass
def self.value : Nil
end

include ExampleModule
end
CRYSTAL

ex.to_s.should contain "error in line 16"
end

it "error raise within macro included hook points to `include` vs `raise`" do
ex = assert_error(<<-'CRYSTAL', "noooo")
module Foo
macro included
{% raise "noooo" %}
end
end

include Foo
CRYSTAL

ex.to_s.should contain "error in line 3"
ex.to_s.should contain "error in line 7"
end

it "error raise within macro inherited hook points to the inheriting type vs `raise`" do
ex = assert_error(<<-'CRYSTAL', "noooo")
abstract struct Parent
macro inherited
{% raise "noooo" %}
end
end

struct Child < Parent
end
CRYSTAL

ex.to_s.should contain "error in line 3"
ex.to_s.should contain "error in line 7"
end

it "gives precise location info when doing yield inside macro" do
assert_error(<<-CRYSTAL, "in line 6")
macro foo
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/crystal/semantic/top_level_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
pushing_type(type) do
run_hooks(hook_type(superclass), type, :inherited, node) if created_new_type
node.body.accept self
rescue ex : MacroRaiseException
# Make the inner most exception to be the inherited node so that it's the last frame in the trace.
# This will make the location show on that node instead of the `raise` call.
ex.inner = Crystal::MacroRaiseException.for_node node, ex.message

raise ex
end

if created_new_type
Expand Down Expand Up @@ -1051,6 +1057,12 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
begin
current_type.as(ModuleType).include type
run_hooks hook_type(type), current_type, kind, node
rescue ex : MacroRaiseException
# Make the inner most exception to be the include/extend node so that it's the last frame in the trace.
# This will make the location show on that node instead of the `raise` call.
ex.inner = Crystal::MacroRaiseException.for_node node, ex.message

raise ex
rescue ex : TypeException
node.raise "at '#{kind}' hook", ex
end
Expand Down

0 comments on commit 5d4d93e

Please sign in to comment.