Generated Files
Code generators (Thrift, Protocol Buffers, parser generators, custom scripts) often produce Erlang modules and header files that nobody edits by hand. Diagnostics and quick fixes on such files are mostly noise: the fix belongs in the generator or its input, and any manual edit is lost the next time the file is regenerated.
ELP recognises two markers that tell it a file is generated:
| Marker | Meaning |
|---|---|
@generated | The whole file is generated. |
@partially-generated | The file mixes generated code with hand-written manual sections. |
ELP only looks for the markers in the first 2000 bytes of a file, so put them in the header comment. If a file contains both, it is treated as fully generated.
Fully generated files
Add the @generated marker to a comment at the top of the file,
ideally together with a note on how to regenerate it:
%% @generated by my_codegen from priv/user.schema. Do not edit by hand.
-module(user_schema).
-export([fields/0]).
fields() ->
[id, name, email].
For a fully generated file:
- Built-in linters skip the file by default. This default can be overridden:
- by the linter itself, which can opt in to processing generated files, as a few do, such as W0017 - Undefined Function;
- by the project, which can turn generated files on or off for any linter via the configuration file.
- The language server reports compiler diagnostics and the diagnostics of the built-in linters that process generated files.
elp lintreports the same diagnostics as the language server, but only when the--include-generatedflag is passed. Without the flag, compiler diagnostics and built-in linters skip the file, whatever the linter or the configuration file says.elp searchskips the file, unless the--include-generatedflag is passed.- eqWAlizer type-checks the module like any other.
Partially generated files
Some generators produce a skeleton that is completed by hand, and preserve the
hand-written parts when the file is regenerated. Add the
@partially-generated marker to the header comment and wrap each
hand-written region between a % BEGIN MANUAL SECTION line and a
% END MANUAL SECTION line. Anything following BEGIN MANUAL SECTION on the
same line is ignored, so it can be used to name the section. Any number of %
characters can start the marker lines.
%% @partially-generated by my_codegen from priv/user_handler.spec.
%% Only edit the code between the manual section markers.
-module(user_handler).
-export([handle/1]).
handle(Request) ->
case validate(Request) of
ok -> process(Request);
Error -> Error
end.
validate(#{id := Id}) when is_integer(Id) -> ok;
validate(_Request) -> {error, invalid_request}.
%% BEGIN MANUAL SECTION process
process(#{id := Id}) ->
{ok, Id}.
%% END MANUAL SECTION
For a partially generated file:
- All enabled linters run, including the ones that skip fully generated files.
- Diagnostics inside manual sections are reported as usual, together with their quick fixes.
- Diagnostics outside manual sections are dropped by
elp lint. With the--include-generatedflag, and always in the language server, they are reported without quick fixes.
Every BEGIN MANUAL SECTION line needs a matching END MANUAL SECTION line:
an unterminated section is treated as generated code.
Configuring individual linters
Whether a linter processes fully generated files can be overridden per linter
via the include_generated key in the
.elp_lint.toml configuration file. The key takes
precedence over the linter's own default, in both directions:
# Report calls to `erlang:garbage_collect/0` in generated files as well
[linters.no_garbage_collect]
include_generated = true
# Stop reporting undefined functions in generated files
[linters.W0017]
include_generated = false
elp lint still requires the --include-generated flag to process fully
generated files.