I recently published the Audit library (described in another thread, hex, github).
It required capturing the current value of __ENV__
at the call-site,
so it had to be implemented as a macro. But it has the potential to create
space leaks, I wanted it turned off by default.
So the idea was to have two versions of the macro:
@enabled? Application.compile_env(:audit, :enabled?, false)
@key :__audit_trail__
...
@spec payload(struct(), Macro.Env.t()) :: trail_t()
def payload(r, e), do: {r, e.file, e.line}
...
@dialyzer {:nowarn_function, audit_fun: 2}
@spec audit_fun(struct(), Macro.Env) :: struct()
def audit_fun(r, e) do
r |> struct([{@key, payload(r, e)}])
end
...
if @enabled? do
defmacro audit(record) do
quote generated: true do
unquote(__MODULE__).audit_fun(unquote(record), __ENV__)
end
end
else
defmacro audit(record) do
quote generated: true do
unquote(record)
end
end
end
And in the code that uses it, a compile-time decision is made as to whether
to include the __audit_trail__
field that it relies on for book-keeping.
I have two problems:
- when the option is turned on, I get lots of dialyzer errors at the places
where the audit macro is used where:
%Foo{ x | bar: value }
is changed to:
%Foo{ audit(x) | bar: value }
to leverage the library.
It’s either:
Function ... will never be called.
or
Function ... has no local return.
or
The created anonymous function has no local return.
- Our CI pipeline mysteriously crashes while parallel compiling the Elixir code leaving no usable errors. (Is there a way to run the compiler single-threaded so we can at least isolate which file it’s crashing on?)
Am I doing something obviously wrong in my approach?
Somewhat baffled.