• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import * as ts from "./_namespaces/ts";
2import {
3    AnyFunction,
4    AssertionLevel,
5    BigIntLiteralType,
6    CheckMode,
7    compareValues,
8    EmitFlags,
9    every,
10    FlowFlags,
11    FlowLabel,
12    FlowNode,
13    FlowNodeBase,
14    FlowSwitchClause,
15    formatStringFromArgs,
16    getEffectiveModifierFlagsNoCache,
17    getEmitFlags,
18    getOwnKeys,
19    getParseTreeNode,
20    getSourceFileOfNode,
21    getSourceTextOfNodeFromSourceFile,
22    hasProperty,
23    idText,
24    IntrinsicType,
25    isArrayTypeNode,
26    isBigIntLiteral,
27    isCallSignatureDeclaration,
28    isConditionalTypeNode,
29    isConstructorDeclaration,
30    isConstructorTypeNode,
31    isConstructSignatureDeclaration,
32    isDefaultClause,
33    isFunctionTypeNode,
34    isGeneratedIdentifier,
35    isGetAccessorDeclaration,
36    isIdentifier,
37    isImportTypeNode,
38    isIndexedAccessTypeNode,
39    isIndexSignatureDeclaration,
40    isInferTypeNode,
41    isIntersectionTypeNode,
42    isLiteralTypeNode,
43    isMappedTypeNode,
44    isNamedTupleMember,
45    isNumericLiteral,
46    isOptionalTypeNode,
47    isParameter,
48    isParenthesizedTypeNode,
49    isParseTreeNode,
50    isPrivateIdentifier,
51    isRestTypeNode,
52    isSetAccessorDeclaration,
53    isStringLiteral,
54    isThisTypeNode,
55    isTupleTypeNode,
56    isTypeLiteralNode,
57    isTypeOperatorNode,
58    isTypeParameterDeclaration,
59    isTypePredicateNode,
60    isTypeQueryNode,
61    isTypeReferenceNode,
62    isUnionTypeNode,
63    LiteralType,
64    map,
65    Map,
66    MatchingKeys,
67    ModifierFlags,
68    Node,
69    NodeArray,
70    NodeFlags,
71    nodeIsSynthesized,
72    noop,
73    objectAllocator,
74    ObjectFlags,
75    ObjectType,
76    RelationComparisonResult,
77    Set,
78    Signature,
79    SignatureCheckMode,
80    SignatureFlags,
81    SnippetKind,
82    SortedReadonlyArray,
83    stableSort,
84    Symbol,
85    SymbolFlags,
86    symbolName,
87    SyntaxKind,
88    TransformFlags,
89    Type,
90    TypeFacts,
91    TypeFlags,
92    TypeMapKind,
93    TypeMapper,
94    unescapeLeadingUnderscores,
95    VarianceFlags,
96    version,
97    Version,
98    zipWith
99} from "./_namespaces/ts";
100
101/** @internal */
102export enum LogLevel {
103    Off,
104    Error,
105    Warning,
106    Info,
107    Verbose
108}
109
110/** @internal */
111export interface LoggingHost {
112    log(level: LogLevel, s: string): void;
113}
114
115/** @internal */
116export interface DeprecationOptions {
117    message?: string;
118    error?: boolean;
119    since?: Version | string;
120    warnAfter?: Version | string;
121    errorAfter?: Version | string;
122    typeScriptVersion?: Version | string;
123    name?: string;
124}
125
126/** @internal */
127export namespace Debug {
128    let typeScriptVersion: Version | undefined;
129
130    /* eslint-disable prefer-const */
131    let currentAssertionLevel = AssertionLevel.None;
132    export let currentLogLevel = LogLevel.Warning;
133    export let isDebugging = false;
134    export let loggingHost: LoggingHost | undefined;
135    export let enableDeprecationWarnings = true;
136    /* eslint-enable prefer-const */
137
138    type AssertionKeys = MatchingKeys<typeof Debug, AnyFunction>;
139    export function getTypeScriptVersion() {
140        return typeScriptVersion ?? (typeScriptVersion = new Version(version));
141    }
142
143    export function shouldLog(level: LogLevel): boolean {
144        return currentLogLevel <= level;
145    }
146
147    function logMessage(level: LogLevel, s: string): void {
148        if (loggingHost && shouldLog(level)) {
149            loggingHost.log(level, s);
150        }
151    }
152
153    export function log(s: string): void {
154        logMessage(LogLevel.Info, s);
155    }
156
157    export namespace log {
158        export function error(s: string): void {
159            logMessage(LogLevel.Error, s);
160        }
161
162        export function warn(s: string): void {
163            logMessage(LogLevel.Warning, s);
164        }
165
166        export function log(s: string): void {
167            logMessage(LogLevel.Info, s);
168        }
169
170        export function trace(s: string): void {
171            logMessage(LogLevel.Verbose, s);
172        }
173    }
174
175    const assertionCache: Partial<Record<AssertionKeys, { level: AssertionLevel, assertion: AnyFunction }>> = {};
176
177    export function getAssertionLevel() {
178        return currentAssertionLevel;
179    }
180
181    export function setAssertionLevel(level: AssertionLevel) {
182        const prevAssertionLevel = currentAssertionLevel;
183        currentAssertionLevel = level;
184
185        if (level > prevAssertionLevel) {
186            // restore assertion functions for the current assertion level (see `shouldAssertFunction`).
187            for (const key of getOwnKeys(assertionCache) as AssertionKeys[]) {
188                const cachedFunc = assertionCache[key];
189                if (cachedFunc !== undefined && Debug[key] !== cachedFunc.assertion && level >= cachedFunc.level) {
190                    (Debug as any)[key] = cachedFunc;
191                    assertionCache[key] = undefined;
192                }
193            }
194        }
195    }
196
197    export function shouldAssert(level: AssertionLevel): boolean {
198        return currentAssertionLevel >= level;
199    }
200
201    /**
202     * Tests whether an assertion function should be executed. If it shouldn't, it is cached and replaced with `ts.noop`.
203     * Replaced assertion functions are restored when `Debug.setAssertionLevel` is set to a high enough level.
204     * @param level The minimum assertion level required.
205     * @param name The name of the current assertion function.
206     */
207    function shouldAssertFunction<K extends AssertionKeys>(level: AssertionLevel, name: K): boolean {
208        if (!shouldAssert(level)) {
209            assertionCache[name] = { level, assertion: Debug[name] };
210            (Debug as any)[name] = noop;
211            return false;
212        }
213        return true;
214    }
215
216    export function fail(message?: string, stackCrawlMark?: AnyFunction): never {
217        debugger;
218        const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
219        if ((Error as any).captureStackTrace) {
220            (Error as any).captureStackTrace(e, stackCrawlMark || fail);
221        }
222        throw e;
223    }
224
225    export function failBadSyntaxKind(node: Node, message?: string, stackCrawlMark?: AnyFunction): never {
226        return fail(
227            `${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`,
228            stackCrawlMark || failBadSyntaxKind);
229    }
230
231    export function assert(expression: unknown, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): asserts expression {
232        if (!expression) {
233            message = message ? `False expression: ${message}` : "False expression.";
234            if (verboseDebugInfo) {
235                message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
236            }
237            fail(message, stackCrawlMark || assert);
238        }
239    }
240
241    export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string, stackCrawlMark?: AnyFunction): void {
242        if (a !== b) {
243            const message = msg ? msg2 ? `${msg} ${msg2}` : msg : "";
244            fail(`Expected ${a} === ${b}. ${message}`, stackCrawlMark || assertEqual);
245        }
246    }
247
248    export function assertLessThan(a: number, b: number, msg?: string, stackCrawlMark?: AnyFunction): void {
249        if (a >= b) {
250            fail(`Expected ${a} < ${b}. ${msg || ""}`, stackCrawlMark || assertLessThan);
251        }
252    }
253
254    export function assertLessThanOrEqual(a: number, b: number, stackCrawlMark?: AnyFunction): void {
255        if (a > b) {
256            fail(`Expected ${a} <= ${b}`, stackCrawlMark || assertLessThanOrEqual);
257        }
258    }
259
260    export function assertGreaterThanOrEqual(a: number, b: number, stackCrawlMark?: AnyFunction): void {
261        if (a < b) {
262            fail(`Expected ${a} >= ${b}`, stackCrawlMark || assertGreaterThanOrEqual);
263        }
264    }
265
266    export function assertIsDefined<T>(value: T, message?: string, stackCrawlMark?: AnyFunction): asserts value is NonNullable<T> {
267        // eslint-disable-next-line no-null/no-null
268        if (value === undefined || value === null) {
269            fail(message, stackCrawlMark || assertIsDefined);
270        }
271    }
272
273    export function checkDefined<T>(value: T | null | undefined, message?: string, stackCrawlMark?: AnyFunction): T {
274        assertIsDefined(value, message, stackCrawlMark || checkDefined);
275        return value;
276    }
277
278    export function assertEachIsDefined<T extends Node>(value: NodeArray<T>, message?: string, stackCrawlMark?: AnyFunction): asserts value is NodeArray<T>;
279    export function assertEachIsDefined<T>(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction): asserts value is readonly NonNullable<T>[];
280    export function assertEachIsDefined<T>(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction) {
281        for (const v of value) {
282            assertIsDefined(v, message, stackCrawlMark || assertEachIsDefined);
283        }
284    }
285
286    export function checkEachDefined<T, A extends readonly T[]>(value: A, message?: string, stackCrawlMark?: AnyFunction): A {
287        assertEachIsDefined(value, message, stackCrawlMark || checkEachDefined);
288        return value;
289    }
290
291    export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never {
292        const detail = typeof member === "object" && hasProperty(member, "kind") && hasProperty(member, "pos") ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member);
293        return fail(`${message} ${detail}`, stackCrawlMark || assertNever);
294    }
295
296    export function assertEachNode<T extends Node, U extends T>(nodes: NodeArray<T>, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is NodeArray<U>;
297    export function assertEachNode<T extends Node, U extends T>(nodes: readonly T[], test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is readonly U[];
298    export function assertEachNode<T extends Node, U extends T>(nodes: NodeArray<T> | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is NodeArray<U> | undefined;
299    export function assertEachNode<T extends Node, U extends T>(nodes: readonly T[] | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is readonly U[] | undefined;
300    export function assertEachNode(nodes: readonly Node[], test: (node: Node) => boolean, message?: string, stackCrawlMark?: AnyFunction): void;
301    export function assertEachNode(nodes: readonly Node[] | undefined, test: (node: Node) => boolean, message?: string, stackCrawlMark?: AnyFunction) {
302        if (shouldAssertFunction(AssertionLevel.Normal, "assertEachNode")) {
303            assert(
304                test === undefined || every(nodes, test),
305                message || "Unexpected node.",
306                () => `Node array did not pass test '${getFunctionName(test)}'.`,
307                stackCrawlMark || assertEachNode);
308        }
309    }
310
311    export function assertNode<T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U;
312    export function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void;
313    export function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) {
314        if (shouldAssertFunction(AssertionLevel.Normal, "assertNode")) {
315            assert(
316                node !== undefined && (test === undefined || test(node)),
317                message || "Unexpected node.",
318                () => `Node ${formatSyntaxKind(node?.kind)} did not pass test '${getFunctionName(test!)}'.`,
319                stackCrawlMark || assertNode);
320        }
321    }
322
323    export function assertNotNode<T extends Node, U extends T>(node: T | undefined, test: (node: Node) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is Exclude<T, U>;
324    export function assertNotNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void;
325    export function assertNotNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) {
326        if (shouldAssertFunction(AssertionLevel.Normal, "assertNotNode")) {
327            assert(
328                node === undefined || test === undefined || !test(node),
329                message || "Unexpected node.",
330                () => `Node ${formatSyntaxKind(node!.kind)} should not have passed test '${getFunctionName(test!)}'.`,
331                stackCrawlMark || assertNotNode);
332        }
333    }
334
335    export function assertOptionalNode<T extends Node, U extends T>(node: T, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U;
336    export function assertOptionalNode<T extends Node, U extends T>(node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U | undefined;
337    export function assertOptionalNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void;
338    export function assertOptionalNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) {
339        if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalNode")) {
340            assert(
341                test === undefined || node === undefined || test(node),
342                message || "Unexpected node.",
343                () => `Node ${formatSyntaxKind(node?.kind)} did not pass test '${getFunctionName(test!)}'.`,
344                stackCrawlMark || assertOptionalNode);
345        }
346    }
347
348    export function assertOptionalToken<T extends Node, K extends SyntaxKind>(node: T, kind: K, message?: string, stackCrawlMark?: AnyFunction): asserts node is Extract<T, { readonly kind: K }>;
349    export function assertOptionalToken<T extends Node, K extends SyntaxKind>(node: T | undefined, kind: K, message?: string, stackCrawlMark?: AnyFunction): asserts node is Extract<T, { readonly kind: K }> | undefined;
350    export function assertOptionalToken(node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction): void;
351    export function assertOptionalToken(node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction) {
352        if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalToken")) {
353            assert(
354                kind === undefined || node === undefined || node.kind === kind,
355                message || "Unexpected node.",
356                () => `Node ${formatSyntaxKind(node?.kind)} was not a '${formatSyntaxKind(kind)}' token.`,
357                stackCrawlMark || assertOptionalToken);
358        }
359    }
360
361    export function assertMissingNode(node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction): asserts node is undefined;
362    export function assertMissingNode(node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction) {
363        if (shouldAssertFunction(AssertionLevel.Normal, "assertMissingNode")) {
364            assert(
365                node === undefined,
366                message || "Unexpected node.",
367                () => `Node ${formatSyntaxKind(node!.kind)} was unexpected'.`,
368                stackCrawlMark || assertMissingNode);
369        }
370    }
371
372    /**
373     * Asserts a value has the specified type in typespace only (does not perform a runtime assertion).
374     * This is useful in cases where we switch on `node.kind` and can be reasonably sure the type is accurate, and
375     * as a result can reduce the number of unnecessary casts.
376     */
377    export function type<T>(value: unknown): asserts value is T;
378    export function type(_value: unknown) { }
379
380    export function getFunctionName(func: AnyFunction) {
381        if (typeof func !== "function") {
382            return "";
383        }
384        else if (hasProperty(func, "name")) {
385            return (func as any).name;
386        }
387        else {
388            const text = Function.prototype.toString.call(func);
389            const match = /^function\s+([\w\$]+)\s*\(/.exec(text);
390            return match ? match[1] : "";
391        }
392    }
393
394    export function formatSymbol(symbol: Symbol): string {
395        return `{ name: ${unescapeLeadingUnderscores(symbol.escapedName)}; flags: ${formatSymbolFlags(symbol.flags)}; declarations: ${map(symbol.declarations, node => formatSyntaxKind(node.kind))} }`;
396    }
397
398    /**
399     * Formats an enum value as a string for debugging and debug assertions.
400     */
401    export function formatEnum(value = 0, enumObject: any, isFlags?: boolean) {
402        const members = getEnumMembers(enumObject);
403        if (value === 0) {
404            return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0";
405        }
406        if (isFlags) {
407            const result: string[] = [];
408            let remainingFlags = value;
409            for (const [enumValue, enumName] of members) {
410                if (enumValue > value) {
411                    break;
412                }
413                if (enumValue !== 0 && enumValue & value) {
414                    result.push(enumName);
415                    remainingFlags &= ~enumValue;
416                }
417            }
418            if (remainingFlags === 0) {
419                return result.join("|");
420            }
421        }
422        else {
423            for (const [enumValue, enumName] of members) {
424                if (enumValue === value) {
425                    return enumName;
426                }
427            }
428        }
429        return value.toString();
430    }
431
432    const enumMemberCache = new Map<any, SortedReadonlyArray<[number, string]>>();
433
434    function getEnumMembers(enumObject: any) {
435        // Assuming enum objects do not change at runtime, we can cache the enum members list
436        // to reuse later. This saves us from reconstructing this each and every time we call
437        // a formatting function (which can be expensive for large enums like SyntaxKind).
438        const existing = enumMemberCache.get(enumObject);
439        if (existing) {
440            return existing;
441        }
442
443        const result: [number, string][] = [];
444        for (const name in enumObject) {
445            const value = enumObject[name];
446            if (typeof value === "number") {
447                result.push([value, name]);
448            }
449        }
450
451        const sorted = stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0]));
452        enumMemberCache.set(enumObject, sorted);
453        return sorted;
454    }
455
456    export function formatSyntaxKind(kind: SyntaxKind | undefined): string {
457        return formatEnum(kind, (ts as any).SyntaxKind, /*isFlags*/ false);
458    }
459
460    export function formatSnippetKind(kind: SnippetKind | undefined): string {
461        return formatEnum(kind, (ts as any).SnippetKind, /*isFlags*/ false);
462    }
463
464    export function formatNodeFlags(flags: NodeFlags | undefined): string {
465        return formatEnum(flags, (ts as any).NodeFlags, /*isFlags*/ true);
466    }
467
468    export function formatModifierFlags(flags: ModifierFlags | undefined): string {
469        return formatEnum(flags, (ts as any).ModifierFlags, /*isFlags*/ true);
470    }
471
472    export function formatTransformFlags(flags: TransformFlags | undefined): string {
473        return formatEnum(flags, (ts as any).TransformFlags, /*isFlags*/ true);
474    }
475
476    export function formatEmitFlags(flags: EmitFlags | undefined): string {
477        return formatEnum(flags, (ts as any).EmitFlags, /*isFlags*/ true);
478    }
479
480    export function formatSymbolFlags(flags: SymbolFlags | undefined): string {
481        return formatEnum(flags, (ts as any).SymbolFlags, /*isFlags*/ true);
482    }
483
484    export function formatTypeFlags(flags: TypeFlags | undefined): string {
485        return formatEnum(flags, (ts as any).TypeFlags, /*isFlags*/ true);
486    }
487
488    export function formatSignatureFlags(flags: SignatureFlags | undefined): string {
489        return formatEnum(flags, (ts as any).SignatureFlags, /*isFlags*/ true);
490    }
491
492    export function formatObjectFlags(flags: ObjectFlags | undefined): string {
493        return formatEnum(flags, (ts as any).ObjectFlags, /*isFlags*/ true);
494    }
495
496    export function formatFlowFlags(flags: FlowFlags | undefined): string {
497        return formatEnum(flags, (ts as any).FlowFlags, /*isFlags*/ true);
498    }
499
500    export function formatRelationComparisonResult(result: RelationComparisonResult | undefined): string {
501        return formatEnum(result, (ts as any).RelationComparisonResult, /*isFlags*/ true);
502    }
503
504    export function formatCheckMode(mode: CheckMode | undefined): string {
505        return formatEnum(mode, (ts as any).CheckMode, /*isFlags*/ true);
506    }
507
508    export function formatSignatureCheckMode(mode: SignatureCheckMode | undefined): string {
509        return formatEnum(mode, (ts as any).SignatureCheckMode, /*isFlags*/ true);
510    }
511
512    export function formatTypeFacts(facts: TypeFacts | undefined): string {
513        return formatEnum(facts, (ts as any).TypeFacts, /*isFlags*/ true);
514    }
515
516    let isDebugInfoEnabled = false;
517
518    let flowNodeProto: FlowNodeBase | undefined;
519
520    function attachFlowNodeDebugInfoWorker(flowNode: FlowNodeBase) {
521        if (!("__debugFlowFlags" in flowNode)) { // eslint-disable-line local/no-in-operator
522            Object.defineProperties(flowNode, {
523                // for use with vscode-js-debug's new customDescriptionGenerator in launch.json
524                __tsDebuggerDisplay: {
525                    value(this: FlowNodeBase) {
526                        const flowHeader =
527                            this.flags & FlowFlags.Start ? "FlowStart" :
528                            this.flags & FlowFlags.BranchLabel ? "FlowBranchLabel" :
529                            this.flags & FlowFlags.LoopLabel ? "FlowLoopLabel" :
530                            this.flags & FlowFlags.Assignment ? "FlowAssignment" :
531                            this.flags & FlowFlags.TrueCondition ? "FlowTrueCondition" :
532                            this.flags & FlowFlags.FalseCondition ? "FlowFalseCondition" :
533                            this.flags & FlowFlags.SwitchClause ? "FlowSwitchClause" :
534                            this.flags & FlowFlags.ArrayMutation ? "FlowArrayMutation" :
535                            this.flags & FlowFlags.Call ? "FlowCall" :
536                            this.flags & FlowFlags.ReduceLabel ? "FlowReduceLabel" :
537                            this.flags & FlowFlags.Unreachable ? "FlowUnreachable" :
538                            "UnknownFlow";
539                        const remainingFlags = this.flags & ~(FlowFlags.Referenced - 1);
540                        return `${flowHeader}${remainingFlags ? ` (${formatFlowFlags(remainingFlags)})`: ""}`;
541                    }
542                },
543                __debugFlowFlags: { get(this: FlowNodeBase) { return formatEnum(this.flags, (ts as any).FlowFlags, /*isFlags*/ true); } },
544                __debugToString: { value(this: FlowNodeBase) { return formatControlFlowGraph(this); } }
545            });
546        }
547    }
548
549    export function attachFlowNodeDebugInfo(flowNode: FlowNodeBase) {
550        if (isDebugInfoEnabled) {
551            if (typeof Object.setPrototypeOf === "function") {
552                // if we're in es2015, attach the method to a shared prototype for `FlowNode`
553                // so the method doesn't show up in the watch window.
554                if (!flowNodeProto) {
555                    flowNodeProto = Object.create(Object.prototype) as FlowNodeBase;
556                    attachFlowNodeDebugInfoWorker(flowNodeProto);
557                }
558                Object.setPrototypeOf(flowNode, flowNodeProto);
559            }
560            else {
561                // not running in an es2015 environment, attach the method directly.
562                attachFlowNodeDebugInfoWorker(flowNode);
563            }
564        }
565    }
566
567    let nodeArrayProto: NodeArray<Node> | undefined;
568
569    function attachNodeArrayDebugInfoWorker(array: NodeArray<Node>) {
570        if (!("__tsDebuggerDisplay" in array)) { // eslint-disable-line local/no-in-operator
571            Object.defineProperties(array, {
572                __tsDebuggerDisplay: {
573                    value(this: NodeArray<Node>, defaultValue: string) {
574                        // An `Array` with extra properties is rendered as `[A, B, prop1: 1, prop2: 2]`. Most of
575                        // these aren't immediately useful so we trim off the `prop1: ..., prop2: ...` part from the
576                        // formatted string.
577                        // This regex can trigger slow backtracking because of overlapping potential captures.
578                        // We don't care, this is debug code that's only enabled with a debugger attached -
579                        // we're just taking note of it for anyone checking regex performance in the future.
580                        defaultValue = String(defaultValue).replace(/(?:,[\s\w\d_]+:[^,]+)+\]$/, "]");
581                        return `NodeArray ${defaultValue}`;
582                    }
583                }
584            });
585        }
586    }
587
588    export function attachNodeArrayDebugInfo(array: NodeArray<Node>) {
589        if (isDebugInfoEnabled) {
590            if (typeof Object.setPrototypeOf === "function") {
591                // if we're in es2015, attach the method to a shared prototype for `NodeArray`
592                // so the method doesn't show up in the watch window.
593                if (!nodeArrayProto) {
594                    nodeArrayProto = Object.create(Array.prototype) as NodeArray<Node>;
595                    attachNodeArrayDebugInfoWorker(nodeArrayProto);
596                }
597                Object.setPrototypeOf(array, nodeArrayProto);
598            }
599            else {
600                // not running in an es2015 environment, attach the method directly.
601                attachNodeArrayDebugInfoWorker(array);
602            }
603        }
604    }
605
606    /**
607     * Injects debug information into frequently used types.
608     */
609    export function enableDebugInfo() {
610        if (isDebugInfoEnabled) return;
611
612        // avoid recomputing
613        let weakTypeTextMap: WeakMap<Type, string> | undefined;
614        let weakNodeTextMap: WeakMap<Node, string> | undefined;
615
616        function getWeakTypeTextMap() {
617            if (weakTypeTextMap === undefined) {
618                if (typeof WeakMap === "function") weakTypeTextMap = new WeakMap();
619            }
620            return weakTypeTextMap;
621        }
622
623        function getWeakNodeTextMap() {
624            if (weakNodeTextMap === undefined) {
625                if (typeof WeakMap === "function") weakNodeTextMap = new WeakMap();
626            }
627            return weakNodeTextMap;
628        }
629
630
631        // Add additional properties in debug mode to assist with debugging.
632        Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, {
633            // for use with vscode-js-debug's new customDescriptionGenerator in launch.json
634            __tsDebuggerDisplay: {
635                value(this: Symbol) {
636                    const symbolHeader =
637                        this.flags & SymbolFlags.Transient ? "TransientSymbol" :
638                        "Symbol";
639                    const remainingSymbolFlags = this.flags & ~SymbolFlags.Transient;
640                    return `${symbolHeader} '${symbolName(this)}'${remainingSymbolFlags ? ` (${formatSymbolFlags(remainingSymbolFlags)})` : ""}`;
641                }
642            },
643            __debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } }
644        });
645
646        Object.defineProperties(objectAllocator.getTypeConstructor().prototype, {
647            // for use with vscode-js-debug's new customDescriptionGenerator in launch.json
648            __tsDebuggerDisplay: {
649                value(this: Type) {
650                    const typeHeader =
651                        this.flags & TypeFlags.Nullable ? "NullableType" :
652                        this.flags & TypeFlags.StringOrNumberLiteral ? `LiteralType ${JSON.stringify((this as LiteralType).value)}` :
653                        this.flags & TypeFlags.BigIntLiteral ? `LiteralType ${(this as BigIntLiteralType).value.negative ? "-" : ""}${(this as BigIntLiteralType).value.base10Value}n` :
654                        this.flags & TypeFlags.UniqueESSymbol ? "UniqueESSymbolType" :
655                        this.flags & TypeFlags.Enum ? "EnumType" :
656                        this.flags & TypeFlags.Intrinsic ? `IntrinsicType ${(this as IntrinsicType).intrinsicName}` :
657                        this.flags & TypeFlags.Union ? "UnionType" :
658                        this.flags & TypeFlags.Intersection ? "IntersectionType" :
659                        this.flags & TypeFlags.Index ? "IndexType" :
660                        this.flags & TypeFlags.IndexedAccess ? "IndexedAccessType" :
661                        this.flags & TypeFlags.Conditional ? "ConditionalType" :
662                        this.flags & TypeFlags.Substitution ? "SubstitutionType" :
663                        this.flags & TypeFlags.TypeParameter ? "TypeParameter" :
664                        this.flags & TypeFlags.Object ?
665                            (this as ObjectType).objectFlags & ObjectFlags.ClassOrInterface ? "InterfaceType" :
666                            (this as ObjectType).objectFlags & ObjectFlags.Reference ? "TypeReference" :
667                            (this as ObjectType).objectFlags & ObjectFlags.Tuple ? "TupleType" :
668                            (this as ObjectType).objectFlags & ObjectFlags.Anonymous ? "AnonymousType" :
669                            (this as ObjectType).objectFlags & ObjectFlags.Mapped ? "MappedType" :
670                            (this as ObjectType).objectFlags & ObjectFlags.ReverseMapped ? "ReverseMappedType" :
671                            (this as ObjectType).objectFlags & ObjectFlags.EvolvingArray ? "EvolvingArrayType" :
672                            "ObjectType" :
673                        "Type";
674                    const remainingObjectFlags = this.flags & TypeFlags.Object ? (this as ObjectType).objectFlags & ~ObjectFlags.ObjectTypeKindMask : 0;
675                    return `${typeHeader}${this.symbol ? ` '${symbolName(this.symbol)}'` : ""}${remainingObjectFlags ? ` (${formatObjectFlags(remainingObjectFlags)})` : ""}`;
676                }
677            },
678            __debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } },
679            __debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((this as ObjectType).objectFlags) : ""; } },
680            __debugTypeToString: {
681                value(this: Type) {
682                    // avoid recomputing
683                    const map = getWeakTypeTextMap();
684                    let text = map?.get(this);
685                    if (text === undefined) {
686                        text = this.checker.typeToString(this);
687                        map?.set(this, text);
688                    }
689                    return text;
690                }
691            },
692        });
693
694        Object.defineProperties(objectAllocator.getSignatureConstructor().prototype, {
695            __debugFlags: { get(this: Signature) { return formatSignatureFlags(this.flags); } },
696            __debugSignatureToString: { value(this: Signature) { return this.checker?.signatureToString(this); } }
697        });
698
699        const nodeConstructors = [
700            objectAllocator.getNodeConstructor(),
701            objectAllocator.getIdentifierConstructor(),
702            objectAllocator.getTokenConstructor(),
703            objectAllocator.getSourceFileConstructor()
704        ];
705
706        for (const ctor of nodeConstructors) {
707            if (!hasProperty(ctor.prototype, "__debugKind")) {
708                Object.defineProperties(ctor.prototype, {
709                    // for use with vscode-js-debug's new customDescriptionGenerator in launch.json
710                    __tsDebuggerDisplay: {
711                        value(this: Node) {
712                            const nodeHeader =
713                                isGeneratedIdentifier(this) ? "GeneratedIdentifier" :
714                                isIdentifier(this) ? `Identifier '${idText(this)}'` :
715                                isPrivateIdentifier(this) ? `PrivateIdentifier '${idText(this)}'` :
716                                isStringLiteral(this) ? `StringLiteral ${JSON.stringify(this.text.length < 10 ? this.text : this.text.slice(10) + "...")}` :
717                                isNumericLiteral(this) ? `NumericLiteral ${this.text}` :
718                                isBigIntLiteral(this) ? `BigIntLiteral ${this.text}n` :
719                                isTypeParameterDeclaration(this) ? "TypeParameterDeclaration" :
720                                isParameter(this) ? "ParameterDeclaration" :
721                                isConstructorDeclaration(this) ? "ConstructorDeclaration" :
722                                isGetAccessorDeclaration(this) ? "GetAccessorDeclaration" :
723                                isSetAccessorDeclaration(this) ? "SetAccessorDeclaration" :
724                                isCallSignatureDeclaration(this) ? "CallSignatureDeclaration" :
725                                isConstructSignatureDeclaration(this) ? "ConstructSignatureDeclaration" :
726                                isIndexSignatureDeclaration(this) ? "IndexSignatureDeclaration" :
727                                isTypePredicateNode(this) ? "TypePredicateNode" :
728                                isTypeReferenceNode(this) ? "TypeReferenceNode" :
729                                isFunctionTypeNode(this) ? "FunctionTypeNode" :
730                                isConstructorTypeNode(this) ? "ConstructorTypeNode" :
731                                isTypeQueryNode(this) ? "TypeQueryNode" :
732                                isTypeLiteralNode(this) ? "TypeLiteralNode" :
733                                isArrayTypeNode(this) ? "ArrayTypeNode" :
734                                isTupleTypeNode(this) ? "TupleTypeNode" :
735                                isOptionalTypeNode(this) ? "OptionalTypeNode" :
736                                isRestTypeNode(this) ? "RestTypeNode" :
737                                isUnionTypeNode(this) ? "UnionTypeNode" :
738                                isIntersectionTypeNode(this) ? "IntersectionTypeNode" :
739                                isConditionalTypeNode(this) ? "ConditionalTypeNode" :
740                                isInferTypeNode(this) ? "InferTypeNode" :
741                                isParenthesizedTypeNode(this) ? "ParenthesizedTypeNode" :
742                                isThisTypeNode(this) ? "ThisTypeNode" :
743                                isTypeOperatorNode(this) ? "TypeOperatorNode" :
744                                isIndexedAccessTypeNode(this) ? "IndexedAccessTypeNode" :
745                                isMappedTypeNode(this) ? "MappedTypeNode" :
746                                isLiteralTypeNode(this) ? "LiteralTypeNode" :
747                                isNamedTupleMember(this) ? "NamedTupleMember" :
748                                isImportTypeNode(this) ? "ImportTypeNode" :
749                                formatSyntaxKind(this.kind);
750                            return `${nodeHeader}${this.flags ? ` (${formatNodeFlags(this.flags)})` : ""}`;
751                        }
752                    },
753                    __debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } },
754                    __debugNodeFlags: { get(this: Node) { return formatNodeFlags(this.flags); } },
755                    __debugModifierFlags: { get(this: Node) { return formatModifierFlags(getEffectiveModifierFlagsNoCache(this)); } },
756                    __debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } },
757                    __debugIsParseTreeNode: { get(this: Node) { return isParseTreeNode(this); } },
758                    __debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } },
759                    __debugGetText: {
760                        value(this: Node, includeTrivia?: boolean) {
761                            if (nodeIsSynthesized(this)) return "";
762                            // avoid recomputing
763                            const map = getWeakNodeTextMap();
764                            let text = map?.get(this);
765                            if (text === undefined) {
766                                const parseNode = getParseTreeNode(this);
767                                const sourceFile = parseNode && getSourceFileOfNode(parseNode);
768                                text = sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : "";
769                                map?.set(this, text);
770                            }
771                            return text;
772                        }
773                    }
774                });
775            }
776        }
777
778        isDebugInfoEnabled = true;
779    }
780
781    function formatDeprecationMessage(name: string, error: boolean | undefined, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) {
782        let deprecationMessage = error ? "DeprecationError: " : "DeprecationWarning: ";
783        deprecationMessage += `'${name}' `;
784        deprecationMessage += since ? `has been deprecated since v${since}` : "is deprecated";
785        deprecationMessage += error ? " and can no longer be used." : errorAfter ? ` and will no longer be usable after v${errorAfter}.` : ".";
786        deprecationMessage += message ? ` ${formatStringFromArgs(message, [name], 0)}` : "";
787        return deprecationMessage;
788    }
789
790    function createErrorDeprecation(name: string, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) {
791        const deprecationMessage = formatDeprecationMessage(name, /*error*/ true, errorAfter, since, message);
792        return () => {
793            throw new TypeError(deprecationMessage);
794        };
795    }
796
797    function createWarningDeprecation(name: string, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) {
798        let hasWrittenDeprecation = false;
799        return () => {
800            if (enableDeprecationWarnings && !hasWrittenDeprecation) {
801                log.warn(formatDeprecationMessage(name, /*error*/ false, errorAfter, since, message));
802                hasWrittenDeprecation = true;
803            }
804        };
805    }
806
807    export function createDeprecation(name: string, options: DeprecationOptions & { error: true }): () => never;
808    export function createDeprecation(name: string, options?: DeprecationOptions): () => void;
809    export function createDeprecation(name: string, options: DeprecationOptions = {}) {
810        const version = typeof options.typeScriptVersion === "string" ? new Version(options.typeScriptVersion) : options.typeScriptVersion ?? getTypeScriptVersion();
811        const errorAfter = typeof options.errorAfter === "string" ? new Version(options.errorAfter) : options.errorAfter;
812        const warnAfter = typeof options.warnAfter === "string" ? new Version(options.warnAfter) : options.warnAfter;
813        const since = typeof options.since === "string" ? new Version(options.since) : options.since ?? warnAfter;
814        const error = options.error || errorAfter && version.compareTo(errorAfter) <= 0;
815        const warn = !warnAfter || version.compareTo(warnAfter) >= 0;
816        return error ? createErrorDeprecation(name, errorAfter, since, options.message) :
817            warn ? createWarningDeprecation(name, errorAfter, since, options.message) :
818            noop;
819    }
820
821    function wrapFunction<F extends (...args: any[]) => any>(deprecation: () => void, func: F): F {
822        return function (this: unknown) {
823            deprecation();
824            return func.apply(this, arguments);
825        } as F;
826    }
827
828    export function deprecate<F extends (...args: any[]) => any>(func: F, options?: DeprecationOptions): F {
829        const deprecation = createDeprecation(options?.name ?? getFunctionName(func), options);
830        return wrapFunction(deprecation, func);
831    }
832
833    export function formatVariance(varianceFlags: VarianceFlags) {
834        const variance = varianceFlags & VarianceFlags.VarianceMask;
835        let result =
836            variance === VarianceFlags.Invariant ? "in out" :
837            variance === VarianceFlags.Bivariant ? "[bivariant]" :
838            variance === VarianceFlags.Contravariant ? "in" :
839            variance === VarianceFlags.Covariant ? "out" :
840            variance === VarianceFlags.Independent ? "[independent]" : "";
841        if (varianceFlags & VarianceFlags.Unmeasurable) {
842            result += " (unmeasurable)";
843        }
844        else if (varianceFlags & VarianceFlags.Unreliable) {
845            result += " (unreliable)";
846        }
847        return result;
848    }
849
850    export type DebugType = Type & { __debugTypeToString(): string }; // eslint-disable-line @typescript-eslint/naming-convention
851    export class DebugTypeMapper {
852        declare kind: TypeMapKind;
853        __debugToString(): string { // eslint-disable-line @typescript-eslint/naming-convention
854            type<TypeMapper>(this);
855            switch (this.kind) {
856                case TypeMapKind.Function: return this.debugInfo?.() || "(function mapper)";
857                case TypeMapKind.Simple: return `${(this.source as DebugType).__debugTypeToString()} -> ${(this.target as DebugType).__debugTypeToString()}`;
858                case TypeMapKind.Array: return zipWith<DebugType, DebugType | string, unknown>(
859                    this.sources as readonly DebugType[],
860                    this.targets as readonly DebugType[] || map(this.sources, () => "any"),
861                    (s, t) => `${s.__debugTypeToString()} -> ${typeof t === "string" ? t : t.__debugTypeToString()}`).join(", ");
862                case TypeMapKind.Deferred: return zipWith(
863                    this.sources,
864                    this.targets,
865                    (s, t) => `${(s as DebugType).__debugTypeToString()} -> ${(t() as DebugType).__debugTypeToString()}`).join(", ");
866                case TypeMapKind.Merged:
867                case TypeMapKind.Composite: return `m1: ${(this.mapper1 as unknown as DebugTypeMapper).__debugToString().split("\n").join("\n    ")}
868m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n").join("\n    ")}`;
869                default: return assertNever(this);
870            }
871        }
872    }
873
874    export function attachDebugPrototypeIfDebug(mapper: TypeMapper): TypeMapper {
875        if (isDebugging) {
876            return Object.setPrototypeOf(mapper, DebugTypeMapper.prototype);
877        }
878        return mapper;
879    }
880
881    export function printControlFlowGraph(flowNode: FlowNode) {
882        return console.log(formatControlFlowGraph(flowNode));
883    }
884
885    export function formatControlFlowGraph(flowNode: FlowNode) {
886        let nextDebugFlowId = -1;
887
888        function getDebugFlowNodeId(f: FlowNode) {
889            if (!f.id) {
890                f.id = nextDebugFlowId;
891                nextDebugFlowId--;
892            }
893            return f.id;
894        }
895
896        const enum BoxCharacter {
897            lr = "─",
898            ud = "│",
899            dr = "╭",
900            dl = "╮",
901            ul = "╯",
902            ur = "╰",
903            udr = "├",
904            udl = "┤",
905            dlr = "┬",
906            ulr = "┴",
907            udlr = "╫",
908        }
909
910        const enum Connection {
911            None = 0,
912            Up = 1 << 0,
913            Down = 1 << 1,
914            Left = 1 << 2,
915            Right = 1 << 3,
916
917            UpDown = Up | Down,
918            LeftRight = Left | Right,
919            UpLeft = Up | Left,
920            UpRight = Up | Right,
921            DownLeft = Down | Left,
922            DownRight = Down | Right,
923            UpDownLeft = UpDown | Left,
924            UpDownRight = UpDown | Right,
925            UpLeftRight = Up | LeftRight,
926            DownLeftRight = Down | LeftRight,
927            UpDownLeftRight = UpDown | LeftRight,
928
929            NoChildren = 1 << 4,
930        }
931
932        interface FlowGraphNode {
933            id: number;
934            flowNode: FlowNode;
935            edges: FlowGraphEdge[];
936            text: string;
937            lane: number;
938            endLane: number;
939            level: number;
940            circular: boolean | "circularity";
941        }
942
943        interface FlowGraphEdge {
944            source: FlowGraphNode;
945            target: FlowGraphNode;
946        }
947
948        const hasAntecedentFlags =
949            FlowFlags.Assignment |
950            FlowFlags.Condition |
951            FlowFlags.SwitchClause |
952            FlowFlags.ArrayMutation |
953            FlowFlags.Call |
954            FlowFlags.ReduceLabel;
955
956        const hasNodeFlags =
957            FlowFlags.Start |
958            FlowFlags.Assignment |
959            FlowFlags.Call |
960            FlowFlags.Condition |
961            FlowFlags.ArrayMutation;
962
963        const links: Record<number, FlowGraphNode> = Object.create(/*o*/ null); // eslint-disable-line no-null/no-null
964        const nodes: FlowGraphNode[] = [];
965        const edges: FlowGraphEdge[] = [];
966        const root = buildGraphNode(flowNode, new Set());
967        for (const node of nodes) {
968            node.text = renderFlowNode(node.flowNode, node.circular);
969            computeLevel(node);
970        }
971
972        const height = computeHeight(root);
973        const columnWidths = computeColumnWidths(height);
974        computeLanes(root, 0);
975        return renderGraph();
976
977        function isFlowSwitchClause(f: FlowNode): f is FlowSwitchClause {
978            return !!(f.flags & FlowFlags.SwitchClause);
979        }
980
981        function hasAntecedents(f: FlowNode): f is FlowLabel & { antecedents: FlowNode[] } {
982            return !!(f.flags & FlowFlags.Label) && !!(f as FlowLabel).antecedents;
983        }
984
985        function hasAntecedent(f: FlowNode): f is Extract<FlowNode, { antecedent: FlowNode }> {
986            return !!(f.flags & hasAntecedentFlags);
987        }
988
989        function hasNode(f: FlowNode): f is Extract<FlowNode, { node?: Node }> {
990            return !!(f.flags & hasNodeFlags);
991        }
992
993        function getChildren(node: FlowGraphNode) {
994            const children: FlowGraphNode[] = [];
995            for (const edge of node.edges) {
996                if (edge.source === node) {
997                    children.push(edge.target);
998                }
999            }
1000            return children;
1001        }
1002
1003        function getParents(node: FlowGraphNode) {
1004            const parents: FlowGraphNode[] = [];
1005            for (const edge of node.edges) {
1006                if (edge.target === node) {
1007                    parents.push(edge.source);
1008                }
1009            }
1010            return parents;
1011        }
1012
1013        function buildGraphNode(flowNode: FlowNode, seen: Set<FlowNode>): FlowGraphNode {
1014            const id = getDebugFlowNodeId(flowNode);
1015            let graphNode = links[id];
1016            if (graphNode && seen.has(flowNode)) {
1017                graphNode.circular = true;
1018                graphNode = {
1019                    id: -1,
1020                    flowNode,
1021                    edges: [],
1022                    text: "",
1023                    lane: -1,
1024                    endLane: -1,
1025                    level: -1,
1026                    circular: "circularity"
1027                };
1028                nodes.push(graphNode);
1029                return graphNode;
1030            }
1031            seen.add(flowNode);
1032            if (!graphNode) {
1033                links[id] = graphNode = { id, flowNode, edges: [], text: "", lane: -1, endLane: -1, level: -1, circular: false };
1034                nodes.push(graphNode);
1035                if (hasAntecedents(flowNode)) {
1036                    for (const antecedent of flowNode.antecedents) {
1037                        buildGraphEdge(graphNode, antecedent, seen);
1038                    }
1039                }
1040                else if (hasAntecedent(flowNode)) {
1041                    buildGraphEdge(graphNode, flowNode.antecedent, seen);
1042                }
1043            }
1044            seen.delete(flowNode);
1045            return graphNode;
1046        }
1047
1048        function buildGraphEdge(source: FlowGraphNode, antecedent: FlowNode, seen: Set<FlowNode>) {
1049            const target = buildGraphNode(antecedent, seen);
1050            const edge: FlowGraphEdge = { source, target };
1051            edges.push(edge);
1052            source.edges.push(edge);
1053            target.edges.push(edge);
1054        }
1055
1056        function computeLevel(node: FlowGraphNode): number {
1057            if (node.level !== -1) {
1058                return node.level;
1059            }
1060            let level = 0;
1061            for (const parent of getParents(node)) {
1062                level = Math.max(level, computeLevel(parent) + 1);
1063            }
1064            return node.level = level;
1065        }
1066
1067        function computeHeight(node: FlowGraphNode): number {
1068            let height = 0;
1069            for (const child of getChildren(node)) {
1070                height = Math.max(height, computeHeight(child));
1071            }
1072            return height + 1;
1073        }
1074
1075        function computeColumnWidths(height: number) {
1076            const columns: number[] = fill(Array(height), 0);
1077            for (const node of nodes) {
1078                columns[node.level] = Math.max(columns[node.level], node.text.length);
1079            }
1080            return columns;
1081        }
1082
1083        function computeLanes(node: FlowGraphNode, lane: number) {
1084            if (node.lane === -1) {
1085                node.lane = lane;
1086                node.endLane = lane;
1087                const children = getChildren(node);
1088                for (let i = 0; i < children.length; i++) {
1089                    if (i > 0) {
1090                        lane++;
1091                    }
1092                    const child = children[i];
1093                    computeLanes(child, lane);
1094                    if (child.endLane > node.endLane) {
1095                        lane = child.endLane;
1096                    }
1097                }
1098                node.endLane = lane;
1099            }
1100        }
1101
1102        function getHeader(flags: FlowFlags) {
1103            if (flags & FlowFlags.Start) return "Start";
1104            if (flags & FlowFlags.BranchLabel) return "Branch";
1105            if (flags & FlowFlags.LoopLabel) return "Loop";
1106            if (flags & FlowFlags.Assignment) return "Assignment";
1107            if (flags & FlowFlags.TrueCondition) return "True";
1108            if (flags & FlowFlags.FalseCondition) return "False";
1109            if (flags & FlowFlags.SwitchClause) return "SwitchClause";
1110            if (flags & FlowFlags.ArrayMutation) return "ArrayMutation";
1111            if (flags & FlowFlags.Call) return "Call";
1112            if (flags & FlowFlags.ReduceLabel) return "ReduceLabel";
1113            if (flags & FlowFlags.Unreachable) return "Unreachable";
1114            throw new Error();
1115        }
1116
1117        function getNodeText(node: Node) {
1118            const sourceFile = getSourceFileOfNode(node);
1119            return getSourceTextOfNodeFromSourceFile(sourceFile, node, /*includeTrivia*/ false);
1120        }
1121
1122        function renderFlowNode(flowNode: FlowNode, circular: boolean | "circularity") {
1123            let text = getHeader(flowNode.flags);
1124            if (circular) {
1125                text = `${text}#${getDebugFlowNodeId(flowNode)}`;
1126            }
1127            if (hasNode(flowNode)) {
1128                if (flowNode.node) {
1129                    text += ` (${getNodeText(flowNode.node)})`;
1130                }
1131            }
1132            else if (isFlowSwitchClause(flowNode)) {
1133                const clauses: string[] = [];
1134                for (let i = flowNode.clauseStart; i < flowNode.clauseEnd; i++) {
1135                    const clause = flowNode.switchStatement.caseBlock.clauses[i];
1136                    if (isDefaultClause(clause)) {
1137                        clauses.push("default");
1138                    }
1139                    else {
1140                        clauses.push(getNodeText(clause.expression));
1141                    }
1142                }
1143                text += ` (${clauses.join(", ")})`;
1144            }
1145            return circular === "circularity" ? `Circular(${text})` : text;
1146        }
1147
1148        function renderGraph() {
1149            const columnCount = columnWidths.length;
1150            const laneCount = nodes.reduce((x, n) => Math.max(x, n.lane), 0) + 1;
1151            const lanes: string[] = fill(Array(laneCount), "");
1152            const grid: (FlowGraphNode | undefined)[][] = columnWidths.map(() => Array(laneCount));
1153            const connectors: Connection[][] = columnWidths.map(() => fill(Array(laneCount), 0));
1154
1155            // build connectors
1156            for (const node of nodes) {
1157                grid[node.level][node.lane] = node;
1158                const children = getChildren(node);
1159                for (let i = 0; i < children.length; i++) {
1160                    const child = children[i];
1161                    let connector: Connection = Connection.Right;
1162                    if (child.lane === node.lane) connector |= Connection.Left;
1163                    if (i > 0) connector |= Connection.Up;
1164                    if (i < children.length - 1) connector |= Connection.Down;
1165                    connectors[node.level][child.lane] |= connector;
1166                }
1167                if (children.length === 0) {
1168                    connectors[node.level][node.lane] |= Connection.NoChildren;
1169                }
1170                const parents = getParents(node);
1171                for (let i = 0; i < parents.length; i++) {
1172                    const parent = parents[i];
1173                    let connector: Connection = Connection.Left;
1174                    if (i > 0) connector |= Connection.Up;
1175                    if (i < parents.length - 1) connector |= Connection.Down;
1176                    connectors[node.level - 1][parent.lane] |= connector;
1177                }
1178            }
1179
1180            // fill in missing connectors
1181            for (let column = 0; column < columnCount; column++) {
1182                for (let lane = 0; lane < laneCount; lane++) {
1183                    const left = column > 0 ? connectors[column - 1][lane] : 0;
1184                    const above = lane > 0 ? connectors[column][lane - 1] : 0;
1185                    let connector = connectors[column][lane];
1186                    if (!connector) {
1187                        if (left & Connection.Right) connector |= Connection.LeftRight;
1188                        if (above & Connection.Down) connector |= Connection.UpDown;
1189                        connectors[column][lane] = connector;
1190                    }
1191                }
1192            }
1193
1194            for (let column = 0; column < columnCount; column++) {
1195                for (let lane = 0; lane < lanes.length; lane++) {
1196                    const connector = connectors[column][lane];
1197                    const fill = connector & Connection.Left ? BoxCharacter.lr : " ";
1198                    const node = grid[column][lane];
1199                    if (!node) {
1200                        if (column < columnCount - 1) {
1201                            writeLane(lane, repeat(fill, columnWidths[column] + 1));
1202                        }
1203                    }
1204                    else {
1205                        writeLane(lane, node.text);
1206                        if (column < columnCount - 1) {
1207                            writeLane(lane, " ");
1208                            writeLane(lane, repeat(fill, columnWidths[column] - node.text.length));
1209                        }
1210                    }
1211                    writeLane(lane, getBoxCharacter(connector));
1212                    writeLane(lane, connector & Connection.Right && column < columnCount - 1 && !grid[column + 1][lane] ? BoxCharacter.lr : " ");
1213                }
1214            }
1215
1216            return `\n${lanes.join("\n")}\n`;
1217
1218            function writeLane(lane: number, text: string) {
1219                lanes[lane] += text;
1220            }
1221        }
1222
1223        function getBoxCharacter(connector: Connection) {
1224            switch (connector) {
1225                case Connection.UpDown: return BoxCharacter.ud;
1226                case Connection.LeftRight: return BoxCharacter.lr;
1227                case Connection.UpLeft: return BoxCharacter.ul;
1228                case Connection.UpRight: return BoxCharacter.ur;
1229                case Connection.DownLeft: return BoxCharacter.dl;
1230                case Connection.DownRight: return BoxCharacter.dr;
1231                case Connection.UpDownLeft: return BoxCharacter.udl;
1232                case Connection.UpDownRight: return BoxCharacter.udr;
1233                case Connection.UpLeftRight: return BoxCharacter.ulr;
1234                case Connection.DownLeftRight: return BoxCharacter.dlr;
1235                case Connection.UpDownLeftRight: return BoxCharacter.udlr;
1236            }
1237            return " ";
1238        }
1239
1240        function fill<T>(array: T[], value: T) {
1241            if (array.fill) {
1242                array.fill(value);
1243            }
1244            else {
1245                for (let i = 0; i < array.length; i++) {
1246                    array[i] = value;
1247                }
1248            }
1249            return array;
1250        }
1251
1252        function repeat(ch: string, length: number) {
1253            if (ch.repeat) {
1254                return length > 0 ? ch.repeat(length) : "";
1255            }
1256            let s = "";
1257            while (s.length < length) {
1258                s += ch;
1259            }
1260            return s;
1261        }
1262    }
1263}
1264