Skip to content

Commit 0ea809e

Browse files
authored
Check redundant clauses for definitions (#15124)
1 parent cd38841 commit 0ea809e

17 files changed

Lines changed: 447 additions & 291 deletions

File tree

lib/elixir/lib/calendar/date_range.ex

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,20 @@ defmodule Date.Range do
5959
end
6060

6161
# TODO: Remove me on v2.0
62-
def member?(
63-
%{__struct__: Date.Range, first_in_iso_days: first_days, last_in_iso_days: last_days} =
64-
date_range,
65-
date
66-
) do
62+
member? =
63+
quote generated: true do
64+
member?(
65+
%{
66+
__struct__: Date.Range,
67+
first_in_iso_days: var!(first_days),
68+
last_in_iso_days: var!(last_days)
69+
} =
70+
var!(date_range),
71+
var!(date)
72+
)
73+
end
74+
75+
def unquote(member?) do
6776
step = if first_days <= last_days, do: 1, else: -1
6877
member?(Map.put(date_range, :step, step), date)
6978
end
@@ -218,11 +227,11 @@ defmodule Date.Range do
218227
defimpl Inspect do
219228
import Kernel, except: [inspect: 2]
220229

221-
def inspect(%Date.Range{first: first, last: last, step: 1}, _) do
230+
def inspect(%Date.Range{first: first, last: last, step: 1}, %Inspect.Opts{}) do
222231
"Date.range(" <> inspect(first) <> ", " <> inspect(last) <> ")"
223232
end
224233

225-
def inspect(%Date.Range{first: first, last: last, step: step}, _) do
234+
def inspect(%Date.Range{first: first, last: last, step: step}, %Inspect.Opts{}) do
226235
"Date.range(" <> inspect(first) <> ", " <> inspect(last) <> ", #{step})"
227236
end
228237

lib/elixir/lib/enum.ex

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5168,7 +5168,16 @@ defimpl Enumerable, for: Range do
51685168
end
51695169

51705170
# TODO: Remove me on v2.0
5171-
def reduce(%{__struct__: Range, first: first, last: last} = range, acc, fun) do
5171+
reduce =
5172+
quote generated: true do
5173+
reduce(
5174+
%{__struct__: Range, first: var!(first), last: var!(last)} = var!(range),
5175+
var!(acc),
5176+
var!(fun)
5177+
)
5178+
end
5179+
5180+
def unquote(reduce) do
51725181
step = if first <= last, do: 1, else: -1
51735182
reduce(Map.put(range, :step, step), acc, fun)
51745183
end
@@ -5191,12 +5200,12 @@ defimpl Enumerable, for: Range do
51915200
{:done, acc}
51925201
end
51935202

5194-
def member?(first..last//step, value) when is_integer(value) do
5195-
if step > 0 do
5196-
{:ok, first <= value and value <= last and rem(value - first, step) == 0}
5197-
else
5198-
{:ok, last <= value and value <= first and rem(value - first, step) == 0}
5199-
end
5203+
def member?(first..last//step, value) when is_integer(value) and step > 0 do
5204+
{:ok, first <= value and value <= last and rem(value - first, step) == 0}
5205+
end
5206+
5207+
def member?(first..last//step, value) when is_integer(value) and step < 0 do
5208+
{:ok, last <= value and value <= first and rem(value - first, step) == 0}
52005209
end
52015210

52025211
# TODO: Remove me on v2.0
@@ -5219,7 +5228,13 @@ defimpl Enumerable, for: Range do
52195228
end
52205229

52215230
# TODO: Remove me on v2.0
5222-
def slice(%{__struct__: Range, first: first, last: last} = range) do
5231+
5232+
slice =
5233+
quote generated: true do
5234+
slice(%{__struct__: Range, first: var!(first), last: var!(last)} = var!(range))
5235+
end
5236+
5237+
def unquote(slice) do
52235238
step = if first <= last, do: 1, else: -1
52245239
slice(Map.put(range, :step, step))
52255240
end

lib/elixir/lib/inspect.ex

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,15 @@ defimpl Inspect, for: Range do
696696
end
697697

698698
# TODO: Remove me on v2.0
699-
def inspect(%{__struct__: Range, first: first, last: last} = range, opts) do
699+
inspect =
700+
quote generated: true do
701+
inspect(
702+
%{__struct__: Range, first: var!(first), last: var!(last)} = var!(range),
703+
var!(opts)
704+
)
705+
end
706+
707+
def unquote(inspect) do
700708
step = if first <= last, do: 1, else: -1
701709
inspect(Map.put(range, :step, step), opts)
702710
end

lib/elixir/lib/module/types.ex

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -319,16 +319,17 @@ defmodule Module.Types do
319319
defp local_handler(mode, fun_arity, kind, meta, clauses, expected, stack, context) do
320320
{fun, _arity} = fun_arity
321321
stack = stack |> fresh_stack(mode, fun_arity) |> with_file_meta(meta)
322+
base_info = {:def, kind, fun, expected}
322323

323-
{_, _, mapping, clauses_types, clauses_context} =
324-
Enum.reduce(clauses, {0, 0, [], [], context}, fn
325-
{meta, args, guards, body}, {index, total, mapping, inferred, context} ->
326-
context = fresh_context(context)
327-
info = {:def, kind, fun, args, guards, expected}
324+
{_, _, _, mapping, clauses_types, clauses_context} =
325+
Enum.reduce(clauses, {0, 0, [], [], [], context}, fn
326+
{meta, args, guards, body}, {index, total, previous, mapping, inferred, acc_context} ->
327+
fresh_context = fresh_context(acc_context)
328+
info = {base_info, args, guards}
328329

329330
try do
330-
{trees, _precise?, context} =
331-
Pattern.of_head(args, guards, expected, info, meta, stack, context)
331+
{trees, previous, context} =
332+
Pattern.of_head(args, guards, expected, previous, info, meta, stack, fresh_context)
332333

333334
{return_type, context} =
334335
Expr.of_expr(body, Descr.term(), body, stack, context)
@@ -339,9 +340,9 @@ defmodule Module.Types do
339340
add_inferred(inferred, args_types, return_type, total - 1, [])
340341

341342
if type_index == -1 do
342-
{index + 1, total + 1, [{index, total} | mapping], inferred, context}
343+
{index + 1, total + 1, previous, [{index, total} | mapping], inferred, context}
343344
else
344-
{index + 1, total, [{index, type_index} | mapping], inferred, context}
345+
{index + 1, total, previous, [{index, type_index} | mapping], inferred, context}
345346
end
346347
rescue
347348
e ->

lib/elixir/lib/module/types/expr.ex

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -719,62 +719,24 @@ defmodule Module.Types.Expr do
719719
defp dynamic_unless_static({_, _} = output, %{mode: :static}), do: output
720720
defp dynamic_unless_static({type, context}, %{mode: _}), do: {dynamic(type), context}
721721

722-
defp of_clauses(clauses, domain, expected, clause_info, stack, context, acc) do
722+
defp of_clauses(clauses, domain, expected, base_info, stack, context, acc) do
723723
fun = fn _args_types, result, _context, acc -> union(result, acc) end
724-
of_clauses_fun(clauses, domain, expected, clause_info, stack, context, acc, fun)
724+
of_clauses_fun(clauses, domain, expected, base_info, stack, context, acc, fun)
725725
end
726726

727-
defp of_clauses_fun(clauses, domain, expected, clause_info, stack, original, acc, fun) do
727+
defp of_clauses_fun(clauses, domain, expected, base_info, stack, original, acc, fun) do
728728
%{failed: failed?} = original
729729

730730
{result, _previous, context} =
731731
Enum.reduce(clauses, {acc, [], original}, fn
732-
{:->, meta, [head, body]} = clause, {acc, previous, context} ->
732+
{:->, meta, [head, body]}, {acc, previous, context} ->
733733
{failed?, context} = reset_failed(context, failed?)
734734
{patterns, guards} = extract_head(head)
735-
info = {clause_info, head, previous}
735+
info = {base_info, head}
736736

737-
{trees, precise?, context} =
737+
{trees, previous, context} =
738738
Pattern.of_head(patterns, guards, domain, previous, info, meta, stack, context)
739739

740-
# It failed, let's try to detect if it was due a previous or current clause.
741-
# The current clause will be easier to understand, so we prefer that
742-
{trees, precise?, context} =
743-
if context.failed and previous != [] do
744-
info = {clause_info, head, []}
745-
746-
case Pattern.of_head(patterns, guards, domain, info, meta, stack, context) do
747-
{_, _, %{failed: true}} = result -> result
748-
_ -> {trees, precise?, context}
749-
end
750-
else
751-
{trees, precise?, context}
752-
end
753-
754-
{previous, context} =
755-
if context.failed do
756-
{previous, context}
757-
else
758-
args_types =
759-
Enum.map(trees, fn {tree, _, _} ->
760-
tree
761-
|> Pattern.of_pattern_tree(stack, context)
762-
|> upper_bound()
763-
end)
764-
765-
cond do
766-
stack.mode != :infer and previous != [] and
767-
Pattern.args_subtype?(args_types, previous) ->
768-
{previous, Pattern.redundant_warn(clause, previous, stack, context)}
769-
770-
precise? ->
771-
{[args_types | previous], context}
772-
773-
true ->
774-
{previous, context}
775-
end
776-
end
777-
778740
{result, context} = of_expr(body, expected, body, stack, context)
779741

780742
{fun.(trees, result, context, acc), previous,

lib/elixir/lib/module/types/of.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ defmodule Module.Types.Of do
187187
}),
188188
do: %{context | subpatterns: subpatterns, vars: vars, conditional_vars: conditional_vars}
189189

190+
@doc """
191+
Returns true if all entries have the same conditional vars.
192+
"""
193+
def all_same_conditional_vars?([{_, cond} | tail]) do
194+
Enum.all?(tail, fn {_, tail_cond} -> cond == tail_cond end)
195+
end
196+
190197
@doc """
191198
Executes the args with acc using conditional variables.
192199
"""

0 commit comments

Comments
 (0)