Compare commits

...
26 Commits
Author SHA1 Message Date
José Valim 9c91d2cc91 Update CHANGELOG.md 2016-10-29 10:40:53 +02:00
José Valim 3dbfb92860 Release v1.2.6 2016-06-06 15:26:56 +02:00
José Valim a77b85665a Mark variables captured by modules as generated
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-06-06 15:18:56 +02:00
José Valim f27a6735d6 Update CHANGELOG 2016-06-03 01:51:52 +02:00
José Valim e415148d3a More explicitly use the term annotation throughout 2016-06-03 01:19:35 +02:00
José Valim 9d95a0bcbf Propagate generated: true in Elixir definitions 2016-06-03 00:31:31 +02:00
José Valim 3df2a02d23 Bump to 1.2.6-dev 2016-06-03 00:27:36 +02:00
José Valim 55a4c5980d Support generated: true in quote
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-06-02 23:54:30 +02:00
Eric Meadows-Jönsson 30cbdb8fbf Fix binary typespecs
* Removes <<_::size*unit>>
  * Adds <<_::size, _::_*unit>>

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-05-30 14:06:46 +02:00
Daniel Perez d1773cf96d Fix bug in Path.join. Fix #4252.
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-05-21 17:32:33 +02:00
José Valim 5754da26f2 Fixes for Erlang 19 RC
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-05-16 13:06:27 +02:00
Miles Starkenburg 41b5091073 Fix nfd normalization bug (#4606)
* Fix nfd normalization bug

* Change String.normalize return type to binary

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-05-13 09:29:45 +02:00
José Valim e5374cf5d3 Release v1.2.5 2016-04-30 13:34:56 +02:00
José Valim 352a357460 Do not assume @impl is a list, closes #4519
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-04-30 13:29:32 +02:00
Michael Pope cc018f90bf Stringify truncated function data in Logger (#4562)
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-04-30 13:27:46 +02:00
José Valim 9374fbeae6 Revert manifest changes for release (they are part of master) 2016-04-30 13:27:30 +02:00
José Valim 9a44775b68 Make prune part of the public API in the formatter
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-04-30 13:25:05 +02:00
José Valim 07c6356354 Ensure poorly formatted chardata is pruned in Logger watcher
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-04-30 13:24:40 +02:00
Aleksei Magusev 55b124a478 Fix String.replace_trailing/3 replacement bug 2016-04-25 18:33:32 +02:00
Aleksei Magusev 5249d1d2f0 Fix String.replace_leading/3 replacement bug 2016-04-25 18:33:22 +02:00
Aleksei Magusev 59c0752143 Fix String.replace_leading/3 bug when the rest of string equals match 2016-04-25 18:33:08 +02:00
José Valim 39160df6a6 Improve Access deprecation warning 2016-04-15 09:36:46 +02:00
José Valim 08d2aaaf53 Also purge modules on outdated manifest
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-04-13 17:17:39 +02:00
José Valim 715a38a6e5 Integrate non-fetchable (path) dependencies into compilers
This commit changes Elixir compilers so a path dependencies
no longer forces the "parent" project to recompile. This
means each compiler must know how to track dependencies but
it gives faster compilation times as a benefit.

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-04-13 16:29:57 +02:00
José Valim 7cc1ab4c63 Migrate from previous manifest versions
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-04-13 13:27:40 +02:00
José Valim 3798dff0fc Only recompile empty files if they changed
Prior to this commit, an Elixir file that did not
generate any modules would always be regenerated
whenever mix compile was invoked.

This commit addresses this issue by keeping source
files on its own rows in the Elixir manifest.

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2016-04-13 12:56:00 +02:00
31 changed files with 293 additions and 157 deletions
+25
View File
@@ -85,6 +85,28 @@ ask Mix to use Rebar 3 to compile it by passing the `manager: :rebar3` option.
Once configured, Mix will prompt you to install Rebar 3 if it is not yet
available.
## v1.2.6 (2016-06-06)
### 1. Enhancements
* [Kernel] Support Erlang 19
* [Kernel] Supported generated: true in the `quote` special form
### 2. Bug fixes
* [Path] Fix a bug in path join with "/" followed by empty segments
* [String] Fix a bug in NFD normalization when followed by one-byte sized graphemes
* [Typespec] Correctly support `<<_::size, _::_*unit>>` syntax
## v1.2.5 (2016-04-30)
### 1. Bug fixes
* [Logger] Stringify truncated function data in Logger
* [Logger] Ensure poorly formatted char data can also be logged by using the replacement character "�" (diamond question mark)
* [Mix] Do not assume `@impl` is always a list
* [String] Fix bugs in `String.replace_*` functions where it would not include the accumulated value for certain replacements
## v1.2.4 (2016-04-01)
### 1. Enhancements
@@ -255,3 +277,6 @@ available.
* [Mix] `Mix.Utils.camelize/1` and `Mix.Utils.underscore/1` are soft deprecated in favor of `Macro.camelize/1` and `Macro.underscore/1`
## v1.1
The CHANGELOG for v1.1 releases can be found [in the v1.1 branch](https://github.com/elixir-lang/elixir/blob/v1.1/CHANGELOG.md).
+1 -1
View File
@@ -1 +1 @@
1.2.4
1.2.6
+3 -2
View File
@@ -44,8 +44,9 @@ defmodule Access do
nil
Since Access is a behaviour, it can be implemented to key-value
data structures. Access requires the key comparison to be
implemented using the `===` operator.
data structures. The implementation should be added to the
module that defines the struct being access. Access requires the
key comparison to be implemented using the `===` operator.
## Field-based lookups
+9 -8
View File
@@ -20,14 +20,15 @@ defmodule File.Stream do
raw = :lists.keyfind(:encoding, 1, modes) == false
modes =
if raw do
if :lists.keyfind(:read_ahead, 1, modes) == {:read_ahead, false} do
[:raw|modes]
else
[:raw, :read_ahead|modes]
end
else
modes
case raw do
true ->
if :lists.keyfind(:read_ahead, 1, modes) == {:read_ahead, false} do
[:raw | modes]
else
[:raw, :read_ahead | modes]
end
false ->
modes
end
%File.Stream{path: path, modes: modes, raw: raw, line_or_bytes: line_or_bytes}
+6 -1
View File
@@ -650,7 +650,12 @@ defmodule GenEvent do
def init_it(starter, parent, name, _, _, options) do
Process.put(:"$initial_call", {__MODULE__, :init_it, 6})
debug = :gen.debug_options(options)
debug =
if function_exported?(:gen, :debug_options, 2) do
:gen.debug_options(name, options)
else
:gen.debug_options(options)
end
:proc_lib.init_ack(starter, {:ok, self()})
loop(parent, name(name), [], debug, false)
end
+2 -2
View File
@@ -2931,8 +2931,8 @@ defmodule Kernel do
defp module_vars([{key, kind}|vars], counter) do
var =
case is_atom(kind) do
true -> {key, [warn: false], kind}
false -> {key, [counter: kind, warn: false], nil}
true -> {key, [generated: true], kind}
false -> {key, [counter: kind, generated: true], nil}
end
under = String.to_atom(<<"_@", :erlang.integer_to_binary(counter)::binary>>)
+4
View File
@@ -765,6 +765,10 @@ defmodule Kernel.SpecialForms do
quote. Read the Stacktrace information section below for more
information.
* `:generated` - marks the given chunk as generated so it does not emit warnings.
Currently it only works on special forms (for example, you cannot annotate
a `case` but not an `if`).
* `:context` - sets the resolution context.
* `:bind_quoted` - passes a binding to the macro. Whenever a binding is
+10 -10
View File
@@ -580,13 +580,13 @@ defmodule Kernel.Typespec do
defp typespec_to_ast({:type, line, :binary, [arg1, arg2]}) do
[arg1, arg2] = for arg <- [arg1, arg2], do: typespec_to_ast(arg)
cond do
arg2 == 0 ->
case {typespec_to_ast(arg1), typespec_to_ast(arg2)} do
{arg1, 0} ->
quote line: line, do: <<_ :: unquote(arg1)>>
arg1 == 0 ->
{0, arg2} ->
quote line: line, do: <<_ :: _ * unquote(arg2)>>
true ->
quote line: line, do: <<_ :: unquote(arg1) * unquote(arg2)>>
{arg1, arg2} ->
quote line: line, do: <<_ :: unquote(arg1), _ :: _ * unquote(arg2)>>
end
end
@@ -707,16 +707,16 @@ defmodule Kernel.Typespec do
{:type, line(meta), :binary, [{:integer, line(meta), 0}, {:integer, line(unit_meta), unit}]}
end
defp typespec({:<<>>, meta, [{:::, shared_meta, [{:_, _, ctx}, {:*, _, [size, unit]}]}]}, _, _)
when is_atom(ctx) and is_integer(unit) and is_integer(size) do
{:type, line(meta), :binary, [{:integer, line(shared_meta), size}, {:integer, line(shared_meta), unit}]}
end
defp typespec({:<<>>, meta, [{:::, size_meta, [{:_, _, ctx}, size]}]}, _, _)
when is_atom(ctx) and is_integer(size) do
{:type, line(meta), :binary, [{:integer, line(size_meta), size}, {:integer, line(meta), 0}]}
end
defp typespec({:<<>>, meta, [{:::, size_meta, [{:_, _, ctx1}, size]}, {:::, unit_meta, [{:_, _, ctx2}, {:*, _, [{:_, _, ctx3}, unit]}]}]}, _, _)
when is_atom(ctx1) and is_atom(ctx2) and is_atom(ctx3) and is_integer(size) and is_integer(unit) do
{:type, line(meta), :binary, [{:integer, line(size_meta), size}, {:integer, line(unit_meta), unit}]}
end
## Handle maps and structs
defp typespec({:map, meta, args}, _vars, _caller) when args == [] or is_atom(args) do
{:type, line(meta), :map, :any}
+2 -1
View File
@@ -470,10 +470,11 @@ defmodule Path do
end
defp do_join("", right, os_type), do: relative(right, os_type)
defp do_join(left, "", _os_type), do: left
defp do_join("/", right, os_type), do: "/" <> relative(right, os_type)
defp do_join(left, right, os_type), do: remove_dirsep(left, os_type) <> "/" <> relative(right, os_type)
defp remove_dirsep("", _os_type), do: ""
defp remove_dirsep("/", _os_type), do: "/"
defp remove_dirsep(bin, os_type) do
last = :binary.last(bin)
if last == ?/ or (last == ?\\ and os_type == :win32) do
+1 -1
View File
@@ -537,7 +537,7 @@ defmodule Protocol do
if Atom.to_string(protocol) =~ "Elixir.Access" do
:elixir_errors.warn __ENV__.line, __ENV__.file,
"implementation of the Access protocol is deprecated. For customization of " <>
"the dict[key] syntax, please implement the Dict behaviour instead"
"the data[key] syntax, please implement the Access behaviour in your struct"
else
Protocol.assert_protocol!(protocol)
Protocol.__ensure_defimpl__(protocol, for, __ENV__)
+6 -6
View File
@@ -504,14 +504,14 @@ defmodule String do
## Examples
iex> String.normalize("yêṩ", :nfd)
"yêṩ"
"yêṩ"
iex> String.normalize("leña", :nfc)
"leña"
"""
@spec normalize(t, atom) :: boolean
defdelegate normalize(binary, form), to: String.Normalizer
@spec normalize(t, atom) :: t
defdelegate normalize(string, form), to: String.Normalizer
@doc """
Converts all characters in the given string to uppercase.
@@ -628,12 +628,12 @@ defmodule String do
replace_leading(string, match, replacement, prefix_size, suffix_size, "")
end
defp replace_leading(string, match, replacement, prefix_size, suffix_size, acc) when suffix_size > 0 do
defp replace_leading(string, match, replacement, prefix_size, suffix_size, acc) when suffix_size >= 0 do
case string do
<<prefix::size(prefix_size)-binary, suffix::size(suffix_size)-binary>> when prefix == match ->
replace_leading(suffix, match, replacement, prefix_size, suffix_size - prefix_size, acc <> replacement)
_ ->
string
acc <> string
end
end
@@ -671,7 +671,7 @@ defmodule String do
<<prefix::size(prefix_size)-binary, suffix::size(suffix_size)-binary>> when suffix == match ->
replace_trailing(prefix, match, replacement, prefix_size - suffix_size, suffix_size, acc <> replacement)
_ ->
string
string <> acc
end
end
+3 -3
View File
@@ -58,9 +58,9 @@ The following literals are also supported in typespecs:
| 1.0 ## Floats
| <<>> ## Bitstrings
| <<_::size>> # size is 0 or a positive integer
| <<_::_ * unit>> # unit is an integer from 1 to 256
| <<_::size * unit>>
| <<_::size>> # size is 0 or a positive integer
| <<_::_*unit>> # unit is an integer from 1 to 256
| <<_::size, _::_*unit>>
| [type] ## Lists
| [] # empty list
+2 -1
View File
@@ -29,7 +29,8 @@
imports_hygiene=true,
unquote=true,
unquoted=false,
escape=false
escape=false,
generated=false
}).
-record(elixir_tokenizer, {
+79 -79
View File
@@ -65,13 +65,15 @@ store_definition(Line, Kind, CheckClauses, Call, Body, Pos) when is_integer(Line
LinifyArgs = elixir_quote:linify(Line, Key, Args),
LinifyGuards = elixir_quote:linify(Line, Key, Guards),
LinifyBody = elixir_quote:linify(Line, Key, Body),
LinifyMeta = lists:keystore(line, 1, Meta, {line, Line}),
assert_no_aliases_name(Line, Name, Args, E),
assert_valid_name(Line, Kind, Name, Args, E),
store_definition(Line, Kind, DoCheckClauses, Name,
assert_no_aliases_name(LinifyMeta, Name, Args, E),
assert_valid_name(LinifyMeta, Kind, Name, Args, E),
store_definition(LinifyMeta, Line, Kind, DoCheckClauses, Name,
LinifyArgs, LinifyGuards, LinifyBody, Location, E).
store_definition(Line, Kind, CheckClauses, Name, Args, Guards, Body, KeepLocation, #{module := Module} = ER) ->
store_definition(Meta, Line, Kind, CheckClauses, Name, Args, Guards, Body, KeepLocation,
#{module := Module} = ER) ->
Arity = length(Args),
Tuple = {Name, Arity},
Location = retrieve_location(KeepLocation, Module),
@@ -83,8 +85,9 @@ store_definition(Line, Kind, CheckClauses, Name, Args, Guards, Body, KeepLocatio
elixir_locals:record_definition(Tuple, Kind, Module),
{Function, Defaults, Super} = translate_definition(Kind, Line, Name, Args, Guards, Body, E),
run_on_definition_callbacks(Kind, Line, Module, Name, Args, Guards, expr_from_body(Line, Body), E),
WrappedBody = expr_from_body(Line, Body),
{Function, Defaults, Super} = translate_definition(Kind, Meta, Name, Args, Guards, Body, WrappedBody, E),
run_on_definition_callbacks(Kind, Line, Module, Name, Args, Guards, WrappedBody, E),
DefaultsLength = length(Defaults),
elixir_locals:record_defaults(Tuple, Kind, Module, DefaultsLength),
@@ -152,42 +155,39 @@ compile_super(_Module, _, _E) -> ok.
%% Translate the given call and expression given
%% and then store it in memory.
translate_definition(Kind, Line, Name, Args, Guards, Body, E) when is_integer(Line) ->
translate_definition(Kind, Meta, Name, Args, Guards, Body, WrappedBody, E) ->
Arity = length(Args),
{EArgs, EGuards, EBody, _} = elixir_exp_clauses:def(fun elixir_def_defaults:expand/2,
Args, Guards, expr_from_body(Line, Body), E),
case Body of
nil -> check_args_for_bodyless_clause(Line, EArgs, E);
_ -> ok
end,
Args, Guards, WrappedBody, E),
S = elixir_env:env_to_scope(E),
{Unpacked, Defaults} = elixir_def_defaults:unpack(Kind, Name, EArgs, S),
{Clauses, Super} = translate_clause(Body, Line, Kind, Unpacked, EGuards, EBody, S),
{Clauses, Super} = translate_clause(Body, Kind, Meta, Unpacked, EGuards, EBody, S),
Function = {function, Line, Name, Arity, Clauses},
Function = {function, ?ann(Meta), Name, Arity, Clauses},
{Function, Defaults, Super}.
translate_clause(nil, _Line, _Kind, _Args, [], _Body, _S) ->
translate_clause(nil, _Kind, Meta, Args, [], _Body, S) ->
check_args_for_bodyless_clause(Meta, Args, S),
{[], false};
translate_clause(nil, Line, Kind, _Args, _Guards, _Body, #elixir_scope{file=File}) ->
elixir_errors:form_error([{line, Line}], File, ?MODULE, {missing_do, Kind});
translate_clause(_, Line, Kind, Args, Guards, Body, S) ->
{TClause, TS} = elixir_clauses:clause([{line, Line}],
translate_clause(nil, Kind, Meta, _Args, _Guards, _Body, #elixir_scope{file=File}) ->
elixir_errors:form_error(Meta, File, ?MODULE, {missing_do, Kind});
translate_clause(_, Kind, Meta, Args, Guards, Body, S) ->
{TClause, TS} = elixir_clauses:clause(Meta,
fun elixir_translator:translate_args/2, Args, Body, Guards, S),
FClause = case is_macro(Kind) of
true ->
FArgs = {var, Line, '_@CALLER'},
Ann = ?ann(Meta),
FArgs = {var, Ann, '_@CALLER'},
MClause = setelement(3, TClause, [FArgs|element(3, TClause)]),
case TS#elixir_scope.caller of
true ->
FBody = {'match', Line,
{'var', Line, '__CALLER__'},
elixir_utils:erl_call(Line, elixir_env, linify, [{var, Line, '_@CALLER'}])
FBody = {'match', Ann,
{'var', Ann, '__CALLER__'},
elixir_utils:erl_call(Ann, elixir_env, linify, [{var, Ann, '_@CALLER'}])
},
setelement(5, MClause, [FBody|element(5, TClause)]);
false ->
@@ -219,21 +219,21 @@ unwrap_definitions(File, Module) ->
split_definition(All, Unreachable, [], [], [], [], [], {[], []}).
unwrap_definition([Fun|T], File, Module, CTable, All, Private) ->
{Tuple, Kind, Line, _, Check, Location, {Defaults, _, _}} = Fun,
{Tuple, Kind, Ann, _, Check, Location, {Defaults, _, _}} = Fun,
Export = export(Kind, Tuple),
case [Clause || {_, Clause} <- ets:lookup(CTable, Tuple)] of
[] ->
warn_bodyless_function(Line, File, Module, Kind, Tuple),
warn_bodyless_function(Ann, File, Module, Kind, Tuple),
unwrap_definition(T, File, Module, CTable, All, Private);
Clauses ->
Unwrapped = {Tuple, Kind, Line, Location,
function_for_stored_definition(Line, Export, Clauses)},
Unwrapped = {Tuple, Kind, Ann, Location,
function_for_stored_definition(Ann, Export, Clauses)},
NewPrivate =
if
Kind == defp; Kind == defmacrop ->
[{Tuple, Kind, Line, Check, Defaults}|Private];
[{Tuple, Kind, Ann, Check, Defaults}|Private];
true ->
Private
end,
@@ -244,30 +244,30 @@ unwrap_definition([Fun|T], File, Module, CTable, All, Private) ->
unwrap_definition([], _File, _Module, _CTable, All, Private) ->
{All, Private}.
split_definition([{Tuple, def, Line, Location, Body}|T], Unreachable,
split_definition([{Tuple, def, Ann, Location, Body}|T], Unreachable,
Def, Defp, Defmacro, Defmacrop, Exports, Functions) ->
split_definition(T, Unreachable, [Tuple|Def], Defp, Defmacro, Defmacrop,
[export(def, Tuple)|Exports],
add_definition(Line, Location, Body, Functions));
add_definition(Ann, Location, Body, Functions));
split_definition([{Tuple, defp, Line, Location, Body}|T], Unreachable,
split_definition([{Tuple, defp, Ann, Location, Body}|T], Unreachable,
Def, Defp, Defmacro, Defmacrop, Exports, Functions) ->
case lists:member(Tuple, Unreachable) of
false ->
split_definition(T, Unreachable, Def, [Tuple|Defp], Defmacro, Defmacrop,
Exports, add_definition(Line, Location, Body, Functions));
Exports, add_definition(Ann, Location, Body, Functions));
true ->
split_definition(T, Unreachable, Def, [Tuple|Defp], Defmacro, Defmacrop,
Exports, Functions)
end;
split_definition([{Tuple, defmacro, Line, Location, Body}|T], Unreachable,
split_definition([{Tuple, defmacro, Ann, Location, Body}|T], Unreachable,
Def, Defp, Defmacro, Defmacrop, Exports, Functions) ->
split_definition(T, Unreachable, Def, Defp, [Tuple|Defmacro], Defmacrop,
[export(defmacro, Tuple)|Exports],
add_definition(Line, Location, Body, Functions));
add_definition(Ann, Location, Body, Functions));
split_definition([{Tuple, defmacrop, _Line, _Location, _Body}|T], Unreachable,
split_definition([{Tuple, defmacrop, _Ann, _Location, _Body}|T], Unreachable,
Def, Defp, Defmacro, Defmacrop, Exports, Functions) ->
split_definition(T, Unreachable, Def, Defp, Defmacro, [Tuple|Defmacrop],
Exports, Functions);
@@ -282,33 +282,33 @@ export(Kind, {Name, Arity}) when Kind == defmacro; Kind == defmacrop ->
export(Kind, {Name, Arity}) when Kind == def; Kind == defp ->
{Name, Arity}.
function_for_stored_definition(Line, {Name, Arity}, Clauses) ->
{function, Line, Name, Arity, Clauses}.
function_for_stored_definition(Ann, {Name, Arity}, Clauses) ->
{function, Ann, Name, Arity, Clauses}.
add_definition(_Line, nil, Body, {Head, Tail}) ->
add_definition(_Ann, nil, Body, {Head, Tail}) ->
{[Body|Head], Tail};
add_definition(Line, Location, Body, {Head, Tail}) ->
add_definition(Ann, Location, Body, {Head, Tail}) ->
{Head,
[{attribute, Line, file, Location}, Body|Tail]}.
[{attribute, Ann, file, Location}, Body|Tail]}.
default_function_for(Kind, Name, {clause, Line, Args, _Guards, _Exprs} = Clause)
default_function_for(Kind, Name, {clause, Ann, Args, _Guards, _Exprs} = Clause)
when Kind == defmacro; Kind == defmacrop ->
{function, Line, Name, length(Args) - 1, [Clause]};
default_function_for(_, Name, {clause, Line, Args, _Guards, _Exprs} = Clause) ->
{function, Line, Name, length(Args), [Clause]}.
{function, Ann, Name, length(Args) - 1, [Clause]};
default_function_for(_, Name, {clause, Ann, Args, _Guards, _Exprs} = Clause) ->
{function, Ann, Name, length(Args), [Clause]}.
warn_bodyless_function(_Line, _File, Special, _Kind, _Tuple)
warn_bodyless_function(_Ann, _File, Special, _Kind, _Tuple)
when Special == 'Elixir.Kernel.SpecialForms'; Special == 'Elixir.Module' ->
ok;
warn_bodyless_function(Line, File, _Module, Kind, Tuple) ->
elixir_errors:form_warn([{line, Line}], File, ?MODULE, {bodyless_fun, Kind, Tuple}),
warn_bodyless_function(Ann, File, _Module, Kind, Tuple) ->
elixir_errors:form_warn([{line, erl_anno:line(Ann)}], File, ?MODULE, {bodyless_fun, Kind, Tuple}),
ok.
%% Store each definition in the table.
%% This function also checks and emit warnings in case
%% the kind, of the visibility of the function changes.
store_each(Check, Kind, File, Location, Module, Defaults, {function, Line, Name, Arity, Clauses}) ->
store_each(Check, Kind, File, Location, Module, Defaults, {function, Ann, Name, Arity, Clauses}) ->
Data = elixir_module:data_table(Module),
Defs = elixir_module:defs_table(Module),
Clas = elixir_module:clas_table(Module),
@@ -317,60 +317,60 @@ store_each(Check, Kind, File, Location, Module, Defaults, {function, Line, Name,
HasBody = Clauses =/= [],
case ets:lookup(Defs, Tuple) of
[{Tuple, StoredKind, StoredLine, StoredFile, StoredCheck,
[{Tuple, StoredKind, StoredAnn, StoredFile, StoredCheck,
StoredLocation, {StoredDefaults, LastHasBody, LastDefaults}}] ->
FinalLine = StoredLine,
FinalAnn = StoredAnn,
FinalLocation = StoredLocation,
FinalDefaults = {max(Defaults, StoredDefaults), HasBody, Defaults},
check_valid_kind(Line, File, Name, Arity, Kind, StoredKind),
check_valid_kind(Ann, File, Name, Arity, Kind, StoredKind),
(Check and StoredCheck) andalso
check_valid_clause(Line, File, Name, Arity, Kind, Data, StoredLine, StoredFile),
check_valid_defaults(Line, File, Name, Arity, Kind, Defaults, StoredDefaults, LastDefaults, LastHasBody);
check_valid_clause(Ann, File, Name, Arity, Kind, Data, StoredAnn, StoredFile),
check_valid_defaults(Ann, File, Name, Arity, Kind, Defaults, StoredDefaults, LastDefaults, LastHasBody);
[] ->
FinalLine = Line,
FinalAnn = Ann,
FinalLocation = Location,
FinalDefaults = {Defaults, HasBody, Defaults}
end,
Check andalso ets:insert(Data, {?last_def, {Name, Arity}}),
ets:insert(Clas, [{Tuple, Clause} || Clause <- Clauses]),
ets:insert(Defs, {Tuple, Kind, FinalLine, File, Check, FinalLocation, FinalDefaults}).
ets:insert(Defs, {Tuple, Kind, FinalAnn, File, Check, FinalLocation, FinalDefaults}).
%% Validations
check_valid_kind(_Line, _File, _Name, _Arity, Kind, Kind) -> [];
check_valid_kind(Line, File, Name, Arity, Kind, StoredKind) ->
elixir_errors:form_error([{line, Line}], File, ?MODULE,
check_valid_kind(_Ann, _File, _Name, _Arity, Kind, Kind) -> [];
check_valid_kind(Ann, File, Name, Arity, Kind, StoredKind) ->
elixir_errors:form_error([{line, erl_anno:line(Ann)}], File, ?MODULE,
{changed_kind, {Name, Arity, StoredKind, Kind}}).
check_valid_clause(Line, File, Name, Arity, Kind, Data, StoredLine, StoredFile) ->
check_valid_clause(Ann, File, Name, Arity, Kind, Data, StoredAnn, StoredFile) ->
case ets:lookup_element(Data, ?last_def, 2) of
{Name, Arity} -> [];
[] -> [];
_ ->
Relative = elixir_utils:relative_to_cwd(elixir_utils:relative_to_cwd(StoredFile)),
elixir_errors:form_warn([{line, Line}], File, ?MODULE,
{ungrouped_clause, {Kind, Name, Arity, StoredLine, Relative}})
elixir_errors:form_warn([{line, erl_anno:line(Ann)}], File, ?MODULE,
{ungrouped_clause, {Kind, Name, Arity, erl_anno:line(StoredAnn), Relative}})
end.
% Clause with defaults after clause with defaults
check_valid_defaults(Line, File, Name, Arity, Kind, Defaults, StoredDefaults, _, _) when Defaults > 0, StoredDefaults > 0 ->
elixir_errors:form_error([{line, Line}], File, ?MODULE,
check_valid_defaults(Ann, File, Name, Arity, Kind, Defaults, StoredDefaults, _, _) when Defaults > 0, StoredDefaults > 0 ->
elixir_errors:form_error([{line, erl_anno:line(Ann)}], File, ?MODULE,
{clauses_with_defaults, {Kind, Name, Arity}});
% Clause with defaults after clause(s) without defaults
check_valid_defaults(Line, File, Name, Arity, Kind, Defaults, 0, 0, _) when Defaults > 0 ->
elixir_errors:form_warn([{line, Line}], File, ?MODULE, {out_of_order_defaults, {Kind, Name, Arity}});
check_valid_defaults(Ann, File, Name, Arity, Kind, Defaults, 0, 0, _) when Defaults > 0 ->
elixir_errors:form_warn([{line, erl_anno:line(Ann)}], File, ?MODULE, {out_of_order_defaults, {Kind, Name, Arity}});
% Clause without defaults directly after clause with defaults (body less does not count)
check_valid_defaults(Line, File, Name, Arity, Kind, 0, _, LastDefaults, true) when LastDefaults > 0 ->
elixir_errors:form_warn([{line, Line}], File, ?MODULE,
check_valid_defaults(Ann, File, Name, Arity, Kind, 0, _, LastDefaults, true) when LastDefaults > 0 ->
elixir_errors:form_warn([{line, erl_anno:line(Ann)}], File, ?MODULE,
{out_of_order_defaults, {Kind, Name, Arity}});
% Clause without defaults
check_valid_defaults(_Line, _File, _Name, _Arity, _Kind, 0, _, _, _) -> [].
check_valid_defaults(_Ann, _File, _Name, _Arity, _Kind, 0, _, _, _) -> [].
check_previous_defaults(Line, Module, Name, Arity, Kind, Defaults, E) ->
check_previous_defaults(Ann, Module, Name, Arity, Kind, Defaults, E) ->
Matches = ets:match(elixir_module:defs_table(Module), {{Name, '$2'}, '$1', '_', '_', '_', '_', {'$3', '_', '_'}}),
[ begin
elixir_errors:form_error([{line, Line}], ?m(E, file), ?MODULE,
elixir_errors:form_error([{line, erl_anno:line(Ann)}], ?m(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)].
@@ -378,9 +378,9 @@ defaults_conflict(A, D, Arity, Defaults) ->
((Arity >= (A - D)) andalso (Arity < A)) orelse
((A >= (Arity - Defaults)) andalso (A < Arity)).
check_args_for_bodyless_clause(Line, Args, E) ->
check_args_for_bodyless_clause(Meta, Args, S) ->
[ begin
elixir_errors:form_error([{line, Line}], ?m(E, file), ?MODULE,
elixir_errors:form_error(Meta, S#elixir_scope.file, ?MODULE,
invalid_args_for_bodyless_clause)
end || Arg <- Args, invalid_arg(Arg) ].
@@ -391,14 +391,14 @@ invalid_arg({'\\\\', _, [{Name, _, Kind}, _]}) when is_atom(Name), is_atom(Kind)
invalid_arg(_) ->
true.
assert_no_aliases_name(Line, '__aliases__', [Atom], #{file := File}) when is_atom(Atom) ->
elixir_errors:form_error([{line, Line}], File, ?MODULE, {no_alias, Atom});
assert_no_aliases_name(_Line, _Aliases, _Args, _S) ->
assert_no_aliases_name(Meta, '__aliases__', [Atom], #{file := File}) when is_atom(Atom) ->
elixir_errors:form_error(Meta, File, ?MODULE, {no_alias, Atom});
assert_no_aliases_name(_Meta, _Aliases, _Args, _S) ->
ok.
assert_valid_name(Line, Kind, is_record, [_, _], #{file := File}) when Kind == defp; Kind == def ->
elixir_errors:form_error([{line, Line}], File, ?MODULE, {is_record, Kind});
assert_valid_name(_Line, _Kind, _Name, _Args, _S) ->
assert_valid_name(Meta, Kind, is_record, [_, _], #{file := File}) when Kind == defp; Kind == def ->
elixir_errors:form_error(Meta, File, ?MODULE, {is_record, Kind});
assert_valid_name(_Meta, _Kind, _Name, _Args, _S) ->
ok.
%% Format errors
+8 -2
View File
@@ -157,7 +157,7 @@ expand({quote, Meta, [KV, Do]}, E) when is_list(Do) ->
false -> compile_error(Meta, E#elixir_scope.file, "missing do keyword in quote")
end,
ValidOpts = [context, location, line, file, unquote, bind_quoted],
ValidOpts = [context, location, line, file, unquote, bind_quoted, generated],
{EKV, ET} = expand_opts(Meta, quote, ValidOpts, KV, E),
Context = case lists:keyfind(context, 1, EKV) of
@@ -198,7 +198,13 @@ expand({quote, Meta, [KV, Do]}, E) when is_list(Do) ->
false -> DefaultUnquote
end,
Q = #elixir_quote{line=Line, file=File, unquote=Unquote, context=Context},
Generated = lists:keyfind(generated, 1, EKV) == {generated, true},
%% TODO: Do not allow negative line numbers once Erlang 18
%% support is dropped as it only allows negative line
%% annotations alongside the generated check.
Q = #elixir_quote{line=Line, file=File, unquote=Unquote,
context=Context, generated=Generated},
{Quoted, _Q} = elixir_quote:quote(Exprs, Binding, Q, ET),
expand(Quoted, ET);
+4 -1
View File
@@ -321,7 +321,10 @@ do_quote_tuple({Left, Meta, Right}, Q, E) ->
{{'{}', [], [TLeft, meta(Meta, Q), TRight]}, RQ}.
meta(Meta, Q) ->
file(line(Meta, Q), Q).
generated(file(line(Meta, Q), Q), Q).
generated(Meta, #elixir_quote{generated=true}) -> [{generated, true}|Meta];
generated(Meta, #elixir_quote{generated=false}) -> Meta.
file(Meta, #elixir_quote{file=nil}) -> Meta;
file(Meta, #elixir_quote{file=File}) -> [{file, File}|Meta].
+6 -2
View File
@@ -27,6 +27,10 @@ defmodule Kernel.QuoteTest do
assert quote(line: line, do: bar(1, 2, 3)) == {:bar, [line: 26], [1, 2, 3]}
end
test "generated" do
assert quote(generated: true, do: bar(1)) == {:bar, [generated: true], [1]}
end
test "unquote call" do
assert quote(do: foo(bar)[unquote(:baz)]) == quote(do: foo(bar)[:baz])
assert quote(do: unquote(:bar)()) == quote(do: bar())
@@ -216,7 +220,7 @@ defmodule Kernel.QuoteTest.ErrorsTest do
mod = Kernel.QuoteTest.ErrorsTest
file = __ENV__.file |> Path.relative_to_cwd |> String.to_char_list
assert [{^mod, :add, 2, [file: ^file, line: 196]}|_] = System.stacktrace
assert [{^mod, :add, 2, [file: ^file, line: 200]}|_] = System.stacktrace
end
test "outside function error" do
@@ -226,7 +230,7 @@ defmodule Kernel.QuoteTest.ErrorsTest do
mod = Kernel.QuoteTest.ErrorsTest
file = __ENV__.file |> Path.relative_to_cwd |> String.to_char_list
assert [{^mod, _, _, [file: ^file, line: 224]}|_] = System.stacktrace
assert [{^mod, _, _, [file: ^file, line: 228]}|_] = System.stacktrace
end
end
@@ -144,7 +144,7 @@ defmodule Kernel.TypespecTest do
test "@type with a binary with a base size" do
module = test_module do
@type mytype :: <<_ :: 3>>
@type mytype :: <<_::3>>
end
assert [type: {:mytype, {:type, _, :binary, [{:integer, _, 3}, {:integer, _, 0}]}, []}] =
@@ -160,6 +160,23 @@ defmodule Kernel.TypespecTest do
types(module)
end
test "@type with a binary with a size and unit size" do
module = test_module do
@type mytype :: <<_::3, _::_*8>>
end
assert [type: {:mytype, {:type, _, :binary, [{:integer, _, 3}, {:integer, _, 8}]}, []}] =
types(module)
end
test "@type with invalid binary spec" do
assert_raise CompileError, fn ->
test_module do
@type mytype :: <<_::3*8>>
end
end
end
test "@type with a range op" do
module = test_module do
@type mytype :: 1..10
@@ -545,9 +562,9 @@ defmodule Kernel.TypespecTest do
(quote do: @type simple_type() :: integer()),
(quote do: @type param_type(p) :: [p]),
(quote do: @type union_type() :: integer() | binary() | boolean()),
(quote do: @type binary_type1() :: <<_ :: _ * 8>>),
(quote do: @type binary_type2() :: <<_ :: 3 * 8>>),
(quote do: @type binary_type3() :: <<_ :: 3>>),
(quote do: @type binary_type1() :: <<_::_*8>>),
(quote do: @type binary_type2() :: <<_::3>>),
(quote do: @type binary_type3() :: <<_::3, _::_*8>>),
(quote do: @type tuple_type() :: {integer()}),
(quote do: @type ftype() :: (() -> any()) | (() -> integer()) | ((integer() -> integer()))),
(quote do: @type cl() :: char_list()),
+2
View File
@@ -207,6 +207,8 @@ defmodule PathTest do
assert Path.join(["/", "foo", "bar"]) == "/foo/bar"
assert Path.join(["~", "foo", "bar"]) == "~/foo/bar"
assert Path.join(['/foo/', "/bar/"]) == "/foo/bar"
assert Path.join(["/", ""]) == "/"
assert Path.join(["/", "", "bar"]) == "/bar"
end
test "join two" do
+28 -9
View File
@@ -164,15 +164,34 @@ defmodule StringTest do
assert String.capitalize("fin") == "Fin"
end
test "replace_leading" do
assert String.replace_leading("aa abc ", "a", "b") == "bb abc "
assert String.replace_leading("__ abc ", "_", "b") == "bb abc "
assert String.replace_leading("aaaaaaaa ", "a", "b") == "bbbbbbbb "
assert String.replace_leading("aaaaaaaa ", "aaa", "b") == "bbaa "
assert String.replace_leading("aaaaaaaaa", "a", "b") == "bbbbbbbbb"
assert String.replace_leading("]]]]]]", "]", "[]") == "[][][][][][]"
assert String.replace_leading("]]]]]]]]", "]", "") == ""
assert String.replace_leading("]]]]]] ]", "]", "") == " ]"
assert String.replace_leading("猫猫 cat ", "猫", "й") == "йй cat "
assert String.replace_leading("test", "t", "T") == "Test"
assert String.replace_leading("t", "t", "T") == "T"
assert String.replace_leading("aaa", "b", "c") == "aaa"
end
test "replace_trailing" do
assert String.replace_trailing(" abc aa", "a", "") == " abc "
assert String.replace_trailing(" abc __", "_", "") == " abc "
assert String.replace_trailing(" aaaaaaaaa", "a", "") == " "
assert String.replace_trailing("aaaaaaaaaa", "a", "") == ""
assert String.replace_trailing("]]]]]]]]]]", "]", "") == ""
assert String.replace_trailing(" cat 猫猫", "猫", "") == " cat "
assert String.replace_trailing("test", "t", "") == "tes"
assert String.replace_trailing("t", "t", "") == ""
assert String.replace_trailing(" abc aa", "a", "b") == " abc bb"
assert String.replace_trailing(" abc __", "_", "b") == " abc bb"
assert String.replace_trailing(" aaaaaaaa", "a", "b") == " bbbbbbbb"
assert String.replace_trailing(" aaaaaaaa", "aaa", "b") == " aabb"
assert String.replace_trailing("aaaaaaaaa", "a", "b") == "bbbbbbbbb"
assert String.replace_trailing("]]]]]]", "]", "[]") == "[][][][][][]"
assert String.replace_trailing("]]]]]]]]", "]", "") == ""
assert String.replace_trailing("] ]]]]]]", "]", "") == "] "
assert String.replace_trailing(" cat 猫猫", "猫", "й") == " cat йй"
assert String.replace_trailing("test", "t", "T") == "tesT"
assert String.replace_trailing("t", "t", "T") == "T"
assert String.replace_trailing("aaa", "b", "c") == "aaa"
end
test "rstrip" do
@@ -314,7 +333,7 @@ defmodule StringTest do
test "normalize" do
assert String.normalize("ŝ", :nfd) == "ŝ"
assert String.normalize("ḇravô", :nfd) == "ḇravô"
assert String.normalize("ḇravô", :nfd) == "ḇravô"
assert String.normalize("ṩierra", :nfd) == "ṩierra"
assert String.normalize("뢴", :nfd) == "뢴"
assert String.normalize("êchǭ", :nfc) == "êchǭ"
+1 -1
View File
@@ -505,7 +505,7 @@ defmodule String.Normalizer do
{n, rest} = String.Unicode.next_grapheme_size(binary)
part = :binary.part(binary, 0, n)
case n do
1 -> normalize_nfc(rest, acc <> part)
1 -> normalize_nfd(rest, acc <> part)
_ -> normalize_nfd(rest, acc <> canonical_order(part, []))
end
end
+1 -1
View File
@@ -562,7 +562,7 @@ defmodule Logger do
end
defp truncate(data, n) when is_function(data, 0),
do: Logger.Utils.truncate(data.(), n)
do: truncate(data.(), n)
defp truncate(data, n) when is_list(data) or is_binary(data),
do: Logger.Utils.truncate(data, n)
defp truncate(data, n),
+7 -2
View File
@@ -74,11 +74,16 @@ defmodule Logger.Backends.Console do
enabled: Keyword.get(colors, :enabled, IO.ANSI.enabled?)}
end
defp log_event(level, msg, ts, md, %{colors: colors} = state) do
defp log_event(level, msg, ts, md, %{colors: colors, device: device} = state) do
output =
format_event(level, msg, ts, md, state)
|> color_event(level, colors)
IO.write(state.device, output)
try do
IO.write(device, output)
rescue
ArgumentError ->
IO.write(device, Logger.Formatter.prune(output))
end
end
defp format_event(level, msg, ts, md, %{format: format, metadata: keys}) do
+20
View File
@@ -44,6 +44,26 @@ defmodule Logger.Formatter do
@type pattern :: :date | :level | :levelpad | :message | :metadata | :node | :time
@valid_patterns [:time, :date, :message, :level, :node, :metadata, :levelpad]
@default_pattern "\n$time $metadata[$level] $levelpad$message\n"
@replacement "�"
@doc """
Prune non-valid UTF-8 codepoints.
Typically called after formatting when the data cannot be printed.
"""
@spec prune(IO.chardata) :: IO.chardata
def prune(binary) when is_binary(binary), do: prune_binary(binary, "")
def prune([h|t]) when h in 0..1114111, do: [h|prune(t)]
def prune([h|t]), do: [prune(h)|prune(t)]
def prune([]), do: []
def prune(_), do: @replacement
defp prune_binary(<<h::utf8, t::binary>>, acc),
do: prune_binary(t, <<acc::binary, h::utf8>>)
defp prune_binary(<<_, t::binary>>, acc),
do: prune_binary(t, <<acc::binary, @replacement>>)
defp prune_binary(<<>>, acc),
do: acc
@doc ~S"""
Compiles a format string into an array that the `format/5` can handle.
+3 -3
View File
@@ -10,9 +10,9 @@ defmodule Logger.Watcher do
"""
def start_link(m, f, a) do
import Supervisor.Spec
child = worker(__MODULE__, [],
[function: :watcher, restart: :transient])
options = [strategy: :simple_one_for_one, name: @name]
child = worker(__MODULE__, [], [function: :watcher, restart: :transient])
options = [strategy: :simple_one_for_one, name: @name,
max_restarts: 30, max_seconds: 3]
case Supervisor.start_link([child], options) do
{:ok, _} = ok ->
_ = for {mod, handler, args} <- apply(m, f, a) do
@@ -4,6 +4,14 @@ defmodule Logger.FormatterTest do
import Logger.Formatter
test "prune/1" do
assert prune(1) == "�"
assert prune(<<"hí", 233>>) == "hí�"
assert prune(["hi"|233]) == ["hi"|"�"]
assert prune([233|"hi"]) == [233|"hi"]
assert prune([[]|[]]) == [[]]
end
defmodule CompileMod do
def format(_level, _msg, _ts, _md) do
true
+3 -3
View File
@@ -80,16 +80,16 @@ defmodule Logger.UtilsTest do
{'~ts~ts~ts', ["abcdeabcde", "", ""]}
end
test "timestamp" do
test "timestamp/1" do
assert {{_, _, _}, {_, _, _, _}} = timestamp(true)
end
test "format_date" do
test "format_date/1" do
date = {2015, 1, 30}
assert format_date(date) == ["2015", ?-, [?0, "1"], ?-, "30"]
end
test "format_time" do
test "format_time/1" do
time = {12, 30, 10, 1}
assert format_time(time) == ["12", ?:, "30", ?:, "10", ?., [?0, ?0, "1"]]
+10 -4
View File
@@ -145,10 +145,12 @@ defmodule Mix.Compilers.Elixir do
end
defp detect_kind(module) do
impl = Module.get_attribute(module, :impl)
cond do
impl = Module.get_attribute(module, :impl) ->
is_list(impl) and impl[:protocol] ->
{:impl, impl[:protocol]}
Module.get_attribute(module, :protocol) ->
is_list(Module.get_attribute(module, :protocol)) ->
:protocol
true ->
:module
@@ -218,8 +220,12 @@ defmodule Mix.Compilers.Elixir do
## Manifest handling
defp read_manifest(manifest) do
case :file.consult(manifest) do
{:ok, [@manifest_vsn|t]} -> t
try do
case :file.consult(manifest) do
{:ok, [@manifest_vsn|t]} -> t
_ -> []
end
rescue
_ -> []
end
end
+11 -7
View File
@@ -17,13 +17,17 @@ defmodule Mix.Dep.ElixirSCM do
end
def read(manifest_path \\ Mix.Project.manifest_path) do
case :file.consult(manifest(manifest_path)) do
{:ok, [{@manifest_vsn, vsn, scm}]} ->
{:ok, vsn, scm}
{:error, {_, :erl_parse, _}} ->
{:ok, "1.0.0", nil} # Force old version if file exists but old format
_ ->
:error
try do
case :file.consult(manifest(manifest_path)) do
{:ok, [{@manifest_vsn, vsn, scm}]} ->
{:ok, vsn, scm}
{:error, {_, :erl_parse, _}} ->
{:ok, "1.0.0", nil} # Force old version if file exists but old format
_ ->
:error
end
rescue
_ -> :error
end
end
end
+6 -2
View File
@@ -116,8 +116,12 @@ defmodule Mix.Tasks.Compile.Protocols do
end
defp read_manifest(manifest) do
case :file.consult(manifest) do
{:ok, [@manifest_vsn|t]} -> t
try do
case :file.consult(manifest) do
{:ok, [@manifest_vsn|t]} -> t
_ -> []
end
rescue
_ -> []
end
end
+1 -1
View File
@@ -1,6 +1,6 @@
{application, elixir,
[{description, "elixir"},
{vsn, "1.2.4"},
{vsn, "1.2.6"},
{modules, [
elixir
]},