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

source-academy / js-slang / 24842185799

23 Apr 2026 02:52PM UTC coverage: 78.515% (+0.1%) from 78.391%
24842185799

Pull #1893

github

web-flow
Merge fccf53f80 into 715603479
Pull Request #1893: Error Handling and Stringify Changes

3126 of 4195 branches covered (74.52%)

Branch coverage included in aggregate %.

898 of 1088 new or added lines in 96 files covered. (82.54%)

21 existing lines in 12 files now uncovered.

7033 of 8744 relevant lines covered (80.43%)

178650.98 hits per line

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

90.74
/src/stepper/nodes/Expression/ArrowFunctionExpression.ts
1
import type { ArrowFunctionExpression, Comment, Expression, SourceLocation } from 'estree';
2
import type { StepperExpression, StepperPattern } from '..';
3
import { convert } from '../../generator';
4
import { StepperBaseNode } from '../../interface';
5
import { getFreshName } from '../../utils';
6
import type { StepperBlockStatement } from '../Statement/BlockStatement';
7
import { InternalRuntimeError } from '../../../errors/base';
8
import type { RedexInfo } from '../..';
9
import { mapAndFilter } from '../../../utils/misc';
10
import { isIdentifier } from '../../../utils/ast/typeGuards';
11

12
export class StepperArrowFunctionExpression
13
  extends StepperBaseNode<ArrowFunctionExpression>
14
  implements ArrowFunctionExpression
15
{
16
  constructor(
17
    public readonly params: StepperPattern[],
1,577✔
18
    public readonly body: StepperExpression,
1,577✔
19
    public readonly name?: string,
1,577✔
20
    public readonly expression: boolean = true,
1,577✔
21
    public readonly generator: boolean = false,
1,577✔
22
    public readonly async: boolean = false,
1,577✔
23
    leadingComments?: Comment[],
24
    trailingComments?: Comment[],
25
    loc?: SourceLocation | null,
26
    range?: [number, number],
27
  ) {
28
    super('ArrowFunctionExpression', leadingComments, trailingComments, loc, range);
1,577✔
29
  }
30

31
  static create(node: ArrowFunctionExpression) {
32
    return new StepperArrowFunctionExpression(
302✔
33
      node.params.map(param => convert(param)),
406✔
34
      convert(node.body as Expression),
35
      undefined,
36
      node.expression,
37
      node.generator,
38
      node.async,
39
      node.leadingComments,
40
      node.trailingComments,
41
      node.loc,
42
      node.range,
43
    );
44
  }
45

46
  public override isContractible(): boolean {
47
    return false;
×
48
  }
49

50
  public override isOneStepPossible(): boolean {
51
    return false;
3,917✔
52
  }
53

54
  public override contract(): StepperExpression {
NEW
55
    throw new InternalRuntimeError('Cannot contract an arrow function expression', this);
×
56
  }
57

58
  public override oneStep(): StepperExpression {
NEW
59
    throw new InternalRuntimeError('Cannot step an arrow function expression', this);
×
60
  }
61

62
  assignName(name: string): StepperArrowFunctionExpression {
63
    return new StepperArrowFunctionExpression(
41✔
64
      this.params,
65
      this.body,
66
      name,
67
      this.expression,
68
      this.generator,
69
      this.async,
70
      this.leadingComments,
71
      this.trailingComments,
72
      this.loc,
73
      this.range,
74
    );
75
  }
76

77
  scanAllDeclarationNames(): string[] {
78
    const paramNames = this.params.map(param => param.name);
3,447✔
79

80
    let bodyDeclarations: string[] = [];
3,447✔
81
    // @ts-expect-error gracefully handle block statement as block expression
82
    if (this.body.type === 'BlockStatement') {
3,447✔
83
      const body = this.body as StepperBlockStatement;
1,545✔
84
      bodyDeclarations = body.body
1,545✔
85
        .filter(stmt => stmt.type === 'VariableDeclaration')
1,814✔
86
        .flatMap(decl => (decl as any).declarations.map((d: any) => d.id.name));
108✔
87
    }
88

89
    return [...paramNames, ...bodyDeclarations];
3,447✔
90
  }
91

92
  // TODO: Fix name handling for lambda
93
  public override substitute(
94
    id: StepperPattern,
95
    value: StepperExpression,
96
    redex: RedexInfo,
97
    upperBoundName?: string[],
98
  ): StepperExpression {
99
    const valueFreeNames = value.freeNames();
1,350✔
100
    const scopeNames = this.scanAllDeclarationNames();
1,350✔
101
    const repeatedNames = valueFreeNames.filter(name => scopeNames.includes(name));
1,350✔
102

103
    let protectedNamesSet = new Set([this.allNames(), upperBoundName ?? []].flat());
1,350✔
104
    repeatedNames.forEach(name => protectedNamesSet.delete(name));
1,350✔
105
    const protectedNames = Array.from(protectedNamesSet);
1,350✔
106
    const newNames = getFreshName(repeatedNames, protectedNames);
1,350✔
107
    const currentArrowFunction = newNames.reduce(
1,350✔
108
      (current: StepperArrowFunctionExpression, name: string, index: number) =>
109
        current.rename(repeatedNames[index], name) as StepperArrowFunctionExpression,
26✔
110
      this,
111
    );
112
    if (currentArrowFunction.scanAllDeclarationNames().includes(id.name)) {
1,350✔
113
      return currentArrowFunction;
233✔
114
    }
115

116
    return new StepperArrowFunctionExpression(
1,117✔
117
      currentArrowFunction.params,
118
      currentArrowFunction.body.substitute(
119
        id,
120
        value,
121
        redex,
122
        currentArrowFunction.params.flatMap(p => p.allNames()),
995✔
123
      ),
124
      currentArrowFunction.name,
125
      currentArrowFunction.expression,
126
      currentArrowFunction.generator,
127
      currentArrowFunction.async,
128
      currentArrowFunction.leadingComments,
129
      currentArrowFunction.trailingComments,
130
      currentArrowFunction.loc,
131
      currentArrowFunction.range,
132
    );
133
  }
134

135
  public override freeNames(): string[] {
136
    const paramNames = mapAndFilter(this.params, param =>
2,207!
137
      isIdentifier(param) ? param.name : undefined,
138
    );
139
    return this.body.freeNames().filter(name => !paramNames.includes(name));
2,714✔
140
  }
141

142
  public override allNames(): string[] {
143
    const paramNames = mapAndFilter(this.params, param =>
2,480!
144
      isIdentifier(param) ? param.name : undefined,
145
    );
146
    return Array.from(new Set([paramNames, this.body.allNames()].flat()));
2,439✔
147
  }
148

149
  public override rename(before: string, after: string): StepperExpression {
150
    return new StepperArrowFunctionExpression(
34✔
151
      this.params.map(param => param.rename(before, after)),
40✔
152
      this.body.rename(before, after),
153
      this.name,
154
      this.expression,
155
      this.generator,
156
      this.async,
157
      this.leadingComments,
158
      this.trailingComments,
159
      this.loc,
160
      this.range,
161
    );
162
  }
163
}
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