77281e76

Created Started Cancelled

Say each of the three things once

A diagnostic prints three pieces of text: the finding message as the
error headline, the rule text as the label under the marker, and the
rule why as a note. Every rule was answering the same question in all
three. A reader stopped by an `Eq` finding got:

```
error[HsNoCustomEq]: This Eq instance is written out. Derive it instead, so that what it compares is the fields of the type.
   │ ^^^^^^^^^^^^^^^^^^^^^ Eq is derived.
   = note: If you need a custom equality operation, use a separate function, not the Eq instance.
```

"Derive it instead", "Eq is derived", and then the argument for deriving
a third time. Nine of the sixteen finding messages carried a remedy
sentence that the marker two lines below already said.

This gives each field one job.

**The finding message names what was found here, and only that.** Where
the rule knows which one it found, it now says so, because that is what
varies between two findings of one rule and what a reader scanning a
list of them needs:

| | before | after |
|---|---|---|
| `HsNoCustomEq` | This Eq instance is written out. Derive it instead, so that what it compares is the fields of the type. | `Eq User is written out.` |
| `HsNoCustomShowRead` | This Show instance is written out. Derive it instead, or discard what it is given if the point is to hide the value. | `Show Secret is written out, and uses the value.` |
| `TestNoTestHelpers` | A top-level binding in a test file, beside spec. | `A top-level binding beside spec: resourceDir.` |
| `HsAppOnlyMain` | A declaration in an executable's own source, beside main. Nothing here can be imported or tested, so move it into the library. | `A declaration beside main: twice.` |
| `HsGenValidInGenPackage` | A GenValid instance outside a -gen package. | `A GenValid instance in thing's library.` |
| `CommentBareTodo` | A bare TODO. Reference an issue or a URL, or do it. | `A TODO with no issue and no URL.` |
| `HsNoFilePath` | The type FilePath. Say what the path is with Path from path. | `The type FilePath.` |

The names come off facts that were already there: `instanceFactType`
for the four instance rules, the decl name for the two that report a
declaration, the package name for the generator rule.

**The rule text is the standard, in the one line the marker has room
for.** Six were two sentences or a clause chain where the marker fits a
noun phrase. `HsNoFilePath` went from "Say what a path is with path's
Path, rather than with FilePath." to "A path is a Path, not a
FilePath."; `TestJsonSpecPerJsonType` from 26 words to "ToJSON and
FromJSON oblige jsonSpec @T in the gen package's test suite."

**The rule why is the argument for the rule.** It stays general to the
rule and stops when it has made it. `HsNoFilePath` went 130 words to
43, `TestJsonSpecPerJsonType` 110 to 38, `HsNoCustomShowRead` 105 to
40, `HsAppOnlyMain` 85 to 32, `CommentBareTodo` 80 to 20.

Two exceptions were worth their sentence and stayed: `toFilePath` at
the edge is not what `HsNoFilePath` reports, and a `Show` that ignores
the value is how a secret stays out of the logs.
`HsNoSemigroupOnText`'s nine-word why was already the benchmark and is
untouched.

`ExampleNoShouting` in `hopinion-gen` is left alone. It exists to show a
repository adding a rule of its own, and its why is about that rather
than about shouting.

Goldens reset. 496 tests pass, `nix flake check` passes.
PR #8