asummers / erlex

Convert Erlang style structs and error messages to equivalent Elixir.
Other
34 stars 17 forks source link

Pretty Printing Spec With When #16

Closed jeremyjh closed 5 years ago

jeremyjh commented 5 years ago

This code fails on dialyxir master. It looks like we just need to keep a space after "when" in the spec we pass to the code formatter from pretty_print_contract, Code.format_string! is complaining about 'ok'whenone.

defmodule WhatTheSpec do
  @type key :: atom()
  @type transaction :: map()

  def put(_, _, _ \\ nil)

  @spec put(one, key(), any()) :: transaction() when one: transaction()
  def put(t, _, _) when is_map(t), do: t

  @spec put(one, any(), Keyword.t()) :: :ok when one: key()
  def put(_, _, _), do: :ok

  @spec put_foo(any()) :: :ok
  def put_foo(value) do
    put(:foo, value, nil) # note the explicit nil here
  end
end

'(one,key(),any()) -> transaction() when one :: transaction();(one,any(),\'Elixir.Keyword\':t()) -> \'ok\' when one :: key()'
%SyntaxError{
  description: "syntax error before: whenone",
  file: "nofile",
  line: 1
}
** (throw) {:error, :formatting, "@spec a(:one, any(), Keyword.t()) :: 'ok'whenone :: key()\ndef a() do\n  :ok\nend\n"}
asummers commented 5 years ago

Yep that looks like a bug!

asummers commented 5 years ago

It's a little more complicated than just a missing space.

https://github.com/asummers/erlex/blob/master/lib/erlex.ex#L133

That guy breaks apart the different heads and calls do_pretty_print_contract which is not when aware. That needs to call back into pretty_print_contract which would be able to differentiate properly. Good find, Won't have time to pick this up til later, but that's about what the fix is gonna look like.

asummers commented 5 years ago

Much deeper than I thought. When lexing we get the following:

[
  {:"(", 1},
  {:atom_part, 1, 'o'},
  {:atom_part, 1, 'n'},
  {:atom_part, 1, 'e'},
  {:",", 1},
  {:atom_part, 1, 'a'},
  {:atom_part, 1, 'n'},
  {:atom_part, 1, 'y'},
  {:"(", 1},
  {:")", 1},
  {:",", 1},
  {:atom_full, 1, '\'Elixir.Keyword\''},
  {:":", 1},
  {:atom_part, 1, 't'},
  {:"(", 1},
  {:")", 1},
  {:")", 1},
  {:->, 1},
  {:atom_full, 1, '\'ok\''},
  {:atom_part, 1, 'w'},
  {:atom_part, 1, 'h'},
  {:atom_part, 1, 'e'},
  {:atom_part, 1, 'n'},
  {:atom_part, 1, 'o'},
  {:atom_part, 1, 'n'},
  {:atom_part, 1, 'e'},
  {:::, 1},
  {:atom_part, 1, 'k'},
  {:atom_part, 1, 'e'},
  {:atom_part, 1, 'y'},
  {:"(", 1},
  {:")", 1}
]

Note that that when is just in the middle there. That's because of this line in the lexer: {WHITESPACE} : skip_token.. This is going to be tricky to solve.