or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

analysis.mddefinition-system.mdindex.mdreference-system.mdscope-management.mdscope-types.mdvariable-system.md

reference-system.mddocs/

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

```