Skip to content

Commit 4e7bfda

Browse files
committed
Detect unused clauses
1 parent 01474e0 commit 4e7bfda

File tree

8 files changed

+132
-83
lines changed

8 files changed

+132
-83
lines changed

lib/elixir/lib/module/types.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ defmodule Module.Types do
5353
end
5454

5555
defp warnings_from_clause(meta, args, guards, body, stack, context) do
56-
{_types, context} = Pattern.of_head(args, guards, meta, stack, context)
56+
dynamic = Module.Types.Descr.dynamic()
57+
expected = Enum.map(args, fn _ -> dynamic end)
58+
{_types, context} = Pattern.of_head(args, guards, expected, :default, meta, stack, context)
5759
{_type, context} = Expr.of_expr(body, stack, context)
5860
context.warnings
5961
end

lib/elixir/lib/module/types/descr.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,15 @@ defmodule Module.Types.Descr do
582582
def number_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_number) != 0, do: true
583583
def number_type?(_), do: false
584584

585+
@doc """
586+
Optimized version of `not empty?(intersection(atom(), type))`.
587+
"""
588+
def atom_type?(:term), do: true
589+
def atom_type?(%{dynamic: :term}), do: true
590+
def atom_type?(%{dynamic: %{atom: _}}), do: true
591+
def atom_type?(%{atom: _}), do: true
592+
def atom_type?(_), do: false
593+
585594
## Bitmaps
586595

587596
defp bitmap_to_quoted(val) do

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

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ defmodule Module.Types.Expr do
1010
functions_and_macros = list(tuple([atom(), list(tuple([atom(), integer()]))]))
1111
list_of_modules = list(atom())
1212

13+
@try_catch atom([:error, :exit, :throw])
14+
1315
@caller closed_map(
1416
__struct__: atom([Macro.Env]),
1517
aliases: aliases,
1618
context: atom([:match, :guard, nil]),
1719
context_modules: list_of_modules,
1820
file: binary(),
19-
function: union(tuple(), atom([nil])),
21+
function: union(tuple([atom(), integer()]), atom([nil])),
2022
functions: functions_and_macros,
2123
lexical_tracker: union(pid(), atom([nil])),
2224
line: integer(),
@@ -108,13 +110,13 @@ defmodule Module.Types.Expr do
108110
end
109111

110112
# left = right
111-
def of_expr({:=, _meta, [left_expr, right_expr]} = expr, stack, context) do
113+
def of_expr({:=, _, [left_expr, right_expr]} = expr, stack, context) do
112114
{right_type, context} = of_expr(right_expr, stack, context)
113115

114116
# We do not raise on underscore in case someone writes _ = raise "omg"
115117
case left_expr do
116118
{:_, _, ctx} when is_atom(ctx) -> {right_type, context}
117-
_ -> Pattern.of_match(left_expr, right_type, expr, stack, context)
119+
_ -> Pattern.of_match(left_expr, right_type, expr, {:match, expr}, stack, context)
118120
end
119121
end
120122

@@ -245,15 +247,16 @@ defmodule Module.Types.Expr do
245247
{expr_type, context} = of_expr(case_expr, stack, context)
246248

247249
clauses
248-
|> of_clauses(stack, [expr_type], {none(), context})
250+
|> of_clauses([expr_type], {:case, case_expr}, stack, {none(), context})
249251
|> dynamic_unless_static(stack)
250252
end
251253

252254
# TODO: fn pat -> expr end
253255
def of_expr({:fn, _meta, clauses}, stack, context) do
254-
[{:->, _, [args, _]} | _] = clauses
255-
expected = Enum.map(args, fn _ -> dynamic() end)
256-
{_acc, context} = of_clauses(clauses, stack, expected, {none(), context})
256+
[{:->, _, [head, _]} | _] = clauses
257+
{patterns, _guards} = extract_head(head)
258+
expected = Enum.map(patterns, fn _ -> dynamic() end)
259+
{_acc, context} = of_clauses(clauses, expected, :fn, stack, {none(), context})
257260
{fun(), context}
258261
end
259262

@@ -280,22 +283,22 @@ defmodule Module.Types.Expr do
280283
{acc, context}
281284

282285
{:catch, clauses}, acc_context ->
283-
of_clauses(clauses, stack, [atom([:error, :exit, :throw]), dynamic()], acc_context)
286+
of_clauses(clauses, [@try_catch, dynamic()], :try_catch, stack, acc_context)
284287

285288
{:else, clauses}, acc_context ->
286-
of_clauses(clauses, stack, [body_type], acc_context)
289+
of_clauses(clauses, [body_type], :try_else, stack, acc_context)
287290
end)
288291
|> dynamic_unless_static(stack)
289292
end
290293

291294
def of_expr({:receive, _meta, [blocks]}, stack, context) do
292295
blocks
293296
|> Enum.reduce({none(), context}, fn
294-
{:do, {:__block__, _, []}}, {acc, context} ->
295-
{acc, context}
297+
{:do, {:__block__, _, []}}, acc_context ->
298+
acc_context
296299

297-
{:do, clauses}, {acc, context} ->
298-
of_clauses(clauses, stack, [dynamic()], {acc, context})
300+
{:do, clauses}, acc_context ->
301+
of_clauses(clauses, [dynamic()], :receive, stack, acc_context)
299302

300303
{:after, [{:->, meta, [[timeout], body]}]}, {acc, context} ->
301304
{timeout_type, context} = of_expr(timeout, stack, context)
@@ -318,7 +321,7 @@ defmodule Module.Types.Expr do
318321
context = Enum.reduce(opts, context, &for_option(&1, stack, &2))
319322

320323
if Keyword.has_key?(opts, :reduce) do
321-
{_, context} = of_clauses(block, stack, [dynamic()], {none(), context})
324+
{_, context} = of_clauses(block, [dynamic()], :for_reduce, stack, {none(), context})
322325
{dynamic(), context}
323326
else
324327
{_type, context} = of_expr(block, stack, context)
@@ -441,16 +444,21 @@ defmodule Module.Types.Expr do
441444

442445
## Comprehensions
443446

444-
defp for_clause({:<-, meta, [left, expr]}, stack, context) do
447+
defp for_clause({:<-, _, [left, right]} = expr, stack, context) do
445448
{pattern, guards} = extract_head([left])
446-
{_expr_type, context} = of_expr(expr, stack, context)
447-
{[_type], context} = Pattern.of_head([pattern], guards, meta, stack, context)
449+
{_, context} = of_expr(right, stack, context)
450+
451+
{_type, context} =
452+
Pattern.of_match(pattern, guards, dynamic(), expr, {:for, expr}, stack, context)
453+
448454
context
449455
end
450456

451-
defp for_clause({:<<>>, _, [{:<-, meta, [left, right]}]}, stack, context) do
457+
defp for_clause({:<<>>, _, [{:<-, meta, [left, right]}]} = expr, stack, context) do
452458
{right_type, context} = of_expr(right, stack, context)
453-
{_pattern_type, context} = Pattern.of_match(left, binary(), left, stack, context)
459+
460+
{_pattern_type, context} =
461+
Pattern.of_match(left, binary(), expr, {:for, expr}, stack, context)
454462

455463
if binary_type?(right_type) do
456464
context
@@ -481,11 +489,13 @@ defmodule Module.Types.Expr do
481489

482490
## With
483491

484-
defp with_clause({:<-, meta, [left, expr]}, stack, context) do
492+
defp with_clause({:<-, meta, [left, right]} = expr, stack, context) do
485493
{pattern, guards} = extract_head([left])
486494

487-
{[_type], context} = Pattern.of_head([pattern], guards, meta, stack, context)
488-
{_expr_type, context} = of_expr(expr, stack, context)
495+
{[_type], context} =
496+
Pattern.of_head([pattern], guards, [dynamic()], {:with, expr}, meta, stack, context)
497+
498+
{_, context} = of_expr(right, stack, context)
489499
context
490500
end
491501

@@ -500,7 +510,7 @@ defmodule Module.Types.Expr do
500510
end
501511

502512
defp with_option({:else, clauses}, stack, context) do
503-
{_, context} = of_clauses(clauses, stack, [dynamic()], {none(), context})
513+
{_, context} = of_clauses(clauses, [dynamic()], :with_else, stack, {none(), context})
504514
context
505515
end
506516

@@ -532,10 +542,10 @@ defmodule Module.Types.Expr do
532542
defp dynamic_unless_static({_, _} = output, %{mode: :static}), do: output
533543
defp dynamic_unless_static({type, context}, %{mode: _}), do: {dynamic(type), context}
534544

535-
defp of_clauses(clauses, stack, _expected, acc_context) do
545+
defp of_clauses(clauses, expected, info, stack, acc_context) do
536546
Enum.reduce(clauses, acc_context, fn {:->, meta, [head, body]}, {acc, context} ->
537547
{patterns, guards} = extract_head(head)
538-
{_types, context} = Pattern.of_head(patterns, guards, meta, stack, context)
548+
{_types, context} = Pattern.of_head(patterns, guards, expected, info, meta, stack, context)
539549
{body, context} = of_expr(body, stack, context)
540550
{union(acc, body), context}
541551
end)

lib/elixir/lib/module/types/helpers.ex

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,21 +223,32 @@ defmodule Module.Types.Helpers do
223223
if Keyword.get(meta, :generated, false) do
224224
context
225225
else
226-
{fun, arity} = stack.function
227-
location = {stack.file, meta, {stack.module, fun, arity}}
228-
%{context | warnings: [{module, warning, location} | context.warnings]}
226+
effective_warn(module, warning, meta, stack, context)
229227
end
230228
end
231229

230+
defp effective_warn(module, warning, meta, stack, context) do
231+
{fun, arity} = stack.function
232+
location = {stack.file, meta, {stack.module, fun, arity}}
233+
%{context | warnings: [{module, warning, location} | context.warnings]}
234+
end
235+
232236
@doc """
233237
Emits an error.
234238
235239
In practice an error is a warning that halts other errors from being collected.
236240
"""
237241
def error(module, warning, meta, stack, context) do
238242
case context do
239-
%{failed: true} -> context
240-
%{failed: false} -> warn(module, warning, meta, stack, %{context | failed: true})
243+
%{failed: true} ->
244+
context
245+
246+
%{failed: false} ->
247+
if Keyword.get(meta, :generated, false) do
248+
context
249+
else
250+
effective_warn(module, warning, meta, stack, %{context | failed: true})
251+
end
241252
end
242253
end
243254

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ defmodule Module.Types.Of do
121121
{dynamic?, fallback, single, multiple, assert, context} =
122122
Enum.reduce(pairs, {false, none(), [], [], [], context}, fn
123123
{key, value}, {dynamic?, fallback, single, multiple, assert, context} ->
124-
{dynamic_key?, keys, context} = of_finite_key_type(key, stack, context, of_fun)
124+
{dynamic_key?, keys, context} = finite_key_type(key, stack, context, of_fun)
125125
{value_type, context} = of_fun.(value, stack, context)
126126
dynamic? = dynamic? or dynamic_key? or gradual?(value_type)
127127

@@ -164,11 +164,11 @@ defmodule Module.Types.Of do
164164
if dynamic?, do: {dynamic(map), context}, else: {map, context}
165165
end
166166

167-
defp of_finite_key_type(key, _stack, context, _of_fun) when is_atom(key) do
167+
defp finite_key_type(key, _stack, context, _of_fun) when is_atom(key) do
168168
{false, [key], context}
169169
end
170170

171-
defp of_finite_key_type(key, stack, context, of_fun) do
171+
defp finite_key_type(key, stack, context, of_fun) do
172172
{key_type, context} = of_fun.(key, stack, context)
173173

174174
case atom_fetch(key_type) do

0 commit comments

Comments
 (0)