-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
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
Make remove_linenums!
more precise: remove lines from full Expr (was missing macrocalls), but _NOT_ from user's code (by preventing recursion into esc()
)
#31335
base: master
Are you sure you want to change the base?
Conversation
Currently `Base.remove_linenums!` removes line information from code blocks, but not from macro calls, which embed line information as the second argument in the expression. Having the line information in there makes it difficult to test user code that performs transformations on expressions, since the line information may differ in the input and resulting expressions. This change extends `Base.remove_linenums!` to handle macro call expressions, replacing their line information with `nothing`. The result is: ```julia julia> :(@macro argument) :(#= REPL[10]:1 =# @macro argument) julia> Base.remove_linenums!(:(@macro argument)) :(@macro argument) ```
EDIT: That solution did not work. This PR has been updated. |
487b92d
to
e8585bc
Compare
…, unclear how to fix kwargs case
This makes sense because the code we introduced that we want to strip linenumbers from and the user's code we want to `esc()` are complementary. - The reason we use `Base.remove_linenums!` is to avoid injecting the linenumbers of the Test code into the user's test expressions. So we want to remove linenums from any code _iff_ it was introduced by _us_ and not the user. - Conversely, we want to always `esc` _any code provided by the user_ exactly once, and we _don't_ want to `esc()` our code, since we want our variable names to get gensym'd.
e8585bc
to
445f216
Compare
@inferred
Expr inputs@inferred
and @test
macros to not strip linenumbers from input Expressions.
@inferred
and @test
macros to not strip linenumbers from input Expressions.@inferred
and @test
macros to not strip linenumbers from input Expressions: Stop remove_linenums!
from recursing into esc()
The trouble now is that all sorts of tests that compare Lines 46 to 47 in 445f216
Test Failed at /Users/sabae/buildbot/worker-tabularasa/tester_macos64/build/share/julia/test/syntax.jl:46
Expression: $(Expr(:quote, :(module $(Expr(:$, :a))
#= /Users/sabae/buildbot/worker-tabularasa/tester_macos64/build/share/julia/test/syntax.jl:46 =#
#= /Users/sabae/buildbot/worker-tabularasa/tester_macos64/build/share/julia/test/syntax.jl:46 =#
end))) == $(Expr(:quote, :(module a
#= /Users/sabae/buildbot/worker-tabularasa/tester_macos64/build/share/julia/test/syntax.jl:46 =#
#= /Users/sabae/buildbot/worker-tabularasa/tester_macos64/build/share/julia/test/syntax.jl:47 =#
end))) Can we make this kind of change and just require callers to call It's a breaking change, but only breaking tests, which is maybe easier? It's maybe the kind of breaking where no one realllly minds, and we can just change it? (that is, I'm not sure if we actually promised not to change ASTs, and this feels kinda similar?) The other option seems to me to somehow either change the definition of |
FWIW, I like this. |
Same... Any suggestions on the name? |
Adds and exports `isequal_nolines` to `Test` stdlib.
…ed name seems just fine.
9560559
to
3143b88
Compare
This will protect user macros that expect to find a LineNumberNode in the macrocall AST
Honestly, I like the idea of just defining You can add a new function called something like But if you use |
Thanks Dilum! :) Actually, after thinking about it more, this function should be for parsing ASTs, which might be expressions or might be literal values, so we should use a function that could mean something else in the case of a literal. (Note that So i think this should be its own, Expr-tree-/AST- specific function. I pushed up some commits that implements this and calls it |
You could also do |
@inferred
and @test
macros to not strip linenumbers from input Expressions: Stop remove_linenums!
from recursing into esc()
remove_linenums!
more precise: remove lines from full Expr (was missing macrocalls), but _NOT_ from user's code (by preventing recursion into esc()
)
(I have merged in #29619, as well, which makes |
Please take a look, I think this PR is now ready to be reviewed. (The existing test failure appears to be a flake.) Thanks! :) |
See also: [`isapprox`](@ref) for _numerical_ approximate equality | ||
""" | ||
function isequal_nolines(a, b) | ||
Base.remove_linenums!(a) == Base.remove_linenums!(b) |
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.
This should copy
its arguments so the predicate overall is non-mutating.
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.
Oops, duh, thanks.
deepcopy()
or copy()
? I feel like deepcopy
, but i still don't really understand the difference b/w them.
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.
copy
on a container makes a new container with the same elements. So copy of an array allocates exactly one new array. Exprs are more of a tree, so it copies the tree structure (Exprs and all sub-Exprs) but keeps the "leaves". So I think copy
is correct here.
deepcopy
attempts to copy everything reachable from an object, as if you serialized the whole thing to disk and read it back in. deepcopy
is best avoided, since it violates abstraction.
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.
Base.remove_linenums!(a) == Base.remove_linenums!(b) | |
a = Base.remove_linenums!(copy(a)) | |
b = Base.remove_linenums!(copy(b)) | |
return a == b |
I don't think excluding Did you try simply removing the |
Bump. |
See also: [`isapprox`](@ref) for _numerical_ approximate equality | ||
""" | ||
function isequal_nolines(a, b) | ||
Base.remove_linenums!(a) == Base.remove_linenums!(b) |
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.
Base.remove_linenums!(a) == Base.remove_linenums!(b) | |
a = Base.remove_linenums!(copy(a)) | |
b = Base.remove_linenums!(copy(b)) | |
return a == b |
# Replace line information embedded into macro calls with `nothing` | ||
# Removing the argument entirely invalidates the expression | ||
map!(ex.args, ex.args) do arg | ||
arg isa LineNumberNode ? LineNumberNode(1, :none) : remove_linenums!(arg) |
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.
These seems controversial, since these are values, not line numbers it’s removing. We could strip the one from the first argument (replacing __source__
with nothing
), but I don’t think we should modify the other argument literals in the list.
Friendly ping |
Fix the
@inferred
and@test
macros to stop modifying user inputs.After several edits, this PR achieves that by making
Base.remove_linenums!
not recurse intoesc()
'd Exprs.This PR also merges in #29619, which strengthens
remove_linenums!
to also remove linenumbers from macro calls.Finally, it introduces a new comparison function,
Base. isequal_nolines(a, b)
, which strips lineinfo nodes from both ASTs and then compares them for equality.Fixes #31334