complexity/cognitive.bend
bend-lint-pack@0.1.1.0 · 117 lines · 12 definitions
1
2
3
4
5
6
7
8
9
10
11
12import Base
13import bend-lint-pack@0.1.1.0/vendor/lint.bend as Lint
14import bend-lint-pack@0.1.1.0/common.bend as Common
15
16def id() -> String:
17 "complexity/cognitive"
18
19def facts() -> Lint.Want:
20 Lint.Want{Lint.File{}, [], [], []}
21
22
23
24
25
26@unsafe
27def walk(n: Lint.Node, depth: U32, chain: Bool, total: U32) -> IO(U32):
28 do IO<U32>:
29 s : Lint.Shape <- Lint.shape(n)
30 walk.shape(s, depth, chain, total)
31
32
33def walk.all(cs: List<&2, Lint.Node>, +depth: U32, +chain: Bool, total: U32) -> IO(U32):
34 match cs:
35 case Nil{}:
36 IO.pure(U32, total)
37 case Con{c, rest}:
38 IO.bind(U32, U32, walk(c, depth, chain, total), t2 => walk.all(rest, depth, chain, t2))
39
40
41def walk.arms(cs: List<&2, Lint.Node>, +depth: U32, +first: Bool, total: U32) -> IO(U32):
42 match cs:
43 case Nil{}:
44 IO.pure(U32, total)
45 case Con{c, rest}:
46 IO.bind(
47 U32,
48 U32,
49 walk(c, Bool.pick(U32, first, U32.add(depth, 1), depth), Bool.not(first), total),
50 t2 => walk.arms(rest, depth, False{}, t2)
51 )
52
53def walk.step(
54 is_mat: Bool,
55 is_ann: Bool,
56 children: List<&2, Lint.Node>,
57 +depth: U32,
58 +chain: Bool,
59 total: U32
60) -> IO(U32):
61 match is_mat is_ann:
62 case True{} _:
63 walk.arms(
64 children,
65 depth,
66 True{},
67 U32.add(total, Bool.pick(U32, chain, 0, U32.add(depth, 1)))
68 )
69 case False{} True{}:
70 walk.all(children, depth, chain, total)
71 case False{} False{}:
72 walk.all(children, Bool.pick(U32, chain, U32.add(depth, 1), depth), False{}, total)
73
74def walk.shape(s: Lint.Shape, depth: U32, chain: Bool, total: U32) -> IO(U32):
75 match s:
76 case Lint.Shape{+kind, name, span, children}:
77 walk.step(String.eq(kind, "Mat"), String.eq(kind, "Ann"), children, depth, chain, total)
78
79def score.of(body: Maybe<&2, Lint.Node>) -> IO(U32):
80 match body:
81 case None{}:
82 IO.pure(U32, 0)
83 case Some{n}:
84 walk(n, 0, False{}, 0)
85
86def message(+name: String, score: U32, max: U32) -> String:
87 "def " ++ name ++ " has cognitive complexity " ++ U32.show(score) ++ "; the limit is " ++ U32.show(max) ++ ". Flatten nested matches into helper defs."
88
89def judge(+max: U32, seen: List<&2, Common.Seen>, out: List<&2, Lint.Diag>) -> IO(List<&2, Lint.Diag>):
90 match seen:
91 case Nil{}:
92 IO.pure(List<&2, Lint.Diag>, List.reverse(&2, Lint.Diag, out))
93 case Con{Common.Seen{+owner, span}, rest}:
94 do IO<List<&2, Lint.Diag>>:
95 body : Maybe<&2, Lint.Node> <- Lint.body(owner)
96 +score : U32 <- score.of(body)
97 judge(
98 max,
99 rest,
100 Common.onto(
101 Common.when(
102 U32.is_gt(score, max),
103 Common.diag(Lint.Warning{}, message(owner, score, max), span)
104 ),
105 out
106 )
107 )
108
109def run(input: Lint.Input) -> IO(List<&2, Lint.Diag>):
110 match input:
111 case Lint.Input{sources, options}:
112 do IO<List<&2, Lint.Diag>>:
113 owners : Common.Owners <- Lint.fold_facts(~Common.Owners, ~Common.Owners.collect, Common.Owners.empty())
114 judge(Lint.option_number(options, "max", 15), Common.Owners.ordered(owners), [])
115
116def main() -> IO(Unit):
117 Lint.serve(run)