dead/def.bend
bend-lint-pack@0.1.1.0 · 199 lines · 16 definitions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import Base
21import bend-lint-pack@0.1.1.0/vendor/lint.bend as Lint
22import bend-lint-pack@0.1.1.0/common.bend as Common
23
24def id() -> String:
25 "dead/def"
26
27def facts() -> Lint.Want:
28 Lint.Want{Lint.Program{}, [], [], []}
29
30
31
32def is_root(+laws: Set(), +entries: List<&2, String>, +owner: String) -> Bool:
33 Bool.or(
34 String.eq(owner, "main"),
35 Bool.or(Common.Laws.has(laws, owner), Common.Entry.any(entries, owner))
36 )
37
38def roots(
39 seen: List<&2, Common.Seen>,
40 +laws: Set(),
41 +entries: List<&2, String>,
42 +out: List<&2, String>
43) -> List<&2, String>:
44 match seen:
45 case Nil{}:
46 out
47 case Con{Common.Seen{+owner, span}, rest}:
48 roots(
49 rest,
50 laws,
51 entries,
52 Bool.pick(List<&2, String>, is_root(laws, entries, owner), owner <> out, out)
53 )
54
55
56
57
58
59@unsafe
60def reach(work: List<&2, String>, +edges: Map<&2, List<&2, String>>, +seen: Set()) -> Set():
61 match work:
62 case Nil{}:
63 seen
64 case Con{+name, rest}:
65 reach.go(Common.has(seen, name), name, rest, edges, seen)
66
67def reach.go(
68 known: Bool,
69 +name: String,
70 rest: List<&2, String>,
71 +edges: Map<&2, List<&2, String>>,
72 +seen: Set()
73) -> Set():
74 match known:
75 case True{}:
76 reach(rest, edges, seen)
77 case False{}:
78 reach(
79 List.append(&2, String, Common.Edges.of(edges, name), rest),
80 edges,
81 Set.add(seen, name)
82 )
83
84
85
86def message(+owner: String, referenced: Bool) -> String:
87 Bool.pick(
88 String,
89 referenced,
90 "def " ++ owner ++ " is dead: only dead defs use it. Delete it, or name it in the `entries` option if code outside this program calls it.",
91 "def " ++ owner ++ " is never referenced. Delete it, or name it in the `entries` option if code outside this program calls it."
92 )
93
94def is_dead(+nodes: Set(), +reached: Set(), +owner: String) -> Bool:
95 Bool.and(Common.has(nodes, owner), Bool.not(Common.has(reached, owner)))
96
97def report(
98 seen: List<&2, Common.Seen>,
99 +nodes: Set(),
100 +reached: Set(),
101 +into: Map<&2, List<&2, String>>,
102 out: List<&2, Lint.Diag>
103) -> List<&2, Lint.Diag>:
104 match seen:
105 case Nil{}:
106 out
107 case Con{Common.Seen{+owner, span}, rest}:
108 report(
109 rest,
110 nodes,
111 reached,
112 into,
113 Common.onto(
114 Common.when(
115 is_dead(nodes, reached, owner),
116 Common.diag(
117 Lint.Warning{},
118 message(
119 owner,
120 Bool.not(List.is_empty(&2, String, Common.Edges.of(into, owner)))
121 ),
122 span
123 )
124 ),
125 out
126 )
127 )
128
129def nothing() -> Lint.Diag:
130 Common.diag(
131 Lint.Hint{},
132 "dead/def has nothing to measure from: the program has no `main`, no law and no `entries` option. Lint the entry file, or set `entries`.",
133 None{}
134 )
135
136
137def verdict.go(
138 none: Bool,
139 +rs: List<&2, String>,
140 shown: List<&2, Common.Seen>,
141 nodes: Set(),
142 +edges: Map<&2, List<&2, String>>,
143 into: Map<&2, List<&2, String>>
144) -> List<&2, Lint.Diag>:
145 match none:
146 case True{}:
147 [nothing()]
148 case False{}:
149 List.reverse(
150 &2,
151 Lint.Diag,
152 report(shown, nodes, reach(rs, edges, Set.new()), into, [])
153 )
154
155def verdict.roots(
156 +rs: List<&2, String>,
157 shown: List<&2, Common.Seen>,
158 nodes: Set(),
159 edges: Map<&2, List<&2, String>>,
160 into: Map<&2, List<&2, String>>
161) -> List<&2, Lint.Diag>:
162 verdict.go(List.is_empty(&2, String, rs), rs, shown, nodes, edges, into)
163
164def verdict.laws(
165 +laws: Set(),
166 entries: String,
167 +seen: List<&2, Common.Seen>,
168 scope: Common.Scope,
169 nodes: Set(),
170 edges: Map<&2, List<&2, String>>,
171 into: Map<&2, List<&2, String>>
172) -> List<&2, Lint.Diag>:
173 verdict.roots(
174 roots(seen, laws, Common.Entry.parse(entries), []),
175 Common.scoped(scope, seen),
176 nodes,
177 edges,
178 into
179 )
180
181def verdict(p: Common.Prog, entries: String, scope: Common.Scope) -> List<&2, Lint.Diag>:
182 match p:
183 case Common.Prog{seen, edges, into, nodes, laws}:
184 verdict.laws(laws, entries, seen, scope, nodes, edges, into)
185
186def run(input: Lint.Input) -> IO(List<&2, Lint.Diag>):
187 match input:
188 case Lint.Input{+sources, +options}:
189 do IO<List<&2, Lint.Diag>>:
190 g : Common.Graph <- Lint.fold_facts(~Common.Graph, ~Common.Graph.collect, Common.Graph.empty())
191 p : Common.Prog <- Common.Prog.of(sources, g)
192 return verdict(
193 p,
194 Lint.option_text(options, "entries", ""),
195 Common.Scope.of(sources, options)
196 )
197
198def main() -> IO(Unit):
199 Lint.serve(run)