elint 0.1.2

Erlang code linter
Documentation
# Preprocessing

How elint preprocesses Erlang source, in terms of what a user can observe.
The implementation is documented in `src/context.rs`.

elint lints `.erl` / `.hrl` files; `collect_erlang_files` in `src/fs.rs`
collects them under a given file or directory.

## Every conditional arm is linted

elint analyzes **every arm of every conditional**, not only the branch the
current macro state would select:

- At each `-ifdef` / `-ifndef` / `-if` / `-elif`, every arm is scanned.
- The **mainline** always takes `Branch::Then` at every conditional and
  continues past each `-endif`. This is a deliberate choice: it is not the
  OTP `recommended` branch, and `-if` / `-elif` conditions are never
  evaluated.
- A non-mainline arm is scanned only up to the `-endif` that closes the
  conditional it was forked from; it does not continue past it.

The arms are not combined as a full product: each source region is scanned
by exactly one branch, so findings do not duplicate across branches.

A parse error in one branch only skips that branch's lint; findings from the
other branches are kept.

## Includes are not resolved

`-include` / `-include_lib` are **not** followed to the filesystem. elint
keeps no search-path configuration (`-I`, `ERL_LIBS`, ...), so headers are
skipped entirely. Macros, records, and types defined in headers are never
seen and are not incorporated into the analysis. The skipped directive sites
themselves are retained on each branch so rules that care about attribute
placement can still see them. Likewise, `-define` directives are consumed
when macros are registered; those sites are retained the same way.

## Unknown macros expand to `elint_dummy`

Unknown macros -- header macros, but also OTP predefined macros such as
`?MODULE` / `?FUNCTION_NAME`, of which `erl_pp` only implements `?FILE` and
`?LINE` -- expand to the placeholder atom `elint_dummy`. The placeholder is
a lowercase atom, so it is valid wherever a macro value could appear and the
file still parses.

No unknown-macro diagnostic is emitted. Without include resolution there is
no way to tell a macro that is genuinely undefined from one defined in an
unresolvable header, so reporting them would be noisy.

The consequence for lint rules is that they cannot see through
header-defined macros or records today. A rule that needs header information
requires include resolution to be reconsidered.

## `-error` / `-warning` are ignored

elint scans every arm of every conditional. A compiler takes one branch per
conditional and only meets the directives in the branches it selects; elint
would meet `-error` / `-warning` in every arm it scans, so reporting them
would be noisy. Judging these directives is the compiler's job, not a
linter's, so elint ignores them entirely.