Compare commits

...
59 Commits
Author SHA1 Message Date
José Valim 7340ca2d92 Release v1.5.3 2017-12-19 18:12:48 +01:00
José Valim 3beb72e6f4 Update CHANGELOG 2017-12-15 11:16:33 +01:00
José Valim 45f9c9c1ce Add upcoming attributes to no warn list 2017-12-15 10:27:56 +01:00
José Valim cde12f75fc Also consider when acc_count is more than count
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2017-12-15 10:27:19 +01:00
Alberto Almagro Sotelo eb7e0984bd Fix Stream.chunk_every/4 odd results when step > count (#7114)
Stream.chunk_every/4 was giving odd results when step was greater than
count and the last element of the enumerable was the same as last
chunk's last element.

This commit fixes this issue and adds tests for different scenarios when
step > count. It also adds a doctest example to reflect this in the
documentation and test it simultaneously.

Fixes #7096

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2017-12-15 10:26:58 +01:00
José Valim 57a52b9a05 Remove leftover space 2017-12-12 09:43:16 +01:00
José Valim c352766f43 Update CHANGELOG.md 2017-12-12 09:42:59 +01:00
Aleksei Magusev 15222304eb Consider microseconds in maximum possible Calendar.ISO datetime (#7055) 2017-11-23 09:13:13 +01:00
Glauber Campinho f6aced3072 Remove dialyzer warnings from else in with clauses (#7031)
Add generated: true to guard clauses from else in with clauses

Resolves #6426
2017-11-15 17:55:41 -02:00
Glauber Campinho 5e5eaca78f Warn duplicate definitions in macros (#7037)
Closes #6637
2017-11-15 17:55:16 -02:00
Aleksei Magusev ab61c09b3d Fix compiler warnings in ExUnit.assert_receive/3
Closes #6429.
2017-10-07 23:07:42 +02:00
Aleksei Magusev ccf939e9e7 Fix typo in MapSetTest 2017-10-03 17:39:21 -07:00
Shane Howley 331a70ade9 Return valid MapSet when union-ing a legacy MapSet (#6534) 2017-10-03 17:32:24 -07:00
Scott Riley 4329429823 Fix typo/reference in Supervisor @moduledoc (#6622)
All examples leading up to this snippet have used the module name
`Stack`, this updates the snippet to use the same.
2017-10-01 18:44:42 +02:00
José Valim 05418eaa4b Release v1.5.2 2017-09-29 14:00:01 +02:00
Artur Cygan d1efe7c012 Improve error message on oversized atom (#6612) 2017-09-28 14:05:01 +02:00
José Valim 0494629c4c Properly convert from list to string in git commands 2017-09-28 11:02:02 +02:00
James Long 941ce758ed Fix negative microsecond result from DateTime.from_unix/2 (#6599) 2017-09-26 00:47:11 +02:00
Glauber Campinho 759457ecfa Fix @impl attribute for default arguments (#6595)
`Module.compile_impl/6` was ignoring the default attributes
when compiling the impl, now it checks the count of default
attributes and adds an impl for each one.

Fixes #6550
2017-09-24 12:23:20 +02:00
Wojtek Mach bfa894a15f Fix local.hex --if-missing (#6576) 2017-09-15 22:35:47 +02:00
José Valim 407d2e1835 Raise better child_spec error if module is not defined, closes #6565 2017-09-12 16:59:17 +02:00
Yordis Prieto 94e5c72793 Use the name of a registry as its child spec id (#6560) 2017-09-12 14:17:48 +02:00
José Valim d90c7bbb79 Translate built-in Elixir apps sources, closes #6562 2017-09-11 15:52:36 +02:00
José Valim 5b3e13c90b Fix overridable tests 2017-09-09 12:19:06 -07:00
José Valim 2021fed11a Do not traverse clauses table to retrieve defaults
Closes #6553
2017-09-09 11:52:25 -07:00
José Valim e74701ef07 Backport application docs improvements 2017-09-04 16:23:54 +02:00
Aleksei Magusev 284a7d00d6 Improve error when NaiveDateTime given to DateTime.to_iso8601/2
Closes #6503.
2017-08-24 23:45:48 +02:00
Aleksei Magusev a31549cc7d Ensure Calendar.ISO enforces date limits 2017-08-24 21:56:08 +02:00
Aleksei Magusev be6e54de16 Fix mix_exs option in Mix.Tasks.Run 2017-08-20 22:07:52 +02:00
Glauber Campinho 6fd203ba7b Warn if unary operators are followed by new lines (#6482) 2017-08-20 13:14:56 +02:00
José Valim 0a677fe403 Properly advertise the --no-mix-exs flag 2017-08-20 12:33:55 +02:00
José Valim 5b25761d4c Do not trigger additional error reports on failures when loading tests 2017-08-20 00:20:00 +02:00
José Valim e1f10a9386 Ensure nested evaluator entries exit 2017-08-15 18:14:53 +02:00
Glauber Campinho 37321cd529 Do not crash for unknown var inside binary pattern in match (#6459) 2017-08-15 11:52:48 +02:00
José Valim 8023b5e0c6 Remove --if-missing when calling archive, closes #6453 2017-08-13 10:03:05 +02:00
Steve Domin 19d41f1fdb Update CHANGELOG for v1.5.0 (#6431) 2017-08-04 17:21:11 +02:00
José Valim 907149c8c7 Improve docs for Enum.filter/2 2017-08-02 11:25:14 +02:00
José Valim c99e1cdc9b Do not crash IEx on :init.stop 2017-08-01 21:10:42 +02:00
Tom Gurion 402a33867b Improve formatting of Supervisor documentation (#6416) 2017-08-01 21:10:39 +02:00
José Valim 1406d853e0 Release v1.5.1 2017-08-01 17:09:01 +02:00
José Valim 42f8a069c5 Improve error message on invalid use of Supervisor.Spec
Closes #6407
2017-08-01 17:00:03 +02:00
José Valim e060a458cb Do not always write tuple vars as tuple types, closes #6411 2017-08-01 13:50:13 +02:00
José Valim 9aeaa61411 Make it clear new child specs are not supported, closes #6407 2017-07-31 06:47:09 +02:00
James Edward Gray II 28ee5bf396 Fix a copy and paste error in the documentation (#6408) 2017-07-31 06:44:07 +02:00
José Valim ba85d139fb Always use in_context definition for struct expansion, closes #6409 2017-07-31 06:40:40 +02:00
José Valim 039ca64890 Add handle_begin and handle_end to EEx
Elixir v1.5.0 attempted to repurpose the value of init
to use throughout the engine but such was a breaking change.

To address this problem, we added handle_begin and
handle_end which were designed exactly to handle inner
buffers.

This comes with the benefit that EEx.Engine can now have
any data structure as state and no longer only quoted
expressions.

Closes #6391.
2017-07-29 22:11:27 +02:00
José Valim 705952be2d Ensure elixir_erl:debug_info/4 does not require elixir running (#6401) 2017-07-29 17:27:47 +02:00
José Valim 43b54f9a4b Do not serialize references, closes #6393 2017-07-29 12:59:28 +02:00
Troels Brødsgaard 9dfc7a88b3 Fix documentation formatting where ` follows \ (#6400) 2017-07-28 23:07:20 +02:00
José Valim 6ce02e6a8c Allow profile tasks to run without a project 2017-07-28 22:17:19 +02:00
José Valim 463fd60f2e Properly track unreachable defmacrops in locals tracker 2017-07-28 12:48:40 +02:00
José Valim f2719b6fea Properly trim all quotes on Windows 2017-07-28 10:14:15 +02:00
Griffin Byatt bf3ef047dc Fix operation passed to assert_no_null_byte (#6397) 2017-07-28 09:44:12 +02:00
José Valim 896d0b4580 More doc fixes, see #6395 2017-07-27 21:40:31 +02:00
José Valim bb028704f7 Update docs for Task.async_stream, closes #6395
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2017-07-27 20:29:59 +02:00
José Valim b27db522e0 Make sure both old and new specs can play together at the supervisor level 2017-07-26 16:45:25 +02:00
Aaron Seigo f5e4635087 Support :infinity timeout on Task streams (#6382)
Fixes a regression introduced in 1.5
2017-07-26 11:13:42 +02:00
José Valim 288fb9ec73 Fix compiler and dialyzer warnings on with
We mark all else clauses as generated and make sure
the `<-` patterns are not marked as so.

Closes #6378.
Closes #6380.
2017-07-26 09:59:26 +02:00
Paul Swartz f76f9f6112 Add a test case for failed dialyzer check in with
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2017-07-26 09:59:21 +02:00
79 changed files with 996 additions and 285 deletions
+8 -1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -1 +1 @@
1.5.0
1.5.3
+5 -5
View File
@@ -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
View File
@@ -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.
"""
+20 -7
View File
@@ -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
+58 -14
View File
@@ -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)
+2 -8
View File
@@ -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.
+6 -6
View File
@@ -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).
+7 -6
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
+4 -5
View File
@@ -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),
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+2 -1
View File
@@ -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}
+16 -3
View File
@@ -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)
+32 -9
View File
@@ -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
+1 -1
View File
@@ -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
}
+1 -1
View File
@@ -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)), []}
+74 -23
View File
@@ -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
-4
View File
@@ -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
+15 -3
View File
@@ -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,
+3 -5
View File
@@ -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
+10 -2
View File
@@ -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)
+3 -5
View File
@@ -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
+1 -1
View File
@@ -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 """
+2 -2
View File
@@ -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.
+3 -1
View File
@@ -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),
+1 -1
View File
@@ -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.
+2 -2
View File
@@ -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),
+2 -2
View File
@@ -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) ->
+9 -3
View 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) ->
+30 -20
View File
@@ -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(""
+3 -3
View File
@@ -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,
+1 -1
View File
@@ -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 ->
+24 -4
View File
@@ -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),
+7 -7
View File
@@ -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]);
+1 -1
View File
@@ -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
+11 -10
View File
@@ -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.
+1 -4
View File
@@ -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
+11 -4
View File
@@ -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)].
+1 -4
View File
@@ -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,
+21 -2
View File
@@ -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(
+2 -2
View File
@@ -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) ->
+6 -4
View File
@@ -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.
+18 -2
View File
@@ -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
+5
View File
@@ -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
+19 -1
View File
@@ -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 "? "
+34
View File
@@ -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
+21 -1
View File
@@ -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
+5
View File
@@ -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} =
+28 -2
View File
@@ -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)
+4
View File
@@ -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
+1 -1
View File
@@ -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").
+40 -25
View File
@@ -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
+7 -1
View File
@@ -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
View File
@@ -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.
+12
View File
@@ -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
+17 -1
View File
@@ -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.
"""
+14 -14
View File
@@ -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
+10 -2
View File
@@ -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
+5 -3
View File
@@ -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)
+11 -5
View File
@@ -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)
+2 -1
View File
@@ -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
+2 -1
View File
@@ -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
+12 -2
View File
@@ -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 ->
+2 -2
View File
@@ -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
View File
@@ -1,6 +1,6 @@
{application, elixir,
[{description, "elixir"},
{vsn, "1.5.0"},
{vsn, "1.5.3"},
{modules, [
elixir
]},