src/rules/suspicious/fromrev.bend

bolt@1.21.0.0 · 49 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.21.0.0/src/src.bend as Src
15import bolt@1.21.0.0/src/paths.bend as Paths
16import bolt@1.21.0.0/src/lazy/lazy.bend as Lazy
17import bolt@1.21.0.0/src/finding.bend as F
18import bolt@1.21.0.0/src/syntax/lex.bend as Lex
19import bolt@1.21.0.0/src/rules/tokens.bend as T
20
21# are the four texts `String.from_list`, `(`, `List.reverse` and `(`?
22def shape(+tt: String, +aa: String, +bb: String, +dd: String) -> Bool:
23  Bool.and(String.eq(tt, "String.from_list"),
24    Bool.and(String.eq(aa, "("), Bool.and(String.eq(bb, "List.reverse"), String.eq(dd, "("))))
25
26# a token of text tt, then these: do the four open the shape?
27def at(+tt: String, toks: List<&2, Lex.Tok>) -> Bool:
28  match toks:
29    case Con{Lex.Tok{k1, +aa, l1, c1}, Con{Lex.Tok{k2, +bb, l2, c2}, Con{Lex.Tok{k3, +dd, l3, c3}, rest}}}:
30      shape(tt, aa, bb, dd)
31    case other:
32      False{}
33
34# every run of the shape among the tokens, in order
35def walk(toks: List<&2, Lex.Tok>, +path: String) -> List<&2, F.Finding>:
36  match toks:
37    case Nil{}:
38      Nil{}
39    case Con{Lex.Tok{k, +t, +l, +c}, +rest}:
40      +more = walk(rest, path)
41      Bool.pick(List<&2, F.Finding>, at(t, rest),
42        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."}
43          <> more,
44        more)
45
46# the rule
47def check(ss: Src.Src) -> List<&2, F.Finding>:
48  Src.Src{+path, text, toks, tree, bound, items} = ss
49  Lazy.stop(List<&2, F.Finding>, Paths.is_law_file(path), [], _u => walk(T.sig(toks), path))