Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7340ca2d92 | ||
|
|
3beb72e6f4 | ||
|
|
45f9c9c1ce | ||
|
|
cde12f75fc | ||
|
|
eb7e0984bd | ||
|
|
57a52b9a05 | ||
|
|
c352766f43 | ||
|
|
15222304eb | ||
|
|
f6aced3072 | ||
|
|
5e5eaca78f | ||
|
|
ab61c09b3d | ||
|
|
ccf939e9e7 | ||
|
|
331a70ade9 | ||
|
|
4329429823 | ||
|
|
05418eaa4b | ||
|
|
d1efe7c012 | ||
|
|
0494629c4c | ||
|
|
941ce758ed | ||
|
|
759457ecfa | ||
|
|
bfa894a15f | ||
|
|
407d2e1835 | ||
|
|
94e5c72793 | ||
|
|
d90c7bbb79 | ||
|
|
5b3e13c90b | ||
|
|
2021fed11a | ||
|
|
e74701ef07 | ||
|
|
284a7d00d6 | ||
|
|
a31549cc7d | ||
|
|
be6e54de16 | ||
|
|
6fd203ba7b | ||
|
|
0a677fe403 | ||
|
|
5b25761d4c | ||
|
|
e1f10a9386 | ||
|
|
37321cd529 | ||
|
|
8023b5e0c6 | ||
|
|
19d41f1fdb | ||
|
|
907149c8c7 | ||
|
|
c99e1cdc9b | ||
|
|
402a33867b | ||
|
|
1406d853e0 | ||
|
|
42f8a069c5 | ||
|
|
e060a458cb | ||
|
|
9aeaa61411 | ||
|
|
28ee5bf396 | ||
|
|
ba85d139fb | ||
|
|
039ca64890 | ||
|
|
705952be2d | ||
|
|
43b54f9a4b | ||
|
|
9dfc7a88b3 | ||
|
|
6ce02e6a8c | ||
|
|
463fd60f2e | ||
|
|
f2719b6fea | ||
|
|
bf3ef047dc | ||
|
|
896d0b4580 | ||
|
|
bb028704f7 | ||
|
|
b27db522e0 | ||
|
|
f5e4635087 | ||
|
|
288fb9ec73 | ||
|
|
f76f9f6112 |
+8
-1
@@ -23,7 +23,14 @@ matrix:
|
||||
- os: linux
|
||||
otp_release: 20.0
|
||||
|
||||
script: "make compile && rm -rf .git && make test"
|
||||
env:
|
||||
- ELIXIR_ASSERT_TIMEOUT=2000
|
||||
|
||||
script:
|
||||
- make compile
|
||||
- rm -rf .git
|
||||
- make test
|
||||
- dialyzer -pa lib/elixir/ebin --build_plt --output_plt elixir.plt --apps lib/elixir/ebin/elixir.beam lib/elixir/ebin/Elixir.Kernel.beam
|
||||
|
||||
notifications:
|
||||
recipients:
|
||||
|
||||
+92
-1
@@ -14,7 +14,17 @@ Or:
|
||||
|
||||
saudação = "Bom dia!"
|
||||
|
||||
Elixir follows the recommendations in [Unicode Annex #31](http://unicode.org/reports/tr31/) to make the language more accessible to other languages and communities. Identifiers must still be a sequence of letters, followed by digits and combining marks. This means symbols, such as mathematical notations and emoji, are not allowed identifiers.
|
||||
Elixir follows the recommendations in [Unicode Annex #31](http://unicode.org/reports/tr31/) to make the language more accessible to other languages and communities. Identifiers must start with a letter or underscore, optionally followed by letters, digits, and underscores. Here letter means any UTF-8 letter-character (optionally with a combining mark) and digit means a UTF-8 decimal-digit-character. If you're using ASCII, this does what you'd expect.
|
||||
|
||||
Examples of valid variables are:
|
||||
|
||||
name josé _age まつもと _42 адрес
|
||||
|
||||
Examples of invalid variables include:
|
||||
|
||||
name• a±2 42
|
||||
|
||||
Symbols, such as mathematical notations and emoji, are not allowed identifiers.
|
||||
|
||||
For a complete reference on Elixir syntax, see the [Syntax Reference](https://hexdocs.pm/elixir/syntax-reference.html). For technical details on Unicode support, see [Unicode Syntax](https://hexdocs.pm/elixir/unicode-syntax.html).
|
||||
|
||||
@@ -156,6 +166,86 @@ Overall, using `@impl` has the following advantages:
|
||||
|
||||
This release brings further improvements to Calendar types. It adds arithmetic and others functions to `Time`, `Date`, `NaiveDateTime` and `Datetime` as well as conversion between different calendars.
|
||||
|
||||
## v1.5.3 (2017-12-19)
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Calendar] Consider microseconds in maximum possible Calendar.ISO datetime
|
||||
* [Enum] Fix `chunk_every/4` when `step > count`
|
||||
* [Kernel] Warn duplicate definitions in macros
|
||||
* [Kernel] Remove dialyzer warnings from else in with clauses
|
||||
* [Kernel] Do not warning on upcoming `@deprecated` and `@since` attributes for v1.6
|
||||
* [MapSet] Return valid MapSet when union-ing a legacy MapSet
|
||||
|
||||
#### ExUnit
|
||||
|
||||
* [ExUnit] Fix compiler warnings in `assert_receive/3`
|
||||
|
||||
## v1.5.2 (2017-09-29)
|
||||
|
||||
### 1. Enhacements
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Kernel] Optimize function definition with multiple clauses by not traversing the internal clauses table
|
||||
* [Kernel] Warn if unary operators are followed by new lines
|
||||
* [Registry] Use the name of the Registry as its `:id` in the `child_spec/1` function
|
||||
|
||||
### 2. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [DateTime] Fix negative microsecond result when passing negative Unix epochs to `from_unix/2`
|
||||
* [Kernel] Improve error message for oversized atoms
|
||||
* [Kernel] Ensure `@impl` attribute also propagates to clauses from default arguments
|
||||
* [Kernel] Emit proper error for unknown vars inside binary pattern in match
|
||||
|
||||
#### IEx
|
||||
|
||||
* [IEx] Do not crash IEx unexpectedly on `System.stop/0`
|
||||
* [IEx.Helpers] Ensure exiting a breakpoint set inside a breakpoint does not terminate the shell unexpectedly
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix local.hex] Ensure `--if-missing` flag works as advertised
|
||||
* [mix test] Do not trigger additional error reports when there is a failure when loading test files
|
||||
* [Mix.SCM.Git] Ensure errors when invoking `git` propagate correctly
|
||||
|
||||
## v1.5.1 (2017-08-01)
|
||||
|
||||
### 1. Enhancements
|
||||
|
||||
#### EEx
|
||||
|
||||
* [EEx.Engine] Add `handle_begin` and `handle_end` to EEx
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Kernel] Do not use references on function/macro definitions - this provides large improvements in compilation times in some rare corner cases
|
||||
* [Supervisor] Support mixing old and new typespecs in `Supervisor.init/2` and `Supevisor.start_link/2`
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix profile] Allow profile tasks to run without a project
|
||||
|
||||
### 2. Bug fixes
|
||||
|
||||
#### EEx
|
||||
|
||||
* [EEx.Engine] Do not re-use the value of the `init/1` callback throughout the compilation stack
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Kernel] Ensure dialyzer does not emit warnings in some uses of `with`
|
||||
* [Kernel] Fix dialyzer warnings when `defmacrop` is used in modules
|
||||
* [Kernel] Ensure Elixir modules can be dialyzed without starting the Elixir application
|
||||
* [Kernel] Do not serialize references in quoted expressions
|
||||
* [Kernel] Make sure structs expansion use the latest definition available when struct modules are recompiled
|
||||
* [Task] Support `:infinity` timeout on Task streams
|
||||
* [Typespec] Ensure typespecs allow `tuple` to be used as variable names
|
||||
|
||||
## v1.5.0 (2017-07-25)
|
||||
|
||||
### 1. Enhancements
|
||||
@@ -263,6 +353,7 @@ This release brings further improvements to Calendar types. It adds arithmetic a
|
||||
#### ExUnit
|
||||
|
||||
* [ExUnit] Properly account failed tests when `setup_all` fails
|
||||
* [ExUnit] Having two or more `describe` blocks with the same name will now raise an error.
|
||||
|
||||
#### IEx
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@ defmodule EEx.Compiler do
|
||||
trim = opts[:trim] || false
|
||||
case EEx.Tokenizer.tokenize(source, line, trim: trim) do
|
||||
{:ok, tokens} ->
|
||||
state = %{engine: opts[:engine] || @default_engine, init: nil,
|
||||
state = %{engine: opts[:engine] || @default_engine,
|
||||
file: file, line: line, quoted: [], start_line: nil}
|
||||
init = state.engine.init(opts)
|
||||
generate_buffer(tokens, init, [], %{state | init: init})
|
||||
generate_buffer(tokens, init, [], state)
|
||||
{:error, line, message} ->
|
||||
raise EEx.SyntaxError, line: line, file: file, message: message
|
||||
end
|
||||
@@ -42,7 +42,7 @@ defmodule EEx.Compiler do
|
||||
defp generate_buffer([{:start_expr, start_line, mark, chars} | rest], buffer, scope, state) do
|
||||
{contents, line, rest} = look_ahead_text(rest, start_line, chars)
|
||||
{contents, rest} =
|
||||
generate_buffer(rest, state.init, [contents | scope],
|
||||
generate_buffer(rest, state.engine.handle_begin(buffer), [contents | scope],
|
||||
%{state | quoted: [], line: line, start_line: start_line})
|
||||
buffer = state.engine.handle_expr(buffer, IO.chardata_to_string(mark), contents)
|
||||
generate_buffer(rest, buffer, scope, state)
|
||||
@@ -50,7 +50,7 @@ defmodule EEx.Compiler do
|
||||
|
||||
defp generate_buffer([{:middle_expr, line, '', chars} | rest], buffer, [current | scope], state) do
|
||||
{wrapped, state} = wrap_expr(current, line, buffer, chars, state)
|
||||
generate_buffer(rest, state.init, [wrapped | scope], %{state | line: line})
|
||||
generate_buffer(rest, state.engine.handle_begin(buffer), [wrapped | scope], %{state | line: line})
|
||||
end
|
||||
|
||||
defp generate_buffer([{:middle_expr, line, modifier, chars} | t], buffer, scope, state) do
|
||||
@@ -99,7 +99,7 @@ defmodule EEx.Compiler do
|
||||
key = length(state.quoted)
|
||||
placeholder = '__EEX__(' ++ Integer.to_charlist(key) ++ ');'
|
||||
{current ++ placeholder ++ new_lines ++ chars,
|
||||
%{state | quoted: [{key, buffer} | state.quoted]}}
|
||||
%{state | quoted: [{key, state.engine.handle_end(buffer)} | state.quoted]}}
|
||||
end
|
||||
|
||||
# Look text ahead on expressions
|
||||
|
||||
+45
-12
@@ -2,19 +2,26 @@ defmodule EEx.Engine do
|
||||
@moduledoc ~S"""
|
||||
Basic EEx engine that ships with Elixir.
|
||||
|
||||
An engine needs to implement four functions:
|
||||
An engine needs to implement six functions:
|
||||
|
||||
* `init(opts)` - returns the initial buffer
|
||||
* `init(opts)` - called at the beginning of every text
|
||||
and it must return the initial state.
|
||||
|
||||
* `handle_body(quoted)` - receives the final built quoted
|
||||
expression, should do final post-processing and return a
|
||||
quoted expression.
|
||||
* `handle_body(state)` - receives the state of the document
|
||||
and it must return a quoted expression.
|
||||
|
||||
* `handle_text(buffer, text)` - it receives the buffer,
|
||||
* `handle_text(state, text)` - it receives the state,
|
||||
the text and must return a new quoted expression.
|
||||
|
||||
* `handle_expr(buffer, marker, expr)` - it receives the buffer,
|
||||
the marker, the expr and must return a new quoted expression.
|
||||
* `handle_expr(state, marker, expr)` - it receives the state,
|
||||
the marker, the expr and must return a new state.
|
||||
|
||||
* `handle_begin(state)` - called every time there a new state
|
||||
is needed with an empty buffer. Typically called for do/end
|
||||
blocks, case expressions, anonymous functions, etc
|
||||
|
||||
* `handle_end(state)` - opposite of `handle_begin(state)` and
|
||||
it must return quoted expression
|
||||
|
||||
The marker is what follows exactly after `<%`. For example,
|
||||
`<% foo %>` has an empty marker, but `<%= foo %>` has `"="`
|
||||
@@ -27,10 +34,14 @@ defmodule EEx.Engine do
|
||||
default implementations for the functions above.
|
||||
"""
|
||||
|
||||
@callback init(opts :: keyword) :: Macro.t
|
||||
@callback handle_body(quoted :: Macro.t) :: Macro.t
|
||||
@callback handle_text(buffer :: Macro.t, text :: String.t) :: Macro.t
|
||||
@callback handle_expr(buffer :: Macro.t, marker :: String.t, expr :: Macro.t) :: Macro.t
|
||||
@type state :: term
|
||||
|
||||
@callback init(opts :: keyword) :: state
|
||||
@callback handle_body(state) :: Macro.t
|
||||
@callback handle_text(state, text :: String.t) :: state
|
||||
@callback handle_expr(state, marker :: String.t, expr :: Macro.t) :: state
|
||||
@callback handle_begin(state) :: state
|
||||
@callback handle_end(state) :: Macro.t
|
||||
|
||||
@doc false
|
||||
defmacro __using__(_) do
|
||||
@@ -45,6 +56,14 @@ defmodule EEx.Engine do
|
||||
EEx.Engine.handle_body(quoted)
|
||||
end
|
||||
|
||||
def handle_begin(quoted) do
|
||||
EEx.Engine.handle_begin(quoted)
|
||||
end
|
||||
|
||||
def handle_end(quoted) do
|
||||
EEx.Engine.handle_end(quoted)
|
||||
end
|
||||
|
||||
def handle_text(buffer, text) do
|
||||
EEx.Engine.handle_text(buffer, text)
|
||||
end
|
||||
@@ -104,6 +123,20 @@ defmodule EEx.Engine do
|
||||
""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an empty string as the new buffer.
|
||||
"""
|
||||
def handle_begin(_previous) do
|
||||
""
|
||||
end
|
||||
|
||||
@doc """
|
||||
End of the new buffer.
|
||||
"""
|
||||
def handle_end(quoted) do
|
||||
quoted
|
||||
end
|
||||
|
||||
@doc """
|
||||
The default implementation simply returns the given expression.
|
||||
"""
|
||||
|
||||
@@ -436,25 +436,38 @@ defmodule EExTest do
|
||||
@behaviour EEx.Engine
|
||||
|
||||
def init(_opts) do
|
||||
""
|
||||
"INIT"
|
||||
end
|
||||
|
||||
def handle_body(body) do
|
||||
{:wrapped, body}
|
||||
"BODY(#{body})"
|
||||
end
|
||||
|
||||
def handle_begin(_) do
|
||||
"BEGIN"
|
||||
end
|
||||
|
||||
def handle_end(buffer) do
|
||||
buffer <> ":END"
|
||||
end
|
||||
|
||||
def handle_text(buffer, text) do
|
||||
EEx.Engine.handle_text(buffer, text)
|
||||
buffer <> ":TEXT(#{String.trim(text)})"
|
||||
end
|
||||
|
||||
def handle_expr(buffer, mark, expr) do
|
||||
EEx.Engine.handle_expr(buffer, mark, expr)
|
||||
def handle_expr(buffer, "=", expr) do
|
||||
buffer <> ":EQUAL(#{Macro.to_string(expr)})"
|
||||
end
|
||||
end
|
||||
|
||||
describe "custom engines" do
|
||||
test "calls handle_body" do
|
||||
assert {:wrapped, "foo"} = EEx.eval_string("foo", [], engine: TestEngine)
|
||||
test "text" do
|
||||
assert_eval "BODY(INIT:TEXT(foo))", "foo", [], engine: TestEngine
|
||||
end
|
||||
|
||||
test "begin/end" do
|
||||
assert_eval ~s[BODY(INIT:TEXT(foo):EQUAL(if() do\n "BEGIN:TEXT(this):END"\nelse\n "BEGIN:TEXT(that):END"\nend))],
|
||||
"foo <%= if do %>this<% else %>that<% end %>", [], engine: TestEngine
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -9,17 +9,57 @@ defmodule Application do
|
||||
Applications are defined with an application file named `APP.app` where
|
||||
`APP` is the application name, usually in `underscore_case`. The application
|
||||
file must reside in the same `ebin` directory as the compiled modules of the
|
||||
application.
|
||||
application. In Elixir, the Mix build tool is responsible for compiling your
|
||||
source code and generating your application `.app` file. You can learn more
|
||||
about the generation of `.app` files by typing `mix help compile.app`.
|
||||
|
||||
In Elixir, Mix is responsible for compiling your source code and
|
||||
generating your application `.app` file. Furthermore, Mix is also
|
||||
responsible for configuring, starting and stopping your application
|
||||
and its dependencies. For this reason, this documentation will focus
|
||||
on the remaining aspects of your application: the application environment
|
||||
and the application callback module.
|
||||
Once your application is compiled, running your system is a matter of starting
|
||||
your current application and its dependencies. Differently from other languages,
|
||||
Elixir does not have a `main` procedure that is responsible for starting your
|
||||
system. Instead, you start one or more applications, each with their own
|
||||
initialization and termination logic.
|
||||
|
||||
You can learn more about Mix generation of `.app` files by typing
|
||||
`mix help compile.app`.
|
||||
Starting an application is done via the "application module callback", which
|
||||
is a module that defines the `start/2` function. The `start/2` function should
|
||||
then start a supervisor, which is often called as the top-level supervisor, since
|
||||
it sits at the root of a potentially long supervision tree. When the system is
|
||||
shutting down, all applications shut down their top-level supervisor, which
|
||||
terminates children in the opposite order they are started.
|
||||
|
||||
We have mentioned the Mix build tool is responsible for compiling applications,
|
||||
but it is also capable of running applications. For example, `mix test`
|
||||
automatically starts your application dependencies and your application itself
|
||||
before your test runs. `mix run --no-halt` also boots your current project and
|
||||
can be used to start a long running system. See `mix help run`.
|
||||
|
||||
Developers can also use tools like [Distillery](https://github.com/bitwalker/distillery)
|
||||
that build **releases**. Releases are able to package all of your source code
|
||||
as well as the Erlang VM into a single directory. Releases also give you explicit
|
||||
control over how each application is started and in which order. They also provide
|
||||
a more streamlined mechanism for starting and stopping systems, debugging, logging,
|
||||
as well as system monitoring.
|
||||
|
||||
Finally, Elixir provides tools such as escripts and archives, which are
|
||||
different mechanisms for packaging your application. Those are typically used
|
||||
when tools must be shared between developers and not as deployment options.
|
||||
See `mix help archive.build` and `mix help escript.build` for more detail.
|
||||
|
||||
Shutting down a live system cleanly can be done by calling `System.stop/1`.
|
||||
It will shut down all applications in the opposite order they are started.
|
||||
Each application will then shutdown its top-level supervisor, if one is
|
||||
available, which then shuts down its children.
|
||||
|
||||
From Erlang/OTP 19.1, a SIGTERM from the operating system will automatically
|
||||
translate to `System.stop/0`. Erlang/OTP 20 gives user more explicit control
|
||||
over OS signals via the `:os.set_signal/2` function.
|
||||
|
||||
Applications also provide an "application environment", which is how
|
||||
applications are configured. The application environment can either be set
|
||||
statically, via a configuration file, or dynamically via `put_env/3` and
|
||||
friends.
|
||||
|
||||
Over the next sections, we will cover the "application environment" and
|
||||
the "application module callback" in more detail.
|
||||
|
||||
## Application environment
|
||||
|
||||
@@ -40,9 +80,13 @@ defmodule Application do
|
||||
Application.get_env(:APP_NAME, :hello)
|
||||
#=> :world
|
||||
|
||||
It is also possible to put and delete values from the application value,
|
||||
including new values that are not defined in the environment file (although
|
||||
this should be avoided).
|
||||
Applications and dependencies in Mix projects are typically configured
|
||||
via the `config/config.exs` file. For example, someone using your
|
||||
application can configure the `:hello` key as follows:
|
||||
|
||||
config :APP_NAME, hello: :brand_new_world
|
||||
|
||||
It is also possible to configure applications dynamically via `put_env/3`.
|
||||
|
||||
Keep in mind that each application is responsible for its environment.
|
||||
Do not use the functions in this module for directly accessing or modifying
|
||||
@@ -78,8 +122,8 @@ defmodule Application do
|
||||
|
||||
The `type` argument passed to `start/2` is usually `:normal` unless in a
|
||||
distributed setup where application takeovers and failovers are configured.
|
||||
This particular aspect of applications is explained in more detail in the
|
||||
OTP documentation:
|
||||
Distributed applications is beyond the scope of this documentation. For those
|
||||
interested on the topic, please access the OTP documentation:
|
||||
|
||||
* [`:application` module](http://www.erlang.org/doc/man/application.html)
|
||||
* [Applications – OTP Design Principles](http://www.erlang.org/doc/design_principles/applications.html)
|
||||
|
||||
@@ -221,15 +221,9 @@ defmodule Date do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Parses the extended "Date and time of day" format described by
|
||||
Parses the extended "Dates" format described by
|
||||
[ISO 8601:2004](https://en.wikipedia.org/wiki/ISO_8601).
|
||||
|
||||
Timezone offset may be included in the string but they will be
|
||||
simply discarded as such information is not included in naive date
|
||||
times.
|
||||
|
||||
Time representations with reduced accuracy are not supported.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Date.from_iso8601("2015-01-23")
|
||||
@@ -261,7 +255,7 @@ defmodule Date do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Parses the extended "Date and time of day" format described by
|
||||
Parses the extended "Dates" format described by
|
||||
[ISO 8601:2004](https://en.wikipedia.org/wiki/ISO_8601).
|
||||
|
||||
Raises if the format is invalid.
|
||||
|
||||
@@ -326,25 +326,25 @@ defmodule DateTime do
|
||||
@spec to_iso8601(Calendar.datetime, :extended | :basic ) :: String.t
|
||||
def to_iso8601(datetime, format \\ :extended)
|
||||
|
||||
def to_iso8601(_, format) when format not in [:extended, :basic] do
|
||||
raise ArgumentError, "DateTime.to_iso8601/2 expects format to be :extended or :basic, got: #{inspect format}"
|
||||
end
|
||||
|
||||
def to_iso8601(%{calendar: Calendar.ISO, year: year, month: month, day: day,
|
||||
hour: hour, minute: minute, second: second, microsecond: microsecond,
|
||||
time_zone: time_zone, zone_abbr: zone_abbr, utc_offset: utc_offset, std_offset: std_offset}, format) when format in [:extended, :basic] do
|
||||
time_zone: time_zone, zone_abbr: zone_abbr, utc_offset: utc_offset, std_offset: std_offset}, format) do
|
||||
Calendar.ISO.datetime_to_iso8601(year, month, day, hour, minute, second, microsecond,
|
||||
time_zone, zone_abbr, utc_offset, std_offset, format)
|
||||
end
|
||||
|
||||
def to_iso8601(%{calendar: _, year: _, month: _, day: _,
|
||||
hour: _, minute: _, second: _, microsecond: _,
|
||||
time_zone: _, zone_abbr: _, utc_offset: _, std_offset: _} = datetime, format) when format in [:extended, :basic] do
|
||||
time_zone: _, zone_abbr: _, utc_offset: _, std_offset: _} = datetime, format) do
|
||||
datetime
|
||||
|> convert!(Calendar.ISO)
|
||||
|> to_iso8601(format)
|
||||
end
|
||||
|
||||
def to_iso8601(_, format) do
|
||||
raise ArgumentError, "DateTime.to_iso8601/2 expects format to be :extended or :basic, got: #{inspect format}"
|
||||
end
|
||||
|
||||
@doc """
|
||||
Parses the extended "Date and time of day" format described by
|
||||
[ISO 8601:2004](https://en.wikipedia.org/wiki/ISO_8601).
|
||||
|
||||
@@ -17,7 +17,7 @@ defmodule Calendar.ISO do
|
||||
|
||||
@unix_epoch 62167219200
|
||||
@unix_start 1_000_000 * -@unix_epoch
|
||||
@unix_end 1_000_000 * (315569519999 - @unix_epoch)
|
||||
@unix_end 315569519999999999 - @unix_epoch * 1_000_000
|
||||
@unix_range_microseconds @unix_start..@unix_end
|
||||
|
||||
@type year :: 0..9999
|
||||
@@ -122,13 +122,13 @@ defmodule Calendar.ISO do
|
||||
def date_to_iso_days_days(1970, 1, 1) do
|
||||
719528
|
||||
end
|
||||
def date_to_iso_days_days(year, month, day) do
|
||||
def date_to_iso_days_days(year, month, day) when year <= 9999 do
|
||||
:calendar.date_to_gregorian_days(year, month, day)
|
||||
end
|
||||
|
||||
# Converts count of days since 0000-01-01 to {year, month, day} tuple.
|
||||
@doc false
|
||||
def date_from_iso_days_days(days) do
|
||||
def date_from_iso_days_days(days) when days <= 3652424 do
|
||||
:calendar.gregorian_days_to_date(days)
|
||||
end
|
||||
|
||||
@@ -314,10 +314,11 @@ defmodule Calendar.ISO do
|
||||
def from_unix(integer, unit) when is_integer(integer) do
|
||||
total = System.convert_time_unit(integer, unit, :microsecond)
|
||||
if total in @unix_range_microseconds do
|
||||
microsecond = rem(total, 1_000_000)
|
||||
microseconds = Integer.mod(total, @microseconds_per_second)
|
||||
seconds = @unix_epoch + Integer.floor_div(total, @microseconds_per_second)
|
||||
precision = precision_for_unit(unit)
|
||||
{date, time} = :calendar.gregorian_seconds_to_datetime(@unix_epoch + div(total, 1_000_000))
|
||||
{:ok, date, time, {microsecond, precision}}
|
||||
{date, time} = :calendar.gregorian_seconds_to_datetime(seconds)
|
||||
{:ok, date, time, {microseconds, precision}}
|
||||
else
|
||||
{:error, :invalid_unix_time}
|
||||
end
|
||||
|
||||
+19
-1
@@ -362,6 +362,9 @@ defmodule Enum do
|
||||
iex> Enum.chunk_every([1, 2, 3, 4], 10)
|
||||
[[1, 2, 3, 4]]
|
||||
|
||||
iex> Enum.chunk_every([1, 2, 3, 4, 5], 2, 3, [])
|
||||
[[1, 2], [4, 5]]
|
||||
|
||||
"""
|
||||
@spec chunk_every(t, pos_integer, pos_integer, t | :discard) :: [list]
|
||||
def chunk_every(enumerable, count, step, leftover \\ [])
|
||||
@@ -832,13 +835,28 @@ defmodule Enum do
|
||||
Filters the enumerable, i.e. returns only those elements
|
||||
for which `fun` returns a truthy value.
|
||||
|
||||
See also `reject/2`.
|
||||
See also `reject/2` which discards all elements where the
|
||||
function returns true.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.filter([1, 2, 3], fn(x) -> rem(x, 2) == 0 end)
|
||||
[2]
|
||||
|
||||
Keep in mind that `filter` is not capable of filtering and
|
||||
transforming an element at the same time. If you would like
|
||||
to do so, consider using `flat_map/2`. For example, if you
|
||||
want to convert all strings that represent an integer and
|
||||
discard the invalid one in one pass:
|
||||
|
||||
strings = ["1234", "abc", "12ab"]
|
||||
Enum.flat_map(strings, fn string ->
|
||||
case Integer.parse(string) do
|
||||
{int, _rest} -> [int] # transform to integer
|
||||
:error -> [] # skip the value
|
||||
end
|
||||
end)
|
||||
|
||||
"""
|
||||
@spec filter(t, (element -> as_boolean(term))) :: list
|
||||
def filter(enumerable, fun) when is_list(enumerable) do
|
||||
|
||||
@@ -955,7 +955,7 @@ defmodule File do
|
||||
def rm_rf(path) do
|
||||
path
|
||||
|> IO.chardata_to_string()
|
||||
|> assert_no_null_byte!("File.cp_r/3")
|
||||
|> assert_no_null_byte!("File.rm_rf/1")
|
||||
|> do_rm_rf({:ok, []})
|
||||
end
|
||||
|
||||
|
||||
@@ -3565,7 +3565,6 @@ defmodule Kernel do
|
||||
defp define(kind, call, expr, env) do
|
||||
module = assert_module_scope(env, kind, 2)
|
||||
assert_no_function_scope(env, kind, 2)
|
||||
table = :elixir_module.data_table(module)
|
||||
|
||||
{escaped_call, unquoted_call} = :elixir_quote.escape(call, true)
|
||||
{escaped_expr, unquoted_expr} = :elixir_quote.escape(expr, true)
|
||||
@@ -3575,14 +3574,14 @@ defmodule Kernel do
|
||||
true ->
|
||||
escaped_expr
|
||||
false ->
|
||||
key = {:cache_def, :erlang.unique_integer()}
|
||||
:ets.insert(table, {key, expr})
|
||||
quote do: :ets.lookup_element(unquote(table), unquote(key), 2)
|
||||
key = :erlang.unique_integer()
|
||||
:elixir_module.write_cache(module, key, expr)
|
||||
quote do: :elixir_module.read_cache(unquote(module), unquote(key))
|
||||
end
|
||||
|
||||
# Do not check clauses if any expression was unquoted
|
||||
check_clauses = not(unquoted_expr or unquoted_call)
|
||||
pos = :elixir_locals.cache_env(table, env)
|
||||
pos = :elixir_locals.cache_env(env)
|
||||
|
||||
quote do
|
||||
:elixir_def.store_definition(unquote(kind), unquote(check_clauses),
|
||||
|
||||
@@ -903,7 +903,7 @@ defmodule Kernel.Typespec do
|
||||
end
|
||||
|
||||
# Handle tuples
|
||||
defp typespec({:tuple, meta, args}, _vars, _caller) when args == [] or is_atom(args) do
|
||||
defp typespec({:tuple, meta, []}, _vars, _caller) do
|
||||
{:type, line(meta), :tuple, :any}
|
||||
end
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ defmodule Macro do
|
||||
valid_alias?(charlist) ->
|
||||
:alias
|
||||
true ->
|
||||
case :elixir_config.get(:identifier_tokenizer).tokenize(charlist) do
|
||||
case :elixir_config.safe_get(:identifier_tokenizer, String.Tokenizer).tokenize(charlist) do
|
||||
{kind, _acc, [], _, _, special} ->
|
||||
if kind == :identifier and not :lists.member(?@, special) do
|
||||
:callable
|
||||
|
||||
@@ -353,7 +353,8 @@ defmodule MapSet do
|
||||
%{map_set | map: Map.merge(map1, map2)}
|
||||
end
|
||||
def union(%MapSet{map: map1}, %MapSet{map: map2}) do
|
||||
new_from_list(Map.keys(map1) ++ Map.keys(map2), [])
|
||||
map = new_from_list(Map.keys(map1) ++ Map.keys(map2), [])
|
||||
%MapSet{map: map}
|
||||
end
|
||||
|
||||
defp order_by_size(map1, map2) when map_size(map1) > map_size(map2), do: {map2, map1}
|
||||
|
||||
@@ -898,7 +898,7 @@ defmodule Module do
|
||||
"cannot make function #{function_name}/#{arity} overridable because it was not defined"
|
||||
clause ->
|
||||
neighbours =
|
||||
if :elixir_compiler.get_opt(:internal) do
|
||||
if :elixir_config.get(:bootstrap) do
|
||||
[]
|
||||
else
|
||||
Module.LocalsTracker.yank(module, tuple)
|
||||
@@ -1192,8 +1192,13 @@ defmodule Module do
|
||||
case :ets.take(table, :impl) do
|
||||
[{:impl, value, _, _}] ->
|
||||
impls = :ets.lookup_element(table, {:elixir, :impls}, 2)
|
||||
impl = {{name, length(args)}, kind, line, file, value}
|
||||
:ets.insert(table, {{:elixir, :impls}, [impl | impls]})
|
||||
{total, defaults} = args_count(args, 0, 0)
|
||||
|
||||
impl = for arity <- total..(total - defaults), into: impls do
|
||||
{{name, arity}, kind, line, file, value}
|
||||
end
|
||||
|
||||
:ets.insert(table, {{:elixir, :impls}, impl})
|
||||
[] ->
|
||||
:ok
|
||||
end
|
||||
@@ -1201,6 +1206,14 @@ defmodule Module do
|
||||
:ok
|
||||
end
|
||||
|
||||
defp args_count([{:\\, _, _} | tail], total, defaults) do
|
||||
args_count(tail, total + 1, defaults + 1)
|
||||
end
|
||||
defp args_count([_head | tail], total, defaults) do
|
||||
args_count(tail, total + 1, defaults)
|
||||
end
|
||||
defp args_count([], total, defaults), do: {total, defaults}
|
||||
|
||||
@doc false
|
||||
def check_behaviours_and_impls(env, table, all_definitions, overridable_pairs) do
|
||||
behaviours = :ets.lookup_element(table, :behaviour, 2)
|
||||
|
||||
@@ -138,8 +138,8 @@ defmodule Module.LocalsTracker do
|
||||
|
||||
# Reattach a previously yanked node
|
||||
@doc false
|
||||
def reattach(pid, kind, tuple, neighbours) do
|
||||
:gen_server.cast(to_pid(pid), {:reattach, kind, tuple, neighbours})
|
||||
def reattach(pid, tuple, kind, function, neighbours) do
|
||||
:gen_server.cast(to_pid(pid), {:reattach, tuple, kind, function, neighbours})
|
||||
end
|
||||
|
||||
# Collecting all conflicting imports with the given functions
|
||||
@@ -162,15 +162,25 @@ defmodule Module.LocalsTracker do
|
||||
def collect_unused_locals(ref, private) do
|
||||
d = :gen_server.call(to_pid(ref), :digraph, @timeout)
|
||||
reachable = reachable_from(d, :local)
|
||||
{unreachable(reachable, private), collect_warnings(reachable, private)}
|
||||
reattached = :digraph.out_neighbours(d, :reattach)
|
||||
{unreachable(reachable, reattached, private), collect_warnings(reachable, private)}
|
||||
end
|
||||
|
||||
defp unreachable(reachable, private) do
|
||||
for {tuple, _, _, _} <- private,
|
||||
not :sets.is_element(tuple, reachable),
|
||||
defp unreachable(reachable, reattached, private) do
|
||||
for {tuple, kind, _, _} <- private,
|
||||
not reachable?(tuple, kind, reachable, reattached),
|
||||
do: tuple
|
||||
end
|
||||
|
||||
defp reachable?(tuple, :defmacrop, reachable, reattached) do
|
||||
# All private micros are unreachable unless they have been
|
||||
# reattached and they are reachable.
|
||||
:lists.member(tuple, reattached) and :sets.is_element(tuple, reachable)
|
||||
end
|
||||
defp reachable?(tuple, :defp, reachable, _reattached) do
|
||||
:sets.is_element(tuple, reachable)
|
||||
end
|
||||
|
||||
defp collect_warnings(reachable, private) do
|
||||
:lists.foldl(&collect_warnings(&1, &2, reachable), [], private)
|
||||
end
|
||||
@@ -221,6 +231,7 @@ defmodule Module.LocalsTracker do
|
||||
def init([]) do
|
||||
d = :digraph.new([:protected])
|
||||
:digraph.add_vertex(d, :local)
|
||||
:digraph.add_vertex(d, :reattach)
|
||||
{:ok, d}
|
||||
end
|
||||
|
||||
@@ -262,17 +273,29 @@ defmodule Module.LocalsTracker do
|
||||
{:noreply, d}
|
||||
end
|
||||
|
||||
def handle_cast({:reattach, _kind, tuple, {in_neigh, out_neigh}}, d) do
|
||||
def handle_cast({:reattach, tuple, kind, function, {in_neigh, out_neigh}}, d) do
|
||||
# Reattach the old function
|
||||
for from <- in_neigh do
|
||||
:digraph.add_vertex(d, from)
|
||||
replace_edge!(d, from, tuple)
|
||||
replace_edge!(d, from, function)
|
||||
end
|
||||
|
||||
for to <- out_neigh do
|
||||
:digraph.add_vertex(d, to)
|
||||
replace_edge!(d, tuple, to)
|
||||
replace_edge!(d, function, to)
|
||||
end
|
||||
|
||||
# Add the new definition
|
||||
handle_add_definition(d, kind, tuple)
|
||||
|
||||
# Make a call from the old function to the new one
|
||||
if function != tuple do
|
||||
handle_add_local(d, function, tuple)
|
||||
end
|
||||
|
||||
# Finally marked the new one as reattached
|
||||
replace_edge!(d, :reattach, tuple)
|
||||
|
||||
{:noreply, d}
|
||||
end
|
||||
|
||||
|
||||
@@ -304,7 +304,7 @@ defmodule Registry do
|
||||
@doc false
|
||||
def child_spec(opts) do
|
||||
%{
|
||||
id: Registry,
|
||||
id: Keyword.get(opts, :name, Registry),
|
||||
start: {Registry, :start_link, [opts]},
|
||||
type: :supervisor
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ defmodule Stream.Reducers do
|
||||
{:cont, new_state}
|
||||
end
|
||||
end, fn {acc_buffer, acc_count} ->
|
||||
if leftover == :discard or acc_count == 0 do
|
||||
if leftover == :discard or acc_count == 0 or (step > count and acc_count >= count) do
|
||||
{:cont, []}
|
||||
else
|
||||
{:cont, :lists.reverse(acc_buffer, Enum.take(leftover, count - acc_count)), []}
|
||||
|
||||
@@ -82,7 +82,7 @@ defmodule Supervisor do
|
||||
supervisor will automatically start a new one, with the initial stack
|
||||
of `[:hello]`:
|
||||
|
||||
GenServer.call(MyStack, :pop)
|
||||
GenServer.call(Stack, :pop)
|
||||
#=> :hello
|
||||
|
||||
Supervisors support different strategies; in the example above, we
|
||||
@@ -187,7 +187,7 @@ defmodule Supervisor do
|
||||
`GenServer` with a shutdown limit of 10 seconds (10_000 miliseconds),
|
||||
one might do:
|
||||
|
||||
use GenServer, shutdown: 10000
|
||||
use GenServer, shutdown: 10_000
|
||||
|
||||
Let's understand what the `:shutdown` and `:restart` options control.
|
||||
|
||||
@@ -333,14 +333,14 @@ defmodule Supervisor do
|
||||
The first argument given to `start_link/2` is a list of children which may
|
||||
be either:
|
||||
|
||||
* a module - such as `Stack`. In this case, it is equivalent to passing
|
||||
`{Stack, []}` (which means `Stack.child_spec/1` is invoked with an empty
|
||||
keywords list)
|
||||
* a tuple with a module as first element and the start argument as second -
|
||||
such as `{Stack, [:hello]}`. When such format is used, the supervisor
|
||||
will retrieve the child specification from the given module.
|
||||
* a map representing the child specification itself - such as the child
|
||||
specification map outlined in the previous section.
|
||||
* a module - such as `Stack`. In this case, it is equivalent to passing
|
||||
`{Stack, []}` (which means `Stack.child_spec/1` is invoked with an empty
|
||||
keywords list)
|
||||
* a tuple with a module as first element and the start argument as second -
|
||||
such as `{Stack, [:hello]}`. When such format is used, the supervisor
|
||||
will retrieve the child specification from the given module.
|
||||
* a map representing the child specification itself - such as the child
|
||||
specification map outlined in the previous section.
|
||||
|
||||
The second argument is a keyword list of options:
|
||||
|
||||
@@ -528,7 +528,7 @@ defmodule Supervisor do
|
||||
process and exits not only on crashes but also if the parent process exits
|
||||
with `:normal` reason.
|
||||
"""
|
||||
@spec start_link([child_spec | {module, term} | module], options) :: on_start
|
||||
@spec start_link([:supervisor.child_spec | {module, term} | module], options) :: on_start
|
||||
def start_link(children, options) when is_list(children) do
|
||||
{sup_opts, start_opts} = Keyword.split(options, [:strategy, :max_seconds, :max_restarts])
|
||||
start_link(Supervisor.Default, {children, sup_opts}, start_opts)
|
||||
@@ -570,7 +570,7 @@ defmodule Supervisor do
|
||||
is allowed within 5 seconds. Check the `Supervisor` module for a detailed
|
||||
description of the available strategies.
|
||||
"""
|
||||
@spec init([child_spec | {module, term} | module], flag) :: {:ok, tuple}
|
||||
@spec init([:supervisor.child_spec | {module, term} | module], flag) :: {:ok, tuple}
|
||||
def init(children, options) when is_list(children) and is_list(options) do
|
||||
unless strategy = options[:strategy] do
|
||||
raise ArgumentError, "expected :strategy option to be given"
|
||||
@@ -591,12 +591,7 @@ defmodule Supervisor do
|
||||
e in UndefinedFunctionError ->
|
||||
case System.stacktrace do
|
||||
[{^module, :child_spec, [^arg], _} | _] ->
|
||||
raise ArgumentError,
|
||||
"The module #{inspect module} was given as a child to a supervisor but it " <>
|
||||
"does not implement child_spec/1. If you own the given module, please define " <>
|
||||
"a child_spec/1 function that receives an argument and returns a child " <>
|
||||
"specification. If not, pass a map child specification instead of the " <>
|
||||
"given module to the supervisor. See the Supervisor module for documentation."
|
||||
raise ArgumentError, child_spec_error(module)
|
||||
stack ->
|
||||
reraise e, stack
|
||||
end
|
||||
@@ -605,10 +600,59 @@ defmodule Supervisor do
|
||||
defp init_child(map) when is_map(map) do
|
||||
map
|
||||
end
|
||||
defp init_child({_, _, _, _, _, _} = tuple) do
|
||||
tuple
|
||||
end
|
||||
defp init_child(other) do
|
||||
raise ArgumentError,
|
||||
"supervisors expect the child to be a module, a {module, arg} tuple or " <>
|
||||
"a map with the child specification, got: #{inspect other}"
|
||||
raise ArgumentError, """
|
||||
supervisors expect each child to be one of:
|
||||
|
||||
* a module
|
||||
* a {module, arg} tuple
|
||||
* a child specification as a map with at least the :id and :start fields
|
||||
* or a tuple with 6 elements generated by Supervisor.Spec (deprecated)
|
||||
|
||||
Got: #{inspect other}
|
||||
"""
|
||||
end
|
||||
|
||||
defp child_spec_error(module) do
|
||||
if Code.ensure_loaded?(module) do
|
||||
"""
|
||||
The module #{inspect module} was given as a child to a supervisor
|
||||
but it does not implement child_spec/1.
|
||||
|
||||
If you own the given module, please define a child_spec/1 function
|
||||
that receives an argument and returns a child specification as a map.
|
||||
For example:
|
||||
|
||||
def child_spec(opts) do
|
||||
%{
|
||||
id: __MODULE__,
|
||||
start: {__MODULE__, :start_link, [opts]},
|
||||
type: :worker,
|
||||
restart: :permanent,
|
||||
shutdown: 500
|
||||
}
|
||||
end
|
||||
|
||||
Note that "use Agent", "use GenServer" and so on automatically define
|
||||
this function for you.
|
||||
|
||||
However, if you don't own the given module and it doesn't implement
|
||||
child_spec/1, instead of passing the module name directly as a supervisor
|
||||
child, you will have to pass a child specification as a map:
|
||||
|
||||
%{
|
||||
id: #{inspect module},
|
||||
start: {#{inspect module}, :start_link, [arg1, arg2]}
|
||||
}
|
||||
|
||||
See the Supervisor documentation for more information.
|
||||
"""
|
||||
else
|
||||
"The module #{inspect module} was given as a child to a supervisor but it does not exist."
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -645,7 +689,14 @@ defmodule Supervisor do
|
||||
start: {Agent, :start_link, []}}
|
||||
|
||||
"""
|
||||
@spec child_spec(child_spec | {module, arg :: term} | module, keyword) :: child_spec()
|
||||
@spec child_spec(child_spec() | {module, arg :: term} | module, keyword) :: child_spec()
|
||||
def child_spec(module_or_map, overrides)
|
||||
|
||||
def child_spec({_, _, _, _, _, _} = tuple, _overrides) do
|
||||
raise ArgumentError, "old tuple-based child specification #{inspect tuple} " <>
|
||||
"is not supported in Supervisor.child_spec/2"
|
||||
end
|
||||
|
||||
def child_spec(module_or_map, overrides) do
|
||||
Enum.reduce overrides, init_child(module_or_map), fn
|
||||
{key, value}, acc when key in [:id, :start, :restart, :shutdown, :type, :modules] ->
|
||||
@@ -731,7 +782,7 @@ defmodule Supervisor do
|
||||
"""
|
||||
# TODO: Once we add DynamicSupervisor, we need to enforce receiving
|
||||
# a map here and deprecate the list and tuple formats.
|
||||
@spec start_child(supervisor, child_spec | [term]) :: on_start_child
|
||||
@spec start_child(supervisor, :supervisor.child_spec | [term]) :: on_start_child
|
||||
def start_child(supervisor, child_spec_or_args) do
|
||||
call(supervisor, {:start_child, child_spec_or_args})
|
||||
end
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
defmodule Supervisor.Default do
|
||||
@moduledoc false
|
||||
|
||||
def init({[{_, _, _, _, _, _} | _] = children, opts}) do
|
||||
Supervisor.Spec.supervise(children, opts)
|
||||
end
|
||||
|
||||
def init({children, opts}) do
|
||||
Supervisor.init(children, opts)
|
||||
end
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
defmodule Supervisor.Spec do
|
||||
@moduledoc """
|
||||
NOTE: The functions in this module are deprecated. Please see
|
||||
`Supervisor` instead.
|
||||
NOTE: The functions in this module are deprecated and they do not
|
||||
work with the module-based child specs introduced in Elixir v1.5.
|
||||
Please see the `Supervisor` documentation instead.
|
||||
|
||||
Convenience functions for defining supervisor specifications.
|
||||
|
||||
@@ -172,10 +173,21 @@ defmodule Supervisor.Spec do
|
||||
maxR = Keyword.get(options, :max_restarts, 3)
|
||||
maxS = Keyword.get(options, :max_seconds, 5)
|
||||
|
||||
assert_unique_ids(Enum.map(children, &elem(&1, 0)))
|
||||
assert_unique_ids(Enum.map(children, &get_id/1))
|
||||
{:ok, {{strategy, maxR, maxS}, children}}
|
||||
end
|
||||
|
||||
defp get_id({id, _, _, _, _, _}) do
|
||||
id
|
||||
end
|
||||
defp get_id(other) do
|
||||
raise ArgumentError,
|
||||
"invalid tuple specification given to supervise/2. If you are trying to use " <>
|
||||
"the map child specification that is part of the Elixir v1.5, use Supervisor.init/2 " <>
|
||||
"instead of Supervisor.Spec.supervise/2. See the Supervisor module for more information. " <>
|
||||
"Got: #{inspect other}"
|
||||
end
|
||||
|
||||
defp assert_unique_ids([id | rest]) do
|
||||
if id in rest do
|
||||
raise ArgumentError,
|
||||
|
||||
@@ -345,9 +345,8 @@ defmodule Task do
|
||||
terminates all tasks.
|
||||
|
||||
When streamed, each task will emit `{:ok, value}` upon successful
|
||||
completion or `{:exit, reason, element}` if the caller is trapping
|
||||
exits, where `element` is the stream element. Results are emitted
|
||||
in the same order as the original `enumerable`.
|
||||
completion or `{:exit, reason}` if the caller is trapping exits.
|
||||
Results are emitted in the same order as the original `enumerable`.
|
||||
|
||||
The level of concurrency can be controlled via the `:max_concurrency`
|
||||
option and defaults to `System.schedulers_online/0`. A timeout
|
||||
@@ -373,8 +372,7 @@ defmodule Task do
|
||||
values are:
|
||||
* `:exit` (default) - the process that spawned the tasks exits.
|
||||
* `:kill_task` - the task that timed out is killed. The value
|
||||
emitted for that task is `{:exit, :timeout, element}`, where
|
||||
`element` is the element it timed out on.
|
||||
emitted for that task is `{:exit, :timeout}`.
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -405,7 +405,13 @@ defmodule Task.Supervised do
|
||||
{:spawn, position, value} ->
|
||||
{type, pid} = spawn.(parent_pid, normalize_mfa_with_arg(mfa, value))
|
||||
ref = Process.monitor(pid)
|
||||
timer_ref = Process.send_after(self(), {:timeout, {monitor_ref, ref}}, timeout)
|
||||
|
||||
# Schedule a timeout message to ourselves, unless the timeout was set to :infinity
|
||||
timer_ref = case timeout do
|
||||
:infinity -> nil
|
||||
timeout -> Process.send_after(self(), {:timeout, {monitor_ref, ref}}, timeout)
|
||||
end
|
||||
|
||||
send(parent_pid, {:spawned, {monitor_ref, position}, pid})
|
||||
task_info = %{
|
||||
position: position,
|
||||
@@ -443,7 +449,9 @@ defmodule Task.Supervised do
|
||||
# this and keep going.
|
||||
{:DOWN, ref, _, _, reason} ->
|
||||
{%{position: position, timer_ref: timer_ref, timed_out?: timed_out?}, running_tasks} = Map.pop(running_tasks, ref)
|
||||
:ok = Process.cancel_timer(timer_ref, async: true, info: false)
|
||||
if timer_ref != nil do
|
||||
:ok = Process.cancel_timer(timer_ref, async: true, info: false)
|
||||
end
|
||||
message_kind = if(timed_out?, do: :timed_out, else: :down)
|
||||
send(parent_pid, {message_kind, {monitor_ref, position}, reason})
|
||||
stream_monitor_loop(running_tasks, config)
|
||||
|
||||
@@ -153,9 +153,8 @@ defmodule Task.Supervisor do
|
||||
linked to the current process, similarly to `async/4`.
|
||||
|
||||
When streamed, each task will emit `{:ok, value}` upon successful
|
||||
completion or `{:exit, reason, element}` if the caller is trapping
|
||||
exits, where `element` is the stream element. Results are emitted
|
||||
in the same order as the original `enumerable`.
|
||||
completion or `{:exit, reason}` if the caller is trapping exits.
|
||||
Results are emitted in the same order as the original `enumerable`.
|
||||
|
||||
The level of concurrency can be controlled via the `:max_concurrency`
|
||||
option and defaults to `System.schedulers_online/0`. A timeout
|
||||
@@ -185,8 +184,7 @@ defmodule Task.Supervisor do
|
||||
values are:
|
||||
* `:exit` (default) - the process that spawned the tasks exits.
|
||||
* `:kill_task` - the task that timed out is killed. The value
|
||||
emitted for that task is `{:exit, :timeout, element}`, where
|
||||
`element` is the element it timed out on.
|
||||
emitted for that task is `{:exit, :timeout}`.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ defmodule URI do
|
||||
"""
|
||||
@spec default_port(binary) :: nil | non_neg_integer
|
||||
def default_port(scheme) when is_binary(scheme) do
|
||||
:elixir_config.get({:uri, scheme})
|
||||
:elixir_config.safe_get({:uri, scheme}, nil)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
@@ -82,13 +82,13 @@ end
|
||||
|
||||
### Strings
|
||||
|
||||
Strings in Elixir are written between double-quotes, such as `"foo"`. Any double-quote inside the string must be escaped with `\`. Strings support Unicode characters and are stored in UTF-8 encoding.
|
||||
Strings in Elixir are written between double-quotes, such as `"foo"`. Any double-quote inside the string must be escaped with `\ `. Strings support Unicode characters and are stored in UTF-8 encoding.
|
||||
|
||||
Strings are always represented as themselves in the AST.
|
||||
|
||||
### Charlists
|
||||
|
||||
Charlists in Elixir are written in single-quotes, such as `'foo'`. Any single-quote inside the string must be escaped with `\`. Charlists are a list of integers, each integer representing a Unicode character.
|
||||
Charlists in Elixir are written in single-quotes, such as `'foo'`. Any single-quote inside the string must be escaped with `\ `. Charlists are a list of integers, each integer representing a Unicode character.
|
||||
|
||||
Charlists are always represented as themselves in the AST.
|
||||
|
||||
|
||||
@@ -102,8 +102,10 @@ start(_Type, _Args) ->
|
||||
relative_paths => true},
|
||||
{ok, [[Home] | _]} = init:get_argument(home),
|
||||
Config = [{at_exit, []},
|
||||
{home, unicode:characters_to_binary(Home, Encoding, Encoding)},
|
||||
{argv, []},
|
||||
{bootstrap, false},
|
||||
{compiler_options, CompilerOpts},
|
||||
{home, unicode:characters_to_binary(Home, Encoding, Encoding)},
|
||||
{identifier_tokenizer, Tokenizer}
|
||||
| URIConfig],
|
||||
Tab = elixir_config:new(Config),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
-include("elixir.hrl").
|
||||
|
||||
inspect(Atom) when is_atom(Atom) ->
|
||||
case elixir_compiler:get_opt(internal) of
|
||||
case elixir_config:get(bootstrap) of
|
||||
true -> atom_to_binary(Atom, utf8);
|
||||
false -> 'Elixir.Inspect.Atom':inspect(Atom)
|
||||
end.
|
||||
|
||||
@@ -22,8 +22,8 @@ expand(BitstrMeta, Fun, [{'::', Meta, [Left, Right]} | T], Acc, E, RequireSize)
|
||||
%% on subparts, however we can't assign new variables.
|
||||
{ER, MatchSize} =
|
||||
case E of
|
||||
{EExtracted, _} -> {EExtracted, false}; %% expand_arg, no assigns
|
||||
_ -> {E#{context := nil}, T /= []} %% expand, revert assigns
|
||||
{EExtracted, _} -> {EExtracted, false}; %% expand_arg, no assigns
|
||||
_ -> {E#{context := nil, match_vars := warn}, T /= []} %% expand, revert assigns
|
||||
end,
|
||||
|
||||
ERight = expand_specs(expr_type(ELeft), Meta, Right, ER, RequireSize or MatchSize),
|
||||
|
||||
@@ -125,9 +125,9 @@ allows_fast_compilation(_) -> false.
|
||||
|
||||
bootstrap() ->
|
||||
{ok, _} = application:ensure_all_started(elixir),
|
||||
Update = fun(Old) -> maps:merge(Old, #{docs => false, internal => true,
|
||||
relative_paths => false}) end,
|
||||
Update = fun(Old) -> maps:merge(Old, #{docs => false, relative_paths => false}) end,
|
||||
_ = elixir_config:update(compiler_options, Update),
|
||||
_ = elixir_config:put(bootstrap, true),
|
||||
[bootstrap_file(File) || File <- bootstrap_main()].
|
||||
|
||||
bootstrap_file(File) ->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
-module(elixir_config).
|
||||
-compile({no_auto_import, [get/1]}).
|
||||
-export([new/1, delete/1, put/2, get/1, update/2, get_and_put/2]).
|
||||
-export([new/1, delete/1, put/2, get/1, safe_get/2, update/2, get_and_put/2]).
|
||||
-export([start_link/0, init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, code_change/3, terminate/2]).
|
||||
-behaviour(gen_server).
|
||||
@@ -19,9 +19,15 @@ put(Key, Value) ->
|
||||
gen_server:call(?MODULE, {put, Key, Value}).
|
||||
|
||||
get(Key) ->
|
||||
case ets:lookup(?MODULE, Key) of
|
||||
[{_, Value}] = ets:lookup(?MODULE, Key),
|
||||
Value.
|
||||
|
||||
safe_get(Key, Default) ->
|
||||
try ets:lookup(?MODULE, Key) of
|
||||
[{_, Value}] -> Value;
|
||||
[] -> nil
|
||||
[] -> Default
|
||||
catch
|
||||
_:_ -> Default
|
||||
end.
|
||||
|
||||
update(Key, Fun) ->
|
||||
|
||||
@@ -34,10 +34,11 @@ local_for(Module, Name, Arity, Kinds) ->
|
||||
|
||||
%% Take a definition out of the table
|
||||
|
||||
take_definition(Module, Tuple) ->
|
||||
take_definition(Module, {Name, Arity} = Tuple) ->
|
||||
Table = elixir_module:defs_table(Module),
|
||||
case ets:take(Table, {def, Tuple}) of
|
||||
[Result] ->
|
||||
[{{def, Tuple}, _, _, _, _, {Defaults, _, _}} = Result] ->
|
||||
ets:delete_object(Table, {{default, Name}, Arity, Defaults}),
|
||||
{Result, [Clause || {_, Clause} <- ets:take(Table, {clauses, Tuple})]};
|
||||
[] ->
|
||||
false
|
||||
@@ -58,8 +59,8 @@ fetch_definition([[Tuple] | T], File, Module, Table, All, Private) ->
|
||||
|
||||
try ets:lookup_element(Table, {clauses, Tuple}, 2) of
|
||||
Clauses ->
|
||||
Unwrapped =
|
||||
{Tuple, Kind, Meta, Clauses},
|
||||
NewAll =
|
||||
[{Tuple, Kind, Meta, Clauses} | All],
|
||||
NewPrivate =
|
||||
case (Kind == defp) orelse (Kind == defmacrop) of
|
||||
true ->
|
||||
@@ -68,7 +69,7 @@ fetch_definition([[Tuple] | T], File, Module, Table, All, Private) ->
|
||||
false ->
|
||||
Private
|
||||
end,
|
||||
fetch_definition(T, File, Module, Table, [Unwrapped | All], NewPrivate)
|
||||
fetch_definition(T, File, Module, Table, NewAll, NewPrivate)
|
||||
catch
|
||||
error:badarg ->
|
||||
warn_bodyless_function(Check, Meta, File, Module, Kind, Tuple),
|
||||
@@ -95,6 +96,10 @@ store_definition(Kind, CheckClauses, Call, Body, Pos) ->
|
||||
%% extract meta information like file and context.
|
||||
{_, Meta, _} = Call,
|
||||
DoCheckClauses = (not lists:keymember(context, 1, Meta)) andalso (CheckClauses),
|
||||
Generated = case lists:keyfind(generated, 1, Meta) of
|
||||
{generated, true} -> ?generated([]);
|
||||
_ -> []
|
||||
end,
|
||||
|
||||
%% Check if there is a file information in the definition.
|
||||
%% If so, we assume this come from another source and
|
||||
@@ -113,7 +118,6 @@ store_definition(Kind, CheckClauses, Call, Body, Pos) ->
|
||||
LinifyArgs = elixir_quote:linify(Line, Key, Args),
|
||||
LinifyGuards = elixir_quote:linify(Line, Key, Guards),
|
||||
LinifyBody = elixir_quote:linify(Line, Key, Body),
|
||||
Generated = case DoCheckClauses of true -> []; false -> ?generated([]) end,
|
||||
|
||||
{EL, MetaLocation} =
|
||||
case retrieve_location(Location, ?key(E, module)) of
|
||||
@@ -186,6 +190,13 @@ store_definition(Check, Kind, Meta, Name, Arity, File, Module, Defaults, Clauses
|
||||
Tuple = {Name, Arity},
|
||||
HasBody = Clauses =/= [],
|
||||
|
||||
if
|
||||
Defaults > 0 ->
|
||||
ets:insert(Defs, {{default, Name}, Arity, Defaults});
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
|
||||
MaxDefaults =
|
||||
case ets:take(Defs, {def, Tuple}) of
|
||||
[{_, StoredKind, StoredMeta, StoredFile, StoredCheck,
|
||||
@@ -194,14 +205,14 @@ store_definition(Check, Kind, Meta, Name, Arity, File, Module, Defaults, Clauses
|
||||
(Check and StoredCheck) andalso
|
||||
check_valid_clause(Meta, File, Name, Arity, Kind, Data, StoredMeta, StoredFile),
|
||||
check_valid_defaults(Meta, File, Name, Arity, Kind, Defaults, StoredDefaults, LastDefaults, LastHasBody),
|
||||
{max(Defaults, StoredDefaults), HasBody, Defaults};
|
||||
max(Defaults, StoredDefaults);
|
||||
[] ->
|
||||
{Defaults, HasBody, Defaults}
|
||||
Defaults
|
||||
end,
|
||||
|
||||
Check andalso ets:insert(Data, {?last_def, Tuple}),
|
||||
ets:insert(Defs, [{{clauses, Tuple}, Clause} || Clause <- Clauses]),
|
||||
ets:insert(Defs, {{def, Tuple}, Kind, Meta, File, Check, MaxDefaults}).
|
||||
ets:insert(Defs, {{def, Tuple}, Kind, Meta, File, Check, {MaxDefaults, HasBody, Defaults}}).
|
||||
|
||||
%% Handling of defaults
|
||||
|
||||
@@ -271,7 +282,7 @@ check_valid_defaults(Meta, File, Name, Arity, Kind, Defaults, 0, 0, _) when Defa
|
||||
check_valid_defaults(Meta, File, Name, Arity, Kind, 0, _, LastDefaults, true) when LastDefaults > 0 ->
|
||||
elixir_errors:form_warn(Meta, File, ?MODULE, {clauses_with_defaults, {Kind, Name, Arity}});
|
||||
% Clause without defaults
|
||||
check_valid_defaults(_Meta, _File, _Name, _Arity, _Kind, 0, _, _, _) -> [].
|
||||
check_valid_defaults(_Meta, _File, _Name, _Arity, _Kind, 0, _, _, _) -> ok.
|
||||
|
||||
warn_bodyless_function(Check, _Meta, _File, Module, _Kind, _Tuple)
|
||||
when Check == false; Module == 'Elixir.Module' ->
|
||||
@@ -289,12 +300,11 @@ invalid_arg({Name, _, Kind}) when is_atom(Name), is_atom(Kind) -> false;
|
||||
invalid_arg(_) -> true.
|
||||
|
||||
check_previous_defaults(Meta, Module, Name, Arity, Kind, Defaults, E) ->
|
||||
Matches = ets:match(elixir_module:defs_table(Module),
|
||||
{{def, {Name, '$2'}}, '$1', '_', '_', '_', {'$3', '_', '_'}}),
|
||||
Matches = ets:lookup(elixir_module:defs_table(Module), {default, Name}),
|
||||
[begin
|
||||
elixir_errors:form_error(Meta, ?key(E, file), ?MODULE,
|
||||
{defs_with_defaults, Name, {Kind, Arity}, {K, A}})
|
||||
end || [K, A, D] <- Matches, A /= Arity, D /= 0, defaults_conflict(A, D, Arity, Defaults)].
|
||||
{defs_with_defaults, Kind, Name, Arity, A})
|
||||
end || {_, A, D} <- Matches, A /= Arity, D /= 0, defaults_conflict(A, D, Arity, Defaults)].
|
||||
|
||||
defaults_conflict(A, D, Arity, Defaults) ->
|
||||
((Arity >= (A - D)) andalso (Arity < A)) orelse
|
||||
@@ -324,13 +334,13 @@ format_error({bodyless_clause, Kind, {Name, Arity}}) ->
|
||||
format_error({no_module, {Kind, Name, Arity}}) ->
|
||||
io_lib:format("cannot define function outside module, invalid scope for ~ts ~ts/~B", [Kind, Name, Arity]);
|
||||
|
||||
format_error({defs_with_defaults, Name, {Kind, Arity}, {K, A}}) when Arity > A ->
|
||||
io_lib:format("~ts ~ts/~B defaults conflicts with ~ts ~ts/~B",
|
||||
[Kind, Name, Arity, K, Name, A]);
|
||||
format_error({defs_with_defaults, Kind, Name, Arity, A}) when Arity > A ->
|
||||
io_lib:format("~ts ~ts/~B defaults conflicts with ~ts/~B",
|
||||
[Kind, Name, Arity, Name, A]);
|
||||
|
||||
format_error({defs_with_defaults, Name, {Kind, Arity}, {K, A}}) when Arity < A ->
|
||||
io_lib:format("~ts ~ts/~B conflicts with defaults from ~ts ~ts/~B",
|
||||
[Kind, Name, Arity, K, Name, A]);
|
||||
format_error({defs_with_defaults, Kind, Name, Arity, A}) when Arity < A ->
|
||||
io_lib:format("~ts ~ts/~B conflicts with defaults from ~ts/~B",
|
||||
[Kind, Name, Arity, Name, A]);
|
||||
|
||||
format_error({clauses_with_defaults, {Kind, Name, Arity}}) ->
|
||||
io_lib:format(""
|
||||
|
||||
@@ -121,9 +121,9 @@ elixir_to_erl(Function) when is_function(Function) ->
|
||||
false ->
|
||||
error(badarg)
|
||||
end;
|
||||
elixir_to_erl(PidOrRef) when is_pid(PidOrRef); is_reference(PidOrRef) ->
|
||||
elixir_to_erl(Pid) when is_pid(Pid) ->
|
||||
elixir_erl:remote(0, erlang, binary_to_term,
|
||||
[elixir_erl:elixir_to_erl(term_to_binary(PidOrRef))]);
|
||||
[elixir_erl:elixir_to_erl(term_to_binary(Pid))]);
|
||||
elixir_to_erl(_Other) ->
|
||||
error(badarg).
|
||||
|
||||
@@ -153,7 +153,7 @@ compile(#{module := Module} = Map) ->
|
||||
Data = elixir_module:data_table(Module),
|
||||
{Prefix, Forms, Defmacro, Unreachable} = dynamic_form(Map),
|
||||
Specs =
|
||||
case elixir_compiler:get_opt(internal) of
|
||||
case elixir_config:get(bootstrap) of
|
||||
true -> [];
|
||||
false -> specs_form(Data, Defmacro, Unreachable, types_form(Data, []))
|
||||
end,
|
||||
|
||||
@@ -58,7 +58,7 @@ handle_file_warning(_, _File, {_Line, erl_lint, {shadowed_var, _Var, _Where}}) -
|
||||
handle_file_warning(_, _File, {_Line, erl_lint, {exported_var, _Var, _Where}}) -> ok;
|
||||
|
||||
handle_file_warning(_, File, {Line, erl_lint, {undefined_behaviour, Module}}) ->
|
||||
case elixir_compiler:get_opt(internal) of
|
||||
case elixir_config:get(bootstrap) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
|
||||
@@ -371,10 +371,31 @@ translate_with_else(Meta, [{else, Else}], S) ->
|
||||
RaiseExpr = {{'.', Generated, [erlang, error]}, Generated, [{with_clause, RaiseVar}]},
|
||||
RaiseClause = {'->', Generated, [[RaiseVar], RaiseExpr]},
|
||||
|
||||
Case = {'case', [{export_vars, false} | Meta], [ElseVarEx, [{do, Else ++ [RaiseClause]}]]},
|
||||
GeneratedElse = [build_generated_clause(Generated, ElseClause) || ElseClause <- Else],
|
||||
|
||||
Case = {'case', [{export_vars, false} | Generated], [ElseVarEx, [{do, GeneratedElse ++ [RaiseClause]}]]},
|
||||
{TranslatedCase, SC} = elixir_erl_pass:translate(Case, SV),
|
||||
{{clause, ?ann(Generated), [ElseVarErl], [], [TranslatedCase]}, SC}.
|
||||
|
||||
build_generated_clause(Generated, {'->', _, [Args, Clause]}) ->
|
||||
NewArgs = [build_generated_clause_arg(Generated, Arg) || Arg <- Args],
|
||||
{'->', Generated, [NewArgs, Clause]}.
|
||||
|
||||
build_generated_clause_arg(Generated, Arg) ->
|
||||
{Expr, Guards} = elixir_utils:extract_guards(Arg),
|
||||
NewGuards = [build_generated_guard(Generated, Guard) || Guard <- Guards],
|
||||
concat_guards(Generated, Expr, NewGuards).
|
||||
|
||||
build_generated_guard(Generated, {{'.', _, _} = Call, _, Args}) ->
|
||||
{Call, Generated, [build_generated_guard(Generated, Arg) || Arg <- Args]};
|
||||
build_generated_guard(_, Expr) ->
|
||||
Expr.
|
||||
|
||||
concat_guards(_Meta, Expr, []) ->
|
||||
Expr;
|
||||
concat_guards(Meta, Expr, [Guard | Tail]) ->
|
||||
{'when', Meta, [Expr, concat_guards(Meta, Guard, Tail)]}.
|
||||
|
||||
translate_with_do([{'<-', Meta, [Left, Expr]} | Rest], Do, Else, S) ->
|
||||
{Args, Guards} = elixir_utils:extract_guards(Left),
|
||||
{TExpr, SR} = elixir_erl_pass:translate(Expr, S),
|
||||
@@ -382,9 +403,8 @@ translate_with_do([{'<-', Meta, [Left, Expr]} | Rest], Do, Else, S) ->
|
||||
TGuards = elixir_erl_clauses:guards(Guards, [], SA),
|
||||
{TBody, SB} = translate_with_do(Rest, Do, Else, SA),
|
||||
|
||||
Ann = ?ann(?generated(Meta)),
|
||||
Clause = {clause, Ann, [TArgs], TGuards, unblock(TBody)},
|
||||
{{'case', Ann, TExpr, [Clause, Else]}, SB};
|
||||
Clause = {clause, ?ann(Meta), [TArgs], TGuards, unblock(TBody)},
|
||||
{{'case', ?ann(?generated(Meta)), TExpr, [Clause, Else]}, SB};
|
||||
translate_with_do([Expr | Rest], Do, Else, S) ->
|
||||
{TExpr, TS} = elixir_erl_pass:translate(Expr, S),
|
||||
{TRest, RS} = translate_with_do(Rest, Do, Else, TS),
|
||||
|
||||
@@ -448,15 +448,15 @@ expand(Function, E) when is_function(Function) ->
|
||||
end;
|
||||
|
||||
|
||||
expand(PidOrRef, E) when is_pid(PidOrRef); is_reference(PidOrRef) ->
|
||||
expand(Pid, E) when is_pid(Pid) ->
|
||||
case ?key(E, function) of
|
||||
nil ->
|
||||
{PidOrRef, E};
|
||||
{Pid, E};
|
||||
Function ->
|
||||
%% TODO: Make me an error on 2.0
|
||||
elixir_errors:form_warn([], ?key(E, file), ?MODULE,
|
||||
{invalid_pid_or_ref_in_function, PidOrRef, Function}),
|
||||
{PidOrRef, E}
|
||||
{invalid_pid_in_function, Pid, Function}),
|
||||
{Pid, E}
|
||||
end;
|
||||
|
||||
expand(Other, E) when is_number(Other); is_atom(Other); is_binary(Other) ->
|
||||
@@ -949,9 +949,9 @@ format_error({invalid_local_invocation, Context, {Name, _, Args} = Call}) ->
|
||||
format_error({invalid_remote_invocation, Context, Receiver, Right, Arity}) ->
|
||||
io_lib:format("cannot invoke remote function ~ts.~ts/~B inside ~ts",
|
||||
['Elixir.Macro':to_string(Receiver), Right, Arity, Context]);
|
||||
format_error({invalid_pid_or_ref_in_function, PidOrRef, {Name, Arity}}) ->
|
||||
io_lib:format("cannot compile PID/Reference ~ts inside quoted expression for function ~ts/~B",
|
||||
['Elixir.Kernel':inspect(PidOrRef, []), Name, Arity]);
|
||||
format_error({invalid_pid_in_function, Pid, {Name, Arity}}) ->
|
||||
io_lib:format("cannot compile PID ~ts inside quoted expression for function ~ts/~B",
|
||||
['Elixir.Kernel':inspect(Pid, []), Name, Arity]);
|
||||
format_error({unsupported_option, Kind, Key}) ->
|
||||
io_lib:format("unsupported option ~ts given to ~s",
|
||||
['Elixir.Macro':to_string(Key), Kind]);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
-define(tracker, 'Elixir.Kernel.LexicalTracker').
|
||||
|
||||
run(File, Dest, Callback) ->
|
||||
case elixir_compiler:get_opt(internal) of
|
||||
case elixir_config:get(bootstrap) of
|
||||
false ->
|
||||
{ok, Pid} = ?tracker:start_link(Dest),
|
||||
try Callback(Pid) of
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
%% Module responsible for tracking invocations of module calls.
|
||||
-module(elixir_locals).
|
||||
-export([
|
||||
setup/1, cleanup/1, cache_env/1, cache_env/2, get_cached_env/1,
|
||||
setup/1, cleanup/1, cache_env/1, get_cached_env/1,
|
||||
record_local/2, record_local/3, record_import/4,
|
||||
record_definition/3, record_defaults/4,
|
||||
record_definition/3, record_defaults/4, reattach/5,
|
||||
ensure_no_import_conflict/3, warn_unused_local/3, format_error/1
|
||||
]).
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
-define(tracker, 'Elixir.Module.LocalsTracker').
|
||||
|
||||
setup(Module) ->
|
||||
case elixir_compiler:get_opt(internal) of
|
||||
case elixir_config:get(bootstrap) of
|
||||
false ->
|
||||
{ok, Pid} = ?tracker:start_link(),
|
||||
ets:insert(elixir_module:data_table(Module), {?attr, Pid}),
|
||||
@@ -25,6 +25,9 @@ setup(Module) ->
|
||||
cleanup(Module) ->
|
||||
if_tracker(Module, fun(Pid) -> unlink(Pid), ?tracker:stop(Pid), ok end).
|
||||
|
||||
reattach(Tuple, Kind, Module, Function, Neighbours) ->
|
||||
if_tracker(Module, fun(Pid) -> ?tracker:reattach(Pid, Tuple, Kind, Function, Neighbours) end).
|
||||
|
||||
record_local(Tuple, Module) when is_atom(Module) ->
|
||||
if_tracker(Module, fun(Pid) -> ?tracker:add_local(Pid, Tuple), ok end).
|
||||
record_local(Tuple, _Module, Function)
|
||||
@@ -57,10 +60,8 @@ if_tracker(Module, Default, Callback) ->
|
||||
|
||||
%% CACHING
|
||||
|
||||
cache_env(#{module := Module} = E) ->
|
||||
cache_env(elixir_module:data_table(Module), E).
|
||||
|
||||
cache_env(Table, #{line := Line} = E) ->
|
||||
cache_env(#{line := Line, module := Module} = E) ->
|
||||
Table = elixir_module:data_table(Module),
|
||||
Cache = E#{line := nil, vars := []},
|
||||
|
||||
Pos =
|
||||
@@ -74,10 +75,10 @@ cache_env(Table, #{line := Line} = E) ->
|
||||
Key
|
||||
end,
|
||||
|
||||
{Table, {Line, Pos}}.
|
||||
{Module, {Line, Pos}}.
|
||||
|
||||
get_cached_env({Table, {Line, Pos}}) ->
|
||||
(ets:lookup_element(Table, {cache_env, Pos}, 2))#{line := Line};
|
||||
get_cached_env({Module, {Line, Pos}}) ->
|
||||
(ets:lookup_element(elixir_module:data_table(Module), {cache_env, Pos}, 2))#{line := Line};
|
||||
get_cached_env(Env) ->
|
||||
Env.
|
||||
|
||||
|
||||
@@ -121,10 +121,7 @@ validate_struct(_, _) -> false.
|
||||
|
||||
load_struct(Meta, Name, Args, InContext, E) ->
|
||||
Arity = length(Args),
|
||||
|
||||
Local =
|
||||
not(ensure_loaded(Name)) andalso
|
||||
(InContext orelse wait_for_struct(Name)),
|
||||
Local = InContext orelse (not(ensure_loaded(Name)) andalso wait_for_struct(Name)),
|
||||
|
||||
try
|
||||
case Local andalso elixir_def:local_for(Name, '__struct__', Arity, [def]) of
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
-module(elixir_module).
|
||||
-export([data_table/1, defs_table/1, is_open/1, delete_doc/6,
|
||||
compile/4, expand_callback/6, format_error/1,
|
||||
compiler_modules/0, delete_impl/6]).
|
||||
compiler_modules/0, delete_impl/6,
|
||||
write_cache/3, read_cache/2]).
|
||||
-include("elixir.hrl").
|
||||
|
||||
-define(lexical_attr, {elixir, lexical_tracker}).
|
||||
@@ -39,6 +40,12 @@ delete_impl(#{module := Module}, _, _, _, _, _) ->
|
||||
ets:delete(data_table(Module), impl),
|
||||
ok.
|
||||
|
||||
write_cache(Module, Key, Value) ->
|
||||
ets:insert(data_table(Module), {{cache, Key}, Value}).
|
||||
|
||||
read_cache(Module, Key) ->
|
||||
ets:lookup_element(data_table(Module), {cache, Key}, 2).
|
||||
|
||||
%% Compilation hook
|
||||
|
||||
compile(Module, Block, Vars, #{line := Line} = Env) when is_atom(Module) ->
|
||||
@@ -79,7 +86,7 @@ compile(Line, Module, Block, Vars, E) ->
|
||||
|
||||
{AllDefinitions, Unreachable} = elixir_def:fetch_definitions(File, Module),
|
||||
|
||||
(not elixir_compiler:get_opt(internal)) andalso
|
||||
(not elixir_config:get(bootstrap)) andalso
|
||||
'Elixir.Module':check_behaviours_and_impls(E, Data, AllDefinitions, OverridablePairs),
|
||||
|
||||
CompileOpts = lists:flatten(ets:lookup_element(Data, compile, 2)),
|
||||
@@ -179,7 +186,7 @@ build(Line, File, Module, Lexical) ->
|
||||
end,
|
||||
|
||||
ImplOnDefinition =
|
||||
case elixir_compiler:get_opt(internal) of
|
||||
case elixir_config:get(bootstrap) of
|
||||
true -> [{elixir_module, delete_impl}];
|
||||
_ -> [{'Elixir.Module', compile_impl}]
|
||||
end,
|
||||
@@ -311,7 +318,7 @@ beam_location(#{lexical_tracker := Pid, module := Module}) ->
|
||||
%% Handle unused attributes warnings and special cases.
|
||||
|
||||
warn_unused_attributes(File, Data, PersistedAttrs) ->
|
||||
ReservedAttrs = [after_compile, before_compile, moduledoc, on_definition | PersistedAttrs],
|
||||
ReservedAttrs = [after_compile, before_compile, deprecated, moduledoc, on_definition, since | PersistedAttrs],
|
||||
Keys = ets:select(Data, [{{'$1', '_', '_', '$2'}, [{is_atom, '$1'}, {is_integer, '$2'}], [['$1', '$2']]}]),
|
||||
[elixir_errors:form_warn([{line, Line}], File, ?MODULE, {unused_attribute, Key}) ||
|
||||
[Key, Line] <- Keys, not lists:member(Key, ReservedAttrs)].
|
||||
|
||||
@@ -52,12 +52,9 @@ store(Module, Function, Hidden) ->
|
||||
case Overridden of
|
||||
false ->
|
||||
overridable(Module, maps:put(Function, {Count, Def, Neighbours, true}, Overridable)),
|
||||
(not elixir_compiler:get_opt(internal)) andalso
|
||||
'Elixir.Module.LocalsTracker':reattach(Module, Kind, Function, Neighbours),
|
||||
elixir_def:store_definition(false, FinalKind, Meta, FinalName, FinalArity,
|
||||
File, Module, Defaults, FinalClauses),
|
||||
elixir_locals:record_definition(Tuple, FinalKind, Module),
|
||||
elixir_locals:record_local(Tuple, Module, Function);
|
||||
elixir_locals:reattach(Tuple, FinalKind, Module, Function, Neighbours);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
|
||||
@@ -378,9 +378,9 @@ capture_op_eol -> capture_op : '$1'.
|
||||
capture_op_eol -> capture_op eol : '$1'.
|
||||
|
||||
unary_op_eol -> unary_op : '$1'.
|
||||
unary_op_eol -> unary_op eol : '$1'.
|
||||
unary_op_eol -> unary_op eol : warn_unary_operator_eol('$1'), '$1'.
|
||||
unary_op_eol -> dual_op : '$1'.
|
||||
unary_op_eol -> dual_op eol : '$1'.
|
||||
unary_op_eol -> dual_op eol : warn_unary_operator_eol('$1'), '$1'.
|
||||
|
||||
match_op_eol -> match_op : '$1'.
|
||||
match_op_eol -> match_op eol : '$1'.
|
||||
@@ -857,6 +857,25 @@ warn_empty_stab_clause({stab_op, {Line, _Begin, _End}, '->'}) ->
|
||||
"an expression is always required on the right side of ->. "
|
||||
"Please provide a value after ->").
|
||||
|
||||
warn_unary_operator_eol({dual_op, {Line, _Begin, _End}, Op}) ->
|
||||
elixir_errors:warn(Line, ?file(),
|
||||
io_lib:format(
|
||||
"unary operator ~ts followed by new line. "
|
||||
"This may happen when you try to use a binary operator over multiple lines. "
|
||||
"Please make sure that the operator and all arguments are on the same line",
|
||||
[Op]
|
||||
)
|
||||
);
|
||||
warn_unary_operator_eol({unary_op, {Line, _Begin, _End}, Op}) ->
|
||||
elixir_errors:warn(Line, ?file(),
|
||||
io_lib:format(
|
||||
"unary operator ~ts followed by new line. "
|
||||
"Please make sure the unary operator and the expression that follows "
|
||||
"the operator are on the same line",
|
||||
[Op]
|
||||
)
|
||||
).
|
||||
|
||||
warn_pipe({arrow_op, {Line, _Begin, _End}, Op}, {_, [_ | _], [_ | _]}) ->
|
||||
elixir_errors:warn(Line, ?file(),
|
||||
io_lib:format(
|
||||
|
||||
@@ -142,7 +142,7 @@ tokenize(String, Line, Column, Opts) ->
|
||||
existing_atoms_only=ExistingAtomsOnly,
|
||||
check_terminators=CheckTerminators,
|
||||
preserve_comments=PreserveComments,
|
||||
identifier_tokenizer=elixir_config:get(identifier_tokenizer)
|
||||
identifier_tokenizer=elixir_config:safe_get(identifier_tokenizer, 'Elixir.String.Tokenizer')
|
||||
}).
|
||||
|
||||
tokenize(String, Line, Opts) ->
|
||||
@@ -670,7 +670,7 @@ eol(Line, Column, Tokens) -> [{eol, {Line, Column, Column + 1}} | Tokens].
|
||||
unsafe_to_atom(Part, Line, #elixir_tokenizer{}) when
|
||||
is_binary(Part) andalso size(Part) > 255;
|
||||
is_list(Part) andalso length(Part) > 255 ->
|
||||
{error, {Line, "atom length must be less than system limit", ":"}};
|
||||
{error, {Line, "atom length must be less than system limit: ", [$: | Part]}};
|
||||
unsafe_to_atom(Binary, _Line, #elixir_tokenizer{existing_atoms_only=true}) when is_binary(Binary) ->
|
||||
{ok, binary_to_existing_atom(Binary, utf8)};
|
||||
unsafe_to_atom(Binary, _Line, #elixir_tokenizer{}) when is_binary(Binary) ->
|
||||
|
||||
@@ -86,15 +86,17 @@ change_universal_time(Name, {{Y, M, D}, {H, Min, Sec}}=Time)
|
||||
file:write_file_info(Name, #file_info{mtime=Time}, [{time, universal}]).
|
||||
|
||||
relative_to_cwd(Path) ->
|
||||
case elixir_compiler:get_opt(relative_paths) of
|
||||
try elixir_compiler:get_opt(relative_paths) of
|
||||
true -> 'Elixir.Path':relative_to_cwd(Path);
|
||||
false -> Path
|
||||
catch
|
||||
_:_ -> Path
|
||||
end.
|
||||
|
||||
characters_to_list(Data) when is_list(Data) ->
|
||||
Data;
|
||||
characters_to_list(Data) ->
|
||||
case elixir_compiler:get_opt(internal) of
|
||||
case elixir_config:safe_get(bootstrap, true) of
|
||||
true -> unicode:characters_to_list(Data);
|
||||
false -> 'Elixir.String':to_charlist(Data)
|
||||
end.
|
||||
@@ -102,8 +104,8 @@ characters_to_list(Data) ->
|
||||
characters_to_binary(Data) when is_binary(Data) ->
|
||||
Data;
|
||||
characters_to_binary(Data) ->
|
||||
case elixir_compiler:get_opt(internal) of
|
||||
true -> unicode:characters_to_binary(Data);
|
||||
case elixir_config:safe_get(bootstrap, true) of
|
||||
true -> unicode:characters_to_binary(Data);
|
||||
false -> 'Elixir.List':to_string(Data)
|
||||
end.
|
||||
|
||||
|
||||
@@ -66,6 +66,15 @@ defmodule DateTest do
|
||||
{:ok, Calendar.Holocene.date(12000, 01, 01)}
|
||||
end
|
||||
|
||||
test "add/2" do
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
Date.add(~D[0000-01-01], 3652425)
|
||||
end
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
Date.add(~D[0000-01-01], -1)
|
||||
end
|
||||
end
|
||||
|
||||
test "diff/2" do
|
||||
assert Date.diff(~D[2000-01-31], ~D[2000-01-01]) == 30
|
||||
assert Date.diff(~D[2000-01-01], ~D[2000-01-31]) == -30
|
||||
@@ -221,13 +230,20 @@ defmodule DateTimeTest do
|
||||
assert DateTime.from_unix(-62167219201) == {:error, :invalid_unix_time}
|
||||
|
||||
max_datetime = %DateTime{
|
||||
calendar: Calendar.ISO, day: 31, hour: 23, microsecond: {0, 0},
|
||||
calendar: Calendar.ISO, day: 31, hour: 23, microsecond: {999999, 6},
|
||||
minute: 59, month: 12, second: 59, std_offset: 0, time_zone: "Etc/UTC",
|
||||
utc_offset: 0, year: 9999, zone_abbr: "UTC"
|
||||
}
|
||||
|
||||
assert DateTime.from_unix(253402300799) == {:ok, max_datetime}
|
||||
assert DateTime.from_unix(253402300799999999, :microsecond) == {:ok, max_datetime}
|
||||
assert DateTime.from_unix(253402300800) == {:error, :invalid_unix_time}
|
||||
|
||||
minus_datetime = %DateTime{
|
||||
calendar: Calendar.ISO, day: 31, hour: 23, microsecond: {999999, 6},
|
||||
minute: 59, month: 12, second: 59, std_offset: 0, time_zone: "Etc/UTC",
|
||||
utc_offset: 0, year: 1969, zone_abbr: "UTC"
|
||||
}
|
||||
assert DateTime.from_unix(-1, :microsecond) == {:ok, minus_datetime}
|
||||
end
|
||||
|
||||
test "from_unix!/2" do
|
||||
|
||||
@@ -70,6 +70,11 @@ defmodule EnumTest do
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6], 3, 2, []) == [[1, 2, 3], [3, 4, 5], [5, 6]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6], 3, 3, []) == [[1, 2, 3], [4, 5, 6]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5], 4, 4, 6..10) == [[1, 2, 3, 4], [5, 6, 7, 8]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5], 2, 3, []) == [[1, 2], [4, 5]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6], 2, 3, []) == [[1, 2], [4, 5]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6, 7], 2, 3, []) == [[1, 2], [4, 5], [7]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6, 7], 2, 3, [8]) == [[1, 2], [4, 5], [7, 8]]
|
||||
assert Enum.chunk_every([1, 2, 3, 4, 5, 6, 7], 2, 4, []) == [[1, 2], [5, 6]]
|
||||
end
|
||||
|
||||
test "chunk_by/2" do
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
defmodule Dialyzer.Defmacrop do
|
||||
defmacrop good_macro(id) do
|
||||
quote do
|
||||
{:good, {:good_macro, unquote(id)}}
|
||||
end
|
||||
end
|
||||
|
||||
def run() do
|
||||
good_macro("Not So Bad")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,45 @@
|
||||
defmodule Dialyzer.With do
|
||||
def with_else do
|
||||
with :ok <- ok_or_error(),
|
||||
:ok <- ok_or_other_error(),
|
||||
:ok <- ok_or_tuple_error(),
|
||||
:ok <- ok_or_tuple_list_error() do
|
||||
:ok
|
||||
else
|
||||
:error ->
|
||||
:error
|
||||
|
||||
:other_error ->
|
||||
:other_error
|
||||
|
||||
{:error, msg} when is_list(msg) or is_tuple(msg) ->
|
||||
:error
|
||||
|
||||
{:error, msg} when is_list(msg) when is_tuple(msg) ->
|
||||
:error
|
||||
|
||||
{:error, _msg} ->
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
@spec ok_or_error() :: :ok | :error
|
||||
defp ok_or_error do
|
||||
Enum.random([:ok, :error])
|
||||
end
|
||||
|
||||
@spec ok_or_other_error() :: :ok | :other_error
|
||||
defp ok_or_other_error do
|
||||
Enum.random([:ok, :other_error])
|
||||
end
|
||||
|
||||
@spec ok_or_tuple_error() :: :ok | {:error, :err}
|
||||
defp ok_or_tuple_error do
|
||||
Enum.random([:ok, {:error, :err}])
|
||||
end
|
||||
|
||||
@spec ok_or_tuple_list_error() :: :ok | {:error, [:err]}
|
||||
defp ok_or_tuple_list_error do
|
||||
Enum.random([:ok, {:error, [:err]}])
|
||||
end
|
||||
end
|
||||
@@ -97,6 +97,18 @@ defmodule Kernel.DialyzerTest do
|
||||
assert_dialyze_no_warnings! context
|
||||
end
|
||||
|
||||
if :erlang.system_info(:otp_release) >= '20' do
|
||||
test "no warnings on with/else", context do
|
||||
copy_beam! context, Dialyzer.With
|
||||
assert_dialyze_no_warnings! context
|
||||
end
|
||||
end
|
||||
|
||||
test "no warnings on defmacrop", context do
|
||||
copy_beam! context, Dialyzer.Defmacrop
|
||||
assert_dialyze_no_warnings! context
|
||||
end
|
||||
|
||||
defp copy_beam!(context, module) do
|
||||
name = "#{module}.beam"
|
||||
File.cp! Path.join(context[:base_dir], name),
|
||||
|
||||
@@ -238,7 +238,7 @@ defmodule Kernel.ErrorsTest do
|
||||
|
||||
test "different defs with defaults" do
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:3: def hello/3 defaults conflicts with def hello/2",
|
||||
"nofile:3: def hello/3 defaults conflicts with hello/2",
|
||||
~C'''
|
||||
defmodule Kernel.ErrorsTest.DifferentDefsWithDefaults1 do
|
||||
def hello(a, b \\ nil), do: a + b
|
||||
@@ -247,7 +247,7 @@ defmodule Kernel.ErrorsTest do
|
||||
'''
|
||||
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:3: def hello/2 conflicts with defaults from def hello/3",
|
||||
"nofile:3: def hello/2 conflicts with defaults from hello/3",
|
||||
~C'''
|
||||
defmodule Kernel.ErrorsTest.DifferentDefsWithDefaults2 do
|
||||
def hello(a, b \\ nil, c \\ nil), do: a + b + c
|
||||
|
||||
@@ -1180,6 +1180,14 @@ defmodule Kernel.ExpansionTest do
|
||||
assert_raise CompileError, ~r"unhandled operator ->", fn ->
|
||||
expand(quote do: (foo -> bar))
|
||||
end
|
||||
|
||||
assert_raise CompileError,
|
||||
~r"size in bitstring expects an integer or a variable as argument, got: foo\(\)",
|
||||
fn ->
|
||||
expand(quote do
|
||||
fn <<_::size(foo)>> -> :ok end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
## Helpers
|
||||
|
||||
@@ -17,7 +17,11 @@ defmodule Kernel.ImplTest do
|
||||
end
|
||||
|
||||
defmodule Behaviour do
|
||||
@callback foo :: any
|
||||
@callback foo() :: any
|
||||
end
|
||||
|
||||
defmodule BehaviourWithArgument do
|
||||
@callback foo(any) :: any
|
||||
end
|
||||
|
||||
defmodule MacroBehaviour do
|
||||
@@ -285,6 +289,20 @@ defmodule Kernel.ImplTest do
|
||||
end) =~ "got @impl Kernel.ImplTest.MacroBehaviour for def bar/0 but the given behaviour was not declared with @behaviour"
|
||||
end
|
||||
|
||||
test "does not warn for @impl when using default arguments" do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string ~S"""
|
||||
defmodule Kernel.ImplTest.ImplAttributes do
|
||||
@behaviour Kernel.ImplTest.Behaviour
|
||||
@behaviour Kernel.ImplTest.BehaviourWithArgument
|
||||
|
||||
@impl true
|
||||
def foo(args \\ []), do: args
|
||||
end
|
||||
"""
|
||||
end) == ""
|
||||
end
|
||||
|
||||
test "does not warn for no @impl when overriding callback" do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
|
||||
@@ -507,6 +507,19 @@ defmodule Kernel.TypespecTest do
|
||||
specs(bytecode)
|
||||
end
|
||||
|
||||
test "@spec(spec) with tuples and tuple vars" do
|
||||
bytecode = test_module do
|
||||
def my_fun1(x), do: x
|
||||
def my_fun2(x), do: x
|
||||
@spec my_fun1(tuple) :: tuple
|
||||
@spec my_fun2(tuple) :: tuple when tuple: {integer, integer}
|
||||
end
|
||||
|
||||
assert [{{:my_fun1, 1}, [{:type, _, :fun, [{:type, _, :product, [{:type, _, :tuple, :any}]}, {:type, _, :tuple, :any}]}]},
|
||||
{{:my_fun2, 1}, [{:type, _, :bounded_fun, [{:type, _, :fun, [{:type, _, :product, [{:var, _, :tuple}]}, {:var, _, :tuple}]}, _]}]}] =
|
||||
specs(bytecode)
|
||||
end
|
||||
|
||||
test "@spec(spec) for unreachable private function" do
|
||||
# Run it inside capture_io/2 so that the "my_fun/1 is unused"
|
||||
# warning doesn't get printed among the ExUnit test results.
|
||||
|
||||
@@ -84,6 +84,25 @@ defmodule Kernel.WarningTest do
|
||||
end) =~ message
|
||||
end
|
||||
|
||||
test "unary operator followed by new line" do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
1
|
||||
+
|
||||
1
|
||||
"""
|
||||
end) =~ "unary operator + followed by new line"
|
||||
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
!
|
||||
1
|
||||
"""
|
||||
end) =~ "unary operator ! followed by new line"
|
||||
after
|
||||
purge Sample
|
||||
end
|
||||
|
||||
test "useless attr" do
|
||||
message = capture_err(fn ->
|
||||
Code.eval_string """
|
||||
@@ -468,6 +487,27 @@ defmodule Kernel.WarningTest do
|
||||
purge Sample
|
||||
end
|
||||
|
||||
test "generated clause not match" do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string("""
|
||||
defmodule Sample do
|
||||
defmacro __using__(_) do
|
||||
quote do
|
||||
def hello, do: nil
|
||||
def hello, do: nil
|
||||
end
|
||||
end
|
||||
end
|
||||
defmodule UseSample do
|
||||
use Sample
|
||||
end
|
||||
""")
|
||||
end) =~ "this clause cannot match because a previous clause at line 10 always matches"
|
||||
after
|
||||
purge(Sample)
|
||||
purge(UseSample)
|
||||
end
|
||||
|
||||
test "clause with defaults should be first" do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string ~S"""
|
||||
@@ -676,6 +716,31 @@ defmodule Kernel.WarningTest do
|
||||
purge Sample
|
||||
end
|
||||
|
||||
test "with and do clauses emit errors, else clauses do not" do
|
||||
assert capture_err(fn ->
|
||||
Code.compile_string """
|
||||
with {:first, int} when is_integer(int) <- {:second, Integer.gcd(2, 4)} do
|
||||
int
|
||||
end
|
||||
"""
|
||||
end) =~ "this clause cannot match"
|
||||
|
||||
assert capture_err(fn ->
|
||||
Code.compile_string """
|
||||
with {:first, int1} when is_integer(int1) <- {:first, Integer.gcd(2, 4)},
|
||||
{:second, int2} when is_integer(int2) <- {:second, Integer.gcd(2, 4)} do
|
||||
{:ok, int1 + int2}
|
||||
else
|
||||
{:first, nil} -> {:error, "first number is not integer"}
|
||||
{:second, nil} -> {:error, "second number is not integer"}
|
||||
end
|
||||
"""
|
||||
end) == ""
|
||||
after
|
||||
purge Sample1
|
||||
purge Sample2
|
||||
end
|
||||
|
||||
test "warning on codepoint escape" do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string "? "
|
||||
|
||||
@@ -108,4 +108,38 @@ defmodule MapSetTest do
|
||||
list = MapSet.to_list(MapSet.new(5..120))
|
||||
assert Enum.sort(list) == Enum.to_list(5..120)
|
||||
end
|
||||
|
||||
test "MapSet v1 compatibility" do
|
||||
result = 1..5 |> map_set_v1() |> MapSet.new()
|
||||
assert MapSet.equal?(result, MapSet.new(1..5))
|
||||
|
||||
result = MapSet.put(map_set_v1(1..5), 6)
|
||||
assert MapSet.equal?(result, MapSet.new(1..6))
|
||||
|
||||
result = MapSet.union(map_set_v1(1..5), MapSet.new(6..10))
|
||||
assert MapSet.equal?(result, MapSet.new(1..10))
|
||||
|
||||
result = MapSet.intersection(map_set_v1(1..10), MapSet.new(6..15))
|
||||
assert MapSet.equal?(result, MapSet.new(6..10))
|
||||
|
||||
result = MapSet.difference(map_set_v1(1..10), MapSet.new(6..50))
|
||||
assert MapSet.equal?(result, MapSet.new(1..5))
|
||||
|
||||
result = MapSet.delete(map_set_v1(1..10), 1)
|
||||
assert MapSet.equal?(result, MapSet.new(2..10))
|
||||
|
||||
assert MapSet.size(map_set_v1(1..5)) == 5
|
||||
assert MapSet.to_list(map_set_v1(1..5)) == Enum.to_list(1..5)
|
||||
|
||||
assert MapSet.disjoint?(map_set_v1(1..5), MapSet.new(10..15))
|
||||
refute MapSet.disjoint?(map_set_v1(1..5), MapSet.new(5..10))
|
||||
|
||||
assert MapSet.subset?(map_set_v1(3..7), MapSet.new(1..10))
|
||||
refute MapSet.subset?(map_set_v1(7..12), MapSet.new(1..10))
|
||||
end
|
||||
|
||||
defp map_set_v1(enumerable) do
|
||||
map = Map.new(enumerable, &{&1, true})
|
||||
%{__struct__: MapSet, map: map}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -216,7 +216,7 @@ defmodule MapTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "defstruct allow keys to be enforced" do
|
||||
test "defstruct allows keys to be enforced" do
|
||||
message = "the following keys must also be given when building struct TestMod: [:foo]"
|
||||
assert_raise ArgumentError, message, fn ->
|
||||
Code.eval_string("""
|
||||
@@ -243,6 +243,26 @@ defmodule MapTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "struct always expands context module" do
|
||||
Code.compiler_options(ignore_module_conflict: true)
|
||||
|
||||
defmodule LocalPoint do
|
||||
defstruct x: 0
|
||||
def new, do: %LocalPoint{}
|
||||
end
|
||||
|
||||
assert LocalPoint.new == %{__struct__: LocalPoint, x: 0}
|
||||
|
||||
defmodule LocalPoint do
|
||||
defstruct x: 0, y: 0
|
||||
def new, do: %LocalPoint{}
|
||||
end
|
||||
|
||||
assert LocalPoint.new == %{__struct__: LocalPoint, x: 0, y: 0}
|
||||
after
|
||||
Code.compiler_options(ignore_module_conflict: false)
|
||||
end
|
||||
|
||||
defmodule LocalUser do
|
||||
defmodule NestedUser do
|
||||
defstruct []
|
||||
|
||||
@@ -60,11 +60,11 @@ defmodule Module.LocalsTrackerTest do
|
||||
D.add_local(config[:pid], {:foo, 1}, {:bar, 1})
|
||||
D.add_definition(config[:pid], :defp, {:bar, 1})
|
||||
|
||||
{infoo, outfoo} = D.yank(config[:pid], {:foo, 1})
|
||||
{infoo, outfoo} = D.yank(config[:pid], {:foo, 1})
|
||||
{inbar, outbar} = D.yank(config[:pid], {:bar, 1})
|
||||
|
||||
D.reattach(config[:pid], :defp, {:bar, 1}, {inbar, outbar})
|
||||
D.reattach(config[:pid], :def, {:foo, 1}, {infoo, outfoo})
|
||||
D.reattach(config[:pid], {:bar, 1}, :defp, {:bar, 1}, {inbar, outbar})
|
||||
D.reattach(config[:pid], {:foo, 1}, :def, {:foo, 1}, {infoo, outfoo})
|
||||
assert {:bar, 1} in D.reachable(config[:pid])
|
||||
end
|
||||
|
||||
|
||||
@@ -521,6 +521,11 @@ defmodule RegistryTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "child_spec/1 uses :name as :id" do
|
||||
assert %{id: :custom_name} = Registry.child_spec([name: :custom_name])
|
||||
assert %{id: Registry} = Registry.child_spec([])
|
||||
end
|
||||
|
||||
defp register_task(registry, key, value) do
|
||||
parent = self()
|
||||
{:ok, task} =
|
||||
|
||||
@@ -80,11 +80,15 @@ defmodule SupervisorTest do
|
||||
restart: :permanent, shutdown: :infinity) ==
|
||||
%{id: :foo, start: {:foo, :bar, []}, restart: :permanent, shutdown: :infinity}
|
||||
|
||||
assert_raise ArgumentError, ~r"The module Unknown was given as a child", fn ->
|
||||
assert_raise ArgumentError, ~r"The module SupervisorTest was given as a child.*\nbut it does not implement"m, fn ->
|
||||
Supervisor.child_spec(SupervisorTest, [])
|
||||
end
|
||||
|
||||
assert_raise ArgumentError, ~r"The module Unknown was given as a child.*but it does not exist"m, fn ->
|
||||
Supervisor.child_spec(Unknown, [])
|
||||
end
|
||||
|
||||
assert_raise ArgumentError, ~r"supervisors expect the child to be a module", fn ->
|
||||
assert_raise ArgumentError, ~r"supervisors expect each child to be one of", fn ->
|
||||
Supervisor.child_spec("other", [])
|
||||
end
|
||||
end
|
||||
@@ -111,6 +115,19 @@ defmodule SupervisorTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "init/2 with old and new child specs" do
|
||||
import Supervisor.Spec
|
||||
|
||||
assert Supervisor.init([Task, worker(Task, [])], strategy: :one_for_one) ==
|
||||
{:ok, {
|
||||
%{intensity: 3, period: 5, strategy: :one_for_one},
|
||||
[
|
||||
%{id: Task, restart: :temporary, start: {Task, :start_link, [[]]}},
|
||||
{Task, {Task, :start_link, []}, :permanent, 5000, :worker, [Task]}
|
||||
]
|
||||
}}
|
||||
end
|
||||
|
||||
test "start_link/2 with via" do
|
||||
Supervisor.start_link([], strategy: :one_for_one, name: {:via, :global, :via_sup})
|
||||
assert Supervisor.which_children({:via, :global, :via_sup}) == []
|
||||
@@ -151,6 +168,15 @@ defmodule SupervisorTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "start_link/2 with old and new specs" do
|
||||
import Supervisor.Spec
|
||||
children = [
|
||||
{Stack, {[:hello], [name: :dyn_stack]}},
|
||||
worker(Stack, [{[:hello], []}], id: :old_stack)
|
||||
]
|
||||
{:ok, _} = Supervisor.start_link(children, strategy: :one_for_one)
|
||||
end
|
||||
|
||||
test "start_link/3" do
|
||||
{:ok, pid} = Supervisor.start_link(Stack.Sup, {[:hello], [name: :stat_stack]})
|
||||
wait_until_registered(:stat_stack)
|
||||
|
||||
@@ -512,6 +512,10 @@ defmodule TaskTest do
|
||||
[ok: 1, ok: 1, exit: :timeout, exit: :timeout]
|
||||
refute_received _
|
||||
end
|
||||
|
||||
test "streams an enumerable with infinite timeout" do
|
||||
[ok: :ok] = Task.async_stream([1], fn _ -> :ok end, timeout: :infinity) |> Enum.to_list
|
||||
end
|
||||
end
|
||||
|
||||
for {desc, concurrency} <- ["==": 4, "<": 2, ">": 8] do
|
||||
|
||||
@@ -53,7 +53,7 @@ quoted_atom_test() ->
|
||||
|
||||
oversized_atom_test() ->
|
||||
OversizedAtom = [$: | string:copies("a", 256)],
|
||||
{1, "atom length must be less than system limit", ":"} = tokenize_error(OversizedAtom).
|
||||
{1, "atom length must be less than system limit: ", OversizedAtom} = tokenize_error(OversizedAtom).
|
||||
|
||||
op_atom_test() ->
|
||||
[{atom, {1, 1, 6}, f0_1}] = tokenize(":f0_1").
|
||||
|
||||
@@ -109,7 +109,7 @@ defmodule ExUnit.Assertions do
|
||||
# is not nil nor false. We need to rewrite the if
|
||||
# to avoid silly warnings though.
|
||||
return =
|
||||
no_warning(quote do
|
||||
suppress_warning(quote do
|
||||
case right do
|
||||
x when x in [nil, false] ->
|
||||
raise ExUnit.AssertionError,
|
||||
@@ -121,7 +121,7 @@ defmodule ExUnit.Assertions do
|
||||
end)
|
||||
|
||||
match_expr =
|
||||
no_warning(quote do
|
||||
suppress_warning(quote do
|
||||
case right do
|
||||
unquote(left) ->
|
||||
unquote(return)
|
||||
@@ -401,6 +401,40 @@ defmodule ExUnit.Assertions do
|
||||
quote(do: unquote(left) = received)
|
||||
end
|
||||
|
||||
pattern_finder =
|
||||
quote do
|
||||
fn message ->
|
||||
unquote(suppress_warning(quote do
|
||||
case message do
|
||||
unquote(pattern) ->
|
||||
_ = unquote(vars)
|
||||
true
|
||||
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end))
|
||||
end
|
||||
end
|
||||
|
||||
failure_message_hit =
|
||||
failure_message || quote do
|
||||
"""
|
||||
Found message matching #{unquote(binary)} after #{timeout}ms.
|
||||
|
||||
This means the message was delivered too close to the timeout value, you may want to either:
|
||||
|
||||
1. Give an increased timeout to `assert_receive/2`
|
||||
2. Increase the default timeout to all `assert_receive` in your
|
||||
test_helper.exs by setting ExUnit.configure(assert_receive_timeout: ...)
|
||||
"""
|
||||
end
|
||||
|
||||
failure_message_miss =
|
||||
failure_message || quote do
|
||||
"No message matching #{unquote(binary)} after #{timeout}ms."
|
||||
end
|
||||
|
||||
quote do
|
||||
timeout = unquote(timeout)
|
||||
|
||||
@@ -412,29 +446,10 @@ defmodule ExUnit.Assertions do
|
||||
timeout ->
|
||||
{:messages, messages} = Process.info(self(), :messages)
|
||||
|
||||
pattern_finder = fn message ->
|
||||
case message do
|
||||
unquote(pattern) ->
|
||||
_ = unquote(vars)
|
||||
true
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
if Enum.any?(messages, pattern_finder) do
|
||||
flunk(unquote(failure_message) || """
|
||||
Found message matching #{unquote(binary)} after #{timeout}ms.
|
||||
|
||||
This means the message was delivered too close to the timeout value, you may want to either:
|
||||
|
||||
1. Give an increased timeout to `assert_receive/2`
|
||||
2. Increase the default timeout to all `assert_receive` in your
|
||||
test_helper.exs by setting ExUnit.configure(assert_receive_timeout: ...)
|
||||
""")
|
||||
if Enum.any?(messages, unquote(pattern_finder)) do
|
||||
flunk(unquote(failure_message_hit))
|
||||
else
|
||||
failure_message = unquote(failure_message) || "No message matching #{unquote(binary)} after #{timeout}ms."
|
||||
flunk(failure_message <>
|
||||
flunk(unquote(failure_message_miss) <>
|
||||
ExUnit.Assertions.__pins__(unquote(pins)) <>
|
||||
ExUnit.Assertions.__mailbox__(messages))
|
||||
end
|
||||
@@ -515,7 +530,7 @@ defmodule ExUnit.Assertions do
|
||||
|> elem(1)
|
||||
end
|
||||
|
||||
defp no_warning({name, meta, [expr, [do: clauses]]}) do
|
||||
defp suppress_warning({name, meta, [expr, [do: clauses]]}) do
|
||||
clauses = Enum.map clauses, fn {:->, meta, args} ->
|
||||
{:->, [generated: true] ++ meta, args}
|
||||
end
|
||||
|
||||
@@ -179,6 +179,12 @@ defmodule ExUnit.AssertionsTest do
|
||||
:hello = assert_receive :hello
|
||||
end
|
||||
|
||||
test "assert receive accepts custom failure message" do
|
||||
send self(), :hello
|
||||
assert_receive message, 0, "failure message"
|
||||
:hello = message
|
||||
end
|
||||
|
||||
test "assert receive with message in mailbox after timeout, but before reading mailbox tells user to increase timeout" do
|
||||
parent = self()
|
||||
# This is testing a race condition, so it's not
|
||||
@@ -297,7 +303,7 @@ defmodule ExUnit.AssertionsTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "assert received leaks" do
|
||||
test "assert received binds variables" do
|
||||
send self(), {:hello, :world}
|
||||
assert_received {:hello, world}
|
||||
:world = world
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ defmodule IEx do
|
||||
|
||||
* via the `BREAK` menu (available via `Ctrl+C`) by typing `q`, pressing enter
|
||||
* by hitting `Ctrl+C`, `Ctrl+C`
|
||||
* by hitting `Ctrl+\`
|
||||
* by hitting `Ctrl+\ `
|
||||
|
||||
If you are connected to remote shell, it remains alive after disconnection.
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ defmodule IEx.Evaluator do
|
||||
old_leader = Process.group_leader
|
||||
Process.group_leader(self(), leader)
|
||||
|
||||
evaluator? = !!Process.get(:iex_evaluator)
|
||||
Process.put(:iex_evaluator, true)
|
||||
|
||||
state = loop_state(server, IEx.History.init, opts)
|
||||
command == :ack && :proc_lib.init_ack(self())
|
||||
|
||||
@@ -21,6 +24,15 @@ defmodule IEx.Evaluator do
|
||||
loop(state)
|
||||
after
|
||||
Process.group_leader(self(), old_leader)
|
||||
|
||||
# If there was an evaluator, nest failures.
|
||||
if evaluator? do
|
||||
send self(), {:done, server}
|
||||
else
|
||||
Process.delete(:iex_evaluator)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -114,7 +114,8 @@ defmodule IEx.Introspection do
|
||||
defp open_mfa(module, fun, arity) do
|
||||
with {:module, _} <- Code.ensure_loaded(module),
|
||||
source when is_list(source) <- module.module_info(:compile)[:source] do
|
||||
open_abstract_code(module, fun, arity, List.to_string(source))
|
||||
source = rewrite_elixir_source(module, source)
|
||||
open_abstract_code(module, fun, arity, source)
|
||||
else
|
||||
_ -> :error
|
||||
end
|
||||
@@ -158,6 +159,21 @@ defmodule IEx.Introspection do
|
||||
end
|
||||
end
|
||||
|
||||
defp rewrite_elixir_source(module, source) do
|
||||
case :application.get_application(module) do
|
||||
{:ok, app} when app in [:eex, :elixir, :ex_unit, :iex, :logger, :mix] ->
|
||||
{in_app, [lib_or_src | _]} =
|
||||
source
|
||||
|> Path.split()
|
||||
|> Enum.reverse()
|
||||
|> Enum.split_while(& &1 not in ["lib", "src"])
|
||||
|
||||
Application.app_dir(app, Path.join([lib_or_src | Enum.reverse(in_app)]))
|
||||
_ ->
|
||||
List.to_string(source)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Prints documentation.
|
||||
"""
|
||||
|
||||
@@ -123,8 +123,8 @@ defmodule IEx.HelpersTest do
|
||||
end
|
||||
|
||||
describe "open" do
|
||||
@iex_helpers Path.expand("../../lib/iex/helpers.ex", __DIR__)
|
||||
@elixir_erl Path.expand("../../../elixir/src/elixir.erl", __DIR__)
|
||||
@iex_helpers "iex/lib/iex/helpers.ex"
|
||||
@elixir_erl "elixir/src/elixir.erl"
|
||||
@editor System.get_env("ELIXIR_EDITOR")
|
||||
|
||||
test "opens __FILE__ and __LINE__" do
|
||||
@@ -137,48 +137,48 @@ defmodule IEx.HelpersTest do
|
||||
|
||||
test "opens Elixir module" do
|
||||
assert capture_iex("open(IEx.Helpers)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:1/
|
||||
~r/#{@iex_helpers}:1$/
|
||||
end
|
||||
|
||||
test "opens function" do
|
||||
assert capture_iex("open(h)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
end
|
||||
|
||||
test "opens function/arity" do
|
||||
assert capture_iex("open(b/1)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
assert capture_iex("open(h/0)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
end
|
||||
|
||||
test "opens module.function" do
|
||||
assert capture_iex("open(IEx.Helpers.b)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
assert capture_iex("open(IEx.Helpers.h)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
end
|
||||
|
||||
test "opens module.function/arity" do
|
||||
assert capture_iex("open(IEx.Helpers.b/1)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
assert capture_iex("open(IEx.Helpers.h/0)") |> maybe_trim_quotes() =~
|
||||
~r/#{@iex_helpers}:\d+/
|
||||
~r/#{@iex_helpers}:\d+$/
|
||||
end
|
||||
|
||||
test "opens Erlang module" do
|
||||
assert capture_iex("open(:elixir)") |> maybe_trim_quotes() =~
|
||||
~r/#{@elixir_erl}:\d+/
|
||||
~r/#{@elixir_erl}:\d+$/
|
||||
end
|
||||
|
||||
test "opens Erlang module.function" do
|
||||
assert capture_iex("open(:elixir.start)") |> maybe_trim_quotes() =~
|
||||
~r/#{@elixir_erl}:\d+/
|
||||
~r/#{@elixir_erl}:\d+$/
|
||||
end
|
||||
|
||||
test "opens Erlang module.function/arity" do
|
||||
assert capture_iex("open(:elixir.start/2)") |> maybe_trim_quotes() =~
|
||||
~r/#{@elixir_erl}:\d+/
|
||||
~r/#{@elixir_erl}:\d+$/
|
||||
end
|
||||
|
||||
test "errors if module is not available" do
|
||||
@@ -221,7 +221,7 @@ defmodule IEx.HelpersTest do
|
||||
|
||||
defp maybe_trim_quotes(string) do
|
||||
case :os.type do
|
||||
{:win32, _} -> String.trim(string, "\"")
|
||||
{:win32, _} -> String.replace(string, "\"", "")
|
||||
_ -> string
|
||||
end
|
||||
end
|
||||
|
||||
@@ -33,7 +33,7 @@ defmodule Mix.Compilers.Test do
|
||||
|
||||
case test_files_to_run do
|
||||
[] when stale ->
|
||||
Mix.shell.info "No stale tests."
|
||||
Mix.shell.info "No stale tests"
|
||||
:noop
|
||||
|
||||
[] when test_patterns == [] ->
|
||||
@@ -45,8 +45,9 @@ defmodule Mix.Compilers.Test do
|
||||
:noop
|
||||
|
||||
test_files ->
|
||||
task = Task.async(ExUnit, :run, [])
|
||||
|
||||
try do
|
||||
task = Task.async(ExUnit, :run, [])
|
||||
Kernel.ParallelRequire.files(test_files, parallel_require_callbacks)
|
||||
ExUnit.Server.cases_loaded()
|
||||
%{failures: failures} = results = Task.await(task, :infinity)
|
||||
@@ -56,6 +57,13 @@ defmodule Mix.Compilers.Test do
|
||||
end
|
||||
|
||||
{:ok, results}
|
||||
catch
|
||||
kind, reason ->
|
||||
stack = System.stacktrace
|
||||
# In case there is an error, shutdown the runner task
|
||||
# before the error propagates up and trigger links.
|
||||
Task.shutdown(task)
|
||||
:erlang.raise(kind, reason, stack)
|
||||
after
|
||||
agent_stop(stale_manifest_pid)
|
||||
end
|
||||
|
||||
@@ -214,10 +214,12 @@ defmodule Mix.SCM.Git do
|
||||
end
|
||||
end
|
||||
|
||||
@rev_command 'git --git-dir=.git config remote.origin.url && git --git-dir=.git rev-parse --verify --quiet HEAD'
|
||||
|
||||
defp get_rev_info do
|
||||
destructure [origin, rev],
|
||||
:os.cmd('git --git-dir=.git config remote.origin.url && git --git-dir=.git rev-parse --verify --quiet HEAD')
|
||||
|> IO.iodata_to_binary
|
||||
:os.cmd(@rev_command)
|
||||
|> List.to_string()
|
||||
|> String.split("\n", trim: true)
|
||||
%{origin: origin, rev: rev}
|
||||
end
|
||||
@@ -257,7 +259,7 @@ defmodule Mix.SCM.Git do
|
||||
:error ->
|
||||
version =
|
||||
:os.cmd('git --version')
|
||||
|> IO.iodata_to_binary
|
||||
|> List.to_string()
|
||||
|> parse_version
|
||||
|
||||
Mix.State.put(:git_version, version)
|
||||
|
||||
@@ -32,13 +32,19 @@ defmodule Mix.Tasks.Local.Hex do
|
||||
@spec run(OptionParser.argv) :: boolean
|
||||
def run(argv) do
|
||||
{opts, _} = OptionParser.parse!(argv, switches: @switches)
|
||||
force? = Keyword.get(opts, :force, false)
|
||||
if_missing? = Keyword.get(opts, :if_missing, false)
|
||||
|
||||
should_install? =
|
||||
case {force?, if_missing?} do
|
||||
{false, true} -> Code.ensure_loaded?(Hex)
|
||||
_ -> true
|
||||
if Keyword.get(opts, :if_missing, false) do
|
||||
not Code.ensure_loaded?(Hex)
|
||||
else
|
||||
true
|
||||
end
|
||||
|
||||
argv =
|
||||
if Keyword.get(opts, :force, false) do
|
||||
["--force"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
should_install? && run_install(argv)
|
||||
|
||||
@@ -99,7 +99,8 @@ defmodule Mix.Tasks.Profile.Cprof do
|
||||
{opts, head} = OptionParser.parse_head!(args,
|
||||
aliases: [r: :require, p: :parallel, e: :eval, c: :config],
|
||||
strict: @switches)
|
||||
Mix.Tasks.Run.run(args, opts, head, &profile_code(&1, opts),
|
||||
Mix.Tasks.Run.run(["--no-mix-exs" | args], opts, head,
|
||||
&profile_code(&1, opts),
|
||||
&profile_code(File.read!(&1), opts))
|
||||
end
|
||||
|
||||
|
||||
@@ -116,7 +116,8 @@ defmodule Mix.Tasks.Profile.Fprof do
|
||||
{opts, head} = OptionParser.parse_head!(args,
|
||||
aliases: [r: :require, p: :parallel, e: :eval, c: :config],
|
||||
strict: @switches)
|
||||
Mix.Tasks.Run.run(args, opts, head, &profile_code(&1, opts),
|
||||
Mix.Tasks.Run.run(["--no-mix-exs" | args], opts, head,
|
||||
&profile_code(&1, opts),
|
||||
&profile_code(File.read!(&1), opts))
|
||||
end
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ defmodule Mix.Tasks.Run do
|
||||
* `--no-deps-check` - does not check dependencies
|
||||
* `--no-archives-check` - does not check archives
|
||||
* `--no-halt` - does not halt the system after running the command
|
||||
* `--no-mix-exs` - allows the command to run even if there is no mix.exs
|
||||
* `--no-start` - does not start applications after compilation
|
||||
* `--no-elixir-version-check` - does not check the Elixir version from mix.exs
|
||||
|
||||
@@ -47,7 +48,7 @@ defmodule Mix.Tasks.Run do
|
||||
def run(args) do
|
||||
{opts, head} = OptionParser.parse_head!(args,
|
||||
aliases: [r: :require, p: :parallel, e: :eval, c: :config],
|
||||
strict: [parallel: :boolean, require: :keep, eval: :keep, config: :keep,
|
||||
strict: [parallel: :boolean, require: :keep, eval: :keep, config: :keep, mix_exs: :boolean,
|
||||
halt: :boolean, compile: :boolean, deps_check: :boolean, start: :boolean,
|
||||
archives_check: :boolean, elixir_version_check: :boolean, parallel_require: :keep])
|
||||
|
||||
@@ -83,7 +84,16 @@ defmodule Mix.Tasks.Run do
|
||||
|
||||
# Start app after rewriting System.argv,
|
||||
# but before requiring and evaling.
|
||||
Mix.Task.run "app.start", args
|
||||
cond do
|
||||
Mix.Project.get ->
|
||||
Mix.Task.run "app.start", args
|
||||
"--no-mix-exs" in args ->
|
||||
:ok
|
||||
true ->
|
||||
Mix.raise "Cannot execute \"mix run\" without a Mix.Project, " <>
|
||||
"please ensure you are running Mix in a directory with a mix.exs file " <>
|
||||
"or pass the --no-mix-exs flag"
|
||||
end
|
||||
|
||||
process_load(opts, expr_evaluator)
|
||||
|
||||
|
||||
@@ -10,10 +10,6 @@ defmodule Mix.Tasks.Profile.CprofTest do
|
||||
@moduletag apps: [:sample]
|
||||
@expr "Enum.each(1..5, &String.Chars.Integer.to_string/1)"
|
||||
|
||||
setup do
|
||||
Mix.Project.push MixTest.Case.Sample
|
||||
end
|
||||
|
||||
test "profiles evaluated expression", context do
|
||||
in_tmp context.test, fn ->
|
||||
assert capture_io(fn ->
|
||||
|
||||
@@ -6,13 +6,8 @@ defmodule Mix.Tasks.Profile.FprofTest do
|
||||
import ExUnit.CaptureIO
|
||||
|
||||
alias Mix.Tasks.Profile.Fprof
|
||||
|
||||
@moduletag apps: [:sample]
|
||||
|
||||
setup do
|
||||
Mix.Project.push MixTest.Case.Sample
|
||||
end
|
||||
|
||||
test "profiles evaluated expression", context do
|
||||
in_tmp context.test, fn ->
|
||||
assert capture_io(fn ->
|
||||
|
||||
@@ -40,7 +40,7 @@ defmodule Mix.Tasks.TestTest do
|
||||
assert_stale_run_output "2 tests, 0 failures"
|
||||
|
||||
assert_stale_run_output """
|
||||
No stale tests.
|
||||
No stale tests
|
||||
"""
|
||||
end
|
||||
end
|
||||
@@ -124,7 +124,7 @@ defmodule Mix.Tasks.TestTest do
|
||||
|
||||
Port.command(port, "\n")
|
||||
|
||||
assert receive_until_match(port, "No stale tests.", []) =~ "Restarting..."
|
||||
assert receive_until_match(port, "No stale tests", []) =~ "Restarting..."
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{application, elixir,
|
||||
[{description, "elixir"},
|
||||
{vsn, "1.5.0"},
|
||||
{vsn, "1.5.3"},
|
||||
{modules, [
|
||||
elixir
|
||||
]},
|
||||
|
||||
Reference in New Issue
Block a user