Deal with dialyzer issues

We end up:
1. making the main interface (`jsx:`) more readable and easier to maintain
(this allows for easier future dropping of the use of `#config{}` for options),
2. fixing some config.-related `-spec(_).`s that weren't using proper elements,
3. having `any()` become a "specific type" - which seems like an improvement,
4. moving stuff like `list()` or `proplists:proplist()` into more appropriate types,
5. making the main config. element (`jsx_config`) "pretty" visible,
6. creating a visible distinction between a module's config. and the generic `jsx_config` elements
This commit is contained in:
Paulo F. Oliveira 2020-09-11 23:09:16 +01:00
parent 1e88fc5455
commit 3a8f364153
10 changed files with 106 additions and 52 deletions

View file

@ -1,2 +1,17 @@
{edoc_opts, [{preprocess, true}]}.
{erl_opts, [debug_info]}.
{dialyzer, [
{warnings, [
unknown,
unmatched_returns,
error_handling,
underspecs
]}
]}.
{profiles, [
{test, [
{dialyzer, [
{plt_extra_apps, [eunit]}
]}
]}
]}.

View file

@ -54,20 +54,20 @@
-type config() :: jsx_config:config().
-spec encode(Source::json_term()) -> json_text().
-spec encode(Source::json_term()) -> json_text() | {incomplete, encoder()}.
encode(Source) -> encode(Source, []).
-spec encode(Source::json_term(), Config::jsx_to_json:config()) -> json_text() | {incomplete, encoder()}.
-spec encode(Source::json_term(), Config::jsx_config:options()) -> json_text() | {incomplete, encoder()}.
encode(Source, Config) -> jsx_to_json:to_json(Source, Config).
-spec decode(Source::json_text()) -> json_term().
-spec decode(Source::json_text()) -> json_term() | {incomplete, decoder()}.
decode(Source) -> decode(Source, []).
-spec decode(Source::json_text(), Config::jsx_to_term:config()) -> json_term() | {incomplete, decoder()}.
-spec decode(Source::json_text(), Config::jsx_config:options()) -> json_term() | {incomplete, decoder()}.
decode(Source, Config) -> jsx_to_term:to_term(Source, Config).
@ -76,7 +76,7 @@ decode(Source, Config) -> jsx_to_term:to_term(Source, Config).
format(Source) -> format(Source, []).
-spec format(Source::json_text(), Config::jsx_to_json:config()) -> json_text() | {incomplete, decoder()}.
-spec format(Source::json_text(), Config::jsx_config:options()) -> json_text().
format(Source, Config) -> jsx_to_json:format(Source, Config).
@ -91,43 +91,44 @@ minify(Source) -> format(Source, []).
prettify(Source) -> format(Source, [space, {indent, 2}]).
-spec is_json(Source::any()) -> boolean().
-spec is_json(Source::binary()) -> boolean() | {incomplete, decoder()}.
is_json(Source) -> is_json(Source, []).
-spec is_json(Source::any(), Config::jsx_verify:config()) -> boolean() | {incomplete, decoder()}.
-spec is_json(Source::binary(), Config::jsx_config:options()) -> boolean() | {incomplete, decoder()}.
is_json(Source, Config) -> jsx_verify:is_json(Source, Config).
-spec is_term(Source::any()) -> boolean().
-spec is_term(Source::json_term() | end_stream | end_json) -> boolean() | {incomplete, encoder()}.
is_term(Source) -> is_term(Source, []).
-spec is_term(Source::any(), Config::jsx_verify:config()) -> boolean() | {incomplete, encoder()}.
-spec is_term(Source::json_term() | end_stream | end_json,
Config::jsx_config:options()) -> boolean() | {incomplete, encoder()}.
is_term(Source, Config) -> jsx_verify:is_term(Source, Config).
-spec consult(File::file:name_all()) -> list(json_term()).
-spec consult(File::file:name_all()) -> list(jsx_consult:json_value()).
consult(File) -> consult(File, []).
-spec consult(File::file:name_all(), Config::jsx_to_term:config()) -> list(json_term()).
-spec consult(File::file:name_all(), Config::jsx_consult:config()) -> list(jsx_consult:json_value()).
consult(File, Config) -> jsx_consult:consult(File, Config).
-type decoder() :: fun((json_text() | end_stream | end_json) -> any()).
-spec decoder(Handler::module(), State::any(), Config::list()) -> decoder().
-spec decoder(Handler::module(), State::any(), Config::jsx_config:options()) -> decoder().
decoder(Handler, State, Config) -> jsx_decoder:decoder(Handler, State, Config).
-type encoder() :: fun((json_term() | end_stream | end_json) -> any()).
-spec encoder(Handler::module(), State::any(), Config::list()) -> encoder().
-spec encoder(Handler::module(), State::any(), Config::jsx_config:options()) -> encoder().
encoder(Handler, State, Config) -> jsx_encoder:encoder(Handler, State, Config).
@ -156,13 +157,14 @@ encoder(Handler, State, Config) -> jsx_encoder:encoder(Handler, State, Config).
-type parser() :: fun((token() | end_stream) -> any()).
-spec parser(Handler::module(), State::any(), Config::list()) -> parser().
-spec parser(Handler::module(), State::any(), Config::jsx_config:options()) -> parser().
parser(Handler, State, Config) -> jsx_parser:parser(Handler, State, Config).
-opaque internal_state() :: tuple().
-spec resume(Term::json_text() | token(), InternalState::internal_state(), Config::list()) -> any().
-spec resume(Term::json_text() | token(), InternalState::internal_state(),
Config::jsx_config:options()) -> jsx:decoder() | {incomplete, jsx:decoder()}.
resume(Term, {decoder, State, Handler, Acc, Stack}, Config) ->
jsx_decoder:resume(Term, State, Handler, Acc, Stack, jsx_config:parse_config(Config));

View file

@ -49,8 +49,55 @@
-type config() :: #config{}.
-export_type([config/0]).
-type option() :: valid_flag()
| {valid_flag(), boolean()}
| {strict, [strict_option()]}
| {error_handler, fun((any(), any(), any()) -> ok)}
| {incomplete_handler, fun((any(), any(), any()) -> ok)}
| {return_maps, boolean()}
| {labels, label_option()}
| {space, non_neg_integer()}
| {indent, non_neg_integer()}
| {depth, non_neg_integer()}
| {newline, binary()}
| legacy_option()
| {legacy_option(), boolean()}.
-type legacy_option() :: strict_comments
| strict_commas
| strict_utf8
| strict_single_quotes
| strict_escapes
| strict_control_codes.
-type options() :: [option()].
-export_type([options/0]).
-type strict_option() :: comments
| trailing_commas
| utf8
| single_quotes
| escapes
| control_codes.
-type label_option() :: binary
| atom
| existing_atom
| attempt_atom.
-type valid_flag() :: escaped_forward_slashes
| escaped_strings
| unescaped_jsonp
| dirty_strings
| multi_term
| return_tail
| repeat_keys
| strict
| stream
| uescape
| error_handler
| incomplete_handler.
%% parsing of jsx config
-spec parse_config(Config::proplists:proplist()) -> config().
-spec parse_config(Config::options()) -> config().
parse_config(Config) -> parse_config(Config, #config{}).
@ -116,7 +163,7 @@ parse_strict(_Strict, _Rest, _Config) ->
-spec config_to_list(Config::config()) -> proplists:proplist().
-spec config_to_list(Config::config()) -> options().
config_to_list(Config) ->
reduce_config(lists:map(
@ -153,7 +200,7 @@ reduce_config([Else|Input], Output, Strict) ->
reduce_config(Input, [Else] ++ Output, Strict).
-spec valid_flags() -> [atom()].
-spec valid_flags() -> [valid_flag(), ...].
valid_flags() ->
[
@ -172,7 +219,7 @@ valid_flags() ->
].
-spec extract_config(Config::proplists:proplist()) -> proplists:proplist().
-spec extract_config(Config::options()) -> options().
extract_config(Config) ->
extract_parser_config(Config, []).

View file

@ -32,7 +32,7 @@
return_maps = false
}).
-type config() :: list().
-type config() :: proplists:proplist().
-export_type([config/0]).
-type json_value() :: list(json_value())
@ -43,6 +43,7 @@
| integer()
| float()
| binary().
-export_type([json_value/0]).
opts(Opts) -> [return_maps, multi_term] ++ Opts.
@ -61,8 +62,8 @@ consult(File, Config) when is_list(Config) ->
end.
-type state() :: {[], proplists:proplist(), {list(), #config{}}}.
-spec init(Config::proplists:proplist()) -> state().
-type state() :: {[], config(), {list(), #config{}}}.
-spec init(Config::config()) -> state().
init(Config) -> {[], Config, jsx_to_term:start_term(Config)}.

View file

@ -32,7 +32,7 @@
-export([decoder/3, resume/6]).
-spec decoder(Handler::module(), State::any(), Config::list()) -> jsx:decoder().
-spec decoder(Handler::module(), State::any(), Config::jsx_config:options()) -> jsx:decoder().
decoder(Handler, State, Config) ->
fun(JSON) -> start(JSON, {Handler, Handler:init(State)}, [], jsx_config:parse_config(Config)) end.

View file

@ -25,19 +25,19 @@
-export([encoder/3, encode/1, encode/2]).
-spec encoder(Handler::module(), State::any(), Config::list()) -> jsx:encoder().
-spec encoder(Handler::module(), State::any(), Config::jsx_config:options()) -> jsx:encoder().
encoder(Handler, State, Config) ->
Parser = jsx:parser(Handler, State, Config),
fun(Term) -> Parser(encode(Term) ++ [end_json]) end.
-spec encode(Term::any()) -> any().
-spec encode(Term::any()) -> [any(), ...].
encode(Term) -> encode(Term, ?MODULE).
-spec encode(Term::any(), EntryPoint::module()) -> any().
-spec encode(Term::any(), EntryPoint::module()) -> [any(), ...].
encode(Map, _EntryPoint) when is_map(Map), map_size(Map) < 1 ->
[start_object, end_object];

View file

@ -27,7 +27,7 @@
-export([init/1, handle_event/2]).
-spec parser(Handler::module(), State::any(), Config::list()) -> jsx:parser().
-spec parser(Handler::module(), State::any(), Config::jsx_config:options()) -> jsx:parser().
parser(Handler, State, Config) ->
fun(Tokens) -> value(Tokens, {Handler, Handler:init(State)}, [], jsx_config:parse_config(Config)) end.
@ -630,7 +630,7 @@ to_hex(X) -> X + 48. %% ascii "1" is [49], "2" is [50], etc...
%% for raw input
-spec init(proplists:proplist()) -> list().
-spec init([]) -> [].
init([]) -> [].

View file

@ -36,17 +36,16 @@
newline = <<$\n>>
}).
-type config() :: list().
-export_type([config/0]).
-type config() :: proplists:proplist().
-spec to_json(Source::any(), Config::config()) -> binary().
-spec to_json(Source::jsx:json_term(), Config::jsx_config:options()) -> binary().
to_json(Source, Config) when is_list(Config) ->
(jsx:encoder(?MODULE, Config, jsx_config:extract_config(Config ++ [escaped_strings])))(Source).
-spec format(Source::binary(), Config::config()) -> binary().
-spec format(Source::binary(), Config::jsx_config:options()) -> jsx:json_text().
format(Source, Config) when is_binary(Source) andalso is_list(Config) ->
(jsx:decoder(?MODULE, Config, jsx_config:extract_config(Config ++ [escaped_strings])))(Source);
@ -91,7 +90,7 @@ parse_config([], Config) ->
-type state() :: {unicode:charlist(), #config{}}.
-spec init(Config::proplists:proplist()) -> state().
-spec init(Config::config()) -> state().
init(Config) -> {[], parse_config(Config)}.
@ -390,7 +389,7 @@ custom_newline_test_() ->
[
{"single key object", ?_assert(
jsx:format(<<"{\"k\":\"v\"}">>, [space, {indent, 2}, {newline, <<$\r>>}])
=:= <<"{\r \"k\": \"v\"\r}">>)
=:= <<"{\r \"k\": \"v\"\r}">>)
}
].

View file

@ -41,21 +41,9 @@
return_maps = false
}).
-type config() :: list().
-export_type([config/0]).
-type config() :: proplists:proplist().
-type json_value() :: list(json_value())
| list({binary() | atom(), json_value()}) | [{},...]
| {with_tail, json_value(), binary()}
| map()
| true
| false
| null
| integer()
| float()
| binary().
-spec to_term(Source::binary(), Config::config()) -> json_value().
-spec to_term(Source::binary(), Config::jsx_config:options()) -> jsx:json_term() | {incomplete, jsx:decoder()}.
to_term(Source, Config) when is_list(Config) ->
(jsx:decoder(?MODULE, [return_maps] ++ Config, jsx_config:extract_config(Config)))(Source).
@ -87,7 +75,7 @@ parse_config([], Config) ->
-type state() :: {list(), #config{}}.
-spec init(Config::proplists:proplist()) -> state().
-spec init(Config::config()) -> state().
init(Config) -> start_term(Config).

View file

@ -26,8 +26,9 @@
-export([is_json/2, is_term/2]).
-export([init/1, handle_event/2]).
-type config() :: proplists:proplist().
-spec is_json(Source::binary(), Config::proplists:proplist()) -> true | false | {incomplete, jsx:decoder()}.
-spec is_json(Source::binary(), Config::jsx_config:options()) -> true | false | {incomplete, jsx:decoder()}.
is_json(Source, Config) when is_list(Config) ->
try (jsx:decoder(?MODULE, Config, jsx_config:extract_config(Config)))(Source)
@ -35,7 +36,8 @@ is_json(Source, Config) when is_list(Config) ->
end.
-spec is_term(Source::any(), Config::proplists:proplist()) -> true | false | {incomplete, jsx:encoder()}.
-spec is_term(Source::jsx:json_term() | end_stream | end_json,
Config::jsx_config:options()) -> true | false | {incomplete, jsx:encoder()}.
is_term(Source, Config) when is_list(Config) ->
try (jsx:encoder(?MODULE, Config, jsx_config:extract_config(Config)))(Source)
@ -68,7 +70,7 @@ parse_config([], Config) ->
%% we don't actually need any state for this
-type state() :: [].
-spec init(Config::proplists:proplist()) -> state().
-spec init(Config::config()) -> state().
init(Config) -> parse_config(Config).