Skip to content

mix dialyzer complains about atom response key #651

@fastjames

Description

@fastjames

I used this file as the reference for my own api_spec.ex and noticed that when I added the responses: %{...} section, I started getting a dialyzer error:

lib/foobar_web/api_spec.ex:21:7:no_return
Function spec/0 has no local return.
________________________________________________________________________________
lib/foobar_web/api_spec.ex:22:40:call
The function call will not succeed.

OpenApiSpex.resolve_schema_modules(%OpenApiSpex.OpenApi{
  :components => %OpenApiSpex.Components{
    :callbacks => nil,
    :examples => nil,
    :extensions => nil,
    :headers => nil,
    :links => nil,
    :parameters => nil,
    :requestBodies => nil,
    :responses => %{
      :unprocessable_entity => %OpenApiSpex.Response{
        :content => %{
          <<_::128>> => %OpenApiSpex.MediaType{
            :encoding => nil,
            :example => nil,
            :examples => nil,
            :extensions => nil,
            :schema => %OpenApiSpex.Schema{
              :additionalProperties => nil,
              :allOf => nil,
              :anyOf => nil,
              :default => nil,
              :deprecated => nil,
              :description => nil,
              :discriminator => nil,
              :enum => nil,
              :example => nil,
              :exclusiveMaximum => nil,
              :exclusiveMinimum => nil,
              :extensions => nil,
              :externalDocs => nil,
              :format => nil,
              :items => nil,
              :maxItems => nil,
              :maxLength => nil,
              :maxProperties => nil,
              :maximum => nil,
              :minItems => nil,
              :minLength => nil,
              :minProperties => nil,
              :minimum => nil,
              :multipleOf => nil,
              :not => nil,
              :nullable => nil,
              :oneOf => nil,
              :pattern => nil,
              :properties => nil,
              :readOnly => nil,
              :required => nil,
              :title => nil,
              :type => :object,
              :uniqueItems => nil,
              :writeOnly => nil,
              :"x-struct" => nil,
              :"x-validate" => nil,
              :xml => nil
            }
          }
        },
        :description => <<_::160>>,
        :extensions => nil,
        :headers => nil,
        :links => nil
      }
    },
    :schemas => nil,
    :securitySchemes => %{
      <<_::80>> => %OpenApiSpex.SecurityScheme{
        :bearerFormat => nil,
        :description => nil,
        :extensions => nil,
        :flows => nil,
        :in => <<_::48>>,
        :name => <<_::104>>,
        :openIdConnectUrl => nil,
        :scheme => nil,
        :type => <<_::48>>
      }
    }
  },
  :extensions => nil,
  :externalDocs => nil,
  :info => %OpenApiSpex.Info{
    :contact => %OpenApiSpex.Contact{
      :email => <<_::200>>,
      :extensions => nil,
      :name => nil,
      :url => nil
    },
    :description => <<_::2912>>,
    :extensions => nil,
    :license => nil,
    :termsOfService => nil,
    :title => <<_::72>>,
    :version => <<_::24>>
  },
  :openapi => <<_::40>>,
  :paths => %{
    binary() => %OpenApiSpex.PathItem{
      :"$ref" => nil | binary(),
      :delete =>
        nil
        | %OpenApiSpex.Operation{
            :callbacks => map(),
            :deprecated => boolean(),
            :description => nil | binary(),
            :extensions => nil | map(),
            :externalDocs => nil | map(),
            :operationId => nil | binary(),
            :parameters => [any()],
            :requestBody => nil | map(),
            :responses => map(),
            :security => nil | [any()],
            :servers => nil | [any()],
            :summary => nil | binary(),
            :tags => [any()]
          },
      :description => nil | binary(),
      :extensions => nil | %{binary() => _},
      :get =>
        nil
        | %OpenApiSpex.Operation{
            :callbacks => map(),
            :deprecated => boolean(),
            :description => nil | binary(),
            :extensions => nil | map(),
            :externalDocs => nil | map(),
            :operationId => nil | binary(),
            :parameters => [any()],
            :requestBody => nil | map(),
            :responses => map(),
            :security => nil | [any()],
            :servers => nil | [any()],
            :summary => nil | binary(),
            :tags => [any()]
          },
      :head =>
        nil
        | %OpenApiSpex.Operation{
            :callbacks => map(),
            :deprecated => boolean(),
            :description => nil | binary(),
            :extensions => nil | map(),
            :externalDocs => nil | map(),
            :operationId => nil | binary(),
            :parameters => [any()],
            :requestBody => nil | map(),
            :responses => map(),
            :security => nil | [any()],
            :servers => nil | [any()],
            :summary => nil | binary(),
            :tags => [any()]
          },
      :options =>
        nil
        | %OpenApiSpex.Operation{
            :callbacks => map(),
            :deprecated => boolean(),
            :description => nil | binary(),
            :extensions => nil | map(),
            :externalDocs => nil | map(),
            :operationId => nil | binary(),
            :parameters => [any()],
            :requestBody => nil | map(),
            :responses => map(),
            :security => nil | [any()],
            :servers => nil | [any()],
            :summary => nil | binary(),
            :tags => [any()]
          },
      :parameters => nil | [map()],
      :patch =>
        nil
        | %OpenApiSpex.Operation{
            :callbacks => map(),
            :deprecated => boolean(),
            :description => nil | binary(),
            :extensions => nil | map(),
            :externalDocs => nil | map(),
            :operationId => nil | binary(),
            :parameters => [any()],
            :requestBody => nil | map(),
            :responses => map(),
            :security => nil | [any()],
            :servers => nil | [any()],
            :summary => nil | binary(),
            :tags => [any()]
          },
      :post =>
        nil
        | %OpenApiSpex.Operation{
            :callbacks => map(),
            :deprecated => boolean(),
            :description => nil | binary(),
            :extensions => nil | map(),
            :externalDocs => nil | map(),
            :operationId => nil | binary(),
            :parameters => [any()],
            :requestBody => nil | map(),
            :responses => map(),
            :security => nil | [any()],
            :servers => nil | [any()],
            :summary => nil | binary(),
            :tags => [any()]
          },
      :put =>
        nil
        | %OpenApiSpex.Operation{
            :callbacks => map(),
            :deprecated => boolean(),
            :description => nil | binary(),
            :extensions => nil | map(),
            :externalDocs => nil | map(),
            :operationId => nil | binary(),
            :parameters => [any()],
            :requestBody => nil | map(),
            :responses => map(),
            :security => nil | [any()],
            :servers => nil | [any()],
            :summary => nil | binary(),
            :tags => [any()]
          },
      :servers => nil | [map()],
      :summary => nil | binary(),
      :trace =>
        nil
        | %OpenApiSpex.Operation{
            :callbacks => map(),
            :deprecated => boolean(),
            :description => nil | binary(),
            :extensions => nil | map(),
            :externalDocs => nil | map(),
            :operationId => nil | binary(),
            :parameters => [any()],
            :requestBody => nil | map(),
            :responses => map(),
            :security => nil | [any()],
            :servers => nil | [any()],
            :summary => nil | binary(),
            :tags => [any()]
          }
    }
  },
  :security => [%{<<_::80>> => []}, ...],
  :servers => [
    %OpenApiSpex.Server{
      :description => nil,
      :extensions => nil,
      :url => binary(),
      :variables => %{}
    },
    ...
  ],
  :tags => []
})

will never return since the success typing is:
(%OpenApiSpex.OpenApi{
  :components =>
    nil
    | %OpenApiSpex.Components{
        :callbacks => nil | %{binary() => map()},
        :examples => nil | %{binary() => map()},
        :extensions => nil | %{binary() => _},
        :headers => nil | %{binary() => map()},
        :links => nil | %{binary() => map()},
        :parameters => nil | %{binary() => map()},
        :requestBodies => nil | %{binary() => map()},
        :responses => nil | %{binary() => map()},
        :schemas => nil | %{binary() => map()},
        :securitySchemes => nil | %{binary() => map()}
      },
  :extensions => nil | %{binary() => _},
  :externalDocs =>
    nil
    | %OpenApiSpex.ExternalDocumentation{
        :description => nil | binary(),
        :extensions => nil | %{binary() => _},
        :url => binary()
      },
  :info => %OpenApiSpex.Info{
    :contact =>
      nil
      | %OpenApiSpex.Contact{
          :email => nil | binary(),
          :extensions => nil | map(),
          :name => nil | binary(),
          :url => nil | binary()
        },
    :description => nil | binary(),
    :extensions => nil | %{binary() => _},
    :license =>
      nil
      | %OpenApiSpex.License{
          :extensions => nil | map(),
          :name => binary(),
          :url => nil | binary()
        },
    :termsOfService => nil | binary(),
    :title => binary(),
    :version => binary()
  },
  :openapi => binary(),
  :paths => %{
    binary() => %OpenApiSpex.PathItem{
      :"$ref" => nil | binary(),
      :delete => nil | map(),
      :description => nil | binary(),
      :extensions => nil | map(),
      :get => nil | map(),
      :head => nil | map(),
      :options => nil | map(),
      :parameters => nil | [any()],
      :patch => nil | map(),
      :post => nil | map(),
      :put => nil | map(),
      :servers => nil | [any()],
      :summary => nil | binary(),
      :trace => nil | map()
    }
  },
  :security => nil | [%{binary() => [any()]}],
  :servers =>
    nil
    | [
        %OpenApiSpex.Server{
          :description => nil | binary(),
          :extensions => nil | map(),
          :url => binary(),
          :variables => map()
        }
      ],
  :tags =>
    nil
    | [
        %OpenApiSpex.Tag{
          :description => nil | binary(),
          :extensions => nil | map(),
          :externalDocs => nil | map(),
          :name => binary()
        }
      ]
}) :: %OpenApiSpex.OpenApi{
  :components => %OpenApiSpex.Components{
    :callbacks => nil | %{binary() => map()},
    :examples => nil | %{binary() => map()},
    :extensions => nil | %{binary() => _},
    :headers => nil | %{binary() => map()},
    :links => nil | %{binary() => map()},
    :parameters => nil | %{binary() => map()},
    :requestBodies => nil | %{binary() => map()},
    :responses => nil | %{binary() => map()},
    :schemas => %{binary() => map()},
    :securitySchemes => nil | %{binary() => map()}
  },
  :extensions => nil | %{binary() => _},
  :externalDocs =>
    nil
    | %OpenApiSpex.ExternalDocumentation{
        :description => nil | binary(),
        :extensions => nil | %{binary() => _},
        :url => binary()
      },
  :info => %OpenApiSpex.Info{
    :contact =>
      nil
      | %OpenApiSpex.Contact{
          :email => nil | binary(),
          :extensions => nil | map(),
          :name => nil | binary(),
          :url => nil | binary()
        },
    :description => nil | binary(),
    :extensions => nil | %{binary() => _},
    :license =>
      nil
      | %OpenApiSpex.License{
          :extensions => nil | map(),
          :name => binary(),
          :url => nil | binary()
        },
    :termsOfService => nil | binary(),
    :title => binary(),
    :version => binary()
  },
  :openapi => binary(),
  :paths => %{
    binary() => %OpenApiSpex.PathItem{
      :"$ref" => nil | binary(),
      :delete => nil | map(),
      :description => nil | binary(),
      :extensions => nil | map(),
      :get => nil | map(),
      :head => nil | map(),
      :options => nil | map(),
      :parameters => nil | [any()],
      :patch => nil | map(),
      :post => nil | map(),
      :put => nil | map(),
      :servers => nil | [any()],
      :summary => nil | binary(),
      :trace => nil | map()
    }
  },
  :security => nil | [%{binary() => [any()]}],
  :servers =>
    nil
    | [
        %OpenApiSpex.Server{
          :description => nil | binary(),
          :extensions => nil | map(),
          :url => binary(),
          :variables => map()
        }
      ],
  :tags =>
    nil
    | [
        %OpenApiSpex.Tag{
          :description => nil | binary(),
          :extensions => nil | map(),
          :externalDocs => nil | map(),
          :name => binary()
        }
      ]
}

and the contract is
(OpenApiSpex.OpenApi.t()) :: OpenApiSpex.OpenApi.t()

________________________________________________________________________________
done (warnings were emitted)
Halting VM with exit status 2

It looks like the typespec says that the keys for responses have to be binaries, but the example shows an atom key. I think that specific spec is defined at

@type responses_map :: %{String.t() => Response.t() | Reference.t()}
. Would it be correct to modify the spec, or is the example what needs to change?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions