1e2e4b29

By: Tom Sydney Kerckhove <syd@cs-syd.eu>

Ask for an error type rather than a String

HsNoStringError reports the first argument of Either, Except, ExceptT,
MonadError and Validation when that argument is String, Text, [Char], or a list
or NonEmpty of those. The finding underlines that argument alone, because the
rest of the signature is right.

A caller can branch on a constructor and cannot branch on prose, so a String
error asks every handler to treat every failure the same way, and the words are
chosen at the point of failure, which is the place that knows least about who
will read them.

Read off the type syntax rather than off the names in it, because the question
is which argument of which type a name is written as: the String in
Either String a is a failure and the one in Either a String is a result. A type
node arrives more than once from the traversal, since a strict field is a bang
around parentheses around the application, so applications are peeled and
deduplicated by location, and the function half of an application is dropped:
the Either String inside Either String Int is the same String. peelType and
peelApp move to Hopinion.Extract.Ghc, which is where the reading no rule owns
lives.

Tried out over nix-ci8, smos and salsa: 153 findings, every one a signature of
the repository's own, mostly the parseX :: Text -> Either String X family, and
two of them tests that assert the exact wording of a failure because there is
no constructor to compare. Only Either fired in all three, so the other four
carriers are untried there.

What it cannot see is an Either String that is never written down, where a
library combinator supplies it and the lambda handed to bimapCodec,
eitherReader or fromPersistValue only writes Left "...". That is a third of the
files holding a string error literal in those repositories, and a rule of its
own if it is worth one.

The one place here is fromPersistValueViaCodec, which is persistent's
fromPersistValue under another name. Its reason moves from the module header
into the suppression, so it is said once.
GitHub
e2e-added-rule-is-rune2e-artifacts-are-reade2e-choices-are-found-beside-the-repositorye2e-cleane2e-dirty-package-is-judged-failinge2e-dirty-project-is-judged-failinge2e-dirty-report-is-readablee2e-disabled-rule-is-not-rune2e-incomplete-artifacts-are-a-failuree2e-judging-nothing-failse2e-named-packages-are-every-packagee2e-no-packages-is-judged-failinge2e-no-store-paths-in-findingse2e-paths-are-repository-relativee2e-withheld-facts-faile2e-wrong-source-mapping-is-a-complainthlint-checkhopinionpre-commitreleaseshellweeder-checkconfiguredevShelldefaultx86_64-linux