Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a9a4f2cab | ||
|
|
0847e4b41a | ||
|
|
7aafd6ca77 | ||
|
|
5ff421ca27 | ||
|
|
4425d684a0 | ||
|
|
e3f538cd25 |
+1
-23
@@ -8,28 +8,6 @@
|
|||||||
|
|
||||||
## Type system improvements
|
## Type system improvements
|
||||||
|
|
||||||
### More type inference
|
|
||||||
|
|
||||||
Elixir now performs inference of whole functions. The best way to show the new capabilities are with examples. Take the following code:
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
def add_foo_and_bar(data) do
|
|
||||||
data.foo + data.bar
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Elixir now infers that the function expects a `map` as first argument, and the map must have the keys `.foo` and `.bar` whose values are either `integer()` or `float()`. The return type will be either `integer()` or `float()`.
|
|
||||||
|
|
||||||
Here is another example:
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
def sum_to_string(a, b) do
|
|
||||||
Integer.to_string(a + b)
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Even though the `+` operator works with both integers and floats, Elixir infers that `a` and `b` must be both integers, as the result of `+` is given to a function that expects an integer. The inferred type information is then used during type checking to find possible typing errors.
|
|
||||||
|
|
||||||
### Type checking of protocol dispatch and implementations
|
### Type checking of protocol dispatch and implementations
|
||||||
|
|
||||||
This release also adds type checking when dispatching and implementing protocols.
|
This release also adds type checking when dispatching and implementing protocols.
|
||||||
@@ -216,7 +194,7 @@ These additions offer greater transparency into the components and licenses of e
|
|||||||
|
|
||||||
This work was performed by Jonatan Männchen and sponsored by the Erlang Ecosystem Foundation.
|
This work was performed by Jonatan Männchen and sponsored by the Erlang Ecosystem Foundation.
|
||||||
|
|
||||||
## v1.19.0-dev
|
## v1.19.0-rc.0 (2025-06-09)
|
||||||
|
|
||||||
### 1. Enhancements
|
### 1. Enhancements
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ PREFIX ?= /usr/local
|
|||||||
TEST_FILES ?= "*_test.exs"
|
TEST_FILES ?= "*_test.exs"
|
||||||
SHARE_PREFIX ?= $(PREFIX)/share
|
SHARE_PREFIX ?= $(PREFIX)/share
|
||||||
MAN_PREFIX ?= $(SHARE_PREFIX)/man
|
MAN_PREFIX ?= $(SHARE_PREFIX)/man
|
||||||
CANONICAL := main/
|
# CANONICAL := main/
|
||||||
ELIXIRC := bin/elixirc --ignore-module-conflict $(ELIXIRC_OPTS)
|
ELIXIRC := bin/elixirc --ignore-module-conflict $(ELIXIRC_OPTS)
|
||||||
ELIXIRC_MIN_SIG := $(ELIXIRC) -e 'Code.put_compiler_option :infer_signatures, []'
|
ELIXIRC_MIN_SIG := $(ELIXIRC) -e 'Code.put_compiler_option :infer_signatures, []'
|
||||||
ERLC := erlc -I lib/elixir/include
|
ERLC := erlc -I lib/elixir/include
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
## Shipping a new version
|
## Shipping a new version
|
||||||
|
|
||||||
1. Update version in /VERSION, bin/elixir, bin/elixir.bat, and bin/elixir.ps1
|
1. Update version in /VERSION, bin/elixir, and bin/elixir.bat
|
||||||
|
|
||||||
2. Ensure /CHANGELOG.md is updated, versioned and add the current date
|
2. Ensure /CHANGELOG.md is updated, versioned and add the current date
|
||||||
- If this release addresses any publicly known security vulnerabilities with
|
- If this release addresses any publicly known security vulnerabilities with
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
### In the new branch
|
### In the new branch
|
||||||
|
|
||||||
1. Comment the `CANONICAL=` in /Makefile
|
1. Comment out `CANONICAL := main/` in /Makefile
|
||||||
|
|
||||||
2. Update tables in /SECURITY.md and "Compatibility and Deprecations"
|
2. Update tables in /SECURITY.md and "Compatibility and Deprecations"
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -12,12 +12,11 @@ Elixir applies bug fixes only to the latest minor branch. Security patches are a
|
|||||||
|
|
||||||
Elixir version | Support
|
Elixir version | Support
|
||||||
:------------- | :-----------------------------
|
:------------- | :-----------------------------
|
||||||
1.19 | Development
|
1.19 | Bug fixes and security patches
|
||||||
1.18 | Bug fixes and security patches
|
1.18 | Security patches only
|
||||||
1.17 | Security patches only
|
1.17 | Security patches only
|
||||||
1.16 | Security patches only
|
1.16 | Security patches only
|
||||||
1.15 | Security patches only
|
1.15 | Security patches only
|
||||||
1.14 | Security patches only
|
|
||||||
|
|
||||||
## Announcements
|
## Announcements
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
ELIXIR_VERSION=1.19.0-dev
|
ELIXIR_VERSION=1.19.0-rc.0
|
||||||
|
|
||||||
if [ $# -eq 0 ] || { [ $# -eq 1 ] && { [ "$1" = "--help" ] || [ "$1" = "-h" ]; }; }; then
|
if [ $# -eq 0 ] || { [ $# -eq 1 ] && { [ "$1" = "--help" ] || [ "$1" = "-h" ]; }; }; then
|
||||||
cat <<USAGE >&2
|
cat <<USAGE >&2
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
:: SPDX-FileCopyrightText: 2021 The Elixir Team
|
:: SPDX-FileCopyrightText: 2021 The Elixir Team
|
||||||
:: SPDX-FileCopyrightText: 2012 Plataformatec
|
:: SPDX-FileCopyrightText: 2012 Plataformatec
|
||||||
|
|
||||||
set ELIXIR_VERSION=1.19.0-dev
|
set ELIXIR_VERSION=1.19.0-rc.0
|
||||||
|
|
||||||
if ""%1""=="""" if ""%2""=="""" goto documentation
|
if ""%1""=="""" if ""%2""=="""" goto documentation
|
||||||
if /I ""%1""==""--help"" if ""%2""=="""" goto documentation
|
if /I ""%1""==""--help"" if ""%2""=="""" goto documentation
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ defmodule Module.Types do
|
|||||||
local_handler: handler,
|
local_handler: handler,
|
||||||
# Control if variable refinement is enabled.
|
# Control if variable refinement is enabled.
|
||||||
# It is disabled only on dynamic dispatches.
|
# It is disabled only on dynamic dispatches.
|
||||||
refine_vars: true
|
refine_vars: false
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ For convenience, the markup notation to generate the admonition block above is t
|
|||||||
|
|
||||||
#### Problem
|
#### Problem
|
||||||
|
|
||||||
This anti-pattern is the opposite of ["Compile-time dependencies"](#compile-time-dependencies) and it happens when a compile-time dependency is accidentally bypassed, making the Elixir compiler is to track dependencies and recompile files correctly. This happens when building aliases (in other words, module names) dynamically, either within a module or within a macro.
|
This anti-pattern is the opposite of ["Compile-time dependencies"](#compile-time-dependencies) and it happens when a compile-time dependency is accidentally bypassed, making the Elixir compiler unable to track dependencies and recompile files correctly. This happens when building aliases (in other words, module names) dynamically, either within a module or within a macro.
|
||||||
|
|
||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,11 @@ Elixir applies bug fixes only to the latest minor branch. Security patches are a
|
|||||||
|
|
||||||
Elixir version | Support
|
Elixir version | Support
|
||||||
:------------- | :-----------------------------
|
:------------- | :-----------------------------
|
||||||
1.19 | Development
|
1.19 | Bug fixes and security patches
|
||||||
1.18 | Bug fixes and security patches
|
1.18 | Security patches only
|
||||||
1.17 | Security patches only
|
1.17 | Security patches only
|
||||||
1.16 | Security patches only
|
1.16 | Security patches only
|
||||||
1.15 | Security patches only
|
1.15 | Security patches only
|
||||||
1.14 | Security patches only
|
|
||||||
|
|
||||||
New releases are announced in the read-only [announcements mailing list](https://groups.google.com/group/elixir-lang-ann). All security releases [will be tagged with `[security]`](https://groups.google.com/forum/#!searchin/elixir-lang-ann/%5Bsecurity%5D%7Csort:date).
|
New releases are announced in the read-only [announcements mailing list](https://groups.google.com/group/elixir-lang-ann). All security releases [will be tagged with `[security]`](https://groups.google.com/forum/#!searchin/elixir-lang-ann/%5Bsecurity%5D%7Csort:date).
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ Erlang/OTP versioning is independent from the versioning of Elixir. Erlang relea
|
|||||||
|
|
||||||
Elixir version | Supported Erlang/OTP versions
|
Elixir version | Supported Erlang/OTP versions
|
||||||
:------------- | :-------------------------------
|
:------------- | :-------------------------------
|
||||||
1.19 | 26 - 27
|
1.19 | 26 - 28
|
||||||
1.18 | 25 - 27
|
1.18 | 25 - 27
|
||||||
1.17 | 25 - 27
|
1.17 | 25 - 27
|
||||||
1.16 | 24 - 26
|
1.16 | 24 - 26
|
||||||
@@ -236,4 +235,4 @@ Version | Deprecated feature | Replaced by (ava
|
|||||||
[v1.16]: https://github.com/elixir-lang/elixir/blob/v1.16/CHANGELOG.md#4-hard-deprecations
|
[v1.16]: https://github.com/elixir-lang/elixir/blob/v1.16/CHANGELOG.md#4-hard-deprecations
|
||||||
[v1.17]: https://github.com/elixir-lang/elixir/blob/v1.17/CHANGELOG.md#4-hard-deprecations
|
[v1.17]: https://github.com/elixir-lang/elixir/blob/v1.17/CHANGELOG.md#4-hard-deprecations
|
||||||
[v1.18]: https://github.com/elixir-lang/elixir/blob/v1.18/CHANGELOG.md#4-hard-deprecations
|
[v1.18]: https://github.com/elixir-lang/elixir/blob/v1.18/CHANGELOG.md#4-hard-deprecations
|
||||||
[v1.19]: https://github.com/elixir-lang/elixir/blob/main/CHANGELOG.md#4-hard-deprecations
|
[v1.19]: https://github.com/elixir-lang/elixir/blob/v1.19/CHANGELOG.md#4-hard-deprecations
|
||||||
|
|||||||
@@ -43,16 +43,6 @@ defmodule Module.Types.ExprTest do
|
|||||||
assert typecheck!([x], [:ok | x]) == dynamic(non_empty_list(term(), term()))
|
assert typecheck!([x], [:ok | x]) == dynamic(non_empty_list(term(), term()))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "inference" do
|
|
||||||
assert typecheck!(
|
|
||||||
[x, y, z],
|
|
||||||
(
|
|
||||||
List.to_integer([x, y | z])
|
|
||||||
{x, y, z}
|
|
||||||
)
|
|
||||||
) == dynamic(tuple([integer(), integer(), list(integer())]))
|
|
||||||
end
|
|
||||||
|
|
||||||
test "hd" do
|
test "hd" do
|
||||||
assert typecheck!([x = [123, :foo]], hd(x)) == dynamic(union(atom([:foo]), integer()))
|
assert typecheck!([x = [123, :foo]], hd(x)) == dynamic(union(atom([:foo]), integer()))
|
||||||
assert typecheck!([x = [123 | :foo]], hd(x)) == dynamic(integer())
|
assert typecheck!([x = [123 | :foo]], hd(x)) == dynamic(integer())
|
||||||
@@ -127,16 +117,6 @@ defmodule Module.Types.ExprTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "funs" do
|
describe "funs" do
|
||||||
test "infers calls" do
|
|
||||||
assert typecheck!(
|
|
||||||
[x],
|
|
||||||
(
|
|
||||||
x.(1, 2)
|
|
||||||
x
|
|
||||||
)
|
|
||||||
) == dynamic(fun(2))
|
|
||||||
end
|
|
||||||
|
|
||||||
test "infers functions" do
|
test "infers functions" do
|
||||||
assert typecheck!(& &1) == fun([dynamic()], dynamic())
|
assert typecheck!(& &1) == fun([dynamic()], dynamic())
|
||||||
assert typecheck!(fn -> :ok end) == fun([], atom([:ok]))
|
assert typecheck!(fn -> :ok end) == fun([], atom([:ok]))
|
||||||
@@ -263,53 +243,6 @@ defmodule Module.Types.ExprTest do
|
|||||||
assert typecheck!([%x{}], x.foo_bar()) == dynamic()
|
assert typecheck!([%x{}], x.foo_bar()) == dynamic()
|
||||||
end
|
end
|
||||||
|
|
||||||
test "infers atoms" do
|
|
||||||
assert typecheck!(
|
|
||||||
[x],
|
|
||||||
(
|
|
||||||
x.foo_bar()
|
|
||||||
x
|
|
||||||
)
|
|
||||||
) == dynamic(atom())
|
|
||||||
|
|
||||||
assert typecheck!(
|
|
||||||
[x],
|
|
||||||
(
|
|
||||||
x.foo_bar(123)
|
|
||||||
x
|
|
||||||
)
|
|
||||||
) == dynamic(atom())
|
|
||||||
|
|
||||||
assert typecheck!(
|
|
||||||
[x],
|
|
||||||
(
|
|
||||||
&x.foo_bar/1
|
|
||||||
x
|
|
||||||
)
|
|
||||||
) == dynamic(atom())
|
|
||||||
end
|
|
||||||
|
|
||||||
test "infers maps" do
|
|
||||||
assert typecheck!(
|
|
||||||
[x],
|
|
||||||
(
|
|
||||||
:foo = x.foo_bar
|
|
||||||
123 = x.baz_bat
|
|
||||||
x
|
|
||||||
)
|
|
||||||
) == dynamic(open_map(foo_bar: atom([:foo]), baz_bat: integer()))
|
|
||||||
end
|
|
||||||
|
|
||||||
test "infers args" do
|
|
||||||
assert typecheck!(
|
|
||||||
[x, y],
|
|
||||||
(
|
|
||||||
z = Integer.to_string(x + y)
|
|
||||||
{x, y, z}
|
|
||||||
)
|
|
||||||
) == dynamic(tuple([integer(), integer(), binary()]))
|
|
||||||
end
|
|
||||||
|
|
||||||
test "undefined function warnings" do
|
test "undefined function warnings" do
|
||||||
assert typewarn!(URI.unknown("foo")) ==
|
assert typewarn!(URI.unknown("foo")) ==
|
||||||
{dynamic(), "URI.unknown/1 is undefined or private"}
|
{dynamic(), "URI.unknown/1 is undefined or private"}
|
||||||
@@ -535,16 +468,6 @@ defmodule Module.Types.ExprTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "binaries" do
|
describe "binaries" do
|
||||||
test "inference" do
|
|
||||||
assert typecheck!(
|
|
||||||
[x, y],
|
|
||||||
(
|
|
||||||
<<x::float-size(y)>>
|
|
||||||
{x, y}
|
|
||||||
)
|
|
||||||
) == dynamic(tuple([union(float(), integer()), integer()]))
|
|
||||||
end
|
|
||||||
|
|
||||||
test "warnings" do
|
test "warnings" do
|
||||||
assert typeerror!([<<x::binary-size(2)>>], <<x::float>>) ==
|
assert typeerror!([<<x::binary-size(2)>>], <<x::float>>) ==
|
||||||
~l"""
|
~l"""
|
||||||
@@ -644,16 +567,6 @@ defmodule Module.Types.ExprTest do
|
|||||||
assert typecheck!([x], {:ok, x}) == dynamic(tuple([atom([:ok]), term()]))
|
assert typecheck!([x], {:ok, x}) == dynamic(tuple([atom([:ok]), term()]))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "inference" do
|
|
||||||
assert typecheck!(
|
|
||||||
[x, y],
|
|
||||||
(
|
|
||||||
{:ok, :error} = {x, y}
|
|
||||||
{x, y}
|
|
||||||
)
|
|
||||||
) == dynamic(tuple([atom([:ok]), atom([:error])]))
|
|
||||||
end
|
|
||||||
|
|
||||||
test "elem/2" do
|
test "elem/2" do
|
||||||
assert typecheck!(elem({:ok, 123}, 0)) == atom([:ok])
|
assert typecheck!(elem({:ok, 123}, 0)) == atom([:ok])
|
||||||
assert typecheck!(elem({:ok, 123}, 1)) == integer()
|
assert typecheck!(elem({:ok, 123}, 1)) == integer()
|
||||||
@@ -1458,16 +1371,6 @@ defmodule Module.Types.ExprTest do
|
|||||||
) == dynamic(atom([:ok, :error, :timeout]))
|
) == dynamic(atom([:ok, :error, :timeout]))
|
||||||
end
|
end
|
||||||
|
|
||||||
test "infers type for timeout" do
|
|
||||||
assert typecheck!(
|
|
||||||
[x],
|
|
||||||
receive do
|
|
||||||
after
|
|
||||||
x -> x
|
|
||||||
end
|
|
||||||
) == dynamic(union(integer(), atom([:infinity])))
|
|
||||||
end
|
|
||||||
|
|
||||||
test "resets branches" do
|
test "resets branches" do
|
||||||
assert typecheck!(
|
assert typecheck!(
|
||||||
[x, timeout = :infinity],
|
[x, timeout = :infinity],
|
||||||
@@ -1854,16 +1757,6 @@ defmodule Module.Types.ExprTest do
|
|||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
test "infers binary generators" do
|
|
||||||
assert typecheck!(
|
|
||||||
[x],
|
|
||||||
(
|
|
||||||
for <<_ <- x>>, do: :ok
|
|
||||||
x
|
|
||||||
)
|
|
||||||
) == dynamic(binary())
|
|
||||||
end
|
|
||||||
|
|
||||||
test ":into" do
|
test ":into" do
|
||||||
assert typecheck!([binary], for(<<x <- binary>>, do: x)) == list(integer())
|
assert typecheck!([binary], for(<<x <- binary>>, do: x)) == list(integer())
|
||||||
assert typecheck!([binary], for(<<x <- binary>>, do: x, into: [])) == list(integer())
|
assert typecheck!([binary], for(<<x <- binary>>, do: x, into: [])) == list(integer())
|
||||||
@@ -1901,20 +1794,6 @@ defmodule Module.Types.ExprTest do
|
|||||||
end
|
end
|
||||||
) == union(atom([:ok]), union(integer(), float()))
|
) == union(atom([:ok]), union(integer(), float()))
|
||||||
end
|
end
|
||||||
|
|
||||||
test ":reduce inference" do
|
|
||||||
assert typecheck!(
|
|
||||||
[list, x],
|
|
||||||
(
|
|
||||||
123 =
|
|
||||||
for _ <- list, reduce: x do
|
|
||||||
x -> x
|
|
||||||
end
|
|
||||||
|
|
||||||
x
|
|
||||||
)
|
|
||||||
) == dynamic(integer())
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "info" do
|
describe "info" do
|
||||||
|
|||||||
@@ -51,20 +51,6 @@ defmodule Module.Types.InferTest do
|
|||||||
assert types[{:fun4, 4}] == {:infer, nil, [{args, atom([:ok])}]}
|
assert types[{:fun4, 4}] == {:infer, nil, [{args, atom([:ok])}]}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "infer types from expressions", config do
|
|
||||||
types =
|
|
||||||
infer config do
|
|
||||||
def fun(x) do
|
|
||||||
x.foo + x.bar
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
number = union(integer(), float())
|
|
||||||
|
|
||||||
assert types[{:fun, 1}] ==
|
|
||||||
{:infer, nil, [{[dynamic(open_map(foo: number, bar: number))], dynamic(number)}]}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "infer with Elixir built-in", config do
|
test "infer with Elixir built-in", config do
|
||||||
types =
|
types =
|
||||||
infer config do
|
infer config do
|
||||||
@@ -95,33 +81,6 @@ defmodule Module.Types.InferTest do
|
|||||||
]}
|
]}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "infers return types from private functions", config do
|
|
||||||
types =
|
|
||||||
infer config do
|
|
||||||
def pub(x), do: priv(x)
|
|
||||||
defp priv(:ok), do: :ok
|
|
||||||
defp priv(:error), do: :error
|
|
||||||
end
|
|
||||||
|
|
||||||
assert types[{:pub, 1}] ==
|
|
||||||
{:infer, nil, [{[dynamic(atom([:ok, :error]))], dynamic(atom([:ok, :error]))}]}
|
|
||||||
|
|
||||||
assert types[{:priv, 1}] == nil
|
|
||||||
end
|
|
||||||
|
|
||||||
test "infers return types from super functions", config do
|
|
||||||
types =
|
|
||||||
infer config do
|
|
||||||
def pub(:ok), do: :ok
|
|
||||||
def pub(:error), do: :error
|
|
||||||
defoverridable pub: 1
|
|
||||||
def pub(x), do: super(x)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert types[{:pub, 1}] ==
|
|
||||||
{:infer, nil, [{[dynamic(atom([:ok, :error]))], dynamic(atom([:ok, :error]))}]}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "infers return types even with loops", config do
|
test "infers return types even with loops", config do
|
||||||
types =
|
types =
|
||||||
infer config do
|
infer config do
|
||||||
|
|||||||
@@ -85,77 +85,6 @@ defmodule Module.Types.IntegrationTest do
|
|||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "writes exports with inferred map types" do
|
|
||||||
files = %{
|
|
||||||
"a.ex" => """
|
|
||||||
defmodule A do
|
|
||||||
defstruct [:x, :y, :z]
|
|
||||||
|
|
||||||
def struct_create_with_atom_keys(x) do
|
|
||||||
infer(y = %A{x: x})
|
|
||||||
{x, y}
|
|
||||||
end
|
|
||||||
|
|
||||||
def map_create_with_atom_keys(x) do
|
|
||||||
infer(%{__struct__: A, x: x, y: nil, z: nil})
|
|
||||||
x
|
|
||||||
end
|
|
||||||
|
|
||||||
def map_update_with_atom_keys(x) do
|
|
||||||
infer(%{x | y: nil})
|
|
||||||
x
|
|
||||||
end
|
|
||||||
|
|
||||||
def map_update_with_unknown_keys(x, y) do
|
|
||||||
infer(%{x | y => 123})
|
|
||||||
x
|
|
||||||
end
|
|
||||||
|
|
||||||
defp infer(%A{x: <<_::binary>>, y: nil}) do
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
modules = compile_modules(files)
|
|
||||||
exports = read_chunk(modules[A]).exports |> Map.new()
|
|
||||||
|
|
||||||
return = fn name, arity ->
|
|
||||||
pair = {name, arity}
|
|
||||||
%{^pair => %{sig: {:infer, nil, [{_, return}]}}} = exports
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
assert return.(:struct_create_with_atom_keys, 1) ==
|
|
||||||
dynamic(
|
|
||||||
tuple([
|
|
||||||
binary(),
|
|
||||||
closed_map(
|
|
||||||
__struct__: atom([A]),
|
|
||||||
x: binary(),
|
|
||||||
y: atom([nil]),
|
|
||||||
z: atom([nil])
|
|
||||||
)
|
|
||||||
])
|
|
||||||
)
|
|
||||||
|
|
||||||
assert return.(:map_create_with_atom_keys, 1) == dynamic(binary())
|
|
||||||
|
|
||||||
assert return.(:map_update_with_atom_keys, 1) ==
|
|
||||||
dynamic(
|
|
||||||
closed_map(
|
|
||||||
__struct__: atom([A]),
|
|
||||||
x: binary(),
|
|
||||||
y: term(),
|
|
||||||
z: term()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
assert return.(:map_update_with_unknown_keys, 2) ==
|
|
||||||
dynamic(open_map())
|
|
||||||
end
|
|
||||||
|
|
||||||
test "writes exports with inferred function types" do
|
test "writes exports with inferred function types" do
|
||||||
files = %{
|
files = %{
|
||||||
"a.ex" => """
|
"a.ex" => """
|
||||||
|
|||||||
@@ -106,16 +106,6 @@ defmodule Module.Types.PatternTest do
|
|||||||
) == uri_type
|
) == uri_type
|
||||||
end
|
end
|
||||||
|
|
||||||
test "refines types" do
|
|
||||||
assert typecheck!(
|
|
||||||
[x, foo = :foo, bar = 123],
|
|
||||||
(
|
|
||||||
{^foo, ^bar} = x
|
|
||||||
x
|
|
||||||
)
|
|
||||||
) == dynamic(tuple([atom([:foo]), integer()]))
|
|
||||||
end
|
|
||||||
|
|
||||||
test "reports incompatible types" do
|
test "reports incompatible types" do
|
||||||
assert typeerror!([x = {:ok, _}], [_ | _] = x) == ~l"""
|
assert typeerror!([x = {:ok, _}], [_ | _] = x) == ~l"""
|
||||||
the following pattern will never match:
|
the following pattern will never match:
|
||||||
|
|||||||
@@ -227,9 +227,9 @@ defmodule Mix.Tasks.Deps.Compile do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_rebar3(%Mix.Dep{opts: opts} = dep, config) do
|
defp do_rebar3(%Mix.Dep{opts: opts, manager: manager} = dep, config) do
|
||||||
if not Mix.Rebar.available?(:rebar3) do
|
if not Mix.Rebar.available?(manager) do
|
||||||
handle_rebar_not_found(dep)
|
Mix.Tasks.Local.Rebar.run(["--force"])
|
||||||
end
|
end
|
||||||
|
|
||||||
dep_path = opts[:dest]
|
dep_path = opts[:dest]
|
||||||
@@ -285,32 +285,6 @@ defmodule Mix.Tasks.Deps.Compile do
|
|||||||
|> Mix.Rebar.serialize_config()
|
|> Mix.Rebar.serialize_config()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_rebar_not_found(%Mix.Dep{app: app, manager: manager}) do
|
|
||||||
shell = Mix.shell()
|
|
||||||
|
|
||||||
shell.info(
|
|
||||||
"Could not find \"#{manager}\", which is needed to build dependency #{inspect(app)}"
|
|
||||||
)
|
|
||||||
|
|
||||||
install_question =
|
|
||||||
"Shall I install #{manager}? (if running non-interactively, " <>
|
|
||||||
"use \"mix local.rebar --force\")"
|
|
||||||
|
|
||||||
if not shell.yes?(install_question) do
|
|
||||||
error_message =
|
|
||||||
"Could not find \"#{manager}\" to compile " <>
|
|
||||||
"dependency #{inspect(app)}, please ensure \"#{manager}\" is available"
|
|
||||||
|
|
||||||
Mix.raise(error_message)
|
|
||||||
end
|
|
||||||
|
|
||||||
Mix.Tasks.Local.Rebar.run(["--force"])
|
|
||||||
|
|
||||||
if not Mix.Rebar.available?(manager) do
|
|
||||||
Mix.raise("\"#{manager}\" installation failed")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp do_make(dep, config) do
|
defp do_make(dep, config) do
|
||||||
command = make_command(dep)
|
command = make_command(dep)
|
||||||
shell_cmd!(dep, config, command, [{"IS_DEP", "1"}])
|
shell_cmd!(dep, config, command, [{"IS_DEP", "1"}])
|
||||||
|
|||||||
Reference in New Issue
Block a user