Compare commits

...
14 Commits
Author SHA1 Message Date
José Valim c107a2fe26 Release v1.6.4 2018-03-16 12:10:21 +01:00
José Valim 17853e63d8 Ensure bitstring comprehensions work on OTP 20.3 (#7453)
Closes #7452

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2018-03-14 21:45:47 +01:00
José Valim 338e242a59 Avoid protocol dispatch and multiple lists conversion in the formatter
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2018-03-14 10:12:53 +01:00
José Valim 2de4680c6e Promote stats format first
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2018-03-14 10:12:21 +01:00
Damien Krotkine 1cffb06981 Remove comma from operators table (#7450)
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2018-03-14 10:12:02 +01:00
José Valim e0f1ba28dd Do not escape quoted keywords on formatting, closes #7451
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2018-03-14 10:11:59 +01:00
José Valim 45c7f828ef Release v1.6.3 2018-03-09 08:57:25 +01:00
Johan Suleiko Allansson 9169b6e302 Properly cancel timeout on compilation error in ParallelCompiler #7428 (#7429)
The :timed_out message was not cancelled properly when compilation failed in ParallelCompiler. This meant the timeout message would be delivered even after compilation completed. This could be problematic when invoking compilation programmatically via e.g. Mix.Task.run("compile", ["--return-errors"]) since it means that the calling process, depending on the implemenation, might crash unexpectedly or get its inbox filled with messages.

The fix simply calls the cancel_waiting_timer when a compilation error has ocurred in the same way it is called in other similar cases.

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2018-03-09 08:55:46 +01:00
José Valim c2bdc66b9e Update CHANGELOG 2018-03-07 12:31:46 +01:00
José Valim d16f354a3b Ensure proper reports from named DynamicSupervisor, closes #7425 2018-03-07 12:29:46 +01:00
José Valim dd30768354 Consider commas when breaking groups, closes #7406
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2018-03-03 00:21:12 +01:00
José Valim bf1d993b2f Ensure proper precedence between & and operators, closes #7412
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2018-03-03 00:20:48 +01:00
José Valim 755639f538 Consider .formatter.exs when formatting stdin
Closes #7411

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2018-03-03 00:20:22 +01:00
José Valim 97e0b1e1ff Support comments in the middle of pipelines and type expressions
Closes #7231

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2018-02-28 11:35:25 +01:00
20 changed files with 522 additions and 155 deletions
+29
View File
@@ -123,6 +123,35 @@ Those improvements will help developers better understand the relationship betwe
Other improvements in Mix include better compiler diagnostics for editor integration, support for the `--slowest N` flag in `mix test` that shows the slowest tests in your suite, and a new `mix profile.eprof` task that provides time based profiling, complementing the existing `mix profile.cprof` (count based) and `mix profile.fprof` (flame based).
## v1.6.4 (2018-03-16)
### 1. Bug fixes
#### Elixir
* [Code.Formatter] Do not double escape quoted keyword list identifiers
* [Kernel] Properly support `into: binary` in Erlang/OTP 20.3
## v1.6.3 (2018-03-09)
### 1. Enhancements
#### Elixir
* [Code.Formatter] Support comments in the middle of pipelines, `when` and `|` expressions
### 2. Bug fixes
#### Elixir
* [Code.Formatter] Consider commas when breaking groups
* [Code.Formatter] Ensure proper precedence between `&` and operators
* [Code.Formatter] Consider `.formatter.exs` when formatting stdin
#### Logger
* [Logger.Translator] Ensure logger doesn't crash when reporting named `DynamicSupervisor`
## v1.6.2 (2018-02-28)
### 1. Enhancements
+1 -1
View File
@@ -1 +1 @@
1.6.2
1.6.4
+171 -87
View File
@@ -10,6 +10,7 @@ defmodule Code.Formatter do
@min_line 0
@max_line 9_999_999
@empty empty()
@ampersand_prec Code.Identifier.unary_op(:&) |> elem(1)
# Operators that do not have space between operands
@no_space_binary_operators [:..]
@@ -17,8 +18,8 @@ defmodule Code.Formatter do
# Operators that do not have newline between operands (as well as => and keywords)
@no_newline_binary_operators [:\\, :in]
# Left associative operators that start on the next line in case of breaks
@left_new_line_before_binary_operators [:|>, :~>>, :<<~, :~>, :<~, :<~>, :<|>]
# Left associative operators that start on the next line in case of breaks (always pipes)
@pipeline_operators [:|>, :~>>, :<<~, :~>, :<~, :<~>, :<|>]
# Right associative operators that start on the next line in case of breaks
@right_new_line_before_binary_operators [:|, :when]
@@ -253,6 +254,7 @@ defmodule Code.Formatter do
|> Keyword.get(:locals_without_parens, [])
|> MapSet.new()
|> MapSet.union(@locals_without_parens)
|> MapSet.to_list()
%{
locals_without_parens: locals_without_parens,
@@ -325,12 +327,12 @@ defmodule Code.Formatter do
# Special AST nodes from compiler feedback.
defp quoted_to_algebra({:special, :clause_args, [args]}, _context, state) do
defp quoted_to_algebra({{:special, :clause_args}, _meta, [args]}, _context, state) do
{doc, state} = clause_args_to_algebra(args, state)
{group(doc), state}
end
defp quoted_to_algebra({:special, :bitstring_segment, [arg, last]}, _context, state) do
defp quoted_to_algebra({{:special, :bitstring_segment}, _meta, [arg, last]}, _context, state) do
bitstring_segment_to_algebra({arg, -1}, state, last)
end
@@ -553,7 +555,16 @@ defmodule Code.Formatter do
{left, state} =
case left_arg do
{:__block__, _, [atom]} when is_atom(atom) ->
{atom |> Code.Identifier.inspect_as_key() |> string(), state}
key =
case Code.Identifier.classify(atom) do
type when type in [:callable_local, :callable_operator, :not_callable] ->
IO.iodata_to_binary([Atom.to_string(atom), ?:])
_ ->
IO.iodata_to_binary([?", Atom.to_string(atom), ?", ?:])
end
{string(key), state}
{{:., _, [:erlang, :binary_to_atom]}, _, [{:<<>>, _, entries}, :utf8]} ->
interpolation_to_algebra(entries, @double_quote, state, "\"", "\":")
@@ -670,23 +681,75 @@ defmodule Code.Formatter do
# strict or flex mode around.
defp binary_op_to_algebra(op, op_string, meta, left_arg, right_arg, context, state) do
%{operand_nesting: nesting} = state
binary_op_to_algebra(op, op_string, meta, left_arg, right_arg, context, state, nil, nesting)
binary_op_to_algebra(op, op_string, meta, left_arg, right_arg, context, state, nesting)
end
defp binary_op_to_algebra(
op,
op_string,
meta,
left_arg,
right_arg,
context,
state,
parent_info,
nesting
) do
defp binary_op_to_algebra(op, op_string, meta, left_arg, right_arg, context, state, _nesting)
when op in @right_new_line_before_binary_operators do
op_info = Code.Identifier.binary_op(op)
left_context = force_many_args_or_operand(context, :parens_arg)
right_context = force_many_args_or_operand(context, :operand)
op_string = op_string <> " "
left_context = left_op_context(context)
right_context = right_op_context(context)
min_line =
case left_arg do
{_, left_meta, _} -> line(left_meta)
_ -> line(meta)
end
{operands, max_line} =
unwrap_right(right_arg, op, meta, right_context, [{{:root, left_context}, left_arg}])
operand_to_algebra = fn
{{:root, context}, arg}, _args, newlines, state ->
{doc, state} = binary_operand_to_algebra(arg, context, state, op, op_info, :left, 2)
{doc, @empty, newlines, state}
{{kind, context}, arg}, _args, newlines, state ->
{doc, state} = binary_operand_to_algebra(arg, context, state, op, op_info, kind, 0)
doc = doc |> nest_by_length(op_string) |> force_keyword(arg)
{concat(op_string, doc), @empty, newlines, state}
end
operand_to_algebra_with_comments(
operands,
meta,
min_line,
max_line,
state,
operand_to_algebra
)
end
defp binary_op_to_algebra(op, _, meta, left_arg, right_arg, context, state, _nesting)
when op in @pipeline_operators do
op_info = Code.Identifier.binary_op(op)
left_context = left_op_context(context)
right_context = right_op_context(context)
max_line = line(meta)
{pipes, min_line} =
unwrap_pipes(left_arg, meta, left_context, [{{op, right_context}, right_arg}])
operand_to_algebra = fn
{{:root, context}, arg}, _args, newlines, state ->
{doc, state} = binary_operand_to_algebra(arg, context, state, op, op_info, :left, 2)
{doc, @empty, newlines, state}
{{op, context}, arg}, _args, newlines, state ->
op_info = Code.Identifier.binary_op(op)
op_string = Atom.to_string(op) <> " "
{doc, state} = binary_operand_to_algebra(arg, context, state, op, op_info, :right, 0)
{concat(op_string, doc), @empty, newlines, state}
end
operand_to_algebra_with_comments(pipes, meta, min_line, max_line, state, operand_to_algebra)
end
defp binary_op_to_algebra(op, op_string, meta, left_arg, right_arg, context, state, nesting) do
op_info = Code.Identifier.binary_op(op)
left_context = left_op_context(context)
right_context = right_op_context(context)
{left, state} =
binary_operand_to_algebra(left_arg, left_context, state, op, op_info, :left, 2)
@@ -703,39 +766,6 @@ defmodule Code.Formatter do
op_string = " " <> op_string <> " "
concat(concat(group(left), op_string), group(right))
op in @left_new_line_before_binary_operators ->
op_string = op_string <> " "
# If the parent is of the same type (computed via same precedence),
# we cannot group the left side yet.
left = if op_info == parent_info, do: left, else: group(left)
doc = glue(left, concat(op_string, group(right)))
if Keyword.get(meta, :eol, false), do: force_unfit(doc), else: doc
op in @right_new_line_before_binary_operators ->
op_string = op_string <> " "
# If the parent is of the same type (computed via same precedence),
# we need to nest the left side because of the associativity.
left =
if op_info == parent_info do
nest_by_length(left, op_string)
else
group(left)
end
# If the right side is of the same type, we will keep recursing
# and do the nesting on the left side later on (as written above).
right =
case right_arg do
{^op, _, [_, _]} -> right
_ -> right |> nest_by_length(op_string) |> force_keyword(right_arg) |> group()
end
doc = glue(left, concat(op_string, right))
if Keyword.get(meta, :eol, false), do: force_unfit(doc), else: doc
true ->
next_break_fits? =
op in @next_break_fits_operators and next_break_fits?(right_arg, state) and
@@ -769,10 +799,11 @@ defmodule Code.Formatter do
end
defp binary_operand_to_algebra(operand, context, state, parent_op, parent_info, side, nesting) do
{parent_assoc, parent_prec} = parent_info
with {op, meta, [left, right]} <- operand,
op_info = Code.Identifier.binary_op(op),
{_assoc, prec} <- op_info do
{parent_assoc, parent_prec} = parent_info
op_string = Atom.to_string(op)
cond do
@@ -780,17 +811,7 @@ defmodule Code.Formatter do
# the correct side, we respect the nesting rule to avoid multiple
# nestings. This only applies for left associativity or same operator.
parent_prec == prec and parent_assoc == side and (side == :left or op == parent_op) ->
binary_op_to_algebra(
op,
op_string,
meta,
left,
right,
context,
state,
parent_info,
nesting
)
binary_op_to_algebra(op, op_string, meta, left, right, context, state, nesting)
# If the parent requires parens or the precedence is inverted or
# it is in the wrong side, then we *need* parenthesis.
@@ -799,16 +820,18 @@ defmodule Code.Formatter do
parent_op in @required_parens_logical_binary_operands) or parent_prec > prec or
(parent_prec == prec and parent_assoc != side) ->
{operand, state} =
binary_op_to_algebra(op, op_string, meta, left, right, context, state, parent_info, 2)
binary_op_to_algebra(op, op_string, meta, left, right, context, state, 2)
{wrap_in_parens(operand), state}
# Otherwise, we rely on precedence but also nest.
true ->
binary_op_to_algebra(op, op_string, meta, left, right, context, state, parent_info, 2)
binary_op_to_algebra(op, op_string, meta, left, right, context, state, 2)
end
else
{:&, _, [arg]} when not is_integer(arg) and side == :left ->
{:&, _, [arg]}
when not is_integer(arg) and side == :left
when not is_integer(arg) and parent_assoc == :left and parent_prec > @ampersand_prec ->
{doc, state} = quoted_to_algebra(operand, context, state)
{wrap_in_parens(doc), state}
@@ -817,6 +840,45 @@ defmodule Code.Formatter do
end
end
defp unwrap_pipes({op, meta, [left, right]}, _meta, context, acc)
when op in @pipeline_operators do
left_context = left_op_context(context)
right_context = right_op_context(context)
unwrap_pipes(left, meta, left_context, [{{op, right_context}, right} | acc])
end
defp unwrap_pipes(left, meta, context, acc) do
min_line =
case left do
{_, meta, _} -> line(meta)
_ -> line(meta)
end
{[{{:root, context}, left} | acc], min_line}
end
defp unwrap_right({op, meta, [left, right]}, op, _meta, context, acc) do
left_context = left_op_context(context)
right_context = right_op_context(context)
unwrap_right(right, op, meta, right_context, [{{:left, left_context}, left} | acc])
end
defp unwrap_right(right, _op, meta, context, acc) do
acc = [{{:right, context}, right} | acc]
{Enum.reverse(acc), line(meta)}
end
defp operand_to_algebra_with_comments(operands, meta, min_line, max_line, state, fun) do
{docs, comments?, state} =
quoted_to_algebra_with_comments(operands, [], min_line, max_line, 1, state, fun)
if comments? or Keyword.get(meta, :eol, false) do
{docs |> Enum.reduce(&line(&2, &1)) |> force_unfit(), state}
else
{docs |> Enum.reduce(&glue(&2, &1)), state}
end
end
## Module attributes
# @Foo
@@ -1025,7 +1087,7 @@ defmodule Code.Formatter do
#
defp call_args_to_algebra([], meta, _context, _parens, _list_to_keyword?, state) do
{args_doc, _join, state} =
args_to_algebra_with_comments([], meta, false, false, :glue, state, &{&1, &2})
args_to_algebra_with_comments([], meta, false, :none, :glue, state, &{&1, &2})
{{surround("(", args_doc, ")"), state}, false}
end
@@ -1073,6 +1135,7 @@ defmodule Code.Formatter do
call_args_to_algebra_with_no_parens_keywords(meta, left, right, context, extra, state)
else
next_break_fits? = next_break_fits?(right, state)
last_arg_mode = if next_break_fits?, do: :next_break_fits, else: :none
force_keyword? = keyword? and force_keyword?(right)
non_empty_eol? = left != [] and not next_break_fits? and Keyword.get(meta, :eol, false)
join = if generators_count > 1 or force_keyword? or non_empty_eol?, do: :line, else: :glue
@@ -1083,7 +1146,7 @@ defmodule Code.Formatter do
args,
meta,
skip_parens?,
next_break_fits?,
last_arg_mode,
join,
state,
&quoted_to_algebra(&1, context, &2)
@@ -1119,12 +1182,12 @@ defmodule Code.Formatter do
to_algebra_fun = &quoted_to_algebra(&1, context, &2)
{left_doc, _join, state} =
args_to_algebra_with_comments(left, meta, true, false, :glue, state, to_algebra_fun)
args_to_algebra_with_comments(left, meta, true, :force_comma, :glue, state, to_algebra_fun)
{right_doc, _join, state} =
args_to_algebra_with_comments(right, meta, false, false, :glue, state, to_algebra_fun)
args_to_algebra_with_comments(right, meta, false, :none, :glue, state, to_algebra_fun)
right_doc = "," |> glue(right_doc) |> force_keyword(right) |> group(:inherit)
right_doc = break() |> concat(right_doc) |> force_keyword(right) |> group(:inherit)
doc =
with_next_break_fits(true, right_doc, fn right_doc ->
@@ -1268,7 +1331,7 @@ defmodule Code.Formatter do
{args_doc, join, state} =
args
|> Enum.with_index()
|> args_to_algebra_with_comments(meta, false, false, join, state, to_algebra_fun)
|> args_to_algebra_with_comments(meta, false, :none, join, state, to_algebra_fun)
if join == :flex_glue do
{"<<" |> concat(args_doc) |> nest(2) |> concat(">>") |> group(), state}
@@ -1278,7 +1341,7 @@ defmodule Code.Formatter do
end
defp bitstring_segment_to_algebra({{:<-, meta, [left, right]}, i}, state, last) do
left = {:special, :bitstring_segment, [left, last]}
left = {{:special, :bitstring_segment}, meta, [left, last]}
{doc, state} = quoted_to_algebra({:<-, meta, [left, right]}, :parens_arg, state)
{bitstring_wrap_parens(doc, i, last), state}
end
@@ -1327,7 +1390,7 @@ defmodule Code.Formatter do
fun = &quoted_to_algebra(&1, :parens_arg, &2)
{args_doc, _join, state} =
args_to_algebra_with_comments(args, meta, false, false, join, state, fun)
args_to_algebra_with_comments(args, meta, false, :none, join, state, fun)
{surround("[", args_doc, "]"), state}
end
@@ -1338,7 +1401,7 @@ defmodule Code.Formatter do
{left_doc, state} = fun.(left, state)
{right_doc, _join, state} =
args_to_algebra_with_comments(right, meta, false, false, join, state, fun)
args_to_algebra_with_comments(right, meta, false, :none, join, state, fun)
args_doc =
left_doc
@@ -1354,7 +1417,7 @@ defmodule Code.Formatter do
fun = &quoted_to_algebra(&1, :parens_arg, &2)
{args_doc, _join, state} =
args_to_algebra_with_comments(args, meta, false, false, join, state, fun)
args_to_algebra_with_comments(args, meta, false, :none, join, state, fun)
name_doc = "%" |> concat(name_doc) |> concat("{")
{surround(name_doc, args_doc, "}"), state}
@@ -1365,7 +1428,7 @@ defmodule Code.Formatter do
fun = &quoted_to_algebra(&1, :parens_arg, &2)
{args_doc, join, state} =
args_to_algebra_with_comments(args, meta, false, false, join, state, fun)
args_to_algebra_with_comments(args, meta, false, :none, join, state, fun)
if join == :flex_glue do
{"{" |> concat(args_doc) |> nest(1) |> concat("}") |> group(), state}
@@ -1475,7 +1538,7 @@ defmodule Code.Formatter do
defp heredoc_line(["", _ | _]), do: nest(line(), :reset)
defp heredoc_line(_), do: line()
defp args_to_algebra_with_comments(args, meta, skip_parens?, next_break_fits?, join, state, fun) do
defp args_to_algebra_with_comments(args, meta, skip_parens?, last_arg_mode, join, state, fun) do
min_line = line(meta)
max_line = end_line(meta)
@@ -1483,10 +1546,11 @@ defmodule Code.Formatter do
{doc, state} = fun.(arg, state)
doc =
cond do
args != [] -> concat(doc, ",")
next_break_fits? -> next_break_fits(doc, :enabled)
true -> doc
case args do
[_ | _] -> concat_to_last_group(doc, ",")
[] when last_arg_mode == :force_comma -> concat_to_last_group(doc, ",")
[] when last_arg_mode == :next_break_fits -> next_break_fits(doc, :enabled)
[] when last_arg_mode == :none -> doc
end
{doc, @empty, newlines, state}
@@ -1701,7 +1765,7 @@ defmodule Code.Formatter do
fun = &clause_args_to_algebra/2
{args_docs, _join, state} =
args_to_algebra_with_comments([args], meta, false, false, :glue, state, fun)
args_to_algebra_with_comments([args], meta, false, :none, :glue, state, fun)
{args_docs, state}
end
@@ -1709,7 +1773,7 @@ defmodule Code.Formatter do
# fn a, b, c when d -> e end
defp clause_args_to_algebra([{:when, meta, args}], state) do
{args, right} = split_last(args)
left = {:special, :clause_args, [args]}
left = {{:special, :clause_args}, meta, [args]}
binary_op_to_algebra(:when, "when", meta, left, right, :no_parens_arg, state)
end
@@ -1839,6 +1903,9 @@ defmodule Code.Formatter do
## Quoted helpers
defp left_op_context(context), do: force_many_args_or_operand(context, :parens_arg)
defp right_op_context(context), do: force_many_args_or_operand(context, :operand)
defp force_many_args_or_operand(:no_parens_one_arg, _choice), do: :no_parens_arg
defp force_many_args_or_operand(:parens_one_arg, _choice), do: :parens_arg
defp force_many_args_or_operand(:no_parens_arg, _choice), do: :no_parens_arg
@@ -2000,26 +2067,29 @@ defmodule Code.Formatter do
)
end
# A literal list is a keyword or (... -> ...)
defp last_arg_to_keyword([_ | _] = arg, _list_to_keyword?) do
{keyword?(arg), arg}
end
# This is a list of tuples, it can be converted to keywords.
defp last_arg_to_keyword({:__block__, _, [[_ | _] = arg]} = block, true) do
if keyword?(arg), do: {true, arg}, else: {false, block}
end
# Otherwise we don't have a keyword.
defp last_arg_to_keyword(arg, _list_to_keyword?) do
{false, arg}
end
defp force_keyword?(keyword) do
match?([_, _ | _], keyword) and force_keyword?(keyword, MapSet.new())
match?([{_, _}, _ | _], keyword) and force_keyword?(keyword, MapSet.new())
end
defp force_keyword?([{{_, meta, _}, _} | keyword], lines) do
line = line(meta)
if line in lines do
if MapSet.member?(lines, line) do
false
else
force_keyword?(keyword, MapSet.put(lines, line))
@@ -2064,6 +2134,20 @@ defmodule Code.Formatter do
## Algebra helpers
# Relying on the inner document is brittle and error prone.
# It would be best if we had a mechanism to apply this.
defp concat_to_last_group({:doc_cons, left, right}, concat) do
{:doc_cons, left, concat_to_last_group(right, concat)}
end
defp concat_to_last_group({:doc_group, group, mode}, concat) do
{:doc_group, {:doc_cons, group, concat}, mode}
end
defp concat_to_last_group(other, concat) do
{:doc_cons, other, concat}
end
defp ungroup_if_group({:doc_group, group, _mode}), do: group
defp ungroup_if_group(other), do: other
+3
View File
@@ -101,6 +101,9 @@ defimpl Collectable, for: BitString do
acc, {:cont, x} when is_bitstring(x) ->
<<IO.iodata_to_binary(acc)::bitstring, x::bitstring>>
acc, :done when is_bitstring(acc) ->
acc
acc, :done ->
IO.iodata_to_binary(acc)
+8 -1
View File
@@ -495,7 +495,14 @@ defmodule DynamicSupervisor do
case mod.init(args) do
{:ok, flags} when is_map(flags) ->
state = %DynamicSupervisor{mod: mod, args: args, name: name || {self(), mod}}
name =
cond do
is_nil(name) -> {self(), mod}
is_atom(name) -> {:local, name}
is_tuple(name) -> name
end
state = %DynamicSupervisor{mod: mod, args: args, name: name}
case init(state, flags) do
{:ok, state} -> {:ok, state}
@@ -356,6 +356,7 @@ defmodule Kernel.ParallelCompiler do
{:file_done, child_pid, file, {kind, reason, stack}} ->
discard_down(child_pid)
print_error(file, kind, reason, stack)
cancel_waiting_timer(queued, child_pid)
terminate(queued)
{:error, [to_error(file, kind, reason, stack)], warnings}
+1 -1
View File
@@ -26,7 +26,7 @@ Operator
`\|` | Right to left
`::` | Right to left
`when` | Right to left
`<-`, `\\` | Left to right
`<-` `\\` | Left to right
## Comparison operators
+1 -1
View File
@@ -1,6 +1,6 @@
{application, elixir,
[{description, "elixir"},
{vsn, "1.6.2"},
{vsn, "1.6.4"},
{modules, [
elixir
]},
+17 -7
View File
@@ -21,7 +21,7 @@ translate(Meta, Args, Return, S) ->
case comprehension_expr(TInto, TExpr) of
{inline, TIntoExpr} ->
{build_inline(Ann, TCases, TIntoExpr, TInto, TUniq, SF), SF};
build_inline(Ann, TCases, TIntoExpr, TInto, TUniq, SF);
{into, TIntoExpr} ->
build_into(Ann, TCases, TIntoExpr, TInto, TUniq, SF)
end.
@@ -91,17 +91,17 @@ collect_filters([], Acc) ->
build_inline(Ann, Clauses, Expr, Into, Uniq, S) ->
case not Uniq and lists:all(fun(Clause) -> element(1, Clause) == bin end, Clauses) of
true -> build_comprehension(Ann, Clauses, Expr, Into);
true -> {build_comprehension(Ann, Clauses, Expr, Into), S};
false -> build_inline_each(Ann, Clauses, Expr, Into, Uniq, S)
end.
build_inline_each(Ann, Clauses, Expr, false, Uniq, S) ->
InnerFun = fun(InnerExpr, _InnerAcc) -> InnerExpr end,
build_reduce(Ann, Clauses, InnerFun, Expr, {nil, Ann}, Uniq, S);
{build_reduce(Ann, Clauses, InnerFun, Expr, {nil, Ann}, Uniq, S), S};
build_inline_each(Ann, Clauses, Expr, {nil, _} = Into, Uniq, S) ->
InnerFun = fun(InnerExpr, InnerAcc) -> {cons, Ann, InnerExpr, InnerAcc} end,
ReduceExpr = build_reduce(Ann, Clauses, InnerFun, Expr, Into, Uniq, S),
elixir_erl:remote(Ann, lists, reverse, [ReduceExpr]);
{elixir_erl:remote(Ann, lists, reverse, [ReduceExpr]), S};
build_inline_each(Ann, Clauses, Expr, {bin, _, []}, Uniq, S) ->
{InnerValue, SV} = build_var(Ann, S),
@@ -135,11 +135,21 @@ build_inline_each(Ann, Clauses, Expr, {bin, _, []}, Uniq, S) ->
end,
ReduceExpr = build_reduce(Ann, Clauses, InnerFun, Expr, {nil, Ann}, Uniq, SV),
elixir_erl:remote(Ann, erlang, iolist_to_binary, [ReduceExpr]).
{{'case', Ann, ReduceExpr, [
{clause, Ann,
[InnerValue],
[[elixir_erl:remote(Ann, erlang, is_bitstring, [InnerValue])]],
[InnerValue]},
{clause, Ann,
[InnerValue],
[],
[elixir_erl:remote(Ann, erlang, iolist_to_binary, [InnerValue])]}
]}, SV}.
build_into(Ann, Clauses, Expr, {map, _, []}, Uniq, S) ->
ReduceExpr = build_inline_each(Ann, Clauses, Expr, {nil, Ann}, Uniq, S),
{elixir_erl:remote(Ann, maps, from_list, [ReduceExpr]), S};
{ReduceExpr, SR} = build_inline_each(Ann, Clauses, Expr, {nil, Ann}, Uniq, S),
{elixir_erl:remote(Ann, maps, from_list, [ReduceExpr]), SR};
build_into(Ann, Clauses, Expr, Into, Uniq, S) ->
{Fun, SF} = build_var(Ann, S),
{Acc, SA} = build_var(Ann, SF),
@@ -201,6 +201,21 @@ defmodule Code.Formatter.CallsTest do
assert_format bad, good, @short_length
end
test "with arguments on comma limit" do
bad = """
import(foo(abc, cde), :next)
"""
good = """
import(
foo(abc, cde),
:next
)
"""
assert_format bad, good, @medium_length
end
test "with keyword lists" do
assert_same "foo(foo: 1, bar: 2)"
@@ -263,6 +278,22 @@ defmodule Code.Formatter.CallsTest do
assert_format bad, good, @short_length
end
test "without parens on comma limit" do
bad = """
import foo(abc, cde), :next
"""
good = """
import foo(
abc,
cde
),
:next
"""
assert_format bad, good, @medium_length
end
test "without parens and with keyword lists preserves multiline" do
assert_same """
defstruct foo: 1,
@@ -276,6 +307,22 @@ defmodule Code.Formatter.CallsTest do
"""
end
test "without parens and with keyword lists on comma limit" do
bad = """
import foo(abc, cde), opts: :next
"""
good = """
import foo(
abc,
cde
),
opts: :next
"""
assert_format bad, good, @medium_length
end
test "without parens and with keyword lists on line limit" do
assert_same "import :atom, opts: [foo: :bar]"
@@ -56,36 +56,19 @@ defmodule Code.Formatter.CommentsTest do
assert_format bad, good
bad = """
foo # this is foo
|> bar # this is bar
|> baz # this is baz
"""
good = """
# this is foo
# this is bar
# this is baz
foo
|> bar
|> baz
"""
assert_format bad, good, @short_length
bad = """
foo # this is foo
| bar # this is bar
| baz # this is baz
++ bar # this is bar
++ baz # this is baz
"""
good = """
# this is foo
# this is bar
# this is baz
foo
| bar
| baz
foo ++
bar ++
baz
"""
assert_format bad, good, @short_length
@@ -721,6 +704,118 @@ defmodule Code.Formatter.CommentsTest do
end
end
describe "operators" do
test "with comment before, during and after uniform pipelines" do
assert_same """
foo
# |> bar
# |> baz
|> bat
"""
bad = """
# before
foo # this is foo
|> bar # this is bar
|> baz # this is baz
# after
"""
good = """
# before
# this is foo
foo
# this is bar
|> bar
# this is baz
|> baz
# after
"""
assert_format bad, good, @short_length
end
test "with comment before, during and after mixed pipelines" do
assert_same """
foo
# |> bar
# |> baz
~> bat
"""
bad = """
# before
foo # this is foo
~> bar # this is bar
<|> baz # this is baz
# after
"""
good = """
# before
# this is foo
foo
# this is bar
~> bar
# this is baz
<|> baz
# after
"""
assert_format bad, good, @short_length
end
test "with comment before, during and after uniform right" do
assert_same """
foo
# | bar
# | baz
| bat
"""
bad = """
# before
foo # this is foo
| bar # this is bar
| baz # this is baz
# after
"""
good = """
# before
# this is foo
foo
# this is bar
| bar
# this is baz
| baz
# after
"""
assert_format bad, good, @short_length
end
test "with comment before, during and after mixed right" do
assert_same """
one
# when two
# when three
when four
# | five
| six
"""
end
test "handles nodes without meta info" do
assert_same "(a -> b) |> (c -> d)"
assert_same "(a -> b) when c: d"
assert_same "(a -> b) when (c -> d)"
end
end
describe "containers" do
test "with comment outside before, during and after" do
assert_same ~S"""
@@ -178,9 +178,30 @@ defmodule Code.Formatter.ContainersTest do
assert_same keyword, @short_length
end
test "with keyword lists on comma line limit" do
bad = """
[
foooo: 1,
barrr: 2
]
"""
good = """
[
foooo:
1,
barrr: 2
]
"""
assert_format bad, good, @short_length
end
test "with quoted keyword lists" do
assert_same ~S(["with spaces": 1])
assert_same ~S(["one #{two} three": 1])
assert_same ~S(["\w": 1, "\\w": 2])
assert_same ~S(["Elixir.Foo": 1, "Elixir.Bar": 2])
assert_format ~S(["Foo": 1, "Bar": 2]), ~S([Foo: 1, Bar: 2])
end
@@ -455,7 +476,7 @@ defmodule Code.Formatter.ContainersTest do
}
"""
assert_format bad, good, @short_length
assert_format bad, good, line_length: 11
end
test "removes trailing comma" do
@@ -559,7 +580,7 @@ defmodule Code.Formatter.ContainersTest do
}
"""
assert_format bad, good, @short_length
assert_format bad, good, line_length: 11
end
test "removes trailing comma" do
@@ -348,17 +348,17 @@ defmodule Code.Formatter.IntegrationTest do
test "no parens keywords at the end of the line" do
bad = """
defmodule Mod do
defp token_list_downcase(<<char, rest::binary>>, acc) when is_whitespace(char) or is_comma(char), do: token_list_downcase(rest, acc)
defp token_list_downcase(some_really_long_arg11, some_really_long_arg22, some_really_long_arg33), do: token_list_downcase(rest, acc)
def token_list_downcase(<<char, rest::binary>>, acc) when is_whitespace(char) or is_comma(char), do: token_list_downcase(rest, acc)
def token_list_downcase(some_really_long_arg11, some_really_long_arg22, some_really_long_arg33), do: token_list_downcase(rest, acc)
end
"""
assert_format bad, """
defmodule Mod do
defp token_list_downcase(<<char, rest::binary>>, acc) when is_whitespace(char) or is_comma(char),
def token_list_downcase(<<char, rest::binary>>, acc) when is_whitespace(char) or is_comma(char),
do: token_list_downcase(rest, acc)
defp token_list_downcase(some_really_long_arg11, some_really_long_arg22, some_really_long_arg33),
def token_list_downcase(some_really_long_arg11, some_really_long_arg22, some_really_long_arg33),
do: token_list_downcase(rest, acc)
end
"""
@@ -436,8 +436,8 @@ defmodule Code.Formatter.IntegrationTest do
"""
end
test "first argument in a call without parens" do
bad = """
test "first argument in a call without parens with comments" do
assert_same """
with bar ::
:ok
| :invalid
@@ -445,28 +445,58 @@ defmodule Code.Formatter.IntegrationTest do
| :other
"""
assert_format bad, """
# | :unknown
with bar ::
:ok
| :invalid
| :other
"""
bad = """
assert_same """
@spec bar ::
:ok
| :invalid
# | :unknown
| :other
"""
end
test "capture with operators" do
assert_same """
"this works" |> (&String.upcase/1) |> (&String.downcase/1)
"""
assert_same """
"this works" || (&String.upcase/1) || (&String.downcase/1)
"""
assert_same """
"this works" == (&String.upcase/1) == (&String.downcase/1)
"""
bad = """
"this works" = (&String.upcase/1) = (&String.downcase/1)
"""
assert_format bad, """
# | :unknown
@spec bar ::
:ok
| :invalid
| :other
"this works" = (&String.upcase/1) = &String.downcase/1
"""
bad = """
"this works" ++ (&String.upcase/1) ++ (&String.downcase/1)
"""
assert_format bad, """
"this works" ++ (&String.upcase/1) ++ &String.downcase/1
"""
bad = """
"this works" | (&String.upcase/1) | (&String.downcase/1)
"""
assert_format bad, """
"this works" | (&String.upcase/1) | &String.downcase/1
"""
bad = ~S"""
"this works" \\ (&String.upcase/1) \\ (&String.downcase/1)
"""
assert_format bad, ~S"""
"this works" \\ &String.upcase/1 \\ &String.downcase/1
"""
end
end
@@ -812,9 +812,9 @@ defmodule Code.Formatter.OperatorsTest do
end
test "with operators outside" do
assert_same "(& &1) == & &2"
assert_same "(& &1) and & &2"
assert_same "(&foo/1) and &bar/1"
assert_same "(& &1) == (& &2)"
assert_same "(& &1) and (& &2)"
assert_same "(&foo/1) and (&bar/1)"
assert_same "[(&IO.puts/1) | &IO.puts/2]"
end
+3 -3
View File
@@ -58,7 +58,7 @@ defmodule Kernel.FnTest do
assert (&List.flatten/1).([[0]]) == [0]
assert (&List.flatten/1).([[0]]) == [0]
assert (&List.flatten(&1)).([[0]]) == [0]
assert (&List.flatten(&1)) == &List.flatten/1
assert (&List.flatten(&1)) == (&List.flatten/1)
end
test "capture local" do
@@ -77,7 +77,7 @@ defmodule Kernel.FnTest do
assert (&is_atom/1).(:a)
assert (&is_atom/1).(:a)
assert (&is_atom(&1)).(:a)
assert (&is_atom(&1)) == &is_atom/1
assert (&is_atom(&1)) == (&is_atom/1)
end
test "capture macro" do
@@ -102,7 +102,7 @@ defmodule Kernel.FnTest do
mod = List
assert (&mod.flatten(&1)).([1, [2], 3]) == [1, 2, 3]
assert (&mod.flatten/1).([1, [2], 3]) == [1, 2, 3]
assert (&mod.flatten/1) == &List.flatten/1
assert (&mod.flatten/1) == (&List.flatten/1)
end
test "local partial application" do
@@ -835,8 +835,12 @@ defmodule Kernel.TypespecTest do
quote(do: @type(literal_keyword_list_type_key() :: [{binary(), integer()}])),
quote(do: @type(literal_empty_map() :: %{})),
quote(do: @type(literal_map_with_key() :: %{key: integer()})),
quote(do: @type(literal_map_with_required_key() :: %{required(bitstring()) => integer()})),
quote(do: @type(literal_map_with_optional_key() :: %{optional(bitstring()) => integer()})),
quote(
do: @type(literal_map_with_required_key() :: %{required(bitstring()) => integer()})
),
quote(
do: @type(literal_map_with_optional_key() :: %{optional(bitstring()) => integer()})
),
quote(do: @type(literal_struct_all_fields_any_type() :: %SomeStruct{})),
quote(do: @type(literal_struct_all_fields_key_type() :: %SomeStruct{key: integer()})),
quote(do: @type(literal_empty_tuple() :: {})),
@@ -863,7 +867,9 @@ defmodule Kernel.TypespecTest do
quote(do: @type(builtin_list() :: list())),
quote(do: @type(builtin_nonempty_list() :: nonempty_list())),
quote(do: @type(builtin_maybe_improper_list() :: maybe_improper_list())),
quote(do: @type(builtin_nonempty_maybe_improper_list() :: nonempty_maybe_improper_list())),
quote(
do: @type(builtin_nonempty_maybe_improper_list() :: nonempty_maybe_improper_list())
),
quote(do: @type(builtin_mfa() :: mfa())),
quote(do: @type(builtin_module() :: module())),
quote(do: @type(builtin_no_return() :: no_return())),
@@ -731,6 +731,23 @@ defmodule Logger.TranslatorTest do
"""
end
test "translates named DynamicSupervisor reports abnormal shutdown" do
assert capture_log(:info, fn ->
trap = Process.flag(:trap_exit, true)
child = %{id: __MODULE__, start: {__MODULE__, :abnormal, []}}
{:ok, pid} = DynamicSupervisor.start_link(strategy: :one_for_one, name: __MODULE__)
{:ok, _pid2} = DynamicSupervisor.start_child(pid, child)
Process.exit(pid, :normal)
receive do: ({:EXIT, ^pid, _} -> :ok)
Process.flag(:trap_exit, trap)
end) =~ ~r"""
\[error\] Child :undefined of Supervisor Logger.TranslatorTest shutdown abnormally
\*\* \(exit\) :stop
Pid: #PID<\d+\.\d+\.\d+>
Start Call: Logger.TranslatorTest.abnormal\(\)
"""
end
test "translates :supervisor_bridge progress" do
assert capture_log(:info, fn ->
trap = Process.flag(:trap_exit, true)
+3 -3
View File
@@ -308,7 +308,7 @@ defmodule Mix.Tasks.Format do
|> Enum.uniq()
end
defp expand_args(files_and_patterns, _dot_formatter, formatter_opts_and_subs) do
defp expand_args(files_and_patterns, _dot_formatter, {formatter_opts, subs}) do
files =
for file_or_pattern <- files_and_patterns,
file <- stdin_or_wildcard(file_or_pattern),
@@ -324,10 +324,10 @@ defmodule Mix.Tasks.Format do
for file <- files do
if file == :stdin do
{file, []}
{file, formatter_opts}
else
split = file |> Path.relative_to_cwd() |> Path.split()
{file, find_formatter_opts_for_file(split, formatter_opts_and_subs)}
{file, find_formatter_opts_for_file(split, {formatter_opts, subs})}
end
end
end
+1 -1
View File
@@ -63,7 +63,7 @@ defmodule Mix.Tasks.Xref do
Prints a file dependency graph where an edge from `A` to `B` indicates
that `A` depends on `B`.
mix xref graph --format dot
mix xref graph --format stats
The following options are accepted:
+17
View File
@@ -90,6 +90,23 @@ defmodule Mix.Tasks.FormatTest do
end)
end
test "reads file from stdin and prints to stdout with formatter", context do
in_tmp(context.test, fn ->
File.write!(".formatter.exs", """
[locals_without_parens: [foo: 1]]
""")
output =
capture_io("foo :bar", fn ->
Mix.Tasks.Format.run(["-"])
end)
assert output == """
foo :bar
"""
end)
end
test "checks if file is formatted with --check-formatted", context do
in_tmp(context.test, fn ->
File.write!("a.ex", """