Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c91d2cc91 | ||
|
|
3dbfb92860 | ||
|
|
a77b85665a | ||
|
|
f27a6735d6 | ||
|
|
e415148d3a | ||
|
|
9d95a0bcbf | ||
|
|
3df2a02d23 | ||
|
|
55a4c5980d | ||
|
|
30cbdb8fbf | ||
|
|
d1773cf96d | ||
|
|
5754da26f2 | ||
|
|
41b5091073 | ||
|
|
e5374cf5d3 | ||
|
|
352a357460 | ||
|
|
cc018f90bf | ||
|
|
9374fbeae6 | ||
|
|
9a44775b68 | ||
|
|
07c6356354 | ||
|
|
55b124a478 | ||
|
|
5249d1d2f0 | ||
|
|
59c0752143 | ||
|
|
39160df6a6 | ||
|
|
08d2aaaf53 | ||
|
|
715a38a6e5 | ||
|
|
7cc1ab4c63 | ||
|
|
3798dff0fc |
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>>)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
imports_hygiene=true,
|
||||
unquote=true,
|
||||
unquoted=false,
|
||||
escape=false
|
||||
escape=false,
|
||||
generated=false
|
||||
}).
|
||||
|
||||
-record(elixir_tokenizer, {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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ǭ"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -1,6 +1,6 @@
|
||||
{application, elixir,
|
||||
[{description, "elixir"},
|
||||
{vsn, "1.2.4"},
|
||||
{vsn, "1.2.6"},
|
||||
{modules, [
|
||||
elixir
|
||||
]},
|
||||
|
||||
Reference in New Issue
Block a user