~/bend-docscommunity

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

type Got source · line 27 · raw

Data

a name = expr whose left side is one plain name

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