src/rules.bend
bolt@1.20.0.0 · 134 lines · 0 definitions
1# src/rules: every rule, and its group. A rule is a module under
2# rules/<group>/ with `check(src) -> List<Finding>`, run over each file read
3# once (src/src.bend); a project rule has `check(ds) -> List<Finding>` and
4# sees every file at once, as the digests of rules/digest.bend. The directory
5# names the group, and codes.bend says it again for the config.
6# Adding one is adding it here and a row in codes.bend. One rule is not
7# listed here, `noqa` (rules/style/noqa.bend): it reads the other rules'
8# graded findings, so the planner runs it after them (lint/plan.bend). Nor
9# is `setting` (rules/correctness/setting.bend): it reads each bolt.bend that
10# grades the run, not a source, so the planner runs it between the per-file
11# and the project rules.
12# The groups are what a bolt.bend switches at once: correctness (what will
13# fail or blow up), suspicious (what is probably a slip), style, laws, and
14# pedantic (advice that is noisy on idiomatic code: off unless a project
15# asks for it).
16import Base
17import bolt@1.20.0.0/src/finding.bend as F
18import bolt@1.20.0.0/src/codes.bend as Codes
19import bolt@1.20.0.0/src/src.bend as Src
20import bolt@1.20.0.0/src/rules/digest.bend as Digest
21import bolt@1.20.0.0/src/config.bend as Config
22import bolt@1.20.0.0/src/rules/style/doc.bend as Doc
23import bolt@1.20.0.0/src/rules/suspicious/unused.bend as Unused
24import bolt@1.20.0.0/src/rules/correctness/hole.bend as Hole
25import bolt@1.20.0.0/src/rules/style/space.bend as Space
26import bolt@1.20.0.0/src/rules/style/wrap.bend as Wrap
27import bolt@1.20.0.0/src/rules/style/param.bend as Param
28import bolt@1.20.0.0/src/rules/style/transcong.bend as Transcong
29import bolt@1.20.0.0/src/rules/correctness/pick.bend as Pick
30import bolt@1.20.0.0/src/rules/laws/law.bend as Law
31import bolt@1.20.0.0/src/rules/suspicious/strict.bend as Strict
32import bolt@1.20.0.0/src/rules/suspicious/eager.bend as Eager
33import bolt@1.20.0.0/src/rules/pedantic/tail.bend as Tail
34import bolt@1.20.0.0/src/rules/suspicious/concat.bend as Concat
35import bolt@1.20.0.0/src/rules/suspicious/index.bend as Index
36import bolt@1.20.0.0/src/rules/suspicious/table.bend as Table
37import bolt@1.20.0.0/src/rules/suspicious/hoist.bend as Hoist
38import bolt@1.20.0.0/src/rules/suspicious/ring.bend as Ring
39import bolt@1.20.0.0/src/rules/suspicious/rewalk.bend as Rewalk
40import bolt@1.20.0.0/src/rules/suspicious/unit.bend as Unit
41import bolt@1.20.0.0/src/rules/suspicious/scan.bend as Scan
42import bolt@1.20.0.0/src/rules/suspicious/thunk.bend as Thunk
43import bolt@1.20.0.0/src/rules/suspicious/fromrev.bend as Fromrev
44import bolt@1.20.0.0/src/rules/correctness/put.bend as Put
45import bolt@1.20.0.0/src/rules/correctness/escape.bend as Escape
46import bolt@1.20.0.0/src/rules/correctness/strings.bend as Strings
47import bolt@1.20.0.0/src/rules/correctness/chars.bend as Chars
48import bolt@1.20.0.0/src/rules/correctness/twice.bend as Twice
49import bolt@1.20.0.0/src/rules/correctness/arms.bend as Arms
50import bolt@1.20.0.0/src/rules/correctness/foreign.bend as Foreign
51import bolt@1.20.0.0/src/rules/suspicious/fuel.bend as Fuel
52import bolt@1.20.0.0/src/rules/laws/closed.bend as Closed
53import bolt@1.20.0.0/src/rules/laws/trace.bend as Trace
54import bolt@1.20.0.0/src/rules/laws/unsafe.bend as Unsafe
55
56# the file a Src was read from
57def on.path(ss: Src.Src) -> String:
58 Src.Src{path, _text, _toks, _tree, _bound, _items} = ss
59 path
60
61# findings put at a path: a per-file rule reports on its own file, so this
62# keeps what each rule gave and makes it hold by construction
63def on.at(+path: String, fs: List<&2, F.Finding>) -> List<&2, F.Finding>:
64 match fs:
65 case Nil{}:
66 Nil{}
67 case Con{F.Finding{_path, line, col, len, rule, msg}, rest}:
68 F.Finding{path, line, col, len, rule, msg} <> on.at(path, rest)
69
70# every rule's findings on a file read once, as each rule gives them
71def on.rules(+ss: Src.Src) -> List<&2, F.Finding>:
72 List.concat(&2, F.Finding, [
73 Doc.check(ss),
74 Unused.check(ss),
75 Hole.check(ss),
76 Space.check(ss),
77 Wrap.check(ss),
78 Param.check(ss),
79 Pick.check(ss),
80 Strict.check(ss),
81 Eager.check(ss),
82 Tail.check(ss),
83 Concat.check(ss),
84 Index.check(ss),
85 Table.check(ss),
86 Hoist.check(ss),
87 Ring.check(ss),
88 Rewalk.check(ss),
89 Unit.check(ss),
90 Scan.check(ss),
91 Thunk.check(ss),
92 Fromrev.check(ss),
93 Put.check(ss),
94 Escape.check(ss),
95 Strings.check(ss),
96 Chars.check(ss),
97 Twice.check(ss),
98 Arms.check(ss),
99 Foreign.check(ss),
100 Fuel.check(ss),
101 Closed.check(ss),
102 Transcong.check(ss)])
103
104# every rule's findings on a file read once, each at that file
105def on(+ss: Src.Src) -> List<&2, F.Finding>:
106 on.at(on.path(ss), on.rules(ss))
107
108# every rule's findings on a file
109def all(+path: String, +text: String) -> List<&2, F.Finding>:
110 on(Src.of(path, text))
111
112# every project rule's findings on the files and SPEC.md's text (None when
113# there is none): each file is read down to its digest once
114# (rules/digest.bend), and the rules run over those
115def project(srcs: List<&2, Src.Src>, whole: Bool, spec: Maybe<&2, String>) -> List<&2, F.Finding>:
116 +ds = Digest.all(srcs)
117 List.concat(&2, F.Finding, [Law.check(ds), Unsafe.check(ds), Trace.check(whole, spec, ds)])
118
119# the group a rule is in; an unknown slug grades with correctness
120def group(+rule: String) -> String:
121 +named = Codes.group(rule)
122 Bool.pick(String, String.is_empty(named), "correctness", named)
123
124# the findings at the levels a config gives their rules; the ones set off
125# are dropped
126def graded(+cfg: Config.Config, fs: List<&2, F.Finding>) -> List<&2, F.Graded>:
127 match fs:
128 case Nil{}:
129 Nil{}
130 case Con{F.Finding{path, line, col, len, +rule, msg}, rest}:
131 +more = graded(cfg, rest)
132 +l = Config.level(cfg, group(rule), rule)
133 Bool.pick(List<&2, F.Graded>, Config.is_off(l), more,
134 F.Graded{l, F.Finding{path, line, col, len, rule, msg}} <> more)