src/rules/correctness/twice.bend
bolt@1.27.0.0 · 89 lines · 8 definitions
1
2
3
4
5
6
7
8
9
10import Base
11import bolt@1.27.0.0/src/src.bend as Src
12import bolt@1.27.0.0/src/finding.bend as F
13import bolt@1.27.0.0/src/syntax/lex.bend as Lex
14import bolt@1.27.0.0/src/syntax/tree.bend as Tree
15import bolt@1.27.0.0/src/syntax/bind.bend as Bind
16import bolt@1.27.0.0/src/lazy/lazy.bend as Lazy
17import bolt@1.27.0.0/src/rules/tokens.bend as T
18import bolt@1.27.0.0/src/rules/calls.bend as Calls
19
20
21def head(pat: Tree.Node) -> Maybe<&2, Lex.Tok>:
22 match pat:
23 case Tree.NCons{Tree.Leaf{Lex.Tok{+k, +t, l, c}}, Tree.NCons{Tree.Leaf{Lex.Tok{k2, +o, l2, c2}}, rest}}:
24 Bool.pick(Maybe<&2, Lex.Tok>, Bool.and(T.is_lit(k), String.eq(o, "<>")), Some{Lex.Tok{k, t, l, c}}, None{})
25 case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TUpper{}, +t, l, c}},
26 Tree.NCons{Tree.Group{open, Tree.NCons{Tree.Leaf{Lex.Tok{+k, +x, l2, c2}}, kids}, close}, rest}}:
27 Bool.pick(Maybe<&2, Lex.Tok>, Bool.and(String.eq(t, "Con"), T.is_lit(k)), Some{Lex.Tok{k, x, l2, c2}}, None{})
28 case other:
29 None{}
30
31
32def tail(pat: Tree.Node) -> Tree.Node:
33 match pat:
34 case Tree.NCons{Tree.Leaf{Lex.Tok{Lex.TUpper{}, t, l, c}},
35 Tree.NCons{Tree.Group{open, Tree.NCons{x, Tree.NCons{comma, kids}}, close}, rest}}:
36 kids
37 case Tree.NCons{x, Tree.NCons{op, rest}}:
38 rest
39 case other:
40 Tree.NNil{}
41
42
43def same(aa: Maybe<&2, Lex.Tok>, bb: Maybe<&2, Lex.Tok>, +path: String) -> List<&2, F.Finding>:
44 match aa bb:
45 case Some{Lex.Tok{k, +t, l, c}} Some{Lex.Tok{k2, +t2, l2, c2}}:
46 Bool.pick(List<&2, F.Finding>, String.eq(t, t2),
47 [F.Finding{path, l2, c2, T.width(t2), "twice",
48 "This literal appears twice in one list pattern, which makes the checker hang; match one element per step."}],
49 [])
50 case a2 b2:
51 []
52
53
54def arm(+pat: Tree.Node, +path: String) -> List<&2, F.Finding>:
55 same(head(pat), head(tail(pat)), path)
56
57
58def walk(nn: Tree.Node, +path: String) -> List<&2, F.Finding>:
59 match nn:
60 case Tree.NCons{Tree.Stmt{Tree.SCase{}, kids, body}, rest}:
61 List.concat(&2, F.Finding, [arm(T.pattern(kids), path), walk(body, path), walk(rest, path)])
62 case Tree.NCons{Tree.Stmt{kind, kids, body}, rest}:
63 List.append(&2, F.Finding, walk(body, path), walk(rest, path))
64 case Tree.NCons{h, rest}:
65 walk(rest, path)
66 case other:
67 Nil{}
68
69
70def check.def(mm: Maybe<&2, String>, +body: Tree.Node, path: String) -> List<&2, F.Finding>:
71 match mm:
72 case None{}:
73 Nil{}
74 case Some{+name}:
75 Lazy.stop(List<&2, F.Finding>, Bool.not(Calls.calls(body, name)), [], _u => walk(body, path))
76
77def check.go(root: Tree.Node, +path: String) -> List<&2, F.Finding>:
78 match root:
79 case Tree.NCons{Tree.Stmt{Tree.SDef{}, +kids, body}, rest}:
80 List.append(&2, F.Finding, check.def(Bind.declared(kids), body, path), check.go(rest, path))
81 case Tree.NCons{h, rest}:
82 check.go(rest, path)
83 case other:
84 Nil{}
85
86
87def check(ss: Src.Src) -> List<&2, F.Finding>:
88 Src.Src{path, text, toks, tree, bound, items} = ss
89 check.go(tree, path)