cfa40a45

Two rules about what a test file exports and defines

Two module-level rules, one per commit, both about `*Spec.hs` files.

**`HsTestOneSpecPerFile`** — a test file exports exactly `(spec)`. Three shapes
are reported and each gets its own sentence: no export list at all, an empty
one, and one holding anything besides `spec` (the message names what it
exports). The finding is file-scoped, so the suppression the report offers is
the `[allow:file:...]` form the parser will accept.

**`TestNoTestHelpers`** — one finding per top-level value binding beside `spec`,
scoped to that declaration. A type signature is not one, since it cannot move on
its own and its binding is reported already. Neither is a type, a class or an
instance. Let-bindings are untouched.

## What they cost

`ModuleContext` carries two more facts. The declaration list was already read
for comment attachment and was simply not exposed. The export list is new:
`Hopinion.Facts.Export`, twenty-five lines of fact and thirty of reading, and
documentation in an export list is not an entry, since a section heading exports
nothing. Neither rule walks the module itself, which is the shape M5 wants.

What a test file is gets asked in one place, `moduleContextIsSpecFile`, rather
than once per rule.

## Measurement

Zero findings for the export rule against this repository. Sixty-one for the
helper rule, all of them in this repository's own test suite, and all of them
fixed rather than suppressed:

- helpers that hide what a test asserts became let-bindings in the spec that
  uses them, across twelve spec files;
- helpers that build a `Spec` became functions in a new `Hopinion.TestUtils`:
  `ruleSpec`, `moduleScenario`, `projectScenario`, `projectScenarios`,
  `ownedSpec`, and the IO helpers only they use.

That second kind is most of `RuleSpec`, which is now four meta-properties and
one `mapM_`, and it is the fix the rule's own text names, so a repository
adopting the rule is told where such a helper goes rather than only that it may
not stay.

## Checking

`nix flake check` passes on each commit separately, not only on the branch tip.

Total

2h14m06s

Created Started Finished

Time to Start Worker time Duration Time to finish Idle
Config 19s 1s 1s 20s 19s
Eval 31s 11s 11s 43s 10s
Build 41s 15s 2h13m44s 2h14m25s 2h13m31s
Suite 19s 28s 2h14m06s 2h14m25s 2h14m01s

Timeline

Config Eval Build Compressed gap
0s20s40s2h14m