Compare commits

...
49 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
José Valim b63f8f541e Release v1.13.3 2022-02-09 16:26:17 +01:00
José Valim babbae7e47 Handle more unicode sequences in split_at, closes #11617 2022-02-09 15:53:39 +01:00
José Valim 0f0d4c0f6e Track all stale modules from config/lock as exports too 2022-02-04 16:50:37 +01:00
Steve Hall 5ee492af55 Fix coverage threshold ignored for Total line in Summary (#11571)
An incorrect variable is being passed to the `display/2` function on line 353 for the Total line in the coverage summary report. The result is that that Total line will always display in red no matter what the threshold is set to.
2022-02-03 13:22:32 +01:00
José Valim 25e480f3d0 Do not break signatures over multiple lines 2022-02-01 10:04:30 +01:00
José Valim 2dc6773d6c Fix recursion on guards with map fields, closes #11602 2022-01-31 14:58:39 +01:00
José Valim ff103b19b9 Fix indentation of options in mix format 2022-01-24 16:48:28 +01:00
Matt McCoy d99a16256b Supply file and line to formatter plugins (#11591)
Allow formatter plugins to print errors with accurate file and
line information. The line is only provided when a sigil is being
formatted.
2022-01-24 08:37:17 +01:00
José Valim 1601b309b8 Check for fun arity in formatter 2022-01-24 08:37:17 +01:00
Jonatan Männchen 421a77d508 Embedded Elixir Expressions in Formatter Plugins (#11587)
Support Sigils inside embedded expressions in files formatted
by a formatter plugin.
2022-01-22 09:36:02 +01:00
felipe stival cc9e9b29a7 Fix duplicate bindings causing weird behaviour (#11584)
Before this fix, evaluating `b = a` with assignments of: `a: 1, a: 2, c:
3` would eval AST equivalent to `^c = a`. This happened due to binding
normalization generating version numbers larger than the total number of
bindings. Later in the pipeline, the number of bindings is used to
compute the next version number, which would then conflict with an
existing binding.

This commit fixes that by not increasing the version number when
a repeated binding is normalized.
2022-01-19 19:38:23 +01:00
Eksperimental 67f7c6ba5b Fix bug with edge-case when converting to algebra quoted Elixir alias (#11572)
The following code would crash:

    iex> Code.quoted_to_algebra(Elixir)
    ** (MatchError) no match of right hand side value: "Elixir"
        (elixir 1.13.1) lib/code/normalizer.ex:254: Code.Normalizer.normalize_literal/3
        (elixir 1.13.1) lib/code.ex:1107: Code.quoted_to_algebra/2

Therefore, this as well:

    iex> Macro.to_string(Elixir)
    ** (MatchError) no match of right hand side value: "Elixir"
        (elixir 1.13.1) lib/code/normalizer.ex:254: Code.Normalizer.normalize_literal/3
        (elixir 1.13.1) lib/code.ex:1107: Code.quoted_to_algebra/2
        (elixir 1.13.1) lib/macro.ex:948: Macro.to_string/1
2022-01-16 09:28:25 +01:00
José Valim c99a17b8cd Optimize Map.new/2 2022-01-13 15:28:01 +01:00
José Valim 9ad182986c Release v1.13.2 2022-01-13 10:51:17 +01:00
José Valim 16292c65ba Update CHANGELOG 2022-01-12 14:43:11 +01:00
José Valim d25ce48d5d Bring back filter/reject on Map and Keyword
They are already being used in projects, which
means it is too late for a deprecation. The docs
instead discuss when to use them.
2022-01-12 14:29:13 +01:00
Aaron Gunderson 66b4ab19ae Mix.Tasks.Test.Coverage warns on failure (#11457)
Adds an explicit warning when exiting with an error code because of a
failed test coverage threshold.

Example:
```
-----------|--------------------------
    62.35% | Total

Coverage test failed, threshold not met:
        Coverage:   62.35%
        Threshold: 100.00%
```
2022-01-02 19:58:06 +01:00
Aleksei Matiushkin 01a4889e39 Fix the nested uniq: acc name clash by preserving a scope (#11532) 2022-01-01 09:56:42 +01:00
José Valim d5e7446399 Add missing Logger CHANGELOG entry 2021-12-31 11:12:22 +01:00
Thiago Santos ab1da111bf Warn on zero arity callbacks inside protocols (#11519) 2021-12-22 22:01:33 +01:00
José Valim 6549b00eba Ensure context modules are handled in optimized defmodule 2021-12-22 12:22:22 +01:00
José Valim 4431ac63e2 Do not use type exclusive to Erlang/OTP 24+ 2021-12-22 10:18:02 +01:00
Thales Macedo Garitezi 280215c24f Allow bypassing application mode validation in release spec (#11506)
Today, there is a mode validation check when doing a Mix Release that
prevents a parent application that has an application mode of
`:permanent`, for example, while a child application has mode `:load`,
as it might be unsafe.

However, some complex applications may need more control over the
application load/start order.  For such cases, the user would need a
way to tell Mix.Release to don't be strict while constructing the
`.rel` file.

To allow for better control over the mode validation check instead of
simply disabling the check completely, we introduce the
`:skip_mode_validation_for` to allow users to specify a list of
applications for which the strict application mode validations should
not be enforced.
2021-12-20 12:48:11 +01:00
Marc-André Lafortune 071285403d Check plugins first so they can format .ex and .exs files (#11507) 2021-12-20 08:51:59 +01:00
José Valim 3d3453c1e2 Do not emit warnings on Cursor.Fragment.container_cursor_to_quoted/2 2021-12-16 16:44:10 +01:00
José Valim 2dc3fd26b8 Update CHANGELOG 2021-12-16 15:39:52 +01:00
José Valim e61ed47066 Deprecate map/filter/reject in Map and Keyword 2021-12-16 15:33:41 +01:00
70 changed files with 879 additions and 291 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
+71
View File
@@ -128,6 +128,76 @@ 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
#### Mix
* [mix format] Supply file and line to formatter plugins
* [mix format] Support embedded Elixir expressions inside formatter plugins
### 2. Bug fixes
#### Elixir
* [Code] Fix duplicate bindings causing errors during evaluation
* [Kernel] Make sure signatures stored in the Documentation chunk does not contain newlines
* [Kernel] Fix infinite loop when compiling guards with recursive map access
* [Macro] Fix error on `Macro.to_string/1` when the plain alias `Elixir` is given
* [String] Fix error for certain codepoint combinations in `String.split_at/2`
#### Mix
* [mix compile] Recompile project files when exports from dependencies change
* [mix test] Fix total coverage always showing in red even when above the threshold
## v1.13.2 (2022-01-13)
### 1. Enhancements
#### Mix
* [mix format] Allow plugins to also format `.ex` and `.exs` files
* [mix release] Allow bypassing application mode validation in release spec
* [mix test] Print a message when the suite fails due to the coverage threshold
### 2. Bug fixes
#### Elixir
* [Code] Do not emit warnings on `Code.Fragment.container_cursor_to_quoted/2`
* [Kernel] Fix a crash when a for-comprehension with `:uniq` was used inside another comprehension with `:uniq`
* [Kernel] Ensure `env.context_modules` is properly set inside optimized `defmodule`
* [Keyword] Deprecate the recently added `Keyword.map/2` as it is equivalent to `Keyword.new/2`
* [Map] Deprecate the recently added `Map.map/2` as it is equivalent to `Map.new/2`
* [Protocol] Warn on zero arity callbacks inside protocols
## v1.13.1 (2021-12-14)
### 1. Bug fixes
@@ -210,6 +280,7 @@ Finally, the `Code` module has also been augmented with two functions: `Code.str
#### Logger
* [Logger] Add `Logger.put_application_level/2`
* [Logger] Print all log levels in accordance to Erlang/OTP. This also means `[warn]` is now shown as `[warning]`
#### Mix
+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.1
1.13.4
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/sh
set -e
ELIXIR_VERSION=1.13.1
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.1
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
+15 -5
View File
@@ -177,6 +177,7 @@ defmodule Code.Formatter do
defp state(comments, opts) do
force_do_end_blocks = Keyword.get(opts, :force_do_end_blocks, false)
locals_without_parens = Keyword.get(opts, :locals_without_parens, [])
file = Keyword.get(opts, :file, nil)
sigils = Keyword.get(opts, :sigils, [])
sigils =
@@ -199,7 +200,8 @@ defmodule Code.Formatter do
operand_nesting: 2,
skip_eol: false,
comments: comments,
sigils: sigils
sigils: sigils,
file: file
}
end
@@ -1320,13 +1322,21 @@ defmodule Code.Formatter do
entries =
case state.sigils do
%{^name => callback} ->
case callback.(hd(entries), sigil: List.to_atom([name]), modifiers: modifiers) do
binary when is_binary(binary) ->
[binary]
metadata = [
file: state.file,
line: meta[:line],
sigil: List.to_atom([name]),
modifiers: modifiers,
opening_delimiter: opening_delimiter
]
case callback.(hd(entries), metadata) do
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
%{} ->
+1 -1
View File
@@ -827,7 +827,7 @@ defmodule Code.Fragment do
case :elixir_tokenizer.tokenize(fragment, line, column, tokenizer_opts) do
{:ok, _, _, _warnings, tokens} ->
:elixir.tokens_to_quoted(tokens, file, columns: columns, token_metadata: token_metadata)
:elixir.tokens_to_quoted(tokens, nil, columns: columns, token_metadata: token_metadata)
{:error, {line, column, {prefix, suffix}, token}, _rest, _warnings, _so_far} ->
location = [line: line, column: column]
+9 -5
View File
@@ -251,12 +251,16 @@ defmodule Code.Normalizer do
if is_atom(literal) and Code.Identifier.classify(literal) == :alias and
is_nil(meta[:delimiter]) do
"Elixir." <> segments = Atom.to_string(literal)
segments =
segments
|> String.split(".")
|> Enum.map(&String.to_atom/1)
case Atom.to_string(literal) do
"Elixir" ->
[:"Elixir"]
"Elixir." <> segments ->
segments
|> String.split(".")
|> Enum.map(&String.to_atom/1)
end
{:__aliases__, meta, segments}
else
+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])
+4 -22
View File
@@ -1374,27 +1374,9 @@ defmodule Keyword do
end
end
@doc """
Maps the function `fun` over all key-value pairs in `keywords`,
returning a keyword list with all the values replaced with
the result of the function.
## Examples
iex> Keyword.map([one: 1, two: 2, three: 3], fn {_key, val} -> to_string(val) end)
[one: "1", two: "2", three: "3"]
"""
@doc since: "1.13.0"
@spec map(t, ({key, value} -> value)) :: t
def map(keywords, fun) when is_list(keywords) and is_function(fun, 1) do
do_map(keywords, fun)
end
defp do_map([], _fun), do: []
defp do_map([{key, value} | rest], fun) do
new_value = fun.({key, value})
[{key, new_value} | do_map(rest, fun)]
@doc false
@deprecated "Use Keyword.new/2 instead"
def map(keywords, fun) when is_list(keywords) do
Enum.map(keywords, fn {k, v} -> {k, fun.({k, v})} end)
end
end
+24 -33
View File
@@ -214,7 +214,24 @@ defmodule Map do
"""
@spec new(Enumerable.t(), (term -> {key, value})) :: map
def new(enumerable, transform) when is_function(transform, 1) do
def new(enumerable, transform)
def new(%_{} = enumerable, transform), do: new_from_enum(enumerable, transform)
def new(%{} = map, transform), do: new_from_map(map, transform)
def new(enumerable, transform), do: new_from_enum(enumerable, transform)
defp new_from_map(map, transform) when is_function(transform, 1) do
iter = :maps.iterator(map)
next = :maps.next(iter)
:maps.from_list(do_map(next, transform))
end
defp do_map(:none, _fun), do: []
defp do_map({key, value, iter}, transform) do
[transform.({key, value}) | do_map(:maps.next(iter), transform)]
end
defp new_from_enum(enumerable, transform) when is_function(transform, 1) do
enumerable
|> Enum.map(transform)
|> :maps.from_list()
@@ -982,8 +999,8 @@ defmodule Map do
See also `reject/2` which discards all elements where the
function returns a truthy value.
> Note: if you find yourself doing multiple calls to `Map.map/2`
> and `Map.filter/2` in a pipeline, it is likely more efficient
> Note: if you find yourself doing multiple calls to `Map.filter/2`
> and `Map.reject/2` in a pipeline, it is likely more efficient
> to use `Enum.map/2` and `Enum.filter/2` instead and convert to
> a map at the end using `Map.new/1`.
@@ -1041,35 +1058,9 @@ defmodule Map do
end
end
@doc """
Maps the function `fun` over all key-value pairs in `map`
It returns a map with all the values replaced with the result
of the function.
> Note: if you find yourself doing multiple calls to `Map.map/2`
> and `Map.filter/2` in a pipeline, it is likely more efficient
> to use `Enum.map/2` and `Enum.filter/2` instead and convert to
> a map at the end using `Map.new/1`.
## Examples
iex> Map.map(%{1 => "joe", 2 => "mike", 3 => "robert"}, fn {_key, val} -> String.capitalize(val) end)
%{1 => "Joe", 2 => "Mike", 3 => "Robert"}
"""
@doc since: "1.13.0"
@spec map(map, ({key, value} -> value)) :: map
def map(map, fun) when is_map(map) and is_function(fun, 1) do
iter = :maps.iterator(map)
next = :maps.next(iter)
:maps.from_list(do_map(next, fun))
end
defp do_map(:none, _fun), do: []
defp do_map({key, value, iter}, fun) do
new_value = fun.({key, value})
[{key, new_value} | do_map(:maps.next(iter), fun)]
@doc false
@deprecated "Use Map.new/2 instead"
def map(map, fun) when is_map(map) do
new(map, fn {k, v} -> {k, fun.({k, v})} end)
end
end
+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))
+11 -7
View File
@@ -403,10 +403,8 @@ defmodule Module.Types.Pattern do
# bar, {:ok, baz}
# bar, {:ok, bat}
expanded_args =
args
|> Enum.map(&flatten_union(&1, context))
|> cartesian_product()
flatten_args = Enum.map(args, &flatten_union(&1, context))
cartesian_args = cartesian_product(flatten_args)
# Remove clauses that do not match the expected type
# Ignore type variables in parameters by changing them to dynamic
@@ -425,11 +423,11 @@ defmodule Module.Types.Pattern do
# the type contexts from unifying argument and parameter to
# infer type variables in arguments
result =
flat_map_ok(expanded_args, fn expanded_args ->
flat_map_ok(cartesian_args, fn cartesian_args ->
result =
Enum.flat_map(clauses, fn {params, return} ->
result =
map_ok(Enum.zip(expanded_args, params), fn {arg, param} ->
map_ok(Enum.zip(cartesian_args, params), fn {arg, param} ->
case unify(arg, param, stack, context) do
{:ok, _type, context} -> {:ok, context}
{:error, reason} -> {:error, reason}
@@ -453,7 +451,13 @@ defmodule Module.Types.Pattern do
{:ok, returns_contexts} ->
{success_returns, contexts} = Enum.unzip(returns_contexts)
contexts = Enum.concat(contexts)
indexes = Enum.uniq(Enum.flat_map(args, &collect_var_indexes_from_type/1))
indexes =
for types <- flatten_args,
type <- types,
index <- collect_var_indexes_from_type(type),
do: index,
uniq: true
# Build unions from collected type contexts to unify with
# type variables from arguments
+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 ->
+5 -1
View File
@@ -383,7 +383,7 @@ defmodule Process do
@type spawn_opt ::
:link
| :monitor
| {:monitor, :erlang.monitor_option()}
| {:monitor, monitor_option()}
| {:priority, :low | :normal | :high}
| {:fullsweep_after, non_neg_integer}
| {:min_heap_size, non_neg_integer}
@@ -392,6 +392,10 @@ defmodule Process do
| {:message_queue_data, :off_heap | :on_heap}
@type spawn_opts :: [spawn_opt]
# TODO: Use :erlang.monitor_option() on Erlang/OTP 24+
@typep monitor_option ::
[alias: :explicit_unalias | :demonitor | :reply_demonitor, tag: term()]
@doc """
Spawns the given function according to the given options.
+2 -2
View File
@@ -723,14 +723,14 @@ defmodule Protocol do
defp callback_ast_to_fa({kind, {:"::", meta, [{name, _, args}, _return]}, _pos})
when kind in [:callback, :macrocallback] do
[{{name, length(args)}, meta}]
[{{name, length(List.wrap(args))}, meta}]
end
defp callback_ast_to_fa(
{kind, {:when, _, [{:"::", meta, [{name, _, args}, _return]}, _vars]}, _pos}
)
when kind in [:callback, :macrocallback] do
[{{name, length(args)}, meta}]
[{{name, length(List.wrap(args))}, meta}]
end
defp callback_ast_to_fa({kind, _, _pos}) when kind in [:callback, :macrocallback] do
+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)
+8 -4
View File
@@ -2723,12 +2723,16 @@ defmodule String do
graphemes_and_length: 1,
reverse_characters_to_binary: 1}
defp byte_size_remaining_at(binary, 0) do
byte_size(binary)
defp byte_size_unicode(binary) when is_binary(binary), do: byte_size(binary)
defp byte_size_unicode([head]), do: byte_size_unicode(head)
defp byte_size_unicode([head | tail]), do: byte_size_unicode(head) + byte_size_unicode(tail)
defp byte_size_remaining_at(unicode, 0) do
byte_size_unicode(unicode)
end
defp byte_size_remaining_at(binary, n) do
case :unicode_util.gc(binary) do
defp byte_size_remaining_at(unicode, n) do
case :unicode_util.gc(unicode) do
[_] -> 0
[_ | rest] -> byte_size_remaining_at(rest, n - 1)
[] -> 0
+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"])
+19 -12
View File
@@ -254,7 +254,7 @@ eval_quoted(Tree, Binding, #{line := Line} = E) ->
eval_forms(Tree, Binding, Opts) when is_list(Opts) ->
eval_forms(Tree, Binding, env_for_eval(Opts));
eval_forms(Tree, RawBinding, OrigE) ->
{Vars, Binding} = normalize_binding(RawBinding, [], [], 0),
{Vars, Binding} = normalize_binding(RawBinding, #{}, [], 0),
E = elixir_env:with_vars(OrigE, Vars),
{_, S} = elixir_env:env_to_erl(E),
{Erl, NewErlS, NewExS, NewE} = quoted_to_erl(Tree, E, S),
@@ -275,12 +275,19 @@ eval_forms(Tree, RawBinding, OrigE) ->
{Value, elixir_erl_var:dump_binding(NewBinding, NewExS, NewErlS), NewE}
end.
normalize_binding([{Key, Value} | Binding], Vars, Acc, I) when is_atom(Key) ->
normalize_binding(Binding, [{{Key, nil}, I} | Vars], [{{Key, nil}, Value} | Acc], I + 1);
normalize_binding([{Pair, Value} | Binding], Vars, Acc, I) ->
normalize_binding(Binding, [{Pair, I} | Vars], [{Pair, Value} | Acc], I + 1);
normalize_binding([], Vars, Acc, _I) ->
{maps:from_list(Vars), Acc}.
normalize_binding([Binding | NextBindings], VarsMap, Normalized, Counter) ->
{Pair, Value} = normalize_pair(Binding),
case VarsMap of
#{Pair := _} ->
normalize_binding(NextBindings, VarsMap, [{Pair, Value} | Normalized], Counter);
#{} ->
normalize_binding(NextBindings, VarsMap#{Pair => Counter}, [{Pair, Value} | Normalized], Counter + 1)
end;
normalize_binding([], VarsMap, Normalized, _Counter) ->
{VarsMap, Normalized}.
normalize_pair({Key, Value}) when is_atom(Key) -> {{Key, nil}, Value};
normalize_pair({Pair, Value}) -> {Pair, Value}.
recur_eval([Expr | Exprs], Binding, Env) ->
{value, Value, NewBinding} =
@@ -359,8 +366,8 @@ string_to_tokens(String, StartLine, StartColumn, File, Opts) when is_integer(Sta
{error, {Location, to_binary(Error), to_binary(Token)}}
end.
tokens_to_quoted(Tokens, File, Opts) ->
handle_parsing_opts(File, Opts),
tokens_to_quoted(Tokens, WarningFile, Opts) ->
handle_parsing_opts(WarningFile, Opts),
try elixir_parser:parse(Tokens) of
{ok, Forms} ->
@@ -370,7 +377,7 @@ tokens_to_quoted(Tokens, File, Opts) ->
{error, {Line, _, [Error, Token]}} ->
{error, {parser_location(Line), to_binary(Error), to_binary(Token)}}
after
erase(elixir_parser_file),
erase(elixir_parser_warning_file),
erase(elixir_parser_columns),
erase(elixir_token_metadata),
erase(elixir_literal_encoder)
@@ -406,7 +413,7 @@ parser_location(Meta) ->
to_binary(List) when is_list(List) -> elixir_utils:characters_to_binary(List);
to_binary(Atom) when is_atom(Atom) -> atom_to_binary(Atom, utf8).
handle_parsing_opts(File, Opts) ->
handle_parsing_opts(WarningFile, Opts) ->
LiteralEncoder =
case lists:keyfind(literal_encoder, 1, Opts) of
{literal_encoder, Fun} -> Fun;
@@ -414,7 +421,7 @@ handle_parsing_opts(File, Opts) ->
end,
TokenMetadata = lists:keyfind(token_metadata, 1, Opts) == {token_metadata, true},
Columns = lists:keyfind(columns, 1, Opts) == {columns, true},
put(elixir_parser_file, File),
put(elixir_parser_warning_file, WarningFile),
put(elixir_parser_columns, Columns),
put(elixir_token_metadata, TokenMetadata),
put(elixir_literal_encoder, LiteralEncoder).
+2 -1
View File
@@ -133,7 +133,8 @@ fast_compile({defmodule, Meta, [Mod, [{do, TailBlock}]]}, NoLineE) ->
'Elixir.Macro':expand(Mod, E)
end,
elixir_module:compile(Expanded, Block, [], E).
ContextModules = [Expanded | ?key(E, context_modules)],
elixir_module:compile(Expanded, Block, [], E#{context_modules := ContextModules}).
%% Bootstrapper
+3 -1
View File
@@ -552,7 +552,9 @@ signature_to_binary(Module, '__struct__', []) ->
<<"%", ('Elixir.Kernel':inspect(Module))/binary, "{}">>;
signature_to_binary(_, Name, Signature) ->
'Elixir.Macro':to_string({Name, [{closing, []}], Signature}).
Quoted = {Name, [{closing, []}], Signature},
Doc = 'Elixir.Inspect.Algebra':format('Elixir.Code':quoted_to_algebra(Quoted), infinity),
'Elixir.IO':iodata_to_binary(Doc).
checker_chunk(#{definitions := Definitions, deprecated := Deprecated, is_behaviour := IsBehaviour}) ->
DeprecatedMap = maps:from_list(Deprecated),
+10 -10
View File
@@ -21,7 +21,7 @@ translate_reduce(Meta, Cases, Expr, Reduce, S) ->
({'case', CaseAnn, _, CaseBlock}, InnerAcc) -> {'case', CaseAnn, InnerAcc, CaseBlock}
end,
{build_reduce(Ann, TCases, InnerFun, TExpr, TReduce, false, SE), SE}.
build_reduce(Ann, TCases, InnerFun, TExpr, TReduce, false, SE).
translate_into(Meta, Cases, Expr, Opts, Return, S) ->
Ann = ?ann(Meta),
@@ -115,11 +115,11 @@ build_inline(Ann, Clauses, Expr, Into, Uniq, S) ->
build_inline_each(Ann, Clauses, Expr, false, Uniq, S) ->
InnerFun = fun(InnerExpr, _InnerAcc) -> InnerExpr end,
{build_reduce(Ann, Clauses, InnerFun, Expr, {nil, Ann}, Uniq, S), S};
build_reduce(Ann, Clauses, InnerFun, Expr, {nil, Ann}, Uniq, S);
build_inline_each(Ann, Clauses, Expr, {nil, _} = Into, Uniq, S) ->
InnerFun = fun(InnerExpr, InnerAcc) -> {cons, Ann, InnerExpr, InnerAcc} end,
ReduceExpr = build_reduce(Ann, Clauses, InnerFun, Expr, Into, Uniq, S),
{?remote(Ann, lists, reverse, [ReduceExpr]), S};
{ReduceExpr, SR} = build_reduce(Ann, Clauses, InnerFun, Expr, Into, Uniq, S),
{?remote(Ann, lists, reverse, [ReduceExpr]), SR};
build_inline_each(Ann, Clauses, Expr, {bin, _, []}, Uniq, S) ->
{InnerValue, SV} = build_var(Ann, S),
Generated = erl_anno:set_generated(true, Ann),
@@ -138,8 +138,8 @@ build_inline_each(Ann, Clauses, Expr, {bin, _, []}, Uniq, S) ->
]}
end,
ReduceExpr = build_reduce(Ann, Clauses, InnerFun, Expr, {nil, Ann}, Uniq, SV),
{?remote(Ann, erlang, list_to_bitstring, [ReduceExpr]), SV}.
{ReduceExpr, SR} = build_reduce(Ann, Clauses, InnerFun, Expr, {nil, Ann}, Uniq, SV),
{?remote(Ann, erlang, list_to_bitstring, [ReduceExpr]), SR}.
build_into(Ann, Clauses, Expr, {map, _, []}, Uniq, S) ->
{ReduceExpr, SR} = build_inline_each(Ann, Clauses, Expr, {nil, Ann}, Uniq, S),
@@ -161,7 +161,7 @@ build_into(Ann, Clauses, Expr, Into, Uniq, S) ->
?remote(Ann, 'Elixir.Collectable', into, [Into])
},
IntoReduceExpr = build_reduce(Ann, Clauses, InnerFun, Expr, Acc, Uniq, SD),
{IntoReduceExpr, SN} = build_reduce(Ann, Clauses, InnerFun, Expr, Acc, Uniq, SD),
TryExpr =
{'try', Ann,
@@ -173,7 +173,7 @@ build_into(Ann, Clauses, Expr, Into, Uniq, S) ->
[stacktrace_clause(Ann, Fun, Acc, Kind, Reason, Stack)],
[]},
{{block, Ann, [MatchExpr, TryExpr]}, SD}.
{{block, Ann, [MatchExpr, TryExpr]}, SN}.
stacktrace_clause(Ann, Fun, Acc, Kind, Reason, Stack) ->
{clause, Ann,
@@ -186,7 +186,7 @@ stacktrace_clause(Ann, Fun, Acc, Kind, Reason, Stack) ->
build_reduce(Ann, Clauses, InnerFun, Expr, Into, false, S) ->
{Acc, SA} = build_var(Ann, S),
build_reduce_each(Clauses, InnerFun(Expr, Acc), Into, Acc, SA);
{build_reduce_each(Clauses, InnerFun(Expr, Acc), Into, Acc, SA), SA};
build_reduce(Ann, Clauses, InnerFun, Expr, Into, true, S) ->
%% Those variables are used only inside the anonymous function
%% so we don't need to worry about returning the scope.
@@ -209,7 +209,7 @@ build_reduce(Ann, Clauses, InnerFun, Expr, Into, true, S) ->
]},
EnumReduceCall = build_reduce_each(Clauses, InnerExpr, NewInto, Acc, SU),
?remote(Ann, erlang, element, [{integer, Ann, 1}, EnumReduceCall]).
{?remote(Ann, erlang, element, [{integer, Ann, 1}, EnumReduceCall]), SU}.
build_reduce_each([{enum, Meta, Left, Right, Filters} | T], Expr, Arg, Acc, S) ->
Ann = ?ann(Meta),
+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)),
+21 -11
View File
@@ -640,7 +640,6 @@ map -> struct_op struct_expr eol map_args : {'%', meta_from_token('$1'), ['$2',
Erlang code.
-define(file(), get(elixir_parser_file)).
-define(columns(), get(elixir_parser_columns)).
-define(token_metadata(), get(elixir_token_metadata)).
@@ -729,10 +728,12 @@ build_op(AST, {_Kind, Location, '//'}, Right) ->
build_op({UOp, _, [Left]}, {_Kind, {Line, Column, _} = Location, 'in'}, Right) when ?rearrange_uop(UOp) ->
%% TODO: Remove "not left in right" rearrangement on v2.0
elixir_errors:erl_warn({Line, Column}, ?file(),
warn(
{Line, Column},
"\"not expr1 in expr2\" is deprecated. "
"Instead use \"expr1 not in expr2\" if you require Elixir v1.5+, "
"or \"not(expr1 in expr2)\" if you have to support earlier Elixir versions"),
"or \"not(expr1 in expr2)\" if you have to support earlier Elixir versions"
),
Meta = meta_from_location(Location),
{UOp, Meta, [{'in', Meta, [Left, Right]}]};
@@ -1169,21 +1170,22 @@ error_invalid_kw_identifier({_, Location, KW}) ->
%% TODO: Make this an error on v2.0
warn_trailing_comma({',', {Line, Column, _}}) ->
elixir_errors:erl_warn({Line, Column}, ?file(),
"trailing commas are not allowed inside function/macro call arguments"
).
warn({Line, Column}, "trailing commas are not allowed inside function/macro call arguments").
%% TODO: Make this an error on v2.0
warn_empty_paren({_, {Line, Column, _}}) ->
elixir_errors:erl_warn({Line, Column}, ?file(),
warn(
{Line, Column},
"invalid expression (). "
"If you want to invoke or define a function, make sure there are "
"no spaces between the function name and its arguments. If you wanted "
"to pass an empty block or code, pass a value instead, such as a nil or an atom").
"to pass an empty block or code, pass a value instead, such as a nil or an atom"
).
%% TODO: Make this an error on v2.0
warn_pipe({arrow_op, {Line, Column, _}, Op}, {_, [_ | _], [_ | _]}) ->
elixir_errors:erl_warn({Line, Column}, ?file(),
warn(
{Line, Column},
io_lib:format(
"parentheses are required when piping into a function call. For example:\n\n"
" foo 1 ~ts bar 2 ~ts baz 3\n\n"
@@ -1197,6 +1199,14 @@ warn_pipe(_Token, _) ->
ok.
warn_empty_stab_clause({stab_op, {Line, Column, _}, '->'}) ->
elixir_errors:erl_warn({Line, Column}, ?file(),
warn(
{Line, Column},
"an expression is always required on the right side of ->. "
"Please provide a value after ->").
"Please provide a value after ->"
).
warn(LineColumn, Message) ->
case get(elixir_parser_warning_file) of
nil -> ok;
File -> elixir_errors:erl_warn(LineColumn, File, Message)
end.
@@ -126,23 +126,23 @@ defmodule Code.Formatter.GeneralTest do
"""
formatter = fn content, opts ->
assert opts == [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 == [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 == [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 == [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
@@ -915,6 +915,11 @@ defmodule CodeFragmentTest do
assert cc2q("foo(123, ~r/") == s2q("foo(123, __cursor__())")
end
test "no warnings" do
assert cc2q(~s"?\\ ") == s2q("__cursor__()")
assert cc2q(~s"{fn -> end, ") == s2q("{fn -> nil end, __cursor__()}")
end
test "options" do
opts = [columns: true]
assert cc2q("foo(", opts) == s2q("foo(__cursor__())", opts)
@@ -115,6 +115,7 @@ defmodule Code.Normalizer.FormatterASTTest do
test "does not reformat aliases" do
assert_same ~S[:"Elixir.String"]
assert_same ~S[:"Elixir"]
end
test "quoted operators" do
@@ -620,6 +620,7 @@ defmodule Code.Normalizer.QuotedASTTest do
assert quoted_to_string({:__block__, [], [:"a\nb\tc"]}, escape: false) == ~s/:"a\nb\tc"/
assert quoted_to_string({:__block__, [], [:"a\nb\tc"]}) == ~S/:"a\nb\tc"/
assert quoted_to_string(quote(do: :"Elixir")) == "Elixir"
assert quoted_to_string(quote(do: :"Elixir.Foo")) == "Foo"
assert quoted_to_string(quote(do: :"Elixir.Foo.Bar")) == "Foo.Bar"
assert quoted_to_string(quote(do: :"Elixir.foobar")) == ~S/:"Elixir.foobar"/
+15 -10
View File
@@ -52,6 +52,9 @@ defmodule CodeTest do
# The order of which values win is not guaranteed, but it should evaluate successfully.
assert Code.eval_string("b = String.Chars.to_string(a)", a: 0, a: 1) ==
{"1", [{:b, "1"}, {:a, 1}]}
assert Code.eval_string("b = String.Chars.to_string(a)", a: 0, a: 1, c: 2) ==
{"1", [{:c, 2}, {:b, "1"}, {:a, 1}]}
end
test "with many options" do
@@ -75,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
@@ -98,6 +98,19 @@ defmodule Kernel.ComprehensionTest do
assert Process.get(:into_halt)
end
test "nested for comprehensions with unique values" do
assert for(x <- [1, 1, 2], uniq: true, do: for(y <- [3, 3], uniq: true, do: x * y)) == [
[3],
[6]
]
assert for(<<x <- "abcabc">>,
uniq: true,
into: "",
do: for(<<y <- "zz">>, uniq: true, into: "", do: to_bin(x) <> to_bin(y))
) == "azbzcz"
end
test "for comprehensions with nilly filters" do
assert for(x <- 1..3, nilly(), do: x * 2) == []
end
@@ -119,6 +119,14 @@ defmodule Kernel.DocsTest do
def two_good_names(first, :ok), do: first
def two_good_names(second, :error), do: second
def really_long_signature(
really_long_var_named_one,
really_long_var_named_two,
really_long_var_named_three
) do
{really_long_var_named_one, really_long_var_named_two, really_long_var_named_three}
end
end
)
@@ -128,6 +136,7 @@ defmodule Kernel.DocsTest do
assert [
arg_names,
only_underscore,
really_long_signature,
two_good_names,
with_defaults,
with_map_and_default,
@@ -141,6 +150,12 @@ defmodule Kernel.DocsTest do
# only_underscore/1
assert {{:only_underscore, 1}, ["only_underscore(_)"]} = only_underscore
# really_long_signature/3
assert {{:really_long_signature, 3},
[
"really_long_signature(really_long_var_named_one, really_long_var_named_two, really_long_var_named_three)"
]} = really_long_signature
# two_good_names/2
assert {{:two_good_names, 2}, ["two_good_names(first, atom)"]} = two_good_names
@@ -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("""
@@ -521,6 +521,9 @@ defmodule Kernel.QuoteTest.AliasHygieneTest do
assert {:__aliases__, [alias: false], [:Foo, :Bar]} = quote(do: Foo.Bar)
assert {:__aliases__, [alias: false], [:Dict, :Bar]} = quote(do: Dict.Bar)
assert {:__aliases__, [alias: Dict.Bar], [:SuperDict, :Bar]} = quote(do: SuperDict.Bar)
# Edge-case
assert {:__aliases__, _, [Elixir]} = quote(do: Elixir)
end
test "expand aliases" do
@@ -1163,9 +1163,11 @@ defmodule Kernel.WarningTest do
def without_specs(term, options \\ [])
@callback foo :: {:ok, term}
@callback foo(term) :: {:ok, term}
@callback foo(term, keyword) :: {:ok, term, keyword}
@callback foo_when :: {:ok, x} when x: term
@callback foo_when(x) :: {:ok, x} when x: term
@callback foo_when(x, opts) :: {:ok, x, opts} when x: term, opts: keyword
@@ -1178,12 +1180,18 @@ defmodule Kernel.WarningTest do
""")
end)
assert message =~
"cannot define @callback foo/0 inside protocol, use def/1 to outline your protocol definition\n nofile:1"
assert message =~
"cannot define @callback foo/1 inside protocol, use def/1 to outline your protocol definition\n nofile:1"
assert message =~
"cannot define @callback foo/2 inside protocol, use def/1 to outline your protocol definition\n nofile:1"
assert message =~
"cannot define @callback foo_when/0 inside protocol, use def/1 to outline your protocol definition\n nofile:1"
assert message =~
"cannot define @callback foo_when/1 inside protocol, use def/1 to outline your protocol definition\n nofile:1"
+10
View File
@@ -326,6 +326,8 @@ defmodule MacroTest do
end
test "aliases call" do
assert macro_to_string(quote(do: Elixir)) == "Elixir"
assert macro_to_string(quote(do: Foo)) == "Foo"
assert macro_to_string(quote(do: Foo.Bar.baz(1, 2, 3))) == "Foo.Bar.baz(1, 2, 3)"
assert macro_to_string(quote(do: Foo.Bar.baz([1, 2, 3]))) == "Foo.Bar.baz([1, 2, 3])"
assert macro_to_string(quote(do: Foo.bar(<<>>, []))) == "Foo.bar(<<>>, [])"
@@ -810,6 +812,14 @@ defmodule MacroTest do
defmodule Foo.Bar do
assert __MODULE__ in __ENV__.context_modules
end
assert Foo.Bar in __ENV__.context_modules
Code.compile_string("""
defmodule Foo.Bar.Compiled do
true = __MODULE__ in __ENV__.context_modules
end
""")
end
test "to_match/1" do
@@ -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
@@ -687,5 +692,45 @@ defmodule Module.Types.TypesTest do
end
) == :none
end
test "no-recursion on guards with map fields" do
assert warning(
[assigns],
(
variable_enum = assigns.variable_enum
case true do
_ when variable_enum != nil -> assigns.variable_enum
end
)
) == :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()
+5
View File
@@ -131,6 +131,11 @@ defmodule StringTest do
end
end
test "split_at/2 with invalid guard" do
assert String.split_at(<<?a, 195, 10, ?a>>, 2) == {<<?a, 195>>, <<10, ?a>>}
assert String.split_at(<<107, 205, 135, 184>>, 1) == {<<107, 205, 135>>, <<184>>}
end
test "upcase/1" do
assert String.upcase("123 abcd 456 efg hij ( %$#) kl mnop @ qrst = -_ uvwxyz") ==
"123 ABCD 456 EFG HIJ ( %$#) KL MNOP @ QRST = -_ UVWXYZ"
+32 -25
View File
@@ -2,6 +2,7 @@ defmodule Mix.Compilers.Elixir do
@moduledoc false
@manifest_vsn 13
@checkpoint_vsn 2
import Record
@@ -102,7 +103,7 @@ defmodule Mix.Compilers.Elixir do
{false, stale, old_lock, old_config}
end
{stale_local_mods, stale_local_exports, all_local_exports} =
{stale_modules, stale_exports, all_local_exports} =
stale_local_deps(manifest, stale, modified, all_local_exports)
prev_paths = for source(source: source) <- all_sources, do: source
@@ -120,8 +121,8 @@ defmodule Mix.Compilers.Elixir do
all_modules,
all_sources,
removed,
stale_local_mods,
Map.merge(stale_local_exports, removed_modules),
Map.merge(stale_modules, removed_modules),
Map.merge(stale_exports, removed_modules),
dest
)
end
@@ -180,7 +181,7 @@ defmodule Mix.Compilers.Elixir do
delete_compiler_info()
end
else
# We need to return ok if deps_changed? or stale_local_mods changed,
# We need to return ok if deps_changed? or stale_modules changed,
# even if no code was compiled, because we need to propagate the changed
# status to compile.protocols. This will be the case whenever:
#
@@ -193,7 +194,7 @@ defmodule Mix.Compilers.Elixir do
# will only compute the diff with current protocols. In fact, there is no
# need to reconsolidate if an Erlang file changes and it doesn't trigger
# any other change, but the diff check should be reasonably fast anyway.
status = if removed != [] or deps_changed? or stale_local_mods != %{}, do: :ok, else: :noop
status = if removed != [] or deps_changed? or stale_modules != %{}, do: :ok, else: :noop
# If nothing changed but there is one more recent mtime, bump the manifest
if status != :noop or Enum.any?(Map.values(sources_stats), &(elem(&1, 0) > modified)) do
@@ -283,8 +284,8 @@ defmodule Mix.Compilers.Elixir do
all_modules,
all_sources,
removed,
stale_local_mods,
stale_local_exports,
stale_modules,
stale_exports,
dest
) do
# TODO: Use :maps.from_keys/2 on Erlang/OTP 24+
@@ -294,13 +295,17 @@ defmodule Mix.Compilers.Elixir do
into: %{},
do: {module, []}
{checkpoint_stale, checkpoint_modules} = parse_checkpoint(manifest)
modules_to_recompile = Map.merge(checkpoint_modules, modules_to_recompile)
stale_local_mods = Map.merge(checkpoint_stale, stale_local_mods)
{checkpoint_stale_modules, checkpoint_stale_exports, checkpoint_modules} =
parse_checkpoint(manifest)
if map_size(stale_local_mods) != map_size(checkpoint_stale) or
modules_to_recompile = Map.merge(checkpoint_modules, modules_to_recompile)
stale_modules = Map.merge(checkpoint_stale_modules, stale_modules)
stale_exports = Map.merge(checkpoint_stale_exports, stale_exports)
if map_size(stale_modules) != map_size(checkpoint_stale_modules) or
map_size(stale_exports) != map_size(checkpoint_stale_exports) or
map_size(modules_to_recompile) != map_size(checkpoint_modules) do
write_checkpoint(manifest, stale_local_mods, modules_to_recompile)
write_checkpoint(manifest, stale_modules, stale_exports, modules_to_recompile)
end
sources_stats =
@@ -332,8 +337,8 @@ defmodule Mix.Compilers.Elixir do
all_modules,
all_sources,
removed ++ changed,
stale_local_mods,
stale_local_exports,
stale_modules,
stale_exports,
dest
)
@@ -654,16 +659,16 @@ defmodule Mix.Compilers.Elixir do
# files that have changed. Then it recursively figures out
# all the files that changed (via the module dependencies) and
# return the non-changed entries and the removed sources.
defp update_stale_entries(modules, _sources, [], stale_mods, stale_exports, _compile_path)
when stale_mods == %{} and stale_exports == %{} do
defp update_stale_entries(modules, _sources, [], stale_modules, stale_exports, _compile_path)
when stale_modules == %{} and stale_exports == %{} do
{modules, %{}, []}
end
defp update_stale_entries(modules, sources, changed, stale_mods, stale_exports, compile_path) do
defp update_stale_entries(modules, sources, changed, stale_modules, stale_exports, compile_path) do
# TODO: Use :maps.from_keys/2 on Erlang/OTP 24+
changed = Enum.into(changed, %{}, &{&1, []})
reducer = &remove_stale_entry(&1, &2, sources, stale_exports, compile_path)
remove_stale_entries(modules, %{}, changed, stale_mods, reducer)
remove_stale_entries(modules, %{}, changed, stale_modules, reducer)
end
defp remove_stale_entries(modules, exports, old_changed, old_stale, reducer) do
@@ -720,6 +725,8 @@ defmodule Mix.Compilers.Elixir do
defp stale_local_deps(manifest, stale_modules, modified, old_exports) do
base = Path.basename(manifest)
# The stale modules so far will become both stale_modules and stale_exports,
# as any export from a dependency needs to be recompiled.
# TODO: Use :maps.from_keys/2 on Erlang/OTP 24+
stale_modules = for module <- stale_modules, do: {module, []}, into: %{}
@@ -727,7 +734,7 @@ defmodule Mix.Compilers.Elixir do
not scm.fetchable?,
manifest = Path.join([opts[:build], ".mix", base]),
Mix.Utils.last_modified(manifest) > modified,
reduce: {stale_modules, %{}, old_exports} do
reduce: {stale_modules, stale_modules, old_exports} do
{modules, exports, new_exports} ->
{_manifest_modules, dep_sources} = read_manifest(manifest)
@@ -1017,19 +1024,19 @@ defmodule Mix.Compilers.Elixir do
(manifest <> ".checkpoint") |> File.read!() |> :erlang.binary_to_term()
rescue
_ ->
{%{}, %{}}
{%{}, %{}, %{}}
else
{@manifest_vsn, stale, recompile_modules} ->
{stale, recompile_modules}
{@checkpoint_vsn, stale_modules, stale_exports, recompile_modules} ->
{stale_modules, stale_exports, recompile_modules}
_ ->
{%{}, %{}}
{%{}, %{}, %{}}
end
end
defp write_checkpoint(manifest, stale, recompile_modules) do
defp write_checkpoint(manifest, stale_modules, stale_exports, recompile_modules) do
File.mkdir_p!(Path.dirname(manifest))
term = {@manifest_vsn, stale, recompile_modules}
term = {@checkpoint_vsn, stale_modules, stale_exports, recompile_modules}
checkpoint_data = :erlang.term_to_binary(term, [:compressed])
File.write!(manifest <> ".checkpoint", checkpoint_data)
end
+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} =
+13 -2
View File
@@ -609,13 +609,24 @@ defmodule Mix.Release do
end
defp build_release_spec(release, modes) do
%{name: name, version: version, erts_version: erts_version, applications: apps} = release
%{
name: name,
version: version,
erts_version: erts_version,
applications: apps,
options: options
} = release
skip_mode_validation_for =
options
|> Keyword.get(:skip_mode_validation_for, [])
|> MapSet.new()
rel_apps =
for {app, mode} <- modes do
properties = Map.get(apps, app) || throw({:error, "Unknown application #{inspect(app)}"})
children = Keyword.get(properties, :applications, [])
validate_mode!(app, mode, modes, children)
app in skip_mode_validation_for || validate_mode!(app, mode, modes, children)
build_app_for_release(app, mode, properties)
end
+16 -13
View File
@@ -104,11 +104,11 @@ defmodule Mix.Tasks.Format do
The `opts` passed to `format/2` contains all the formatting options and either:
* `:sigil` (atom) - the sigil being formatted, e.g. `:M`.
* `:sigil` (atom) - the sigil being formatted, e.g. `:M`.
* `:modifiers` (charlist) - list of sigil modifiers.
* `:modifiers` (charlist) - list of sigil modifiers.
* `:extension` (string) - the extension of the file being formatted, e.g. `".md"`.
* `:extension` (string) - the extension of the file being formatted, e.g. `".md"`.
Now any application can use your formatter as follows:
@@ -288,7 +288,15 @@ defmodule Mix.Tasks.Format do
end
end
formatter_opts = Keyword.put(formatter_opts, :plugins, plugins)
sigils =
for plugin <- plugins,
sigil <- find_sigils_from_plugins(plugin, formatter_opts),
do: {sigil, &plugin.format(&1, &2 ++ formatter_opts)}
formatter_opts =
formatter_opts
|> Keyword.put(:plugins, plugins)
|> Keyword.put(:sigils, sigils)
if deps == [] and subs == [] do
{{formatter_opts, []}, sources}
@@ -495,12 +503,12 @@ defmodule Mix.Tasks.Format do
ext = Path.extname(file)
cond do
plugin = find_plugin_for_extension(formatter_opts, ext) ->
&plugin.format(&1, [extension: ext, file: file] ++ formatter_opts)
ext in ~w(.ex .exs) ->
&elixir_format(&1, [file: file] ++ formatter_opts)
plugin = find_plugin_for_extension(formatter_opts, ext) ->
&plugin.format(&1, [extension: ext] ++ formatter_opts)
true ->
& &1
end
@@ -537,12 +545,7 @@ defmodule Mix.Tasks.Format do
defp stdin_or_wildcard(path), do: path |> Path.expand() |> Path.wildcard(match_dot: true)
defp elixir_format(content, formatter_opts) do
sigils =
for plugin <- Keyword.fetch!(formatter_opts, :plugins),
sigil <- find_sigils_from_plugins(plugin, formatter_opts),
do: {sigil, &plugin.format(&1, &2 ++ formatter_opts)}
IO.iodata_to_binary([Code.format_string!(content, [sigils: sigils] ++ formatter_opts), ?\n])
IO.iodata_to_binary([Code.format_string!(content, formatter_opts), ?\n])
end
defp find_sigils_from_plugins(plugin, formatter_opts) do
+8
View File
@@ -455,6 +455,14 @@ defmodule Mix.Tasks.Release do
* `:steps` - a list of steps to execute when assembling the release. See
the "Steps" section for more information.
* `:skip_mode_validation_for` - a list of application names
(atoms) specifying applications to skip strict validation of
"unsafe" modes. An "unsafe" case is when a parent application
mode is `:permanent` but one of the applications it depends on
is set to `:load`. Use this with care, as a release with
invalid modes may no longer boot without additional tweaks.
Defaults to `[]`.
Note each release definition can be given as an anonymous function. This
is useful if some release attributes are expensive to compute:
+12 -1
View File
@@ -299,6 +299,7 @@ defmodule Mix.Tasks.Test.Coverage do
print_summary(module_results, totals, summary_opts)
if totals < get_threshold(summary_opts) do
print_failed_threshold(totals, get_threshold(summary_opts))
System.at_exit(fn _ -> exit({:shutdown, 3}) end)
end
@@ -349,7 +350,14 @@ defmodule Mix.Tasks.Test.Coverage do
Mix.shell().info("-----------|--------------------------")
results |> Enum.sort() |> Enum.each(&display(&1, threshold))
Mix.shell().info("-----------|--------------------------")
display({totals, "Total"}, opts)
display({totals, "Total"}, threshold)
Mix.shell().info("")
end
defp print_failed_threshold(totals, threshold) do
Mix.shell().info("Coverage test failed, threshold not met:\n")
Mix.shell().info(" Coverage: #{format_number(totals, 6)}%")
Mix.shell().info(" Threshold: #{format_number(threshold, 6)}%")
Mix.shell().info("")
end
@@ -369,6 +377,9 @@ defmodule Mix.Tasks.Test.Coverage do
defp color(percentage, threshold) when percentage >= threshold, do: :green
defp color(_, _), do: :red
defp format_number(number, length) when is_integer(number),
do: format_number(number / 1, length)
defp format_number(number, length), do: :io_lib.format("~#{length}.2f", [number])
defp format_name(name) when is_binary(name), do: name
+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)
+7
View File
@@ -479,6 +479,13 @@ defmodule Mix.ReleaseTest do
assert message =~
"Application :mix has mode :permanent but it depends on :elixir which is set to :none"
end
test "does not raise on child unsafe mode if parent is in `skip_mode_validation_for`" do
# `:mix` is a parent that depends on `:elixir`
release = release(applications: [elixir: :load], skip_mode_validation_for: [:mix])
assert make_boot_script(release, @boot_script_path, release.boot_scripts.start) == :ok
assert make_boot_script(release, @boot_script_path, release.boot_scripts.start_clean) == :ok
end
end
describe "make_cookie/1" do
@@ -182,6 +182,11 @@ defmodule Mix.Tasks.Compile.ElixirTest do
Process.put({MixTest.Case.Sample, :application}, extra_applications: [:logger])
File.mkdir_p!("config")
File.write!("config/config.exs", """
import Config
config :logger, :level, :debug
""")
File.write!("lib/a.ex", """
defmodule A do
_ = Logger.metadata()
@@ -234,6 +239,12 @@ defmodule Mix.Tasks.Compile.ElixirTest do
refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
assert File.stat!("_build/dev/lib/sample/.mix/compile.elixir").mtime > @old_time
# No-op does not recompile
File.touch!("_build/dev/lib/sample/.mix/compile.elixir", @old_time)
assert recompile.() == {:ok, []}
refute_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
# Changing self fully recompiles
File.write!("config/config.exs", """
import Config
@@ -263,6 +274,76 @@ defmodule Mix.Tasks.Compile.ElixirTest do
Application.delete_env(:sample, :foo, persistent: true)
end
test "recompiles files when config changes export dependencies" do
in_fixture("no_mixfile", fn ->
Mix.Project.push(MixTest.Case.Sample)
Process.put({MixTest.Case.Sample, :application}, extra_applications: [:ex_unit])
File.mkdir_p!("config")
File.write!("lib/a.ex", """
defmodule A do
def test_struct do
%ExUnit.Test{}
end
end
""")
assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:ok, []}
assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
assert_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
recompile = fn ->
Mix.ProjectStack.pop()
Mix.Project.push(MixTest.Case.Sample)
Mix.Tasks.Loadconfig.load_compile("config/config.exs")
Mix.Tasks.Compile.Elixir.run(["--verbose"])
end
# Adding config recompiles
File.write!("config/config.exs", """
import Config
config :ex_unit, :some, :config
""")
File.touch!("_build/dev/lib/sample/.mix/compile.elixir", @old_time)
assert recompile.() == {:ok, []}
assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
assert File.stat!("_build/dev/lib/sample/.mix/compile.elixir").mtime > @old_time
# Changing config recompiles
File.write!("config/config.exs", """
import Config
config :ex_unit, :some, :another
""")
File.touch!("_build/dev/lib/sample/.mix/compile.elixir", @old_time)
assert recompile.() == {:ok, []}
assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
assert File.stat!("_build/dev/lib/sample/.mix/compile.elixir").mtime > @old_time
# Removing config recompiles
File.write!("config/config.exs", """
import Config
""")
File.touch!("_build/dev/lib/sample/.mix/compile.elixir", @old_time)
assert recompile.() == {:ok, []}
assert_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
assert File.stat!("_build/dev/lib/sample/.mix/compile.elixir").mtime > @old_time
# No-op does not recompile
File.touch!("_build/dev/lib/sample/.mix/compile.elixir", @old_time)
assert recompile.() == {:ok, []}
refute_received {:mix_shell, :info, ["Compiled lib/a.ex"]}
refute_received {:mix_shell, :info, ["Compiled lib/b.ex"]}
end)
after
Application.delete_env(:ex_unit, :some, persistent: true)
end
test "recompiles files when config changes with crashes" do
in_fixture("no_mixfile", fn ->
Mix.Project.push(MixTest.Case.Sample)
+6 -1
View File
@@ -206,6 +206,8 @@ defmodule Mix.Tasks.FormatTest do
assert opts[:from_formatter_exs] == :yes
assert opts[:sigil] == :W
assert opts[:modifiers] == 'abc'
assert opts[:line] == 2
assert opts[:file] =~ ~r/\/a\.ex$/
contents |> String.split(~r/\s/) |> Enum.join("\n")
end
end
@@ -247,12 +249,15 @@ defmodule Mix.Tasks.FormatTest do
def features(opts) do
assert opts[:from_formatter_exs] == :yes
[extensions: ~w(.w)]
[extensions: ~w(.w), sigils: [:W]]
end
def format(contents, opts) do
assert opts[:from_formatter_exs] == :yes
assert opts[:extension] == ".w"
assert opts[:file] =~ ~r/\/a\.w$/
assert [W: sigil_fun] = opts[:sigils]
assert is_function(sigil_fun, 2)
contents |> String.split(~r/\s/) |> Enum.join("\n")
end
end
+14 -2
View File
@@ -168,7 +168,14 @@ defmodule Mix.Tasks.TestTest do
# We skip a test in bar to force coverage below the default threshold
# which should result in an exit status of 1.
assert {_, code} = mix_code(["test", "--cover", "--exclude", "maybe_skip"])
assert {output, code} = mix_code(["test", "--cover", "--exclude", "maybe_skip"])
assert output =~ """
Coverage test failed, threshold not met:
Coverage: 0.00%
Threshold: 90.00%
"""
unless windows?() do
assert code == 3
@@ -426,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")