From 6aca6560022f22d2ab0dc4de0724387b035f040f Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Thu, 26 Sep 2024 23:42:08 +0200 Subject: [PATCH] do not infer special forms as local calls --- lib/elixir_sense/core/type_inference.ex | 48 ++++++++++++++++ .../elixir_sense/core/type_inference_test.exs | 55 +++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/lib/elixir_sense/core/type_inference.ex b/lib/elixir_sense/core/type_inference.ex index 26a02804..e31a1fbc 100644 --- a/lib/elixir_sense/core/type_inference.ex +++ b/lib/elixir_sense/core/type_inference.ex @@ -167,6 +167,54 @@ defmodule ElixirSense.Core.TypeInference do {:list, list |> Enum.map(&type_of(&1, context))} end + # block expressions + def type_of({:__block__, _meta, exprs}, context) do + case List.last(exprs) do + nil -> nil + last_expr -> type_of(last_expr, context) + end + end + + # anonymous functions + def type_of({:fn, _meta, _clauses}, _context), do: nil + + # special forms + # for case/cond/with/receive/for/try we have no idea what the type is going to be + # we don't support binaries + # TODO guard? + # other are not worth handling + def type_of({form, _meta, _clauses}, _context) + when form in [ + :case, + :cond, + :try, + :receive, + :for, + :with, + :quote, + :unquote, + :unquote_splicing, + :import, + :alias, + :require, + :__aliases__, + :__cursor__, + :__DIR__, + :super, + :<<>>, + :"::" + ], + do: nil + + # __ENV__ is already expanded to map + def type_of({form, _meta, _clauses}, _context) when form in [:__CALLER__] do + {:struct, [], {:atom, Macro.Env}, nil} + end + + def type_of({:__STACKTRACE__, _meta, _clauses}, _context) do + {:list, nil} + end + # local call def type_of({var, _, args}, context) when is_atom(var) and is_list(args) do {:local_call, var, Enum.map(args, &type_of(&1, context))} diff --git a/test/elixir_sense/core/type_inference_test.exs b/test/elixir_sense/core/type_inference_test.exs index 5059395c..ca63badb 100644 --- a/test/elixir_sense/core/type_inference_test.exs +++ b/test/elixir_sense/core/type_inference_test.exs @@ -488,5 +488,60 @@ defmodule ElixirSense.Core.TypeInferenceTest do assert type_of("\"asd\"") == nil assert type_of("1.23") == nil end + + test "__STACKTRACE__ returns {:list, nil}" do + assert type_of("__STACKTRACE__") == {:list, nil} + end + + test "anonymous function returns nil" do + assert type_of("fn -> a end") == nil + assert type_of("fn x -> x + 1 end") == nil + assert type_of("fn x, y -> x * y end") == nil + end + end + + describe "block expressions" do + test "non-empty block returns type of last expression" do + assert type_of("(a = 1; b = 2; c = 3)") == {:integer, 3} + + assert type_of(""" + ( + a = 1 + b = 2 + c = 3 + ) + """) == {:integer, 3} + end + + test "empty block returns nil" do + assert type_of("( )") == nil + end + + test "__CALLER__ returns {:struct, [], {:atom, Macro.Env}, nil}" do + assert type_of("__CALLER__") == {:struct, [], {:atom, Macro.Env}, nil} + end + end + + describe "special forms" do + special_forms = [ + "case a do\n :ok -> 1\n :error -> 2\nend", + "cond do\n a -> 1\n b -> 2\nend", + "try do\n risky_operation()\nrescue\n e -> handle(e)\nend", + "receive do\n {:msg, msg} -> process(msg)\nend", + "for x <- list, do: x * 2", + "with {:ok, a} <- fetch_a(), {:ok, b} <- fetch_b(a), do: a + b", + "quote do: a + b", + "unquote(expr)", + "unquote_splicing(expr)", + "import Module", + "alias Module.SubModule", + "require Module" + ] + + for form <- special_forms do + test "special form: #{inspect(form)} returns nil" do + assert type_of(unquote(form)) == nil + end + end end end