2

I am receiving an error in dialyzer when it analyzes the following function.

-spec do_request(Method, Type, Url, Expect, Headers, Body, Client) -> Response::client_response() 
when 
    Method  :: method(),
    Type    :: content_type(),
    Url     :: url(),
    Expect  :: status_codes(),
    Headers :: headers(),
    Body    :: body(),
    Client  :: #client{}.

do_request(Method, Type, Url, Expect, Headers, Body, Client) -> 
    Client2 = check_expired(Client),
    Headers2 = add_auth_header(Headers, Client2),
    %% error occurs on this line
    Response = restc:request(Method, Type, binary_to_list(Url), Expect, Headers2, Body),
    %%
    {Response, Client2}.

The error is:

The call restc:request(Method::any(),Type::any(),
[byte()],Expect::any(),Headers2::[{binary(),binary()},...],
Body::any()) breaks the contract (Method::method(), Type::content_type(),  
Url::url(), Expect::status_codes(), Headers::headers(), 
Body::body()) -> Response::response()

restc:request has the following typespec:

-spec request(Method::method(), Type::content_type(), Url::url(),
Expect::status_codes(), Headers::headers(), Body::body()) -> Response::response().

The types used by the call are:

-type method()       :: head | get | put | post | trace | options | delete.
-type url()          :: binary().
-type headers()      :: [header()].
-type header()       :: {binary(), binary()}.
-type status_codes() :: [status_code()].
-type status_code()  :: integer().
-type reason()       :: term().
-type content_type() :: json | xml | percent.
-type property()     :: atom() | tuple().
-type proplist()     :: [property()].
-type body()         :: proplist().
-type response()     :: {ok, Status::status_code(), Headers::headers(), Body::body()} |
                        {error, Status::status_code(), Headers::headers(), Body::body()} |
                        {error, Reason::reason()}.
-type client_response()       :: {response(), #client{}}.
-type token_type()     :: bearer | unsupported.

Why does dialyzer say that my call is passing variables with an any() type when I've specified the types of the variables being passed? I've looked through the call chain to verify that the type specifications are consistent (and are consistent with the other module).

dethtron5000
  • 10,363
  • 1
  • 31
  • 32
  • You would need to add in the expanded types for each of those in your `when` part of the typespec; It's possible that `[byte()]` is clashing with `url()` for example, but we have no way to know it from the information you have provided. – I GIVE TERRIBLE ADVICE Jun 07 '15 at 13:21
  • Indeed... Either that, or the `Headers` argument is not matching the contract. The `any()` types you see are due to Dialyzer using your argument contract only "externally" (e.g. for actual calls to the function). While analyzing the function itself, Dialyzer assumes that any argument may be given, and narrows it down if such a thing is needed for other calls to succeed. – aronisstav Jun 07 '15 at 22:59

1 Answers1

2

The problem is that url() is specified as a binary() but you pass in [byte()] (strings) in there. You need to change the type specification of url() to something like iodata() instead, or to restrict your input by converting it to a binary first.

I GIVE TERRIBLE ADVICE
  • 9,578
  • 2
  • 32
  • 40