9e0f88e8

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

Say what a path is, and add the rule that holds us to it

A FilePath is a String, so nothing about it says whether it holds a file or a
directory, whether it is absolute or relative, or whether it is a path at all.
Every function taking one documents what it accepts and then trusts its
callers.

HsNoFilePath reports the type where a module's own signatures use it. Read off
the token stream rather than the parse tree, because that is where a name the
code wrote is already told apart from the same word in a comment, in a string
literal, or in the tail of a module name: `-- a FilePath is a String` and
`"a FilePath is a String"` are each one token of their own, and a golden asserts
both stay quiet.

Each finding is scoped to the declaration it is inside, so a boundary that
genuinely wants a String is suppressed at that declaration and nowhere wider.
Converting at the edge is not reported at all: `readFile (toFilePath file)` is
correct and stays quiet.

The repository is clean under it. Twenty findings, all in the test specs, are
gone: `resourceDir` is a `Path Rel Dir`, the three copies of `rootAt` take one,
and `listDirRel` answers with directories and files already told apart, which
several listing assertions are now exact about rather than comparing sorted
strings. `hopinion-gen` no longer depends on `directory`.

The library needed no changes to pass, which is worth saying: it had no FilePath
in its own signatures already. Its one word-comparison of a directory name is
now a comparison of directories.

What is not here: the same rule over `import System.FilePath` and
`import System.Directory`. An import is not a declaration, so the comment
attachment pass has nowhere to attach a suppression above one, and a
file-scoped suppression would exempt every other finding in the file. That
wants an AttachedToImport first.

Suite timing

Time to Start Worker time Duration Time to finish Idle
Config 4s 5s 5s 9s 4s
Eval 40s 14s 14s 54s 30s
Build 54s 10s 58s 1m52s 47s
Suite 4s 30s 1m48s 1m52s 1m22s

Timeline

0s50s1m50s