Repository: jeremyjh/dialyxir
Pull request #211 (2018-06-30 01:51:10)

Prettier standard types & README touchups
Add some specific ASTs per #178. 

@asummers no new art here, these are all ones you sent me. The only additional one I considered adding was `list()` for `[any()]`, but that wouldn't be consistent with how we print empty list and the brackets are more familiar to people than the type name.

I also updated the README some to try to tie together the ignore file story a little better.


Patch:
diff --git a/README.md b/README.md
index e2deaa1..1d43fb2 100644
--- a/README.md
+++ b/README.md
@@ -140,7 +140,7 @@ Explanations are available for classes of warnings by passing the `--explain war
 #### Formats
 Dialyxir supports formatting the errors in several different ways:
   * Short - By passing `--format short`, the structs and other spec/type information will be dropped from the error message, with a minimal message. This is useful for CI environments. Includes `warning_name ` for use in explanations.
-  * Dialyzer - By passing `--format dialyzer`, the messages will be printed in the default Dialyzer format.
+  * Dialyzer - By passing `--format dialyzer`, the messages will be printed in the default Dialyzer format. This format is used in [legacy string matching](#simple-string-matches) ignore files.
   * Raw - By passing `--format raw`, messages will be printed in their form before being pretty printed by Dialyzer or Dialyxir.
   * Dialyxir (default) -- By passing `--format dialyxir`, messages will be converted to Elixir style messages then pretty printed and formatted. Includes `warning_name ` for use in explanations.
 
@@ -192,11 +192,15 @@ def project do
 end
 ```
 
-Any line of dialyzer output (partially) matching a line in `"dialyzer.ignore-warnings"` is filtered.
+This file comes in two formats: `--format dialyzer` string matches (compatbile with <= 0.5.1 ignore files), and the [term format](#elixir-term-format).
+
+#### Simple String Matches
+
+Any line of dialyzer format output (partially) matching a line in `"dialyzer.ignore-warnings"` is filtered.
 
 Note that copying output in the default format will not work!  Run `mix dialyzer --format dialyzer` to produce output suitable for the ignore file.
 
-For example, in project where `mix dialyzer --format dialyzer` outputs:
+For example, in a project where `mix dialyzer --format dialyzer` outputs:
 
 ```
   Proceeding with analysis...
@@ -223,11 +227,12 @@ config.ex:64: The call ets:insert('Elixir.MyApp.Config',{'Elixir.MyApp.Config',_
 done (warnings were emitted)
 ```
 
-### Elixir Term Format
+#### Elixir Term Format
 
-Dialyzer also recognizes an Elixir format of the ignore file. If your ignore file is an `exs` file, Dialyxir will evaluate it and process its data structure. The file looks like the following:
+Dialyxir also recognizes an Elixir format of the ignore file. If your ignore file is an `exs` file, Dialyxir will evaluate it and process its data structure. The file looks like the following:
 
 ```elixir
+# .dialyzer_ignore.exs
 [
   # {short_description}
   {":0:unknown_function Function :erl_types.t_is_opaque/1/1 does not exist."},
diff --git a/lib/dialyxir/pretty_print.ex b/lib/dialyxir/pretty_print.ex
index 534d3a4..9a848ed 100644
--- a/lib/dialyxir/pretty_print.ex
+++ b/lib/dialyxir/pretty_print.ex
@@ -207,6 +207,14 @@ defmodule Dialyxir.PrettyPrint do
     "#{do_pretty_print(value)} :: #{do_pretty_print(size)}"
   end
 
+  defp do_pretty_print({:binary, [{:binary_part, {:any}, {:any}, {:size, {:int, 8}}}]}) do
+    "binary()"
+  end
+
+  defp do_pretty_print({:binary, [{:binary_part, {:any}, {:any}, {:size, {:int, 1}}}]}) do
+    "bitstring()"
+  end
+
   defp do_pretty_print({:binary, binary_parts}) do
     binary_parts = Enum.map_join(binary_parts, ", ", &do_pretty_print/1)
     "<<#{binary_parts}>>"
@@ -252,6 +260,30 @@ defmodule Dialyxir.PrettyPrint do
     "(#{Enum.map_join(items, ", ", &do_pretty_print/1)})"
   end
 
+  defp do_pretty_print(
+         {:list, :square,
+          [
+            tuple: [
+              {:type_list, ['a', 't', 'o', 'm'], {:empty_list, :paren}},
+              {:atom, [:_]}
+            ]
+          ]}
+       ) do
+    "Keyword.t()"
+  end
+
+  defp do_pretty_print(
+         {:list, :square,
+          [
+            tuple: [
+              {:type_list, ['a', 't', 'o', 'm'], {:empty_list, :paren}},
+              t
+            ]
+          ]}
+       ) do
+    "Keyword.t(#{do_pretty_print(t)})"
+  end
+
   defp do_pretty_print({:list, :square, items}) do
     "[#{Enum.map_join(items, ", ", &do_pretty_print/1)}]"
   end
@@ -260,6 +292,17 @@ defmodule Dialyxir.PrettyPrint do
     "#{do_pretty_print(key)} => #{do_pretty_print(value)}"
   end
 
+  defp do_pretty_print(
+         {:map,
+          [
+            {:map_entry, {:atom, '\'__struct__\''},
+             {:type_list, ['a', 't', 'o', 'm'], {:empty_list, :paren}}},
+            {:map_entry, {:atom, [:_]}, {:atom, [:_]}}
+          ]}
+       ) do
+    "struct()"
+  end
+
   defp do_pretty_print({:map, map_keys}) do
     %{struct_name: struct_name, entries: entries} = struct_parts(map_keys)
 
@@ -315,6 +358,20 @@ defmodule Dialyxir.PrettyPrint do
     "#{Enum.map_join(pattern_items, ", ", &do_pretty_print/1)}"
   end
 
+  defp do_pretty_print(
+         {:pipe_list, {:atom, ['f', 'a', 'l', 's', 'e']}, {:atom, ['t', 'r', 'u', 'e']}}
+       ) do
+    "boolean()"
+  end
+
+  defp do_pretty_print(
+         {:pipe_list, {:atom, '\'infinity\''},
+          {:type_list, ['n', 'o', 'n', :_, 'n', 'e', 'g', :_, 'i', 'n', 't', 'e', 'g', 'e', 'r'],
+           {:empty_list, :paren}}}
+       ) do
+    "timeout()"
+  end
+
   defp do_pretty_print({:pipe_list, head, tail}) do
     "#{do_pretty_print(head)} | #{do_pretty_print(tail)}"
   end


Test patch:
diff --git a/test/pretty_print_test.exs b/test/pretty_print_test.exs
index 37b55e0..31c169d 100644
--- a/test/pretty_print_test.exs
+++ b/test/pretty_print_test.exs
@@ -394,6 +394,83 @@ defmodule Dialyxir.Test.PretyPrintTest do
     assert pretty_printed == expected_output
   end
 
+  test "binary transformation layer pretty prints appropriately" do
+    input = ~S"""
+    <<_:_*8>>
+    """
+
+    pretty_printed = Dialyxir.PrettyPrint.pretty_print(input)
+
+    expected_output = "binary()"
+    assert pretty_printed == expected_output
+  end
+
+  test "bitstring transformation layer pretty prints appropriately" do
+    input = ~S"""
+    <<_:_*1>>
+    """
+
+    pretty_printed = Dialyxir.PrettyPrint.pretty_print(input)
+
+    expected_output = "bitstring()"
+    assert pretty_printed == expected_output
+  end
+
+  test "boolean transformation layer pretty prints appropriately" do
+    input = ~S"""
+    false | true
+    """
+
+    pretty_printed = Dialyxir.PrettyPrint.pretty_print(input)
+
+    expected_output = "boolean()"
+    assert pretty_printed == expected_output
+  end
+
+  test "struct transformation layer pretty prints appropriately" do
+    input = ~S"""
+    #{'__struct__':=atom(), _=>_}
+    """
+
+    pretty_printed = Dialyxir.PrettyPrint.pretty_print(input)
+
+    expected_output = "struct()"
+    assert pretty_printed == expected_output
+  end
+
+  test "timeout transformation layer pretty prints appropriately" do
+    input = ~S"""
+    'infinity' | non_neg_integer()
+    """
+
+    pretty_printed = Dialyxir.PrettyPrint.pretty_print(input)
+
+    expected_output = "timeout()"
+    assert pretty_printed == expected_output
+  end
+
+  test "Keyword.t() transformation layer pretty prints appropriately" do
+    input = ~S"""
+    [{atom(), _}]
+    """
+
+    pretty_printed = Dialyxir.PrettyPrint.pretty_print(input)
+
+    expected_output = "Keyword.t()"
+    assert pretty_printed == expected_output
+  end
+
+  test "Keyword.t(integer()) transformation layer pretty prints appropriately" do
+    input = ~S"""
+    [{atom(), integer()}]
+    """
+
+    pretty_printed = Dialyxir.PrettyPrint.pretty_print(input)
+
+    expected_output = "Keyword.t(integer())"
+    assert pretty_printed == expected_output
+  end
+
   test "contracts with semicolons are pretty printed appropriately" do
     input = ~S"""
     ('nil','Elixir.Dnsimple.Events.HostCreateRequested':t()) -> {'ok',{'Elixir.Dnsimple.Models.Host':t(),'Elixir.Dnsimple.Models.Order':t()}} ; ({'Elixir.Dnsimple.Models.Host':t(),'Elixir.Dnsimple.Models.Order':t()},'Elixir.Dnsimple.Events.HostCreateSucceeded':t()) -> {'ok',{'Elixir.Dnsimple.Models.Host':t(),'Elixir.Dnsimple.Models.Order':t()}}