Search code examples
elixirelixirls

ElixirLS Dialyzer errors: "no local return" and "will never be called"


I have a relatively simple Elixir code:

gql_parser.ex

defmodule GqlParser do
  def get_sql(gql) do
    result = GraphQL.Parser.parse(gql)

    case result do
      {:ok, ast} -> build_sql(ast)
      {:error, message} -> raise GraphQL.Parser.SyntaxError, message: message
    end
  end

  defp build_sql(ast) do
    ast
  end
end

parser.ex

defmodule GraphQL.Parser do
  def parse(graphql) do
    result = GraphQL.Parser.Nif.parse(graphql)

    case result do
      {:ok, json} ->
        {:ok, Poison.Parser.parse!(json, keys: :atoms)}
      {:error, message} ->
        {:error, message}
    end
  end
end

The code seems to be working fine.

> iex -S mix
> GqlParser.get_sql "{ hello }"

%{
  definitions: [
    %{
      directives: nil,
      kind: "OperationDefinition",
      loc: %{end: 10, start: 1},
      name: nil,
      operation: "query",
      selectionSet: %{
        kind: "SelectionSet",
        loc: %{end: 10, start: 1},
        selections: [
          %{
            alias: nil,
            arguments: nil,
            directives: nil,
            kind: "Field",
            loc: %{end: 8, start: 3},
            name: %{kind: "Name", loc: %{end: 8, start: 3}, value: "hello"},
            selectionSet: nil
          }
        ]
      },
      variableDefinitions: nil
    }
  ],
  kind: "Document",
  loc: %{end: 10, start: 1}
}

However, I am seeing the following 2 errors in my VSCode: Function get_sql/1 has no local return. and Function build_sql/1 will never be called

enter image description here

I was not expecting any errors in VSCode.


Solution

  • I'm guessing it's because GraphQL.Parser.Nif.parse only ever exits according to the source code:

    def parse(_) do
      exit(:nif_library_not_loaded)
    end
    

    So from Dialyzer's perspective, the result = GraphQL.Parser.parse(gql) line never returns as the process has exited, although in reality it's calling a NIF. I'm not sure how/if Dialyzer accounts for NIFs.

    This question is probably related: Erlang: NIFs and dialyzer warning