Compare commits

...
12 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
23 changed files with 204 additions and 132 deletions
+16
View File
@@ -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).
+1 -1
View File
@@ -1 +1 @@
1.2.5
1.2.6
+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
+3 -3
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.
+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
+1 -1
View File
@@ -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ǭ"
+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
+6 -2
View File
@@ -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
+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.5"},
{vsn, "1.2.6"},
{modules, [
elixir
]},