Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c91d2cc91 | ||
|
|
3dbfb92860 | ||
|
|
a77b85665a | ||
|
|
f27a6735d6 | ||
|
|
e415148d3a | ||
|
|
9d95a0bcbf | ||
|
|
3df2a02d23 | ||
|
|
55a4c5980d | ||
|
|
30cbdb8fbf | ||
|
|
d1773cf96d | ||
|
|
5754da26f2 | ||
|
|
41b5091073 |
@@ -85,6 +85,19 @@ 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
|
||||
@@ -264,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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -333,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
|
||||
|
||||
@@ -220,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.5"},
|
||||
{vsn, "1.2.6"},
|
||||
{modules, [
|
||||
elixir
|
||||
]},
|
||||
|
||||
Reference in New Issue
Block a user