~/bend-docscommunity

src/rules/suspicious/rewalk.bend checks

raw source on the hub · import 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/rules/suspicious/rewalk.bend as Rewalk

rule rewalk: one straight piece of a def calls the same walk twice on the same argument, and one result is used only for a single value (a get of one index, one field, or a let whose name is only read that way) while the other result is kept whole. Take the value from that other result. The same argument is the same text with no let of any name it uses (= or <-, taking effect at the end of the let) between the two calls. A different case arm is a different path, and two narrow reads are left alone. A walk is a def of this file that loops, or a Base walk (List.map and the like). Laws and proofs do not run (a def with no type at all fills a law: it is a proof). This is not twice, which is duplicate literals in a list pattern.

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 ../calls.bend as Calls
import ../../lazy/lazy.bend as Lazy

Types

type How source · line 20 · raw

Data

how the result of a call is used

type Site source · line 27 · raw

Data

one expensive call

type Hit source · line 31 · raw

Data

a narrow read and a wide read of one name

type Slot source · line 35 · raw

Data

where an accessor reads its collection

type Pos source · line 39 · raw

Data

a position a mark pass recorded

type Lhs source · line 43 · raw

Data

the left of = : one name, and whether a group makes it a field

type Bind source · line 47 · raw

Data

a let of one name, and where it takes effect

type Note source · line 51 · raw

Data

a let whose right-hand side is one call

Definitions

def none_hit source · line 55 · raw

Hit

neither read

def either source · line 59 · raw

@aa:Hit -> @+bb:Hit -> Hit

either read from two results

def name_slot source · line 65 · raw

@+slot:Bool -> Hit

a mention of the name: wide, unless this position is a narrow slot

def name_hit source · line 73 · raw

@+same:Bool -> @+slot:Bool -> Hit

a mention of the name, or nothing when it is a different name

def slot_pick source · line 81 · raw

@+list:Bool -> @+str:Bool -> @+arr:Bool -> Slot

which argument a narrow accessor reads

def slot_of source · line 93 · raw

@+tt:String -> Slot

List.get / head / last / length, String.get / length, Array.get; not List.set

def step_is_zero source · line 100 · raw

@pp:Nat -> Bool

after a comma, still inside the slot argument?

def step_hot_live source · line 108 · raw

@left:Nat -> Bool

after a comma, is the next argument the slot?

def step_hot source · line 116 · raw

@+hot:Bool -> @+left:Nat -> @+live:Bool -> Bool

the slot flag after a comma

def step_left_live source · line 124 · raw

@left:Nat -> Nat

the arguments still to skip after a comma

def step_left source · line 132 · raw

@+left:Nat -> @+live:Bool -> Nat

the arguments still to skip after a comma

def as_open source · line 140 · raw

@+open:Bool -> @tok:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.Tok -> Maybe<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.Tok>

the chain is exactly one call

def as_inside source · line 148 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Maybe<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.Tok>

the inside of one pair of parentheses, when it is exactly a call

def as_wrapped source · line 156 · raw

@+paren:Bool -> @kids:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Maybe<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.Tok>

a parenthesized call, or nothing

def as_call source · line 164 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Maybe<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.Tok>

the chain is exactly one call, or one pair of parentheses around one: its callee

def pos_of source · line 174 · raw

@mm:Maybe<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.Tok> -> List<&2, Pos>

the callee, when this argument is exactly a call

def mark_slot source · line 182 · raw

@ss:Slot -> @+as:List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node> -> List<&2, Pos>

the collection argument's call, when this is a narrow accessor

def mark source · line 191 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> List<&2, Pos>

calls that sit in a narrow accessor's collection argument

def here_call source · line 208 · raw

@+call:Bool -> @+tt:String -> @+name:String -> @+hot:Bool -> Hit

the name before a group is a narrow read, a callee, or an ordinary mention

def here_hit source · line 216 · raw

@+brack:Bool -> @+call:Bool -> @+tt:String -> @+name:String -> @+hot:Bool -> Hit

the name before a group is a narrow read, a callee, or an ordinary mention

def inner_hot_call source · line 224 · raw

@+call:Bool -> @+hot:Bool -> Bool

a bracket's inside is cold; a call's arguments start before the slot

def inner_hot source · line 232 · raw

@+brack:Bool -> @+call:Bool -> @+hot:Bool -> Bool

a bracket's inside is cold; a call's arguments start before the slot

def slot_at source · line 240 · raw

@ss:Slot -> Nat

the argument index a slot reads

def inner_left_call source · line 245 · raw

@+call:Bool -> @+tt:String -> Nat

the slot index of a call; a bracket has none

def inner_left source · line 253 · raw

@+brack:Bool -> @+call:Bool -> @+tt:String -> Nat

the slot index of a call; a bracket has none

def slot_on source · line 261 · raw

@ss:Slot -> Bool

whether an accessor has a collection argument

def inner_live_call source · line 266 · raw

@+call:Bool -> @+tt:String -> Bool

only a call's arguments count commas as separators

def inner_live source · line 274 · raw

@+brack:Bool -> @+call:Bool -> @+tt:String -> Bool

only a call's arguments count commas as separators

def has_eq source · line 282 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Bool

does the chain bind?

def hit_keep source · line 292 · raw

@skip:Bool -> @hh:Hit -> Hit

a read counts only once the left of = is over

def pick_hot source · line 300 · raw

@+skip:Bool -> @+brack:Bool -> @+call:Bool -> @+hot:Bool -> Bool

inside a call, unless this chain is still the left of a let

def pick_left source · line 308 · raw

@+skip:Bool -> @+brack:Bool -> @+call:Bool -> @+tt:String -> Nat

inside a call, unless this chain is still the left of a let

def pick_live source · line 316 · raw

@+skip:Bool -> @+brack:Bool -> @+call:Bool -> @+tt:String -> Bool

inside a call, unless this chain is still the left of a let

def hits source · line 325 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @+name:String -> @+hot:Bool -> @+left:Nat -> @+live:Bool -> @+skip:Bool -> Hit

reads of the name. hot: this chain is a collection argument. live/left: commas count toward that argument. skip: ignore names until = (the left of a let)

def narrow_let source · line 357 · raw

@hh:Hit -> Bool

a let of one name is narrow when that name is only read narrowly

def narrow source · line 362 · raw

@hh:How -> @+body:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Bool

a slot or a field is narrow; a wide use is not; a let depends on the name's reads

def called source · line 374 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> List<&2, String>

every name a node calls

def any_of source · line 389 · raw

@cs:List<&2, String> -> @+set:List<&2, String> -> Bool

does the list hold a name the set has?

def loops.go source · line 396 · raw

@ds:List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/rules/calls.Def> -> @+acc:List<&2, String> -> List<&2, String>

def loops source · line 406 · raw

@ds:List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/rules/calls.Def> -> List<&2, String>

defs of this file that loop

def heavy source · line 410 · raw

@+tt:String -> Bool

Base walks a rewalk cares about

def expensive source · line 417 · raw

@+tt:String -> @+self:String -> @+lp:List<&2, String> -> Bool

a loop of this file, or a Base walk, and not this def itself

def indexed source · line 421 · raw

@rest:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Bool

the rest of the chain indexes the call

def how_of source · line 429 · raw

@+nar:Bool -> How

a slot when the call is indexed, otherwise wide, when the call is expensive

def site_how source · line 437 · raw

@+mine:Bool -> @+nar:Bool -> Maybe<&2, How>

a slot when the call is indexed, otherwise wide, when the call is expensive

def site_list source · line 445 · raw

@mm:Maybe<&2, How> -> @+name:String -> @+args:String -> @+line:U32 -> @+col:U32 -> @+len:U32 -> List<&2, Site>

the call as a site, when it is one

def open_site source · line 453 · raw

@+open:Bool -> @+indexed:Bool -> @+tt:String -> @+args:String -> @+line:U32 -> @+col:U32 -> @+len:U32 -> @+self:String -> @+lp:List<&2, String> -> List<&2, Site>

one site when this application is an expensive call

def names source · line 471 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @+acc:List<&2, String> -> List<&2, String>

every name under the node

def lhs_names source · line 485 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @+acc:List<&2, String> -> List<&2, String>

the names a let binds: every name left of its = or <-

def last_pos source · line 501 · raw

@ts:List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.Tok> -> Pos

where a let's names take effect: its last token, after the right-hand side

def bind_all source · line 509 · raw

@ns:List<&2, String> -> @+line:U32 -> @+col:U32 -> @+acc:List<&2, Bind> -> List<&2, Bind>

one binding per name, at the let's end

def bind_at source · line 517 · raw

@ns:List<&2, String> -> @pp:Pos -> @+acc:List<&2, Bind> -> List<&2, Bind>

one binding per name, at this position

def binds source · line 522 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @+acc:List<&2, Bind> -> List<&2, Bind>

the lets of one straight piece; a case arm is not part of it

def before source · line 537 · raw

@+l1:U32 -> @+c1:U32 -> @+l2:U32 -> @+c2:U32 -> Bool

(l1, c1) comes before (l2, c2)

def count source · line 541 · raw

@bs:List<&2, Bind> -> @+name:String -> @+line:U32 -> @+col:U32 -> U32

how many lets of this name take effect before this position

def stamp source · line 551 · raw

@ns:List<&2, String> -> @+bs:List<&2, Bind> -> @+line:U32 -> @+col:U32 -> String

which binding of each name an argument reads: two calls with the same text read the same values only when no let of those names sits between them

def gather source · line 560 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @+self:String -> @+lp:List<&2, String> -> @+bs:List<&2, Bind> -> List<&2, Site>

every expensive call, wide unless the call is indexed on the spot; its arguments are their text and which let of each name they read

def add_seen source · line 579 · raw

@+seen:Bool -> @+extra:Bool -> @+field:Bool -> @+nm:String -> @+tt:String -> Lhs

a second name on the left

def fold_names source · line 587 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @st:Lhs -> Lhs

names inside a field group, which makes the let a field read

def eat source · line 600 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @st:Lhs -> Lhs

the left of =

def note_how source · line 615 · raw

@+field:Bool -> @+nm:String -> How

a field when the left has a group, otherwise a let of that name

def note_none source · line 623 · raw

@mm:Maybe<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.Tok> -> Maybe<&2, Note>

nothing, and the maybe is consumed

def note_some source · line 629 · raw

@+field:Bool -> @+nm:String -> @mm:Maybe<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.Tok> -> Maybe<&2, Note>

the note when the right-hand side is a call

def note_rhs source · line 637 · raw

@+one:Bool -> @+field:Bool -> @+nm:String -> @mm:Maybe<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/lex.Tok> -> Maybe<&2, Note>

the note for a call, when the left is exactly one name

def rhs_of source · line 645 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node

the tokens after =

def note_from source · line 655 · raw

@st:Lhs -> @+rhs:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Maybe<&2, Note>

a let or a field binding of exactly one call

def note_st source · line 660 · raw

@+kids:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Maybe<&2, Note>

a let or a field binding of exactly one call

def retag_one source · line 664 · raw

@+hit:Bool -> @how:How -> @+note:How -> @+name:String -> @+args:String -> @+line:U32 -> @+col:U32 -> @+len:U32 -> Site

one site, retagged when it is the let's call

def note_line source · line 672 · raw

@nn:Note -> U32

a note's line

def note_col source · line 677 · raw

@nn:Note -> U32

a note's column

def note_how_of source · line 682 · raw

@nn:Note -> How

a note's how

def retag source · line 687 · raw

@sites:List<&2, Site> -> @+note:Note -> List<&2, Site>

sites whose callee is this let's call take its how

def retag_may source · line 699 · raw

@mm:Maybe<&2, Note> -> @sites:List<&2, Site> -> List<&2, Site>

retag when the statement is a let of a call

def apply source · line 707 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @sites:List<&2, Site> -> List<&2, Site>

lets of a call retag that call

def pinned source · line 721 · raw

@+line:U32 -> @+col:U32 -> @ps:List<&2, Pos> -> Bool

this position was marked a collection argument

def pin_wide source · line 731 · raw

@how:How -> @+name:String -> @+args:String -> @+line:U32 -> @+col:U32 -> @+len:U32 -> Site

a wide call at a marked position becomes a slot; a let or a field stays

def pin_how source · line 739 · raw

@+hit:Bool -> @how:How -> @+name:String -> @+args:String -> @+line:U32 -> @+col:U32 -> @+len:U32 -> Site

a wide call at a marked position becomes a slot

def pin_one source · line 747 · raw

@ps:List<&2, Pos> -> @ss:Site -> Site

one site

def pin source · line 752 · raw

@+ps:List<&2, Pos> -> @sites:List<&2, Site> -> List<&2, Site>

marked collection arguments become slots

def other_full source · line 760 · raw

@+yes:Bool -> @how:How -> @+body:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Bool

the other site keeps the whole result

def other_go source · line 768 · raw

@sites:List<&2, Site> -> @+name:String -> @+args:String -> @+line:U32 -> @+col:U32 -> @+body:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Bool

another site has the same callee and the same arguments, and keeps the whole result

def earlier_nar source · line 780 · raw

@+yes:Bool -> @how:How -> @+body:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Bool

this earlier site is a narrow read of the same call

def earlier_one source · line 788 · raw

@ss:Site -> @+name:String -> @+args:String -> @+line:U32 -> @+col:U32 -> @+body:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Bool

this earlier site is a narrow read of the same call

def earlier source · line 795 · raw

@seen:List<&2, Site> -> @+name:String -> @+args:String -> @+line:U32 -> @+col:U32 -> @+body:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> Bool

a narrow site of this call already reported

def report_fresh source · line 805 · raw

@+prior:Bool -> @+name:String -> @+line:U32 -> @+col:U32 -> @+len:U32 -> @+path:String -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>

the finding when no earlier narrow site took this call

def report_nar source · line 821 · raw

@+nar:Bool -> @+prior:Bool -> @+name:String -> @+line:U32 -> @+col:U32 -> @+len:U32 -> @+path:String -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>

the finding, when this is the first narrow site of a duplicated call

def report_dup source · line 837 · raw

@+dup:Bool -> @how:How -> @+name:String -> @+args:String -> @+line:U32 -> @+col:U32 -> @+len:U32 -> @+body:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @+path:String -> @+seen:List<&2, Site> -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>

the finding when a duplicate exists and this site is the first narrow one

def report_one source · line 857 · raw

@ss:Site -> @+all:List<&2, Site> -> @+body:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @+path:String -> @+seen:List<&2, Site> -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>

one site

def report source · line 868 · raw

@sites:List<&2, Site> -> @+all:List<&2, Site> -> @+body:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @+path:String -> @+seen:List<&2, Site> -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>

the first narrow site of each duplicated call

def local source · line 882 · raw

@+nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @+self:String -> @+lp:List<&2, String> -> @+path:String -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>

findings in one straight region; a case arm is not part of it

def visit source · line 887 · raw

@nn:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/syntax/tree.Node -> @+self:String -> @+lp:List<&2, String> -> @+path:String -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>

each case arm on its own, so two arms are not one path

def check.go source · line 902 · raw

@ds:List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/rules/calls.Def> -> @+lp:List<&2, String> -> @+path:String -> @acc:List<&2, List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>> -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>

def check source · line 917 · raw

@ss:0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/src.Src -> List<&2, 0x582b4b0fdf3dafdeecc8c3bfddc5e4db/src/finding.Finding>

the rule