Skip to content

Commit

Permalink
Merge branch 'elixir-lang:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasvegi authored Sep 11, 2023
2 parents 304fde0 + 4774c27 commit 9103a18
Show file tree
Hide file tree
Showing 20 changed files with 257 additions and 77 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/builds.hex.pm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
build_docs: build_docs
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 50
- name: Get tags
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-markdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:

steps:
- name: Check out the repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 10

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
development: true
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 50
- uses: erlef/setup-beam@v1
Expand Down Expand Up @@ -82,7 +82,7 @@ jobs:
steps:
- name: Configure Git
run: git config --global core.autocrlf input
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 50
- uses: erlef/setup-beam@v1
Expand All @@ -107,7 +107,7 @@ jobs:
name: Check POSIX-compliant
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 50
- name: Install Shellcheck
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/notify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-20.04
name: Notify
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 50
- uses: erlef/setup-beam@v1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
build_docs: build_docs
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 50
- uses: ./.github/workflows/release_pre_built
Expand Down
61 changes: 55 additions & 6 deletions lib/elixir/lib/access.ex
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,13 @@ defmodule Access do
case __STACKTRACE__ do
[unquote(top) | _] ->
reason =
"#{inspect(unquote(module))} does not implement the Access behaviour. " <>
"If you are using get_in/put_in/update_in, you can specify the field " <>
"to be accessed using Access.key!/1"
"""
#{inspect(unquote(module))} does not implement the Access behaviour
You can use the "struct.field" syntax to access struct fields. \
You can also use Access.key!/1 to access struct fields dynamically \
inside get_in/put_in/update_in\
"""

%{unquote(exception) | reason: reason}

Expand Down Expand Up @@ -326,9 +330,23 @@ defmodule Access do
end
end

def get(list, key, _default) when is_list(list) and is_integer(key) do
raise ArgumentError, """
the Access module does not support accessing lists by index, got: #{inspect(key)}
Accessing a list by index is typically discouraged in Elixir, \
instead we prefer to use the Enum module to manipulate lists \
as a whole. If you really must access a list element by index, \
you can Enum.at/1 or the functions in the List module\
"""
end

def get(list, key, _default) when is_list(list) do
raise ArgumentError,
"the Access calls for keywords expect the key to be an atom, got: " <> inspect(key)
raise ArgumentError, """
the Access module supports only keyword lists (with atom keys), got: #{inspect(key)}
If you want to search lists of tuples, use List.keyfind/3\
"""
end

def get(nil, _key, default) do
Expand Down Expand Up @@ -377,10 +395,26 @@ defmodule Access do
Map.get_and_update(map, key, fun)
end

def get_and_update(list, key, fun) when is_list(list) do
def get_and_update(list, key, fun) when is_list(list) and is_atom(key) do
Keyword.get_and_update(list, key, fun)
end

def get_and_update(list, key, _fun) when is_list(list) and is_integer(key) do
raise ArgumentError, """
the Access module does not support accessing lists by index, got: #{inspect(key)}
Accessing a list by index is typically discouraged in Elixir, \
instead we prefer to use the Enum module to manipulate lists \
as a whole. If you really must mostify a list element by index, \
you can Access.at/1 or the functions in the List module\
"""
end

def get_and_update(list, key, _fun) when is_list(list) do
raise ArgumentError,
"the Access module supports only keyword lists (with atom keys), got: " <> inspect(key)
end

def get_and_update(nil, key, _fun) do
raise ArgumentError, "could not put/update key #{inspect(key)} on a nil value"
end
Expand Down Expand Up @@ -507,6 +541,21 @@ defmodule Access do
iex> get_in(map, [Access.key!(:user), Access.key!(:unknown)])
** (KeyError) key :unknown not found in: %{name: \"john\"}
The examples above could be partially written as:
iex> map = %{user: %{name: "john"}}
iex> map.user.name
"john"
iex> get_and_update_in(map.user.name, fn prev ->
...> {prev, String.upcase(prev)}
...> end)
{"john", %{user: %{name: "JOHN"}}}
However, it is not possible to remove fields using the dot notation,
as it is implified those fields must also be present. In any case,
`Access.key!/1` is useful when the key is not known in advance
and must be accessed dynamically.
An error is raised if the accessed structure is not a map/struct:
iex> get_in([], [Access.key!(:foo)])
Expand Down
1 change: 1 addition & 0 deletions lib/elixir/lib/code.ex
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ defmodule Code do
required(:message) => String.t(),
required(:position) => position,
required(:stacktrace) => Exception.stacktrace(),
required(:span) => {non_neg_integer, non_neg_integer} | nil,
optional(any()) => any()
}

Expand Down
11 changes: 7 additions & 4 deletions lib/elixir/lib/exception.ex
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ defmodule SyntaxError do
})
when not is_nil(snippet) and not is_nil(column) do
snippet =
:elixir_errors.format_snippet({line, column}, file, description, snippet, :error, [])
:elixir_errors.format_snippet({line, column}, file, description, snippet, :error, [], nil)

format_message(file, line, column, snippet)
end
Expand All @@ -965,7 +965,8 @@ defmodule SyntaxError do
column: column,
description: description
}) do
snippet = :elixir_errors.format_snippet({line, column}, file, description, nil, :error, [])
snippet =
:elixir_errors.format_snippet({line, column}, file, description, nil, :error, [], nil)

padded = " " <> String.replace(snippet, "\n", "\n ")
format_message(file, line, column, padded)
Expand Down Expand Up @@ -1002,7 +1003,7 @@ defmodule TokenMissingError do
})
when not is_nil(snippet) and not is_nil(column) do
snippet =
:elixir_errors.format_snippet({line, column}, file, description, snippet, :error, [])
:elixir_errors.format_snippet({line, column}, file, description, snippet, :error, [], nil)

format_message(file, line, column, snippet)
end
Expand All @@ -1014,7 +1015,9 @@ defmodule TokenMissingError do
column: column,
description: description
}) do
snippet = :elixir_errors.format_snippet({line, column}, file, description, nil, :error, [])
snippet =
:elixir_errors.format_snippet({line, column}, file, description, nil, :error, [], nil)

padded = " " <> String.replace(snippet, "\n", "\n ")
format_message(file, line, column, padded)
end
Expand Down
9 changes: 6 additions & 3 deletions lib/elixir/lib/io.ex
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,10 @@ defmodule IO do

def warn(message, %Macro.Env{line: line, file: file} = env) do
message = to_chardata(message)
:elixir_errors.emit_diagnostic(:warning, line, file, message, Macro.Env.stacktrace(env), true)

:elixir_errors.emit_diagnostic(:warning, line, file, message, Macro.Env.stacktrace(env),
read_snippet: true
)
end

def warn(message, [{_, _} | _] = keyword) do
Expand All @@ -351,12 +354,12 @@ defmodule IO do

def warn(message, []) do
message = to_chardata(message)
:elixir_errors.emit_diagnostic(:warning, 0, nil, message, [], false)
:elixir_errors.emit_diagnostic(:warning, 0, nil, message, [], read_snippet: false)
end

def warn(message, [{_, _, _, _} | _] = stacktrace) do
message = to_chardata(message)
:elixir_errors.emit_diagnostic(:warning, 0, nil, message, stacktrace, false)
:elixir_errors.emit_diagnostic(:warning, 0, nil, message, stacktrace, read_snippet: false)
end

@doc false
Expand Down
13 changes: 11 additions & 2 deletions lib/elixir/lib/kernel/parallel_compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,8 @@ defmodule Kernel.ParallelCompiler do
file: Path.absname(file),
position: nil,
message: description,
stacktrace: stacktrace
stacktrace: stacktrace,
span: nil
}
end
end
Expand Down Expand Up @@ -892,7 +893,15 @@ defmodule Kernel.ParallelCompiler do
line = get_line(file, reason, stack)
file = Path.absname(file)
message = :unicode.characters_to_binary(Kernel.CLI.format_error(kind, reason, stack))
%{file: file, position: line || 0, message: message, severity: :error, stacktrace: stack}

%{
file: file,
position: line || 0,
message: message,
severity: :error,
stacktrace: stack,
span: nil
}
end

defp get_line(_file, %{line: line, column: column}, _stack)
Expand Down
3 changes: 2 additions & 1 deletion lib/elixir/lib/module/parallel_checker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,8 @@ defmodule Module.ParallelChecker do
file: file,
position: position_to_tuple(position),
message: IO.iodata_to_binary(message),
stacktrace: [to_stacktrace(file, position, mfa)]
stacktrace: [to_stacktrace(file, position, mfa)],
span: nil
}
end

Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,7 @@ defmodule Supervisor do
`module.child_spec([])`.
After the child specification is retrieved, the fields on `overrides`
are directly applied on the child spec. If `overrides` has keys that
are directly applied to the child spec. If `overrides` has keys that
do not map to any child specification field, an error is raised.
See the "Child specification" section in the module documentation
Expand Down
19 changes: 16 additions & 3 deletions lib/elixir/pages/getting-started/erlang-libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,29 @@

Elixir provides excellent interoperability with Erlang libraries. In fact, Elixir discourages simply wrapping Erlang libraries in favor of directly interfacing with Erlang code. In this section, we will present some of the most common and useful Erlang functionality that is not found in Elixir.

Erlang modules have a different naming convention than in Elixir and start in lowercase. In both cases, module names are atoms and we invoke functions by dispatching to the module name:

```elixir
iex> is_atom(String)
true
iex> String.first("hello")
"h"
iex> is_atom(:binary)
true
iex> :binary.first("hello")
104
```

As you grow more proficient in Elixir, you may want to explore the Erlang [STDLIB Reference Manual](http://www.erlang.org/doc/apps/stdlib/index.html) in more detail.

## The binary module

The built-in Elixir String module handles binaries that are UTF-8 encoded. [The `:binary` module](`:binary`) is useful when you are dealing with binary data that is not necessarily UTF-8 encoded.

```elixir
iex> String.to_charlist "Ø"
iex> String.to_charlist("Ø")
[216]
iex> :binary.bin_to_list "Ø"
iex> :binary.bin_to_list("Ø")
[195, 152]
```

Expand All @@ -25,7 +38,7 @@ Elixir does not contain a function similar to `printf` found in C and other lang
iex> :io.format("Pi is approximately given by:~10.3f~n", [:math.pi])
Pi is approximately given by: 3.142
:ok
iex> to_string :io_lib.format("Pi is approximately given by:~10.3f~n", [:math.pi])
iex> to_string(:io_lib.format("Pi is approximately given by:~10.3f~n", [:math.pi]))
"Pi is approximately given by: 3.142\n"
```

Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/pages/getting-started/lists-and-tuples.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ On the other hand, `String.split_at/2` splits a string in two parts at a given p
```elixir
iex> String.split_at("hello world", 3)
{"hel", "lo world"}
iex> String.split_at("hello world", 3)
iex> String.split_at("hello world", -4)
{"hello w", "orld"}
```

Expand Down
Loading

0 comments on commit 9103a18

Please sign in to comment.