-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
More accurate macro errors #13260
More accurate macro errors #13260
Conversation
Fix bug within interpreter I ran into while testing
@@ -1871,7 +1871,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor | |||
begin | |||
create_compiled_def(node, target_def) | |||
rescue ex : Crystal::TypeException | |||
node.raise ex, inner: ex | |||
node.raise ex.message, inner: ex |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think manually creating the type exceptions via .for_node
changed something that caused these calls to be taken into consideration and error due to the first arg expecting a string not the exception instance.
msg = args.map do |arg| | ||
arg.accept interpreter | ||
interpreter.last.to_macro_id | ||
end | ||
msg = msg.join " " | ||
|
||
node.raise msg, exception_type: Crystal::MacroRaiseException | ||
node.raise msg, exception_type: exception_type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allows differentiating a node specific versus top level raise to make the logic simpler as each case is slightly different in how it should be ordered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. I'm only a bit unsure about the representation of TopLevelMacroRaiseException
. Not entirely sure why. Maybe because I think {% foo.raise "" %}
and {% raise "" %}
should produce the same concept of an exception? I suppose an alternative implementation would be to have a property in MacroRaiseException
to signal whether the raise call has a target or not.
I'm definitely fine with taking it as is, just leaving this as food for thought.
def self.value : Nil | ||
ex.to_s.should contain "OH NO" | ||
ex.to_s.should contain "error in line 5" | ||
ex.to_s.scan("error in line").size.should eq 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not check for the concrete line number ("error in line 2"
?) here like above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this particular case, the trace looks like:
$ ./bin/crystal run --error-trace test.cr
In test.cr:5:1
5 | macro_raise_on 123
^-------------
Error: OH NO
In test.cr:5:16
5 | macro_raise_on 123
^
Error: OH NO
From the OP:
This one is a bit weird, but I didn't think it was worth trying to figure out how to remove the first frame in only this single context.
So the spec is basically asserting there are 2 frames on the same line, unlike the previous example which had two frames on two different lines.
Note that the frame for raise should never be shown. I implemented it that way so that a method call could produce a error just like the compiler can produce an error for it (for example, wrong number of arguments). Pointing to raise makes the error a bit more obscure, and kind of loses the point I had in mind. The other changes look good, though! |
Yeah, that's an important point. I think this change in its entirety is still a good move forward. No existing specs are broken, so the error attribution scope was never specified in code. And as we can see from the examples in #7147 there were quite some inconsistencies. Showing the frame of the |
Of course! I was just mentioning the intention behind not showing raise in the frame, but the overall change is better than that lost feature. And that feature isn't that important either. |
Resolves #7147
Top level raise in macro
Before:
After:
Node raise in macro
Before:
After:
This one is a bit weird, but I didn't think it was worth trying to figure out how to remove the first frame in only this single context.
Node raise in macro missing location
Before:
After:
Node raise in complex/nested macro call
Before:
After:
Top level raise in method
Before:
After: