Skip to content

fix(grpc): remove specs and enforce compiler warnings#552

Merged
polvalente merged 1 commit into
elixir-grpc:masterfrom
smartinio:smartinio/warnings-as-errors
Jul 1, 2026
Merged

fix(grpc): remove specs and enforce compiler warnings#552
polvalente merged 1 commit into
elixir-grpc:masterfrom
smartinio:smartinio/warnings-as-errors

Conversation

@smartinio

Copy link
Copy Markdown
Contributor

Problem

Dialyzer reports GRPC.Stub.connect/2 as returning a value outside its own spec.

connect/2 returns a GRPC.Channel struct, but GRPC.Channel.t() is stricter than the channel states built by the client connection code. In our project, running dialyzer errors on calls to connect/2 which sees a broken public type:

::warning file=lib/grpc/client/connection.ex,line=190,title=call::The function call finalize_connection will not succeed.
::warning file=lib/grpc/client/connection.ex,line=193,title=call::The function call finalize_connection will not succeed.
::warning file=lib/grpc/client/connection.ex,line=354,title=no_return::Function finalize_connection/2 has no local return.
::warning file=lib/grpc/client/connection.ex,line=355,title=call::The function call pick_channel will not succeed.

GRPC.Client.Stream.t() has the same issue for fields that start as nil and are filled later.

Root cause

The Channel and Stream structs support incrementally populating their values, but the public types describe only fully populated state.

Fix

Project direction is to stop supporting Dialyzer and instead leverage Elixir compiler warnings:

  • Remove explicit @spec annotations from source and README examples
  • Compile with warnings as errors in CI
  • Keep tests running with warnings as errors in CI
  • Fix the Gun stream response state update that Elixir 1.20 flags under warnings as errors

Review attention

Elixir's type system did not catch the issue related to stream trailer parsing flagged by Dialyzer, so I'm deferring that to a future PR to avoid conflating things:

::warning file=lib/grpc/client/adapters/gun.ex,line=435,title=guard_fail::The guard clause can never succeed.

Test plan

  • Pulled in grpc and grpc_core from this branch in our project and successfully ran dialyzer


defp push_message(%{messages: messages} = state, message, terminal?) do
{:noreply, %{state | messages: :queue.in(message, messages), done: state.done or terminal?}}
{:noreply, %{state | messages: :queue.in(message, messages), done: terminal?}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotta double-check this change and the one above it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, this change makes sense! There's a clause above catching done: true, so the type checker knows done is false in all clauses below

@polvalente polvalente merged commit 6ec6f74 into elixir-grpc:master Jul 1, 2026
7 checks passed
@polvalente

Copy link
Copy Markdown
Contributor

Thanks for pivoting into this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants