Skip to content

Commit 3c15bcc

Browse files
author
José Valim
committed
Work around a dialyzer bug
1 parent 40cda34 commit 3c15bcc

File tree

2 files changed

+36
-18
lines changed

2 files changed

+36
-18
lines changed

lib/elixir/lib/binary/inspect.ex

+4-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,10 @@ defimpl Binary.Inspect, for: Atom do
130130
valid_ref_identifier?(binary) ->
131131
"Elixir-" <> rest = binary
132132
bc <<r>> inbits rest, do: <<to_dot(r)>>
133-
atom in Macro.binary_ops or atom in Macro.unary_ops ->
133+
# Unfortunately we cannot write these as in
134+
# clauses because it causes dialyzer to hang
135+
# See docs for `Macro.binary_ops`.
136+
List.member?(Macro.binary_ops, atom) or List.member?(Macro.unary_ops, atom) ->
134137
":" <> binary
135138
true ->
136139
":" <> escape(binary, ?")

lib/elixir/lib/macro.ex

+32-17
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ defmodule Macro do
88
@doc """
99
Returns a list of binary operators. This is available
1010
as a macro so it can be used in guard clauses.
11+
12+
## Note
13+
14+
There is a bug in dialyzer that makes it hang (it actually
15+
just takes a huge amount of time) when using `in` with a huge
16+
list. That said, using `atom in Macro.binary_op` will trigger
17+
this bug and should be avoided in modules you want to use
18+
dialyzer with.
1119
"""
1220
defmacro binary_ops do
1321
[
@@ -198,22 +206,24 @@ defmodule Macro do
198206
"&#{num}"
199207
end
200208

201-
# Binary ops
202-
def to_binary({ op, _, [left, right] }) when op in binary_ops do
203-
op_to_binary(left) <> " #{op} " <> op_to_binary(right)
204-
end
205-
206-
# Unary ops
207-
def to_binary({ op, _, [arg] }) when op in unary_ops do
208-
atom_to_binary(op, :utf8) <> to_binary(arg)
209-
end
210-
211-
# All other calls
209+
# Binary, unary ops and other calls
210+
#
211+
# Originally, binary and unary ops were different function
212+
# clauses but they were causing dialyzer to hang. We have
213+
# rewritten it as `cond` as it seems to be fine to dialyzer.
212214
def to_binary({ target, _, args }) when is_list(args) do
213-
{ list, last } = :elixir_tree_helpers.split_last(args)
214-
case is_kw_blocks?(last) do
215-
true -> call_to_binary_with_args(target, list) <> kw_blocks_to_binary(last)
216-
false -> call_to_binary_with_args(target, args)
215+
cond do
216+
is_atom(target) and List.member?(binary_ops, target) and length(args) == 2 ->
217+
[left, right] = args
218+
op_to_binary(left) <> " #{target} " <> op_to_binary(right)
219+
is_atom(target) and List.member?(unary_ops, target) and length(args) == 1 ->
220+
atom_to_binary(target, :utf8) <> to_binary(hd(args))
221+
true ->
222+
{ list, last } = :elixir_tree_helpers.split_last(args)
223+
case is_kw_blocks?(last) do
224+
true -> call_to_binary_with_args(target, list) <> kw_blocks_to_binary(last)
225+
false -> call_to_binary_with_args(target, args)
226+
end
217227
end
218228
end
219229

@@ -278,8 +288,13 @@ defmodule Macro do
278288

279289
defp block_to_binary(other), do: to_binary(other)
280290

281-
defp op_to_binary({ op, _, [_, _] } = expr) when op in binary_ops do
282-
"(" <> to_binary(expr) <> ")"
291+
defp op_to_binary({ op, _, [_, _] } = expr) do
292+
# Avoid op in binary_ops due to dialyzer bug
293+
if List.member?(binary_ops, op) do
294+
"(" <> to_binary(expr) <> ")"
295+
else
296+
to_binary(expr)
297+
end
283298
end
284299

285300
defp op_to_binary(expr), do: to_binary(expr)

0 commit comments

Comments
 (0)