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.