Works out how to turn GenServer modules into their virtual-time equivalents.
It is a planner, not a formatter: it reads a file, decides which tokens to
replace and where, and hands the plan back. mix virtual_time.migrate decides
whether to print that plan or apply it.
The plan is deliberately conservative — a call is only rewritten when the change provably means the same thing:
use GenServerbecomesuse VirtualTimeGenServer, options untouched (use GenStateMachine→use VirtualTimeGenStateMachine);- inside a converted module,
Process.send_after/3,Process.sleep/1and:timer.sleep/1becomeVirtualTimeGenServercalls, because that is what puts the module on the virtual clock; GenServer.call/cast/stop/reply/start_link/start/child_specis rewritten only when its target is a literal module that is converted somewhere in the project, or__MODULE__inside a converted module.
Anything else — a pid, a variable, a module that stays a plain GenServer — is
left alone, and reported when the caller is a converted module and the tool
cannot tell. :erlang.send_after/3 is in that group: it takes the delay
first where the virtual-time call takes it last, and swapping arguments is not
a token replacement, so it is reported for a human to reorder.
Every edit carries the exact source text it expects to find at its position, and is checked against the source before it joins the plan, so a plan can never be applied to text it does not describe.
Summary
Types
One replacement: from is the exact source text at column on line, and
to is what replaces it.
Something the migration noticed but does not rewrite.
Functions
Applies a plan to the source it was planned from.
The modules a source (or an AST) converts, as dotted names.
Plans the migration of one file's source.
Types
@type edit() :: %{ line: pos_integer(), column: pos_integer(), from: String.t(), to: String.t(), change: String.t() }
One replacement: from is the exact source text at column on line, and
to is what replaces it.
@type note() :: %{line: pos_integer(), message: String.t()}
Something the migration noticed but does not rewrite.
Functions
Applies a plan to the source it was planned from.
Edits are applied right to left within a line, so an earlier replacement never moves a later one.
The modules a source (or an AST) converts, as dotted names.
Plans the migration of one file's source.
known_converted is the set of module names the rest of the project converts
(see converted_modules/1), so a call into another file can be recognised.
The modules this file converts are added to it.
Returns {:error, reason} when the source does not parse — the caller reports
that file and carries on with the others.