src/rules/pedantic/strict.bend
bolt@1.27.0.0 · 130 lines · 10 definitions
1
2
3
4
5
6
7
8
9
10
11
12
13
14import Base
15import bolt@1.27.0.0/src/src.bend as Src
16import bolt@1.27.0.0/src/finding.bend as F
17import bolt@1.27.0.0/src/syntax/lex.bend as Lex
18import bolt@1.27.0.0/src/syntax/tree.bend as Tree
19import bolt@1.27.0.0/src/rules/calls.bend as Calls
20import bolt@1.27.0.0/src/lazy/lazy.bend as Lazy
21
22
23def logic(+tt: String) -> Bool:
24 Bool.or(String.eq(tt, "&&"), String.eq(tt, "||"))
25
26
27def seg_op.ends(kk: Lex.TokKind) -> Bool:
28 match kk:
29 case Lex.TComma{}:
30 True{}
31 case Lex.TLam{}:
32 True{}
33 case other:
34 False{}
35
36
37def seg_op.or(+here: Bool, more: Bool) -> Bool:
38 Bool.or(here, more)
39
40
41def seg_op(nn: Tree.Node) -> Bool:
42 match nn:
43 case Tree.NCons{+h, rest}:
44 Lazy.stop(Bool, Calls.kind.leaf(~seg_op.ends, h), False{},
45 _u => seg_op.or(Bool.and(Calls.kind.leaf(~Calls.kind.oper, h), logic(Calls.leaf.text(h))), seg_op(rest)))
46 case other:
47 False{}
48
49
50
51def inner(+hot: Bool, +oo: String, +tt: String) -> Bool:
52 Bool.or(hot, Bool.and(String.eq(oo, "("), Bool.or(String.eq(tt, "Bool.and"), String.eq(tt, "Bool.or"))))
53
54
55
56def call(
57 +tt: String,
58 +ll: U32,
59 +cc: U32,
60 +oo: String,
61 +name: String,
62 +path: String,
63 +hot: Bool,
64 kids: List<&2, F.Finding>,
65 rest: List<&2, F.Finding>
66) -> List<&2, F.Finding>:
67 +more = List.concat(&2, F.Finding, [kids, rest])
68 Bool.pick(List<&2, F.Finding>, Bool.and(Bool.and(String.eq(oo, "("), String.eq(tt, name)), hot),
69 F.Finding{path, ll, cc, U32.from_nat(String.length(name)), "strict",
70 name ++ " calls itself inside Bool.and or Bool.or, which evaluate both sides, so there is no short-circuit; match on the Bool in a helper."} <> more,
71 more)
72
73
74
75def leaf(
76 kk: Lex.TokKind,
77 comma: Unit -> List<&2, F.Finding>,
78 lam: Unit -> List<&2, F.Finding>,
79 plain: Unit -> List<&2, F.Finding>
80) -> List<&2, F.Finding>:
81 match kk:
82 case Lex.TComma{}:
83 comma(Unit{})
84 case Lex.TLam{}:
85 lam(Unit{})
86 case _k:
87 plain(Unit{})
88
89
90
91
92
93def walk(nn: Tree.Node, +name: String, +path: String, +outer: Bool, +hot: Bool) -> List<&2, F.Finding>:
94 match nn:
95 case Tree.NCons{Tree.Leaf{Lex.Tok{k, +t, +l, +c}}, +rest}:
96 match rest:
97 case Tree.NCons{Tree.Group{Lex.Tok{_, +o, _, _}, +kids, _}, r2}:
98 leaf(k, _u => walk(rest, name, path, outer, Bool.or(outer, seg_op(rest))),
99 _v => walk(rest, name, path, outer, seg_op(rest)),
100 _w => call(t, l, c, o, name, path, hot,
101 walk(kids, name, path, inner(hot, o, t), Bool.or(inner(hot, o, t), seg_op(kids))),
102 walk(r2, name, path, outer, hot)))
103 case _r:
104 leaf(k, _u => walk(rest, name, path, outer, Bool.or(outer, seg_op(rest))),
105 _v => walk(rest, name, path, outer, seg_op(rest)), _w => walk(rest, name, path, outer, hot))
106 case Tree.NCons{Tree.Group{open, +kids, close}, rest}:
107 List.concat(&2, F.Finding, [walk(kids, name, path, hot, Bool.or(hot, seg_op(kids))),
108 walk(rest, name, path, outer, hot)])
109 case Tree.NCons{Tree.Stmt{kind, +kids, body}, rest}:
110 List.concat(&2, F.Finding, [walk(kids, name, path, False{}, seg_op(kids)),
111 walk(body, name, path, False{}, False{}),
112 walk(rest, name, path, outer, hot)])
113 case Tree.NCons{h, rest}:
114 walk(rest, name, path, outer, hot)
115 case other:
116 Nil{}
117
118def check.go(ds: List<&2, Calls.Def>, +path: String, acc: List<&2, List<&2, F.Finding>>) -> List<&2, F.Finding>:
119 match ds:
120 case Nil{}:
121 List.concat(&2, F.Finding, List.reverse(&2, List<&2, F.Finding>, acc))
122 case Con{Calls.Def{+name, +sig, body}, rest}:
123 check.go(rest, path,
124 Lazy.stop(List<&2, F.Finding>, Calls.exempt(path, sig), [],
125 _u => walk(body, name, path, False{}, False{})) <> acc)
126
127
128def check(ss: Src.Src) -> List<&2, F.Finding>:
129 Src.Src{path, text, toks, tree, bound, items} = ss
130 check.go(Calls.defs(tree), path, [])