Compare commits

...
35 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
57 changed files with 718 additions and 201 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
+49
View File
@@ -128,6 +128,55 @@ 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
+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.2
1.13.4
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/sh
set -e
ELIXIR_VERSION=1.13.2
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.2
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
%{} ->
+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])
+18 -1
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()
+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 ->
+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"])
+14 -7
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} =
+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),
+1 -1
View File
@@ -85,6 +85,7 @@ expand({require, Meta, [Ref, Opts]}, S, E) ->
if
is_atom(ERef) ->
elixir_aliases:ensure_loaded(Meta, ERef, ET),
elixir_env:trace({require, Meta, ERef, EOpts}, ET),
{ERef, ST, expand_require(Meta, ERef, EOpts, ET)};
true ->
form_error(Meta, E, ?MODULE, {expected_compile_time_module, require, Ref})
@@ -931,7 +932,6 @@ no_alias_expansion(Other) ->
Other.
expand_require(Meta, Ref, Opts, E) ->
elixir_env:trace({require, Meta, Ref, Opts}, E),
RE = E#{requires := ordsets:add_element(Ref, ?key(E, requires))},
expand_alias(Meta, false, Ref, Opts, RE).
+2 -2
View File
@@ -47,10 +47,10 @@ trace({alias_expansion, _Meta, Lookup, _Result}, #{lexical_tracker := Pid}) ->
?tracker:alias_dispatch(Pid, Lookup),
ok;
trace({require, _Meta, Module, _Opts}, #{lexical_tracker := Pid}) ->
?tracker:add_require(Pid, Module),
?tracker:remote_dispatch(Pid, Module, compile),
ok;
trace({struct_expansion, _Meta, Module, _Keys}, #{lexical_tracker := Pid}) ->
?tracker:add_require(Pid, Module),
?tracker:add_export(Pid, Module),
ok;
trace({alias_reference, _Meta, Module}, #{lexical_tracker := Pid} = E) ->
?tracker:remote_dispatch(Pid, Module, mode(E)),
@@ -126,23 +126,23 @@ defmodule Code.Formatter.GeneralTest do
"""
formatter = fn content, opts ->
assert opts == [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
@@ -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
@@ -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
+2
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(<<>>, [])"
@@ -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} =
+14 -11
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}
@@ -496,7 +504,7 @@ defmodule Mix.Tasks.Format do
cond do
plugin = find_plugin_for_extension(formatter_opts, ext) ->
&plugin.format(&1, [extension: ext] ++ formatter_opts)
&plugin.format(&1, [extension: ext, file: file] ++ formatter_opts)
ext in ~w(.ex .exs) ->
&elixir_format(&1, [file: file] ++ formatter_opts)
@@ -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
+1 -1
View File
@@ -350,7 +350,7 @@ 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
+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)
@@ -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
+6 -1
View File
@@ -433,9 +433,14 @@ defmodule Mix.Tasks.TestTest do
in_fixture("umbrella_test", fn ->
# Run false positive test first so at least the code is compiled
# and we can perform more aggressive assertions later
assert mix(["test", "apps/unknown_app/test"]) =~ """
output = mix(["test", "apps/unknown_app/test"])
assert output =~ """
==> bar
Paths given to "mix test" did not match any directory/file: apps/unknown_app/test
"""
assert output =~ """
==> foo
Paths given to "mix test" did not match any directory/file: apps/unknown_app/test
"""
+6 -7
View File
@@ -253,7 +253,7 @@ defmodule Mix.Tasks.XrefTest do
output = """
Compiling 2 files (.ex)
Generated sample app
lib/b.ex:2: require A (export)
lib/b.ex:2: import A (export)
lib/b.ex:3: call A.macro/0 (compile)
lib/b.ex:4: import A.macro/0 (compile)
lib/b.ex:5: call A.fun/0 (compile)
@@ -287,6 +287,7 @@ defmodule Mix.Tasks.XrefTest do
output = """
Compiling 2 files (.ex)
Generated sample app
lib/b.ex:2: require A (compile)
lib/b.ex:3: call A.macro/0 (compile)
"""
@@ -313,10 +314,11 @@ defmodule Mix.Tasks.XrefTest do
output = """
Compiling 2 files (.ex)
Generated sample app
lib/b.ex:3: require A (compile)
lib/b.ex:3: call A.macro/0 (compile)
"""
message = "Too many traces (found: 1, permitted: 0)"
message = "Too many traces (found: 2, permitted: 0)"
assert_raise Mix.Error, message, fn ->
assert_trace(~w[--label compile --fail-above 0], "lib/b.ex", files, output)
@@ -759,10 +761,7 @@ defmodule Mix.Tasks.XrefTest do
defmodule B do
# Let's also test that we track literal atom behaviours
@behaviour :"Elixir.A.Behaviour"
def foo do
A.foo()
end
def foo, do: :foo
end
""")
@@ -772,7 +771,7 @@ defmodule Mix.Tasks.XrefTest do
digraph "xref graph" {
"lib/a.ex"
"lib/a.ex" -> "lib/b.ex" [label="(compile)"]
"lib/b.ex" -> "lib/a.ex" [label="(export)"]
"lib/b.ex" -> "lib/a.ex"
"lib/b.ex"
}
"""
+1 -1
View File
@@ -391,7 +391,7 @@ defmodule Mix.UmbrellaTest do
assert_received {:mix_shell, :info, ["Compiled lib/bar.ex"]}
# But exports dependencies are not recompiled
File.write!("lib/bar.ex", "defmodule Bar, do: (require Foo)")
File.write!("lib/bar.ex", "defmodule Bar, do: (import Foo, warn: false)")
assert Mix.Tasks.Compile.Elixir.run(["--verbose"]) == {:ok, []}
assert_received {:mix_shell, :info, ["Compiled lib/bar.ex"]}
+26 -5
View File
@@ -7,12 +7,13 @@ Application.put_env(:logger, :backends, [])
os_exclude = if match?({:win32, _}, :os.type()), do: [unix: true], else: [windows: true]
epmd_exclude = if match?({:win32, _}, :os.type()), do: [epmd: true], else: []
ExUnit.start(trace: "--trace" in System.argv(), exclude: epmd_exclude ++ os_exclude)
git_exclude = if Mix.SCM.Git.git_version() <= {1, 7, 4}, do: [git_sparse: true], else: []
rebar_exclude = if System.otp_release() >= "25", do: [rebar: true], else: []
unless {1, 7, 4} <= Mix.SCM.Git.git_version() do
IO.puts(:stderr, "Skipping tests with git sparse checkouts...")
ExUnit.configure(exclude: :git_sparse)
end
ExUnit.start(
trace: "--trace" in System.argv(),
exclude: epmd_exclude ++ os_exclude ++ git_exclude ++ rebar_exclude
)
# Clear environment variables that may affect tests
System.delete_env("http_proxy")
@@ -412,6 +413,26 @@ Enum.each([:invalidapp, :invalidvsn, :noappfile, :nosemver, :ok], fn dep ->
File.mkdir_p!(Path.expand("fixtures/deps_status/deps/#{dep}/.git", __DIR__))
end)
# Archive ebin
target = Path.expand("fixtures/archive", __DIR__)
unless File.dir?(Path.join(target, "ebin")) do
File.mkdir_p!(Path.join(target, "ebin"))
File.write!(Path.join([target, "ebin", "local_sample.app"]), """
{application,local_sample,
[
{modules,['Elixir.Mix.Tasks.Local.Sample']},
{applications,[kernel,stdlib,elixir]}
]
}.
""")
[{name, bin}] = Code.compile_file("lib/local.sample.ex", target)
File.write!(Path.join([target, "ebin", Atom.to_string(name) <> ".beam"]), bin)
end
## Generate helper modules
path = MixTest.Case.tmp_path("beams")