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

rokucommunity / brighterscript / #15135

28 Jan 2026 04:38PM UTC coverage: 87.198%. Remained the same
#15135

push

web-flow
Merge f28626da6 into 610607efc

14643 of 17747 branches covered (82.51%)

Branch coverage included in aggregate %.

76 of 78 new or added lines in 11 files covered. (97.44%)

200 existing lines in 8 files now uncovered.

15402 of 16709 relevant lines covered (92.18%)

24805.87 hits per line

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

73.86
/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, 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);
127,241✔
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) {
129,235✔
27
        super(memberKey);
129,235✔
28
        // eslint-disable-next-line no-constructor-return
29
        return new Proxy(this, {
129,235✔
30
            get: (target, propName, receiver) => {
31

32
                if (propName === '__reflection') {
378,602✔
33
                    // Cheeky way to get `isReferenceType` reflection to work
34
                    return this.__reflection;
296,009✔
35
                }
36
                if (propName === '__identifier') {
82,593✔
37
                    // Cheeky way to get `isReferenceType` reflection to work
38
                    return this.__identifier;
23✔
39
                }
40
                if (propName === 'fullName') {
82,570✔
41
                    return this.fullName;
1,150✔
42
                }
43
                if (propName === 'isResolvable') {
81,420✔
44
                    return () => {
10,395✔
45
                        let resultSoFar = this.resolve();
10,395✔
46
                        while (resultSoFar && isReferenceType(resultSoFar)) {
10,395✔
47
                            resultSoFar = (resultSoFar as any).getTarget?.();
×
48
                        }
49
                        return !!resultSoFar;
10,395✔
50
                    };
51
                }
52
                if (propName === 'getCircularReferenceInfo') {
71,025✔
53
                    return (stopAtTypes: ReferenceType[] = []) => {
502✔
54
                        return this.getCircularReferenceInfo(stopAtTypes);
502✔
55
                    };
56
                }
57
                if (propName === 'getTarget') {
70,523✔
58
                    return () => {
9,357✔
59
                        return this.resolve();
9,357✔
60
                    };
61
                }
62
                if (propName === 'tableProvider') {
61,166✔
63
                    return this.tableProvider;
62✔
64
                }
65
                if (propName === 'isEqual') {
61,104✔
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) => {
96✔
69
                        if (!targetType) {
96!
70
                            return false;
×
71
                        }
72
                        const resolvedType = this.resolve();
96✔
73
                        let equal = false;
96✔
74
                        if (resolvedType && !isReferenceType(resolvedType)) {
96✔
75
                            equal = resolvedType.isEqual(targetType, data);
11✔
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;
96✔
84
                    };
85
                }
86
                if (propName === 'isTypeCompatible') {
61,008✔
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) => {
231✔
90
                        if (!targetType) {
231!
91
                            return false;
×
92
                        }
93
                        if (isDynamicType(targetType)) {
231✔
94
                            return true;
1✔
95
                        }
96
                        const resolvedType = this.resolve();
230✔
97
                        if (resolvedType && !isReferenceType(resolvedType)) {
230✔
98
                            return resolvedType.isTypeCompatible(targetType, data);
229✔
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();
60,777✔
112

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

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

129
                            }
130
                            memberTypeReference = new ReferenceType(memberName, this.makeMemberFullName(memberName), options.flags, this.futureMemberTableProvider);
1,334✔
131
                            this.memberTypeReferences.set(refLookUp, memberTypeReference);
1,334✔
132
                            return memberTypeReference;
1,334✔
133
                        };
134
                    } else if (propName === 'getCallFuncType') {
41,333✔
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) => {
248✔
138
                            const resolvedType = this.resolve();
248✔
139
                            if (isComponentType(resolvedType)) {
248!
140
                                return resolvedType.getCallFuncType(memberName, options);
×
141
                            }
142
                            const refLookUp = `${memberName.toLowerCase()}-${options.flags}-callfunc`;
248✔
143
                            let callFuncMemberTypeReference = this.callFuncMemberTypeReferences.get(refLookUp);
248✔
144
                            if (callFuncMemberTypeReference) {
248✔
145
                                return callFuncMemberTypeReference;
230✔
146

147
                            }
148
                            callFuncMemberTypeReference = new ReferenceType(memberName, this.makeMemberFullName(memberName), options.flags, this.futureCallFuncMemberTableProvider);
18✔
149
                            this.callFuncMemberTypeReferences.set(refLookUp, callFuncMemberTypeReference);
18✔
150
                            return callFuncMemberTypeReference;
18✔
151
                        };
152
                    } else if (propName === 'toString') {
41,085✔
153
                        // This type was never found
154
                        // For diagnostics, we should return the expected name of of the type
155
                        return () => this.fullName;
2,867✔
156
                    } else if (propName === 'toTypeString') {
38,218!
157
                        // For transpilation, we should 'dynamic'
158
                        return () => 'dynamic';
×
159
                    } else if (propName === 'returnType') {
38,218✔
160
                        let propRefType = this.propertyTypeReference.get(propName);
212✔
161
                        if (!propRefType) {
212✔
162
                            propRefType = new TypePropertyReferenceType(this, propName);
67✔
163
                            this.propertyTypeReference.set(propName, propRefType);
67✔
164
                        }
165
                        return propRefType;
212✔
166
                    } else if (propName === 'memberTable') {
38,006✔
167
                        return this.memberTable;
101✔
168
                    } else if (propName === 'getMemberTable') {
37,905✔
169
                        return () => {
32✔
170
                            return this.memberTable;
32✔
171
                        };
172
                    } else if (propName === 'callFuncTable') {
37,873!
173
                        return (this as any).callFuncMemberTable;
×
174
                    } else if (propName === 'getCallFuncTable') {
37,873!
175
                        return () => {
×
176
                            return (this as any).callFuncMemberTable;
×
177
                        };
178
                    } else if (propName === 'isTypeCompatible') {
37,873!
179
                        return (targetType: BscType) => {
×
180
                            return isDynamicType(targetType);
×
181
                        };
182
                    } else if (propName === 'isEqual') {
37,873!
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') {
37,873✔
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') {
37,835!
195
                        // this is an unknown type. There is no use in adding built in interfaces
196
                        return true;
×
197
                    } else if (propName === 'isBuiltIn') {
37,835✔
198
                        return false;
23✔
199
                    }
200
                }
201

202
                if (!innerType) {
55,826✔
203
                    innerType = DynamicType.instance;
37,812✔
204
                }
205
                const result = Reflect.get(innerType, propName, innerType);
55,826✔
206
                return result;
55,826✔
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();
779✔
214

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

233
    public readonly kind = BscTypeKind.ReferenceType;
129,235✔
234

235
    getTarget() {
236
        return this.resolve();
332✔
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();
104,556✔
244
        if (!symbolTable) {
104,556✔
245
            return;
19✔
246
        }
247
        // Look for circular references
248
        let resolvedType = symbolTable.getSymbolType(this.memberKey, { flags: this.flags, onlyCacheResolvedTypes: true });
104,537✔
249
        if (!resolvedType) {
104,534✔
250
            // could not find this member
251
            return;
64,134✔
252
        }
253

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

272
        if (resolvedType && !isAnyReferenceType(resolvedType)) {
37,031✔
273
            this.circRefCount = 0;
36,080✔
274
            this.referenceChain.clear();
36,080✔
275
        }
276
        return resolvedType;
37,031✔
277
    }
278

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

283
    makeMemberFullName(memberName: string) {
284
        return this.fullName + '.' + memberName;
1,352✔
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;
129,235✔
311

312
    private referenceChain = new Set<BscType>();
129,235✔
313

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

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

319
    private futureMemberTableProvider = () => {
129,235✔
320
        return {
19,609✔
321
            name: `FutureMemberTableProvider: '${this.__identifier}'`,
322
            getSymbolType: (innerName: string, innerOptions: GetTypeOptions) => {
323
                const resolvedType = this.resolve();
18,796✔
324
                if (resolvedType) {
18,796✔
325
                    return resolvedType.getMemberType(innerName, innerOptions);
9,742✔
326
                }
327
            },
328
            setCachedType: (innerName: string, innerResolvedTypeCacheEntry: TypeChainEntry, options: GetSymbolTypeOptions) => {
329
                const resolvedType = this.resolve();
813✔
330
                if (resolvedType) {
813!
331
                    resolvedType.memberTable.setCachedType(innerName, innerResolvedTypeCacheEntry, options);
813✔
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 = () => {
129,235✔
344
        return {
804✔
345
            name: `FutureCallFuncMemberTableProvider: '${this.__identifier}'`,
346
            getSymbolType: (innerName: string, innerOptions: GetTypeOptions) => {
347
                const resolvedType = this.resolve();
804✔
348
                if (isComponentType(resolvedType)) {
804✔
349
                    return resolvedType.getCallFuncType(innerName, innerOptions);
311✔
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) {
142✔
381
        super(propertyName);
142✔
382
        // eslint-disable-next-line no-constructor-return
383
        return new Proxy(this, {
142✔
384
            get: (target, propName, receiver) => {
385

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

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

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

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

407
                if (isAnyReferenceType(this.outerType) && !this.outerType.isResolvable()) {
1,536✔
408
                    if (propName === 'getMemberType') {
158✔
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 {
60✔
415
                                    name: `TypePropertyReferenceType : '${fullMemberName}'`,
416
                                    getSymbolType: (innerName: string, innerOptions: GetTypeOptions) => {
417
                                        return this.outerType?.[this.propertyName]?.getMemberType(innerName, innerOptions);
60!
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') {
142!
430
                        return () => false;
×
431
                    }
432
                }
433
                let inner = this.getTarget();
1,520✔
434

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

439
                if (inner) {
1,520!
440
                    const result = Reflect.get(inner, propName, inner);
1,520✔
441
                    return result;
1,520✔
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,520✔
461
        if (isAnyReferenceType(this.outerType)) {
1,520✔
462
            if ((this.outerType as ReferenceType).isResolvable()) {
1,515✔
463
                actualOuterType = (this.outerType as ReferenceType)?.getTarget();
1,373!
464
            }
465
        }
466
        return actualOuterType?.[this.propertyName];
1,520✔
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') {
959✔
486
                    // Cheeky way to get `BinaryOperatorReferenceType` reflection to work
487
                    return { name: 'BinaryOperatorReferenceType' };
200✔
488
                }
489

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

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

498
                let resultType: BscType = this.cachedType ?? DynamicType.instance;
747✔
499
                if (!this.cachedType) {
747✔
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) {
677!
516
                    const result = Reflect.get(resultType, propName, resultType);
677✔
517
                    return result;
677✔
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) {
129✔
534
        super('ArrayDefaultType');
129✔
535
        // eslint-disable-next-line no-constructor-return
536
        return new Proxy(this, {
129✔
537
            get: (target, propName, receiver) => {
538

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

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

548
                if (propName === 'getTarget') {
1,597!
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;
1,597✔
558
                if ((isAnyReferenceType(this.objType) && !this.objType.isResolvable())
1,597✔
559
                ) {
560
                    if (propName === 'isResolvable') {
2✔
561
                        return () => false;
1✔
562
                    }
563
                    if (propName === 'getTarget') {
1!
564
                        return () => undefined;
×
565
                    }
566
                } else {
567
                    resultType = util.getIteratorDefaultType(this.objType);
1,595✔
568
                }
569

570
                if (resultType) {
1,596!
571
                    const result = Reflect.get(resultType, propName, resultType);
1,596✔
572
                    return result;
1,596✔
573
                }
574
            }
575
        });
576
    }
577

578
    getTarget: () => BscType;
579

580
    tableProvider: SymbolTableProvider;
581
}
582

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

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

602
                if (propName === 'objType') {
2,553!
UNCOV
603
                    return objType;
×
604
                }
605

606
                if (propName === 'getTarget') {
2,553✔
607
                    return () => {
3✔
608
                        if (this.cachedType) {
3!
609
                            return this.cachedType;
3✔
610
                        }
UNCOV
611
                        if (isReferenceType(this.objType)) {
×
UNCOV
612
                            (this.objType as any).getTarget();
×
613
                        }
UNCOV
614
                        return this.objType;
×
615
                    };
616
                }
617

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

633
                }
634
                if (resultType) {
2,543!
635
                    const result = Reflect.get(resultType, propName, resultType);
2,543✔
636
                    return result;
2,543✔
637
                }
638
            }
639
        });
640
    }
641
}
642

643
/**
644
 * Used when an IntersectionType has at least one member that may have default dynamic members.
645
 * If the inner type is not resolvable, this type will resolve to the given type instead.
646
 * @example
647
 * ```brighterscript
648
 *   interface HasAge
649
 *       age as integer
650
 *   end interface
651
 *
652
 *   type foo = HasAge and roAssociativeArray
653
 *
654
 *   sub useFoo(f as foo)
655
 *       print f.age ' f.age is an integer
656
 *       print f.doesExist("key") ' will result in a boolean, from roAssociativeArray
657
 *       print f.someKey ' no idea - so this is dynamic, and will not cause a validation error
658
 *   end sub
659
 * ```
660
 */
661
export class ReferenceTypeWithDefault extends BscType {
1✔
662
    constructor(public baseType: BscType, public defaultType: BscType) {
26✔
663
        super('ReferenceTypeWithDefault');
26✔
664
        // eslint-disable-next-line no-constructor-return
665
        return new Proxy(this, {
26✔
666
            get: (target, propName, receiver) => {
667

668
                if (propName === '__reflection') {
158✔
669
                    // Cheeky way to get `isReferenceTypeWithDefault` reflection to work
670
                    return { name: 'ReferenceTypeWithDefault' };
48✔
671
                }
672

673
                if (propName === 'isResolvable') {
110✔
674
                    return () => {
30✔
675
                        return true;
30✔
676
                    };
677
                }
678
                let innerType = this.getTarget();
80✔
679

680
                if (!innerType) {
80!
UNCOV
681
                    innerType = this.defaultType;
×
682
                }
683

684
                if (innerType) {
80!
685
                    const result = Reflect.get(innerType, propName, innerType);
80✔
686
                    return result;
80✔
687
                }
688
            }
689
        });
690
    }
691

692
    getTarget(): BscType {
693
        if (isAnyReferenceType(this.baseType)) {
80!
694
            if (this.baseType.isResolvable()) {
80✔
695
                return (this.baseType as any)?.getTarget();
5!
696
            }
697
        }
698
        return this.baseType;
75✔
699
    }
700

701
    tableProvider: SymbolTableProvider;
702
}
703

704

705
/**
706
 * Gives an array of all the symbol names that need to be resolved to make the given reference type be resolved
707
 */
708
export function getAllRequiredSymbolNames(refType: BscType, namespaceLower?: string): Array<{ name: string; namespacedName?: string }> {
1✔
709
    if (refType.isResolvable()) {
86✔
710
        return [];
9✔
711
    }
712
    if (isReferenceType(refType)) {
77✔
713
        return [{ name: refType.fullName, namespacedName: namespaceLower ? `${namespaceLower}.${refType.fullName}` : null }];
71✔
714
    }
715
    if (isTypePropertyReferenceType(refType)) {
6!
716
        const outer = refType.outerType;
×
UNCOV
717
        if (isAnyReferenceType(outer)) {
×
UNCOV
718
            return getAllRequiredSymbolNames(outer);
×
719
        } else {
UNCOV
720
            return [];
×
721
        }
722

723
    }
724
    if (isArrayDefaultTypeReferenceType(refType)) {
6!
725
        const objType = refType.objType;
×
UNCOV
726
        if (isAnyReferenceType(objType)) {
×
UNCOV
727
            return getAllRequiredSymbolNames(objType);
×
728
        } else {
UNCOV
729
            return [];
×
730
        }
731
    }
732
    if (isBinaryOperatorReferenceType(refType)) {
6!
733
        const left = refType.leftType;
6✔
734
        const right = refType.rightType;
6✔
735
        const result = [];
6✔
736
        if (isAnyReferenceType(left)) {
6✔
737
            result.push(...getAllRequiredSymbolNames(left, namespaceLower));
4✔
738
        }
739
        if (isAnyReferenceType(right)) {
6!
740
            result.push(...getAllRequiredSymbolNames(right, namespaceLower));
6✔
741

742
        }
743
        return result;
6✔
744
    }
UNCOV
745
    if (isParamTypeFromValueReferenceType(refType)) {
×
746
        const objType = refType.objType;
×
UNCOV
747
        if (isAnyReferenceType(objType)) {
×
UNCOV
748
            return getAllRequiredSymbolNames(objType);
×
749
        } else {
UNCOV
750
            return [];
×
751
        }
752
    }
UNCOV
753
    return [];
×
754
}
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