Created Started Finished
Three rules: HsLambdaCase, HsAppOnlyMain, TestJsonSpecPerJsonType Three rules, one commit each, from the easy end of `plan/rules.md`. ### `HsLambdaCase` (module level) Both shapes the guide names: one equation that names its last argument and then cases on it, and one equation per pattern with the function's name repeated in each. hlint covers neither, so it stays a check of its own. Reported at the function's name rather than its equations, because a where-bound helper's equations reach to the end of its own where clause and so contain the bindings written in it, and a finding drawn over another finding reads as one thing to fix rather than as three. Both shapes err towards silence: an equation that takes an earlier argument apart, or names it differently from its neighbour, is doing something one `\case` cannot say, and a last argument whose name is written anywhere else in the equation is a name with a use. **14 findings here**, all fixed in the same commit. 79 in nix-ci. ### `HsAppOnlyMain` (package level) An executable's own source holds `main = theRealMain` and nothing else. Two ways to break it: a declaration beside `main`, and a `main` that does the work itself. At the package level because the question is which component claims a module, and a component is a package's answer rather than a module's. The declaration list is what says what is there, so the only thing read out of the parse tree is what `main`'s right-hand side does. A type signature is not counted: it cannot be moved on its own and the binding it belongs to is reported already. **0 findings here, 0 in nix-ci** — both write the one line already, which is what puts it in M1 rather than M3. ### `TestJsonSpecPerJsonType` (project level) A type with both a `ToJSON` and a `FromJSON` instance obliges `jsonSpec @T` in the test suite of its package's gen package. Both classes rather than either, because what `jsonSpec` asserts is a claim about the pair jointly. That makes an obligation a *set* of classes, which makes this rule and `TestGenValidSpecPerGenValid` the same engine, so the engine is now written once in `Hopinion.Check.Obligation` and each rule is thirty lines of classes, combinator and sentences. The rule id becomes a table column, so a row still belongs to exactly one rule and no rule can read another's. That is the one place a rule does not own its own table, and `plan/plan.md` now says so as the family exception it is. One behaviour change falls out of the class set: a finding is per type rather than per instance, since one call meets the obligation of every instance of a type. **0 findings here** (the one suppression is persistent's generated `ToJSON` /`FromJSON` for `Key`), 12 in nix-ci. ### Checks `nix flake check` passes on each of the three commits.