src/rules/suspicious/eager.bend checks
raw source on the hub · import 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/rules/suspicious/eager.bend as Eager
rule eager: a branch of a Bool.pick calls a recursive def of the same
file. Bool.pick is an ordinary def, so both branch arguments are evaluated
before the call: the work in a branch runs whatever the condition says, and
the condition never saves it. pick catches the self-call and strict the
one under Bool.and/or; a call to a neighbour is the same bug and neither
sees it (a game's overlap test moved out of a pick branch took a scene from
31 to 55 fps; one gaps(..) in a branch here ran on every file the linter
read, 88 s of a 100 s run). Bind the call above the pick (+x = gaps(..)),
or take the branch lazily (Lazy.stop, Lazy.or_else from src/lazy/lazy.bend:
the last argument is a Unit -> T thunk, applied only on the branch that
needs it). Two things keep it off idiomatic code: only a call to a def of
this file counts, and only to one that loops (it calls itself, or reaches
something that does), since a Base call and a one-line accessor are
everywhere and cost nothing; only the branch arguments count, not the type
and the condition, which run whatever is written; and what sits after a
=> is a lambda's body, which the call does not run. The body ends at the
next comma of its group, so an argument after it counts again.
7 imports
import Base import ../../src.bend as Src import ../../finding.bend as F import ../../syntax/lex.bend as Lex import ../../syntax/tree.bend as Tree import ../../lazy/lazy.bend as Lazy import ../calls.bend as Calls
Definitions
def called source · line 27 · raw
@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> List<&2, String>
every name a node calls, f(..), at any depth
def any_of source · line 42 · raw
@cs:List<&2, String> -> @+set:List<&2, String> -> Bool
does the list hold a name the set has?
def loops.go source · line 49 · raw
@ds:List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/rules/calls.Def> -> @+acc:List<&2, String> -> List<&2, String>
def loops source · line 61 · raw
@ds:List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/rules/calls.Def> -> List<&2, String>
the defs of the file that loop: one that calls itself, or that reaches one that does (a def is defined above its use and there is no mutual recursion, so one pass down the file settles it)
def work.hot source · line 68 · raw
@kk:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.TokKind -> @+hot:Bool -> @+seg:U32 -> @+keep:Bool -> Bool
the hot a leaf leaves behind: a => clears it for the lambda's body; a
comma of a pick's own group moves to its next argument, hot from the third
on (0 the type, 1 the condition, 2 and 3 the branches), and a comma of any
other group returns it to keep; any other leaf leaves it as it was
def work.seg source · line 80 · raw
@kk:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.TokKind -> @+seg:U32 -> U32
the argument a leaf leaves behind: a comma of a pick's own group moves on
def work.name source · line 88 · raw
@kk:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.TokKind -> Bool
can the leaf name a call (it is neither a => nor a comma)?
def work source · line 105 · raw
@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @+ns:List<&2, String> -> @+self:String -> @+path:String -> @+hot:Bool -> @+seg:U32 -> @+keep:Bool -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>
the work under a def's body: hot says the node sits in a branch argument of
a Bool.pick (so it runs whatever the condition), seg counts the arguments
of the pick whose group this chain is (0 the type, 1 the condition, 2 and 3
the branches; 9 for a chain that is not a pick's), and keep is what hot
returns to at the next comma of a chain that is not a pick's, so a =>
clears hot for the lambda's own body and not for the arguments after it. A
leaf moves the state by its kind (work.hot, work.seg); a => or a comma
before a group names no call, so that group is walked as any other
def check.go source · line 143 · raw
@ds:List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/rules/calls.Def> -> @+ns:List<&2, String> -> @+path:String -> @acc:List<&2, List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>> -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>
def check source · line 158 · raw
@ss:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/src.Src -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>
the rule