From 17e1d2e0f66a2aedcba935a56fbd8165926cfd01 Mon Sep 17 00:00:00 2001 From: Krzysztof Jurewicz Date: Mon, 31 May 2021 17:40:39 +0200 Subject: [PATCH] Fix Dialyzer errors This is not strictly backwards compatible, as Artificery.Console.halt/1 now indeed halts in every case, without ever returning anything. --- lib/console.ex | 11 ++++------- lib/console/color.ex | 2 +- lib/console/events.ex | 6 ++++-- lib/console/prompt.ex | 6 ++++-- lib/console/spinner.ex | 4 ++-- lib/entry.ex | 28 +++++++++++----------------- mix.exs | 6 ++++++ mix.lock | 2 ++ test/artificery/dummy/dummy.ex | 5 +++++ 9 files changed, 39 insertions(+), 31 deletions(-) create mode 100644 test/artificery/dummy/dummy.ex diff --git a/lib/console.ex b/lib/console.ex index 9086782..2daea15 100644 --- a/lib/console.ex +++ b/lib/console.ex @@ -13,11 +13,8 @@ defmodule Artificery.Console do @doc """ Terminates the process with the given status code. """ - @spec halt(non_neg_integer) :: :ok | no_return - def halt(code) - - def halt(0), do: :ok - def halt(code) when code > 0 do + @spec halt(non_neg_integer()) :: no_return() + def halt(code) do if Application.get_env(:artificery, :no_halt, false) do # During tests we don't want to kill the node process, # exit the test process instead. @@ -277,14 +274,14 @@ defmodule Artificery.Console do @spec init() :: :ok def init do # For logger state - :ets.new(__MODULE__, [:public, :set, :named_table]) + __MODULE__ = :ets.new(__MODULE__, [:public, :set, :named_table]) # Start listening for console events Events.start :ok end @doc false - @spec width() :: non_neg_integer + @spec width() :: pos_integer() def width do case :io.columns() do {:error, :enotsup} -> 80 diff --git a/lib/console/color.ex b/lib/console/color.ex index b68a1f5..5415e92 100644 --- a/lib/console/color.ex +++ b/lib/console/color.ex @@ -26,7 +26,7 @@ defmodule Artificery.Console.Color do Returns a formatted binary. """ - @spec style(binary, [atom]) :: binary + @spec style(iodata, [atom]) :: binary def style(msg, styles) when is_list(styles) do do_style(Enum.uniq(styles), [msg], false) end diff --git a/lib/console/events.ex b/lib/console/events.ex index 05515fd..eabdd7c 100644 --- a/lib/console/events.ex +++ b/lib/console/events.ex @@ -43,7 +43,7 @@ defmodule Artificery.Console.Events do # is changed. The default action is to ignore it. # If a program does full-screen display, it should handle SIGWINCH. When the signal # arrives, it should fetch the new screen size and reformat its display accordingly. - notify(subscribers, :sigwinch) + :ok = notify(subscribers, :sigwinch) {:ok, subscribers} end def handle_event(_, subscribers), do: {:ok, subscribers} @@ -69,7 +69,7 @@ defmodule Artificery.Console.Events do @impl :gen_event def handle_info({:DOWN, _ref, _type, pid, _reason}, subscribers) do - {:noreply, Map.delete(subscribers, pid)} + {:ok, Map.delete(subscribers, pid)} end @impl :gen_event @@ -87,5 +87,7 @@ defmodule Artificery.Console.Events do for {pid, _} <- subscribers do send(pid, {:event, event}) end + + :ok end end diff --git a/lib/console/prompt.ex b/lib/console/prompt.ex index c3ac01d..a3a13df 100644 --- a/lib/console/prompt.ex +++ b/lib/console/prompt.ex @@ -67,12 +67,14 @@ defmodule Artificery.Console.Prompt do # TODO: Open EDITOR to a temp file, read in result when closed @doc false - @spec edit() :: String.t + # @spec edit() :: String.t + @spec edit() :: no_return() def edit(), do: exit(:not_implemented) # TODO: Choose from a selection of options @doc false - @spec choose(String.t, [String.t]) :: String.t + # @spec choose(String.t, [String.t]) :: String.t + @spec choose(String.t, [String.t]) :: no_return() def choose(question, choices) when is_binary(question) and is_list(choices) do exit(:not_implemented) end diff --git a/lib/console/spinner.ex b/lib/console/spinner.ex index 7b8c188..db3de08 100644 --- a/lib/console/spinner.ex +++ b/lib/console/spinner.ex @@ -124,7 +124,7 @@ defmodule Artificery.Console.Spinner do end def handle_cast({:stop, status}, data) do Artificery.Console.Events.unsubscribe - render(set_status(data, status)) + _ = render(set_status(data, status)) IO.write @cursor_show {:stop, :normal, nil} end @@ -135,7 +135,7 @@ defmodule Artificery.Console.Spinner do end defp render(%{text: text, status: status} = data) do - clear(data) + data = clear(data) {frame_data, data} = frame(data) output = "#{frame_data} #{text} #{status}\n" IO.write(output) diff --git a/lib/entry.ex b/lib/entry.ex index 751d490..b11668b 100644 --- a/lib/entry.ex +++ b/lib/entry.ex @@ -31,6 +31,7 @@ defmodule Artificery.Entry do Handles parsing and validating arguments, then dispatching the selected command. """ + @spec main() :: no_return() def main() do argv = :init.get_plain_arguments() @@ -42,6 +43,7 @@ defmodule Artificery.Entry do end @doc false + @spec main(OptionParser.argv()) :: no_return() def main(argv) when is_list(argv) do Console.init() @@ -88,7 +90,7 @@ defmodule Artificery.Entry do |> Enum.map(fn {name, val} -> {name, apply_transform(@global_options[name], val)} end) |> Map.new() - Console.configure(verbosity: Map.get(global_flags, :verbose, :normal)) + _ = Console.configure(verbosity: Map.get(global_flags, :verbose, :normal)) case argv do [] -> @@ -285,18 +287,7 @@ defmodule Artificery.Entry do end defp dispatch(%Command{name: command_name, callback: callback} = command, argv, flags) do - new_flags = - case __MODULE__.pre_dispatch(command, argv, flags) do - {:ok, new_flags} -> - new_flags - - other -> - Console.error( - "Expected {:ok, options} returned from #{__MODULE__}.pre_dispatch/3, got: #{ - inspect(other) - }" - ) - end + {:ok, new_flags} = __MODULE__.pre_dispatch(command, argv, flags) if function_exported?(__MODULE__, callback, 2) do apply(__MODULE__, callback, [argv, new_flags]) @@ -321,6 +312,7 @@ defmodule Artificery.Entry do end end + @spec print_help(list(String.t())) :: no_return() defp print_help([]) do # Print global help @@ -343,7 +335,7 @@ defmodule Artificery.Entry do for {flag, help} <- global_options do IO.write([flag, String.duplicate(" ", max(flag_width - byte_size(flag) + 2, 2))]) - print_help_lines(help, flag_width + 2) + :ok = print_help_lines(help, flag_width + 2) end IO.write([?\n, "COMMANDS\n\n"]) @@ -452,14 +444,14 @@ defmodule Artificery.Entry do for {flag, help} <- options do IO.write([flag, String.duplicate(" ", max(flag_width - byte_size(flag) + 2, 2))]) - print_help_lines(help, flag_width + 2) + :ok = print_help_lines(help, flag_width + 2) end if has_args?, do: IO.write("\n") end # Print arguments - if has_args? do + _ = if has_args? do IO.write("ARGUMENTS\n") arguments = format_arguments(cmd.arguments) @@ -467,7 +459,7 @@ defmodule Artificery.Entry do for {arg, help} <- arguments do IO.write([arg, String.duplicate(" ", max(arg_width - byte_size(arg) + 2, 2))]) - print_help_lines(help, arg_width + 2) + :ok = print_help_lines(help, arg_width + 2) end end @@ -500,6 +492,8 @@ defmodule Artificery.Entry do for line <- rest do IO.write([String.duplicate(" ", leading_width), line, ?\n]) end + + :ok end defp format_arguments(args) do diff --git a/mix.exs b/mix.exs index a838a75..c2da556 100644 --- a/mix.exs +++ b/mix.exs @@ -14,6 +14,10 @@ defmodule Artificery.MixProject do description: description(), package: package(), elixirc_paths: elixirc_paths(Mix.env), + dialyzer: [ + flags: ["-Wunmatched_returns", :error_handling, :race_conditions, :underspecs], + plt_add_apps: [:mix] + ], preferred_cli_env: [ docs: :docs, "hex.publish": :docs, @@ -28,6 +32,7 @@ defmodule Artificery.MixProject do defp deps do [ + {:dialyxir, "~> 1.0", only: [:dev], runtime: false}, {:ex_doc, ">= 0.0.0", only: [:docs], runtime: false}, {:eqc_ex, "~> 1.4", only: [:test], runtime: false} ] @@ -55,5 +60,6 @@ defmodule Artificery.MixProject do end defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(:dev), do: ["lib", "test/artificery/dummy"] defp elixirc_paths(_), do: ["lib"] end diff --git a/mix.lock b/mix.lock index d77db12..09685ed 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,9 @@ %{ + "dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"}, "earmark": {:hex, :earmark, "1.4.4", "4821b8d05cda507189d51f2caeef370cf1e18ca5d7dfb7d31e9cafe6688106a4", [:mix], [], "hexpm", "1f93aba7340574847c0f609da787f0d79efcab51b044bb6e242cae5aca9d264d"}, "earmark_parser": {:hex, :earmark_parser, "1.4.12", "b245e875ec0a311a342320da0551da407d9d2b65d98f7a9597ae078615af3449", [:mix], [], "hexpm", "711e2cc4d64abb7d566d43f54b78f7dc129308a63bc103fbd88550d2174b3160"}, "eqc_ex": {:hex, :eqc_ex, "1.4.2", "c89322cf8fbd4f9ddcb18141fb162a871afd357c55c8c0198441ce95ffe2e105", [:mix], [], "hexpm", "6547e68351624ca5387df7e3332136b07f1be73c5a429c1b4e40436dcad50f38"}, + "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_doc": {:hex, :ex_doc, "0.23.0", "a069bc9b0bf8efe323ecde8c0d62afc13d308b1fa3d228b65bca5cf8703a529d", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "f5e2c4702468b2fd11b10d39416ddadd2fcdd173ba2a0285ebd92c39827a5a16"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, diff --git a/test/artificery/dummy/dummy.ex b/test/artificery/dummy/dummy.ex new file mode 100644 index 0000000..2574c4b --- /dev/null +++ b/test/artificery/dummy/dummy.ex @@ -0,0 +1,5 @@ +defmodule Artificery.Test.Dummy do + @moduledoc "A dummy module used to unveil Dialyzer errors" + + use Artificery +end