0
# Reference System
1
2
The reference tracking system provides comprehensive reference tracking that identifies all identifier occurrences and their usage patterns throughout the analyzed code.
3
4
## Capabilities
5
6
### Reference Class
7
8
The main Reference class that represents a single occurrence of an identifier in code.
9
10
```typescript { .api }
11
/**
12
* A Reference represents a single occurrence of an identifier in code.
13
*/
14
class Reference {
15
/** A unique ID for this instance - primarily used to help debugging and testing */
16
readonly $id: number;
17
18
/** Reference to the enclosing Scope */
19
readonly from: Scope;
20
21
/** Identifier syntax node */
22
readonly identifier: TSESTree.Identifier | TSESTree.JSXIdentifier;
23
24
/** True if this reference is the initial declaration/definition */
25
readonly init: boolean;
26
27
/** Information about implicit global references */
28
readonly maybeImplicitGlobal: ReferenceImplicitGlobal | null;
29
30
/** The variable that this identifier references, or null if unresolved */
31
readonly resolved: Variable | null;
32
33
/** The expression being written to, if this is a write reference */
34
readonly writeExpr: TSESTree.Node | null;
35
36
/** True if this reference is in a type context */
37
get isTypeReference(): boolean;
38
39
/** True if this reference is in a value context */
40
get isValueReference(): boolean;
41
42
/** Check if this reference represents a write operation */
43
isWrite(): boolean;
44
45
/** Check if this reference represents a read operation */
46
isRead(): boolean;
47
48
/** Check if this reference is read-only */
49
isReadOnly(): boolean;
50
51
/** Check if this reference is write-only */
52
isWriteOnly(): boolean;
53
54
/** Check if this reference is both read and write */
55
isReadWrite(): boolean;
56
}
57
```
58
59
### Reference Flags
60
61
Enumeration defining the read/write characteristics of references.
62
63
```typescript { .api }
64
enum ReferenceFlag {
65
Read = 0x1, // Reference reads the variable
66
Write = 0x2, // Reference writes to the variable
67
ReadWrite = 0x3 // Reference both reads and writes (e.g., +=, ++, --)
68
}
69
70
enum ReferenceTypeFlag {
71
Value = 0x1, // Reference is in a value context
72
Type = 0x2 // Reference is in a type context
73
}
74
```
75
76
### Implicit Global Reference
77
78
Interface for tracking references that may create implicit global variables.
79
80
```typescript { .api }
81
interface ReferenceImplicitGlobal {
82
/** The AST node where the implicit global is referenced */
83
node: TSESTree.Node;
84
85
/** The identifier pattern being referenced */
86
pattern: TSESTree.BindingName;
87
88
/** The reference object if created */
89
ref?: Reference;
90
}
91
```
92
93
**Usage Examples:**
94
95
```typescript
96
import { analyze } from "@typescript-eslint/scope-manager";
97
import { parse } from "@typescript-eslint/parser";
98
99
const code = `
100
let count = 0;
101
const message = "Hello";
102
103
function increment() {
104
count++; // Read-write reference to 'count'
105
return count; // Read reference to 'count'
106
}
107
108
function greet(name: string) {
109
const greeting = message + ", " + name; // Read reference to 'message'
110
return greeting;
111
}
112
113
// Type reference
114
interface User {
115
name: string;
116
}
117
118
function createUser(): User { // Type reference to 'User'
119
return { name: "Alice" };
120
}
121
`;
122
123
const ast = parse(code);
124
const scopeManager = analyze(ast, { sourceType: 'module' });
125
126
// Analyze all references across all scopes
127
console.log('=== Reference Analysis ===');
128
scopeManager.scopes.forEach(scope => {
129
console.log(`\nScope: ${scope.type}`);
130
console.log(`References: ${scope.references.length}`);
131
132
scope.references.forEach((ref, index) => {
133
console.log(` Reference ${index}: ${ref.identifier.name}`);
134
console.log(` Type: ${ref.isTypeReference ? 'Type' : 'Value'}`);
135
console.log(` Read: ${ref.isRead()}`);
136
console.log(` Write: ${ref.isWrite()}`);
137
console.log(` Resolved: ${ref.resolved ? ref.resolved.name : 'unresolved'}`);
138
139
if (ref.writeExpr) {
140
console.log(` Write expression: ${ref.writeExpr.type}`);
141
}
142
143
console.log(` From scope: ${ref.from.type}`);
144
});
145
});
146
```
147
148
### Reference Usage Patterns
149
150
Analyze different types of reference patterns:
151
152
```typescript
153
// Collect and categorize references
154
const allReferences: Reference[] = [];
155
scopeManager.scopes.forEach(scope => {
156
allReferences.push(...scope.references);
157
});
158
159
// Categorize by operation type
160
const readRefs = allReferences.filter(ref => ref.isReadOnly());
161
const writeRefs = allReferences.filter(ref => ref.isWriteOnly());
162
const readWriteRefs = allReferences.filter(ref => ref.isReadWrite());
163
164
console.log('Reference Operation Analysis:');
165
console.log(` Read-only: ${readRefs.length}`);
166
console.log(` Write-only: ${writeRefs.length}`);
167
console.log(` Read-write: ${readWriteRefs.length}`);
168
169
// Categorize by context
170
const typeRefs = allReferences.filter(ref => ref.isTypeReference);
171
const valueRefs = allReferences.filter(ref => ref.isValueReference);
172
173
console.log('\nReference Context Analysis:');
174
console.log(` Type context: ${typeRefs.length}`);
175
console.log(` Value context: ${valueRefs.length}`);
176
177
// Analyze read-write operations
178
console.log('\nRead-Write Operations:');
179
readWriteRefs.forEach(ref => {
180
console.log(` ${ref.identifier.name}: ${ref.writeExpr?.type}`);
181
});
182
```
183
184
### Reference Resolution Analysis
185
186
Track how references resolve to their variable definitions:
187
188
```typescript
189
// Analyze reference resolution
190
console.log('=== Reference Resolution Analysis ===');
191
192
const resolvedRefs = allReferences.filter(ref => ref.resolved);
193
const unresolvedRefs = allReferences.filter(ref => !ref.resolved);
194
195
console.log(`Resolved references: ${resolvedRefs.length}`);
196
console.log(`Unresolved references: ${unresolvedRefs.length}`);
197
198
// Group resolved references by variable
199
const refsByVariable = new Map<Variable, Reference[]>();
200
resolvedRefs.forEach(ref => {
201
if (ref.resolved) {
202
if (!refsByVariable.has(ref.resolved)) {
203
refsByVariable.set(ref.resolved, []);
204
}
205
refsByVariable.get(ref.resolved)!.push(ref);
206
}
207
});
208
209
console.log('\nReferences per variable:');
210
refsByVariable.forEach((refs, variable) => {
211
console.log(` ${variable.name}: ${refs.length} references`);
212
213
const scopes = new Set(refs.map(ref => ref.from.type));
214
console.log(` Used in scopes: ${Array.from(scopes).join(', ')}`);
215
216
const operations = {
217
read: refs.filter(ref => ref.isRead()).length,
218
write: refs.filter(ref => ref.isWrite()).length
219
};
220
console.log(` Operations: ${operations.read} reads, ${operations.write} writes`);
221
});
222
223
// Analyze unresolved references (potential issues)
224
if (unresolvedRefs.length > 0) {
225
console.log('\nโ ๏ธ Unresolved References:');
226
unresolvedRefs.forEach(ref => {
227
console.log(` ${ref.identifier.name} in ${ref.from.type} scope`);
228
229
if (ref.maybeImplicitGlobal) {
230
console.log(` May be implicit global`);
231
}
232
});
233
}
234
```
235
236
### Cross-Scope Reference Analysis
237
238
Analyze how references cross scope boundaries:
239
240
```typescript
241
// Analyze cross-scope references
242
console.log('=== Cross-Scope Reference Analysis ===');
243
244
scopeManager.scopes.forEach(scope => {
245
console.log(`\nScope: ${scope.type}`);
246
247
// References that resolve to variables in parent scopes
248
const crossScopeRefs = scope.references.filter(ref =>
249
ref.resolved && ref.resolved.scope !== scope
250
);
251
252
if (crossScopeRefs.length > 0) {
253
console.log(` Cross-scope references: ${crossScopeRefs.length}`);
254
crossScopeRefs.forEach(ref => {
255
console.log(` ${ref.identifier.name} -> ${ref.resolved?.scope.type} scope`);
256
});
257
}
258
259
// Through references (unresolved in this scope)
260
if (scope.through.length > 0) {
261
console.log(` Through references: ${scope.through.length}`);
262
scope.through.forEach(ref => {
263
console.log(` ${ref.identifier.name} (unresolved)`);
264
});
265
}
266
});
267
```
268
269
### Reference Context Analysis
270
271
Analyze TypeScript-specific type vs value contexts:
272
273
```typescript
274
// TypeScript context analysis
275
const code = `
276
interface Point {
277
x: number;
278
y: number;
279
}
280
281
class Rectangle {
282
width: number;
283
height: number;
284
}
285
286
function createPoint(): Point { // Type reference
287
return { x: 0, y: 0 };
288
}
289
290
function createRect(): Rectangle { // Type reference
291
return new Rectangle(); // Value reference
292
}
293
294
const point: Point = createPoint(); // Type and value references
295
const rect = new Rectangle(); // Value reference only
296
`;
297
298
const ast = parse(code);
299
const scopeManager = analyze(ast, { sourceType: 'module' });
300
301
// Analyze context usage
302
console.log('=== Context Analysis ===');
303
const contextAnalysis = new Map<string, { type: number, value: number }>();
304
305
scopeManager.scopes.forEach(scope => {
306
scope.references.forEach(ref => {
307
const name = ref.identifier.name;
308
if (!contextAnalysis.has(name)) {
309
contextAnalysis.set(name, { type: 0, value: 0 });
310
}
311
312
const stats = contextAnalysis.get(name)!;
313
if (ref.isTypeReference) stats.type++;
314
if (ref.isValueReference) stats.value++;
315
});
316
});
317
318
contextAnalysis.forEach((stats, name) => {
319
console.log(`${name}:`);
320
console.log(` Type context: ${stats.type} references`);
321
console.log(` Value context: ${stats.value} references`);
322
323
if (stats.type > 0 && stats.value > 0) {
324
console.log(` โ Used in both contexts`);
325
} else if (stats.type > 0) {
326
console.log(` ๐ Type-only usage`);
327
} else {
328
console.log(` ๐พ Value-only usage`);
329
}
330
});
331
```
332
333
### Write Expression Analysis
334
335
Analyze different types of write operations:
336
337
```typescript
338
// Analyze write expressions
339
console.log('=== Write Expression Analysis ===');
340
341
const writeRefs = allReferences.filter(ref => ref.writeExpr);
342
343
const writeTypes = new Map<string, number>();
344
writeRefs.forEach(ref => {
345
const type = ref.writeExpr!.type;
346
writeTypes.set(type, (writeTypes.get(type) || 0) + 1);
347
});
348
349
console.log('Write expression types:');
350
writeTypes.forEach((count, type) => {
351
console.log(` ${type}: ${count}`);
352
});
353
354
// Examples of write expressions:
355
// - AssignmentExpression: x = 5
356
// - UpdateExpression: x++, --y
357
// - VariableDeclarator: let x = 5
358
// - FunctionDeclaration: function f() {}
359
// - Parameter: function f(x) {} (x is written to)
360
```
361
362
### Reference Lifecycle
363
364
Understanding the lifecycle of references:
365
366
```typescript
367
// Track reference lifecycle
368
console.log('=== Reference Lifecycle Analysis ===');
369
370
resolvedRefs.forEach(ref => {
371
if (ref.resolved) {
372
const variable = ref.resolved;
373
console.log(`\nReference: ${ref.identifier.name}`);
374
console.log(` Variable defined in: ${variable.scope.type} scope`);
375
console.log(` Referenced from: ${ref.from.type} scope`);
376
console.log(` Definition count: ${variable.defs.length}`);
377
console.log(` Total references: ${variable.references.length}`);
378
379
// Find if this is the first reference
380
const refIndex = variable.references.indexOf(ref);
381
console.log(` Reference order: ${refIndex + 1} of ${variable.references.length}`);
382
383
if (ref.init) {
384
console.log(` โ This is a defining reference`);
385
}
386
}
387
});
388
```