Skip to content
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

fix for new line num nodes from JuliaSyntax #195

Closed
wants to merge 5 commits into from

Conversation

Krastanov
Copy link
Contributor

@codecov-commenter
Copy link

codecov-commenter commented Jul 29, 2023

Codecov Report

Patch coverage: 93.75% and project coverage change: +5.91% 🎉

Comparison is base (d1937f9) 73.76% compared to head (29d342e) 79.67%.

❗ Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please install the Github App Integration for your organization. Read more.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #195      +/-   ##
==========================================
+ Coverage   73.76%   79.67%   +5.91%     
==========================================
  Files           9       10       +1     
  Lines         404      428      +24     
==========================================
+ Hits          298      341      +43     
+ Misses        106       87      -19     
Files Changed Coverage Δ
src/utils.jl 69.51% <93.75%> (+2.85%) ⬆️

... and 6 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Krastanov
Copy link
Contributor Author

This still feels a bit fragile, but no more fragile than it already was. I call it fragile because julia does not have a :catch block, rather just an implicit third argument to the :try block, so flattening nameless quote blocks into empty :() is dangerous. Probably try should be special-cased for a more robust fix, but I do not dare touch that for now.

src/utils.jl Outdated
# Don't use `unblock` to preserve line nos
return length(ex′.args) == 1 ? ex′.args[1] : ex′
# Don't use `unblock` to preserve line nos. Check for blocks that contain only a line number node.
return length(ex′.args) == 1 && !isline(ex′.args[1]) ? ex′.args[1] : ex′
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say that in general, Expr(:block, x) -> x is a valid transform, including for line numbers, so I'm a bit reluctant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you saying that you prefer that try is special cased here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you special-case try? I'm not super into this code, but it looks to me like the special-casing solution would involve replacing the flatten postwalk with an explicit tree traversal, which I also wouldn't like very much. I don't know! It's not a fun bug to fix. Let's see if it really fixes JuliaLang/julia#50710 (comment). If not, then is there a reason to merge this? It might break someone else's subtle macrology.

@Krastanov
Copy link
Contributor Author

@cstjean , I think I successfully special-cased try. You are right that without reworking the tree-walk, a complete fix is impossible, but I think now a bunch of bugs are fixed and a bunch more silent bugs were turned into loud errors.

The ResumableFunctions issue is fixed by this as well.

!isexpr(ex.args[3]) && (ex.args[3] = quote $(ex.args[3]) end)
elseif length(ex.args) == 4
# try finally end # 4 args, 2nd==3rd==false, 4th is expr
# try catch finally end # 4 args, 2nd is a symbol or false, 3rd and 4th are expr
Copy link
Contributor Author

@Krastanov Krastanov Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To help with the review, here is some context:

Before this PR, the following was being processed incorrectly (the error is not caught):

quote; try; error(); catch; false; finally; println(123); end; end |> striplines |> flatten |> eval

Now flatten raises an error and refuses to proceed.

However, the following used to work, but now it raises an error (with a workaround suggested in the error message):

quote; try; 1+1; finally; println(123); end; end |> striplines |> flatten |> eval

I do not see how to avoid the silent bug in the first snippet without refusing to process the second snippet as well -- well, except for a full rewrite of flatten that does not use postwalk.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think making sure people do not run into silent bugs is a good enough reason to sacrifice a bit of related accidentally working functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is also of note that this is highly improbable to be triggered: someone needs to explicitly use striplines otherwise the line number nodes prevent this ambiguity from happening.

Copy link
Collaborator

@cstjean cstjean Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except for a full rewrite of flatten that does not use postwalk.

I think it'd be worth giving this a try, in a separate PR. Sorry that I said I wouldn't like that approach; I just didn't see a completely satisfying (simple and correct) solution anywhere. Mike Innes' original code is beautifully simple and straight-forward, and I'd like to keep that as much as possible.

In ay case, I would start by looking at pre/postwalk and write the recursive descent flatten that special-cases try , so that the third argument's block is never flattened. I don't want to special-case false, because that's precisely the mistake that Julia's AST did, and that special-case can have ripples downstream.

Thank you for tackling this!

Copy link
Collaborator

@cstjean cstjean Aug 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like

flatten(x) = x
function flatten(x::Expr)
  if isexpr(x, :try)
      Expr(:try, flatten(x.args[1]), flatten(x.args[2]), Expr(:block, map(flatten, x.args[3].args)...))
   else
     flatten1(Expr(x.head, map(flatten, x.args)...))
end

(just a sketch, this is full of mistakes for sure)

In retrospect, that's quite simple and we get to solve all problems perfectly. Sorry for not having suggested it sooner.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am missing something. I was under the impression that only the following options exist for try blocks:

  • 3 argument blocks where the 3rd argument is a block, corresponding to try catch end
  • 4 argument blocks where the 3rd argument is a block, corresponding to try catch finally end
  • 4 argument blocks where the 3rd argument is false, corresponding to try finally end
  • 5 argument blocks where the 4th argument is a block, corresponding to try catch else finally end
  • 5 argument blocks where the 4th argument is false, corresponding to try catch else end

In your example above it seems like you only permit 3 argument blocks?

Or maybe you did not mean to focus on specifically what the special case for try should look like, rather you are telling me to use a "prewalk" style recursion instead of the current "postwalk" style recursion? I think I agree with that, but I will not have the time to attempt to contribute this for a couple of weeks.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe you did not mean to focus on specifically what the special case for try should look like, rather you are telling me to use a "prewalk" style recursion instead of the current "postwalk" style recursion?

Yes, that. Of course we need to handle all the cases you mentioned. Sorry that wasn't clear.

It's not a prewalk, in the sense that it doesn't apply the transformation again on the outcome. It's like postwalk, where we simply do not directly apply the transformation to the third argument of the try, but deal with it appropriately.

I think I agree with that, but I will not have the time to attempt to contribute this for a couple of weeks.

👍 Thank you very much for the PR and issue! It helps a lot. I don't think that Julia 1.10 will be ready for a long time, so I don't think it's very pressing. Could even be fixed after release, arguably.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

flatten deletes catch blocks on Julia 1.10 MacroTools.flatten does not work on 1.10 beta
3 participants