Warning: documentation references type "Phoenix.Router.Route.t()" but the module Phoenix.Router.Route is hidden

Problem

In one of my libs I add type information such as @type routes :: [Phoenix.Router.Route.t()]

This causes mix docs to throw warnings as the module (not the type) is hidden via @moduledoc false

warning: documentation references type "Phoenix.Router.Route.t()" but the module Phoenix.Router.Route is hidden
    │
 17 │   @type routes :: [Phoenix.Router.Route.t()]
    │   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    │
    └─ lib/routex/extension.ex:17: Routex.Extension (module)

Partial solution

In a related Github issue the users works around the module being hidden, by setting skip_undefined_reference_warnings_on. So I applied the same solution.

skip_undefined_reference_warnings_on: ["Routex.Extension","Routex.Processing", "Routex.Route"]

Now, the warnings are gone.

Remaining problem

With the workaround in place, I can add undefined references in 3 modules without being warned.

Question

Who has an idea how to solve this issue?

Solution #1: add a types.ex as a shim so you only need to exclude the shim module.
Solution #2: …?

How are you getting access to private structs? Why do you need to interact with them? Generally the solution would be to not depend on things you’re not expected to depend on.

1 Like

They are not private: phoenix/lib/phoenix/router/route.ex at main · phoenixframework/phoenix · GitHub

I use the typing as Phoenix.Router.Route’s are the input and output of the middleware.

The type is quite literally the only ‘dependency’ the lib has and all red flags should raise when the contract is broken. It is also an @type not an @typep (not even @opaque).

@moduledoc false denotes the whole module as private to the library – hence the ex_doc warning.

1 Like

That would to be a convention unknown to me (as a workaround?) rather than a strict behavior as far as I can tell. From the docs

Conveniently, Elixir allows developers to hide modules and functions from the documentation, by setting @doc false to hide a particular function, or @moduledoc false to hide the whole module.

However, keep in mind @moduledoc false or @doc false do not make a function private.

Typespecs

A type defined with @typep is private

So according to the official documentation, the modules hides documentation but includes a public type.

This could make sense: You are not discouraged to rely on functions in the submodule Route, but is is also the best place to specify the struct and the type.

Maybe not anymore? That message seems to be related to the new checks from elixir 1.18, no?

Nope, this is pre-1.18 behavior. I ignored the warnings for a long time as I was aware they were only emitted by ExDocs. Dialyzer uses the type information without issues afaik. After all, types are mainly used for automated checks not as documentation for humans.

So to summarize:

  • ExDocs warns because it does not want to link to an undocumented type / non-existing page. (works)
  • Not linking can be done with :skip_code_autolink_to (works)
  • Referencing in docs (@doc or .md) should cause a warning (works)
  • Referencing an @type in @type should just work* (fails, causes warning in ExDoc)

Any objections to this summary / opinion? :slight_smile:

1 Like

You cannot just rely on defp/@typep for project level privacy. They only help with the module level. The convention of using @moduledoc false has been used for a long time in elixir itself and many 3rd party projects to define what is meant to be the public api or not.

4 Likes

Not to mention the comment in the file…

This struct is private as it contains internal routing information

….crawls in a corner….

Is this perhaps something that the LSP made you aware of, when it should have ignored the value instead?