Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: elixir-lang/elixir
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.13.0
Choose a base ref
...
head repository: elixir-lang/elixir
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v1.13.1
Choose a head ref
  • 17 commits
  • 34 files changed
  • 6 contributors

Commits on Dec 4, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    d239d99 View commit details
  2. Add missing @doc since (#11443)

    wojtekmach authored and josevalim committed Dec 4, 2021
    Copy the full SHA
    713633a View commit details

Commits on Dec 5, 2021

  1. Copy the full SHA
    ebb347a View commit details
  2. Specific base typespecs (#11449)

    * Use specific options for base option typespecs
    
    Each function had `keyword` as the option type, which didn't help guard
    against typos or mismatched options.
    
    * Fix padding use in encode/decode identity test
    sorentwo authored and josevalim committed Dec 5, 2021
    Copy the full SHA
    cbba61a View commit details

Commits on Dec 6, 2021

  1. Do not deprecate URI.parse/1

    Closes #11450.
    josevalim committed Dec 6, 2021
    Copy the full SHA
    a0b77bd View commit details

Commits on Dec 7, 2021

  1. Copy the full SHA
    2efc1e5 View commit details
  2. Fix halt for --version

    josevalim committed Dec 7, 2021
    Copy the full SHA
    d3b38fb View commit details

Commits on Dec 8, 2021

  1. Copy the full SHA
    31e24b3 View commit details
  2. Copy the full SHA
    233fc09 View commit details

Commits on Dec 9, 2021

  1. Copy the full SHA
    edafcc4 View commit details

Commits on Dec 10, 2021

  1. Copy the full SHA
    47171d9 View commit details

Commits on Dec 11, 2021

  1. Copy the full SHA
    b725b8c View commit details

Commits on Dec 13, 2021

  1. Copy the full SHA
    8afac93 View commit details

Commits on Dec 14, 2021

  1. Improve DateTime docs

    josevalim committed Dec 14, 2021
    Copy the full SHA
    b085ad2 View commit details
  2. Copy the full SHA
    7e66a4c View commit details
  3. Copy the full SHA
    8804ff5 View commit details
  4. Release v1.13.1

    josevalim committed Dec 14, 2021
    Copy the full SHA
    33f9d04 View commit details
25 changes: 24 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -128,6 +128,29 @@ Now any application can use your formatter as follows:

Finally, the `Code` module has also been augmented with two functions: `Code.string_to_quoted_with_comments/2` and `Code.quoted_to_algebra/2`. Those functions allow someone to retrieve the Elixir AST with their original source code comments, and then convert this AST to formatted code. In other words, those functions provide a wrapper around the Elixir Code Formatter, supporting developers who wish to create tools that directly manipulate and custom format Elixir source code.

## v1.13.1 (2021-12-14)

### 1. Bug fixes

#### Elixir

* [Code] Do not show code snippets in `SyntaxError` and `TokenMissingError` if line is empty
* [Exception] Do not fail blaming `ArgumentError` for improper lists on `apply/3`
* [Macro] Set a max `line_length` for `Macro.to_string/1`
* [Macro] Fix formatting of lists on module attributes for `Macro.to_string/1`
* [String] Fix incorrect codepoint byte counting in `slice` with negative positions in ranges
* [Task] Ensure async streams can be consumed from another process than the one that creates them
* [URI] Undeprecate `URI.parse/1` as `URI.new/1` is too strict in many common cases
* [URI] Make sure `URI.new/1` returns nil for empty paths

#### IEx

* [IEx] Make sure the `--version` flag halts IEx

#### Mix

* [Mix] Make protocol consolidation part of the `Mix.install/2` cache

## v1.13.0 (2021-12-03)

### 1. Enhancements
@@ -151,7 +174,7 @@ Finally, the `Code` module has also been augmented with two functions: `Code.str
* [Inspect] Allow default inspect fun to be set globally with `Inspect.Opts.default_inspect_fun/1`
* [IO] Allow `:eof` to be given as limit to `IO.getn/2`
* [Kernel] Support the `:sigils` option in `import Mod, only: :sigils` and allow the sigil modifiers to be also digits
* [Kernel] Make `get_in` consistently abort when `nil` values are found
* [Kernel] Make `get_in` consistently abort and return `nil` when `nil` values are found (previously Elixir would raise an error in this case). This allows a user to use `get_in` as a safe navigation operator.
* [Kernel] Improve compilation times by reducing the amount of copies of the AST across compiler processes
* [Kernel] Raise if trying to define a module with a slash in its name
* [Kernel] Warn when `?\` is used and there is no need for a escape character
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.13.0
1.13.1
4 changes: 2 additions & 2 deletions bin/elixir
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/sh
set -e

ELIXIR_VERSION=1.13.0
ELIXIR_VERSION=1.13.1

if [ $# -eq 0 ] || { [ $# -eq 1 ] && { [ "$1" = "--help" ] || [ "$1" = "-h" ]; }; }; then
cat <<USAGE >&2
@@ -16,7 +16,7 @@ Usage: $(basename "$0") [options] [.exs file] [data]
-pr "FILE" Requires the given files/patterns in parallel (*)
-pa "PATH" Prepends the given path to Erlang code path (*)
-pz "PATH" Appends the given path to Erlang code path (*)
-v, --version Prints Erlang/OTP and Elixir versions
-v, --version Prints Erlang/OTP and Elixir versions (standalone)
--app APP Starts the given app and its dependencies (*)
--erl "SWITCHES" Switches to be passed down to Erlang (*)
4 changes: 2 additions & 2 deletions bin/elixir.bat
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@if defined ELIXIR_CLI_ECHO (@echo on) else (@echo off)

set ELIXIR_VERSION=1.13.0
set ELIXIR_VERSION=1.13.1

setlocal enabledelayedexpansion
if ""%1""=="""" if ""%2""=="""" goto documentation
@@ -23,7 +23,7 @@ echo -S SCRIPT Finds and executes the given script in $PATH
echo -pr "FILE" Requires the given files/patterns in parallel (*)
echo -pa "PATH" Prepends the given path to Erlang code path (*)
echo -pz "PATH" Appends the given path to Erlang code path (*)
echo -v, --version Prints Erlang/OTP and Elixir versions
echo -v, --version Prints Erlang/OTP and Elixir versions (standalone)
echo.
echo --app APP Starts the given app and its dependencies (*)
echo --erl "SWITCHES" Switches to be passed down to Erlang (*)
2 changes: 1 addition & 1 deletion bin/elixirc
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ Usage: $(basename "$0") [elixir switches] [compiler switches] [.ex files]
-h, --help Prints this message and exits
-o The directory to output compiled files
-v, --version Prints Elixir version and exits
-v, --version Prints Elixir version and exits (standalone)
--ignore-module-conflict Does not emit warnings if a module was previously defined
--no-debug-info Does not attach debug info to compiled modules
2 changes: 1 addition & 1 deletion bin/elixirc.bat
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ echo Usage: %~nx0 [elixir switches] [compiler switches] [.ex files]
echo.
echo -h, --help Prints this message and exits
echo -o The directory to output compiled files
echo -v, --version Prints Elixir version and exits
echo -v, --version Prints Elixir version and exits (standalone)
echo.
echo --ignore-module-conflict Does not emit warnings if a module was previously defined
echo --no-debug-info Does not attach debug info to compiled modules
33 changes: 18 additions & 15 deletions lib/elixir/lib/base.ex
Original file line number Diff line number Diff line change
@@ -92,6 +92,9 @@ defmodule Base do
"""

@type encode_case :: :upper | :lower
@type decode_case :: :upper | :lower | :mixed

b16_alphabet = '0123456789ABCDEF'
b64_alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
b64url_alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'
@@ -267,7 +270,7 @@ defmodule Base do
"666f6f626172"
"""
@spec encode16(binary, keyword) :: binary
@spec encode16(binary, case: encode_case) :: binary
def encode16(data, opts \\ []) when is_binary(data) do
case = Keyword.get(opts, :case, :upper)
do_encode16(case, data)
@@ -300,7 +303,7 @@ defmodule Base do
{:ok, "foobar"}
"""
@spec decode16(binary, keyword) :: {:ok, binary} | :error
@spec decode16(binary, case: decode_case) :: {:ok, binary} | :error
def decode16(string, opts \\ []) do
{:ok, decode16!(string, opts)}
rescue
@@ -337,7 +340,7 @@ defmodule Base do
"foobar"
"""
@spec decode16!(binary, keyword) :: binary
@spec decode16!(binary, case: encode_case) :: binary
def decode16!(string, opts \\ [])

def decode16!(string, opts) when is_binary(string) and rem(byte_size(string), 2) == 0 do
@@ -367,7 +370,7 @@ defmodule Base do
"Zm9vYg"
"""
@spec encode64(binary, keyword) :: binary
@spec encode64(binary, padding: boolean) :: binary
def encode64(data, opts \\ []) when is_binary(data) do
pad? = Keyword.get(opts, :padding, true)
do_encode64(data, pad?)
@@ -397,7 +400,7 @@ defmodule Base do
{:ok, "foob"}
"""
@spec decode64(binary, keyword) :: {:ok, binary} | :error
@spec decode64(binary, ignore: :whitespace, padding: boolean) :: {:ok, binary} | :error
def decode64(string, opts \\ []) when is_binary(string) do
{:ok, decode64!(string, opts)}
rescue
@@ -431,7 +434,7 @@ defmodule Base do
"foob"
"""
@spec decode64!(binary, keyword) :: binary
@spec decode64!(binary, ignore: :whitespace, padding: boolean) :: binary
def decode64!(string, opts \\ []) when is_binary(string) do
pad? = Keyword.get(opts, :padding, true)
string |> remove_ignored(opts[:ignore]) |> do_decode64(pad?)
@@ -453,7 +456,7 @@ defmodule Base do
"_3_-_A"
"""
@spec url_encode64(binary, keyword) :: binary
@spec url_encode64(binary, padding: boolean) :: binary
def url_encode64(data, opts \\ []) when is_binary(data) do
pad? = Keyword.get(opts, :padding, true)
do_encode64url(data, pad?)
@@ -481,7 +484,7 @@ defmodule Base do
{:ok, <<255, 127, 254, 252>>}
"""
@spec url_decode64(binary, keyword) :: {:ok, binary} | :error
@spec url_decode64(binary, ignore: :whitespace, padding: boolean) :: {:ok, binary} | :error
def url_decode64(string, opts \\ []) when is_binary(string) do
{:ok, url_decode64!(string, opts)}
rescue
@@ -513,7 +516,7 @@ defmodule Base do
<<255, 127, 254, 252>>
"""
@spec url_decode64!(binary, keyword) :: binary
@spec url_decode64!(binary, ignore: :whitespace, padding: boolean) :: binary
def url_decode64!(string, opts \\ []) when is_binary(string) do
pad? = Keyword.get(opts, :padding, true)
string |> remove_ignored(opts[:ignore]) |> do_decode64url(pad?)
@@ -551,7 +554,7 @@ defmodule Base do
"MZXW6YTBOI"
"""
@spec encode32(binary, keyword) :: binary
@spec encode32(binary, case: encode_case, padding: boolean) :: binary
def encode32(data, opts \\ []) when is_binary(data) do
case = Keyword.get(opts, :case, :upper)
pad? = Keyword.get(opts, :padding, true)
@@ -594,7 +597,7 @@ defmodule Base do
{:ok, "foobar"}
"""
@spec decode32(binary, keyword) :: {:ok, binary} | :error
@spec decode32(binary, case: decode_case, padding: boolean) :: {:ok, binary} | :error
def decode32(string, opts \\ []) do
{:ok, decode32!(string, opts)}
rescue
@@ -640,7 +643,7 @@ defmodule Base do
"foobar"
"""
@spec decode32!(binary, keyword) :: binary
@spec decode32!(binary, case: decode_case, padding: boolean) :: binary
def decode32!(string, opts \\ []) when is_binary(string) do
case = Keyword.get(opts, :case, :upper)
pad? = Keyword.get(opts, :padding, true)
@@ -680,7 +683,7 @@ defmodule Base do
"CPNMUOJ1E8"
"""
@spec hex_encode32(binary, keyword) :: binary
@spec hex_encode32(binary, case: encode_case, padding: boolean) :: binary
def hex_encode32(data, opts \\ []) when is_binary(data) do
case = Keyword.get(opts, :case, :upper)
pad? = Keyword.get(opts, :padding, true)
@@ -724,7 +727,7 @@ defmodule Base do
{:ok, "foobar"}
"""
@spec hex_decode32(binary, keyword) :: {:ok, binary} | :error
@spec hex_decode32(binary, case: decode_case, padding: boolean) :: {:ok, binary} | :error
def hex_decode32(string, opts \\ []) do
{:ok, hex_decode32!(string, opts)}
rescue
@@ -771,7 +774,7 @@ defmodule Base do
"foobar"
"""
@spec hex_decode32!(binary, keyword) :: binary
@spec hex_decode32!(binary, case: decode_case, padding: boolean) :: binary
def hex_decode32!(string, opts \\ []) when is_binary(string) do
case = Keyword.get(opts, :case, :upper)
pad? = Keyword.get(opts, :padding, true)
63 changes: 58 additions & 5 deletions lib/elixir/lib/calendar/datetime.ex
Original file line number Diff line number Diff line change
@@ -2,11 +2,14 @@ defmodule DateTime do
@moduledoc """
A datetime implementation with a time zone.
This datetime can be seen as an ephemeral snapshot
of a datetime at a given time zone. For such purposes,
it also includes both UTC and Standard offsets, as
well as the zone abbreviation field used exclusively
for formatting purposes.
This datetime can be seen as a snapshot of a date and time
at a given time zone. For such purposes, it also includes both
UTC and Standard offsets, as well as the zone abbreviation
field used exclusively for formatting purposes. Note future
datetimes are not necessarily guaranteed to exist, as time
zones may change any time in the future due to geopolitical
reasons. See the "Datetimes as snapshots" section for more
information.
Remember, comparisons in Elixir using `==/2`, `>/2`, `</2` and friends
are structural and based on the DateTime struct fields. For proper
@@ -41,6 +44,56 @@ defmodule DateTime do
Calendar.put_time_zone_database(Tzdata.TimeZoneDatabase)
See the proper names in the library installation instructions.
## Datetimes as snapshots
In the first section, we described datetimes as a "snapshot of
a date and time at a given time zone". To understand precisely
what we mean, let's see an example.
Imagine someone in Poland wants to schedule a meeting with someone
in Brazil in the next year. The meeting will happen at 2:30 AM
in the Polish time zone. At what time will the meeting happen in
Brazil?
You can consult the time zone database today, one year before,
using the API in this module and it will give you an answer that
is valid right now. However, this answer may not be valid in the
future. Why? Because both Brazil and Poland may change their timezone
rules, ultimately affecting the result. For example, a country may
choose to enter or abandon "Daylight Saving Time", which is a
process where we adjust the clock one hour forward or one hour
back once per year. Whenener the rules change, the exact instant
that 2:30 AM in Polish time will be in Brazil may change.
In other words, whenever working with future DateTimes, there is
no guarantee the results you get will always be correct, until
the event actually happens. Therefore, when you ask for a future
time, the answers you get are a snapshot that reflects the current
state of the time zone rules. For datetimes in the past, this is
not a problem, because time zone rules do not change for past
events.
To make matters worse, it may be that the 2:30 AM in Polish time
does not actually even exist or it is ambiguous. If a certain
time zone observes "Daylight Saving Time", they will move their
clock forward once a year. When this happens, there is a whole
hour that does not exist. Then, when they move the clock back,
there is a certain hour that will happen twice. So if you want
to schedule a meeting when this shift back happens, you would
need to explicitly say which of the 2:30 AM you precisely mean.
Applications that are date and time sensitive, need to take
these scenarios into account and correctly communicate them to
users.
The good news is: Elixir contains all of the building blocks
necessary to tackle those problems. The default timezone database
used by Elixir, `Calendar.UTCOnlyTimeZoneDatabase`, only works
with UTC, which does not observe those issues. Once you bring
a proper time zone database, the functions in this module will
query the database and return the relevant information. For
example, look at how `DateTime.new/4` returns different results
based on the scenarios described in this section.
"""

@enforce_keys [:year, :month, :day, :hour, :minute, :second] ++
2 changes: 1 addition & 1 deletion lib/elixir/lib/code/fragment.ex
Original file line number Diff line number Diff line change
@@ -525,7 +525,7 @@ defmodule Code.Fragment do
|> position_surround_context(line, column, opts)
end

def surround_context(other, position, opts) do
def surround_context(other, {_, _} = position, opts) do
surround_context(to_charlist(other), position, opts)
end

13 changes: 9 additions & 4 deletions lib/elixir/lib/code/normalizer.ex
Original file line number Diff line number Diff line change
@@ -200,10 +200,15 @@ defmodule Code.Normalizer do
# Module attributes
defp do_normalize({:@, meta, [{name, name_meta, [value]}]}, state) do
value =
if is_list(value) do
normalize_kw_args(value, state, false)
else
do_normalize(value, state)
cond do
keyword?(value) ->
normalize_kw_args(value, state, true)

is_list(value) ->
normalize_literal(value, meta, state)

true ->
do_normalize(value, state)
end

{:@, meta, [{name, name_meta, [value]}]}
1 change: 1 addition & 0 deletions lib/elixir/lib/enum.ex
Original file line number Diff line number Diff line change
@@ -2578,6 +2578,7 @@ defmodule Enum do
[:a, :b, :c, :e, :f, :g, :d]
"""
@doc since: "1.13.0"
def slide(enumerable, range_or_single_index, insertion_index)

def slide(enumerable, single_index, insertion_index) when is_integer(single_index) do
11 changes: 7 additions & 4 deletions lib/elixir/lib/exception.ex
Original file line number Diff line number Diff line change
@@ -732,6 +732,10 @@ defmodule ArgumentError do
) do
message =
cond do
not proper_list?(args) ->
"you attempted to apply a function named #{inspect(function)} on module #{inspect(module)} " <>
"with arguments #{inspect(args)}. Arguments (the third argument of apply) must always be a proper list"

# Note that args may be an empty list even if they were supplied
not is_atom(module) and is_atom(function) and args == [] ->
"you attempted to apply a function named #{inspect(function)} on #{inspect(module)}. " <>
@@ -747,10 +751,6 @@ defmodule ArgumentError do
"you attempted to apply a function named #{inspect(function)} on module #{inspect(module)}. " <>
"However #{inspect(function)} is not a valid function name. Function names (the second argument " <>
"of apply) must always be an atom"

not is_list(args) ->
"you attempted to apply a function named #{inspect(function)} on module #{inspect(module)} " <>
"with arguments #{inspect(args)}. Arguments (the third argument of apply) must always be a list"
end

{%{exception | message: message}, stacktrace}
@@ -759,6 +759,9 @@ defmodule ArgumentError do
def blame(exception, stacktrace) do
{exception, stacktrace}
end

defp proper_list?(list) when length(list) >= 0, do: true
defp proper_list?(_), do: false
end

defmodule ArithmeticError do
Loading