File elixir-1.16.2-git.patch of Package elixir

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7bcb4556f..0441740da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -78,6 +78,27 @@ Another [ExDoc](https://github.com/elixir-lang/ex_doc) feature we have incorpora
 
 Finally, we have started enriching our documentation with [Mermaid.js](https://mermaid.js.org/) diagrams. You can find examples in the [GenServer](https://hexdocs.pm/elixir/main/GenServer.html) and [Supervisor](https://hexdocs.pm/elixir/main/Supervisor.html) docs.
 
+## v1.16.3-dev
+
+### 1. Bug fixes
+
+#### Elixir
+
+  * [bin/elixir] Properly handle the `--dbg` flag in Elixir's CLI
+  * [Code.Formatter] Add brackets around keyword lists when formatting the left-hand side of `when`
+  * [Kernel] Only infer size in pinned variable in binary strings when needed
+  * [System] Add a note that arguments are unsafe when invoking .bat/.com scripts on Windows via `System.cmd/3`
+  * [Port] Add a note that arguments are unsafe when invoking .bat/.com scripts on Windows
+  * [URI] Ensure `:undefined` fields are properly converted to `nil` when invoking Erlang's API
+
+#### Logger
+
+  * [Logger] Ensure translators are persisted across logger restarts
+
+#### Mix
+
+  * [mix compile] Ensure compile paths are accessible during compilation
+
 ## v1.16.2 (2024-03-10)
 
 ### 1. Enhancements
diff --git a/bin/elixir b/bin/elixir
index d1b6058b7..166f6834a 100755
--- a/bin/elixir
+++ b/bin/elixir
@@ -112,10 +112,10 @@ while [ $I -le $LENGTH ]; do
         C=1
         MODE="iex"
         ;;
-    -v|--no-halt|--dbg)
+    -v|--no-halt)
         C=1
         ;;
-    -e|-r|-pr|-pa|-pz|--eval|--remsh|--dot-iex)
+    -e|-r|-pr|-pa|-pz|--eval|--remsh|--dot-iex|--dbg)
         C=2
         ;;
     --rpc-eval)
diff --git a/lib/elixir/lib/code/formatter.ex b/lib/elixir/lib/code/formatter.ex
index 6d50afb7e..3a159c3f7 100644
--- a/lib/elixir/lib/code/formatter.ex
+++ b/lib/elixir/lib/code/formatter.ex
@@ -1959,6 +1959,14 @@ defp clause_args_to_algebra(args, min_line, state) do
   # fn a, b, c when d -> e end
   defp clause_args_to_algebra([{:when, meta, args}], state) do
     {args, right} = split_last(args)
+
+    # If there are any keywords, wrap them in lists
+    args =
+      Enum.map(args, fn
+        [_ | _] = keyword -> {:__block__, [], [keyword]}
+        other -> other
+      end)
+
     left = {{:special, :clause_args}, meta, [args]}
     binary_op_to_algebra(:when, "when", meta, left, right, :no_parens_arg, state)
   end
diff --git a/lib/elixir/lib/port.ex b/lib/elixir/lib/port.ex
index fefcf2fa6..735529430 100644
--- a/lib/elixir/lib/port.ex
+++ b/lib/elixir/lib/port.ex
@@ -79,6 +79,27 @@ defmodule Port do
   are for advanced usage within the VM. Also consider using `System.cmd/3`
   if all you want is to execute a program and retrieve its return value.
 
+  > #### Windows argument splitting and untrusted arguments {: .warning}
+  >
+  > On Unix systems, arguments are passed to a new operating system
+  > process as an array of strings but on Windows it is up to the child
+  > process to parse them and some Windows programs may apply their own
+  > rules, which are inconsistent with the standard C runtime `argv` parsing
+  >
+  > This is particularly troublesome when invoking `.bat` or `.com` files
+  > as these run implicitly through `cmd.exe`, whose argument parsing is
+  > vulnerable to malicious input and can be used to run arbitrary shell
+  > commands.
+  >
+  > Therefore, if you are running on Windows and you execute batch
+  > files or `.com` applications, you must not pass untrusted input as
+  > arguments to the program. You may avoid accidentally executing them
+  > by explicitly passing the extension of the program you want to run,
+  > such as `.exe`, and double check the program is indeed not a batch
+  > file or `.com` application.
+  >
+  > This affects both `spawn` and `spawn_executable`.
+
   ### spawn
 
   The `:spawn` tuple receives a binary that is going to be executed as a
diff --git a/lib/elixir/lib/system.ex b/lib/elixir/lib/system.ex
index 912b17b77..6d90cff49 100644
--- a/lib/elixir/lib/system.ex
+++ b/lib/elixir/lib/system.ex
@@ -1005,6 +1005,25 @@ def shell(command, opts \\ []) when is_binary(command) do
   `Port` module describes this problem and possible solutions under
   the "Zombie processes" section.
 
+  > #### Windows argument splitting and untrusted arguments {: .warning}
+  >
+  > On Unix systems, arguments are passed to a new operating system
+  > process as an array of strings but on Windows it is up to the child
+  > process to parse them and some Windows programs may apply their own
+  > rules, which are inconsistent with the standard C runtime `argv` parsing
+  >
+  > This is particularly troublesome when invoking `.bat` or `.com` files
+  > as these run implicitly through `cmd.exe`, whose argument parsing is
+  > vulnerable to malicious input and can be used to run arbitrary shell
+  > commands.
+  >
+  > Therefore, if you are running on Windows and you execute batch
+  > files or `.com` applications, you must not pass untrusted input as
+  > arguments to the program. You may avoid accidentally executing them
+  > by explicitly passing the extension of the program you want to run,
+  > such as `.exe`, and double check the program is indeed not a batch
+  > file or `.com` application.
+
   ## Examples
 
       iex> System.cmd("echo", ["hello"])
diff --git a/lib/elixir/lib/uri.ex b/lib/elixir/lib/uri.ex
index fe612a609..c15523dc2 100644
--- a/lib/elixir/lib/uri.ex
+++ b/lib/elixir/lib/uri.ex
@@ -656,16 +656,16 @@ defp uri_from_map(map) do
         scheme = String.downcase(scheme, :ascii)
 
         case map do
-          %{port: port} when port != :undefined ->
+          %{port: port} when is_integer(port) ->
             %{uri | scheme: scheme}
 
           %{} ->
-            case default_port(scheme) do
-              nil -> %{uri | scheme: scheme}
-              port -> %{uri | scheme: scheme, port: port}
-            end
+            %{uri | scheme: scheme, port: default_port(scheme)}
         end
 
+      %{port: :undefined} ->
+        %{uri | port: nil}
+
       %{} ->
         uri
     end
diff --git a/lib/elixir/pages/cheatsheets/enum-cheat.cheatmd b/lib/elixir/pages/cheatsheets/enum-cheat.cheatmd
index 4d5a63ea0..73255de6c 100644
--- a/lib/elixir/pages/cheatsheets/enum-cheat.cheatmd
+++ b/lib/elixir/pages/cheatsheets/enum-cheat.cheatmd
@@ -707,11 +707,11 @@ fruits = ["apple", "banana", "grape", "orange", "pear"]
 iex> Enum.slide(fruits, 2, 0)
 ["grape", "apple", "banana", "orange", "pear"]
 iex> Enum.slide(fruits, 2, 4)
-["apple", "banana", "orange", "pear", "grape", ]
+["apple", "banana", "orange", "pear", "grape"]
 iex> Enum.slide(fruits, 1..3, 0)
 ["banana", "grape", "orange", "apple", "pear"]
 iex> Enum.slide(fruits, 1..3, 4)
-["banana", "pear", "grape", "orange", "apple"]
+["apple", "pear", "banana", "grape", "orange"]
 ```
 
 ## Reversing
diff --git a/lib/elixir/pages/getting-started/basic-types.md b/lib/elixir/pages/getting-started/basic-types.md
index 525d9bb12..3bbaa7c2b 100644
--- a/lib/elixir/pages/getting-started/basic-types.md
+++ b/lib/elixir/pages/getting-started/basic-types.md
@@ -227,7 +227,7 @@ Elixir also supports string interpolation:
 ```elixir
 iex> string = "world"
 iex> "hello #{string}!"
-"hello world"
+"hello world!"
 ```
 
 String concatenation requires both sides to be strings but interpolation supports any data type that may be converted to a string:
diff --git a/lib/elixir/pages/meta-programming/macros.md b/lib/elixir/pages/meta-programming/macros.md
index 1f04a344c..7b70efb5c 100644
--- a/lib/elixir/pages/meta-programming/macros.md
+++ b/lib/elixir/pages/meta-programming/macros.md
@@ -223,7 +223,7 @@ It is important that a macro is defined before its usage. Failing to define a ma
 
 ```elixir
 iex> defmodule Sample do
-...>  def four, do: two + two
+...>  def four, do: two() + two()
 ...>  defmacrop two, do: 2
 ...> end
 ** (CompileError) iex:2: function two/0 undefined
diff --git a/lib/elixir/src/elixir_bitstring.erl b/lib/elixir/src/elixir_bitstring.erl
index 1d85535d0..963be7837 100644
--- a/lib/elixir/src/elixir_bitstring.erl
+++ b/lib/elixir/src/elixir_bitstring.erl
@@ -36,9 +36,9 @@ expand(BitstrMeta, Fun, [{'::', Meta, [Left, Right]} | T], Acc, S, E, Alignment,
   MatchOrRequireSize = RequireSize or is_match_size(T, EL),
   EType = expr_type(ELeft),
   ExpectSize = case ELeft of
+    _ when not MatchOrRequireSize -> optional;
     {'^', _, [{_, _, _}]} -> {infer, ELeft};
-    _ when MatchOrRequireSize -> required;
-    _ -> optional
+    _ -> required
   end,
   {ERight, EAlignment, SS, ES} = expand_specs(EType, Meta, Right, SL, OriginalS, EL, ExpectSize),
 
diff --git a/lib/elixir/test/elixir/code_formatter/general_test.exs b/lib/elixir/test/elixir/code_formatter/general_test.exs
index 525f08ff3..bc3b58ab8 100644
--- a/lib/elixir/test/elixir/code_formatter/general_test.exs
+++ b/lib/elixir/test/elixir/code_formatter/general_test.exs
@@ -296,6 +296,28 @@ test "with a single clause and when" do
       assert_same code, @short_length
     end
 
+    test "keeps parens if argument includes keyword list" do
+      assert_same """
+      fn [] when is_integer(x) ->
+        x + 42
+      end
+      """
+
+      bad = """
+      fn (input: x) when is_integer(x) ->
+        x + 42
+      end
+      """
+
+      good = """
+      fn [input: x] when is_integer(x) ->
+        x + 42
+      end
+      """
+
+      assert_format bad, good
+    end
+
     test "with a single clause, followed by a newline, and can fit in one line" do
       assert_same """
       fn
diff --git a/lib/elixir/test/elixir/kernel/binary_test.exs b/lib/elixir/test/elixir/kernel/binary_test.exs
index dadcfaee6..dcb80f5c4 100644
--- a/lib/elixir/test/elixir/kernel/binary_test.exs
+++ b/lib/elixir/test/elixir/kernel/binary_test.exs
@@ -255,6 +255,12 @@ test "bitsyntax size using guard expressions in match context" do
     assert <<1::size((^foo).bar)>> = <<1::5>>
   end
 
+  test "bitsyntax size with pinned integer" do
+    a = 1
+    b = <<2, 3>>
+    assert <<^a, ^b::binary>> = <<1, 2, 3>>
+  end
+
   test "automatic size computation of matched bitsyntax variable" do
     var = "foo"
     <<^var::binary, rest::binary>> = "foobar"
diff --git a/lib/elixir/test/elixir/uri_test.exs b/lib/elixir/test/elixir/uri_test.exs
index e465cb323..8772ec97d 100644
--- a/lib/elixir/test/elixir/uri_test.exs
+++ b/lib/elixir/test/elixir/uri_test.exs
@@ -277,6 +277,32 @@ test "preserves empty fragments" do
     test "preserves an empty query" do
       assert URI.new!("http://foo.com/?").query == ""
     end
+
+    test "without scheme, undefined port after host translates to nil" do
+      assert URI.new!("//https://www.example.com") ==
+               %URI{
+                 scheme: nil,
+                 userinfo: nil,
+                 host: "https",
+                 port: nil,
+                 path: "//www.example.com",
+                 query: nil,
+                 fragment: nil
+               }
+    end
+
+    test "with scheme, undefined port after host translates to nil" do
+      assert URI.new!("myscheme://myhost:/path/info") ==
+               %URI{
+                 scheme: "myscheme",
+                 userinfo: nil,
+                 host: "myhost",
+                 port: nil,
+                 path: "/path/info",
+                 query: nil,
+                 fragment: nil
+               }
+    end
   end
 
   test "http://http://http://@http://http://?http://#http://" do
diff --git a/lib/iex/test/iex/helpers_test.exs b/lib/iex/test/iex/helpers_test.exs
index 18b19ddce..56e8c0144 100644
--- a/lib/iex/test/iex/helpers_test.exs
+++ b/lib/iex/test/iex/helpers_test.exs
@@ -332,17 +332,20 @@ test "shows help" do
       assert help =~ "Welcome to Interactive Elixir"
     end
 
+    @tag :erlang_doc
     test "prints Erlang module documentation" do
       captured = capture_io(fn -> h(:timer) end)
       assert captured =~ "This module provides useful functions related to time."
     end
 
+    @tag :erlang_doc
     test "prints Erlang module function specs" do
       captured = capture_io(fn -> h(:timer.sleep() / 1) end)
       assert captured =~ ":timer.sleep/1"
       assert captured =~ "-spec sleep(Time) -> ok when Time :: timeout()."
     end
 
+    @tag :erlang_doc
     test "handles non-existing Erlang module function" do
       captured = capture_io(fn -> h(:timer.baz() / 1) end)
       assert captured =~ "No documentation for :timer.baz was found"
@@ -1008,13 +1011,15 @@ defmodule TypeSample do
       cleanup_modules([TypeSample])
     end
 
-    test "prints all types in erlang module" do
+    @tag :erlang_doc
+    test "prints all types in Erlang module" do
       captured = capture_io(fn -> t(:queue) end)
       assert captured =~ "-type queue() :: queue(_)"
       assert captured =~ "-opaque queue(Item)"
     end
 
-    test "prints single type from erlang module" do
+    @tag :erlang_doc
+    test "prints single type from Erlang module" do
       captured = capture_io(fn -> t(:erlang.iovec()) end)
       assert captured =~ "-type iovec() :: [binary()]"
       assert captured =~ "A list of binaries."
@@ -1024,7 +1029,8 @@ test "prints single type from erlang module" do
       assert captured =~ "A list of binaries."
     end
 
-    test "handles non-existing types from erlang module" do
+    @tag :erlang_doc
+    test "handles non-existing types from Erlang module" do
       captured = capture_io(fn -> t(:erlang.foo()) end)
       assert captured =~ "No type information for :erlang.foo was found or :erlang.foo is private"
 
diff --git a/lib/iex/test/test_helper.exs b/lib/iex/test/test_helper.exs
index f5a55f0aa..0512ba2b9 100644
--- a/lib/iex/test/test_helper.exs
+++ b/lib/iex/test/test_helper.exs
@@ -7,11 +7,19 @@
 {line_exclude, line_include} =
   if line = System.get_env("LINE"), do: {[:test], [line: line]}, else: {[], []}
 
+erlang_doc_exclude =
+  if match?({:docs_v1, _, _, _, %{}, _, _}, Code.fetch_docs(:array)) do
+    []
+  else
+    IO.puts("Erlang/OTP compiled without docs, some tests are excluded...")
+    [:erlang_doc]
+  end
+
 ExUnit.start(
   assert_receive_timeout: assert_timeout,
   trace: !!System.get_env("TRACE"),
   include: line_include,
-  exclude: line_exclude
+  exclude: line_exclude ++ erlang_doc_exclude
 )
 
 defmodule IEx.Case do
diff --git a/lib/logger/lib/logger.ex b/lib/logger/lib/logger.ex
index 7eab7eafe..1e16cf98e 100644
--- a/lib/logger/lib/logger.ex
+++ b/lib/logger/lib/logger.ex
@@ -867,9 +867,12 @@ def remove_translator({mod, fun} = translator) when is_atom(mod) and is_atom(fun
 
   defp update_translators(updater) do
     :elixir_config.serial(fn ->
+      translators = updater.(Application.fetch_env!(:logger, :translators))
+      Application.put_env(:logger, :translators, translators)
+
       with %{filters: filters} <- :logger.get_primary_config(),
            {{_, {fun, config}}, filters} <- List.keytake(filters, :logger_translator, 0) do
-        config = update_in(config.translators, updater)
+        config = %{config | translators: translators}
         :ok = :logger.set_primary_config(:filters, filters ++ [logger_translator: {fun, config}])
       end
     end)
diff --git a/lib/logger/test/logger/backends/handler_test.exs b/lib/logger/test/logger/backends/handler_test.exs
index 26e744cda..921699048 100644
--- a/lib/logger/test/logger/backends/handler_test.exs
+++ b/lib/logger/test/logger/backends/handler_test.exs
@@ -58,7 +58,9 @@ test "add_translator/1 and remove_translator/1 for error_logger" do
   end
 
   test "add_translator/1 and remove_translator/1 for logger formats" do
+    refute {CustomTranslator, :t} in Application.fetch_env!(:logger, :translators)
     assert Logger.add_translator({CustomTranslator, :t})
+    assert {CustomTranslator, :t} in Application.fetch_env!(:logger, :translators)
 
     assert capture_log(fn ->
              :logger.info(~c"hello: ~p", [:ok])
diff --git a/lib/mix/lib/mix/tasks/compile.all.ex b/lib/mix/lib/mix/tasks/compile.all.ex
index 268a67275..8f29a2856 100644
--- a/lib/mix/lib/mix/tasks/compile.all.ex
+++ b/lib/mix/lib/mix/tasks/compile.all.ex
@@ -43,7 +43,11 @@ def run(args) do
       Code.delete_paths(current_paths -- loaded_paths)
     end
 
-    Code.prepend_paths(loaded_paths -- current_paths, cache: true)
+    # Add the current compilation path. compile.elixir and compile.erlang
+    # will also add this path, but only if they run, so we always add it
+    # here too. Furthermore, we don't cache it as we may still write to it.
+    compile_path = to_charlist(Mix.Project.compile_path())
+    Code.prepend_paths([compile_path | loaded_paths -- current_paths], cache: true)
 
     result =
       if "--no-compile" in args do
@@ -64,12 +68,6 @@ def run(args) do
       Mix.AppLoader.write_cache(app_cache, Map.new(loaded_modules))
     end
 
-    # Add the current compilation path. compile.elixir and compile.erlang
-    # will also add this path, but only if they run, so we always add it
-    # here too. Furthermore, we don't cache it as we may still write to it.
-    compile_path = to_charlist(Mix.Project.compile_path())
-    _ = Code.prepend_path(compile_path)
-
     unless "--no-app-loading" in args do
       app = config[:app]
 
diff --git a/lib/mix/test/mix/tasks/compile.elixir_test.exs b/lib/mix/test/mix/tasks/compile.elixir_test.exs
index 506b29f9e..47734e92f 100644
--- a/lib/mix/test/mix/tasks/compile.elixir_test.exs
+++ b/lib/mix/test/mix/tasks/compile.elixir_test.exs
@@ -19,6 +19,14 @@ test "compiles a project without per environment build" do
 
     in_fixture("no_mixfile", fn ->
       Mix.Project.push(MixTest.Case.Sample)
+
+      File.write!("lib/a.ex", """
+      defmodule A, do: :ok
+
+      # Also make sure that we access the ebin directory during compilation
+      true = to_charlist(Mix.Project.compile_path()) in :code.get_path()
+      """)
+
       Mix.Tasks.Compile.Elixir.run(["--verbose"])
 
       assert File.regular?("_build/shared/lib/sample/ebin/Elixir.A.beam")
@@ -32,6 +40,14 @@ test "compiles a project without per environment build" do
   test "compiles a project with per environment build" do
     in_fixture("no_mixfile", fn ->
       Mix.Project.push(MixTest.Case.Sample)
+
+      File.write!("lib/a.ex", """
+      defmodule A, do: :ok
+
+      # Also make sure that we access the ebin directory during compilation
+      true = to_charlist(Mix.Project.compile_path()) in :code.get_path()
+      """)
+
       Mix.Tasks.Compile.Elixir.run(["--verbose"])
 
       assert File.regular?("_build/dev/lib/sample/ebin/Elixir.A.beam")
@@ -772,7 +788,7 @@ def inspect(_, _), do: "sample"
     end)
   end
 
-  test "compiles mtime changed files if content changed but not length" do
+  test "recompiles mtime changed files if content changed but not length" do
     in_fixture("no_mixfile", fn ->
       Mix.Project.push(MixTest.Case.Sample)
       assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:ok, []}
@@ -872,7 +888,7 @@ test "does recompile a file restored after a compile error (and .beam file were
     end)
   end
 
-  test "compiles size changed files" do
+  test "recompiles size changed files" do
     in_fixture("no_mixfile", fn ->
       Mix.Project.push(MixTest.Case.Sample)
       past = @old_time
@@ -894,7 +910,7 @@ test "compiles size changed files" do
     end)
   end
 
-  test "compiles dependent changed modules" do
+  test "recompiles dependent changed modules" do
     in_fixture("no_mixfile", fn ->
       Mix.Project.push(MixTest.Case.Sample)
       File.write!("lib/a.ex", "defmodule A, do: B.module_info()")
@@ -914,7 +930,7 @@ test "compiles dependent changed modules" do
     end)
   end
 
-  test "compiles dependent changed modules without beam files" do
+  test "recompiles dependent changed modules without beam files" do
     in_fixture("no_mixfile", fn ->
       Mix.Project.push(MixTest.Case.Sample)
 
@@ -943,7 +959,7 @@ def a, do: A.__info__(:module)
     Code.put_compiler_option(:ignore_module_conflict, false)
   end
 
-  test "compiles dependent changed modules even on removal" do
+  test "recompiles dependent changed modules even on removal" do
     in_fixture("no_mixfile", fn ->
       Mix.Project.push(MixTest.Case.Sample)
       File.write!("lib/a.ex", "defmodule A, do: B.module_info()")
@@ -964,7 +980,7 @@ test "compiles dependent changed modules even on removal" do
     end)
   end
 
-  test "compiles dependent changed on conflict" do
+  test "recompiles dependent changed on conflict" do
     in_fixture("no_mixfile", fn ->
       Mix.Project.push(MixTest.Case.Sample)
 
@@ -991,7 +1007,7 @@ test "compiles dependent changed on conflict" do
     end)
   end
 
-  test "compiles dependent changed external resources" do
+  test "recompiles dependent changed external resources" do
     in_fixture("no_mixfile", fn ->
       Mix.Project.push(MixTest.Case.Sample)
       tmp = tmp_path("c.eex")
openSUSE Build Service is sponsored by