• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

source-academy / js-slang / 15237418122

25 May 2025 11:31AM UTC coverage: 77.048% (-3.5%) from 80.538%
15237418122

push

github

web-flow
Rewrite: Stepper (#1742)

3433 of 4826 branches covered (71.14%)

Branch coverage included in aggregate %.

1032 of 1260 new or added lines in 27 files covered. (81.9%)

440 existing lines in 29 files now uncovered.

10099 of 12737 relevant lines covered (79.29%)

142411.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.92
/src/tracer/nodes/Expression/ArrowFunctionExpression.ts
1
import { ArrowFunctionExpression, Comment, SourceLocation } from 'estree'
2
import { StepperBaseNode } from '../../interface'
3
import { StepperExpression, StepperPattern } from '..'
4
import { convert } from '../../generator'
61✔
5
import { getFreshName } from '../../utils'
61✔
6
import { StepperBlockStatement } from '../Statement/BlockStatement'
7

8
export class StepperArrowFunctionExpression implements ArrowFunctionExpression, StepperBaseNode {
61✔
9
  type: 'ArrowFunctionExpression'
10
  params: StepperPattern[]
11
  body: StepperExpression
12
  expression: boolean
13
  generator: boolean
14
  async: boolean
15
  name?: string
16
  leadingComments?: Comment[]
17
  trailingComments?: Comment[]
18
  loc?: SourceLocation | null
19
  range?: [number, number]
20

21
  constructor(
22
    params: StepperPattern[],
23
    body: StepperExpression,
24
    name?: string,
25
    expression: boolean = true,
8✔
26
    generator: boolean = false,
8✔
27
    async: boolean = false,
8✔
28
    leadingComments?: Comment[],
29
    trailingComments?: Comment[],
30
    loc?: SourceLocation | null,
31
    range?: [number, number]
32
  ) {
33
    this.type = 'ArrowFunctionExpression'
1,564✔
34
    this.params = params
1,564✔
35
    this.expression = expression
1,564✔
36
    this.generator = generator
1,564✔
37
    this.async = async
1,564✔
38
    this.name = name
1,564✔
39
    this.leadingComments = leadingComments
1,564✔
40
    this.trailingComments = trailingComments
1,564✔
41
    this.loc = loc
1,564✔
42
    this.range = range
1,564✔
43
    this.body = body
1,564✔
44
  }
45

46
  static create(node: ArrowFunctionExpression) {
47
    return new StepperArrowFunctionExpression(
298✔
48
      node.params.map(param => convert(param) as StepperPattern),
404✔
49
      convert(node.body) as StepperExpression,
50
      undefined,
51
      node.expression,
52
      node.generator,
53
      node.async,
54
      node.leadingComments,
55
      node.trailingComments,
56
      node.loc,
57
      node.range
58
    )
59
  }
60

61
  isContractible(): boolean {
NEW
62
    return false
×
63
  }
64

65
  isOneStepPossible(): boolean {
66
    return false
3,840✔
67
  }
68

69
  contract(): StepperExpression {
NEW
70
    throw new Error('Cannot contract an arrow function expression')
×
71
  }
72

73
  oneStep(): StepperExpression {
NEW
74
    throw new Error('Cannot step an arrow function expression')
×
75
  }
76

77
  assignName(name: string): StepperArrowFunctionExpression {
78
    return new StepperArrowFunctionExpression(
40✔
79
      this.params,
80
      this.body,
81
      name,
82
      this.expression,
83
      this.generator,
84
      this.async,
85
      this.leadingComments,
86
      this.trailingComments,
87
      this.loc,
88
      this.range
89
    )
90
  }
91

92
  scanAllDeclarationNames(): string[] {
93
    const paramNames = this.params.map(param => param.name)
3,855✔
94

95
    let bodyDeclarations: string[] = []
3,855✔
96
    // @ts-expect-error gracefully handle block statement as block expression
97
    if (this.body.type === 'BlockStatement') {
3,855✔
98
      const body = this.body as StepperBlockStatement
2,044✔
99
      bodyDeclarations = body.body
2,044✔
100
        .filter(stmt => stmt.type === 'VariableDeclaration')
2,313✔
101
        .flatMap(decl => (decl as any).declarations.map((d: any) => d.id.name))
108✔
102
    }
103

104
    return [...paramNames, ...bodyDeclarations]
3,855✔
105
  }
106

107
  // TODO: Fix name handling for lambda
108
  substitute(
109
    id: StepperPattern,
110
    value: StepperExpression,
111
    upperBoundName?: string[]
112
  ): StepperExpression {
113
    const valueFreeNames = value.freeNames()
1,317✔
114
    const scopeNames = this.scanAllDeclarationNames()
1,317✔
115
    const repeatedNames = valueFreeNames.filter(name => scopeNames.includes(name))
1,317✔
116

117
    let protectedNamesSet = new Set([this.allNames(), upperBoundName ?? []].flat())
1,317✔
118
    repeatedNames.forEach(name => protectedNamesSet.delete(name))
1,317✔
119
    const protectedNames = Array.from(protectedNamesSet)
1,317✔
120
    const newNames = getFreshName(repeatedNames, protectedNames)
1,317✔
121
    const currentArrowFunction = newNames.reduce(
1,317✔
122
      (current: StepperArrowFunctionExpression, name: string, index: number) =>
123
        current.rename(repeatedNames[index], name) as StepperArrowFunctionExpression,
26✔
124
      this
125
    )
126
    if (currentArrowFunction.scanAllDeclarationNames().includes(id.name)) {
1,317✔
127
      return currentArrowFunction
208✔
128
    }
129

130
    return new StepperArrowFunctionExpression(
1,109✔
131
      currentArrowFunction.params,
132
      currentArrowFunction.body.substitute(
133
        id,
134
        value,
135
        currentArrowFunction.params.flatMap(p => p.allNames())
987✔
136
      ),
137
      currentArrowFunction.name,
138
      currentArrowFunction.expression,
139
      currentArrowFunction.generator,
140
      currentArrowFunction.async,
141
      currentArrowFunction.leadingComments,
142
      currentArrowFunction.trailingComments,
143
      currentArrowFunction.loc,
144
      currentArrowFunction.range
145
    )
146
  }
147

148
  freeNames(): string[] {
149
    const paramNames = this.params
2,183✔
150
      .filter(param => param.type === 'Identifier')
2,207✔
151
      .map(param => param.name)
2,207✔
152
    return this.body.freeNames().filter(name => !paramNames.includes(name))
2,714✔
153
  }
154

155
  allNames(): string[] {
156
    const paramNames = this.params
2,406✔
157
      .filter(param => param.type === 'Identifier')
2,447✔
158
      .map(param => param.name)
2,447✔
159
    return Array.from(new Set([paramNames, this.body.allNames()].flat()))
2,406✔
160
  }
161

162
  rename(before: string, after: string): StepperExpression {
163
    return new StepperArrowFunctionExpression(
34✔
164
      this.params.map(param => param.rename(before, after)),
40✔
165
      this.body.rename(before, after),
166
      this.name,
167
      this.expression,
168
      this.generator,
169
      this.async,
170
      this.leadingComments,
171
      this.trailingComments,
172
      this.loc,
173
      this.range
174
    )
175
  }
176
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc