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

source-academy / js-slang / 12904208937

22 Jan 2025 08:32AM UTC coverage: 81.63% (-0.02%) from 81.654%
12904208937

Pull #1630

github

web-flow
Merge aa16b5a7d into 8ead9f26e
Pull Request #1630: Upgrade gl to v8, Prettier to v3

3654 of 4864 branches covered (75.12%)

Branch coverage included in aggregate %.

63 of 84 new or added lines in 13 files covered. (75.0%)

7 existing lines in 3 files now uncovered.

11499 of 13699 relevant lines covered (83.94%)

140593.93 hits per line

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

43.3
/src/errors/typeErrors.ts
1
import { generate } from 'astring'
79✔
2
import * as es from 'estree'
3

4
import { UNKNOWN_LOCATION } from '../constants'
79✔
5
import * as tsEs from '../typeChecker/tsESTree'
6
import {
79✔
7
  ErrorSeverity,
8
  ErrorType,
9
  Node,
10
  NodeWithInferredType,
11
  SArray,
12
  SourceError,
13
  Type
14
} from '../types'
15
import { simplify, stripIndent } from '../utils/formatters'
79✔
16
import { typeToString } from '../utils/stringify'
79✔
17

18
// tslint:disable:max-classes-per-file
19

20
export class InvalidArrayIndexType implements SourceError {
79✔
21
  public type = ErrorType.TYPE
×
22
  public severity = ErrorSeverity.WARNING
×
23

24
  constructor(
NEW
25
    public node: NodeWithInferredType<Node>,
×
NEW
26
    public receivedType: Type
×
27
  ) {}
28

29
  get location() {
30
    return this.node.loc ?? UNKNOWN_LOCATION
×
31
  }
32

33
  public explain() {
34
    return `Expected array index as number, got ${typeToString(this.receivedType)} instead`
×
35
  }
36

37
  public elaborate() {
38
    return this.explain()
×
39
  }
40
}
41

42
export class ArrayAssignmentError implements SourceError {
79✔
43
  public type = ErrorType.TYPE
×
44
  public severity = ErrorSeverity.WARNING
×
45

46
  constructor(
47
    public node: NodeWithInferredType<Node>,
×
48
    public arrayType: SArray,
×
49
    public receivedType: SArray
×
50
  ) {}
51

52
  get location() {
53
    return this.node.loc ?? UNKNOWN_LOCATION
×
54
  }
55

56
  public explain() {
57
    return stripIndent`Expected array type: ${typeToString(this.arrayType)}
×
58
    but got: ${typeToString(this.receivedType)}`
59
  }
60

61
  public elaborate() {
62
    return this.explain()
×
63
  }
64
}
65

66
export class ReassignConstError implements SourceError {
79✔
67
  public type = ErrorType.TYPE
×
68
  public severity = ErrorSeverity.WARNING
×
69

70
  constructor(public node: NodeWithInferredType<es.AssignmentExpression>) {}
×
71

72
  get location() {
73
    return this.node.loc ?? UNKNOWN_LOCATION
×
74
  }
75

76
  public explain() {
77
    const [varName] = formatAssignment(this.node)
×
78
    return `Reassignment of constant ${varName}`
×
79
  }
80

81
  public elaborate() {
82
    return this.explain()
×
83
  }
84
}
85

86
export class DifferentAssignmentError implements SourceError {
79✔
87
  public type = ErrorType.TYPE
×
88
  public severity = ErrorSeverity.WARNING
×
89

90
  constructor(
91
    public node: NodeWithInferredType<es.AssignmentExpression>,
×
92
    public expectedType: Type,
×
93
    public receivedType: Type
×
94
  ) {}
95

96
  get location() {
97
    return this.node.loc ?? UNKNOWN_LOCATION
×
98
  }
99

100
  public explain() {
101
    const [varName, assignmentStr] = formatAssignment(this.node)
×
102
    return stripIndent`
×
103
    Expected assignment of ${varName}:
104
      ${assignmentStr}
105
    to get a value of type:
106
      ${typeToString(this.expectedType)}
107
    but got a value of type:
108
      ${typeToString(this.receivedType)}
109
    `
110
  }
111

112
  public elaborate() {
113
    return this.explain()
×
114
  }
115
}
116

117
function formatAssignment(node: NodeWithInferredType<es.AssignmentExpression>): [string, string] {
118
  const leftNode = node.left as NodeWithInferredType<es.Identifier>
×
119
  const assignmentStr = simplify(generate(node.right))
×
120
  return [leftNode.name, assignmentStr]
×
121
}
122

123
export class CyclicReferenceError implements SourceError {
79✔
124
  public type = ErrorType.TYPE
×
125
  public severity = ErrorSeverity.WARNING
×
126

127
  constructor(public node: NodeWithInferredType<Node>) {}
×
128

129
  get location() {
130
    return this.node.loc ?? UNKNOWN_LOCATION
×
131
  }
132

133
  public explain() {
134
    return `${stringifyNode(this.node)} contains cyclic reference to itself`
×
135
  }
136

137
  public elaborate() {
138
    return this.explain()
×
139
  }
140
}
141

142
function stringifyNode(node: NodeWithInferredType<Node>): string {
143
  return ['VariableDeclaration', 'FunctionDeclaration'].includes(node.type)
×
144
    ? node.type === 'VariableDeclaration'
×
145
      ? (node.declarations[0].id as es.Identifier).name
146
      : (node as NodeWithInferredType<es.FunctionDeclaration>).id?.name!
147
    : node.type === 'Identifier'
×
148
      ? node.name
149
      : JSON.stringify(node) // might not be a good idea
150
}
151

152
export class DifferentNumberArgumentsError implements SourceError {
79✔
153
  public type = ErrorType.TYPE
×
154
  public severity = ErrorSeverity.WARNING
×
155

156
  constructor(
157
    public node: NodeWithInferredType<Node>,
×
158
    public numExpectedArgs: number,
×
159
    public numReceived: number
×
160
  ) {}
161

162
  get location() {
163
    return this.node.loc ?? UNKNOWN_LOCATION
×
164
  }
165

166
  public explain() {
167
    return `Function expected ${this.numExpectedArgs} args, but got ${this.numReceived}`
×
168
  }
169

170
  public elaborate() {
171
    return this.explain()
×
172
  }
173
}
174
export class InvalidArgumentTypesError implements SourceError {
79✔
175
  public type = ErrorType.TYPE
×
176
  public severity = ErrorSeverity.WARNING
×
177

178
  constructor(
179
    public node: NodeWithInferredType<Node>,
×
180
    public args: NodeWithInferredType<Node>[],
×
181
    public expectedTypes: Type[],
×
182
    public receivedTypes: Type[]
×
183
  ) {}
184

185
  get location() {
186
    return this.node.loc ?? UNKNOWN_LOCATION
×
187
  }
188

189
  public explain() {
190
    const argStrings = this.args.map(arg => simplify(generate(arg)))
×
191
    if ('operator' in this.node) {
×
192
      const op = this.node.operator
×
193
      if (this.expectedTypes.length === 2) {
×
194
        // binary operator
195
        return stripIndent`
×
196
        A type mismatch was detected in the binary expression:
197
          ${argStrings[0]} ${op} ${argStrings[1]}
198
        The binary operator (${op}) expected two operands with types:
199
          ${typeToString(this.expectedTypes[0])} ${op} ${typeToString(this.expectedTypes[1])}
200
        but instead it received two operands of types:
201
          ${typeToString(this.receivedTypes[0])} ${op} ${typeToString(this.receivedTypes[1])}
202
        `
203
      } else {
204
        // unary operator
205
        return stripIndent`
×
206
        A type mismatch was detected in the unary expression:
207
          ${op} ${argStrings[0]}
208
        The unary operator (${op}) expected its operand to be of type:
209
          ${typeToString(this.expectedTypes[0])}
210
        but instead it received an operand of type:
211
          ${typeToString(this.receivedTypes[0])}
212
        `
213
      }
214
    }
215
    const functionString = simplify(generate(this.node))
×
216
    function formatPhrasing(types: Type[]) {
217
      switch (types.length) {
×
218
        // there will at least be one argument
219
        case 1:
220
          return `an argument of type:
×
221
      ${typeToString(types[0])}`
222
        default:
223
          return `${types.length} arguments of types:
×
224
      ${types.map(typeToString).join(', ')}`
225
      }
226
    }
227
    return stripIndent`
×
228
    A type mismatch was detected in the function call:
229
      ${functionString}
230
    The function expected ${formatPhrasing(this.expectedTypes)}
231
    but instead received ${formatPhrasing(this.receivedTypes)}
232
    `
233
  }
234

235
  public elaborate() {
236
    return this.explain()
×
237
  }
238
}
239

240
function formatNodeWithTest(
241
  node: NodeWithInferredType<
242
    es.IfStatement | es.ConditionalExpression | es.WhileStatement | es.ForStatement
243
  >
244
) {
245
  let exprString = simplify(generate(node.test))
×
246
  let kind: string
247
  switch (node.type) {
×
248
    case 'IfStatement': {
249
      exprString = `if (${exprString}) { ... } else { ... }`
×
250
      kind = 'if statement'
×
251
      break
×
252
    }
253
    case 'ConditionalExpression': {
254
      exprString = `${exprString} ? ... : ...`
×
255
      kind = 'conditional expression'
×
256
      break
×
257
    }
258
    case 'WhileStatement': {
259
      exprString = `while (${exprString}) { ... }`
×
260
      kind = 'while statement'
×
261
      break
×
262
    }
263
    case 'ForStatement': {
264
      exprString = `for (...; ${exprString}; ...) { ... }`
×
265
      kind = 'for statement'
×
266
    }
267
  }
268
  return { exprString, kind }
×
269
}
270

271
export class InvalidTestConditionError implements SourceError {
79✔
272
  public type = ErrorType.TYPE
×
273
  public severity = ErrorSeverity.WARNING
×
274

275
  constructor(
276
    public node: NodeWithInferredType<
×
277
      es.IfStatement | es.ConditionalExpression | es.WhileStatement | es.ForStatement
278
    >,
279
    public receivedType: Type
×
280
  ) {}
281

282
  get location() {
283
    return this.node.loc ?? UNKNOWN_LOCATION
×
284
  }
285

286
  public explain() {
287
    const { exprString, kind } = formatNodeWithTest(this.node)
×
288
    return stripIndent`
×
289
    Expected the test part of the ${kind}:
290
      ${exprString}
291
    to have type boolean, but instead it is type:
292
      ${typeToString(this.receivedType)}
293
    `
294
  }
295

296
  public elaborate() {
297
    return this.explain()
×
298
  }
299
}
300

301
export class UndefinedIdentifierError implements SourceError {
79✔
302
  public type = ErrorType.TYPE
×
303
  public severity = ErrorSeverity.WARNING
×
304

305
  constructor(
NEW
306
    public node: NodeWithInferredType<es.Identifier>,
×
NEW
307
    public name: string
×
308
  ) {}
309

310
  get location() {
311
    return this.node.loc ?? UNKNOWN_LOCATION
×
312
  }
313

314
  public explain() {
315
    return stripIndent`
×
316
    One or more undeclared names detected (e.g. '${this.name}').
317
    If there aren't actually any undeclared names, then is either a Source or misconfiguration bug.
318
    Please report this to the administrators!
319
    `
320
  }
321

322
  public elaborate() {
323
    return this.explain()
×
324
  }
325
}
326

327
export class ConsequentAlternateMismatchError implements SourceError {
79✔
328
  public type = ErrorType.TYPE
×
329
  public severity = ErrorSeverity.WARNING
×
330

331
  constructor(
332
    public node: NodeWithInferredType<es.IfStatement | es.ConditionalExpression>,
×
333
    public consequentType: Type,
×
334
    public alternateType: Type
×
335
  ) {}
336

337
  get location() {
338
    return this.node.loc ?? UNKNOWN_LOCATION
×
339
  }
340

341
  public explain() {
342
    const { exprString, kind } = formatNodeWithTest(this.node)
×
343
    return stripIndent`
×
344
    The two branches of the ${kind}:
345
      ${exprString}
346
    produce different types!
347
    The true branch has type:
348
      ${typeToString(this.consequentType)}
349
    but the false branch has type:
350
      ${typeToString(this.alternateType)}
351
    `
352
  }
353

354
  public elaborate() {
355
    return this.explain()
×
356
  }
357
}
358

359
export class CallingNonFunctionType implements SourceError {
79✔
360
  public type = ErrorType.TYPE
×
361
  public severity = ErrorSeverity.WARNING
×
362

363
  constructor(
NEW
364
    public node: NodeWithInferredType<es.CallExpression>,
×
NEW
365
    public callerType: Type
×
366
  ) {}
367

368
  get location() {
369
    return this.node.loc ?? UNKNOWN_LOCATION
×
370
  }
371

372
  public explain() {
373
    return stripIndent`
×
374
    In
375
      ${simplify(generate(this.node))}
376
    expected
377
      ${simplify(generate(this.node.callee))}
378
    to be a function type, but instead it is type:
379
      ${typeToString(this.callerType)}
380
    `
381
  }
382

383
  public elaborate() {
384
    return this.explain()
×
385
  }
386
}
387

388
export class InconsistentPredicateTestError implements SourceError {
79✔
389
  public type = ErrorType.TYPE
×
390
  public severity = ErrorSeverity.WARNING
×
391

392
  constructor(
393
    public node: NodeWithInferredType<es.CallExpression>,
×
394
    public argVarName: string,
×
395
    public preUnifyType: Type,
×
396
    public predicateType: Type
×
397
  ) {}
398

399
  get location() {
400
    return this.node.loc ?? UNKNOWN_LOCATION
×
401
  }
402

403
  public explain() {
404
    const exprString = generate(this.node)
×
405
    return stripIndent`
×
406
    Inconsistent type constraints when trying to apply the predicate test
407
      ${exprString}
408
    It is inconsistent with the predicate tests applied before it.
409
    The variable ${this.argVarName} has type
410
      ${typeToString(this.preUnifyType)}
411
    but could not unify with type
412
      ${typeToString(this.predicateType)}
413
    `
414
  }
415

416
  public elaborate() {
417
    return this.explain()
×
418
  }
419
}
420

421
// Errors for Source Typed error checker
422

423
export class TypeMismatchError implements SourceError {
79✔
424
  public type = ErrorType.TYPE
143✔
425
  public severity = ErrorSeverity.ERROR
143✔
426

427
  constructor(
428
    public node: tsEs.Node,
143✔
429
    public actualTypeString: string,
143✔
430
    public expectedTypeString: string
143✔
431
  ) {}
432

433
  get location() {
434
    return this.node.loc ?? UNKNOWN_LOCATION
715!
435
  }
436

437
  public explain() {
438
    return `Type '${this.actualTypeString}' is not assignable to type '${this.expectedTypeString}'.`
143✔
439
  }
440

441
  public elaborate() {
442
    return this.explain()
×
443
  }
444
}
445

446
export class TypeNotFoundError implements SourceError {
79✔
447
  public type = ErrorType.TYPE
1✔
448
  public severity = ErrorSeverity.ERROR
1✔
449

450
  constructor(
451
    public node: tsEs.Node,
1✔
452
    public name: string
1✔
453
  ) {}
454

455
  get location() {
456
    return this.node.loc ?? UNKNOWN_LOCATION
5!
457
  }
458

459
  public explain() {
460
    return `Type '${this.name}' not declared.`
1✔
461
  }
462

463
  public elaborate() {
464
    return this.explain()
×
465
  }
466
}
467

468
export class FunctionShouldHaveReturnValueError implements SourceError {
79✔
469
  public type = ErrorType.TYPE
2✔
470
  public severity = ErrorSeverity.ERROR
2✔
471

472
  constructor(public node: tsEs.FunctionDeclaration | tsEs.ArrowFunctionExpression) {}
2✔
473

474
  get location() {
475
    return this.node.loc ?? UNKNOWN_LOCATION
10!
476
  }
477

478
  public explain() {
479
    return "A function whose declared type is neither 'void' nor 'any' must return a value."
2✔
480
  }
481

482
  public elaborate() {
483
    return this.explain()
×
484
  }
485
}
486

487
export class TypeNotCallableError implements SourceError {
79✔
488
  public type = ErrorType.TYPE
1✔
489
  public severity = ErrorSeverity.ERROR
1✔
490

491
  constructor(
492
    public node: tsEs.CallExpression,
1✔
493
    public typeName: string
1✔
494
  ) {}
495

496
  get location() {
497
    return this.node.loc ?? UNKNOWN_LOCATION
5!
498
  }
499

500
  public explain() {
501
    return `Type '${this.typeName}' is not callable.`
1✔
502
  }
503

504
  public elaborate() {
505
    return this.explain()
×
506
  }
507
}
508

509
export class TypecastError implements SourceError {
79✔
510
  public type = ErrorType.TYPE
2✔
511
  public severity = ErrorSeverity.ERROR
2✔
512

513
  constructor(
514
    public node: tsEs.TSAsExpression,
2✔
515
    public originalType: string,
2✔
516
    public typeToCastTo: string
2✔
517
  ) {}
518

519
  get location() {
520
    return this.node.loc ?? UNKNOWN_LOCATION
10!
521
  }
522

523
  public explain() {
524
    return `Type '${this.originalType}' cannot be casted to type '${this.typeToCastTo}' as the two types do not intersect.`
2✔
525
  }
526

527
  public elaborate() {
528
    return this.explain()
×
529
  }
530
}
531

532
export class TypeNotAllowedError implements SourceError {
79✔
533
  public type = ErrorType.TYPE
6✔
534
  public severity = ErrorSeverity.ERROR
6✔
535

536
  constructor(
537
    public node: tsEs.TSType,
6✔
538
    public name: string
6✔
539
  ) {}
540

541
  get location() {
542
    return this.node.loc ?? UNKNOWN_LOCATION
30!
543
  }
544

545
  public explain() {
546
    return `Type '${this.name}' is not allowed.`
6✔
547
  }
548

549
  public elaborate() {
550
    return this.explain()
×
551
  }
552
}
553

554
export class UndefinedVariableTypeError implements SourceError {
79✔
555
  public type = ErrorType.TYPE
1✔
556
  public severity = ErrorSeverity.ERROR
1✔
557

558
  constructor(
559
    public node: tsEs.Node,
1✔
560
    public name: string
1✔
561
  ) {}
562

563
  get location() {
564
    return this.node.loc ?? UNKNOWN_LOCATION
5!
565
  }
566

567
  public explain() {
568
    return `Name ${this.name} not declared.`
1✔
569
  }
570

571
  public elaborate() {
572
    return `Before you can read the value of ${this.name}, you need to declare it as a variable or a constant. You can do this using the let or const keywords.`
×
573
  }
574
}
575

576
export class InvalidNumberOfArgumentsTypeError implements SourceError {
79✔
577
  public type = ErrorType.TYPE
10✔
578
  public severity = ErrorSeverity.ERROR
10✔
579
  public calleeStr: string
580

581
  constructor(
582
    public node: tsEs.CallExpression,
10✔
583
    public expected: number,
10✔
584
    public got: number,
10✔
585
    public hasVarArgs = false
10✔
586
  ) {
587
    this.calleeStr = generate(node.callee)
10✔
588
  }
589

590
  get location() {
591
    return this.node.loc ?? UNKNOWN_LOCATION
50!
592
  }
593

594
  public explain() {
595
    return `Expected ${this.expected} ${this.hasVarArgs ? 'or more ' : ''}arguments, but got ${
10!
596
      this.got
597
    }.`
598
  }
599

600
  public elaborate() {
601
    const calleeStr = this.calleeStr
×
602
    const pluralS = this.expected === 1 ? '' : 's'
×
603

604
    return `Try calling function ${calleeStr} again, but with ${this.expected} argument${pluralS} instead. Remember that arguments are separated by a ',' (comma).`
×
605
  }
606
}
607

608
export class InvalidNumberOfTypeArgumentsForGenericTypeError implements SourceError {
79✔
609
  public type = ErrorType.TYPE
4✔
610
  public severity = ErrorSeverity.ERROR
4✔
611

612
  constructor(
613
    public node: tsEs.Node,
4✔
614
    public name: string,
4✔
615
    public expected: number
4✔
616
  ) {}
617

618
  get location() {
619
    return this.node.loc ?? UNKNOWN_LOCATION
20!
620
  }
621

622
  public explain() {
623
    return `Generic type '${this.name}' requires ${this.expected} type argument(s).`
4✔
624
  }
625

626
  public elaborate() {
627
    return this.explain()
×
628
  }
629
}
630

631
export class TypeNotGenericError implements SourceError {
79✔
632
  public type = ErrorType.TYPE
1✔
633
  public severity = ErrorSeverity.ERROR
1✔
634

635
  constructor(
636
    public node: tsEs.Node,
1✔
637
    public name: string
1✔
638
  ) {}
639

640
  get location() {
641
    return this.node.loc ?? UNKNOWN_LOCATION
5!
642
  }
643

644
  public explain() {
645
    return `Type '${this.name}' is not generic.`
1✔
646
  }
647

648
  public elaborate() {
649
    return this.explain()
×
650
  }
651
}
652

653
export class TypeAliasNameNotAllowedError implements SourceError {
79✔
654
  public type = ErrorType.TYPE
4✔
655
  public severity = ErrorSeverity.ERROR
4✔
656

657
  constructor(
658
    public node: tsEs.TSTypeAliasDeclaration,
4✔
659
    public name: string
4✔
660
  ) {}
661

662
  get location() {
663
    return this.node.loc ?? UNKNOWN_LOCATION
20!
664
  }
665

666
  public explain() {
667
    return `Type alias name cannot be '${this.name}'.`
4✔
668
  }
669

670
  public elaborate() {
671
    return this.explain()
×
672
  }
673
}
674

675
export class TypeParameterNameNotAllowedError implements SourceError {
79✔
676
  public type = ErrorType.TYPE
4✔
677
  public severity = ErrorSeverity.ERROR
4✔
678

679
  constructor(
680
    public node: tsEs.TSTypeParameter,
4✔
681
    public name: string
4✔
682
  ) {}
683

684
  get location() {
685
    return this.node.loc ?? UNKNOWN_LOCATION
20!
686
  }
687

688
  public explain() {
689
    return `Type parameter name cannot be '${this.name}'.`
4✔
690
  }
691

692
  public elaborate() {
693
    return this.explain()
×
694
  }
695
}
696

697
export class InvalidIndexTypeError implements SourceError {
79✔
698
  public type = ErrorType.TYPE
5✔
699
  public severity = ErrorSeverity.ERROR
5✔
700

701
  constructor(
702
    public node: tsEs.MemberExpression,
5✔
703
    public typeName: string
5✔
704
  ) {}
705

706
  get location() {
707
    return this.node.loc ?? UNKNOWN_LOCATION
25!
708
  }
709

710
  public explain() {
711
    return `Type '${this.typeName}' cannot be used as an index type.`
5✔
712
  }
713

714
  public elaborate() {
715
    return this.explain()
×
716
  }
717
}
718

719
export class InvalidArrayAccessTypeError implements SourceError {
79✔
720
  public type = ErrorType.TYPE
5✔
721
  public severity = ErrorSeverity.ERROR
5✔
722

723
  constructor(
724
    public node: tsEs.MemberExpression,
5✔
725
    public typeName: string
5✔
726
  ) {}
727

728
  get location() {
729
    return this.node.loc ?? UNKNOWN_LOCATION
25!
730
  }
731

732
  public explain() {
733
    return `Type '${this.typeName}' cannot be accessed as it is not an array.`
5✔
734
  }
735

736
  public elaborate() {
737
    return this.explain()
×
738
  }
739
}
740

741
export class ConstNotAssignableTypeError implements SourceError {
79✔
742
  public type = ErrorType.TYPE
1✔
743
  public severity = ErrorSeverity.WARNING
1✔
744

745
  constructor(
746
    public node: tsEs.AssignmentExpression,
1✔
747
    public name: string
1✔
748
  ) {}
749

750
  get location() {
751
    return this.node.loc ?? UNKNOWN_LOCATION
5!
752
  }
753

754
  public explain() {
755
    return `Cannot assign to '${this.name}' as it is a constant.`
1✔
756
  }
757

758
  public elaborate() {
759
    return this.explain()
×
760
  }
761
}
762

763
export class DuplicateTypeAliasError implements SourceError {
79✔
764
  public type = ErrorType.TYPE
3✔
765
  public severity = ErrorSeverity.ERROR
3✔
766

767
  constructor(
768
    public node: tsEs.TSTypeAliasDeclaration,
3✔
769
    public name: string
3✔
770
  ) {}
771

772
  get location() {
773
    return this.node.loc ?? UNKNOWN_LOCATION
15!
774
  }
775

776
  public explain() {
777
    return `Type alias '${this.name}' has already been declared.`
3✔
778
  }
779

780
  public elaborate() {
781
    return this.explain()
×
782
  }
783
}
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

© 2025 Coveralls, Inc