Skip to content

Lint layer with selectable rules, following ruff's format/check split #3

Description

@MarioRial22

The gap

aspen can format a file and it can analyse a project, but there is no way to express a finding that will not be fixed automatically.

  • vb6format is fix-only. FormatPass can rewrite tokens and nothing else, and fmt_source returns a String. A pass has no channel to say "this is wrong and I am deliberately not touching it".
  • aspen check is project-only. It needs a .vbp, runs the full semantic analysis, and stops at the first error.

That leaves a class of checks with nowhere to live: cheap, per-file, and not safely auto-fixable. The one that brought me here is non-ASCII characters in identifiers. VB6 accepts Public Function Añadir(), and a code base written in Spanish, French or German is full of them. They are a real portability problem — almost nothing downstream of VB6 accepts them — but renaming an identifier is a judgment call about a public API, not something a formatter should do behind your back. Today there is no way to even report it.

Proposal: follow ruff's split

ruff solved this exact problem for Python and the vocabulary is already familiar to most people, which is the main reason to borrow it rather than invent something:

  • aspen fmt stays what it is: deterministic layout, no opinions, always applied. The equivalent of ruff format.
  • aspen check grows a per-file lint layer over the CST, selectable by rule. The equivalent of ruff check.

Concretely:

A rule registry. Each rule carries a stable code, a name, a category and a fixability:

code stable, e.g. N001, referenced in config and in noqa-style suppressions
fixability Safe, Unsafe, None — ruff's distinction, and the piece that is missing today
default on or off; portability rules like the one above are off by default

Diagnostics. vb6parse::errors already has Severity, spans, labels, notes and pretty printing through ariadne. A finding is that plus a rule code, so lint output and parse errors look the same to the user.

Config. .aspen.toml already exists and is already parsed with serde. Add the selection to it:

[format]
indent-size = 4

[lint]
select = ["W", "N001"]
ignore = ["W002"]

CLI. --select / --ignore / --fix / --diff / --statistics, and ruff's exit codes: 0 clean, 1 findings, 2 the run itself failed.

Seed rules, enough to prove the shape:

  • non-ASCII character in an identifier — no fix
  • mixed line endings in one file — safe fix
  • trailing whitespace — safe fix

Why this is worth doing

Two of these checks are the difference between a tool you run by hand and a tool you can put in CI or a pre-commit hook. Per-file and parse-only means it stays fast enough for the latter, which the project-level check cannot be.

I am happy to implement it. Opening this first rather than arriving with the diff, because it adds a public API to vb6format/vb6parse and the shape should be yours to decide — in particular whether the lint layer belongs in a new crate, inside vb6format, or inside vb6semantic.

Related: the three fixes in #2 are prerequisites in practice — until fmt can read Windows-1252 files and exits non-zero on failure, no gate built on it means anything.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions