Skip to content

Commit bc54e5f

Browse files
committed
Convert more expressions back into their original calls
1 parent 6ef8979 commit bc54e5f

File tree

4 files changed

+200
-74
lines changed

4 files changed

+200
-74
lines changed

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

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -325,12 +325,15 @@ defmodule Module.Types.Expr do
325325
{:rescue, clauses}, acc_context ->
326326
Enum.reduce(clauses, acc_context, fn
327327
{:->, _, [[{:in, meta, [var, exceptions]} = expr], body]}, {acc, context} ->
328-
{type, context} = of_rescue(var, exceptions, body, expr, [], meta, stack, context)
328+
{type, context} =
329+
of_rescue(var, exceptions, body, expr, :rescue, meta, stack, context)
330+
329331
{union(type, acc), context}
330332

331333
{:->, meta, [[var], body]}, {acc, context} ->
332-
hint = [:anonymous_rescue]
333-
{type, context} = of_rescue(var, [], body, var, hint, meta, stack, context)
334+
{type, context} =
335+
of_rescue(var, [], body, var, :anonymous_rescue, meta, stack, context)
336+
334337
{union(type, acc), context}
335338
end)
336339

@@ -532,7 +535,7 @@ defmodule Module.Types.Expr do
532535

533536
## Try
534537

535-
defp of_rescue(var, exceptions, body, expr, hints, meta, stack, original) do
538+
defp of_rescue(var, exceptions, body, expr, info, meta, stack, original) do
536539
args = [__exception__: @atom_true]
537540

538541
{structs, context} =
@@ -557,11 +560,8 @@ defmodule Module.Types.Expr do
557560

558561
_ ->
559562
expected = if structs == [], do: @exception, else: Enum.reduce(structs, &union/2)
560-
formatter = fn expr -> {"rescue #{expr_to_string(expr)} ->", hints} end
561-
562-
{_ok?, _type, context} =
563-
Of.refine_head_var(var, expected, expr, formatter, stack, context)
564-
563+
expr = {:__block__, [type_check: info], [expr]}
564+
{_ok?, _type, context} = Of.refine_head_var(var, expected, expr, stack, context)
565565
context
566566
end
567567

@@ -695,9 +695,10 @@ defmodule Module.Types.Expr do
695695
apply_one(mod, fun, args, expected, expr, stack, context)
696696
end
697697

698-
defp apply_many(mods, fun, args, expected, expr, stack, context) do
698+
defp apply_many(mods, fun, args, expected, {remote, meta, args}, stack, context) do
699699
{returns, context} =
700700
Enum.map_reduce(mods, context, fn mod, context ->
701+
expr = {remote, [type_check: {:invoked_as, mod, fun, length(args)}] ++ meta, args}
701702
apply_one(mod, fun, args, expected, expr, stack, context)
702703
end)
703704

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

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ defmodule Module.Types.Helpers do
137137
type: :variable,
138138
name: name,
139139
context: context,
140-
traces: collect_var_traces(off_traces)
140+
traces: collect_var_traces(expr, off_traces)
141141
})}
142142

143143
_ ->
@@ -153,40 +153,50 @@ defmodule Module.Types.Helpers do
153153
|> Enum.sort_by(& &1.name)
154154
end
155155

156-
defp collect_var_traces(traces) do
156+
defp collect_var_traces(parent_expr, traces) do
157157
traces
158-
|> Enum.reject(fn {_expr, _file, type, _formatter} ->
158+
|> Enum.reject(fn {expr, _file, type} ->
159159
# As an otimization do not care about dynamic terms
160-
type == %{dynamic: :term}
160+
type == %{dynamic: :term} or expr == parent_expr
161161
end)
162162
|> case do
163163
[] -> traces
164164
filtered -> filtered
165165
end
166166
|> Enum.reverse()
167-
|> Enum.map(fn {expr, file, type, formatter} ->
167+
|> Enum.map(fn {expr, file, type} ->
168168
meta = get_meta(expr)
169169

170-
{formatted_expr, formatter_hints} =
171-
case formatter do
172-
:default -> {expr_to_string(expr), []}
173-
formatter -> formatter.(expr)
174-
end
175-
176170
# This information is exposed to language servers and
177171
# therefore must remain backwards compatible.
178172
%{
179173
file: file,
180174
meta: meta,
181-
formatted_expr: formatted_expr,
182-
formatted_hints: format_hints(formatter_hints ++ expr_hints(expr)),
175+
formatted_expr: expr_to_string(expr),
176+
formatted_hints: format_hints(expr_hints(expr)),
183177
formatted_type: Module.Types.Descr.to_quoted_string(type, collapse_structs: true)
184178
}
185179
end)
186180
|> Enum.sort_by(&{&1.meta[:line], &1.meta[:column]})
187181
|> Enum.dedup()
188182
end
189183

184+
defp expr_hints(expr) do
185+
case expr do
186+
{:<<>>, [inferred_bitstring_spec: true] ++ _meta, _} ->
187+
[:inferred_bitstring_spec]
188+
189+
{_, meta, _} ->
190+
case meta[:type_check] do
191+
:anonymous_rescue -> [:anonymous_rescue]
192+
_ -> []
193+
end
194+
195+
_ ->
196+
[]
197+
end
198+
end
199+
190200
@doc """
191201
Format previously collected traces.
192202
"""
@@ -230,18 +240,37 @@ defmodule Module.Types.Helpers do
230240
defp pluralize([_], singular, _plural), do: singular
231241
defp pluralize(_, _singular, plural), do: plural
232242

233-
defp expr_hints({:<<>>, [inferred_bitstring_spec: true] ++ _meta, _}),
234-
do: [:inferred_bitstring_spec]
235-
236-
defp expr_hints(_), do: []
237-
238243
@doc """
239244
Converts the given expression to a string,
240245
translating inlined Erlang calls back to Elixir.
241246
242247
We also undo some macro expressions done by the Kernel module.
243248
"""
244249
def expr_to_string(expr) do
250+
string = prewalk_expr_to_string(expr)
251+
252+
case expr do
253+
{_, meta, _} ->
254+
case meta[:type_check] do
255+
:anonymous_rescue ->
256+
"rescue " <> string
257+
258+
:rescue ->
259+
"rescue " <> string
260+
261+
{:invoked_as, mod, fun, arity} ->
262+
string <> "\n#=> invoked as " <> Exception.format_mfa(mod, fun, arity)
263+
264+
_ ->
265+
string
266+
end
267+
268+
_ ->
269+
string
270+
end
271+
end
272+
273+
defp prewalk_expr_to_string(expr) do
245274
expr
246275
|> Macro.prewalk(fn
247276
{:%, _, [Range, {:%{}, _, fields}]} = node ->
@@ -264,6 +293,42 @@ defmodule Module.Types.Helpers do
264293
{{:., _, [mod, fun]}, meta, args} ->
265294
erl_to_ex(mod, fun, args, meta)
266295

296+
{:case, meta, [expr, [do: clauses]]} = case ->
297+
if meta[:type_check] == :expr do
298+
case clauses do
299+
[
300+
{:->, _,
301+
[
302+
[
303+
{:when, _,
304+
[
305+
{var, _, Kernel},
306+
{{:., _, [:erlang, :orelse]}, _,
307+
[
308+
{{:., _, [:erlang, :"=:="]}, _, [{var, _, Kernel}, false]},
309+
{{:., _, [:erlang, :"=:="]}, _, [{var, _, Kernel}, nil]}
310+
]}
311+
]}
312+
],
313+
else_block
314+
]},
315+
{:->, _, [[{:_, _, Kernel}], do_block]}
316+
] ->
317+
{:if, meta, [expr, [do: do_block, else: else_block]]}
318+
319+
[
320+
{:->, _, [[false], else_block]},
321+
{:->, _, [[true], do_block]}
322+
] ->
323+
{:if, meta, [expr, [do: do_block, else: else_block]]}
324+
325+
_ ->
326+
case
327+
end
328+
else
329+
case
330+
end
331+
267332
other ->
268333
other
269334
end)

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ defmodule Module.Types.Of do
5959
data = %{
6060
data
6161
| type: new_type,
62-
off_traces: new_trace(expr, new_type, :default, stack, off_traces)
62+
off_traces: new_trace(expr, new_type, stack, off_traces)
6363
}
6464

6565
%{context | vars: %{vars | version => data}}
@@ -81,7 +81,7 @@ defmodule Module.Types.Of do
8181
because we want to refine types. Otherwise we should
8282
use compatibility.
8383
"""
84-
def refine_head_var(var, type, expr, formatter \\ :default, stack, context) do
84+
def refine_head_var(var, type, expr, stack, context) do
8585
{var_name, meta, var_context} = var
8686
version = Keyword.fetch!(meta, :version)
8787

@@ -92,7 +92,7 @@ defmodule Module.Types.Of do
9292
data = %{
9393
data
9494
| type: new_type,
95-
off_traces: new_trace(expr, type, formatter, stack, off_traces)
95+
off_traces: new_trace(expr, type, stack, off_traces)
9696
}
9797

9898
context = %{context | vars: %{vars | version => data}}
@@ -110,19 +110,19 @@ defmodule Module.Types.Of do
110110
type: type,
111111
name: var_name,
112112
context: var_context,
113-
off_traces: new_trace(expr, type, formatter, stack, [])
113+
off_traces: new_trace(expr, type, stack, [])
114114
}
115115

116116
context = %{context | vars: Map.put(vars, version, data)}
117117
{:ok, type, context}
118118
end
119119
end
120120

121-
defp new_trace(nil, _type, _formatter, _stack, traces),
121+
defp new_trace(nil, _type, _stack, traces),
122122
do: traces
123123

124-
defp new_trace(expr, type, formatter, stack, traces),
125-
do: [{expr, stack.file, type, formatter} | traces]
124+
defp new_trace(expr, type, stack, traces),
125+
do: [{expr, stack.file, type} | traces]
126126

127127
## Implementations
128128

0 commit comments

Comments
 (0)