Do not consider variables from pattern in bitstring modifier (#14738)

This commit is contained in:
Jean Klingler
2025-08-31 19:21:15 +09:00
committed by sabiwara
parent 61603a8725
commit b3e3e8c6aa
3 changed files with 32 additions and 3 deletions
+7 -3
View File
@@ -30,7 +30,10 @@ expand(Meta, Args, S, E, RequireSize) ->
expand(_BitstrMeta, _Fun, [], Acc, S, E, Alignment, _RequireSize) ->
{lists:reverse(Acc), Alignment, S, E};
expand(BitstrMeta, Fun, [{'::', Meta, [Left, Right]} | T], Acc, S, E, Alignment, RequireSize) ->
{ELeft, {SL, OriginalS}, EL} = expand_expr(Left, Fun, S, E),
% We don't want to consider variables added in the Left pattern inside the Right specs
{#elixir_ex{vars=BeforeVars}, _} = S,
{ELeft, {#elixir_ex{vars=AfterVars} = SL, OriginalS}, EL} = expand_expr(Left, Fun, S, E),
validate_expr(ELeft, Meta, E),
MatchOrRequireSize = RequireSize or is_match_size(T, EL),
@@ -40,10 +43,11 @@ expand(BitstrMeta, Fun, [{'::', Meta, [Left, Right]} | T], Acc, S, E, Alignment,
{'^', _, [{_, _, _}]} -> {infer, ELeft};
_ -> required
end,
{ERight, EAlignment, SS, ES} = expand_specs(EType, Meta, Right, SL, OriginalS, EL, ExpectSize),
{ERight, EAlignment, SS, ES} = expand_specs(EType, Meta, Right, SL#elixir_ex{vars=BeforeVars}, OriginalS, EL, ExpectSize),
EAcc = concat_or_prepend_bitstring(Meta, ELeft, ERight, Acc, ES, MatchOrRequireSize),
expand(BitstrMeta, Fun, T, EAcc, {SS, OriginalS}, ES, alignment(Alignment, EAlignment), RequireSize);
expand(BitstrMeta, Fun, T, EAcc, {SS#elixir_ex{vars=AfterVars}, OriginalS}, ES, alignment(Alignment, EAlignment), RequireSize);
expand(BitstrMeta, Fun, [H | T], Acc, S, E, Alignment, RequireSize) ->
Meta = extract_meta(H, BitstrMeta),
{ELeft, {SS, OriginalS}, ES} = expand_expr(H, Fun, S, E),
@@ -2836,6 +2836,17 @@ defmodule Kernel.ExpansionTest do
end)
end
test "raises for variable used both in pattern and size" do
assert_compile_error(~r/undefined variable "foo"/, fn ->
code =
quote do
fn <<foo::size(foo)>> -> :ok end
end
expand(code, [])
end)
end
test "raises for invalid unit" do
message = ~r"unit in bitstring expects an integer as argument, got: :oops"
@@ -2237,6 +2237,20 @@ defmodule Kernel.WarningTest do
purge(Sample)
end
test "deprecate non-quoted variables in bitstring size modifiers" do
assert_warn_eval(
[
"the variable \"a\" is accessed inside size(...) of a bitstring but it was defined outside of the match",
"You must precede it with the pin operator"
],
"""
a = "foo"
<<a::binary-size(byte_size(a) - 1)>> <> _ = a
"fo" = a
"""
)
end
defp assert_compile_error(messages, string) do
captured =
capture_err(fn ->