src/rules/suspicious/hoist.bend checks
raw source on the hub · import 0xd96f2ab40f5df4925c42e96d0ba857ff/src/rules/suspicious/hoist.bend as Hoist
rule hoist: a fixed table built inside a def that calls itself, from inputs
that do not change, and then indexed. The build runs again on every step.
Build it once, outside the recursion. A build is a list or array literal,
List.replicate / List.range / List.map / Array.new / Array.map, or
a call to a def of this file whose body is itself one fixed table of more
than eight cells; a def whose body cannot be seen (another module's) is not
a build. A table of eight cells or fewer is left alone, and so is a build
whose arguments depend on the step. A name that is not a carried parameter
(a pattern binder, a value computed on the step) keeps the build; a case arm
that does not recurse is cold; laws and proofs do not run (a def with no
type at all fills a law: it is a proof). List.get itself stays with
index.
8 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 ../calls.bend as Calls import ../../lazy/lazy.bend as Lazy import ./table.bend as Table
Types
type Slot source · line 23 · raw
Data
where an accessor reads its collection, when it is one
Slot@on:Bool -> @at:Nat -> Slot
type Got source · line 27 · raw
Data
a name = expr whose left side is one plain name
Got@nm:String -> @expr:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> Got
Definitions
def slot_pick source · line 31 · raw
@+list:Bool -> @+str:Bool -> @+arr:Bool -> Slot
which argument a get or set indexes
def slot_of source · line 43 · raw
@+tt:String -> Slot
List.get / List.set read argument 2, String.get argument 0, Array.get / Array.set argument 1
def pushes source · line 50 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+self:String -> List<&2, List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node>>
the self-calls' argument lists
def lone_eq source · line 66 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+name:String -> Bool
is the chain exactly this name?
def all_same source · line 74 · raw
@cs:List<&2, List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node>> -> @+ii:Nat -> @+name:String -> Bool
every self-call passes this same name at this argument
def carried.go source · line 84 · raw
@ps:List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node> -> @+cs:List<&2, List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node>> -> @+ii:Nat -> @+acc:List<&2, String> -> List<&2, String>
parameters every self-call passes unchanged, when there is a self-call
def carried_of source · line 99 · raw
@cs:List<&2, List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node>> -> @+sig:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> List<&2, String>
parameters passed through unchanged; empty when nothing recurses
def carried source · line 107 · raw
@sig:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @body:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+self:String -> List<&2, String>
parameters passed through unchanged; empty when nothing recurses
def name_ok source · line 111 · raw
@kk:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/lex.TokKind -> @+tt:String -> @+carried:List<&2, String> -> Bool
a plain name is closed only when it is a carried parameter; a type or a dotted name is
def leaf_ok source · line 119 · raw
@+skip:Bool -> @tok:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/lex.Tok -> @+carried:List<&2, String> -> Bool
a token after an operator: a template argument is not a value, anything else is
def value_ok source · line 128 · raw
@+app:Bool -> @kk:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/lex.TokKind -> @+tt:String -> @+carried:List<&2, String> -> Bool
an application is closed in its arguments; a bracketed name is a value
def closed source · line 136 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+carried:List<&2, String> -> Bool
every value name under the node is a carried parameter (callees and templates are not values)
def top_mark source · line 172 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+want:String -> Bool
a top-level token of this text, not one nested in a group
def is_array source · line 185 · raw
@+kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> Bool
[v : T*n] or [v : T^d]: an array, not a list
def top_count source · line 192 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+want:String -> @+got:Nat -> Nat
how many top-level copies of this token
def small_num source · line 205 · raw
@+tt:String -> Bool
a number literal of eight or fewer
def small_size source · line 210 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> Bool
the size after * is a literal of eight or fewer, or the depth after ^ (2^d slots) three or fewer
def wide_shape source · line 226 · raw
@+arr:Bool -> @+kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> Bool
more than eight cells: nine or more list elements, or an array whose size is not that small
def allowed source · line 234 · raw
@+tt:String -> Bool
constructors that build a fixed table rather than walk one
def builder source · line 240 · raw
@kk:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/lex.TokKind -> @+tt:String -> @+self:String -> @+wides:List<&2, String> -> Bool
a Base constructor on the allow list, or a def of this file whose body is
itself a wide table (wides); a def whose body cannot be seen is not one
def group_yes source · line 250 · raw
@+arr:Bool -> @+kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+carried:List<&2, String> -> Bool
a bracket group that is a closed list or array of more than eight cells
def table_group source · line 257 · raw
@+brack:Bool -> @+kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+carried:List<&2, String> -> Bool
a bracket group that is a closed list or array
def num_text source · line 265 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> String
the chain is exactly one number; its text, or empty
def count_wide source · line 273 · raw
@aa:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> Bool
a count is wide when it is not a literal of eight or fewer (a name may be large)
def last_node source · line 277 · raw
@as:List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node> -> 0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node
the last argument
def call_arm source · line 287 · raw
@rep:Bool -> @+as:List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node> -> @+end:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+range:Bool -> Bool
List.replicate / Array.new take argument 2; anything else is wide unless it is List.range
def call_wide source · line 295 · raw
@+tt:String -> @kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> Bool
List.replicate / Array.new / List.range are wide unless their count is a small literal
def table_built source · line 300 · raw
@+ok:Bool -> @+wide:Bool -> @kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+carried:List<&2, String> -> Bool
a call that builds a table from closed arguments, and is not a small literal count
def table_call source · line 310 · raw
@+app:Bool -> @kk:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/lex.TokKind -> @+tt:String -> @+kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+self:String -> @+wides:List<&2, String> -> @+carried:List<&2, String> -> Bool
a call that builds a table from closed arguments
def wide_of source · line 326 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> Bool
a fixed table of more than eight cells, whatever its cells read
def wide_one source · line 337 · raw
@body:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+name:String -> @+acc:List<&2, String> -> List<&2, String>
a def whose body is one wide fixed table (Table.built, and more than eight cells)
def wide_defs source · line 345 · raw
@ds:List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/rules/calls.Def> -> @acc:List<&2, String> -> List<&2, String>
defs of this file whose body is one wide fixed table
def is_table source · line 353 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+self:String -> @+wides:List<&2, String> -> @+carried:List<&2, String> -> Bool
a list, an array, or a builder call, closed over carried parameters
def skip_expr source · line 364 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> Maybe<&2, Got>
drop the right-hand side
def got_bad source · line 370 · raw
@+bad:Bool -> @+nm:String -> @expr:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> Maybe<&2, Got>
the chain after =, when the left side is one plain name and nothing else
def got_at source · line 378 · raw
@+seen:Bool -> @+bad:Bool -> @+nm:String -> @expr:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> Maybe<&2, Got>
the chain after =, when exactly one plain name stands before it
def eat source · line 386 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+seen:Bool -> @+bad:Bool -> @+nm:String -> Maybe<&2, Got>
a second name, a constructor or a group on the left is not a plain binder
def cite source · line 402 · raw
@+nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+path:String -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
a finding on the table expression
def wrapped_inside source · line 407 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+binder:String -> Bool
a name, and nothing after it
def wrapped_open source · line 415 · raw
@+paren:Bool -> @kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+binder:String -> Bool
inside one pair of parentheses
def wrapped source · line 423 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+binder:String -> Bool
the chain is exactly this name, or that name in parentheses
def uses_arg source · line 433 · raw
@ss:Slot -> @+as:List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node> -> @+binder:String -> Bool
the collection argument is this name
def uses_here_call source · line 442 · raw
@+call:Bool -> @+tt:String -> @kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+binder:String -> Bool
name[ is an index; a call is an index when its collection argument is the name
def uses_here source · line 450 · raw
@+brack:Bool -> @+call:Bool -> @+tt:String -> @kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+binder:String -> Bool
name[ is an index; a call is an index when its collection argument is the name
def uses_name source · line 458 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+binder:String -> Bool
the name is indexed later: name[, or the collection of a get or set
def uses_or source · line 480 · raw
@body:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @rest:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+binder:String -> Bool
indexed in the let's body or in a following statement
def let_used source · line 486 · raw
@+used:Bool -> @+expr:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+path:String -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
a finding when the table is also indexed
def let_use source · line 494 · raw
@+tab:Bool -> @+used:Bool -> @+expr:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+path:String -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
a finding when the right-hand side is a closed table that is indexed
def let_of source · line 502 · raw
@mm:Maybe<&2, Got> -> @+body:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+rest:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+self:String -> @+wides:List<&2, String> -> @+carried:List<&2, String> -> @+path:String -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
a plain let of a table
def let_hit source · line 518 · raw
@+hot:Bool -> @kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+body:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+rest:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+self:String -> @+wides:List<&2, String> -> @+carried:List<&2, String> -> @+path:String -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
a hot plain let of a table that is indexed afterwards
def one_yes source · line 535 · raw
@+yes:Bool -> @+aa:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+path:String -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
a finding when this argument is itself a closed table
def arg_table source · line 543 · raw
@ss:Slot -> @+as:List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node> -> @+self:String -> @+wides:List<&2, String> -> @+carried:List<&2, String> -> @+path:String -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
the collection argument, when this call indexes one
def call_open source · line 560 · raw
@+open:Bool -> @+tt:String -> @kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+self:String -> @+wides:List<&2, String> -> @+carried:List<&2, String> -> @+path:String -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
an inline table passed straight to a get or set, when the group is a call
def call_table source · line 576 · raw
@+hot:Bool -> @+open:Bool -> @+tt:String -> @kids:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+self:String -> @+wides:List<&2, String> -> @+carried:List<&2, String> -> @+path:String -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
an inline table passed straight to a get or set
def warm source · line 593 · raw
@+hot:Bool -> @kk:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/lex.TokKind -> Bool
a => cools the rest of its chain: a lambda's body runs when it is called, not per step
def walk source · line 601 · raw
@nn:0xd96f2ab40f5df4925c42e96d0ba857ff/src/syntax/tree.Node -> @+self:String -> @+wides:List<&2, String> -> @+carried:List<&2, String> -> @+path:String -> @+hot:Bool -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
tables built in a hot region
def check.go source · line 634 · raw
@ds:List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/rules/calls.Def> -> @+wide:List<&2, String> -> @+path:String -> @acc:List<&2, List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>> -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
def check source · line 650 · raw
@ss:0xd96f2ab40f5df4925c42e96d0ba857ff/src/src.Src -> List<&2, 0xd96f2ab40f5df4925c42e96d0ba857ff/src/finding.Finding>
the rule