Compare commits

...
22 Commits
Author SHA1 Message Date
José Valim 59bc1598a2 Fix string versioning 2023-05-30 11:19:39 +02:00
José Valim 46dd1f2a54 Fix mix do syntax 2023-05-30 11:13:37 +02:00
José Valim e884060f03 Fix zips paths 2023-05-30 11:09:07 +02:00
José Valim 80268ceee9 Add branch docs publishing 2023-05-30 11:03:30 +02:00
Bobby fb729784e5 Fix typo in the docs for "with" (#11958) 2022-07-03 11:36:25 +02:00
José Valim 7e4fbe657d Release v1.13.4 2022-04-07 08:08:02 +02:00
Wei Huang d52f533761 Make Registry.send work when value part is present (#11742)
Fix #11740.
2022-04-06 11:01:14 +02:00
José Valim e0c8b8af87 Manual stacktrace munging on eval is no longer necessary on Erlang/OTP 25 2022-03-25 12:02:35 +01:00
José Valim f950226e6c Add OTP-24.3 to CI (#11712) 2022-03-19 09:13:08 +01:00
Michał Łępicki 89e57882e1 Tweak type unification to fix infinite loop with recursive vars (#11664) 2022-02-28 14:47:31 +01:00
José Valim 67cf14c857 Add opening delimiter to sigil metadata/opts 2022-02-17 20:43:21 +01:00
José Valim fe43e23a25 Update local rebar3 2022-02-17 19:59:08 +01:00
Michał Łępicki 65681b83d7 Skip tests using Rebar2 on Erlang/OTP 25+ (#11643)
* Skip tests using Rebar2 on Erlang/OTP 25+

and clean up mix test_helper.exs exclude filters

* Remove unnecessary printing from mix test_helper.exs exclude filters
2022-02-17 18:36:33 +01:00
José Valim 63d682b3d7 Deprecate rebar2 2022-02-17 18:26:40 +01:00
Michał Łępicki c028c2d915 Recreate local.sample task fixture in test_helper.exs (#11640) 2022-02-17 16:44:23 +01:00
José Valim 4b480cb7e0 Allow iodata in sigil formatting functions 2022-02-17 14:57:20 +01:00
Michał Łępicki f809d03370 Update Mix.DepTest for Erlang/OTP 25 (#11636)
:digraph_utils.topsort/1 can return a different (but also valid)
topological ordering

The test failure on Erlang/OTP 25.0-rc1 was:

    1) test deps_paths (Mix.DepTest)
       test/mix/dep_test.exs:491
       Assertion with == failed
       code:  assert Enum.map(Mix.Dep.load_on_environment([]), & &1.app) == [:git_repo, :abc_repo, :deps_repo]
       left:  [:abc_repo, :git_repo, :deps_repo]
       right: [:git_repo, :abc_repo, :deps_repo]
       stacktrace:
         test/mix/dep_test.exs:499: anonymous fn/0 in Mix.DepTest."test deps_paths"/1
         (elixir 1.14.0-dev) lib/file.ex:1555: File.cd!/2
         test/test_helper.exs:127: MixTest.Case.in_fixture/3
         test/mix/dep_test.exs:31: Mix.DepTest.with_deps/2
         test/mix/dep_test.exs:497: (test)
2022-02-17 10:07:22 +01:00
Michał Łępicki ee6ed7558f Update Mix.TasksTestTest for Erlang/OTP 25 (#11637)
The order in which tests get executed can be different
depending on Erlang/OTP version

The test failure was:

    1) test logs and errors umbrella with file path (Mix.Tasks.TestTest)
       test/mix/tasks/test_test.exs:432
       Assertion with =~ failed
       code:  assert mix(["test", "apps/unknown_app/test"]) =~
                "==> bar\nPaths given to \"mix test\" did not match any directory/file: apps/unknown_app/test\n==> foo\nPaths given to \"mix test\" did not match any directory/file: apps/unknown_app/test\n"
       left:  "==> foo\nCompiling 1 file (.ex)\nGenerated foo app\n==> bar\nCompiling 1 file (.ex)\nGenerated bar app\n==> foo\nPaths given to \"mix test\" did not match any directory/file: apps/unknown_app/test\n==> bar\nPaths given to \"mix test\" did not match any directory/file: apps/unknown_app/test\n"
       right: "==> bar\nPaths given to \"mix test\" did not match any directory/file: apps/unknown_app/test\n==> foo\nPaths given to \"mix test\" did not match any directory/file: apps/unknown_app/test\n"
       stacktrace:
         test/mix/tasks/test_test.exs:436: anonymous fn/0 in Mix.Tasks.TestTest."test logs and errors umbrella with file path"/1
         (elixir 1.14.0-dev) lib/file.ex:1555: File.cd!/2
         test/test_helper.exs:127: MixTest.Case.in_fixture/3
         test/mix/tasks/test_test.exs:433: (test)
2022-02-17 10:06:54 +01:00
Kevin bb17204741 Only check for unwanted functions (#11632) 2022-02-17 09:05:59 +01:00
José Valim 1e4ed32eda @doc false URI.Error.message/1 2022-02-14 13:25:14 +01:00
José Valim bac5b1d800 Add compile-time dependencies on require
Projects like Plug use require to establish compile
time dependencies inside a Plug. The fact require
only added a compile-time dependency in v1.13.0 was
therefore a regression, addressed by this commit.
2022-02-14 12:41:00 +01:00
Dwi Prihandi 39d8675ac1 Fix typespec on Base.decode16! (#11526) 2022-02-13 20:27:30 +01:00
40 changed files with 444 additions and 137 deletions
+87
View File
@@ -0,0 +1,87 @@
name: builds.hex.pm
on:
push:
branches:
- main
- v*.*
tags:
- v*
env:
ELIXIR_OPTS: "--warnings-as-errors"
ERLC_OPTS: "warnings_as_errors"
LANG: C.UTF-8
concurrency: builds_txt
jobs:
release_pre_built:
strategy:
fail-fast: true
max-parallel: 1
matrix:
include:
- otp: 23
otp_version: '23.3'
build_docs: build_docs
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 50
- name: Get tags
run: git fetch --tags origin
- uses: ./.github/workflows/release_pre_built
with:
otp_version: ${{ matrix.otp_version }}
otp: ${{ matrix.otp }}
build_docs: ${{ matrix.build_docs }}
- name: Utils.sh
run: |
cat << 'EOF' > utils.sh
function purge_key() {
curl \
-X POST \
-H "Fastly-Key: ${FASTLY_KEY}" \
-H "Accept: application/json" \
-H "Content-Length: 0" \
"https://api.fastly.com/service/$1/purge/$2"
}
function purge() {
purge_key ${FASTLY_REPO_SERVICE_ID} $1
purge_key ${FASTLY_BUILDS_SERVICE_ID} $1
sleep 2
purge_key ${FASTLY_REPO_SERVICE_ID} $1
purge_key ${FASTLY_BUILDS_SERVICE_ID} $1
sleep 2
purge_key ${FASTLY_REPO_SERVICE_ID} $1
purge_key ${FASTLY_BUILDS_SERVICE_ID} $1
}
EOF
chmod +x utils.sh
- name: Upload Docs to S3
if: ${{ matrix.build_docs }}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.HEX_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.HEX_AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.HEX_AWS_REGION }}
AWS_S3_BUCKET: ${{ secrets.HEX_AWS_S3_BUCKET }}
FASTLY_REPO_SERVICE_ID: ${{ secrets.HEX_FASTLY_REPO_SERVICE_ID }}
FASTLY_BUILDS_SERVICE_ID: ${{ secrets.HEX_FASTLY_BUILDS_SERVICE_ID }}
FASTLY_KEY: ${{ secrets.HEX_FASTLY_KEY }}
run: |
source utils.sh
version=$(echo ${{ github.ref_name }} | sed -e 's/^v//g')
for f in doc/*; do
if [ -d "$f" ]; then
app=`echo $f | sed s/"doc\/"//`
tarball="${app}-${version}.tar.gz"
surrogate_key="docs/${app}-${version}"
tar -czf "${tarball}" -C "doc/${app}" .
aws s3 cp "${tarball}" "s3://${{ env.AWS_S3_BUCKET }}/docs/${tarball}" \
--cache-control "public,max-age=3600" \
--metadata "{\"surrogate-key\":\"${surrogate_key}\",\"surrogate-control\":\"public,max-age=604800\"}"
purge "${surrogate_key}"
fi
done
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
strategy:
fail-fast: false
matrix:
otp_release: ['OTP-24.0', 'OTP-23.3', 'OTP-23.0', 'OTP-22.3', 'OTP-22.0']
otp_release: ['OTP-24.3', 'OTP-24.0', 'OTP-23.3', 'OTP-23.0', 'OTP-22.3', 'OTP-22.0']
development: [false]
include:
- otp_release: master
@@ -0,0 +1,51 @@
name: "Release pre built"
description: "Builds elixir release, ExDoc and generates docs"
inputs:
otp:
description: "The major OTP version"
otp_version:
description: "The exact OTP version (major.minor[.patch])"
build_docs:
description: "If docs have to be built or not"
runs:
using: "composite"
steps:
- uses: erlef/setup-beam@v1
with:
otp-version: ${{ inputs.otp_version }}
version-type: strict
- name: Build Elixir Release
shell: bash
run: |
make Precompiled.zip
mv Precompiled.zip elixir-otp-${{ inputs.otp }}.zip
shasum -a 1 elixir-otp-${{ inputs.otp }}.zip > elixir-otp-${{ inputs.otp }}.zip.sha1sum
shasum -a 256 elixir-otp-${{ inputs.otp }}.zip > elixir-otp-${{ inputs.otp }}.zip.sha256sum
echo "$PWD/bin" >> $GITHUB_PATH
- name: Get latest stable ExDoc version
if: ${{ inputs.build_docs }}
shell: bash
run: |
EX_DOC_LATEST_STABLE_VERSION=$(curl -s https://hex.pm/api/packages/ex_doc | jq --raw-output '.latest_stable_version')
echo "EX_DOC_LATEST_STABLE_VERSION=${EX_DOC_LATEST_STABLE_VERSION}" >> $GITHUB_ENV
- uses: actions/checkout@v3
if: ${{ inputs.build_docs }}
with:
repository: elixir-lang/ex_doc
ref: v${{ env.EX_DOC_LATEST_STABLE_VERSION }}
path: ex_doc
- name: Build ex_doc
if: ${{ inputs.build_docs }}
shell: bash
run: |
mv ex_doc ../ex_doc
cd ../ex_doc
../elixir/bin/mix do local.rebar --force, local.hex --force, deps.get, compile
cd ../elixir
- name: Build Docs
if: ${{ inputs.build_docs }}
shell: bash
run: |
make Docs.zip
shasum -a 1 Docs.zip > Docs.zip.sha1sum
shasum -a 256 Docs.zip > Docs.zip.sha256sum
+25
View File
@@ -128,6 +128,31 @@ 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.4 (2022-04-07)
This release has been verified to work with Erlang/OTP 25 RC2.
### 1. Enhancements
#### Elixir
* [Code] Allow iodata to be returned in sigil formatting functions
* [Code] Pass opening delimiter information to sigil formatting functions
### 2. Bug fixes
#### Elixir
* [Kernel] Tweak type unification to fix infinite loop with recursive vars
* [Kernel] Add compile-time dependencies on `require`
* [Registry] Make `Registry` send work with named triplets
### 3. Deprecations
#### Mix
* [mix rebar] Deprecate Rebar 2 as it no longer works on Erlang/OTP 25
## v1.13.3 (2022-02-09)
### 1. Enhancements
+26 -17
View File
@@ -171,6 +171,7 @@ clean_residual_files:
rm -rf lib/mix/test/fixtures/git_rebar/
rm -rf lib/mix/test/fixtures/git_repo/
rm -rf lib/mix/test/fixtures/git_sparse_repo/
rm -rf lib/mix/test/fixtures/archive/ebin/
rm -f erl_crash.dump
$(Q) $(MAKE) clean_man
@@ -178,40 +179,48 @@ clean_residual_files:
LOGO_PATH = $(shell test -f ../docs/logo.png && echo "--logo ../docs/logo.png")
SOURCE_REF = $(shell tag="$(call GIT_TAG)" revision="$(call GIT_REVISION)"; echo "$${tag:-$$revision}")
DOCS_FORMAT = html
COMPILE_DOCS = CANONICAL=$(CANONICAL) bin/elixir ../ex_doc/bin/ex_doc "$(1)" "$(VERSION)" "lib/$(2)/ebin" --main "$(3)" --source-url "https://github.com/elixir-lang/elixir" --source-ref "$(call SOURCE_REF)" $(call LOGO_PATH) --output doc/$(2) --canonical "https://hexdocs.pm/$(2)/$(CANONICAL)" --homepage-url "https://elixir-lang.org/docs.html" --formatter "$(DOCS_FORMAT)" $(4)
DOCS_COMPILE = CANONICAL=$(CANONICAL) bin/elixir ../ex_doc/bin/ex_doc "$(1)" "$(VERSION)" "lib/$(2)/ebin" --main "$(3)" --source-url "https://github.com/elixir-lang/elixir" --source-ref "$(call SOURCE_REF)" $(call LOGO_PATH) --output doc/$(2) --canonical "https://hexdocs.pm/$(2)/$(CANONICAL)" --homepage-url "https://elixir-lang.org/docs.html" --formatter "$(DOCS_FORMAT)" $(4)
DOCS_CONFIG = bin/elixir lib/elixir/scripts/docs_config.exs "$(1)"
docs: compile ../ex_doc/bin/ex_doc docs_elixir docs_eex docs_mix docs_iex docs_ex_unit docs_logger
docs_elixir: compile ../ex_doc/bin/ex_doc
@ echo "==> ex_doc (elixir)"
$(Q) rm -rf doc/elixir
$(call COMPILE_DOCS,Elixir,elixir,Kernel,--config "lib/elixir/docs.exs")
$(call DOCS_COMPILE,Elixir,elixir,Kernel,--config "lib/elixir/scripts/elixir_docs.exs")
$(call DOCS_CONFIG,elixir)
docs_eex: compile ../ex_doc/bin/ex_doc
@ echo "==> ex_doc (eex)"
$(Q) rm -rf doc/eex
$(call COMPILE_DOCS,EEx,eex,EEx,--config "lib/mix/docs.exs")
$(call DOCS_COMPILE,EEx,eex,EEx,--config "lib/elixir/scripts/mix_docs.exs")
$(call DOCS_CONFIG,eex)
docs_mix: compile ../ex_doc/bin/ex_doc
@ echo "==> ex_doc (mix)"
$(Q) rm -rf doc/mix
$(call COMPILE_DOCS,Mix,mix,Mix,--config "lib/mix/docs.exs")
$(call DOCS_COMPILE,Mix,mix,Mix,--config "lib/elixir/scripts/mix_docs.exs")
$(call DOCS_CONFIG,mix)
docs_iex: compile ../ex_doc/bin/ex_doc
@ echo "==> ex_doc (iex)"
$(Q) rm -rf doc/iex
$(call COMPILE_DOCS,IEx,iex,IEx,--config "lib/mix/docs.exs")
$(call DOCS_COMPILE,IEx,iex,IEx,--config "lib/elixir/scripts/mix_docs.exs")
$(call DOCS_CONFIG,iex)
docs_ex_unit: compile ../ex_doc/bin/ex_doc
@ echo "==> ex_doc (ex_unit)"
$(Q) rm -rf doc/ex_unit
$(call COMPILE_DOCS,ExUnit,ex_unit,ExUnit,--config "lib/mix/docs.exs")
$(call DOCS_COMPILE,ExUnit,ex_unit,ExUnit,--config "lib/elixir/scripts/mix_docs.exs")
$(call DOCS_CONFIG,ex_unit)
docs_logger: compile ../ex_doc/bin/ex_doc
@ echo "==> ex_doc (logger)"
$(Q) rm -rf doc/logger
$(call COMPILE_DOCS,Logger,logger,Logger,--config "lib/mix/docs.exs")
$(call DOCS_COMPILE,Logger,logger,Logger,--config "lib/elixir/scripts/mix_docs.exs")
$(call DOCS_CONFIG,logger)
../ex_doc/bin/ex_doc:
@ echo "ex_doc is not found in ../ex_doc as expected. See README for more information."
@@ -220,23 +229,23 @@ docs_logger: compile ../ex_doc/bin/ex_doc
#==> Zip tasks
Docs.zip: docs
rm -f Docs-v$(VERSION).zip
zip -9 -r Docs-v$(VERSION).zip CHANGELOG.md doc NOTICE LICENSE README.md
@ echo "Docs file created $(CURDIR)/Docs-v$(VERSION).zip"
rm -f Docs.zip
zip -9 -r Docs.zip CHANGELOG.md doc NOTICE LICENSE README.md
@ echo "Docs file created $(CURDIR)/Docs.zip"
Precompiled.zip: build_man compile
rm -f Precompiled-v$(VERSION).zip
zip -9 -r Precompiled-v$(VERSION).zip bin CHANGELOG.md lib/*/ebin lib/*/lib LICENSE man NOTICE README.md VERSION
@ echo "Precompiled file created $(CURDIR)/Precompiled-v$(VERSION).zip"
rm -f Precompiled.zip
zip -9 -r Precompiled.zip bin CHANGELOG.md lib/*/ebin lib/*/lib LICENSE man NOTICE README.md VERSION
@ echo "Precompiled file created $(CURDIR)/Precompiled.zip"
zips: Precompiled.zip Docs.zip
@ echo ""
@ echo "### Checksums"
@ echo ""
@ shasum -a 1 < Precompiled-v$(VERSION).zip | sed -e "s/-//" | xargs echo " * Precompiled.zip SHA1:"
@ shasum -a 512 < Precompiled-v$(VERSION).zip | sed -e "s/-//" | xargs echo " * Precompiled.zip SHA512:"
@ shasum -a 1 < Docs-v$(VERSION).zip | sed -e "s/-//" | xargs echo " * Docs.zip SHA1:"
@ shasum -a 512 < Docs-v$(VERSION).zip | sed -e "s/-//" | xargs echo " * Docs.zip SHA512:"
@ shasum -a 1 < Precompiled.zip | sed -e "s/-//" | xargs echo " * Precompiled.zip SHA1:"
@ shasum -a 512 < Precompiled.zip | sed -e "s/-//" | xargs echo " * Precompiled.zip SHA512:"
@ shasum -a 1 < Docs.zip | sed -e "s/-//" | xargs echo " * Docs.zip SHA1:"
@ shasum -a 512 < Docs.zip | sed -e "s/-//" | xargs echo " * Docs.zip SHA512:"
@ echo ""
#==> Test tasks
+1 -1
View File
@@ -1 +1 @@
1.13.3
1.13.4
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/sh
set -e
ELIXIR_VERSION=1.13.3
ELIXIR_VERSION=1.13.4
if [ $# -eq 0 ] || { [ $# -eq 1 ] && { [ "$1" = "--help" ] || [ "$1" = "-h" ]; }; }; then
cat <<USAGE >&2
+1 -1
View File
@@ -1,6 +1,6 @@
@if defined ELIXIR_CLI_ECHO (@echo on) else (@echo off)
set ELIXIR_VERSION=1.13.3
set ELIXIR_VERSION=1.13.4
setlocal enabledelayedexpansion
if ""%1""=="""" if ""%2""=="""" goto documentation
+1 -1
View File
@@ -340,7 +340,7 @@ defmodule Base do
"foobar"
"""
@spec decode16!(binary, case: encode_case) :: binary
@spec decode16!(binary, case: decode_case) :: binary
def decode16!(string, opts \\ [])
def decode16!(string, opts) when is_binary(string) and rem(byte_size(string), 2) == 0 do
+5 -4
View File
@@ -1326,16 +1326,17 @@ defmodule Code.Formatter do
file: state.file,
line: meta[:line],
sigil: List.to_atom([name]),
modifiers: modifiers
modifiers: modifiers,
opening_delimiter: opening_delimiter
]
case callback.(hd(entries), metadata) do
binary when is_binary(binary) ->
[binary]
iodata when is_binary(iodata) or is_list(iodata) ->
[IO.iodata_to_binary(iodata)]
other ->
raise ArgumentError,
"expected sigil callback to return a binary, got: #{inspect(other)}"
"expected sigil callback to return iodata, got: #{inspect(other)}"
end
%{} ->
+10 -9
View File
@@ -30,8 +30,8 @@ defmodule Kernel.LexicalTracker do
end
@doc false
def add_require(pid, module) when is_atom(module) do
:gen_server.cast(pid, {:add_require, module})
def add_export(pid, module) when is_atom(module) do
:gen_server.cast(pid, {:add_export, module})
end
@doc false
@@ -168,15 +168,16 @@ defmodule Kernel.LexicalTracker do
{:noreply, update_in(state.compile_env, &:ordsets.add_element({app, path, return}, &1))}
end
def handle_cast({:add_require, module}, state) do
def handle_cast({:add_export, module}, state) do
{:noreply, put_in(state.exports[module], true)}
end
def handle_cast({:add_import, module, fas, line, warn}, state) do
to_remove = for {{:import, {^module, _, _}} = key, _} <- state.directives, do: key
%{directives: directives, exports: exports} = state
to_remove = for {{:import, {^module, _, _}} = key, _} <- directives, do: key
directives =
state.directives
directives
|> Map.drop(to_remove)
|> add_directive(module, line, warn, :import)
@@ -185,7 +186,7 @@ defmodule Kernel.LexicalTracker do
add_directive(directives, {module, function, arity}, line, warn, :import)
end)
{:noreply, %{state | directives: directives}}
{:noreply, %{state | directives: directives, exports: Map.put(exports, module, true)}}
end
def handle_cast({:add_alias, module, line, warn}, state) do
@@ -221,9 +222,9 @@ defmodule Kernel.LexicalTracker do
do: Map.put(references, module, :compile)
defp add_reference(references, module, :runtime) when is_atom(module) do
case Map.fetch(references, module) do
{:ok, _} -> references
:error -> Map.put(references, module, :runtime)
case references do
%{^module => _} -> references
_ -> Map.put(references, module, :runtime)
end
end
+4 -3
View File
@@ -554,7 +554,9 @@ defmodule Kernel.SpecialForms do
defmacro alias(module, opts), do: error!([module, opts])
@doc """
Requires a module in order to use its macros.
Requires a module as a compile-time dependency.
Requiring a module is necessary in order to use its macros.
## Examples
@@ -576,7 +578,6 @@ defmodule Kernel.SpecialForms do
`require/2` also accepts `:as` as an option so it automatically sets
up an alias. Please check `alias/2` for more information.
"""
defmacro require(module, opts), do: error!([module, opts])
@@ -1611,7 +1612,7 @@ defmodule Kernel.SpecialForms do
end
Note how the code above is better organized and clearer once we
make sure each clause in `with` returns a normalize format.
make sure each clause in `with` returns a normalized format.
"""
defmacro with(args), do: error!([args])
+2 -1
View File
@@ -1811,7 +1811,8 @@ defmodule Module do
acc
true ->
:elixir_env.trace({:require, [], behaviour, []}, env)
event = {:remote_function, [], behaviour, :behaviour_info, 1}
:elixir_env.trace(event, %{env | function: {:__info__, 1}})
optional_callbacks = behaviour_info(behaviour, :optional_callbacks)
callbacks = behaviour_info(behaviour, :callbacks)
Enum.reduce(callbacks, acc, &add_callback(&1, behaviour, env, optional_callbacks, &2))
+4 -4
View File
@@ -43,14 +43,14 @@ defmodule Module.Types.Unify do
{:ok, same, context}
end
def unify(type, {:var, var}, stack, context) do
unify_var(var, type, stack, context, _var_source = false)
end
def unify({:var, var}, type, stack, context) do
unify_var(var, type, stack, context, _var_source = true)
end
def unify(type, {:var, var}, stack, context) do
unify_var(var, type, stack, context, _var_source = false)
end
def unify({:tuple, n, sources}, {:tuple, n, targets}, stack, context) do
result =
map_reduce_ok(Enum.zip(sources, targets), context, fn {source, target}, context ->
+4
View File
@@ -260,6 +260,10 @@ defmodule Registry do
end
end
def send({registry, key, _value}, msg) do
Registry.send({registry, key}, msg)
end
@doc false
def unregister_name({registry, key}), do: unregister(registry, key)
def unregister_name({registry, key, _value}), do: unregister(registry, key)
+1
View File
@@ -37,6 +37,7 @@ defmodule URI do
defmodule Error do
defexception [:action, :reason, :part]
@doc false
def message(%Error{action: action, reason: reason, part: part}) do
"cannot #{action} due to reason #{reason}: #{inspect(part)}"
end
+19
View File
@@ -0,0 +1,19 @@
# Generate docs_config.js for version chooser in ExDoc
[app] = System.argv()
{text_tags, 0} = System.cmd("git", ["tag"])
skipped = Version.parse!("1.0.3")
list_contents =
text_tags
|> String.split()
|> Enum.map(fn "v" <> rest -> Version.parse!(rest) end)
|> Enum.filter(&(Version.compare(&1, skipped) == :gt))
|> Enum.sort({:desc, Version})
|> Enum.map_intersperse(", ", fn version ->
version_string = to_string(version)
~s[{"version":"v#{version_string}", "url":"https://hexdocs.pm/#{app}/#{version_string}"}]
end)
File.mkdir_p!("doc/#{app}")
File.write!("doc/#{app}/docs_config.js", ["var versionNodes = [", list_contents, "];\n"])
+1 -1
View File
@@ -85,6 +85,7 @@ expand({require, Meta, [Ref, Opts]}, S, E) ->
if
is_atom(ERef) ->
elixir_aliases:ensure_loaded(Meta, ERef, ET),
elixir_env:trace({require, Meta, ERef, EOpts}, ET),
{ERef, ST, expand_require(Meta, ERef, EOpts, ET)};
true ->
form_error(Meta, E, ?MODULE, {expected_compile_time_module, require, Ref})
@@ -931,7 +932,6 @@ no_alias_expansion(Other) ->
Other.
expand_require(Meta, Ref, Opts, E) ->
elixir_env:trace({require, Meta, Ref, Opts}, E),
RE = E#{requires := ordsets:add_element(Ref, ?key(E, requires))},
expand_alias(Meta, false, Ref, Opts, RE).
+2 -2
View File
@@ -47,10 +47,10 @@ trace({alias_expansion, _Meta, Lookup, _Result}, #{lexical_tracker := Pid}) ->
?tracker:alias_dispatch(Pid, Lookup),
ok;
trace({require, _Meta, Module, _Opts}, #{lexical_tracker := Pid}) ->
?tracker:add_require(Pid, Module),
?tracker:remote_dispatch(Pid, Module, compile),
ok;
trace({struct_expansion, _Meta, Module, _Keys}, #{lexical_tracker := Pid}) ->
?tracker:add_require(Pid, Module),
?tracker:add_export(Pid, Module),
ok;
trace({alias_reference, _Meta, Module}, #{lexical_tracker := Pid} = E) ->
?tracker:remote_dispatch(Pid, Module, mode(E)),
@@ -126,23 +126,23 @@ defmodule Code.Formatter.GeneralTest do
"""
formatter = fn content, opts ->
assert opts == [file: nil, line: 1, sigil: :W, modifiers: []]
assert opts == [file: nil, line: 1, sigil: :W, modifiers: [], opening_delimiter: "/"]
content |> String.split(~r/ +/) |> Enum.join(" ")
end
assert_format bad, good, sigils: [W: formatter]
bad = """
var = ~W/foo bar baz/abc
var = ~W<foo bar baz>abc
"""
good = """
var = ~W/foo bar baz/abc
var = ~W<foo bar baz>abc
"""
formatter = fn content, opts ->
assert opts == [file: nil, line: 1, sigil: :W, modifiers: 'abc']
content |> String.split(~r/ +/) |> Enum.join(" ")
assert opts == [file: nil, line: 1, sigil: :W, modifiers: 'abc', opening_delimiter: "<"]
content |> String.split(~r/ +/) |> Enum.intersperse(" ")
end
assert_format bad, good, sigils: [W: formatter]
@@ -162,34 +162,41 @@ defmodule Code.Formatter.GeneralTest do
"""
formatter = fn content, opts ->
assert opts == [file: nil, line: 1, sigil: :W, modifiers: []]
assert opts == [file: nil, line: 1, sigil: :W, modifiers: [], opening_delimiter: "'''"]
content |> String.split(~r/ +/) |> Enum.join(" ")
end
assert_format bad, good, sigils: [W: formatter]
bad = """
bad = ~S'''
if true do
~W'''
~W"""
foo
bar
baz
'''abc
"""abc
end
"""
'''
good = """
good = ~S'''
if true do
~W'''
~W"""
foo
bar
baz
'''abc
"""abc
end
"""
'''
formatter = fn content, opts ->
assert opts == [file: nil, line: 2, sigil: :W, modifiers: 'abc']
assert opts == [
file: nil,
line: 2,
sigil: :W,
modifiers: 'abc',
opening_delimiter: ~S/"""/
]
content |> String.split(~r/ +/) |> Enum.join("\n")
end
+12 -10
View File
@@ -78,18 +78,20 @@ defmodule CodeTest do
end
end
test "raises streamlined argument errors" do
assert_raise ArgumentError,
~r"argument error while evaluating at line 1",
fn -> Code.eval_string("a <> b", a: :a, b: :b) end
if System.otp_release() < "25" do
test "raises streamlined argument errors" do
assert_raise ArgumentError,
~r"argument error while evaluating at line 1",
fn -> Code.eval_string("a <> b", a: :a, b: :b) end
assert_raise ArgumentError,
~r"argument error while evaluating example.ex at line 1",
fn -> Code.eval_string("a <> b", [a: :a, b: :b], file: "example.ex") end
assert_raise ArgumentError,
~r"argument error while evaluating example.ex at line 1",
fn -> Code.eval_string("a <> b", [a: :a, b: :b], file: "example.ex") end
assert_raise ArgumentError,
~r"argument error while evaluating example.ex between lines 1 and 2",
fn -> Code.eval_string("a <>\nb", [a: :a, b: :b], file: "example.ex") end
assert_raise ArgumentError,
~r"argument error while evaluating example.ex between lines 1 and 2",
fn -> Code.eval_string("a <>\nb", [a: :a, b: :b], file: "example.ex") end
end
end
end
@@ -21,8 +21,8 @@ defmodule Kernel.LexicalTrackerTest do
assert D.references(config[:pid]) == {[String], [], [], []}
end
test "can add requires", config do
D.add_require(config[:pid], URI)
test "can add module requires and exports", config do
D.add_export(config[:pid], URI)
assert D.references(config[:pid]) == {[], [URI], [], []}
D.remote_dispatch(config[:pid], URI, :runtime)
@@ -33,7 +33,6 @@ defmodule Kernel.LexicalTrackerTest do
end
test "can add module imports", config do
D.add_require(config[:pid], String)
D.add_import(config[:pid], String, [], 1, true)
D.import_dispatch(config[:pid], String, {:upcase, 1}, :runtime)
@@ -44,7 +43,7 @@ defmodule Kernel.LexicalTrackerTest do
end
test "can add module with {function, arity} imports", config do
D.add_require(config[:pid], String)
D.add_export(config[:pid], String)
D.add_import(config[:pid], String, [upcase: 1], 1, true)
D.import_dispatch(config[:pid], String, {:upcase, 1}, :compile)
@@ -181,18 +180,18 @@ defmodule Kernel.LexicalTrackerTest do
{{compile, _exports, runtime, _}, _binding} =
Code.eval_string("""
defmodule Kernel.LexicalTrackerTest.Defdelegate do
defdelegate a, to: A
defdelegate parse(arg), to: URI
opts = [to: B]
defdelegate b, opts
opts = [to: Range]
defdelegate disjoint?(left, right), opts
Kernel.LexicalTracker.references(__ENV__.lexical_tracker)
end |> elem(3)
""")
refute A in compile
assert B in compile
assert A in runtime
refute URI in compile
assert Range in compile
assert URI in runtime
end
test "imports adds an export dependency" do
@@ -209,6 +208,20 @@ defmodule Kernel.LexicalTrackerTest do
refute String in runtime
end
test "requires adds a compile dependency" do
{{compile, exports, runtime, _}, _binding} =
Code.eval_string("""
defmodule Kernel.LexicalTrackerTest.Requires do
require String
Kernel.LexicalTracker.references(__ENV__.lexical_tracker)
end |> elem(3)
""")
assert String in compile
refute String in exports
refute String in runtime
end
test "structs are exports or compile time" do
{{compile, exports, runtime, _}, _binding} =
Code.eval_string("""
@@ -119,7 +119,6 @@ defmodule Module.LocalsTrackerTest do
end
test "does not include unreachable locals" do
assert NoPrivate.module_info(:functions) ==
[__info__: 1, baz: 0, module_info: 0, module_info: 1]
assert NoPrivate.module_info(:functions) |> Keyword.take([:foo, :bar, :"MACRO-foo"]) == []
end
end
@@ -200,20 +200,20 @@ defmodule Module.Types.TypesTest do
# types_test.ex:1
is_binary(y)
where "y" was given the same type as "x" in:
where "x" was given the same type as "y" in:
# types_test.ex:1
x = y
where "y" was given the type integer() in:
# types_test.ex:1
is_integer(x)
where "y" was given the type binary() in:
# types_test.ex:1
is_binary(y)
where "x" was given the type integer() in:
# types_test.ex:1
is_integer(x)
"""
end
@@ -256,20 +256,20 @@ defmodule Module.Types.TypesTest do
# types_test.ex:1
is_binary(y)
where "y" was given the same type as "x" in:
where "x" was given the same type as "y" in:
# types_test.ex:1
x = y
where "y" was given the type integer() in:
# types_test.ex:1
is_integer(x)
where "y" was given the type binary() in:
# types_test.ex:1
is_binary(y)
where "x" was given the type integer() in:
# types_test.ex:1
is_integer(x)
"""
end
@@ -443,7 +443,17 @@ defmodule Module.Types.TypesTest do
# types_test.ex:5
%{"id" => user_id} = user
where "user" was given the same type as "amount" in:
where "amount" was given the type binary() in:
# types_test.ex:3
%{"amount" => amount} = event
where "amount" was given the same type as "user" in:
# types_test.ex:4
%{"user" => user} = event
where "user" was given the type binary() in:
# types_test.ex:4
%{"user" => user} = event
@@ -452,11 +462,6 @@ defmodule Module.Types.TypesTest do
# types_test.ex:5
%{"id" => user_id} = user
where "amount" was given the type binary() in:
# types_test.ex:3
%{"amount" => amount} = event
"""
end
@@ -700,5 +705,32 @@ defmodule Module.Types.TypesTest do
)
) == :none
end
test "other recursive" do
assert warning(
[x, y],
(
key_var = y
%{^key_var => _value} = x
key_var2 = y
%{^key_var2 => _value2} = x
y.z
)
) == :none
end
test "other recursive2" do
assert warning(
[x, y],
(
key_var = y
%{^key_var => _value} = x
key_var2 = y
%{^key_var2 => _value2} = x
key_var3 = y
%{^key_var3 => _value3} = x
)
) == :none
end
end
end
@@ -502,25 +502,25 @@ defmodule Module.Types.UnifyTest do
assert {:ok, {:var, _}, context} = unify({:var, 0}, :tuple, var_context)
assert {:ok, {:var, _}, context} = unify({:var, 1}, {:var, 0}, context)
assert {:ok, {:var, _}, context} = unify({:var, 0}, {:var, 1}, context)
assert context.types[0] == {:var, 1}
assert context.types[1] == :tuple
assert context.types[0] == :tuple
assert context.types[1] == {:var, 0}
assert {:ok, {:var, _}, context} = unify({:var, 0}, {:var, 1}, var_context)
assert {:ok, {:var, _}, context} = unify({:var, 1}, {:var, 2}, context)
assert {:ok, {:var, _}, _context} = unify({:var, 2}, {:var, 0}, context)
assert context.types[0] == :unbound
assert context.types[1] == {:var, 0}
assert context.types[2] == {:var, 1}
assert context.types[0] == {:var, 1}
assert context.types[1] == {:var, 2}
assert context.types[2] == :unbound
assert {:ok, {:var, _}, context} = unify({:var, 0}, {:var, 1}, var_context)
assert {:error, {:unable_unify, {{:var, 0}, {:tuple, 1, [{:var, 0}]}, _}}} =
assert {:error, {:unable_unify, {{:var, 1}, {:tuple, 1, [{:var, 0}]}, _}}} =
unify_lift({:var, 1}, {:tuple, 1, [{:var, 0}]}, context)
assert {:ok, {:var, _}, context} = unify({:var, 0}, {:var, 1}, var_context)
assert {:ok, {:var, _}, context} = unify({:var, 1}, {:var, 2}, context)
assert {:error, {:unable_unify, {{:var, 0}, {:tuple, 1, [{:var, 0}]}, _}}} =
assert {:error, {:unable_unify, {{:var, 2}, {:tuple, 1, [{:var, 0}]}, _}}} =
unify_lift({:var, 2}, {:tuple, 1, [{:var, 0}]}, context)
end
@@ -707,9 +707,9 @@ defmodule Module.Types.UnifyTest do
{{:var, 0}, var_context} = new_var({:foo, [version: 0], nil}, new_context())
{{:var, 1}, var_context} = new_var({:bar, [version: 1], nil}, var_context)
{:ok, {:var, 1}, var_context} = unify({:var, 0}, {:var, 1}, var_context)
assert flatten_union({:var, 0}, var_context) == [{:var, 0}]
assert flatten_union({:var, 1}, var_context) == [{:var, 0}]
{:ok, {:var, 0}, var_context} = unify({:var, 0}, {:var, 1}, var_context)
assert flatten_union({:var, 0}, var_context) == [{:var, 1}]
assert flatten_union({:var, 1}, var_context) == [{:var, 1}]
end
test "format_type/1" do
+14
View File
@@ -896,6 +896,20 @@ defmodule RegistryTest do
assert {%RuntimeError{message: "some error"}, _stacktrace} = error
end
test "send works", %{registry: registry} do
name = {registry, "self"}
Registry.register_name(name, self())
GenServer.cast({:via, Registry, name}, :message)
assert_received {:"$gen_cast", :message}
end
test "send works with value", %{registry: registry} do
name = {registry, "self", "value"}
Registry.register_name(name, self())
GenServer.cast({:via, Registry, name}, :message)
assert_received {:"$gen_cast", :message}
end
defp register_task(registry, key, value) do
parent = self()
+9
View File
@@ -90,6 +90,15 @@ defmodule Mix.Dep.Loader do
# 4. Inferred from files in dependency (mix.exs, rebar.config, Makefile)
manager = opts[:manager] || scm_manager(scm, opts) || manager || infer_manager(opts[:dest])
# TODO: Remove rebar2 support on Elixir v1.15+.
if manager == :rebar do
Mix.shell().error(
"dependency #{inspect(dep.app)} is using Rebar 2, " <>
"which is no longer maintained and no longer works in recent Erlang/OTP versions. " <>
"Remove the :manager option or set it to :rebar3 instead"
)
end
dep = %{dep | manager: manager, status: scm_status(scm, opts)}
{dep, children} =
+7 -4
View File
@@ -510,13 +510,16 @@ defmodule Mix.Tasks.Xref do
@doc false
def trace({:require, meta, module, _opts}, env),
do: add_trace(:export, :require, module, module, meta, env)
do: add_trace(:compile, :require, module, module, meta, env)
def trace({:import, meta, module, _opts}, env),
do: add_trace(:export, :import, module, module, meta, env)
def trace({:struct_expansion, meta, module, _keys}, env),
do: add_trace(:export, :struct, module, module, meta, env)
def trace({:alias_reference, meta, module}, env) when env.module != module,
do: add_trace(mode(env), :alias, module, module, meta, env)
do: add_trace(mode(env), :module, module, module, meta, env)
def trace({:remote_function, meta, module, function, arity}, env),
do: add_trace(mode(env), :call, module, {module, function, arity}, meta, env)
@@ -551,14 +554,14 @@ defmodule Mix.Tasks.Xref do
# We don't want to show aliases if there is an entry of the same type
non_aliases =
for {_file, _line, module_or_mfa, mode, type} <- entries,
type != :alias,
type != :module,
into: %{},
do: {{trace_module(module_or_mfa), mode}, []}
shell = Mix.shell()
for {file, line, module_or_mfa, mode, type} <- entries,
type != :alias or not Map.has_key?(non_aliases, {module_or_mfa, mode}) do
type != :module or not Map.has_key?(non_aliases, {module_or_mfa, mode}) do
shell.info([
Exception.format_file_line(Path.relative_to_cwd(file), line),
?\s,
+1
View File
@@ -4,3 +4,4 @@
/git_rebar/
/git_repo/
/git_sparse_repo/
/archive/ebin/
-3
View File
@@ -1,3 +0,0 @@
{application,local_sample,
[{modules,['Elixir.Mix.Tasks.Local.Sample']},
{applications,[kernel,stdlib,elixir]}]}.
BIN
View File
Binary file not shown.
+5 -2
View File
@@ -496,8 +496,11 @@ defmodule Mix.DepTest do
with_deps(deps, fn ->
in_fixture("deps_status", fn ->
assert Enum.map(Mix.Dep.load_on_environment([]), & &1.app) ==
[:git_repo, :abc_repo, :deps_repo]
# Both orders below are valid after topological sort
assert Enum.map(Mix.Dep.load_on_environment([]), & &1.app) in [
[:git_repo, :abc_repo, :deps_repo],
[:abc_repo, :git_repo, :deps_repo]
]
assert Map.keys(Mix.Project.deps_paths()) == [:abc_repo, :deps_repo, :git_repo]
+5 -3
View File
@@ -202,7 +202,7 @@ defmodule Mix.RebarTest do
# We run only on Unix because Windows has a hard time
# removing the Rebar executable after executed.
@tag [unix: true]
@tag :unix
test "gets and compiles dependencies" do
in_tmp("get and compile dependencies", fn ->
Mix.Project.push(RebarAsDep)
@@ -256,7 +256,7 @@ defmodule Mix.RebarTest do
# We run only on Unix because Windows has a hard time
# removing the Rebar executable after executed.
@tag [unix: true]
@tag :unix
test "applies variables from :system_env option when compiling dependencies" do
in_tmp("applies variables from system_env", fn ->
Mix.Project.push(RebarAsDepWithEnv)
@@ -295,6 +295,7 @@ defmodule Mix.RebarTest do
File.rm(MixTest.Case.tmp_path("rebar_dep/mix.exs"))
end
@tag :rebar
test "gets and compiles dependencies with Rebar2" do
in_tmp("get and compile dependencies for Rebar2", fn ->
Mix.Project.push(Rebar2AsDep)
@@ -328,7 +329,8 @@ defmodule Mix.RebarTest do
# We run only on Unix because Windows has a hard time
# removing the Rebar executable after executed.
@tag [unix: true]
@tag :unix
@tag :rebar
test "applies variables from :system_env option when compiling dependencies for Rebar2" do
in_tmp("applies variables from system_env for Rebar2", fn ->
Mix.Project.push(Rebar2AsDep)
+6 -1
View File
@@ -433,9 +433,14 @@ defmodule Mix.Tasks.TestTest do
in_fixture("umbrella_test", fn ->
# Run false positive test first so at least the code is compiled
# and we can perform more aggressive assertions later
assert mix(["test", "apps/unknown_app/test"]) =~ """
output = mix(["test", "apps/unknown_app/test"])
assert output =~ """
==> bar
Paths given to "mix test" did not match any directory/file: apps/unknown_app/test
"""
assert output =~ """
==> foo
Paths given to "mix test" did not match any directory/file: apps/unknown_app/test
"""
+6 -7
View File
@@ -253,7 +253,7 @@ defmodule Mix.Tasks.XrefTest do
output = """
Compiling 2 files (.ex)
Generated sample app
lib/b.ex:2: require A (export)
lib/b.ex:2: import A (export)
lib/b.ex:3: call A.macro/0 (compile)
lib/b.ex:4: import A.macro/0 (compile)
lib/b.ex:5: call A.fun/0 (compile)
@@ -287,6 +287,7 @@ defmodule Mix.Tasks.XrefTest do
output = """
Compiling 2 files (.ex)
Generated sample app
lib/b.ex:2: require A (compile)
lib/b.ex:3: call A.macro/0 (compile)
"""
@@ -313,10 +314,11 @@ defmodule Mix.Tasks.XrefTest do
output = """
Compiling 2 files (.ex)
Generated sample app
lib/b.ex:3: require A (compile)
lib/b.ex:3: call A.macro/0 (compile)
"""
message = "Too many traces (found: 1, permitted: 0)"
message = "Too many traces (found: 2, permitted: 0)"
assert_raise Mix.Error, message, fn ->
assert_trace(~w[--label compile --fail-above 0], "lib/b.ex", files, output)
@@ -759,10 +761,7 @@ defmodule Mix.Tasks.XrefTest do
defmodule B do
# Let's also test that we track literal atom behaviours
@behaviour :"Elixir.A.Behaviour"
def foo do
A.foo()
end
def foo, do: :foo
end
""")
@@ -772,7 +771,7 @@ defmodule Mix.Tasks.XrefTest do
digraph "xref graph" {
"lib/a.ex"
"lib/a.ex" -> "lib/b.ex" [label="(compile)"]
"lib/b.ex" -> "lib/a.ex" [label="(export)"]
"lib/b.ex" -> "lib/a.ex"
"lib/b.ex"
}
"""
+1 -1
View File
@@ -391,7 +391,7 @@ defmodule Mix.UmbrellaTest do
assert_received {:mix_shell, :info, ["Compiled lib/bar.ex"]}
# But exports dependencies are not recompiled
File.write!("lib/bar.ex", "defmodule Bar, do: (require Foo)")
File.write!("lib/bar.ex", "defmodule Bar, do: (import Foo, warn: false)")
assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:ok, []}
assert_received {:mix_shell, :info, ["Compiled lib/bar.ex"]}
+26 -5
View File
@@ -7,12 +7,13 @@ Application.put_env(:logger, :backends, [])
os_exclude = if match?({:win32, _}, :os.type()), do: [unix: true], else: [windows: true]
epmd_exclude = if match?({:win32, _}, :os.type()), do: [epmd: true], else: []
ExUnit.start(trace: "--trace" in System.argv(), exclude: epmd_exclude ++ os_exclude)
git_exclude = if Mix.SCM.Git.git_version() <= {1, 7, 4}, do: [git_sparse: true], else: []
rebar_exclude = if System.otp_release() >= "25", do: [rebar: true], else: []
unless {1, 7, 4} <= Mix.SCM.Git.git_version() do
IO.puts(:stderr, "Skipping tests with git sparse checkouts...")
ExUnit.configure(exclude: :git_sparse)
end
ExUnit.start(
trace: "--trace" in System.argv(),
exclude: epmd_exclude ++ os_exclude ++ git_exclude ++ rebar_exclude
)
# Clear environment variables that may affect tests
System.delete_env("http_proxy")
@@ -412,6 +413,26 @@ Enum.each([:invalidapp, :invalidvsn, :noappfile, :nosemver, :ok], fn dep ->
File.mkdir_p!(Path.expand("fixtures/deps_status/deps/#{dep}/.git", __DIR__))
end)
# Archive ebin
target = Path.expand("fixtures/archive", __DIR__)
unless File.dir?(Path.join(target, "ebin")) do
File.mkdir_p!(Path.join(target, "ebin"))
File.write!(Path.join([target, "ebin", "local_sample.app"]), """
{application,local_sample,
[
{modules,['Elixir.Mix.Tasks.Local.Sample']},
{applications,[kernel,stdlib,elixir]}
]
}.
""")
[{name, bin}] = Code.compile_file("lib/local.sample.ex", target)
File.write!(Path.join([target, "ebin", Atom.to_string(name) <> ".beam"]), bin)
end
## Generate helper modules
path = MixTest.Case.tmp_path("beams")