src/rules/suspicious/fromrev.bend

bolt@1.24.0.0 · 56 lines · 0 definitions

1# rule fromrev: a String read off a reversed list: among the significant
2# tokens (spaces, newlines and comments dropped), four tokens right after one
3# another whose texts are `String.from_list`, `(`, `List.reverse` and `(`;
4# one finding, on the `String.from_list` token, for each such run, and
5# nothing else. Anything between them, another `(` included
6# (`String.from_list((List.reverse(..)))`), is not the shape, and neither is
7# `List.reverse.go`. A buffer of chars consed on the front, then reversed
8# and read into a String, walks the buffer twice and builds a list only to
9# throw it away (and String.from_list, not a tail call, runs the JS lane out
10# of stack on a long buffer): fold the buffer onto a String with SCon in one
11# pass, `go(t, SCon{h, acc})` from `SNil{}` (src/rchars.bend). A LAWS.bend
12# or a PROOF.bend, which never runs, is exempt.
13import Base
14import bolt@1.24.0.0/src/src.bend as Src
15import bolt@1.24.0.0/src/paths.bend as Paths
16import bolt@1.24.0.0/src/role.bend as Role
17import bolt@1.24.0.0/src/lazy/lazy.bend as Lazy
18import bolt@1.24.0.0/src/finding.bend as F
19import bolt@1.24.0.0/src/syntax/lex.bend as Lex
20import bolt@1.24.0.0/src/rules/tokens.bend as T
21
22# are the four texts `String.from_list`, `(`, `List.reverse` and `(`?
23def shape(+tt: String, +aa: String, +bb: String, +dd: String) -> Bool:
24  Bool.and(String.eq(tt, "String.from_list"),
25    Bool.and(String.eq(aa, "("), Bool.and(String.eq(bb, "List.reverse"), String.eq(dd, "("))))
26
27# a token of text tt, then these: do the four open the shape?
28def at(+tt: String, toks: List<&2, Lex.Tok>) -> Bool:
29  match toks:
30    case Con{Lex.Tok{k1, +aa, l1, c1}, Con{Lex.Tok{k2, +bb, l2, c2}, Con{Lex.Tok{k3, +dd, l3, c3}, rest}}}:
31      shape(tt, aa, bb, dd)
32    case other:
33      False{}
34
35# every run of the shape among the tokens, in order
36def walk(toks: List<&2, Lex.Tok>, +path: String) -> List<&2, F.Finding>:
37  match toks:
38    case Nil{}:
39      Nil{}
40    case Con{Lex.Tok{k, +t, +l, +c}, +rest}:
41      +more = walk(rest, path)
42      Bool.pick(List<&2, F.Finding>, at(t, rest),
43        F.Finding{path, l, c, 16, "fromrev", "String.from_list(List.reverse(..)) walks the list twice and builds one it throws away; fold it onto a String with SCon in one pass."}
44          <> more,
45        more)
46
47# the rule
48def check(ss: Src.Src) -> List<&2, F.Finding>:
49  Src.Src{+path, text, toks, tree, bound, items} = ss
50  Lazy.stop(List<&2, F.Finding>, Paths.is_law_file(path), [], _u => walk(T.sig(toks), path))
51
52# the rule, told the file's role (src/role.bend): a law or proof file is
53# skipped
54def check.role(rr: Role.Role, ss: Src.Src) -> List<&2, F.Finding>:
55  Src.Src{path, text, toks, tree, bound, items} = ss
56  Lazy.stop(List<&2, F.Finding>, Role.is_law_file(rr), [], _u => walk(T.sig(toks), path))