From 0208129a92b983c84631e8168cf27143639a5e4e Mon Sep 17 00:00:00 2001 From: sabiwara Date: Sun, 31 Aug 2025 15:48:44 +0900 Subject: [PATCH 1/2] Do not consider variables from pattern in bitstring spec Close https://github.com/elixir-lang/elixir/issues/14737 --- lib/elixir/src/elixir_bitstring.erl | 10 +++++++--- lib/elixir/test/elixir/kernel/warning_test.exs | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/elixir/src/elixir_bitstring.erl b/lib/elixir/src/elixir_bitstring.erl index 9f31a583c88..8e9c6f8e5bb 100644 --- a/lib/elixir/src/elixir_bitstring.erl +++ b/lib/elixir/src/elixir_bitstring.erl @@ -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), diff --git a/lib/elixir/test/elixir/kernel/warning_test.exs b/lib/elixir/test/elixir/kernel/warning_test.exs index eb0dd65930b..4d7c44eace9 100644 --- a/lib/elixir/test/elixir/kernel/warning_test.exs +++ b/lib/elixir/test/elixir/kernel/warning_test.exs @@ -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 + "fo" = a + """ + ) + end + defp assert_compile_error(messages, string) do captured = capture_err(fn -> From 07de822da93b039669307c4a566062c472c922a9 Mon Sep 17 00:00:00 2001 From: sabiwara Date: Sun, 31 Aug 2025 16:53:44 +0900 Subject: [PATCH 2/2] Add test for undefined variable --- lib/elixir/test/elixir/kernel/expansion_test.exs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/elixir/test/elixir/kernel/expansion_test.exs b/lib/elixir/test/elixir/kernel/expansion_test.exs index 2dc037c0f45..d1b2af82280 100644 --- a/lib/elixir/test/elixir/kernel/expansion_test.exs +++ b/lib/elixir/test/elixir/kernel/expansion_test.exs @@ -2890,6 +2890,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 <> -> :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"