Argus.Analyses.MessageContract (Panoptes v0.13.0)

Copy Markdown View Source

A message a module sends itself and cannot handle.

A GenServer is one contract written in two places. The client half is an ordinary function — def get(pid, k), do: GenServer.call(pid, {:get, k}) — and the server half is a handle_call/3 clause. Nothing checks they agree: rename the tag on one side and it compiles clean.

call then raises FunctionClauseError in the server and the caller exits with it. cast is worse — the caller is told nothing at all, the server dies, the supervisor restarts it, and its state is gone.

Why this is sound now and was not before

An earlier version took the client's tag from an extractor that scanned backwards for the last write to {x,1}. That is not the write that reaches the call, and it reported :amqp_channel as casting :ok when the write it found was really gen_server:reply(From, ok).

The tag is now a join: def_use names the write that actually feeds the call, and tuple_literal/literal_value carry the constant and the register it landed in — the register mattering because def_use says which write feeds which read but not which operand.