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

rokucommunity / brighterscript / #15069

09 Jan 2026 02:58PM UTC coverage: 86.959% (-0.01%) from 86.973%
#15069

push

web-flow
Merge a7c0f7b34 into 2ea4d2108

14543 of 17676 branches covered (82.28%)

Branch coverage included in aggregate %.

240 of 319 new or added lines in 14 files covered. (75.24%)

208 existing lines in 11 files now uncovered.

15289 of 16630 relevant lines covered (91.94%)

24236.5 hits per line

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

73.45
/src/types/ReferenceType.ts
1
import type { GetTypeOptions, TypeChainEntry, TypeCircularReferenceInfo, TypeCompatibilityData } from '../interfaces';
2
import type { GetSymbolTypeOptions, SymbolTable, SymbolTableProvider, SymbolTypeGetterProvider } from '../SymbolTable';
3
import type { SymbolTypeFlag } from '../SymbolTypeFlag';
4
import { isAnyReferenceType, isArrayDefaultTypeReferenceType, isArrayType, isBinaryOperatorReferenceType, isComponentType, isDynamicType, isParamTypeFromValueReferenceType, isReferenceType, isTypePropertyReferenceType } from '../astUtils/reflection';
1✔
5
import { BscType } from './BscType';
1✔
6
import { DynamicType } from './DynamicType';
1✔
7
import { BscTypeKind } from './BscTypeKind';
1✔
8
import type { Token } from '../lexer/Token';
9
import { util } from '../util';
1✔
10

11
export type AnyReferenceType = ReferenceType | TypePropertyReferenceType | BinaryOperatorReferenceType | ArrayDefaultTypeReferenceType;
12

13
export function referenceTypeFactory(memberKey: string, fullName, flags: SymbolTypeFlag, tableProvider: SymbolTypeGetterProvider) {
1✔
14
    return new ReferenceType(memberKey, fullName, flags, tableProvider);
125,287✔
15
}
16

17
export class ReferenceType extends BscType {
1✔
18

19
    /**
20
     * ReferenceTypes are used when the actual type may be resolved later from a Symbol table
21
     * @param memberKey which key do we use to look up this type in the given table?
22
     * @param fullName the full/display name for this type
23
     * @param flags is this type available at typetime, runtime, etc.
24
     * @param tableProvider function that returns a SymbolTable that we use for the lookup.
25
     */
26
    constructor(public memberKey: string, public fullName, public flags: SymbolTypeFlag, public tableProvider: SymbolTypeGetterProvider) {
127,094✔
27
        super(memberKey);
127,094✔
28
        // eslint-disable-next-line no-constructor-return
29
        return new Proxy(this, {
127,094✔
30
            get: (target, propName, receiver) => {
31

32
                if (propName === '__reflection') {
363,394✔
33
                    // Cheeky way to get `isReferenceType` reflection to work
34
                    return this.__reflection;
286,155✔
35
                }
36
                if (propName === '__identifier') {
77,239✔
37
                    // Cheeky way to get `isReferenceType` reflection to work
38
                    return this.__identifier;
23✔
39
                }
40
                if (propName === 'fullName') {
77,216✔
41
                    return this.fullName;
1,140✔
42
                }
43
                if (propName === 'isResolvable') {
76,076✔
44
                    return () => {
9,517✔
45
                        let resultSoFar = this.resolve();
9,517✔
46
                        while (resultSoFar && isReferenceType(resultSoFar)) {
9,517✔
47
                            resultSoFar = (resultSoFar as any).getTarget?.();
×
48
                        }
49
                        return !!resultSoFar;
9,517✔
50
                    };
51
                }
52
                if (propName === 'getCircularReferenceInfo') {
66,559✔
53
                    return (stopAtTypes: ReferenceType[] = []) => {
502✔
54
                        return this.getCircularReferenceInfo(stopAtTypes);
502✔
55
                    };
56
                }
57
                if (propName === 'getTarget') {
66,057✔
58
                    return () => {
9,135✔
59
                        return this.resolve();
9,135✔
60
                    };
61
                }
62
                if (propName === 'tableProvider') {
56,922✔
63
                    return this.tableProvider;
62✔
64
                }
65
                if (propName === 'isEqual') {
56,860✔
66
                    //Need to be able to check equality without resolution, because resolution need to check equality
67
                    //To see if you need to make a UnionType
68
                    return (targetType: BscType, data?: TypeCompatibilityData) => {
95✔
69
                        if (!targetType) {
95!
70
                            return false;
×
71
                        }
72
                        const resolvedType = this.resolve();
95✔
73
                        let equal = false;
95✔
74
                        if (resolvedType && !isReferenceType(resolvedType)) {
95✔
75
                            equal = resolvedType.isEqual(targetType, data);
10✔
76
                        } else if (isReferenceType(targetType)) {
85✔
77
                            equal = this.fullName.toLowerCase() === targetType.fullName.toLowerCase() &&
81✔
78
                                (this.tableProvider === targetType.tableProvider ||
79
                                    this.tableProvider().name === targetType.tableProvider().name);
80
                        } else {
81
                            equal = targetType.isEqual(this, data);
4✔
82
                        }
83
                        return equal;
95✔
84
                    };
85
                }
86
                if (propName === 'isTypeCompatible') {
56,765✔
87
                    //Need to be able to check equality without resolution, because resolution need to check equality
88
                    //To see if you need to make a UnionType
89
                    return (targetType: BscType, data?: TypeCompatibilityData) => {
223✔
90
                        if (!targetType) {
223!
91
                            return false;
×
92
                        }
93
                        if (isDynamicType(targetType)) {
223!
94
                            return true;
×
95
                        }
96
                        const resolvedType = this.resolve();
223✔
97
                        if (resolvedType && !isReferenceType(resolvedType)) {
223✔
98
                            return resolvedType.isTypeCompatible(targetType, data);
222✔
99
                        } else if (isReferenceType(targetType)) {
1!
100
                            return this.fullName.toLowerCase() === targetType.fullName.toLowerCase() &&
×
101
                                this.tableProvider === targetType.tableProvider;
102
                        }
103
                        return targetType.isTypeCompatible(this, data);
1✔
104
                    };
105
                }
106

107
                //There may be some need to specifically get members on ReferenceType in the future
108
                // eg: if (Reflect.has(target, name)) {
109
                //   return Reflect.get(target, propName, receiver);
110

111
                let innerType = this.resolve();
56,542✔
112

113
                if (!innerType) {
56,539✔
114
                    // No real BscType found - we may need to handle some specific cases
115

116
                    if (propName === 'getMemberType') {
41,026✔
117
                        // We're looking for a member of a reference type
118
                        // Since we don't know what type this is, yet, return ReferenceType
119
                        return (memberName: string, options: GetTypeOptions) => {
1,426✔
120
                            const resolvedType = this.resolve();
1,426✔
121
                            if (resolvedType) {
1,426!
122
                                return resolvedType.getMemberType(memberName, options);
×
123
                            }
124
                            const refLookUp = `${memberName.toLowerCase()}-${options?.flags}`;
1,426!
125
                            let memberTypeReference = this.memberTypeReferences.get(refLookUp);
1,426✔
126
                            if (memberTypeReference) {
1,426✔
127
                                return memberTypeReference;
93✔
128

129
                            }
130
                            memberTypeReference = new ReferenceType(memberName, this.makeMemberFullName(memberName), options.flags, this.futureMemberTableProvider);
1,333✔
131
                            this.memberTypeReferences.set(refLookUp, memberTypeReference);
1,333✔
132
                            return memberTypeReference;
1,333✔
133
                        };
134
                    } else if (propName === 'getCallFuncType') {
39,600✔
135
                        // We're looking for a callfunc member of a reference type
136
                        // Since we don't know what type this is, yet, return ReferenceType
137
                        return (memberName: string, options: GetTypeOptions) => {
8✔
138
                            const resolvedType = this.resolve();
8✔
139
                            if (isComponentType(resolvedType)) {
8!
140
                                return resolvedType.getCallFuncType(memberName, options);
×
141
                            }
142
                            const refLookUp = `${memberName.toLowerCase()}-${options.flags}-callfunc`;
8✔
143
                            let callFuncMemberTypeReference = this.callFuncMemberTypeReferences.get(refLookUp);
8✔
144
                            if (callFuncMemberTypeReference) {
8!
145
                                return callFuncMemberTypeReference;
×
146

147
                            }
148
                            callFuncMemberTypeReference = new ReferenceType(memberName, this.makeMemberFullName(memberName), options.flags, this.futureCallFuncMemberTableProvider);
8✔
149
                            this.callFuncMemberTypeReferences.set(refLookUp, callFuncMemberTypeReference);
8✔
150
                            return callFuncMemberTypeReference;
8✔
151
                        };
152
                    } else if (propName === 'toString') {
39,592✔
153
                        // This type was never found
154
                        // For diagnostics, we should return the expected name of of the type
155
                        return () => this.fullName;
2,837✔
156
                    } else if (propName === 'toTypeString') {
36,755!
157
                        // For transpilation, we should 'dynamic'
158
                        return () => 'dynamic';
×
159
                    } else if (propName === 'returnType') {
36,755✔
160
                        let propRefType = this.propertyTypeReference.get(propName);
195✔
161
                        if (!propRefType) {
195✔
162
                            propRefType = new TypePropertyReferenceType(this, propName);
62✔
163
                            this.propertyTypeReference.set(propName, propRefType);
62✔
164
                        }
165
                        return propRefType;
195✔
166
                    } else if (propName === 'memberTable') {
36,560✔
167
                        return this.memberTable;
101✔
168
                    } else if (propName === 'getMemberTable') {
36,459✔
169
                        return () => {
32✔
170
                            return this.memberTable;
32✔
171
                        };
172
                    } else if (propName === 'callFuncTable') {
36,427!
173
                        return (this as any).callFuncMemberTable;
×
174
                    } else if (propName === 'getCallFuncTable') {
36,427!
175
                        return () => {
×
176
                            return (this as any).callFuncMemberTable;
×
177
                        };
178
                    } else if (propName === 'isTypeCompatible') {
36,427!
179
                        return (targetType: BscType) => {
×
180
                            return isDynamicType(targetType);
×
181
                        };
182
                    } else if (propName === 'isEqual') {
36,427!
183
                        return (targetType: BscType) => {
×
184
                            if (isReferenceType(targetType)) {
×
185
                                return this.fullName.toLowerCase() === targetType.toString().toLowerCase() &&
×
186
                                    this.tableProvider() === targetType.tableProvider();
187
                            }
188
                            return false;
×
189
                        };
190
                    } else if (propName === 'addBuiltInInterfaces') {
36,427✔
191
                        // this is an unknown type. There is no use in adding built in interfaces
192
                        // return no-op function
193
                        return () => { };
38✔
194
                    } else if (propName === 'hasAddedBuiltInInterfaces') {
36,389!
195
                        // this is an unknown type. There is no use in adding built in interfaces
196
                        return true;
×
197
                    } else if (propName === 'isBuiltIn') {
36,389✔
198
                        return false;
23✔
199
                    }
200
                }
201

202
                if (!innerType) {
51,879✔
203
                    innerType = DynamicType.instance;
36,366✔
204
                }
205
                const result = Reflect.get(innerType, propName, innerType);
51,879✔
206
                return result;
51,879✔
207
            },
208
            set: (target, name, value, receiver) => {
209
                //There may be some need to specifically set members on ReferenceType in the future
210
                // eg: if (Reflect.has(target, name)) {
211
                //   return Reflect.set(target, name, value, receiver);
212

213
                let innerType = this.resolve();
776✔
214

215
                // Look for circular references
216
                if (!innerType || this.referenceChain.has(innerType)) {
776!
217
                    if (name === 'hasAddedBuiltInInterfaces') {
×
218
                        // ignore this
219
                        return true;
×
220
                    }
221
                    console.log(`Proxy set error`, name, value, innerType);
×
222
                    const error = new Error(`Reference Type Proxy set error: ${{ memberKey: memberKey, name: name, value: value }}`);
×
223
                    console.log(error.stack);
×
224
                    return false;
×
225
                }
226
                const result = Reflect.set(innerType, name, value, innerType);
776✔
227
                this.referenceChain.clear();
776✔
228
                return result;
776✔
229
            }
230
        });
231
    }
232

233
    public readonly kind = BscTypeKind.ReferenceType;
127,094✔
234

235
    getTarget() {
236
        return this.resolve();
319✔
237
    }
238

239
    /**
240
     * Resolves the type based on the original name and the table provider
241
     */
242
    private resolve(): BscType {
243
        const symbolTable = this.tableProvider();
98,414✔
244
        if (!symbolTable) {
98,414✔
245
            return;
18✔
246
        }
247
        // Look for circular references
248
        let resolvedType = symbolTable.getSymbolType(this.memberKey, { flags: this.flags, onlyCacheResolvedTypes: true });
98,396✔
249
        if (!resolvedType) {
98,393✔
250
            // could not find this member
251
            return;
62,355✔
252
        }
253

254
        if (isAnyReferenceType(resolvedType)) {
36,038✔
255
            // If this is a referenceType, keep digging down until we have a non reference Type.
256
            while (resolvedType && isAnyReferenceType(resolvedType)) {
3,324✔
257
                if (this.referenceChain.has(resolvedType)) {
3,324✔
258
                    // this is a circular reference
259
                    this.circRefCount++;
2,456✔
260
                }
261
                if (this.circRefCount > 1) {
3,324✔
262
                    //It is possible that we could properly resolve the case that one reference points to itself
263
                    //see test: '[Scope][symbolTable lookups with enhanced typing][finds correct class field type with default value enums are used]
264
                    return;
2,389✔
265
                }
266
                this.referenceChain.add(resolvedType);
935✔
267
                resolvedType = (resolvedType as any)?.getTarget?.();
935!
268
            }
269
            this.tableProvider().setCachedType(this.memberKey, { type: resolvedType }, { flags: this.flags });
935✔
270
        }
271

272
        if (resolvedType && !isAnyReferenceType(resolvedType)) {
33,649✔
273
            this.circRefCount = 0;
32,720✔
274
            this.referenceChain.clear();
32,720✔
275
        }
276
        return resolvedType;
33,649✔
277
    }
278

279
    get __reflection() {
280
        return { name: 'ReferenceType' };
286,773✔
281
    }
282

283
    makeMemberFullName(memberName: string) {
284
        return this.fullName + '.' + memberName;
1,341✔
285
    }
286

287
    getCircularReferenceInfo(stopAtTypes: ReferenceType[] = []): TypeCircularReferenceInfo {
×
288
        this.resolve();
502✔
289
        const isCircRef = this.circRefCount > 1;
502✔
290

291
        const referenceChainNames: string[] = [];
502✔
292
        stopAtTypes.push(this);
502✔
293
        if (isCircRef) {
502✔
294
            referenceChainNames.push(this.fullName);
30✔
295
            for (const chainItem of this.referenceChain) {
30✔
296
                if (!isReferenceType(chainItem) || chainItem === this) {
30!
297
                    continue;
×
298
                }
299
                if (stopAtTypes.map(t => t.fullName).includes(chainItem.fullName)) {
44✔
300
                    break;
16✔
301
                }
302
                const chainItemCircInfo = chainItem.getCircularReferenceInfo(stopAtTypes);
14✔
303
                referenceChainNames.push(...chainItemCircInfo.referenceChainNames);
14✔
304
            }
305
        }
306

307
        return { isCircularReference: isCircRef, referenceChainNames: referenceChainNames };
502✔
308
    }
309

310
    private circRefCount = 0;
127,094✔
311

312
    private referenceChain = new Set<BscType>();
127,094✔
313

314
    private propertyTypeReference = new Map<string, TypePropertyReferenceType>();
127,094✔
315

316
    private memberTypeReferences = new Map<string, ReferenceType>();
127,094✔
317
    private callFuncMemberTypeReferences = new Map<string, ReferenceType>();
127,094✔
318

319
    private futureMemberTableProvider = () => {
127,094✔
320
        return {
19,318✔
321
            name: `FutureMemberTableProvider: '${this.__identifier}'`,
322
            getSymbolType: (innerName: string, innerOptions: GetTypeOptions) => {
323
                const resolvedType = this.resolve();
18,515✔
324
                if (resolvedType) {
18,515✔
325
                    return resolvedType.getMemberType(innerName, innerOptions);
9,511✔
326
                }
327
            },
328
            setCachedType: (innerName: string, innerResolvedTypeCacheEntry: TypeChainEntry, options: GetSymbolTypeOptions) => {
329
                const resolvedType = this.resolve();
803✔
330
                if (resolvedType) {
803!
331
                    resolvedType.memberTable.setCachedType(innerName, innerResolvedTypeCacheEntry, options);
803✔
332
                }
333
            },
334
            addSibling: (symbolTable: SymbolTable) => {
335
                const resolvedType = this.resolve();
×
336
                if (resolvedType) {
×
337
                    resolvedType.memberTable?.addSibling?.(symbolTable);
×
338
                }
339
            }
340
        };
341
    };
342

343
    private futureCallFuncMemberTableProvider = () => {
127,094✔
344
        return {
553✔
345
            name: `FutureCallFuncMemberTableProvider: '${this.__identifier}'`,
346
            getSymbolType: (innerName: string, innerOptions: GetTypeOptions) => {
347
                const resolvedType = this.resolve();
553✔
348
                if (isComponentType(resolvedType)) {
553✔
349
                    return resolvedType.getCallFuncType(innerName, innerOptions);
308✔
350
                }
351
            },
352
            setCachedType: (innerName: string, innerResolvedTypeCacheEntry: TypeChainEntry, options: GetSymbolTypeOptions) => {
353
                const resolvedType = this.resolve();
×
354
                if (isComponentType(resolvedType)) {
×
355
                    resolvedType.getCallFuncTable().setCachedType(innerName, innerResolvedTypeCacheEntry, options);
×
356
                }
357
            },
358
            addSibling: (symbolTable: SymbolTable) => {
359
                const resolvedType = this.resolve();
×
360
                if (isComponentType(resolvedType)) {
×
361
                    resolvedType.getCallFuncTable()?.addSibling?.(symbolTable);
×
362
                }
363
            }
364
        };
365
    };
366
}
367

368
/**
369
 * Use this class for when you need to reference a property of a BscType, and that property is also a BscType,
370
 * Especially when the instance with the property may not have been instantiated/discovered yet
371
 *
372
 * For Example, FunctionType.returnType --- if the FunctionExpression has not been walked yet, it will be a ReferenceType
373
 * if we just access .returnType on a ReferenceType that doesn't have access to an actual FunctionType yet, .returnType will be undefined
374
 * So when we use this class, it maintains that reference to a potential ReferenceType, and this class will change from
375
 * returning undefined to the ACTUAL .returnType when the ReferenceType can be resolved.
376
 *
377
 * This is really cool. It's like programming with time-travel.
378
 */
379
export class TypePropertyReferenceType extends BscType {
1✔
380
    constructor(public outerType: BscType, public propertyName: string) {
132✔
381
        super(propertyName);
132✔
382
        // eslint-disable-next-line no-constructor-return
383
        return new Proxy(this, {
132✔
384
            get: (target, propName, receiver) => {
385

386
                if (propName === '__reflection') {
1,732✔
387
                    // Cheeky way to get `isTypePropertyReferenceType` reflection to work
388
                    return { name: 'TypePropertyReferenceType' };
296✔
389
                }
390

391
                if (propName === 'outerType') {
1,436!
392
                    return outerType;
×
393
                }
394

395
                if (propName === 'getTarget') {
1,436!
396
                    return () => {
×
397
                        return this.getTarget();
×
398
                    };
399
                }
400

401
                if (propName === 'isResolvable') {
1,436✔
402
                    return () => {
115✔
403
                        return !!(isAnyReferenceType(this.outerType) ? (this.outerType as any).getTarget() : this.outerType?.isResolvable());
115!
404
                    };
405
                }
406

407
                if (isAnyReferenceType(this.outerType) && !this.outerType.isResolvable()) {
1,321✔
408
                    if (propName === 'getMemberType') {
142✔
409
                        //If we're calling `getMemberType()`, we need it to proxy to using the actual symbol table
410
                        //So if that symbol is ever populated, the correct type is passed through
411
                        return (memberName: string, options: GetSymbolTypeOptions) => {
16✔
412
                            const fullMemberName = this.outerType.toString() + '.' + memberName;
16✔
413
                            return new ReferenceType(memberName, fullMemberName, options.flags, () => {
16✔
414
                                return {
59✔
415
                                    name: `TypePropertyReferenceType : '${fullMemberName}'`,
416
                                    getSymbolType: (innerName: string, innerOptions: GetTypeOptions) => {
417
                                        return this.outerType?.[this.propertyName]?.getMemberType(innerName, innerOptions);
59!
418
                                    },
419
                                    setCachedType: (innerName: string, innerTypeCacheEntry: TypeChainEntry, innerOptions: GetTypeOptions) => {
420
                                        return this.outerType?.[this.propertyName]?.memberTable.setCachedType(innerName, innerTypeCacheEntry, innerOptions);
×
421
                                    },
422
                                    addSibling: (symbolTable: SymbolTable) => {
423
                                        return this.outerType?.[this.propertyName]?.memberTable?.addSibling?.(symbolTable);
×
424
                                    }
425
                                };
426
                            });
427
                        };
428
                    }
429
                    if (propName === 'isResolvable') {
126!
430
                        return () => false;
×
431
                    }
432
                }
433
                let inner = this.getTarget();
1,305✔
434

435
                if (!inner) {
1,305✔
436
                    inner = DynamicType.instance;
238✔
437
                }
438

439
                if (inner) {
1,305!
440
                    const result = Reflect.get(inner, propName, inner);
1,305✔
441
                    return result;
1,305✔
442
                }
443
            },
444
            set: (target, name, value, receiver) => {
445
                //There may be some need to specifically set members on ReferenceType in the future
446
                // eg: if (Reflect.has(target, name)) {
447
                //   return Reflect.set(target, name, value, receiver);
448

449
                let inner = this.outerType[this.propertyName];
9✔
450

451
                if (inner) {
9!
452
                    const result = Reflect.set(inner, name, value, inner);
9✔
453
                    return result;
9✔
454
                }
455
            }
456
        });
457
    }
458

459
    getTarget(): BscType {
460
        let actualOuterType = this.outerType;
1,305✔
461
        if (isAnyReferenceType(this.outerType)) {
1,305✔
462
            if ((this.outerType as ReferenceType).isResolvable()) {
1,300✔
463
                actualOuterType = (this.outerType as ReferenceType)?.getTarget();
1,174!
464
            }
465
        }
466
        return actualOuterType?.[this.propertyName];
1,305✔
467
    }
468

469
    tableProvider: SymbolTableProvider;
470
}
471

472
/**
473
 * Use this class for when there is a binary operator and either the left hand side and/or the right hand side
474
 * are ReferenceTypes
475
 */
476
export class BinaryOperatorReferenceType extends BscType {
1✔
477
    cachedType: BscType;
478

479
    constructor(public leftType: BscType, public operator: Token, public rightType: BscType, binaryOpResolver: (lType: BscType, operator: Token, rType: BscType) => BscType) {
34✔
480
        super(operator.text);
34✔
481
        // eslint-disable-next-line no-constructor-return
482
        return new Proxy(this, {
34✔
483
            get: (target, propName, receiver) => {
484

485
                if (propName === '__reflection') {
885✔
486
                    // Cheeky way to get `BinaryOperatorReferenceType` reflection to work
487
                    return { name: 'BinaryOperatorReferenceType' };
200✔
488
                }
489

490
                if (propName === 'leftType') {
685✔
491
                    return leftType;
6✔
492
                }
493

494
                if (propName === 'rightType') {
679✔
495
                    return rightType;
6✔
496
                }
497

498
                let resultType: BscType = this.cachedType ?? DynamicType.instance;
673✔
499
                if (!this.cachedType) {
673✔
500
                    if ((isAnyReferenceType(this.leftType) && !this.leftType.isResolvable()) ||
115✔
501
                        (isAnyReferenceType(this.rightType) && !this.rightType.isResolvable())
502
                    ) {
503
                        if (propName === 'isResolvable') {
110✔
504
                            return () => false;
68✔
505
                        }
506
                        if (propName === 'getTarget') {
42✔
507
                            return () => undefined;
2✔
508
                        }
509
                    } else {
510
                        resultType = binaryOpResolver(this.leftType, this.operator, this.rightType) ?? DynamicType.instance;
5!
511
                        this.cachedType = resultType;
5✔
512
                    }
513

514
                }
515
                if (resultType) {
603!
516
                    const result = Reflect.get(resultType, propName, resultType);
603✔
517
                    return result;
603✔
518
                }
519
            }
520
        });
521
    }
522

523
    getTarget: () => BscType;
524

525
    tableProvider: SymbolTableProvider;
526
}
527

528

529
/**
530
 * Use this class for when there is a presumable array type that is a referenceType
531
 */
532
export class ArrayDefaultTypeReferenceType extends BscType {
1✔
533
    constructor(public objType: BscType) {
29✔
534
        super('ArrayDefaultType');
29✔
535
        // eslint-disable-next-line no-constructor-return
536
        return new Proxy(this, {
29✔
537
            get: (target, propName, receiver) => {
538

539
                if (propName === '__reflection') {
1,306✔
540
                    // Cheeky way to get `ArrayDefaultTypeReferenceType` reflection to work
541
                    return { name: 'ArrayDefaultTypeReferenceType' };
422✔
542
                }
543

544
                if (propName === 'objType') {
884!
545
                    return objType;
×
546
                }
547

548
                if (propName === 'getTarget') {
884!
549
                    return () => {
×
550
                        if (isReferenceType(this.objType)) {
×
551
                            (this.objType as any).getTarget();
×
552
                        }
553
                        return this.objType;
×
554
                    };
555
                }
556

557
                let resultType: BscType = DynamicType.instance;
884✔
558
                if ((isAnyReferenceType(this.objType) && !this.objType.isResolvable())
884✔
559
                ) {
560
                    if (propName === 'isResolvable') {
2✔
561
                        return () => false;
1✔
562
                    }
563
                    if (propName === 'getTarget') {
1!
564
                        return () => undefined;
×
565
                    }
566
                } else {
567
                    if (isArrayType(this.objType)) {
882✔
568
                        resultType = this.objType.defaultType;
616✔
569
                    } else {
570
                        resultType = DynamicType.instance;
266✔
571
                    }
572
                }
573

574
                if (resultType) {
883!
575
                    const result = Reflect.get(resultType, propName, resultType);
883✔
576
                    return result;
883✔
577
                }
578
            }
579
        });
580
    }
581

582
    getTarget: () => BscType;
583

584
    tableProvider: SymbolTableProvider;
585
}
586

587
/**
588
 * Used for when a Param's initial value is unresolved
589
 * Generally, this will be resolved later *except* for Enum
590
 * Enum Types will have an initial value of an EnumMemberType, but the param
591
 * should be an EnumType
592
 */
593
export class ParamTypeFromValueReferenceType extends BscType {
1✔
594
    cachedType: BscType;
595

596
    constructor(public objType: BscType) {
449✔
597
        super('ParamInitialValueType');
449✔
598
        // eslint-disable-next-line no-constructor-return
599
        return new Proxy(this, {
449✔
600
            get: (target, propName, receiver) => {
601
                if (propName === '__reflection') {
3,424✔
602
                    // Cheeky way to get reflection to work
603
                    return { name: 'ParamTypeFromValueReferenceType' };
929✔
604
                }
605

606
                if (propName === 'objType') {
2,495!
607
                    return objType;
×
608
                }
609

610
                if (propName === 'getTarget') {
2,495✔
611
                    return () => {
3✔
612
                        if (this.cachedType) {
3!
613
                            return this.cachedType;
3✔
614
                        }
615
                        if (isReferenceType(this.objType)) {
×
616
                            (this.objType as any).getTarget();
×
617
                        }
618
                        return this.objType;
×
619
                    };
620
                }
621

622
                let resultType: BscType = this.cachedType ?? DynamicType.instance;
2,492✔
623
                if (!this.cachedType) {
2,492✔
624
                    if ((isAnyReferenceType(this.objType) && !this.objType.isResolvable())
1,000✔
625
                    ) {
626
                        if (propName === 'isResolvable') {
879✔
627
                            return () => false;
7✔
628
                        }
629
                        if (propName === 'getTarget') {
872!
630
                            return () => undefined;
×
631
                        }
632
                    } else {
633
                        resultType = util.getDefaultTypeFromValueType(this.objType);
121✔
634
                        this.cachedType = resultType;
121✔
635
                    }
636

637
                }
638
                if (resultType) {
2,485!
639
                    const result = Reflect.get(resultType, propName, resultType);
2,485✔
640
                    return result;
2,485✔
641
                }
642
            }
643
        });
644
    }
645
}
646

647
/**
648
 * Used when an IntersectionType has at least one member that may have default dynamic members.
649
 * If the inner type is not resolvable, this type will resolve to DynamicType.
650
 */
651
export class IntersectionWithDefaultDynamicReferenceType extends BscType {
1✔
652
    constructor(public baseType: BscType) {
26✔
653
        super('IntersectionWithDefaultDynamicReferenceType');
26✔
654
        // eslint-disable-next-line no-constructor-return
655
        return new Proxy(this, {
26✔
656
            get: (target, propName, receiver) => {
657

658
                if (propName === '__reflection') {
158✔
659
                    // Cheeky way to get `isIntersectionWithDefaultDynamicReferenceType` reflection to work
660
                    return { name: 'IntersectionWithDefaultDynamicReferenceType' };
48✔
661
                }
662

663
                if (propName === 'isResolvable') {
110✔
664
                    return () => {
30✔
665
                        return true;
30✔
666
                    };
667
                }
668
                let innerType = this.getTarget();
80✔
669

670
                if (!innerType) {
80!
NEW
671
                    innerType = DynamicType.instance;
×
672
                }
673

674
                if (innerType) {
80!
675
                    const result = Reflect.get(innerType, propName, innerType);
80✔
676
                    return result;
80✔
677
                }
678
            }
679
        });
680
    }
681

682
    getTarget(): BscType {
683
        if (isAnyReferenceType(this.baseType)) {
80!
684
            if (this.baseType.isResolvable()) {
80✔
685
                return (this.baseType as any)?.getTarget();
5!
686
            }
687
        }
688
        return this.baseType;
75✔
689
    }
690

691
    tableProvider: SymbolTableProvider;
692
}
693

694

695
/**
696
 * Gives an array of all the symbol names that need to be resolved to make the given reference type be resolved
697
 */
698
export function getAllRequiredSymbolNames(refType: BscType, namespaceLower?: string): Array<{ name: string; namespacedName?: string }> {
1✔
699
    if (refType.isResolvable()) {
86✔
700
        return [];
9✔
701
    }
702
    if (isReferenceType(refType)) {
77✔
703
        return [{ name: refType.fullName, namespacedName: namespaceLower ? `${namespaceLower}.${refType.fullName}` : null }];
71✔
704
    }
705
    if (isTypePropertyReferenceType(refType)) {
6!
UNCOV
706
        const outer = refType.outerType;
×
UNCOV
707
        if (isAnyReferenceType(outer)) {
×
UNCOV
708
            return getAllRequiredSymbolNames(outer);
×
709
        } else {
UNCOV
710
            return [];
×
711
        }
712

713
    }
714
    if (isArrayDefaultTypeReferenceType(refType)) {
6!
UNCOV
715
        const objType = refType.objType;
×
UNCOV
716
        if (isAnyReferenceType(objType)) {
×
UNCOV
717
            return getAllRequiredSymbolNames(objType);
×
718
        } else {
719
            return [];
×
720
        }
721
    }
722
    if (isBinaryOperatorReferenceType(refType)) {
6!
723
        const left = refType.leftType;
6✔
724
        const right = refType.rightType;
6✔
725
        const result = [];
6✔
726
        if (isAnyReferenceType(left)) {
6✔
727
            result.push(...getAllRequiredSymbolNames(left, namespaceLower));
4✔
728
        }
729
        if (isAnyReferenceType(right)) {
6!
730
            result.push(...getAllRequiredSymbolNames(right, namespaceLower));
6✔
731

732
        }
733
        return result;
6✔
734
    }
UNCOV
735
    if (isParamTypeFromValueReferenceType(refType)) {
×
UNCOV
736
        const objType = refType.objType;
×
UNCOV
737
        if (isAnyReferenceType(objType)) {
×
UNCOV
738
            return getAllRequiredSymbolNames(objType);
×
739
        } else {
UNCOV
740
            return [];
×
741
        }
742
    }
UNCOV
743
    return [];
×
744
}
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