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

rokucommunity / brighterscript / #15079

13 Jan 2026 06:21PM UTC coverage: 87.299% (+0.1%) from 87.152%
#15079

push

web-flow
Merge 0c7d51295 into 2ea4d2108

14409 of 17439 branches covered (82.63%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

209 existing lines in 11 files now uncovered.

15091 of 16353 relevant lines covered (92.28%)

24237.03 hits per line

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

72.66
/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);
123,952✔
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) {
125,720✔
27
        super(memberKey);
125,720✔
28
        // eslint-disable-next-line no-constructor-return
29
        return new Proxy(this, {
125,720✔
30
            get: (target, propName, receiver) => {
31

32
                if (propName === '__reflection') {
352,744✔
33
                    // Cheeky way to get `isReferenceType` reflection to work
34
                    return this.__reflection;
282,133✔
35
                }
36
                if (propName === '__identifier') {
70,611✔
37
                    // Cheeky way to get `isReferenceType` reflection to work
38
                    return this.__identifier;
13✔
39
                }
40
                if (propName === 'fullName') {
70,598✔
41
                    return this.fullName;
1,101✔
42
                }
43
                if (propName === 'isResolvable') {
69,497✔
44
                    return () => {
8,983✔
45
                        let resultSoFar = this.resolve();
8,983✔
46
                        while (resultSoFar && isReferenceType(resultSoFar)) {
8,983✔
47
                            resultSoFar = (resultSoFar as any).getTarget?.();
×
48
                        }
49
                        return !!resultSoFar;
8,983✔
50
                    };
51
                }
52
                if (propName === 'getCircularReferenceInfo') {
60,514✔
53
                    return (stopAtTypes: ReferenceType[] = []) => {
501✔
54
                        return this.getCircularReferenceInfo(stopAtTypes);
501✔
55
                    };
56
                }
57
                if (propName === 'getTarget') {
60,013✔
58
                    return () => {
9,095✔
59
                        return this.resolve();
9,095✔
60
                    };
61
                }
62
                if (propName === 'tableProvider') {
50,918✔
63
                    return this.tableProvider;
38✔
64
                }
65
                if (propName === 'isEqual') {
50,880✔
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) => {
57✔
69
                        if (!targetType) {
57!
70
                            return false;
×
71
                        }
72
                        const resolvedType = this.resolve();
57✔
73
                        let equal = false;
57✔
74
                        if (resolvedType && !isReferenceType(resolvedType)) {
57✔
75
                            equal = resolvedType.isEqual(targetType, data);
6✔
76
                        } else if (isReferenceType(targetType)) {
51✔
77
                            equal = this.fullName.toLowerCase() === targetType.fullName.toLowerCase() &&
47!
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;
57✔
84
                    };
85
                }
86
                if (propName === 'isTypeCompatible') {
50,823✔
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();
50,600✔
112

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

116
                    if (propName === 'getMemberType') {
37,772✔
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,337✔
120
                            const resolvedType = this.resolve();
1,337✔
121
                            if (resolvedType) {
1,337!
122
                                return resolvedType.getMemberType(memberName, options);
×
123
                            }
124
                            const refLookUp = `${memberName.toLowerCase()}-${options?.flags}`;
1,337!
125
                            let memberTypeReference = this.memberTypeReferences.get(refLookUp);
1,337✔
126
                            if (memberTypeReference) {
1,337✔
127
                                return memberTypeReference;
31✔
128

129
                            }
130
                            memberTypeReference = new ReferenceType(memberName, this.makeMemberFullName(memberName), options.flags, this.futureMemberTableProvider);
1,306✔
131
                            this.memberTypeReferences.set(refLookUp, memberTypeReference);
1,306✔
132
                            return memberTypeReference;
1,306✔
133
                        };
134
                    } else if (propName === 'getCallFuncType') {
36,435✔
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!
UNCOV
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') {
36,427✔
153
                        // This type was never found
154
                        // For diagnostics, we should return the expected name of of the type
155
                        return () => this.fullName;
2,491✔
156
                    } else if (propName === 'toTypeString') {
33,936!
157
                        // For transpilation, we should 'dynamic'
158
                        return () => 'dynamic';
×
159
                    } else if (propName === 'returnType') {
33,936✔
160
                        let propRefType = this.propertyTypeReference.get(propName);
194✔
161
                        if (!propRefType) {
194✔
162
                            propRefType = new TypePropertyReferenceType(this, propName);
62✔
163
                            this.propertyTypeReference.set(propName, propRefType);
62✔
164
                        }
165
                        return propRefType;
194✔
166
                    } else if (propName === 'memberTable') {
33,742✔
167
                        return this.memberTable;
101✔
168
                    } else if (propName === 'getMemberTable') {
33,641✔
169
                        return () => {
32✔
170
                            return this.memberTable;
32✔
171
                        };
172
                    } else if (propName === 'callFuncTable') {
33,609!
173
                        return (this as any).callFuncMemberTable;
×
174
                    } else if (propName === 'getCallFuncTable') {
33,609!
175
                        return () => {
×
176
                            return (this as any).callFuncMemberTable;
×
177
                        };
178
                    } else if (propName === 'isTypeCompatible') {
33,609!
179
                        return (targetType: BscType) => {
×
180
                            return isDynamicType(targetType);
×
181
                        };
182
                    } else if (propName === 'isEqual') {
33,609!
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') {
33,609✔
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') {
33,571!
195
                        // this is an unknown type. There is no use in adding built in interfaces
196
                        return true;
×
197
                    } else if (propName === 'isBuiltIn') {
33,571✔
198
                        return false;
23✔
199
                    }
200
                }
201

202
                if (!innerType) {
46,373✔
203
                    innerType = DynamicType.instance;
33,548✔
204
                }
205
                const result = Reflect.get(innerType, propName, innerType);
46,373✔
206
                return result;
46,373✔
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();
534✔
214

215
                // Look for circular references
216
                if (!innerType || this.referenceChain.has(innerType)) {
534!
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);
534✔
227
                this.referenceChain.clear();
534✔
228
                return result;
534✔
229
            }
230
        });
231
    }
232

233
    public readonly kind = BscTypeKind.ReferenceType;
125,720✔
234

235
    getTarget() {
236
        return this.resolve();
302✔
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();
90,146✔
244
        if (!symbolTable) {
90,146✔
245
            return;
17✔
246
        }
247
        // Look for circular references
248
        let resolvedType = symbolTable.getSymbolType(this.memberKey, { flags: this.flags, onlyCacheResolvedTypes: true });
90,129✔
249
        if (!resolvedType) {
90,126✔
250
            // could not find this member
251
            return;
57,909✔
252
        }
253

254
        if (isAnyReferenceType(resolvedType)) {
32,217✔
255
            // If this is a referenceType, keep digging down until we have a non reference Type.
256
            while (resolvedType && isAnyReferenceType(resolvedType)) {
3,164✔
257
                if (this.referenceChain.has(resolvedType)) {
3,164✔
258
                    // this is a circular reference
259
                    this.circRefCount++;
2,306✔
260
                }
261
                if (this.circRefCount > 1) {
3,164✔
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,239✔
265
                }
266
                this.referenceChain.add(resolvedType);
925✔
267
                resolvedType = (resolvedType as any)?.getTarget?.();
925!
268
            }
269
            this.tableProvider().setCachedType(this.memberKey, { type: resolvedType }, { flags: this.flags });
925✔
270
        }
271

272
        if (resolvedType && !isAnyReferenceType(resolvedType)) {
29,978✔
273
            this.circRefCount = 0;
29,059✔
274
            this.referenceChain.clear();
29,059✔
275
        }
276
        return resolvedType;
29,978✔
277
    }
278

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

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

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

291
        const referenceChainNames: string[] = [];
501✔
292
        stopAtTypes.push(this);
501✔
293
        if (isCircRef) {
501✔
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 };
501✔
308
    }
309

310
    private circRefCount = 0;
125,720✔
311

312
    private referenceChain = new Set<BscType>();
125,720✔
313

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

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

319
    private futureMemberTableProvider = () => {
125,720✔
320
        return {
17,956✔
321
            name: `FutureMemberTableProvider: '${this.__identifier}'`,
322
            getSymbolType: (innerName: string, innerOptions: GetTypeOptions) => {
323
                const resolvedType = this.resolve();
17,163✔
324
                if (resolvedType) {
17,163✔
325
                    return resolvedType.getMemberType(innerName, innerOptions);
8,937✔
326
                }
327
            },
328
            setCachedType: (innerName: string, innerResolvedTypeCacheEntry: TypeChainEntry, options: GetSymbolTypeOptions) => {
329
                const resolvedType = this.resolve();
793✔
330
                if (resolvedType) {
793!
331
                    resolvedType.memberTable.setCachedType(innerName, innerResolvedTypeCacheEntry, options);
793✔
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 = () => {
125,720✔
344
        return {
550✔
345
            name: `FutureCallFuncMemberTableProvider: '${this.__identifier}'`,
346
            getSymbolType: (innerName: string, innerOptions: GetTypeOptions) => {
347
                const resolvedType = this.resolve();
550✔
348
                if (isComponentType(resolvedType)) {
550✔
349
                    return resolvedType.getCallFuncType(innerName, innerOptions);
305✔
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,697✔
387
                    // Cheeky way to get `isTypePropertyReferenceType` reflection to work
388
                    return { name: 'TypePropertyReferenceType' };
296✔
389
                }
390

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

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

401
                if (propName === 'isResolvable') {
1,401✔
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,286✔
408
                    if (propName === 'getMemberType') {
141✔
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') {
125!
430
                        return () => false;
×
431
                    }
432
                }
433
                let inner = this.getTarget();
1,270✔
434

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

439
                if (inner) {
1,270!
440
                    const result = Reflect.get(inner, propName, inner);
1,270✔
441
                    return result;
1,270✔
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,270✔
461
        if (isAnyReferenceType(this.outerType)) {
1,270✔
462
            if ((this.outerType as ReferenceType).isResolvable()) {
1,265✔
463
                actualOuterType = (this.outerType as ReferenceType)?.getTarget();
1,140!
464
            }
465
        }
466
        return actualOuterType?.[this.propertyName];
1,270✔
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,393✔
602
                    // Cheeky way to get reflection to work
603
                    return { name: 'ParamTypeFromValueReferenceType' };
929✔
604
                }
605

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

610
                if (propName === 'getTarget') {
2,464✔
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,461✔
623
                if (!this.cachedType) {
2,461✔
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,454!
639
                    const result = Reflect.get(resultType, propName, resultType);
2,454✔
640
                    return result;
2,454✔
641
                }
642
            }
643
        });
644
    }
645
}
646

647
/**
648
 * Gives an array of all the symbol names that need to be resolved to make the given reference type be resolved
649
 */
650
export function getAllRequiredSymbolNames(refType: BscType, namespaceLower?: string): Array<{ name: string; namespacedName?: string }> {
1✔
651
    if (refType.isResolvable()) {
86✔
652
        return [];
9✔
653
    }
654
    if (isReferenceType(refType)) {
77✔
655
        return [{ name: refType.fullName, namespacedName: namespaceLower ? `${namespaceLower}.${refType.fullName}` : null }];
71✔
656
    }
657
    if (isTypePropertyReferenceType(refType)) {
6!
UNCOV
658
        const outer = refType.outerType;
×
UNCOV
659
        if (isAnyReferenceType(outer)) {
×
UNCOV
660
            return getAllRequiredSymbolNames(outer);
×
661
        } else {
UNCOV
662
            return [];
×
663
        }
664

665
    }
666
    if (isArrayDefaultTypeReferenceType(refType)) {
6!
UNCOV
667
        const objType = refType.objType;
×
UNCOV
668
        if (isAnyReferenceType(objType)) {
×
UNCOV
669
            return getAllRequiredSymbolNames(objType);
×
670
        } else {
671
            return [];
×
672
        }
673
    }
674
    if (isBinaryOperatorReferenceType(refType)) {
6!
675
        const left = refType.leftType;
6✔
676
        const right = refType.rightType;
6✔
677
        const result = [];
6✔
678
        if (isAnyReferenceType(left)) {
6✔
679
            result.push(...getAllRequiredSymbolNames(left, namespaceLower));
4✔
680
        }
681
        if (isAnyReferenceType(right)) {
6!
682
            result.push(...getAllRequiredSymbolNames(right, namespaceLower));
6✔
683

684
        }
685
        return result;
6✔
686
    }
UNCOV
687
    if (isParamTypeFromValueReferenceType(refType)) {
×
UNCOV
688
        const objType = refType.objType;
×
UNCOV
689
        if (isAnyReferenceType(objType)) {
×
UNCOV
690
            return getAllRequiredSymbolNames(objType);
×
691
        } else {
UNCOV
692
            return [];
×
693
        }
694
    }
UNCOV
695
    return [];
×
696
}
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