Compare commits

...
27 changed files with 469 additions and 70 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
- otp: 26 - otp: 26
otp_version: '26.0' otp_version: '26.0'
build_docs: build_docs build_docs: build_docs
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
+5 -3
View File
@@ -15,7 +15,7 @@ permissions:
jobs: jobs:
create_draft_release: create_draft_release:
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps: steps:
@@ -40,7 +40,7 @@ jobs:
- otp: 26 - otp: 26
otp_version: '26.0' otp_version: '26.0'
build_docs: build_docs build_docs: build_docs
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
@@ -56,7 +56,9 @@ jobs:
run: | run: |
gh release upload --clobber "${{ github.ref_name }}" \ gh release upload --clobber "${{ github.ref_name }}" \
elixir-otp-${{ matrix.otp }}.zip \ elixir-otp-${{ matrix.otp }}.zip \
elixir-otp-${{ matrix.otp }}.zip.sha{1,256}sum elixir-otp-${{ matrix.otp }}.zip.sha{1,256}sum \
elixir-otp-${{ matrix.otp }}.exe \
elixir-otp-${{ matrix.otp }}.exe.sha{1,256}sum
- name: Upload Docs to GitHub - name: Upload Docs to GitHub
if: ${{ matrix.build_docs }} if: ${{ matrix.build_docs }}
env: env:
@@ -22,6 +22,20 @@ runs:
shasum -a 1 elixir-otp-${{ inputs.otp }}.zip > elixir-otp-${{ inputs.otp }}.zip.sha1sum 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 shasum -a 256 elixir-otp-${{ inputs.otp }}.zip > elixir-otp-${{ inputs.otp }}.zip.sha256sum
echo "$PWD/bin" >> $GITHUB_PATH echo "$PWD/bin" >> $GITHUB_PATH
- name: Install NSIS
shell: bash
run: |
sudo apt update
sudo apt install -y nsis
- name: Build Elixir Windows Installer
shell: bash
run: |
export OTP_VERSION=${{ inputs.otp_version }}
export ELIXIR_ZIP=$PWD/elixir-otp-${{ inputs.otp }}.zip
(cd lib/elixir/scripts/windows_installer && ./build.sh)
mv lib/elixir/scripts/windows_installer/tmp/elixir-otp-${{ inputs.otp }}.exe .
shasum -a 1 elixir-otp-${{ inputs.otp }}.exe > elixir-otp-${{ inputs.otp }}.exe.sha1sum
shasum -a 256 elixir-otp-${{ inputs.otp }}.exe > elixir-otp-${{ inputs.otp }}.exe.sha256sum
- name: Get latest stable ExDoc version - name: Get latest stable ExDoc version
if: ${{ inputs.build_docs }} if: ${{ inputs.build_docs }}
shell: bash shell: bash
+36 -6
View File
@@ -68,7 +68,7 @@ In Elixir v1.15, the new reports will look like:
error: undefined function foo/0 (there is no such import) error: undefined function foo/0 (there is no such import)
my_file.exs:1 my_file.exs:1
** (CompileError) nofile: cannot compile file (errors have been logged) ** (CompileError) my_file.exs: cannot compile file (errors have been logged)
A new function, called `Code.with_diagnostics/2`, has been added so this A new function, called `Code.with_diagnostics/2`, has been added so this
information can be leveraged by editors, allowing them to point to several information can be leveraged by editors, allowing them to point to several
@@ -100,16 +100,46 @@ config :logger, :default_formatter,
format: "$time $message $metadata" format: "$time $message $metadata"
``` ```
If you use `Logger.Backends.Console` or other backends, they are If you use `Logger.Backends.Console` with a custom device or other
still fully supported and functional. If you implement your own backends, they are still fully supported and functional. If you
backends, you want to consider migrating to implement your own backends, you want to consider migrating to
[`:logger_backends`](https://github.com/elixir-lang/logger_backends) [`:logger_backends`](https://github.com/elixir-lang/logger_backends)
in the long term. in the long term.
See the new `Logger` documentation for more information on the See the new `Logger` documentation for more information on the
new features and on compatibility. new features and on compatibility.
## v1.15.0-rc.1 (2022-05-29) ## v1.15.0-rc.2 (2023-06-12)
### 1. Enhancements
#### Elixir
* [Module] Mark functions as generated in "Docs" chunk
#### ExUnit
* [ExUnit.Doctest] Include `doctest_data` in doctest tags
### 2. Bug fixes
#### Elixir
* [Kernel] Fix `import m, only: :sigils` for multi-letter sigils (regression)
* [Macro] Fix `Macro.to_string/1` for multi-letter sigils (regression)
#### ExUnit
* [ExUnit] Mark test cases as invalid when an exit occurs during `setup_all`
* [ExUnit] Fix ExUnit diffs for multi-letter sigils (regression)
* [ExUnit.Doctest] Fix line information when a doctest with multiple assertions fails
#### Mix
* [mix compile] Keep erts when pruning load paths (regression)
* [mix xref] Raise early if `mix xref` is used at the umbrella root
## v1.15.0-rc.1 (2023-05-29)
### 1. Enhancements ### 1. Enhancements
@@ -154,7 +184,7 @@ new features and on compatibility.
* [Kernel] Require pin variable when accessing variable inside binary size in match * [Kernel] Require pin variable when accessing variable inside binary size in match
## v1.15.0-rc.0 (2022-05-22) ## v1.15.0-rc.0 (2023-05-22)
### 1. Enhancements ### 1. Enhancements
+1 -1
View File
@@ -2,7 +2,7 @@ PREFIX ?= /usr/local
TEST_FILES ?= "*_test.exs" TEST_FILES ?= "*_test.exs"
SHARE_PREFIX ?= $(PREFIX)/share SHARE_PREFIX ?= $(PREFIX)/share
MAN_PREFIX ?= $(SHARE_PREFIX)/man MAN_PREFIX ?= $(SHARE_PREFIX)/man
#CANONICAL := MAJOR.MINOR/ CANONICAL := 1.15/
CANONICAL ?= main/ CANONICAL ?= main/
DOCS_FORMAT ?= html DOCS_FORMAT ?= html
ELIXIRC := bin/elixirc --ignore-module-conflict $(ELIXIRC_OPTS) ELIXIRC := bin/elixirc --ignore-module-conflict $(ELIXIRC_OPTS)
+2 -3
View File
@@ -6,12 +6,11 @@ Elixir applies bug fixes only to the latest minor branch. Security patches are a
Elixir version | Support Elixir version | Support
:------------- | :----------------------------- :------------- | :-----------------------------
1.15 | Development 1.15 | Bug fixes and security patches
1.14 | Bug fixes and security patches 1.14 | Security patches only
1.13 | Security patches only 1.13 | Security patches only
1.12 | Security patches only 1.12 | Security patches only
1.11 | Security patches only 1.11 | Security patches only
1.10 | Security patches only
## Announcements ## Announcements
+1 -1
View File
@@ -1 +1 @@
1.15.0-rc.1 1.15.0-rc.2
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
set -e set -e
ELIXIR_VERSION=1.15.0-rc.1 ELIXIR_VERSION=1.15.0-rc.2
if [ $# -eq 0 ] || { [ $# -eq 1 ] && { [ "$1" = "--help" ] || [ "$1" = "-h" ]; }; }; then if [ $# -eq 0 ] || { [ $# -eq 1 ] && { [ "$1" = "--help" ] || [ "$1" = "-h" ]; }; }; then
cat <<USAGE >&2 cat <<USAGE >&2
+1 -1
View File
@@ -1,6 +1,6 @@
@if defined ELIXIR_CLI_ECHO (@echo on) else (@echo off) @if defined ELIXIR_CLI_ECHO (@echo on) else (@echo off)
set ELIXIR_VERSION=1.15.0-rc.1 set ELIXIR_VERSION=1.15.0-rc.2
setlocal enabledelayedexpansion setlocal enabledelayedexpansion
if ""%1""=="""" if ""%2""=="""" goto documentation if ""%1""=="""" if ""%2""=="""" goto documentation
-4
View File
@@ -3,10 +3,6 @@ defmodule Code.Fragment do
This module provides conveniences for analyzing fragments of This module provides conveniences for analyzing fragments of
textual code and extract available information whenever possible. textual code and extract available information whenever possible.
Most of the functions in this module provide a best-effort
and may not be accurate under all circumstances. Read each
documentation for more information.
This module should be considered experimental. This module should be considered experimental.
""" """
@@ -8,12 +8,11 @@ Elixir applies bug fixes only to the latest minor branch. Security patches are a
Elixir version | Support Elixir version | Support
:------------- | :----------------------------- :------------- | :-----------------------------
1.15 | Development 1.15 | Bug fixes and security patches
1.14 | Bug fixes and security patches 1.14 | Security patches only
1.13 | Security patches only 1.13 | Security patches only
1.12 | Security patches only 1.12 | Security patches only
1.11 | Security patches only 1.11 | Security patches only
1.10 | Security patches only
New releases are announced in the read-only [announcements mailing list](https://groups.google.com/group/elixir-lang-ann). All security releases [will be tagged with `[security]`](https://groups.google.com/forum/#!searchin/elixir-lang-ann/%5Bsecurity%5D%7Csort:date). New releases are announced in the read-only [announcements mailing list](https://groups.google.com/group/elixir-lang-ann). All security releases [will be tagged with `[security]`](https://groups.google.com/forum/#!searchin/elixir-lang-ann/%5Bsecurity%5D%7Csort:date).
@@ -0,0 +1 @@
tmp/
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

+46
View File
@@ -0,0 +1,46 @@
#!/bin/bash
# Usage:
#
# With Elixir archive:
#
# ELIXIR_ZIP=Precompiled.zip OTP_VERSION=25.3.2.2 ./build.sh
#
# With Elixir version:
#
# ELIXIR_VERSION=1.14.5 OTP_VERSION=25.3.2.2 ./build.sh
set -euo pipefail
OTP_VERSION="${OTP_VERSION:-26.0}"
otp_release=`echo "${OTP_VERSION}" | cut -d. -f1`
mkdir -p tmp
ELIXIR_VERSION="${ELIXIR_VERSION:-}"
if [ -n "${ELIXIR_VERSION}" ]; then
ELIXIR_ZIP="tmp/elixir-${ELIXIR_VERSION}-otp-${otp_release}.zip"
if [ ! -f "${ELIXIR_ZIP}" ]; then
url="https://github.com/elixir-lang/elixir/releases/download/v${ELIXIR_VERSION}/elixir-otp-${otp_release}.zip"
echo "downloading ${url}"
curl --fail -L -o "${ELIXIR_ZIP}" "${url}"
fi
basename=elixir-${ELIXIR_VERSION}-otp-${otp_release}
else
basename=elixir-otp-${otp_release}
fi
if [ ! -d "tmp/${basename}" ]; then
unzip -d "tmp/${basename}" "${ELIXIR_ZIP}"
fi
# brew install makensis
# apt install -y nsis
# choco install -y nsis
export PATH="/c/Program Files (x86)/NSIS:${PATH}"
makensis \
-X"OutFile tmp\\${basename}.exe" \
-DOTP_VERSION=${OTP_VERSION} \
-DOTP_RELEASE="${otp_release}" \
-DELIXIR_DIR=tmp\\${basename} \
installer.nsi
echo "Installer path: tmp/${basename}.exe"
@@ -0,0 +1,209 @@
!include "MUI2.nsh"
!include "StrFunc.nsh"
${Using:StrFunc} StrStr
${Using:StrFunc} UnStrStr
${Using:StrFunc} UnStrRep
Name "Elixir"
ManifestDPIAware true
Unicode True
InstallDir "$PROGRAMFILES64\Elixir"
!define MUI_ICON "assets\drop.ico"
; Install Page: Install Erlang/OTP
Page custom CheckOTPPageShow CheckOTPPageLeave
var Dialog
var DownloadOTPLink
var InstalledERTSVersion
var InstalledOTPRelease
var OTPPath
Function CheckOTPPageShow
!insertmacro MUI_HEADER_TEXT "Checking Erlang/OTP" ""
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
EnumRegKey $0 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang" 0
ReadRegStr $1 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang\$0" ""
StrCpy $InstalledERTSVersion $0
StrCpy $OTPPath $1
${If} $1 == ""
${NSD_CreateLabel} 0 0 100% 20u "Couldn't find existing Erlang/OTP installation. Click the link below to download and install it before proceeding."
${NSD_CreateLink} 0 25u 100% 20u "Download Erlang/OTP ${OTP_RELEASE}"
Pop $DownloadOTPLink
${NSD_OnClick} $DownloadOTPLink OpenOTPDownloads
${Else}
nsExec::ExecToStack `$OTPPath\bin\erl.exe -noinput -eval "\
io:put_chars(erlang:system_info(otp_release)),\
halt()."`
Pop $0
Pop $1
${If} $0 == 0
StrCpy $InstalledOTPRelease $1
${If} $InstalledOTPRelease == ${OTP_RELEASE}
${NSD_CreateLabel} 0 0 100% 20u "Found existing Erlang/OTP $InstalledOTPRelease installation at $OTPPath. Please proceed."
${ElseIf} $2 < ${OTP_RELEASE}
${NSD_CreateLabel} 0 0 100% 30u "Found existing Erlang/OTP $InstalledOTPRelease installation at $OTPPath but this Elixir installer was precompiled for Erlang/OTP ${OTP_RELEASE}. \
We recommend checking if there is an Elixir version precompiled for Erlang/OTP $InstalledOTPRelease. Otherwise, proceed."
${Else}
SetErrorlevel 5
MessageBox MB_ICONSTOP "Found existing Erlang/OTP $InstalledOTPRelease installation at $OTPPath but this Elixir version was precompiled for Erlang/OTP ${OTP_RELEASE}. \
Please upgrade your Erlang/OTP version or choose an Elixir installer matching your Erlang/OTP version"
Quit
${EndIf}
${Else}
SetErrorlevel 5
MessageBox MB_ICONSTOP "Found existing Erlang/OTP installation at $OTPPath but checking it exited with $0: $1"
Quit
${EndIf}
${EndIf}
nsDialogs::Show
FunctionEnd
Function OpenOTPDownloads
ExecShell "open" "https://www.erlang.org/downloads/${OTP_RELEASE}"
FunctionEnd
Function CheckOTPPageLeave
FunctionEnd
; Install Page: Files
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
; Install Page: Finish
Page custom FinishPageShow FinishPageLeave
var AddOTPToPathCheckbox
var AddElixirToPathCheckbox
Function FinishPageShow
!insertmacro MUI_HEADER_TEXT "Finish Setup" ""
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateCheckbox} 0 0 195u 10u "&Add $INSTDIR\bin to %PATH%"
Pop $AddElixirToPathCheckbox
SendMessage $AddElixirToPathCheckbox ${BM_SETCHECK} ${BST_CHECKED} 0
EnumRegKey $0 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang" 0
ReadRegStr $0 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang\$0" ""
StrCpy $OTPPath $0
${If} $0 != ""
${NSD_CreateCheckbox} 0 20u 195u 10u "&Add $0\bin to %PATH%"
Pop $AddOTPToPathCheckbox
SendMessage $AddOTPToPathCheckbox ${BM_SETCHECK} ${BST_CHECKED} 0
${EndIf}
nsDialogs::Show
FunctionEnd
Function FinishPageLeave
${NSD_GetState} $AddOTPToPathCheckbox $0
${If} $0 <> ${BST_UNCHECKED}
ReadRegStr $0 HKCU "Environment" "Path"
${StrStr} $1 "$0" "$OTPPath\bin"
${If} $1 == ""
WriteRegExpandStr HKCU "Environment" "Path" "$OTPPath\bin;$0"
${Else}
MessageBox MB_OK "$OTPPath\bin already in %PATH%"
${EndIf}
${EndIf}
${NSD_GetState} $AddElixirToPathCheckbox $0
${If} $0 <> ${BST_UNCHECKED}
ReadRegStr $0 HKCU "Environment" "Path"
${StrStr} $1 "$0" "$INSTDIR\bin"
${If} $1 == ""
WriteRegExpandStr HKCU "Environment" "Path" "$INSTDIR\bin;$0"
${Else}
MessageBox MB_OK "$INSTDIR\bin already in %PATH%"
${EndIf}
${EndIf}
FunctionEnd
Section "Install Elixir" SectionElixir
SetOutPath "$INSTDIR"
File /r "${ELIXIR_DIR}\"
WriteUninstaller "Uninstall.exe"
SectionEnd
; Uninstall Page: Files
!insertmacro MUI_UNPAGE_DIRECTORY
!insertmacro MUI_UNPAGE_INSTFILES
Section "Uninstall"
RMDir /r "$INSTDIR"
SectionEnd
; Uninstall Page: Finish
var RemoveOTPFromPathCheckbox
var RemoveElixirFromPathCheckbox
Function un.FinishPageShow
!insertmacro MUI_HEADER_TEXT "Finish Setup" ""
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
ReadRegStr $0 HKCU "Environment" "Path"
${UnStrStr} $1 "$0" "$INSTDIR\bin"
${If} $1 != ""
${NSD_CreateCheckbox} 0 0 195u 10u "&Remove $INSTDIR\bin from %PATH%"
Pop $RemoveElixirFromPathCheckbox
SendMessage $RemoveElixirFromPathCheckbox ${BM_SETCHECK} ${BST_CHECKED} 0
${EndIf}
EnumRegKey $1 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang" 0
ReadRegStr $1 HKLM "SOFTWARE\WOW6432NODE\Ericsson\Erlang\$1" ""
StrCpy $OTPPath $1
${UnStrStr} $1 "$0" "$OTPPath\bin"
${If} $1 != ""
${NSD_CreateCheckbox} 0 20u 195u 10u "&Remove $OTPPath\bin from %PATH%"
Pop $RemoveOTPFromPathCheckbox
SendMessage $RemoveOTPFromPathCheckbox ${BM_SETCHECK} ${BST_CHECKED} 0
${EndIf}
nsDialogs::Show
FunctionEnd
Function un.FinishPageLeave
ReadRegStr $0 HKCU "Environment" "Path"
${NSD_GetState} $RemoveOTPFromPathCheckbox $1
${If} $1 <> ${BST_UNCHECKED}
${UnStrRep} $0 "$0" "$OTPPath\bin;" ""
${EndIf}
${NSD_GetState} $RemoveElixirFromPathCheckbox $1
${If} $1 <> ${BST_UNCHECKED}
${UnStrRep} $0 "$0" "$INSTDIR\bin;" ""
${EndIf}
WriteRegExpandStr HKCU "Environment" "Path" "$0"
FunctionEnd
UninstPage custom un.FinishPageShow un.FinishPageLeave
!insertmacro MUI_LANGUAGE "English"
+5 -2
View File
@@ -510,12 +510,15 @@ get_moduledoc_meta(Set) ->
get_docs(Set, Module, Definitions, Kind) -> get_docs(Set, Module, Definitions, Kind) ->
[{Key, [{Key,
erl_anno:new(Line), maybe_generated(erl_anno:new(Line), Ctx),
[signature_to_binary(Module, Name, Signature)], [signature_to_binary(Module, Name, Signature)],
doc_value(Doc, Name), doc_value(Doc, Name),
Meta Meta
} || {Name, Arity} <- Definitions, } || {Name, Arity} <- Definitions,
{Key, _Ctx, Line, Signature, Doc, Meta} <- ets:lookup(Set, {Kind, Name, Arity})]. {Key, Ctx, Line, Signature, Doc, Meta} <- ets:lookup(Set, {Kind, Name, Arity})].
maybe_generated(Ann, nil) -> Ann;
maybe_generated(Ann, _Ctx) -> erl_anno:set_generated(true, Ann).
get_callback_docs(Set, Callbacks) -> get_callback_docs(Set, Callbacks) ->
[{Key, [{Key,
+3 -3
View File
@@ -1030,8 +1030,8 @@ extract_heredoc_with_interpolation(Line, Column, Scope, Interpol, T, H) ->
end; end;
error -> error ->
Message = "heredoc allows only zero or more whitespace characters followed by a new line after ", Message = "heredoc allows only whitespace characters followed by a new line after opening ",
{error, {Line, Column, io_lib:format(Message, []), [H, H, H]}} {error, {Line, Column + 3, io_lib:format(Message, []), [H, H, H]}}
end. end.
extract_heredoc_header("\r\n" ++ Rest) -> extract_heredoc_header("\r\n" ++ Rest) ->
@@ -1796,7 +1796,7 @@ prune_tokens([{OpType, _, _} | _] = Tokens, [], Terminators)
OpType =:= in_match_op; OpType =:= type_op; OpType =:= dual_op; OpType =:= mult_op; OpType =:= in_match_op; OpType =:= type_op; OpType =:= dual_op; OpType =:= mult_op;
OpType =:= power_op; OpType =:= concat_op; OpType =:= range_op; OpType =:= xor_op; OpType =:= power_op; OpType =:= concat_op; OpType =:= range_op; OpType =:= xor_op;
OpType =:= pipe_op; OpType =:= stab_op; OpType =:= when_op; OpType =:= assoc_op; OpType =:= pipe_op; OpType =:= stab_op; OpType =:= when_op; OpType =:= assoc_op;
OpType =:= rel_op; OpType =:= ternary_op -> OpType =:= rel_op; OpType =:= ternary_op; OpType =:= capture_op ->
{Tokens, Terminators}; {Tokens, Terminators};
%%% or we traverse until the end. %%% or we traverse until the end.
prune_tokens([_ | Tokens], Opener, Terminators) -> prune_tokens([_ | Tokens], Opener, Terminators) ->
@@ -1098,6 +1098,8 @@ defmodule CodeFragmentTest do
test "keeps operators" do test "keeps operators" do
assert cc2q("1 + 2") == s2q("1 + __cursor__()") assert cc2q("1 + 2") == s2q("1 + __cursor__()")
assert cc2q("&foo") == s2q("&__cursor__()")
assert cc2q("&foo/") == s2q("&foo/__cursor__()")
end end
test "keeps function calls without parens" do test "keeps function calls without parens" do
@@ -1200,11 +1202,6 @@ defmodule CodeFragmentTest do
assert cc2q("(fn x, y -> x + y end") == s2q("(__cursor__())") assert cc2q("(fn x, y -> x + y end") == s2q("(__cursor__())")
end end
test "removes captures" do
assert cc2q("[& &1") == s2q("[__cursor__()]")
assert cc2q("[&(&1") == s2q("[__cursor__()]")
end
test "removes closed terminators" do test "removes closed terminators" do
assert cc2q("foo([1, 2, 3]") == s2q("foo(__cursor__())") assert cc2q("foo([1, 2, 3]") == s2q("foo(__cursor__())")
assert cc2q("foo({1, 2, 3}") == s2q("foo(__cursor__())") assert cc2q("foo({1, 2, 3}") == s2q("foo(__cursor__())")
@@ -381,4 +381,30 @@ defmodule Kernel.DocsTest do
{{:fuz, 0}, :none} {{:fuz, 0}, :none}
] = Enum.sort(function_docs) ] = Enum.sort(function_docs)
end end
test "generated functions are annotated as such" do
write_beam(
defmodule ToBeUsed do
defmacro __using__(_) do
quote do
@doc "Hello"
def foo, do: :bar
end
end
end
)
write_beam(
defmodule WillBeUsing do
use ToBeUsed
end
)
{:docs_v1, _, _, _, _, _, docs} = Code.fetch_docs(WillBeUsing)
assert [
{{:function, :foo, 0}, [generated: true, location: 399], ["foo()"],
%{"en" => "Hello"}, %{}}
] = docs
end
end end
@@ -538,7 +538,7 @@ defmodule Kernel.ParserTest do
describe "syntax errors" do describe "syntax errors" do
test "invalid heredoc start" do test "invalid heredoc start" do
assert_syntax_error( assert_syntax_error(
~r/nofile:1:1: heredoc allows only zero or more whitespace characters followed by a new line after \"\"\"/, ~r/nofile:1:4: heredoc allows only whitespace characters followed by a new line after opening \"\"\"/,
~c"\"\"\"bar\n\"\"\"" ~c"\"\"\"bar\n\"\"\""
) )
end end
+2 -1
View File
@@ -293,8 +293,9 @@ defmodule ExUnit.Runner do
{test_module, invalid_tests, []} {test_module, invalid_tests, []}
{:DOWN, ^module_ref, :process, ^module_pid, error} -> {:DOWN, ^module_ref, :process, ^module_pid, error} ->
invalid_tests = Enum.map(tests, &%{&1 | state: {:invalid, test_module}})
test_module = %{test_module | state: failed({:EXIT, module_pid}, error, [])} test_module = %{test_module | state: failed({:EXIT, module_pid}, error, [])}
{test_module, [], []} {test_module, invalid_tests, []}
end end
timeout = get_timeout(config, %{}) timeout = get_timeout(config, %{})
@@ -124,6 +124,22 @@ defmodule ExUnit.CallbacksTest do
"** (MatchError) no match of right hand side value: :error" "** (MatchError) no match of right hand side value: :error"
end end
test "doesn't choke on setup_all exits" do
defmodule SetupAllExitTest do
use ExUnit.Case
setup_all _ do
Process.exit(self(), :error)
end
test "ok" do
assert true
end
end
assert capture_io(fn -> ExUnit.run() end) =~ "1 test, 0 failures, 1 invalid"
end
test "doesn't choke on dead supervisor" do test "doesn't choke on dead supervisor" do
defmodule StartSupervisedErrorTest do defmodule StartSupervisedErrorTest do
use ExUnit.Case use ExUnit.Case
+2 -2
View File
@@ -97,8 +97,8 @@ defmodule Mix.AppLoader do
defp extra_apps(config) do defp extra_apps(config) do
case Keyword.get(config, :language, :elixir) do case Keyword.get(config, :language, :elixir) do
:elixir -> [:ex_unit, :iex, :mix, :elixir] :elixir -> [:ex_unit, :iex, :mix, :elixir, :erts]
:erlang -> [:compiler] :erlang -> [:compiler, :erts]
end end
end end
+31 -14
View File
@@ -38,8 +38,9 @@ defmodule Mix.Tasks.Xref do
$ mix xref trace lib/my_app/router.ex --label compile $ mix xref trace lib/my_app/router.ex --label compile
If you have an umbrella application, we also recommend using the If you have an umbrella application, we also recommend using the
`--include-siblings` flag to see the dependencies on other `--include-siblings` flag to see the dependencies from sibling
umbrella applications. applications. The `trace` command is not currently supported at the
umbrella root.
### Example ### Example
@@ -222,6 +223,12 @@ defmodule Mix.Tasks.Xref do
lib/a.ex lib/a.ex
└── lib/b.ex (compile) └── lib/b.ex (compile)
If you have an umbrella application, we also recommend using the
`--include-siblings` flag to see the dependencies from sibling
applications. When invoked at the umbrella root, the `graph`
command will list all files from all umbrella children, without
any namespacing.
### Dependency types ### Dependency types
Elixir tracks three types of dependencies between modules: compile, Elixir tracks three types of dependencies between modules: compile,
@@ -289,12 +296,6 @@ defmodule Mix.Tasks.Xref do
@impl true @impl true
def run(args) do def run(args) do
if Mix.Project.umbrella?() do
Mix.raise(
"mix xref is not supported in the umbrella root. Please run it inside the umbrella applications instead"
)
end
Mix.Task.run("compile", args) Mix.Task.run("compile", args)
Mix.Task.reenable("xref") Mix.Task.reenable("xref")
@@ -302,9 +303,11 @@ defmodule Mix.Tasks.Xref do
case args do case args do
["callers", module] -> ["callers", module] ->
no_umbrella!("callers")
handle_callers(module, opts) handle_callers(module, opts)
["trace", file] -> ["trace", file] ->
no_umbrella!("trace")
handle_trace(file, opts) handle_trace(file, opts)
["graph"] -> ["graph"] ->
@@ -327,6 +330,14 @@ defmodule Mix.Tasks.Xref do
end end
end end
defp no_umbrella!(task) do
if Mix.Project.umbrella?() do
Mix.raise(
"mix xref #{task} is not supported in the umbrella root. Please run it inside the umbrella applications instead"
)
end
end
@doc """ @doc """
Returns a list of information of all the runtime function calls in the project. Returns a list of information of all the runtime function calls in the project.
@@ -1058,12 +1069,18 @@ defmodule Mix.Tasks.Xref do
defp manifests(opts) do defp manifests(opts) do
siblings = siblings =
if opts[:include_siblings] do cond do
for %{scm: Mix.SCM.Path, opts: opts} <- Mix.Dep.cached(), Mix.Project.umbrella?() ->
opts[:in_umbrella], for %{opts: opts} <- Mix.Dep.Umbrella.cached(),
do: Path.join([opts[:build], ".mix", @manifest]) do: Path.join([opts[:build], ".mix", @manifest])
else
[] opts[:include_siblings] ->
for %{scm: Mix.SCM.Path, opts: opts} <- Mix.Dep.cached(),
opts[:in_umbrella],
do: Path.join([opts[:build], ".mix", @manifest])
true ->
[]
end end
[Path.join(Mix.Project.manifest_path(), @manifest) | siblings] [Path.join(Mix.Project.manifest_path(), @manifest) | siblings]
+3
View File
@@ -335,6 +335,9 @@ defmodule Mix.Tasks.CompileTest do
in_fixture("no_mixfile", fn -> in_fixture("no_mixfile", fn ->
assert Mix.Task.run("compile", []) == {:ok, []} assert Mix.Task.run("compile", []) == {:ok, []}
assert :code.where_is_file(~c"parsetools.app") == :non_existing assert :code.where_is_file(~c"parsetools.app") == :non_existing
# Make sure erts is also kept
assert {:docs_v1, _, _, _, _, _, _} = Code.fetch_docs(:zlib)
end) end)
end end
+37
View File
@@ -915,6 +915,43 @@ defmodule Mix.Tasks.XrefTest do
end) end)
end end
test "generates reports from the umbrella root" do
Mix.Project.pop()
in_fixture("umbrella_dep/deps/umbrella", fn ->
Mix.Project.in_project(:umbrella, ".", fn _ ->
File.write!("apps/bar/lib/bar.ex", """
defmodule Bar do
def bar do
Foo.foo()
end
end
""")
Mix.Task.run("compile")
Mix.shell().flush()
Mix.Tasks.Xref.run(["graph", "--format", "stats", "--include-siblings"])
assert receive_until_no_messages([]) == """
Tracked files: 2 (nodes)
Compile dependencies: 0 (edges)
Exports dependencies: 0 (edges)
Runtime dependencies: 1 (edges)
Cycles: 0
Top 2 files with most outgoing dependencies:
* lib/bar.ex (1)
* lib/foo.ex (0)
Top 2 files with most incoming dependencies:
* lib/foo.ex (1)
* lib/bar.ex (0)
"""
end)
end)
end
test "generates reports considering siblings inside umbrellas" do test "generates reports considering siblings inside umbrellas" do
Mix.Project.pop() Mix.Project.pop()
+21 -18
View File
@@ -1,3 +1,16 @@
home = Path.expand("../tmp/.home", __DIR__)
File.mkdir_p!(home)
System.put_env("HOME", home)
mix = Path.expand("../tmp/.mix", __DIR__)
File.mkdir_p!(mix)
System.put_env("MIX_HOME", mix)
System.delete_env("XDG_DATA_HOME")
System.delete_env("XDG_CONFIG_HOME")
## Setup Mix
Mix.start() Mix.start()
Mix.shell(Mix.Shell.Process) Mix.shell(Mix.Shell.Process)
Application.put_env(:mix, :colors, enabled: false) Application.put_env(:mix, :colors, enabled: false)
@@ -5,6 +18,8 @@ Application.put_env(:mix, :colors, enabled: false)
Logger.remove_backend(:console) Logger.remove_backend(:console)
Application.put_env(:logger, :backends, []) Application.put_env(:logger, :backends, [])
## Setup ExUnit
os_exclude = if match?({:win32, _}, :os.type()), do: [unix: true], else: [windows: true] os_exclude = if match?({:win32, _}, :os.type()), do: [unix: true], else: [windows: true]
epmd_exclude = if match?({:win32, _}, :os.type()), do: [epmd: true], else: [] epmd_exclude = if match?({:win32, _}, :os.type()), do: [epmd: true], else: []
git_exclude = if Mix.SCM.Git.git_version() <= {1, 7, 4}, do: [git_sparse: true], else: [] git_exclude = if Mix.SCM.Git.git_version() <= {1, 7, 4}, do: [git_sparse: true], else: []
@@ -212,18 +227,7 @@ defmodule MixTest.Case do
end end
end end
## Set up globals ## Set up Rebar fixtures
home = MixTest.Case.tmp_path(".home")
File.mkdir_p!(home)
System.put_env("HOME", home)
mix = MixTest.Case.tmp_path(".mix")
File.mkdir_p!(mix)
System.put_env("MIX_HOME", mix)
System.delete_env("XDG_DATA_HOME")
System.delete_env("XDG_CONFIG_HOME")
rebar3_source = System.get_env("REBAR3") || Path.expand("fixtures/rebar3", __DIR__) rebar3_source = System.get_env("REBAR3") || Path.expand("fixtures/rebar3", __DIR__)
[major, minor | _] = String.split(System.version(), ".") [major, minor | _] = String.split(System.version(), ".")
@@ -231,8 +235,6 @@ rebar3_target = Path.join([mix, "elixir", "#{major}-#{minor}", "rebar3"])
File.mkdir_p!(Path.dirname(rebar3_target)) File.mkdir_p!(Path.dirname(rebar3_target))
File.cp!(rebar3_source, rebar3_target) File.cp!(rebar3_source, rebar3_target)
## Copy fixtures to tmp
fixtures = ~w(rebar_dep rebar_override) fixtures = ~w(rebar_dep rebar_override)
Enum.each(fixtures, fn fixture -> Enum.each(fixtures, fn fixture ->
@@ -242,12 +244,13 @@ Enum.each(fixtures, fn fixture ->
File.cp_r!(source, dest) File.cp_r!(source, dest)
end) end)
## Generate Git repo fixtures ## Set up Git fixtures
System.cmd("git", ~w[config --global user.email mix@example.com]) System.cmd("git", ~w[config --global user.email mix@example.com])
System.cmd("git", ~w[config --global user.name mix-repo]) System.cmd("git", ~w[config --global user.name mix-repo])
System.cmd("git", ~w[config --global init.defaultBranch not-main]) System.cmd("git", ~w[config --global init.defaultBranch not-main])
# Git repo ### Git repo
target = Path.expand("fixtures/git_repo", __DIR__) target = Path.expand("fixtures/git_repo", __DIR__)
unless File.dir?(target) do unless File.dir?(target) do
@@ -329,7 +332,7 @@ unless File.dir?(target) do
end) end)
end end
# Deps on Git repo ### Deps on Git repo
target = Path.expand("fixtures/deps_on_git_repo", __DIR__) target = Path.expand("fixtures/deps_on_git_repo", __DIR__)
unless File.dir?(target) do unless File.dir?(target) do
@@ -423,7 +426,7 @@ Enum.each([:invalidapp, :invalidvsn, :noappfile, :nosemver, :ok], fn dep ->
File.mkdir_p!(Path.expand("fixtures/deps_status/deps/#{dep}/.git", __DIR__)) File.mkdir_p!(Path.expand("fixtures/deps_status/deps/#{dep}/.git", __DIR__))
end) end)
# Archive ebin ### Archive ebin
target = Path.expand("fixtures/archive", __DIR__) target = Path.expand("fixtures/archive", __DIR__)
unless File.dir?(Path.join(target, "ebin")) do unless File.dir?(Path.join(target, "ebin")) do