1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! Narrow a whole-project tool's findings to the files drep was asked to check.
use BTreeSet;
use ;
use crateFinding;
use crateToolSpec;
/// Narrow a tool's findings to the files actually being checked.
///
/// A whole-project tool (`cargo clippy`, `tsc`, `dotnet format`) reports on
/// everything it compiled, and a commit gate that blocked on pre-existing
/// issues in untouched code would be unusable: the author cannot fix what they
/// did not write, and every commit would fail until the whole project was
/// clean.
///
/// A no-op for a tool that took the file list as arguments - it only reported
/// on what it was given. cppcheck is the first shipped tool where that is not
/// quite true, since it follows `#include` and can raise a finding in a header
/// the commit never touched; that is left alone here deliberately, because
/// narrowing every tool changes a contract this module documents and tests
/// rather than fixing a defect.
///
/// Comparison is on absolute paths, in both forms the two sides can spell
/// them. Stripping a leading `./` is not enough: the caller's list is
/// workspace-relative (`plan_tasks` builds each argument by
/// `strip_prefix(workspace_root)`) while a tool is free to answer in either
/// form, and `dotnet format` answers with the absolute path on every line.
/// And the tool's absolute form can be *canonical* where drep's is not: the
/// child runs with `current_dir(workspace_root)`, so a tool deriving paths
/// from its cwd spells them through resolved symlinks (and emits
/// `../..`-carrying relatives, as tflint `--recursive` does under one) while
/// drep keeps the spelling it was given. Byte-exact against one form, those
/// never match, the filter empties the vector and every file is reported
/// clean - the silent pass this module exists to refuse. `Path::join` with
/// an absolute argument yields that argument, so one join normalises both
/// forms.
///
/// The canonical set is built only once a finding has actually missed on the
/// exact form. Under an ordinary checkout every finding matches exactly, and
/// resolving each requested file up front spends a `realpath` per file to
/// answer a question nothing asks.
pub
/// The canonical forms of a set of paths, resolved on first use.
///
/// Every member costs a `realpath`, so the set is worth building only once
/// something has failed to match in exact form - which under an ordinary
/// checkout is never.
/// Where the tool said the finding is, resolved against `root`.
///
/// The single definition of that, shared by `retain_requested` and the
/// `run_one` rewrite so the two comparisons cannot drift: a tool may report a
/// workspace-relative path or an absolute path, and only `root.join` of the
/// `./`-stripped form spells both the same way.
///
/// The *canonical* form each caller reaches for on a miss is deliberately not
/// computed here. It is the answer to a rarer question - a tool that resolved
/// symlinks or emitted `..` segments for its own cwd - and it costs a
/// filesystem walk per call, which on the common path buys nothing.
pub