Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
524528b973 | ||
|
|
a64d42f5d3 | ||
|
|
7636046bd9 | ||
|
|
552b5abed8 | ||
|
|
74bfab8ee2 | ||
|
|
e00cbe16cd | ||
|
|
9df7a548ed | ||
|
|
9137fd1cb2 | ||
|
|
5189b292ac | ||
|
|
74f9a299ea | ||
|
|
2ce9d25552 | ||
|
|
b558142819 | ||
|
|
d7f96d8332 | ||
|
|
010efd8ec6 | ||
|
|
c0174044bc | ||
|
|
616e0d8ccc | ||
|
|
13ba96be3c | ||
|
|
edb5e7e052 | ||
|
|
58ca6524f8 | ||
|
|
afcefb3e21 | ||
|
|
fb5aabf05a | ||
|
|
d9a81101dd | ||
|
|
8fca53ad31 | ||
|
|
76dfedb2e3 | ||
|
|
8bec593f5d | ||
|
|
03859fb92e | ||
|
|
53e49f33f1 | ||
|
|
c0bdd6101f | ||
|
|
e7cb210712 | ||
|
|
4a4657e413 | ||
|
|
fcada17819 | ||
|
|
6be8bddc02 | ||
|
|
9feda914dc | ||
|
|
62e1d71377 | ||
|
|
d6ed6d18ef | ||
|
|
e024a12c50 | ||
|
|
42807d288e | ||
|
|
3b8d7fa15d | ||
|
|
d1d5e04319 | ||
|
|
ba1fd1abb8 | ||
|
|
8fd6c8f845 | ||
|
|
95dcbe9fe0 | ||
|
|
98a79dc839 | ||
|
|
29444793b7 | ||
|
|
ff3674ddef | ||
|
|
0460017c5c | ||
|
|
f3b870857a | ||
|
|
faa2f35d8e | ||
|
|
3339168330 | ||
|
|
1c3ee4ed72 | ||
|
|
926455ca24 |
@@ -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
|
||||
@@ -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
|
||||
+68
-1
@@ -90,7 +90,65 @@ As of Elixir v1.12, implicitly decreasing ranges are soft-deprecated and warning
|
||||
|
||||
Elixir v1.12 has the additional of many functions across the standard library. The `Enum` module received additions such as `Enum.count_until/2`, `Enum.product/1`, `Enum.zip_with/2`, and more. The `Integer` module now includes `Integer.pow/2` and `Integer.extended_gcd/2`. Finally, the `Kernel` module got two new functions, `Kernel.then/2` and `Kernel.tap/2`, which are specially useful in `|>` pipelines.
|
||||
|
||||
## v1.12.0
|
||||
## v1.12.3 (2021-09-05)
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Code] Make sure that bindings in the default context returned by `Code.eval_*` functions are not returned as tagged tuples
|
||||
* [Kernel] Do not crash when handling ambiguity errors
|
||||
* [Range] Still match on old range patterns throughout the stdlib
|
||||
|
||||
#### IEx
|
||||
|
||||
* [IEx.Autocomplete] Do not error autocompletion with module attribute
|
||||
|
||||
#### Mix
|
||||
|
||||
* [Mix] Rename inconsistent `:exit_code` option to `:exit_status` on `Mix.raise/2`
|
||||
|
||||
## v1.12.2 (2021-07-01)
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Kernel] Ensure deprecated macros emit warnings
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix deps] Ensure unconstrained rebar deps generate valid mix specifications
|
||||
|
||||
### 2. Enhancements
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [elixirc] Change the output of `--profile time` to make it easier to detect outliers
|
||||
* [Application] Do not add compile time deps on args to `Application.compile_env/2` and `Application.compile_env!/2`
|
||||
* [Enum] Optimize `Enum.into/3` and `Map.new/2`
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix compile] Compile most recently changed files first
|
||||
* [mix compile, mix run, mix test] Speed up the time taken to load dependencies. This should make the usage of Mix inside projects quite more responsive
|
||||
|
||||
## v1.12.1 (2021-05-28)
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Code] Make sure `Code.format_string!/2` formats multiline expression inside interpolation on the first run
|
||||
* [Macro] Revert keeping of underscores between digits in camelize
|
||||
|
||||
#### Mix
|
||||
|
||||
* [Mix] Make sure `Mix.install/2` expand paths for deps
|
||||
* [mix deps.get] Silence false positives on `httpc` warnings
|
||||
* [mix test] Do not run the whole suite when there are no --failed tests as it won't behave as expected inside umbrellas
|
||||
|
||||
## v1.12.0 (2021-05-19)
|
||||
|
||||
### 1. Enhancements
|
||||
|
||||
@@ -169,6 +227,7 @@ Elixir v1.12 has the additional of many functions across the standard library. T
|
||||
* [mix release] Add `RELEASE_PROG` to releases with the name of the executable starting the release
|
||||
* [mix release] Support `remote.vm.args` to customize how the connecting VM boots
|
||||
* [mix test] Run all available tests if there are no pending `--failed` tests. This provides a better workflow as you no longer need to toggle the `--failed` flag between runs
|
||||
* [mix test] Support `mix test --warnings-as-errors`, treating warnings as errors and returning a non-zero exit status (only applies to test files, not compilation)
|
||||
|
||||
### 2. Bug fixes
|
||||
|
||||
@@ -237,6 +296,14 @@ Elixir v1.12 has the additional of many functions across the standard library. T
|
||||
|
||||
* [mix compile] The `:xref` compiler is deprecated and it has no effect. Please remove it from your mix.exs file.
|
||||
|
||||
### 5. Backwards incompatible changes
|
||||
|
||||
A backward incompatible change was found on Elixir v1.12.0 after the release. This was not planned and it is documented below:
|
||||
|
||||
#### ExUnit
|
||||
|
||||
* [ExUnit.Formatter] `{:suite_finished, load_time, run_time}` message has been updated to `{:suite_finished, %{load: load_time, run: run_time, async: async_time}}`
|
||||
|
||||
## v1.11
|
||||
|
||||
The CHANGELOG for v1.11 releases can be found [in the v1.11 branch](https://github.com/elixir-lang/elixir/blob/v1.11/CHANGELOG.md).
|
||||
|
||||
@@ -179,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."
|
||||
@@ -221,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
@@ -14,7 +14,7 @@ Usage: $(basename "$0") [elixir switches] [compiler switches] [.ex files]
|
||||
--no-docs Does not attach documentation to compiled modules
|
||||
--profile time Profile the time to compile modules
|
||||
--verbose Prints compilation status
|
||||
--warnings-as-errors Treats warnings as errors and return non-zero exit code
|
||||
--warnings-as-errors Treats warnings as errors and return non-zero exit status
|
||||
|
||||
Options given after -- are passed down to the executed code.
|
||||
Options can be passed to the Erlang runtime using \$ELIXIR_ERL_OPTIONS.
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ echo --no-debug-info Does not attach debug info to compiled modules
|
||||
echo --no-docs Does not attach documentation to compiled modules
|
||||
echo --profile time Profile the time to compile modules
|
||||
echo --verbose Prints compilation status
|
||||
echo --warnings-as-errors Treats warnings as errors and returns non-zero exit code
|
||||
echo --warnings-as-errors Treats warnings as errors and returns non-zero exit status
|
||||
echo.
|
||||
echo ** Options given after -- are passed down to the executed code
|
||||
echo ** Options can be passed to the Erlang runtime using ELIXIR_ERL_OPTIONS
|
||||
|
||||
@@ -507,11 +507,22 @@ defmodule Application do
|
||||
raise "Application.compile_env/3 cannot be called inside functions, only in the module body"
|
||||
end
|
||||
|
||||
key_or_path = expand_key_or_path(key_or_path, __CALLER__)
|
||||
|
||||
quote do
|
||||
Application.__compile_env__(unquote(app), unquote(key_or_path), unquote(default), __ENV__)
|
||||
end
|
||||
end
|
||||
|
||||
defp expand_key_or_path({:__aliases__, _, _} = alias, env),
|
||||
do: Macro.expand(alias, %{env | function: {:__info__, 1}})
|
||||
|
||||
defp expand_key_or_path(list, env) when is_list(list),
|
||||
do: Enum.map(list, &expand_key_or_path(&1, env))
|
||||
|
||||
defp expand_key_or_path(other, _env),
|
||||
do: other
|
||||
|
||||
@doc false
|
||||
def __compile_env__(app, key_or_path, default, env) do
|
||||
case fetch_compile_env(app, key_or_path, env) do
|
||||
@@ -533,6 +544,8 @@ defmodule Application do
|
||||
raise "Application.compile_env!/2 cannot be called inside functions, only in the module body"
|
||||
end
|
||||
|
||||
key_or_path = expand_key_or_path(key_or_path, __CALLER__)
|
||||
|
||||
quote do
|
||||
Application.__compile_env__!(unquote(app), unquote(key_or_path), __ENV__)
|
||||
end
|
||||
|
||||
@@ -26,13 +26,15 @@ defmodule Date.Range do
|
||||
defstruct [:first, :last, :first_in_iso_days, :last_in_iso_days, :step]
|
||||
|
||||
defimpl Enumerable do
|
||||
def member?(%{first: %{calendar: calendar}} = range, %Date{calendar: calendar} = date) do
|
||||
%{
|
||||
first_in_iso_days: first_days,
|
||||
last_in_iso_days: last_days,
|
||||
step: step
|
||||
} = range
|
||||
|
||||
def member?(
|
||||
%Date.Range{
|
||||
first: %{calendar: calendar},
|
||||
first_in_iso_days: first_days,
|
||||
last_in_iso_days: last_days,
|
||||
step: step
|
||||
} = range,
|
||||
%Date{calendar: calendar} = date
|
||||
) do
|
||||
{days, _} = Date.to_iso_days(date)
|
||||
|
||||
cond do
|
||||
@@ -47,24 +49,43 @@ defmodule Date.Range do
|
||||
end
|
||||
end
|
||||
|
||||
def member?(_, _) do
|
||||
def member?(%Date.Range{step: _}, _) do
|
||||
{:ok, false}
|
||||
end
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
def member?(
|
||||
%{__struct__: Date.Range, first_in_iso_days: first_days, last_in_iso_days: last_days} =
|
||||
date_range,
|
||||
date
|
||||
) do
|
||||
step = if first_days <= last_days, do: 1, else: -1
|
||||
member?(Map.put(date_range, :step, step), date)
|
||||
end
|
||||
|
||||
def count(range) do
|
||||
{:ok, size(range)}
|
||||
end
|
||||
|
||||
def slice(range) do
|
||||
%{
|
||||
first_in_iso_days: first,
|
||||
first: %{calendar: calendar},
|
||||
step: step
|
||||
} = range
|
||||
|
||||
def slice(
|
||||
%Date.Range{
|
||||
first_in_iso_days: first,
|
||||
first: %{calendar: calendar},
|
||||
step: step
|
||||
} = range
|
||||
) do
|
||||
{:ok, size(range), &slice(first + &1 * step, step, &2, calendar)}
|
||||
end
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
def slice(
|
||||
%{__struct__: Date.Range, first_in_iso_days: first_days, last_in_iso_days: last_days} =
|
||||
date_range
|
||||
) do
|
||||
step = if first_days <= last_days, do: 1, else: -1
|
||||
slice(Map.put(date_range, :step, step))
|
||||
end
|
||||
|
||||
defp slice(current, _step, 1, calendar) do
|
||||
[date_from_iso_days(current, calendar)]
|
||||
end
|
||||
@@ -76,17 +97,30 @@ defmodule Date.Range do
|
||||
]
|
||||
end
|
||||
|
||||
def reduce(range, acc, fun) do
|
||||
%{
|
||||
first_in_iso_days: first_days,
|
||||
last_in_iso_days: last_days,
|
||||
first: %{calendar: calendar},
|
||||
step: step
|
||||
} = range
|
||||
|
||||
def reduce(
|
||||
%Date.Range{
|
||||
first_in_iso_days: first_days,
|
||||
last_in_iso_days: last_days,
|
||||
first: %{calendar: calendar},
|
||||
step: step
|
||||
},
|
||||
acc,
|
||||
fun
|
||||
) do
|
||||
reduce(first_days, last_days, acc, fun, step, calendar)
|
||||
end
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
def reduce(
|
||||
%{__struct__: Date.Range, first_in_iso_days: first_days, last_in_iso_days: last_days} =
|
||||
date_range,
|
||||
acc,
|
||||
fun
|
||||
) do
|
||||
step = if first_days <= last_days, do: 1, else: -1
|
||||
reduce(Map.put(date_range, :step, step), acc, fun)
|
||||
end
|
||||
|
||||
defp reduce(_first_days, _last_days, {:halt, acc}, _fun, _step, _calendar) do
|
||||
{:halted, acc}
|
||||
end
|
||||
@@ -135,6 +169,15 @@ defmodule Date.Range do
|
||||
defp size(%Date.Range{first_in_iso_days: first_days, last_in_iso_days: last_days, step: step}),
|
||||
do: abs(div(last_days - first_days, step)) + 1
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
defp size(
|
||||
%{__struct__: Date.Range, first_in_iso_days: first_days, last_in_iso_days: last_days} =
|
||||
date_range
|
||||
) do
|
||||
step = if first_days <= last_days, do: 1, else: -1
|
||||
size(Map.put(date_range, :step, step))
|
||||
end
|
||||
|
||||
defp empty?(%Date.Range{
|
||||
first_in_iso_days: first_days,
|
||||
last_in_iso_days: last_days,
|
||||
@@ -151,10 +194,21 @@ defmodule Date.Range do
|
||||
when step < 0 and first_days < last_days,
|
||||
do: true
|
||||
|
||||
defp empty?(%Date.Range{}), do: false
|
||||
defp empty?(%Date.Range{step: _}), do: false
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
defp empty?(
|
||||
%{__struct__: Date.Range, first_in_iso_days: first_days, last_in_iso_days: last_days} =
|
||||
date_range
|
||||
) do
|
||||
step = if first_days <= last_days, do: 1, else: -1
|
||||
empty?(Map.put(date_range, :step, step))
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Inspect do
|
||||
import Kernel, except: [inspect: 2]
|
||||
|
||||
def inspect(%Date.Range{first: first, last: last, step: 1}, _) do
|
||||
"#DateRange<" <> inspect(first) <> ", " <> inspect(last) <> ">"
|
||||
end
|
||||
@@ -162,5 +216,11 @@ defmodule Date.Range do
|
||||
def inspect(%Date.Range{first: first, last: last, step: step}, _) do
|
||||
"#DateRange<" <> inspect(first) <> ", " <> inspect(last) <> ", #{step}>"
|
||||
end
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
def inspect(%{__struct__: Date.Range, first: first, last: last} = date_range, opts) do
|
||||
step = if first <= last, do: 1, else: -1
|
||||
inspect(Map.put(date_range, :step, step), opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -38,8 +38,8 @@ defmodule Code do
|
||||
are loaded as needed. In embedded mode the opposite happens, as all
|
||||
modules need to be loaded upfront or explicitly.
|
||||
|
||||
You can use `ensure_loaded/1` (as well as `ensure_lodead?/1` and
|
||||
`ensure_lodead!/1`) to check if a module is loaded before using it and
|
||||
You can use `ensure_loaded/1` (as well as `ensure_loaded?/1` and
|
||||
`ensure_loaded!/1`) to check if a module is loaded before using it and
|
||||
act.
|
||||
|
||||
## `ensure_compiled/1` and `ensure_compiled!/1`
|
||||
@@ -1550,6 +1550,7 @@ defmodule Code do
|
||||
@doc """
|
||||
Same as `ensure_loaded/1` but raises if the module cannot be loaded.
|
||||
"""
|
||||
@doc since: "1.12.0"
|
||||
@spec ensure_loaded!(module) :: module
|
||||
def ensure_loaded!(module) do
|
||||
case ensure_loaded(module) do
|
||||
|
||||
@@ -257,6 +257,7 @@ defmodule Code.Formatter do
|
||||
force_do_end_blocks: force_do_end_blocks,
|
||||
locals_without_parens: locals_without_parens,
|
||||
operand_nesting: 2,
|
||||
skip_eol: false,
|
||||
comments: comments
|
||||
}
|
||||
end
|
||||
@@ -528,7 +529,7 @@ defmodule Code.Formatter do
|
||||
end
|
||||
|
||||
defp quoted_to_algebra({:fn, meta, [_ | _] = clauses}, _context, state) do
|
||||
anon_fun_to_algebra(clauses, line(meta), closing_line(meta), state, eol?(meta))
|
||||
anon_fun_to_algebra(clauses, line(meta), closing_line(meta), state, eol?(meta, state))
|
||||
end
|
||||
|
||||
defp quoted_to_algebra({fun, meta, args}, context, state) when is_atom(fun) and is_list(args) do
|
||||
@@ -777,7 +778,7 @@ defmodule Code.Formatter do
|
||||
concat(concat(group(left), op_string), group(right))
|
||||
|
||||
true ->
|
||||
eol? = eol?(meta)
|
||||
eol? = eol?(meta, state)
|
||||
|
||||
next_break_fits? =
|
||||
op in @next_break_fits_operators and next_break_fits?(right_arg, state) and not eol?
|
||||
@@ -919,7 +920,7 @@ defmodule Code.Formatter do
|
||||
{docs, comments?, state} =
|
||||
quoted_to_algebra_with_comments(operands, acc, min_line, max_line, state, fun)
|
||||
|
||||
if comments? or eol?(meta) do
|
||||
if comments? or eol?(meta, state) do
|
||||
{docs |> Enum.reduce(&line(&2, &1)) |> force_unfit(), state}
|
||||
else
|
||||
{docs |> Enum.reduce(&glue(&2, &1)), state}
|
||||
@@ -1155,7 +1156,7 @@ defmodule Code.Formatter do
|
||||
end
|
||||
|
||||
args = if keyword?, do: left ++ right, else: left ++ [right]
|
||||
many_eol? = match?([_, _ | _], args) and eol?(meta)
|
||||
many_eol? = match?([_, _ | _], args) and eol?(meta, state)
|
||||
no_generators? = no_generators?(args)
|
||||
to_algebra_fun = "ed_to_algebra(&1, context, &2)
|
||||
|
||||
@@ -1346,8 +1347,7 @@ defmodule Code.Formatter do
|
||||
|
||||
defp list_interpolation_to_algebra([entry | entries], escape, state, acc, last) do
|
||||
{{:., _, [Kernel, :to_string]}, _meta, [quoted]} = entry
|
||||
{doc, state} = block_to_algebra(quoted, @max_line, @min_line, state)
|
||||
doc = surround("\#{", doc, "}") |> interpolation_to_string()
|
||||
{doc, state} = interpolation_to_string(quoted, state)
|
||||
list_interpolation_to_algebra(entries, escape, state, concat(acc, doc), last)
|
||||
end
|
||||
|
||||
@@ -1363,8 +1363,7 @@ defmodule Code.Formatter do
|
||||
|
||||
defp interpolation_to_algebra([entry | entries], escape, state, acc, last) do
|
||||
{:"::", _, [{{:., _, [Kernel, :to_string]}, _meta, [quoted]}, {:binary, _, _}]} = entry
|
||||
{doc, state} = block_to_algebra(quoted, @max_line, @min_line, state)
|
||||
doc = surround("\#{", doc, "}") |> interpolation_to_string()
|
||||
{doc, state} = interpolation_to_string(quoted, state)
|
||||
interpolation_to_algebra(entries, escape, state, concat(acc, doc), last)
|
||||
end
|
||||
|
||||
@@ -1372,6 +1371,12 @@ defmodule Code.Formatter do
|
||||
{concat(acc, last), state}
|
||||
end
|
||||
|
||||
defp interpolation_to_string(quoted, %{skip_eol: skip_eol} = state) do
|
||||
{doc, state} = block_to_algebra(quoted, @max_line, @min_line, %{state | skip_eol: true})
|
||||
doc = interpolation_to_string(surround("\#{", doc, "}"))
|
||||
{doc, %{state | skip_eol: skip_eol}}
|
||||
end
|
||||
|
||||
defp interpolation_to_string(doc) do
|
||||
[head | tail] =
|
||||
doc
|
||||
@@ -1421,7 +1426,7 @@ defmodule Code.Formatter do
|
||||
|
||||
defp bitstring_to_algebra(meta, args, state) do
|
||||
last = length(args) - 1
|
||||
join = if eol?(meta), do: :line, else: :flex_break
|
||||
join = if eol?(meta, state), do: :line, else: :flex_break
|
||||
to_algebra_fun = &bitstring_segment_to_algebra(&1, &2, last)
|
||||
|
||||
{args_doc, join, state} =
|
||||
@@ -1489,7 +1494,7 @@ defmodule Code.Formatter do
|
||||
## Literals
|
||||
|
||||
defp list_to_algebra(meta, args, state) do
|
||||
join = if eol?(meta), do: :line, else: :break
|
||||
join = if eol?(meta, state), do: :line, else: :break
|
||||
fun = "ed_to_algebra(&1, :parens_arg, &2)
|
||||
|
||||
{args_doc, _join, state} =
|
||||
@@ -1499,7 +1504,7 @@ defmodule Code.Formatter do
|
||||
end
|
||||
|
||||
defp map_to_algebra(meta, name_doc, [{:|, _, [left, right]}], state) do
|
||||
join = if eol?(meta), do: :line, else: :break
|
||||
join = if eol?(meta, state), do: :line, else: :break
|
||||
fun = "ed_to_algebra(&1, :parens_arg, &2)
|
||||
{left_doc, state} = fun.(left, state)
|
||||
|
||||
@@ -1516,7 +1521,7 @@ defmodule Code.Formatter do
|
||||
end
|
||||
|
||||
defp map_to_algebra(meta, name_doc, args, state) do
|
||||
join = if eol?(meta), do: :line, else: :break
|
||||
join = if eol?(meta, state), do: :line, else: :break
|
||||
fun = "ed_to_algebra(&1, :parens_arg, &2)
|
||||
|
||||
{args_doc, _join, state} =
|
||||
@@ -1527,7 +1532,7 @@ defmodule Code.Formatter do
|
||||
end
|
||||
|
||||
defp tuple_to_algebra(meta, args, join, state) do
|
||||
join = if eol?(meta), do: :line, else: join
|
||||
join = if eol?(meta, state), do: :line, else: join
|
||||
fun = "ed_to_algebra(&1, :parens_arg, &2)
|
||||
|
||||
{args_doc, join, state} =
|
||||
@@ -1709,7 +1714,7 @@ defmodule Code.Formatter do
|
||||
|> glue(body_doc)
|
||||
|> nest(2)
|
||||
|> glue("end")
|
||||
|> maybe_force_clauses(clauses)
|
||||
|> maybe_force_clauses(clauses, state)
|
||||
|> group()
|
||||
|
||||
{doc, state}
|
||||
@@ -1743,7 +1748,7 @@ defmodule Code.Formatter do
|
||||
|> glue(body_doc)
|
||||
|> nest(2)
|
||||
|> glue("end")
|
||||
|> maybe_force_clauses(clauses)
|
||||
|> maybe_force_clauses(clauses, state)
|
||||
|> group()
|
||||
|
||||
{doc, state}
|
||||
@@ -1771,7 +1776,7 @@ defmodule Code.Formatter do
|
||||
"(() -> "
|
||||
|> concat(nest(body_doc, :cursor))
|
||||
|> concat(")")
|
||||
|> maybe_force_clauses(clauses)
|
||||
|> maybe_force_clauses(clauses, state)
|
||||
|> group()
|
||||
|
||||
{doc, state}
|
||||
@@ -1792,7 +1797,7 @@ defmodule Code.Formatter do
|
||||
|> group()
|
||||
|> concat(break() |> concat(body_doc) |> nest(2))
|
||||
|> wrap_in_parens()
|
||||
|> maybe_force_clauses(clauses)
|
||||
|> maybe_force_clauses(clauses, state)
|
||||
|> group()
|
||||
|
||||
{doc, state}
|
||||
@@ -1811,8 +1816,8 @@ defmodule Code.Formatter do
|
||||
|
||||
## Clauses
|
||||
|
||||
defp maybe_force_clauses(doc, clauses) do
|
||||
if Enum.any?(clauses, fn {:->, meta, _} -> eol?(meta) end) do
|
||||
defp maybe_force_clauses(doc, clauses, state) do
|
||||
if Enum.any?(clauses, fn {:->, meta, _} -> eol?(meta, state) end) do
|
||||
force_unfit(doc)
|
||||
else
|
||||
doc
|
||||
@@ -1835,7 +1840,7 @@ defmodule Code.Formatter do
|
||||
{doc_acc, state_acc}
|
||||
end)
|
||||
|
||||
{clauses_doc |> maybe_force_clauses([clause | clauses]) |> group(), state}
|
||||
{clauses_doc |> maybe_force_clauses([clause | clauses], state) |> group(), state}
|
||||
end
|
||||
|
||||
defp clauses_to_algebra(other, min_line, max_line, state) do
|
||||
@@ -2176,8 +2181,8 @@ defmodule Code.Formatter do
|
||||
false
|
||||
end
|
||||
|
||||
defp eol_or_comments?(meta, %{comments: comments}) do
|
||||
eol?(meta) or
|
||||
defp eol_or_comments?(meta, %{comments: comments} = state) do
|
||||
eol?(meta, state) or
|
||||
(
|
||||
min_line = line(meta)
|
||||
max_line = closing_line(meta)
|
||||
@@ -2264,9 +2269,8 @@ defmodule Code.Formatter do
|
||||
defp keyword_key?(_),
|
||||
do: false
|
||||
|
||||
defp eol?(meta) do
|
||||
Keyword.get(meta, :newlines, 0) > 0
|
||||
end
|
||||
defp eol?(_meta, %{skip_eol: true}), do: false
|
||||
defp eol?(meta, _state), do: Keyword.get(meta, :newlines, 0) > 0
|
||||
|
||||
defp meta?(meta, key) do
|
||||
is_list(meta[key])
|
||||
|
||||
@@ -419,7 +419,7 @@ defmodule DynamicSupervisor do
|
||||
@doc since: "1.6.0"
|
||||
@spec which_children(Supervisor.supervisor()) :: [
|
||||
# module() | :dynamic here because :supervisor.modules() is not exported
|
||||
{:undefined, pid | :restarting, :worker | :supervisor, module() | :dynamic}
|
||||
{:undefined, pid | :restarting, :worker | :supervisor, [module()] | :dynamic}
|
||||
]
|
||||
def which_children(supervisor) do
|
||||
call(supervisor, :which_children)
|
||||
|
||||
+50
-15
@@ -1421,28 +1421,35 @@ defmodule Enum do
|
||||
into_protocol(enumerable, collectable)
|
||||
end
|
||||
|
||||
def into(%{} = enumerable, %{} = collectable) do
|
||||
Map.merge(collectable, enumerable)
|
||||
end
|
||||
|
||||
def into(enumerable, %{} = collectable) when is_list(enumerable) do
|
||||
Map.merge(collectable, :maps.from_list(enumerable))
|
||||
end
|
||||
|
||||
def into(enumerable, %{} = collectable) do
|
||||
reduce(enumerable, collectable, fn {key, val}, acc ->
|
||||
Map.put(acc, key, val)
|
||||
end)
|
||||
if map_size(collectable) == 0 do
|
||||
into_map(enumerable)
|
||||
else
|
||||
into_map(enumerable, collectable)
|
||||
end
|
||||
end
|
||||
|
||||
def into(enumerable, collectable) do
|
||||
into_protocol(enumerable, collectable)
|
||||
end
|
||||
|
||||
defp into_map(%{} = enumerable), do: enumerable
|
||||
defp into_map(enumerable) when is_list(enumerable), do: :maps.from_list(enumerable)
|
||||
defp into_map(enumerable), do: enumerable |> Enum.to_list() |> :maps.from_list()
|
||||
|
||||
defp into_map(%{} = enumerable, collectable),
|
||||
do: Map.merge(collectable, enumerable)
|
||||
|
||||
defp into_map(enumerable, collectable) when is_list(enumerable),
|
||||
do: Map.merge(collectable, :maps.from_list(enumerable))
|
||||
|
||||
defp into_map(enumerable, collectable),
|
||||
do: Enum.reduce(enumerable, collectable, fn {key, val}, acc -> Map.put(acc, key, val) end)
|
||||
|
||||
defp into_protocol(enumerable, collectable) do
|
||||
{initial, fun} = Collectable.into(collectable)
|
||||
|
||||
into(enumerable, initial, fun, fn entry, acc ->
|
||||
into_protocol(enumerable, initial, fun, fn entry, acc ->
|
||||
fun.(acc, {:cont, entry})
|
||||
end)
|
||||
end
|
||||
@@ -1461,20 +1468,42 @@ defmodule Enum do
|
||||
|
||||
"""
|
||||
@spec into(Enumerable.t(), Collectable.t(), (term -> term)) :: Collectable.t()
|
||||
|
||||
def into(enumerable, collectable, transform) when is_list(collectable) do
|
||||
collectable ++ map(enumerable, transform)
|
||||
end
|
||||
|
||||
def into(%_{} = enumerable, collectable, transform) do
|
||||
into_protocol(enumerable, collectable, transform)
|
||||
end
|
||||
|
||||
def into(enumerable, %_{} = collectable, transform) do
|
||||
into_protocol(enumerable, collectable, transform)
|
||||
end
|
||||
|
||||
def into(enumerable, %{} = collectable, transform) do
|
||||
if map_size(collectable) == 0 do
|
||||
enumerable |> Enum.map(transform) |> :maps.from_list()
|
||||
else
|
||||
Enum.reduce(enumerable, collectable, fn entry, acc ->
|
||||
{key, val} = transform.(entry)
|
||||
Map.put(acc, key, val)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
def into(enumerable, collectable, transform) do
|
||||
into_protocol(enumerable, collectable, transform)
|
||||
end
|
||||
|
||||
defp into_protocol(enumerable, collectable, transform) do
|
||||
{initial, fun} = Collectable.into(collectable)
|
||||
|
||||
into(enumerable, initial, fun, fn entry, acc ->
|
||||
into_protocol(enumerable, initial, fun, fn entry, acc ->
|
||||
fun.(acc, {:cont, transform.(entry)})
|
||||
end)
|
||||
end
|
||||
|
||||
defp into(enumerable, initial, fun, callback) do
|
||||
defp into_protocol(enumerable, initial, fun, callback) do
|
||||
try do
|
||||
reduce(enumerable, initial, callback)
|
||||
catch
|
||||
@@ -2632,6 +2661,12 @@ defmodule Enum do
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
def slice(enumerable, %{__struct__: Range, first: first, last: last} = index_range) do
|
||||
step = if first <= last, do: 1, else: -1
|
||||
slice(enumerable, Map.put(index_range, :step, step))
|
||||
end
|
||||
|
||||
defp slice_range(enumerable, first, last) when last >= first and last >= 0 and first >= 0 do
|
||||
slice_any(enumerable, first, last - first + 1)
|
||||
end
|
||||
|
||||
@@ -516,6 +516,7 @@ defmodule IO do
|
||||
IO.stream(:stdio, :line)
|
||||
|
||||
"""
|
||||
@doc since: "1.12.0"
|
||||
def stream, do: stream(:stdio, :line)
|
||||
|
||||
@doc """
|
||||
@@ -534,6 +535,9 @@ defmodule IO do
|
||||
Note that an IO stream has side effects and every time
|
||||
you go over the stream you may get different results.
|
||||
|
||||
`stream/1` has been introduced in Elixir v1.12.0,
|
||||
while `stream/2` has been available since v1.0.0.
|
||||
|
||||
## Examples
|
||||
|
||||
Here is an example on how we mimic an echo server
|
||||
@@ -557,6 +561,7 @@ defmodule IO do
|
||||
IO.binstream(:stdio, :line)
|
||||
|
||||
"""
|
||||
@doc since: "1.12.0"
|
||||
def binstream, do: binstream(:stdio, :line)
|
||||
|
||||
@doc """
|
||||
@@ -574,6 +579,9 @@ defmodule IO do
|
||||
|
||||
Finally, do not use this function on IO devices in Unicode
|
||||
mode as it will return the wrong result.
|
||||
|
||||
`binstream/1` has been introduced in Elixir v1.12.0,
|
||||
while `binstream/2` has been available since v1.0.0.
|
||||
"""
|
||||
@spec binstream(device, :line | pos_integer) :: Enumerable.t()
|
||||
def binstream(device \\ :stdio, line_or_bytes)
|
||||
|
||||
@@ -5554,7 +5554,7 @@ defmodule Kernel do
|
||||
quote(do: List.to_charlist(unquote(unescape_list_tokens(pieces))))
|
||||
end
|
||||
|
||||
@doc """
|
||||
@doc ~S"""
|
||||
Handles the sigil `~r` for regular expressions.
|
||||
|
||||
It returns a regular expression pattern, unescaping characters and replacing
|
||||
@@ -5564,12 +5564,15 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Regex.match?(~r(foo), "foo")
|
||||
iex> Regex.match?(~r/foo/, "foo")
|
||||
true
|
||||
|
||||
iex> Regex.match?(~r/a#{:b}c/, "abc")
|
||||
true
|
||||
|
||||
While the `~r` sigil allows parens and brackets to be used as delimiters,
|
||||
it is preferred to use `"` or `/` to avoid escaping conflicts with reserved
|
||||
regex characters.
|
||||
"""
|
||||
defmacro sigil_r(term, modifiers)
|
||||
|
||||
|
||||
@@ -623,13 +623,13 @@ defmodule Kernel.ParallelCompiler do
|
||||
data = maybe_warn_long_compilation(data, state)
|
||||
|
||||
if state.profile != :none do
|
||||
compiling = System.convert_time_unit(data.compiling, :native, :millisecond)
|
||||
waiting = System.convert_time_unit(data.waiting, :native, :millisecond)
|
||||
extra = if waiting > 0, do: " (plus #{waiting}ms waiting)", else: ""
|
||||
compiling = to_padded_ms(data.compiling)
|
||||
waiting = to_padded_ms(data.waiting)
|
||||
relative = Path.relative_to_cwd(data.file)
|
||||
|
||||
IO.puts(
|
||||
:stderr,
|
||||
"[profile] #{Path.relative_to_cwd(data.file)} compiled in #{compiling}ms" <> extra
|
||||
"[profile] #{compiling}ms compiling + #{waiting}ms waiting for #{relative}"
|
||||
)
|
||||
end
|
||||
|
||||
@@ -640,6 +640,13 @@ defmodule Kernel.ParallelCompiler do
|
||||
end)
|
||||
end
|
||||
|
||||
defp to_padded_ms(time) do
|
||||
time
|
||||
|> System.convert_time_unit(:native, :millisecond)
|
||||
|> Integer.to_string()
|
||||
|> String.pad_leading(6, " ")
|
||||
end
|
||||
|
||||
defp discard_down(pid) do
|
||||
receive do
|
||||
{:DOWN, _, :process, ^pid, _} -> :ok
|
||||
|
||||
@@ -1735,9 +1735,6 @@ defmodule Macro do
|
||||
defp do_camelize(<<?_, h, t::binary>>) when h >= ?a and h <= ?z,
|
||||
do: <<to_upper_char(h)>> <> do_camelize(t)
|
||||
|
||||
defp do_camelize(<<p, ?_, h, t::binary>>) when p >= ?0 and p <= ?9 and h >= ?0 and h <= ?9,
|
||||
do: <<p, ?_, h>> <> do_camelize(t)
|
||||
|
||||
defp do_camelize(<<?_, h, t::binary>>) when h >= ?0 and h <= ?9, do: <<h>> <> do_camelize(t)
|
||||
defp do_camelize(<<?_>>), do: <<>>
|
||||
defp do_camelize(<<?/, t::binary>>), do: <<?.>> <> camelize(t)
|
||||
|
||||
@@ -105,7 +105,9 @@ defmodule Macro.Env do
|
||||
vars: vars
|
||||
}
|
||||
|
||||
# Define the __struct__ callbacks by hand for bootstrap reasons.
|
||||
# TODO: Remove :vars field on v2.0
|
||||
@doc false
|
||||
def __struct__ do
|
||||
%{
|
||||
__struct__: __MODULE__,
|
||||
@@ -130,6 +132,7 @@ defmodule Macro.Env do
|
||||
}
|
||||
end
|
||||
|
||||
@doc false
|
||||
def __struct__(kv) do
|
||||
Enum.reduce(kv, __struct__(), fn {k, v}, acc -> :maps.update(k, v, acc) end)
|
||||
end
|
||||
|
||||
+1
-11
@@ -216,20 +216,10 @@ defmodule Map do
|
||||
@spec new(Enumerable.t(), (term -> {key, value})) :: map
|
||||
def new(enumerable, transform) when is_function(transform, 1) do
|
||||
enumerable
|
||||
|> Enum.to_list()
|
||||
|> new_transform(transform, [])
|
||||
end
|
||||
|
||||
defp new_transform([], _fun, acc) do
|
||||
acc
|
||||
|> :lists.reverse()
|
||||
|> Enum.map(transform)
|
||||
|> :maps.from_list()
|
||||
end
|
||||
|
||||
defp new_transform([element | rest], fun, acc) do
|
||||
new_transform(rest, fun, [fun.(element) | acc])
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns whether the given `key` exists in the given `map`.
|
||||
|
||||
|
||||
@@ -185,6 +185,12 @@ defmodule Range do
|
||||
def size(first..last//step) when step < 0 and first < last, do: 0
|
||||
def size(first..last//step), do: abs(div(last - first, step)) + 1
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
def size(%{__struct__: Range, first: first, last: last} = range) do
|
||||
step = if first <= last, do: 1, else: -1
|
||||
size(Map.put(range, :step, step))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if two ranges are disjoint.
|
||||
|
||||
@@ -273,6 +279,12 @@ defimpl Enumerable, for: Range do
|
||||
reduce(first, last, acc, fun, step)
|
||||
end
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
def reduce(%{__struct__: Range, first: first, last: last} = range, acc, fun) do
|
||||
step = if first <= last, do: 1, else: -1
|
||||
reduce(Map.put(range, :step, step), acc, fun)
|
||||
end
|
||||
|
||||
defp reduce(_first, _last, {:halt, acc}, _fun, _step) do
|
||||
{:halted, acc}
|
||||
end
|
||||
@@ -304,6 +316,13 @@ defimpl Enumerable, for: Range do
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
def member?(%{__struct__: Range, first: first, last: last} = range, value)
|
||||
when is_integer(value) do
|
||||
step = if first <= last, do: 1, else: -1
|
||||
member?(Map.put(range, :step, step), value)
|
||||
end
|
||||
|
||||
def member?(_, _value) do
|
||||
{:ok, false}
|
||||
end
|
||||
@@ -316,12 +335,19 @@ defimpl Enumerable, for: Range do
|
||||
{:ok, Range.size(range), &slice(first + &1 * step, step, &2)}
|
||||
end
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
def slice(%{__struct__: Range, first: first, last: last} = range) do
|
||||
step = if first <= last, do: 1, else: -1
|
||||
slice(Map.put(range, :step, step))
|
||||
end
|
||||
|
||||
defp slice(current, _step, 1), do: [current]
|
||||
defp slice(current, step, remaining), do: [current | slice(current + step, step, remaining - 1)]
|
||||
end
|
||||
|
||||
defimpl Inspect, for: Range do
|
||||
import Inspect.Algebra
|
||||
import Kernel, except: [inspect: 2]
|
||||
|
||||
def inspect(first..last//1, opts) do
|
||||
concat([to_doc(first, opts), "..", to_doc(last, opts)])
|
||||
@@ -330,4 +356,10 @@ defimpl Inspect, for: Range do
|
||||
def inspect(first..last//step, opts) do
|
||||
concat([to_doc(first, opts), "..", to_doc(last, opts), "//", to_doc(step, opts)])
|
||||
end
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
def inspect(%{__struct__: Range, first: first, last: last} = range, opts) do
|
||||
step = if first <= last, do: 1, else: -1
|
||||
inspect(Map.put(range, :step, step), opts)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2098,6 +2098,12 @@ defmodule String do
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Remove me on v2.0
|
||||
def slice(string, %{__struct__: Range, first: first, last: last} = range) do
|
||||
step = if first <= last, do: 1, else: -1
|
||||
slice(string, Map.put(range, :step, step))
|
||||
end
|
||||
|
||||
defp slice_range("", _, _), do: ""
|
||||
|
||||
defp slice_range(string, first, -1) when first >= 0 do
|
||||
|
||||
@@ -930,7 +930,7 @@ defmodule Supervisor do
|
||||
"""
|
||||
@spec which_children(supervisor) :: [
|
||||
# inlining module() | :dynamic here because :supervisor.modules() is not exported
|
||||
{term() | :undefined, child | :restarting, :worker | :supervisor, module() | :dynamic}
|
||||
{term() | :undefined, child | :restarting, :worker | :supervisor, [module()] | :dynamic}
|
||||
]
|
||||
def which_children(supervisor) do
|
||||
call(supervisor, :which_children)
|
||||
|
||||
@@ -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"])
|
||||
@@ -252,7 +252,7 @@ eval_forms(Tree, RawBinding, OE) ->
|
||||
|
||||
case Erl of
|
||||
{atom, _, Atom} ->
|
||||
{Atom, Binding, NewE};
|
||||
{Atom, RawBinding, NewE};
|
||||
|
||||
_ ->
|
||||
Exprs =
|
||||
|
||||
@@ -77,7 +77,7 @@ require_function(Meta, Receiver, Name, Arity, E) ->
|
||||
end.
|
||||
|
||||
remote_function(Meta, Receiver, Name, Arity, E) ->
|
||||
check_deprecated(Meta, Receiver, Name, Arity, E),
|
||||
check_deprecated(Meta, function, Receiver, Name, Arity, E),
|
||||
|
||||
case elixir_rewrite:inline(Receiver, Name, Arity) of
|
||||
{AR, AN} -> {remote, AR, AN, Arity};
|
||||
@@ -161,7 +161,7 @@ do_expand_import(Meta, {Name, Arity} = Tuple, Args, Module, E, Result) ->
|
||||
elixir_locals:record_import(Tuple, Receiver, Module, ?key(E, function)),
|
||||
{ok, Receiver, Name, Args};
|
||||
{macro, Receiver} ->
|
||||
check_deprecated(Meta, Receiver, Name, Arity, E),
|
||||
check_deprecated(Meta, macro, Receiver, Name, Arity, E),
|
||||
elixir_env:trace({imported_macro, Meta, Receiver, Name, Arity}, E),
|
||||
elixir_locals:record_import(Tuple, Receiver, Module, ?key(E, function)),
|
||||
{ok, Receiver, expand_macro_named(Meta, Receiver, Name, Arity, Args, E)};
|
||||
@@ -180,17 +180,18 @@ do_expand_import(Meta, {Name, Arity} = Tuple, Args, Module, E, Result) ->
|
||||
end.
|
||||
|
||||
expand_require(Meta, Receiver, {Name, Arity} = Tuple, Args, E) ->
|
||||
check_deprecated(Meta, Receiver, Name, Arity, E),
|
||||
Required = (Receiver == ?key(E, module)) orelse required(Meta) orelse is_element(Receiver, ?key(E, requires)),
|
||||
|
||||
case is_element(Tuple, get_macros(Receiver, Required)) of
|
||||
true when Required ->
|
||||
check_deprecated(Meta, macro, Receiver, Name, Arity, E),
|
||||
elixir_env:trace({remote_macro, Meta, Receiver, Name, Arity}, E),
|
||||
{ok, Receiver, expand_macro_named(Meta, Receiver, Name, Arity, Args, E)};
|
||||
true ->
|
||||
Info = {unrequired_module, {Receiver, Name, length(Args)}},
|
||||
elixir_errors:form_error(Meta, E, ?MODULE, Info);
|
||||
false ->
|
||||
check_deprecated(Meta, function, Receiver, Name, Arity, E),
|
||||
error
|
||||
end.
|
||||
|
||||
@@ -361,12 +362,13 @@ elixir_imported_macros() ->
|
||||
error:undef -> []
|
||||
end.
|
||||
|
||||
check_deprecated(_, erlang, _, _, _) ->
|
||||
ok;
|
||||
check_deprecated(_, ?kernel, _, _, _) ->
|
||||
ok;
|
||||
check_deprecated(Meta, Receiver, Name, Arity, E) ->
|
||||
case (?key(E, function) == nil) andalso get_deprecations(Receiver) of
|
||||
check_deprecated(_, _, erlang, _, _, _) -> ok;
|
||||
check_deprecated(_, _, elixir_def, _, _, _) -> ok;
|
||||
check_deprecated(_, _, elixir_module, _, _, _) -> ok;
|
||||
check_deprecated(_, _, ?kernel, _, _, _) -> ok;
|
||||
check_deprecated(Meta, Kind, Receiver, Name, Arity, E) ->
|
||||
%% Any compile time behaviour cannot be verified by the runtime group pass.
|
||||
case ((?key(E, function) == nil) or (Kind == macro)) andalso get_deprecations(Receiver) of
|
||||
[_ | _] = Deprecations ->
|
||||
case lists:keyfind({Name, Arity}, 1, Deprecations) of
|
||||
{_, Message} ->
|
||||
|
||||
@@ -223,9 +223,9 @@ translate({{'.', _, [Left, Right]}, Meta, []}, S) when is_tuple(Left), is_atom(R
|
||||
Ann = ?ann(Meta),
|
||||
TRight = {atom, Ann, Right},
|
||||
|
||||
{Var, SV} = elixir_erl_var:build('_', SL),
|
||||
TVar = {var, Ann, Var},
|
||||
Generated = erl_anno:set_generated(true, Ann),
|
||||
{Var, SV} = elixir_erl_var:build('_', SL),
|
||||
TVar = {var, Generated, Var},
|
||||
TError = {tuple, Ann, [{atom, Ann, badkey}, TRight, TVar]},
|
||||
|
||||
{{'case', Generated, TLeft, [
|
||||
|
||||
@@ -1161,23 +1161,19 @@ format_error({invalid_alias, Expr}) ->
|
||||
"it at runtime. If instead you wanted to invoke a function or access a field, "
|
||||
"wrap the function or field name in double quotes",
|
||||
io_lib:format(Message, ['Elixir.Macro':to_string(Expr)]);
|
||||
format_error({op_ambiguity, Name, {Op, _, [Arg]}}) ->
|
||||
format_error({op_ambiguity, Name, Arg}) ->
|
||||
NameString = atom_to_binary(Name, utf8),
|
||||
OpString = atom_to_binary(Op, utf8),
|
||||
ArgString = 'Elixir.Macro':to_string(Arg),
|
||||
|
||||
Message =
|
||||
"\"~ts ~ts~ts\" looks like a function call but there is a variable named \"~ts\".\n"
|
||||
"\"~ts ~ts\" looks like a function call but there is a variable named \"~ts\". "
|
||||
"If you want to perform a function call, use parentheses:\n"
|
||||
"\n"
|
||||
" ~ts(~ts~ts)\n"
|
||||
" ~ts(~ts)\n"
|
||||
"\n"
|
||||
"If you want to perform an operation on the variable ~ts, use even spaces instead:\n"
|
||||
"\n"
|
||||
" ~ts ~ts ~ts",
|
||||
io_lib:format(Message, [NameString, OpString, ArgString, NameString,
|
||||
NameString, OpString, ArgString, NameString,
|
||||
NameString, OpString, ArgString]);
|
||||
"If you want to perform an operation on the variable ~ts, use spaces "
|
||||
"around the unary operator",
|
||||
io_lib:format(Message, [NameString, ArgString, NameString, NameString, ArgString, NameString]);
|
||||
format_error({invalid_clauses, Name}) ->
|
||||
Message =
|
||||
"the function \"~ts\" cannot handle clauses with the -> operator because it is not a macro. "
|
||||
|
||||
@@ -141,4 +141,71 @@ defmodule Date.RangeTest do
|
||||
Date.range(~D[2000-01-01], ~D[2000-01-31], step)
|
||||
end
|
||||
end
|
||||
|
||||
describe "old date ranges" do
|
||||
test "inspect" do
|
||||
asc = %{
|
||||
__struct__: Date.Range,
|
||||
first: ~D[2021-07-14],
|
||||
first_in_iso_days: 738_350,
|
||||
last: ~D[2021-07-17],
|
||||
last_in_iso_days: 738_353
|
||||
}
|
||||
|
||||
desc = %{
|
||||
__struct__: Date.Range,
|
||||
first: ~D[2021-07-17],
|
||||
first_in_iso_days: 738_353,
|
||||
last: ~D[2021-07-14],
|
||||
last_in_iso_days: 738_350
|
||||
}
|
||||
|
||||
assert inspect(asc) == "#DateRange<~D[2021-07-14], ~D[2021-07-17]>"
|
||||
assert inspect(desc) == "#DateRange<~D[2021-07-17], ~D[2021-07-14], -1>"
|
||||
end
|
||||
|
||||
test "enumerable" do
|
||||
asc = %{
|
||||
__struct__: Date.Range,
|
||||
first: ~D[2021-07-14],
|
||||
first_in_iso_days: 738_350,
|
||||
last: ~D[2021-07-17],
|
||||
last_in_iso_days: 738_353
|
||||
}
|
||||
|
||||
desc = %{
|
||||
__struct__: Date.Range,
|
||||
first: ~D[2021-07-17],
|
||||
first_in_iso_days: 738_353,
|
||||
last: ~D[2021-07-14],
|
||||
last_in_iso_days: 738_350
|
||||
}
|
||||
|
||||
# member? implementations tests also empty?
|
||||
assert Enumerable.member?(asc, ~D[2021-07-15])
|
||||
assert {:ok, 4, _} = Enumerable.slice(asc)
|
||||
|
||||
assert Enum.reduce(asc, [], fn x, acc -> [x | acc] end) == [
|
||||
~D[2021-07-17],
|
||||
~D[2021-07-16],
|
||||
~D[2021-07-15],
|
||||
~D[2021-07-14]
|
||||
]
|
||||
|
||||
assert Enum.count(asc) == 4
|
||||
|
||||
# member? implementations tests also empty?
|
||||
assert Enumerable.member?(desc, ~D[2021-07-15])
|
||||
assert {:ok, 4, _} = Enumerable.slice(desc)
|
||||
|
||||
assert Enum.reduce(desc, [], fn x, acc -> [x | acc] end) == [
|
||||
~D[2021-07-14],
|
||||
~D[2021-07-15],
|
||||
~D[2021-07-16],
|
||||
~D[2021-07-17]
|
||||
]
|
||||
|
||||
assert Enum.count(desc) == 4
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -548,6 +548,21 @@ defmodule Code.Formatter.IntegrationTest do
|
||||
"""
|
||||
end
|
||||
|
||||
test "multiline expression inside interpolation" do
|
||||
bad = ~S"""
|
||||
Logger.info("Example: #{
|
||||
inspect(%{
|
||||
a: 1,
|
||||
b: 2
|
||||
})
|
||||
}")
|
||||
"""
|
||||
|
||||
assert_format bad, ~S"""
|
||||
Logger.info("Example: #{inspect(%{a: 1, b: 2})}")
|
||||
"""
|
||||
end
|
||||
|
||||
test "comment inside operator with when" do
|
||||
bad = """
|
||||
raise function(x) ::
|
||||
|
||||
@@ -167,6 +167,10 @@ defmodule CodeTest do
|
||||
assert Code.eval_string("two = 1 + 1") == {2, [two: 2]}
|
||||
end
|
||||
|
||||
test "keeps bindings on optimized evals" do
|
||||
assert Code.eval_string("import Enum", x: 1) == {Enum, [x: 1]}
|
||||
end
|
||||
|
||||
test "supports a %Macro.Env{} struct as the third argument" do
|
||||
assert {3, _} = Code.eval_string("a + b", [a: 1, b: 2], __ENV__)
|
||||
end
|
||||
|
||||
@@ -396,6 +396,8 @@ defmodule EnumTest do
|
||||
test "into/2" do
|
||||
assert Enum.into([a: 1, b: 2], %{}) == %{a: 1, b: 2}
|
||||
assert Enum.into([a: 1, b: 2], %{c: 3}) == %{a: 1, b: 2, c: 3}
|
||||
assert Enum.into(MapSet.new(a: 1, b: 2), %{}) == %{a: 1, b: 2}
|
||||
assert Enum.into(MapSet.new(a: 1, b: 2), %{c: 3}) == %{a: 1, b: 2, c: 3}
|
||||
assert Enum.into(%{a: 1, b: 2}, []) == [a: 1, b: 2]
|
||||
assert Enum.into(1..3, []) == [1, 2, 3]
|
||||
assert Enum.into(["H", "i"], "") == "Hi"
|
||||
@@ -405,7 +407,7 @@ defmodule EnumTest do
|
||||
assert Enum.into([1, 2, 3], [], fn x -> x * 2 end) == [2, 4, 6]
|
||||
assert Enum.into([1, 2, 3], "numbers: ", &to_string/1) == "numbers: 123"
|
||||
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
assert_raise MatchError, fn ->
|
||||
Enum.into([2, 3], %{a: 1}, & &1)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -470,11 +470,12 @@ defmodule ExceptionTest do
|
||||
* min/1
|
||||
"""
|
||||
|
||||
assert blame_message(:erlang, & &1.gt_cookie()) == """
|
||||
function :erlang.gt_cookie/0 is undefined or private. Did you mean one of:
|
||||
assert blame_message(:erlang, & &1.hal()) == """
|
||||
function :erlang.hal/0 is undefined or private. Did you mean one of:
|
||||
|
||||
* get_cookie/0
|
||||
* set_cookie/2
|
||||
* halt/0
|
||||
* halt/1
|
||||
* halt/2
|
||||
"""
|
||||
end
|
||||
|
||||
|
||||
@@ -2538,10 +2538,10 @@ defmodule Kernel.ExpansionTest do
|
||||
|
||||
describe "op ambiguity" do
|
||||
test "raises when a call is ambiguous" do
|
||||
# We use string_to_quoted! here to avoid the formatter adding parentheses
|
||||
message = ~r["a -1" looks like a function call but there is a variable named "a"]
|
||||
|
||||
assert_raise CompileError, message, fn ->
|
||||
# We use string_to_quoted! here to avoid the formatter adding parentheses to "a -1".
|
||||
code =
|
||||
Code.string_to_quoted!("""
|
||||
a = 1
|
||||
@@ -2550,6 +2550,18 @@ defmodule Kernel.ExpansionTest do
|
||||
|
||||
expand(code)
|
||||
end
|
||||
|
||||
message = ~r["a -1\.\.a \+ 1" looks like a function call but there is a variable named "a"]
|
||||
|
||||
assert_raise CompileError, message, fn ->
|
||||
code =
|
||||
Code.string_to_quoted!("""
|
||||
a = 1
|
||||
a -1 .. a + 1
|
||||
""")
|
||||
|
||||
expand(code)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -202,5 +202,20 @@ defmodule Kernel.LexicalTrackerTest do
|
||||
assert URI in exports
|
||||
refute URI in runtime
|
||||
end
|
||||
|
||||
test "compile_env! does not add a compile dependency" do
|
||||
{{compile, exports, runtime, _}, _binding} =
|
||||
Code.eval_string("""
|
||||
defmodule Kernel.LexicalTrackerTest.CompileEnvStruct do
|
||||
Application.compile_env(:elixir, URI)
|
||||
Application.compile_env(:elixir, [:foo, URI, :bar])
|
||||
Kernel.LexicalTracker.references(__ENV__.lexical_tracker)
|
||||
end |> elem(3)
|
||||
""")
|
||||
|
||||
refute URI in compile
|
||||
refute URI in exports
|
||||
assert URI in runtime
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -43,7 +43,9 @@ defmodule Kernel.ParallelCompilerTest do
|
||||
assert HelloWorld in modules
|
||||
end)
|
||||
|
||||
assert profile =~ ~r"\[profile\] .*tmp/profile_time/bar.ex compiled in \d+ms"
|
||||
assert profile =~
|
||||
~r"\[profile\] [\s\d]{6}ms compiling \+ 0ms waiting for .*tmp/profile_time/bar.ex"
|
||||
|
||||
assert profile =~ ~r"\[profile\] Finished compilation cycle of 1 modules in \d+ms"
|
||||
assert profile =~ ~r"\[profile\] Finished group pass check of 1 modules in \d+ms"
|
||||
after
|
||||
|
||||
@@ -1036,7 +1036,8 @@ defmodule MacroTest do
|
||||
assert Macro.camelize("foo__bar") == "FooBar"
|
||||
assert Macro.camelize("foo/bar") == "Foo.Bar"
|
||||
assert Macro.camelize("Foo.Bar") == "Foo.Bar"
|
||||
assert Macro.camelize("foo1_0") == "Foo1_0"
|
||||
assert Macro.camelize("foo1_0") == "Foo10"
|
||||
assert Macro.camelize("foo_123_4_567") == "Foo1234567"
|
||||
assert Macro.camelize("FOO_BAR") == "FOO_BAR"
|
||||
assert Macro.camelize("FOO.BAR") == "FOO.BAR"
|
||||
assert Macro.camelize("") == ""
|
||||
|
||||
@@ -663,6 +663,31 @@ defmodule Module.Types.IntegrationTest do
|
||||
|
||||
assert_warnings(files, warning)
|
||||
end
|
||||
|
||||
test "reports macro" do
|
||||
files = %{
|
||||
"a.ex" => """
|
||||
defmodule A do
|
||||
@deprecated "oops"
|
||||
defmacro a, do: :ok
|
||||
end
|
||||
""",
|
||||
"b.ex" => """
|
||||
defmodule B do
|
||||
require A
|
||||
def b, do: A.a()
|
||||
end
|
||||
"""
|
||||
}
|
||||
|
||||
warning = """
|
||||
warning: A.a/0 is deprecated. oops
|
||||
b.ex:3: B.b/0
|
||||
|
||||
"""
|
||||
|
||||
assert_warnings(files, warning)
|
||||
end
|
||||
end
|
||||
|
||||
defp assert_warnings(files, expected) when is_binary(expected) do
|
||||
|
||||
@@ -94,4 +94,49 @@ defmodule RangeTest do
|
||||
assert Range.disjoint?(1..1, 1..1) == false
|
||||
end
|
||||
end
|
||||
|
||||
describe "old ranges" do
|
||||
test "inspect" do
|
||||
asc = %{__struct__: Range, first: 1, last: 3}
|
||||
desc = %{__struct__: Range, first: 3, last: 1}
|
||||
|
||||
assert inspect(asc) == "1..3"
|
||||
assert inspect(desc) == "3..1//-1"
|
||||
end
|
||||
|
||||
test "enum" do
|
||||
asc = %{__struct__: Range, first: 1, last: 3}
|
||||
desc = %{__struct__: Range, first: 3, last: 1}
|
||||
|
||||
assert Enum.to_list(asc) == [1, 2, 3]
|
||||
assert Enum.member?(asc, 2)
|
||||
assert Enum.count(asc) == 3
|
||||
assert Enum.drop(asc, 1) == [2, 3]
|
||||
assert Enum.slice([1, 2, 3, 4, 5, 6], asc) == [2, 3, 4]
|
||||
# testing private Enum.aggregate
|
||||
assert Enum.max(asc) == 3
|
||||
assert Enum.sum(asc) == 6
|
||||
assert Enum.min_max(asc) == {1, 3}
|
||||
assert Enum.reduce(asc, 0, fn a, b -> a + b end) == 6
|
||||
|
||||
assert Enum.to_list(desc) == [3, 2, 1]
|
||||
assert Enum.member?(desc, 2)
|
||||
assert Enum.count(desc) == 3
|
||||
assert Enum.drop(desc, 1) == [2, 1]
|
||||
assert Enum.slice([1, 2, 3, 4, 5, 6], desc) == []
|
||||
# testing private Enum.aggregate
|
||||
assert Enum.max(desc) == 3
|
||||
assert Enum.sum(desc) == 6
|
||||
assert Enum.min_max(desc) == {1, 3}
|
||||
assert Enum.reduce(desc, 0, fn a, b -> a + b end) == 6
|
||||
end
|
||||
|
||||
test "string" do
|
||||
asc = %{__struct__: Range, first: 1, last: 3}
|
||||
desc = %{__struct__: Range, first: 3, last: 1}
|
||||
|
||||
assert String.slice("elixir", asc) == "lix"
|
||||
assert String.slice("elixir", desc) == ""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -138,7 +138,7 @@ defmodule String.Casing do
|
||||
codes =
|
||||
special_path
|
||||
|> File.read!()
|
||||
|> String.split("\n", trim: true)
|
||||
|> String.split(["\r\n", "\n"], trim: true)
|
||||
|> Enum.reduce(codes, fn
|
||||
"", acc ->
|
||||
acc
|
||||
@@ -325,7 +325,7 @@ defmodule String.Break do
|
||||
whitespace =
|
||||
prop_path
|
||||
|> File.read!()
|
||||
|> String.split("\n")
|
||||
|> String.split(["\r\n", "\n"])
|
||||
|> Enum.reduce([], fn line, acc ->
|
||||
case :binary.split(line, ";") do
|
||||
[<<first::4-bytes, "..", last::4-bytes, _::binary>>, <<" White_Space", _::binary>>] ->
|
||||
|
||||
@@ -47,7 +47,7 @@ defmodule String.Tokenizer do
|
||||
{start, continue, patterns} =
|
||||
prop_path
|
||||
|> File.read!()
|
||||
|> String.split("\n", trim: true)
|
||||
|> String.split(["\r\n", "\n"], trim: true)
|
||||
|> Enum.reduce({start, continue, []}, fn line, acc ->
|
||||
[codepoints | category] = :binary.split(line, ";")
|
||||
|
||||
|
||||
@@ -163,6 +163,10 @@ defmodule IEx.Autocomplete do
|
||||
{:ok, List.to_atom(var)}
|
||||
end
|
||||
|
||||
defp expand_dot_path({:module_attribute, _}, _shell) do
|
||||
:error
|
||||
end
|
||||
|
||||
defp expand_dot_path({:dot, parent, call}, shell) do
|
||||
case expand_dot_path(parent, shell) do
|
||||
{:ok, %{} = map} -> Map.fetch(map, List.to_atom(call))
|
||||
|
||||
@@ -161,6 +161,7 @@ defmodule IEx.AutocompleteTest do
|
||||
assert expand('Xyz') == {:no, '', []}
|
||||
assert expand('x.Foo') == {:no, '', []}
|
||||
assert expand('x.Foo.get_by') == {:no, '', []}
|
||||
assert expand('@foo.bar') == {:no, '', []}
|
||||
end
|
||||
|
||||
test "Elixir root submodule completion" do
|
||||
@@ -221,21 +222,21 @@ defmodule IEx.AutocompleteTest do
|
||||
assert expand('map.f') == {:no, '', []}
|
||||
end
|
||||
|
||||
test "autocompletion off a bound variable only works for modules and maps" do
|
||||
test "bound variables for modules and maps" do
|
||||
eval("num = 5; map = %{nested: %{num: 23}}")
|
||||
assert expand('num.print') == {:no, '', []}
|
||||
assert expand('map.nested.num.f') == {:no, '', []}
|
||||
assert expand('map.nested.num.key.f') == {:no, '', []}
|
||||
end
|
||||
|
||||
test "autocompletion using access syntax does is not supported" do
|
||||
test "access syntax is not supported" do
|
||||
eval("map = %{nested: %{deeply: %{num: 23}}}")
|
||||
assert expand('map[:nested][:deeply].n') == {:no, '', []}
|
||||
assert expand('map[:nested].deeply.n') == {:no, '', []}
|
||||
assert expand('map.nested.[:deeply].n') == {:no, '', []}
|
||||
end
|
||||
|
||||
test "autocompletion off of unbound variables is not supported" do
|
||||
test "unbound variables is not supported" do
|
||||
eval("num = 5")
|
||||
assert expand('other_var.f') == {:no, '', []}
|
||||
assert expand('a.b.c.d') == {:no, '', []}
|
||||
|
||||
+30
-10
@@ -433,11 +433,11 @@ defmodule Mix do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Raises a Mix error that is nicely formatted, defaulting to exit code `1`.
|
||||
Raises a Mix error that is nicely formatted, defaulting to exit status `1`.
|
||||
"""
|
||||
@spec raise(binary) :: no_return
|
||||
def raise(message) do
|
||||
__MODULE__.raise(message, exit_code: 1)
|
||||
__MODULE__.raise(message, exit_status: 1)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -445,13 +445,14 @@ defmodule Mix do
|
||||
|
||||
## Options
|
||||
|
||||
* `:exit_code` - defines exit code value, defaults to `1`
|
||||
* `:exit_status` - defines exit status, defaults to `1`
|
||||
|
||||
"""
|
||||
@doc since: "1.12.0"
|
||||
@spec raise(binary, exit_code: non_neg_integer()) :: no_return
|
||||
@doc since: "1.12.3"
|
||||
@spec raise(binary, exit_status: non_neg_integer()) :: no_return
|
||||
def raise(message, opts) when is_binary(message) and is_list(opts) do
|
||||
Kernel.raise(Mix.Error, mix: Keyword.get(opts, :exit_code, 1), message: message)
|
||||
status = opts[:exit_status] || opts[:exit_code] || 1
|
||||
Kernel.raise(Mix.Error, mix: status, message: message)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -518,8 +519,17 @@ defmodule Mix do
|
||||
|
||||
deps =
|
||||
Enum.map(deps, fn
|
||||
dep when is_atom(dep) -> {dep, ">= 0.0.0"}
|
||||
dep -> dep
|
||||
dep when is_atom(dep) ->
|
||||
{dep, ">= 0.0.0"}
|
||||
|
||||
{app, opts} when is_atom(app) and is_list(opts) ->
|
||||
{app, maybe_expand_path_dep(opts)}
|
||||
|
||||
{app, requirement, opts} when is_atom(app) and is_binary(requirement) and is_list(opts) ->
|
||||
{app, requirement, maybe_expand_path_dep(opts)}
|
||||
|
||||
other ->
|
||||
other
|
||||
end)
|
||||
|
||||
force? = !!opts[:force]
|
||||
@@ -567,9 +577,10 @@ defmodule Mix do
|
||||
|
||||
:ok = Mix.Local.append_archives()
|
||||
:ok = Mix.ProjectStack.push(__MODULE__.InstallProject, config, "nofile")
|
||||
build_dir = Path.join(dir, "_build")
|
||||
|
||||
try do
|
||||
run_deps? = not File.dir?(Path.join(dir, "_build"))
|
||||
run_deps? = not File.dir?(build_dir)
|
||||
File.mkdir_p!(dir)
|
||||
|
||||
File.cd!(dir, fn ->
|
||||
@@ -577,7 +588,8 @@ defmodule Mix do
|
||||
Mix.Task.rerun("deps.get")
|
||||
end
|
||||
|
||||
Mix.Task.run("compile")
|
||||
Mix.Task.rerun("deps.loadpaths")
|
||||
Mix.Task.rerun("compile")
|
||||
end)
|
||||
|
||||
for app <- Mix.Project.deps_apps() do
|
||||
@@ -590,4 +602,12 @@ defmodule Mix do
|
||||
Mix.ProjectStack.pop()
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_expand_path_dep(opts) do
|
||||
if Keyword.has_key?(opts, :path) do
|
||||
Keyword.update!(opts, :path, &Path.expand/1)
|
||||
else
|
||||
opts
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -226,6 +226,15 @@ defmodule Mix.Compilers.Elixir do
|
||||
dest
|
||||
)
|
||||
|
||||
# Now sort the files so the ones changed more recently come first.
|
||||
# We do an optimized version of sort_by since we don't care about
|
||||
# stable sorting.
|
||||
changed =
|
||||
changed
|
||||
|> Enum.map(&{-elem(Map.fetch!(sources_stats, &1), 0), &1})
|
||||
|> Enum.sort()
|
||||
|> Enum.map(&elem(&1, 1))
|
||||
|
||||
{modules, exports, changed, sources_stats}
|
||||
end
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ defmodule Mix.Rebar do
|
||||
# Translate a Rebar dependency declaration to a Mix declaration
|
||||
# From http://www.rebar3.org/docs/dependencies#section-declaring-dependencies
|
||||
defp parse_dep(app) when is_atom(app) do
|
||||
{app, nil, override: true}
|
||||
{app, override: true}
|
||||
end
|
||||
|
||||
defp parse_dep({app, req}) when is_list(req) do
|
||||
|
||||
@@ -418,13 +418,41 @@ defmodule Mix.Release do
|
||||
:ok
|
||||
|
||||
{:error, reason} ->
|
||||
{:error,
|
||||
"Could not read configuration file. It likely has invalid configuration terms " <>
|
||||
"such as functions, references, and pids. Please make sure your configuration " <>
|
||||
"is made of numbers, atoms, strings, maps, tuples and lists. Reason: #{inspect(reason)}"}
|
||||
invalid =
|
||||
for {app, kv} <- sys_config,
|
||||
{key, value} <- kv,
|
||||
not valid_config?(value),
|
||||
do: """
|
||||
|
||||
Application: #{inspect(app)}
|
||||
Key: #{inspect(key)}
|
||||
Value: #{inspect(value)}
|
||||
"""
|
||||
|
||||
message =
|
||||
case invalid do
|
||||
[] ->
|
||||
"Could not read configuration file. Reason: #{inspect(reason)}"
|
||||
|
||||
_ ->
|
||||
"Could not read configuration file. It has invalid configuration terms " <>
|
||||
"such as functions, references, and pids. Please make sure your configuration " <>
|
||||
"is made of numbers, atoms, strings, maps, tuples and lists. The following entries " <>
|
||||
"are wrong:\n#{Enum.join(invalid)}"
|
||||
end
|
||||
|
||||
{:error, message}
|
||||
end
|
||||
end
|
||||
|
||||
defp valid_config?(n) when is_number(n), do: true
|
||||
defp valid_config?(a) when is_atom(a), do: true
|
||||
defp valid_config?(b) when is_binary(b), do: true
|
||||
defp valid_config?(l) when is_list(l), do: Enum.all?(l, &valid_config?/1)
|
||||
defp valid_config?(m) when is_map(m), do: Enum.all?(m, &valid_config?/1)
|
||||
defp valid_config?(t) when is_tuple(t), do: Enum.all?(Tuple.to_list(t), &valid_config?/1)
|
||||
defp valid_config?(_), do: false
|
||||
|
||||
defp merge_provider_config(%{config_providers: []}, sys_config, _), do: {sys_config, false}
|
||||
|
||||
defp merge_provider_config(release, sys_config, config_path) do
|
||||
|
||||
@@ -14,6 +14,7 @@ defmodule Mix.Tasks.Compile.All do
|
||||
Mix.Project.get!()
|
||||
config = Mix.Project.config()
|
||||
validate_compile_env? = "--no-validate-compile-env" not in args
|
||||
lib_path = Path.join(Mix.Project.build_path(config), "lib")
|
||||
|
||||
# Make sure Mix.Dep is cached to avoid loading dependencies
|
||||
# during compilation. It is likely this will be invoked anyway,
|
||||
@@ -21,7 +22,7 @@ defmodule Mix.Tasks.Compile.All do
|
||||
Mix.Dep.cached()
|
||||
|
||||
unless "--no-app-loading" in args do
|
||||
load_apps(config, validate_compile_env?)
|
||||
load_apps(config, lib_path, validate_compile_env?)
|
||||
end
|
||||
|
||||
result =
|
||||
@@ -39,8 +40,9 @@ defmodule Mix.Tasks.Compile.All do
|
||||
end)
|
||||
end
|
||||
|
||||
app = config[:app]
|
||||
_ = Code.prepend_path(Mix.Project.compile_path())
|
||||
load_app(config[:app], validate_compile_env?)
|
||||
load_app(app, lib_path, validate_compile_env?)
|
||||
result
|
||||
end
|
||||
|
||||
@@ -93,44 +95,63 @@ defmodule Mix.Tasks.Compile.All do
|
||||
|
||||
## App loading helpers
|
||||
|
||||
defp load_apps(config, validate_compile_env?) do
|
||||
defp load_apps(config, lib_path, validate_compile_env?) do
|
||||
{runtime, optional} = Mix.Tasks.Compile.App.project_apps(config)
|
||||
parent = self()
|
||||
opts = [ordered: false, timeout: :infinity]
|
||||
deps = for dep <- Mix.Dep.cached(), into: %{}, do: {dep.app, lib_path}
|
||||
|
||||
%{}
|
||||
|> load_apps(runtime, validate_compile_env?)
|
||||
|> load_apps(optional, validate_compile_env?)
|
||||
stream_apps(runtime ++ optional, deps)
|
||||
|> Task.async_stream(&load_stream_app(&1, parent, validate_compile_env?), opts)
|
||||
|> Stream.run()
|
||||
end
|
||||
|
||||
defp load_stream_app({app, lib_path}, parent, validate_compile_env?) do
|
||||
children =
|
||||
case load_app(app, lib_path, validate_compile_env?) do
|
||||
:ok ->
|
||||
Application.spec(app, :applications) ++ Application.spec(app, :included_applications)
|
||||
|
||||
:error ->
|
||||
[]
|
||||
end
|
||||
|
||||
send(parent, {:done, app, children})
|
||||
:ok
|
||||
end
|
||||
|
||||
defp load_apps(seen, apps, validate_compile_env?) do
|
||||
Enum.reduce(apps, seen, fn app, seen ->
|
||||
if Map.has_key?(seen, app) do
|
||||
seen
|
||||
else
|
||||
seen = Map.put(seen, app, true)
|
||||
|
||||
case load_app(app, validate_compile_env?) do
|
||||
:ok ->
|
||||
seen
|
||||
|> load_apps(Application.spec(app, :applications), validate_compile_env?)
|
||||
|> load_apps(Application.spec(app, :included_applications), validate_compile_env?)
|
||||
|
||||
:error ->
|
||||
seen
|
||||
end
|
||||
end
|
||||
end)
|
||||
defp stream_apps(initial, deps) do
|
||||
Stream.unfold({initial, %{}, %{}, deps}, &stream_app/1)
|
||||
end
|
||||
|
||||
defp load_app(app, validate_compile_env?) do
|
||||
# We already processed this app, skip it.
|
||||
defp stream_app({[app | apps], seen, done, deps}) when is_map_key(seen, app) do
|
||||
stream_app({apps, seen, done, deps})
|
||||
end
|
||||
|
||||
# We haven't processed this app, emit it.
|
||||
defp stream_app({[app | apps], seen, done, deps}) do
|
||||
{{app, deps[app]}, {apps, Map.put(seen, app, true), done, deps}}
|
||||
end
|
||||
|
||||
# We have processed all apps and all seen have been done.
|
||||
defp stream_app({[], seen, done, _deps}) when map_size(seen) == map_size(done) do
|
||||
nil
|
||||
end
|
||||
|
||||
# We have processed all apps but there is work being done.
|
||||
defp stream_app({[], seen, done, deps}) do
|
||||
receive do
|
||||
{:done, app, children} -> stream_app({children, seen, Map.put(done, app, true), deps})
|
||||
end
|
||||
end
|
||||
|
||||
defp load_app(app, lib_path, validate_compile_env?) do
|
||||
if Application.spec(app, :vsn) do
|
||||
:ok
|
||||
else
|
||||
name = Atom.to_charlist(app) ++ '.app'
|
||||
|
||||
with [_ | _] = path <- :code.where_is_file(name),
|
||||
{:ok, {:application, _, properties} = application_data} <- consult_app_file(path),
|
||||
with {:ok, bin} <- read_app(app, lib_path),
|
||||
{:ok, {:application, _, properties} = application_data} <- consult_app_file(bin),
|
||||
:ok <- :application.load(application_data) do
|
||||
if compile_env = validate_compile_env? && properties[:compile_env] do
|
||||
Config.Provider.validate_compile_env(compile_env, false)
|
||||
@@ -143,10 +164,22 @@ defmodule Mix.Tasks.Compile.All do
|
||||
end
|
||||
end
|
||||
|
||||
defp consult_app_file(path) do
|
||||
# The app didn't come from a dep, go through the slow path (code/erl_prim_loader)
|
||||
defp read_app(app, nil) do
|
||||
name = Atom.to_charlist(app) ++ '.app'
|
||||
|
||||
with [_ | _] = path <- :code.where_is_file(name),
|
||||
{:ok, bin, _full_name} <- :erl_prim_loader.get_file(path),
|
||||
do: {:ok, bin}
|
||||
end
|
||||
|
||||
defp read_app(app, lib_path) do
|
||||
File.read("#{lib_path}/#{app}/ebin/#{app}.app")
|
||||
end
|
||||
|
||||
defp consult_app_file(bin) do
|
||||
# The path could be located in an .ez archive, so we use the prim loader.
|
||||
with {:ok, bin, _full_name} <- :erl_prim_loader.get_file(path),
|
||||
{:ok, tokens, _} <- :erl_scan.string(String.to_charlist(bin)) do
|
||||
with {:ok, tokens, _} <- :erl_scan.string(String.to_charlist(bin)) do
|
||||
:erl_parse.parse_term(tokens)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -52,7 +52,7 @@ defmodule Mix.Tasks.Compile.Elixir do
|
||||
* `--debug-info` (`--no-debug-info`) - attaches (or not) debug info to compiled modules
|
||||
* `--ignore-module-conflict` - does not emit warnings if a module was previously defined
|
||||
* `--warnings-as-errors` - treats warnings in the current project as errors and
|
||||
return a non-zero exit code
|
||||
return a non-zero exit status
|
||||
* `--long-compilation-threshold N` - sets the "long compilation" threshold
|
||||
(in seconds) to `N` (see the docs for `Kernel.ParallelCompiler.compile/2`)
|
||||
* `--profile` - if set to `time`, outputs timing information of compilation steps
|
||||
|
||||
@@ -206,20 +206,6 @@ defmodule Mix.Tasks.Deps.Compile do
|
||||
|
||||
File.mkdir_p!(dep_path)
|
||||
File.write!(config_path, rebar_config(dep))
|
||||
|
||||
# We need to copy/symlink include/priv in case it is
|
||||
# needed for compiling the dependency itself. Later on
|
||||
# we will build the whole structure and also symlink/copy
|
||||
# ebin.
|
||||
#
|
||||
# Note both configurations are necessary when using the
|
||||
# bare compiler with compiler output dir, otherwise includes
|
||||
# and priv files can't be found. For example, without those
|
||||
# options, we were unable to compile certifi, which uses
|
||||
# both parse transforms and reads from priv at compilation.
|
||||
Mix.Utils.symlink_or_copy(Path.join(opts[:dest], "include"), Path.join(dep_path, "include"))
|
||||
Mix.Utils.symlink_or_copy(Path.join(opts[:dest], "priv"), Path.join(dep_path, "priv"))
|
||||
|
||||
do_command(dep, config, cmd, false, env)
|
||||
end
|
||||
|
||||
|
||||
@@ -116,8 +116,7 @@ defmodule Mix.Tasks.Test do
|
||||
* `--export-coverage` - the name of the file to export coverage results to.
|
||||
Only has an effect when used with `--cover`
|
||||
|
||||
* `--failed` - runs only tests that failed the last time they ran.
|
||||
If there are no pending --failed tests, `mix test` will run all available tests
|
||||
* `--failed` - runs only tests that failed the last time they ran
|
||||
|
||||
* `--force` - forces compilation regardless of modification times
|
||||
|
||||
@@ -175,7 +174,7 @@ defmodule Mix.Tasks.Test do
|
||||
Note that in trace mode test timeouts will be ignored as timeout is set to `:infinity`
|
||||
|
||||
* `--warnings-as-errors` - (since v1.12.0) treats warnings as errors and returns a non-zero
|
||||
exit code. This option only applies to test files. To treat warnings as errors during
|
||||
exit status. This option only applies to test files. To treat warnings as errors during
|
||||
compilation and during tests, run:
|
||||
MIX_ENV=test mix do compile --warnings-as-errors, test --warnings-as-errors
|
||||
|
||||
@@ -702,13 +701,7 @@ defmodule Mix.Tasks.Test do
|
||||
end
|
||||
|
||||
{allowed_files, failed_ids} = ExUnit.Filters.failure_info(manifest_file)
|
||||
|
||||
if MapSet.size(failed_ids) == 0 do
|
||||
Mix.shell().info("No pending --failed tests, re-running all available tests...")
|
||||
{opts, nil}
|
||||
else
|
||||
{Keyword.put(opts, :only_test_ids, failed_ids), allowed_files}
|
||||
end
|
||||
{Keyword.put(opts, :only_test_ids, failed_ids), allowed_files}
|
||||
else
|
||||
{opts, nil}
|
||||
end
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
defmodule Mix.Utils do
|
||||
@moduledoc false
|
||||
|
||||
# For bootstrapping purposes
|
||||
@compile {:no_warn_undefined, Logger}
|
||||
|
||||
@doc """
|
||||
Gets the Mix home.
|
||||
|
||||
@@ -638,17 +641,24 @@ defmodule Mix.Utils do
|
||||
# If a proxy environment variable was supplied add a proxy to httpc.
|
||||
http_options = [relaxed: true] ++ proxy_config(path)
|
||||
|
||||
case httpc_request(request, http_options) do
|
||||
{:error, {:failed_connect, [{:to_address, _}, {inet, _, reason}]}}
|
||||
when inet in [:inet, :inet6] and reason in [:ehostunreach, :enetunreach] ->
|
||||
:httpc.set_options([ipfamily: fallback(inet)], :mix)
|
||||
request |> httpc_request(http_options) |> httpc_response()
|
||||
# Silence the warning from OTP as we verify the contents
|
||||
level = Logger.level()
|
||||
Logger.configure(level: :error)
|
||||
|
||||
response ->
|
||||
httpc_response(response)
|
||||
try do
|
||||
case httpc_request(request, http_options) do
|
||||
{:error, {:failed_connect, [{:to_address, _}, {inet, _, reason}]}}
|
||||
when inet in [:inet, :inet6] and reason in [:ehostunreach, :enetunreach] ->
|
||||
:httpc.set_options([ipfamily: fallback(inet)], :mix)
|
||||
request |> httpc_request(http_options) |> httpc_response()
|
||||
|
||||
response ->
|
||||
httpc_response(response)
|
||||
end
|
||||
after
|
||||
Logger.configure(level: level)
|
||||
:inets.stop(:httpc, :mix)
|
||||
end
|
||||
after
|
||||
:inets.stop(:httpc, :mix)
|
||||
end
|
||||
|
||||
defp fallback(:inet), do: :inet6
|
||||
|
||||
@@ -20,7 +20,7 @@ defmodule Mix.CLITest do
|
||||
end
|
||||
|
||||
@tag :unix
|
||||
test "Mix.raise/2 can set exit code", %{tmp_dir: tmp_dir} do
|
||||
test "Mix.raise/2 can set exit status", %{tmp_dir: tmp_dir} do
|
||||
File.cd!(tmp_dir, fn ->
|
||||
File.mkdir_p!("lib")
|
||||
|
||||
@@ -34,7 +34,7 @@ defmodule Mix.CLITest do
|
||||
|
||||
defp aliases do
|
||||
[
|
||||
custom: &error(&1, exit_code: 99),
|
||||
custom: &error(&1, exit_status: 99),
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -112,6 +112,8 @@ defmodule Mix.RebarTest do
|
||||
|
||||
assert parse_dep({:git_rebar, '', {:git, @git_rebar_charlist, {:ref, '64691eb'}}}) ==
|
||||
{:git_rebar, ~r"", override: true, git: @git_rebar_string, ref: "64691eb"}
|
||||
|
||||
assert parse_dep(:git_rebar) == {:git_rebar, override: true}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -584,7 +584,7 @@ defmodule Mix.ReleaseTest do
|
||||
|
||||
test "errors on bad config" do
|
||||
assert {:error, "Could not read configuration file." <> _} =
|
||||
make_sys_config(release([]), [foo: self()], "unused/runtime/path")
|
||||
make_sys_config(release([]), [foo: [bar: self()]], "unused/runtime/path")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -214,13 +214,8 @@ defmodule Mix.Tasks.TestTest do
|
||||
System.put_env("PASS_FAILING_TESTS", "true")
|
||||
assert mix(["test", "--failed"]) =~ "2 tests, 0 failures"
|
||||
|
||||
# All tests should be run if we try it again with no failing tests.
|
||||
# This prevents `mix test --failed` from passing in cases where
|
||||
# `mix test` had a compilation error before having failing tests.
|
||||
# It also provides a better workflow as you can always run with --failed.
|
||||
output = mix(["test", "--failed"])
|
||||
assert output =~ "No pending --failed tests, re-running all available tests..."
|
||||
assert output =~ "4 tests, 0 failures"
|
||||
# Nothing should get run if we try it again since everything is passing.
|
||||
assert mix(["test", "--failed"]) =~ "There are no tests to run"
|
||||
|
||||
# `--failed` and `--stale` cannot be combined
|
||||
output = mix(["test", "--failed", "--stale"])
|
||||
|
||||
@@ -85,6 +85,20 @@ defmodule MixTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "install after errors", %{tmp_dir: tmp_dir} do
|
||||
assert_raise Mix.Error, "Can't continue due to errors on dependencies", fn ->
|
||||
Mix.install([
|
||||
{:bad, path: Path.join(tmp_dir, "bad")}
|
||||
])
|
||||
end
|
||||
|
||||
Mix.install([
|
||||
{:install_test, path: Path.join(tmp_dir, "install_test")}
|
||||
])
|
||||
|
||||
assert apply(InstallTest, :hello, []) == :world
|
||||
end
|
||||
|
||||
test "consolidate_protocols: false", %{tmp_dir: tmp_dir} do
|
||||
Mix.install(
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user