Skip to content

Commit c37aabb

Browse files
Chris Xuclaude
andcommitted
fix(dialyzer): resolve all dialyzer warnings
- Remove dead code patterns that could never match: - detect_root_type([]) in structural_parser.ex - append_lines(writer, [], _depth) in objects.ex - Define precise types to fix supertype warnings: - Add @type decoded in decode.ex - Add @type validated in options.ex - Use nonempty_list() in arrays.ex and strings.ex specs - Fix protocol fallback no_return warning: - Remove @SPEC from Toon.Encoder.Any encode/2 functions - Add .dialyzer_ignore.exs for intentional no_return behavior - Add edge case tests to verify behavior before dead code removal Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 1c2b798 commit c37aabb

File tree

11 files changed

+82
-24
lines changed

11 files changed

+82
-24
lines changed

.dialyzer_ignore.exs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Dialyzer warnings to ignore
2+
#
3+
# Protocol fallback implementation that intentionally raises.
4+
# This is expected behavior - the Any implementation raises Protocol.UndefinedError
5+
# when a struct doesn't have an explicit Toon.Encoder implementation.
6+
[
7+
{"lib/toon/encoder.ex", :no_return}
8+
]

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
[![Hex.pm](https://img.shields.io/hexpm/v/toon.svg)](https://hex.pm/packages/toon)
44
[![Documentation](https://img.shields.io/badge/docs-hexdocs-blue.svg)](https://hexdocs.pm/toon)
5+
[![Coverage Status](https://coveralls.io/repos/github/xu-chris/toon_ex/badge.svg?branch=main)](https://coveralls.io/github/xu-chris/toon_ex?branch=main)
56

67
**TOON (Token-Oriented Object Notation)** encoder and decoder for Elixir.
78

lib/toon/decode/decode.ex

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ defmodule Toon.Decode do
88
alias Toon.Decode.{Options, StructuralParser}
99
alias Toon.DecodeError
1010

11+
@typedoc "Decoded TOON value"
12+
@type decoded :: nil | boolean() | binary() | number() | list() | map()
13+
1114
@doc """
1215
Decodes a TOON format string to Elixir data.
1316
@@ -94,7 +97,7 @@ defmodule Toon.Decode do
9497
iex> Toon.Decode.decode!("count: 42")
9598
%{"count" => 42}
9699
"""
97-
@spec decode!(String.t(), keyword()) :: term()
100+
@spec decode!(String.t(), keyword()) :: decoded()
98101
def decode!(string, opts \\ []) when is_binary(string) do
99102
case decode(string, opts) do
100103
{:ok, result} -> result
@@ -104,7 +107,7 @@ defmodule Toon.Decode do
104107

105108
# Private functions
106109

107-
@spec do_decode(String.t(), map()) :: term()
110+
@spec do_decode(String.t(), map()) :: decoded()
108111
defp do_decode(string, opts) do
109112
# Use structural parser for full TOON support
110113
case StructuralParser.parse(string, opts) do

lib/toon/decode/structural_parser.ex

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,6 @@ defmodule Toon.Decode.StructuralParser do
157157
end
158158
end
159159

160-
defp detect_root_type([]), do: {:object, nil}
161-
162160
# Parse root primitive value (single value without key)
163161
defp parse_root_primitive([%{content: content}], _opts) do
164162
# For root primitives, we parse directly without parser combinator

lib/toon/encode/arrays.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ defmodule Toon.Encode.Arrays do
4949
iex> IO.iodata_to_binary(result)
5050
"items[0]:"
5151
"""
52-
@spec encode_empty(String.t(), String.t() | nil) :: [iodata()]
52+
@spec encode_empty(String.t(), String.t() | nil) ::
53+
nonempty_list(nonempty_list(binary() | nonempty_list(binary())))
5354
def encode_empty(key, length_marker \\ nil) do
5455
marker = format_length_marker(0, length_marker)
5556
[[Strings.encode_key(key), "[", marker, "]", Constants.colon()]]

lib/toon/encode/objects.ex

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ defmodule Toon.Encode.Objects do
1717
iex> Toon.Encode.Objects.encode(map, 0, opts)
1818
1919
"""
20-
@spec encode(map(), non_neg_integer(), map()) :: iodata()
20+
@spec encode(map(), non_neg_integer(), map()) :: [iodata()]
2121
def encode(map, depth, opts) when is_map(map) do
2222
writer = Writer.new(opts.indent)
2323

@@ -103,22 +103,14 @@ defmodule Toon.Encode.Objects do
103103

104104
# Private helpers
105105

106-
defp append_lines(writer, [], _depth), do: writer
107-
108-
defp append_lines(writer, lines, depth) when is_list(lines) do
106+
defp append_lines(writer, [header | data_rows], depth) do
109107
# For arrays, the first line is the header at current depth
110108
# Subsequent lines (data rows for tabular format) should be one level deeper
111-
case lines do
112-
[header | data_rows] ->
113-
writer = Writer.push(writer, header, depth)
114-
115-
Enum.reduce(data_rows, writer, fn row, acc ->
116-
Writer.push(acc, row, depth + 1)
117-
end)
109+
writer = Writer.push(writer, header, depth)
118110

119-
[] ->
120-
writer
121-
end
111+
Enum.reduce(data_rows, writer, fn row, acc ->
112+
Writer.push(acc, row, depth + 1)
113+
end)
122114
end
123115

124116
defp append_iodata(writer, iodata, _base_depth) do

lib/toon/encode/options.ex

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ defmodule Toon.Encode.Options do
55

66
alias Toon.Constants
77

8+
@typedoc "Validated encoding options"
9+
@type validated :: %{
10+
indent: pos_integer(),
11+
delimiter: String.t(),
12+
length_marker: String.t() | nil,
13+
key_order: term(),
14+
indent_string: String.t()
15+
}
16+
817
@options_schema [
918
indent: [
1019
type: :pos_integer,
@@ -90,7 +99,7 @@ defmodule Toon.Encode.Options do
9099
iex> Toon.Encode.Options.validate!(indent: 4)
91100
%{indent: 4, delimiter: ",", length_marker: nil, indent_string: " "}
92101
"""
93-
@spec validate!(keyword()) :: map()
102+
@spec validate!(keyword()) :: validated()
94103
def validate!(opts) when is_list(opts) do
95104
case validate(opts) do
96105
{:ok, validated} ->

lib/toon/encode/strings.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ defmodule Toon.Encode.Strings do
2424
iex> Toon.Encode.Strings.encode_string("line1\\nline2") |> IO.iodata_to_binary()
2525
~s("line1\\\\nline2")
2626
"""
27-
@spec encode_string(String.t(), String.t()) :: iodata()
27+
@spec encode_string(String.t(), String.t()) :: binary() | nonempty_list(binary())
2828
def encode_string(string, delimiter \\ ",") when is_binary(string) do
2929
if safe_unquoted?(string, delimiter) do
3030
string

lib/toon/encoder.ex

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ defimpl Toon.Encoder, for: Any do
5151
end
5252
end
5353

54-
@spec encode(struct(), keyword()) :: no_return()
5554
def encode(%_{} = struct, _opts) do
5655
raise Protocol.UndefinedError,
5756
protocol: @protocol,
@@ -71,7 +70,6 @@ defimpl Toon.Encoder, for: Any do
7170
"""
7271
end
7372

74-
@spec encode(term(), keyword()) :: no_return()
7573
def encode(value, _opts) do
7674
raise Protocol.UndefinedError,
7775
protocol: @protocol,

mix.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ defmodule Toon.MixProject do
2626
:underspecs,
2727
:unmatched_returns,
2828
:unknown
29-
]
29+
],
30+
ignore_warnings: ".dialyzer_ignore.exs"
3031
],
3132
aliases: aliases()
3233
]

0 commit comments

Comments
 (0)