-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Add __escape__/1 and use it to fix Regex escaping in OTP28.1 #14720
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
Conversation
Notes to self: how we're going to check that the
|
14f9002
to
6cb4b82
Compare
# OTP 28.0 works in degraded mode performance-wise, we need to recompile from the source | ||
true -> | ||
quote do | ||
{:ok, pattern} = | ||
:re.compile(unquote(Macro.escape(regex.source)), unquote(Macro.escape(regex.opts))) | ||
|
||
pattern | ||
end | ||
end |
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.
Was wondering. Perhaps we could/should warn here, so that people notice and pro-actively bump to OTP28.1?
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.
Yes, warning sounds good to me!
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.
Hum I tried e1907ea but it seems to cause some bootstrapping issues.
Not too familiar with IO.warn_once
, maybe will give up for now 😅
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.
So let's warn on boot in elixir.erl. Check if version is >= 28 and the import function is not available.
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 seems to work, but would break too many integration tests in the CI?
d1536b4
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.
@sabiwara I think for CI we will run on 28.1. So perhaps we add the warning in a separate PR, which we will merge once we move CI to 28.1. WDYT?
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.
Adding the warning separately sounds good! 💜
maybe | ||
#{'__struct__' := Module} ?= Map, | ||
true ?= is_atom(Module), | ||
{module, Module} ?= code:ensure_loaded(Module), | ||
true ?= erlang:function_exported(Module, '__escape__', 1), | ||
Module:'__escape__'(Map) | ||
else | ||
_ -> | ||
TT = [escape_map_key_value(K, V, Map, Q) || {K, V} <- lists:sort(maps:to_list(Map))], | ||
{'%{}', [], TT} | ||
end; |
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.
Note: We can finally use maybe
🎉
It fits perfectly here.
Since we're requiring OTP26, there is no runtime flag anymore, just the module one.
end | ||
end | ||
|
||
@tag skip: not function_exported?(:re, :import, 1) |
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.
Let's use exclude in the test/test_helper.exs instead, for consistency? We should probably do Code.ensure_loaded?(:re)
as well...
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.
Do you mean adding an otp_version_exclude
here?
https://github.com/elixir-lang/elixir/blob/main/lib/elixir/test/elixir/test_helper.exs#L138-L139
I'm slightly concerned that the extra indirection would make it harder to follow, esp. if it is just used at one place?
But happy to do it if you think that's best.
We should probably do Code.ensure_loaded?(:re) as well.
Indeed, sorry about that. I'm starting to wish we had one function for this as suggested recently 😅
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 would add one called re_import_exclude
, yes, so we have all of them handled in a single place instead of scattered.
I think we're good to go! I was even able to apply it to a fine-based project I'm working on, which also has runtime refs, a text format and a binary format - it worked beautifully ✨ |
Agreed, we just need docs and we can ship it! :) |
What's a good place for it? |
|
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 added a comment with a slightly different spin on the documentation. Feel free to use it or discard it as you prefer.
Co-authored-by: José Valim <[email protected]>
I love it 💜 |
@josevalim OK to backport right? (since it's from 1.19) |
Yes, 100%. |
Leverages newly added :re.import/1. erlang/otp#9976
Take 2 for #14719.
Leverages
:re.import
added in erlang/otp#9976.Here I went for
Macro.escape
rather than:elixir_expand
, because:elixir_expand
is harder to work with since we're already in AST land and the AST for a struct is hard to pattern-match on.This works since
Macro.escape
is used in~r
, and also fixes the escaping issue discussed previously. But this is violating the assumption that escape emits AST for a literal, which is probably a no-go.Will work on supporting the
:export
option separately since this is not strictly needed for this.