language/forward_reference.bend
bend-lint-rules@0.2.0.0 · 126 lines · 11 definitions
1
2
3
4
5
6
7
8import Base
9import bend-lint-api@0.2.0.0/linter/lint.bend as Lint
10import bend-lint-api@0.2.0.0/shared/lists.bend as Lists
11import bend-lint-api@0.2.0.0/shared/names.bend as Names
12
13
14
15
16
17type Place is Data:
18 Place{at: U32, name: String}
19
20
21
22type User is Data:
23 User{places: Map<&2, Maybe<&2, Place>>, at: U32, name: String}
24
25
26
27def message(+used: String, +user: String) -> String:
28 used
29 ++ " is defined below "
30 ++ user
31 ++ ", and Bend checks definitions in file order: live code can use only what is defined above it, so this use fails the check (\"a filled definition\"). Move "
32 ++ used
33 ++ " above "
34 ++ user
35 ++ ". If the two call each other, merge them into one definition with an argument that selects which to run: Bend does not allow mutual recursion. This is a false positive if the use is an argument for an erased parameter (`-x`): that is dead code."
36
37
38def places(
39 nodes: List<&2, Lint.Node>,
40 +at: U32,
41 out: Map<&2, Maybe<&2, Place>>
42) -> Map<&2, Maybe<&2, Place>>:
43 match nodes:
44 case Nil{}:
45 out
46 case Con{Lint.Node{Lint.Definition{t, u}, name, s, k, f}, rest}:
47 places(rest, (at + 1: U32), Map.set(&2, Maybe<&2, Place>, out, t, Some{Place{at, name}}))
48 case Con{node, rest}:
49 places(rest, (at + 1: U32), out)
50
51
52
53def below(
54 hit: Bool,
55 path: List<&2, Lint.Above>,
56 used: String,
57 user: String,
58 span: Maybe<&2, Lint.Span>
59) -> List<&2, Lint.Finding>:
60 match hit:
61 case True{}:
62 Lint.finding(Lint.Warning{}, Lint.is_live(path), span, message(used, user))
63 case False{}:
64 []
65
66def judged(
67 path: List<&2, Lint.Above>,
68 place: Maybe<&2, Place>,
69 user: User,
70 span: Maybe<&2, Lint.Span>
71) -> List<&2, Lint.Finding>:
72 match place user:
73 case Some{Place{p, used}} User{places, at, name}:
74 below(U32.is_gt(p, at), path, used, name, span)
75 case _ _:
76 []
77
78def User.places(user: User) -> Map<&2, Maybe<&2, Place>>:
79 match user:
80 case User{places, at, name}:
81 places
82
83def use(+user: User, +path: List<&2, Lint.Above>, +node: Lint.Node) -> List<&2, Lint.Finding>:
84 match node:
85 case Lint.Node{Lint.Reference{t}, n, span, k, f}:
86 judged(path, Names.find(Place, User.places(user), t), user, span)
87 case _:
88 []
89
90
91def users(
92 nodes: List<&2, Lint.Node>,
93 +places: Map<&2, Maybe<&2, Place>>,
94 +at: U32,
95 out: List<&2, Lint.Finding>
96) -> List<&2, Lint.Finding>:
97 match nodes:
98 case Nil{}:
99 List.reverse(&2, Lint.Finding, out)
100 case Con{Lint.Node{Lint.Definition{t, False{}}, +name, s, k, f}, rest}:
101 users(
102 rest,
103 places,
104 (at + 1: U32),
105 Lists.onto(
106 &2,
107 Lint.Finding,
108 Lint.visit(
109 ~User,
110 ~Lint.Finding,
111 ~use,
112 User{places, at, name},
113 [Lint.Node{Lint.Definition{t, False{}}, name, s, k, f}]
114 ),
115 out
116 )
117 )
118 case Con{node, rest}:
119 users(rest, places, (at + 1: U32), out)
120
121def run(+here: Lint.Here) -> Lint.Ask(List<&2, Lint.Finding>):
122 +nodes = Lint.Here.nodes(here)
123 Lint.done(users(nodes, places(nodes, 0, Map.new(&2, Maybe<&2, Place>)), 0, []))
124
125def rule() -> Lint.Rule:
126 Lint.Rule{Lint.Needs{False{}, False{}}, [], Lint.OnFile{run}}