|
1 |
| -import { |
2 |
| - addEmitHelpers, |
3 |
| - addRange, |
4 |
| - append, |
5 |
| - arrayFrom, |
6 |
| - BindingElement, |
7 |
| - Block, |
8 |
| - Bundle, |
9 |
| - CaseOrDefaultClause, |
10 |
| - chainBundle, |
11 |
| - ClassDeclaration, |
12 |
| - Debug, |
13 |
| - EmitFlags, |
14 |
| - ExportAssignment, |
15 |
| - ExportSpecifier, |
16 |
| - Expression, |
17 |
| - firstOrUndefined, |
18 |
| - ForOfStatement, |
19 |
| - ForStatement, |
20 |
| - GeneratedIdentifierFlags, |
21 |
| - getEmitFlags, |
22 |
| - hasSyntacticModifier, |
23 |
| - Identifier, |
24 |
| - IdentifierNameMap, |
25 |
| - isArray, |
26 |
| - isBindingPattern, |
27 |
| - isBlock, |
28 |
| - isCaseClause, |
29 |
| - isCustomPrologue, |
30 |
| - isExpression, |
31 |
| - isGeneratedIdentifier, |
32 |
| - isIdentifier, |
33 |
| - isLocalName, |
34 |
| - isNamedEvaluation, |
35 |
| - isOmittedExpression, |
36 |
| - isPrologueDirective, |
37 |
| - isSourceFile, |
38 |
| - isStatement, |
39 |
| - isVariableDeclarationList, |
40 |
| - isVariableStatement, |
41 |
| - ModifierFlags, |
42 |
| - Node, |
43 |
| - NodeFlags, |
44 |
| - setCommentRange, |
45 |
| - setEmitFlags, |
46 |
| - setOriginalNode, |
47 |
| - setSourceMapRange, |
48 |
| - setTextRange, |
49 |
| - skipOuterExpressions, |
50 |
| - SourceFile, |
51 |
| - Statement, |
52 |
| - SwitchStatement, |
53 |
| - SyntaxKind, |
54 |
| - TransformationContext, |
55 |
| - TransformFlags, |
56 |
| - transformNamedEvaluation, |
57 |
| - VariableDeclaration, |
58 |
| - VariableDeclarationList, |
59 |
| - VariableStatement, |
60 |
| - visitArray, |
61 |
| - visitEachChild, |
62 |
| - visitNode, |
63 |
| - visitNodes, |
64 |
| - VisitResult, |
| 1 | +import { |
| 2 | + addEmitHelpers, |
| 3 | + addRange, |
| 4 | + append, |
| 5 | + arrayFrom, |
| 6 | + BindingElement, |
| 7 | + Block, |
| 8 | + Bundle, |
| 9 | + CaseOrDefaultClause, |
| 10 | + chainBundle, |
| 11 | + ClassDeclaration, |
| 12 | + Debug, |
| 13 | + EmitFlags, |
| 14 | + ExportAssignment, |
| 15 | + ExportSpecifier, |
| 16 | + Expression, |
| 17 | + firstOrUndefined, |
| 18 | + forEachChild, |
| 19 | + ForOfStatement, |
| 20 | + ForStatement, |
| 21 | + GeneratedIdentifierFlags, |
| 22 | + getEmitFlags, |
| 23 | + hasSyntacticModifier, |
| 24 | + Identifier, |
| 25 | + IdentifierNameMap, |
| 26 | + isArray, |
| 27 | + isBindingPattern, |
| 28 | + isBlock, |
| 29 | + isCaseClause, |
| 30 | + isCustomPrologue, |
| 31 | + isExpression, |
| 32 | + isGeneratedIdentifier, |
| 33 | + isIdentifier, |
| 34 | + isLocalName, |
| 35 | + isNamedEvaluation, |
| 36 | + isOmittedExpression, |
| 37 | + isPrologueDirective, |
| 38 | + isSourceFile, |
| 39 | + isStatement, |
| 40 | + isVariableDeclaration, |
| 41 | + isVariableDeclarationList, |
| 42 | + isVariableStatement, |
| 43 | + ModifierFlags, |
| 44 | + Node, |
| 45 | + NodeFlags, |
| 46 | + setCommentRange, |
| 47 | + setEmitFlags, |
| 48 | + setOriginalNode, |
| 49 | + setSourceMapRange, |
| 50 | + setTextRange, |
| 51 | + skipOuterExpressions, |
| 52 | + SourceFile, |
| 53 | + Statement, |
| 54 | + SwitchStatement, |
| 55 | + SyntaxKind, |
| 56 | + TransformationContext, |
| 57 | + TransformFlags, |
| 58 | + transformNamedEvaluation, |
| 59 | + VariableDeclaration, |
| 60 | + VariableDeclarationList, |
| 61 | + VariableStatement, |
| 62 | + visitArray, |
| 63 | + visitEachChild, |
| 64 | + visitNode, |
| 65 | + visitNodes, |
| 66 | + VisitResult, |
65 | 67 | } from "../_namespaces/ts.js";
|
66 | 68 |
|
67 | 69 | const enum UsingKind {
|
@@ -289,54 +291,121 @@ export function transformESNext(context: TransformationContext): (x: SourceFile
|
289 | 291 | );
|
290 | 292 | }
|
291 | 293 |
|
292 |
| - return visitEachChild(node, visitor, context); |
293 |
| - } |
294 |
| - |
295 |
| - function visitForOfStatement(node: ForOfStatement) { |
296 |
| - if (isUsingVariableDeclarationList(node.initializer)) { |
297 |
| - // given: |
298 |
| - // |
299 |
| - // for (using x of y) { ... } |
300 |
| - // |
301 |
| - // produces a shallow transformation to: |
302 |
| - // |
303 |
| - // for (const x_1 of y) { |
304 |
| - // using x = x; |
305 |
| - // ... |
306 |
| - // } |
307 |
| - // |
308 |
| - // before handing the shallow transformation back to the visitor for an in-depth transformation. |
309 |
| - const forInitializer = node.initializer; |
310 |
| - const forDecl = firstOrUndefined(forInitializer.declarations) || factory.createVariableDeclaration(factory.createTempVariable(/*recordTempVariable*/ undefined)); |
311 |
| - |
312 |
| - const isAwaitUsing = getUsingKindOfVariableDeclarationList(forInitializer) === UsingKind.Async; |
313 |
| - const temp = factory.getGeneratedNameForNode(forDecl.name); |
314 |
| - const usingVar = factory.updateVariableDeclaration(forDecl, forDecl.name, /*exclamationToken*/ undefined, /*type*/ undefined, temp); |
315 |
| - const usingVarList = factory.createVariableDeclarationList([usingVar], isAwaitUsing ? NodeFlags.AwaitUsing : NodeFlags.Using); |
316 |
| - const usingVarStatement = factory.createVariableStatement(/*modifiers*/ undefined, usingVarList); |
317 |
| - return visitNode( |
318 |
| - factory.updateForOfStatement( |
319 |
| - node, |
320 |
| - node.awaitModifier, |
321 |
| - factory.createVariableDeclarationList([ |
322 |
| - factory.createVariableDeclaration(temp), |
323 |
| - ], NodeFlags.Const), |
324 |
| - node.expression, |
325 |
| - isBlock(node.statement) ? |
326 |
| - factory.updateBlock(node.statement, [ |
327 |
| - usingVarStatement, |
328 |
| - ...node.statement.statements, |
329 |
| - ]) : |
330 |
| - factory.createBlock([ |
331 |
| - usingVarStatement, |
332 |
| - node.statement, |
333 |
| - ], /*multiLine*/ true), |
334 |
| - ), |
335 |
| - visitor, |
336 |
| - isStatement, |
337 |
| - ); |
338 |
| - } |
339 |
| - return visitEachChild(node, visitor, context); |
| 294 | + return visitEachChild(node, visitor, context); |
| 295 | + } |
| 296 | + |
| 297 | + /** |
| 298 | + * Collects all variable declarations that shadow a given identifier name in a statement. |
| 299 | + */ |
| 300 | + function collectShadowingVariables(statement: Statement, shadowedName: string): VariableDeclaration[] { |
| 301 | + const shadowingVars: VariableDeclaration[] = []; |
| 302 | + |
| 303 | + function visit(node: Node): void { |
| 304 | + if (isVariableStatement(node)) { |
| 305 | + for (const declaration of node.declarationList.declarations) { |
| 306 | + if (isIdentifier(declaration.name) && declaration.name.escapedText === shadowedName) { |
| 307 | + shadowingVars.push(declaration); |
| 308 | + } |
| 309 | + } |
| 310 | + } |
| 311 | + forEachChild(node, visit); |
| 312 | + } |
| 313 | + |
| 314 | + visit(statement); |
| 315 | + return shadowingVars; |
| 316 | + } |
| 317 | + |
| 318 | + /** |
| 319 | + * Creates a visitor that renames shadowing variables to avoid conflicts. |
| 320 | + */ |
| 321 | + function createShadowingVariableRenamer(shadowedName: string): (node: Node) => VisitResult<Node> { |
| 322 | + const renamingMap = new Map<string, Identifier>(); |
| 323 | + |
| 324 | + return function renameShadowingVariables(node: Node): VisitResult<Node> { |
| 325 | + if (isVariableDeclaration(node) && isIdentifier(node.name) && node.name.escapedText === shadowedName) { |
| 326 | + // Create a unique name for this shadowing variable |
| 327 | + const uniqueName = factory.createUniqueName(shadowedName as string, GeneratedIdentifierFlags.Optimistic); |
| 328 | + renamingMap.set(node.name.escapedText as string, uniqueName); |
| 329 | + |
| 330 | + return factory.updateVariableDeclaration( |
| 331 | + node, |
| 332 | + uniqueName, |
| 333 | + node.exclamationToken, |
| 334 | + node.type, |
| 335 | + visitNode(node.initializer, renameShadowingVariables, isExpression) |
| 336 | + ); |
| 337 | + } |
| 338 | + |
| 339 | + if (isIdentifier(node)) { |
| 340 | + const renamed = renamingMap.get(node.escapedText as string); |
| 341 | + if (renamed) { |
| 342 | + return renamed; |
| 343 | + } |
| 344 | + } |
| 345 | + |
| 346 | + return visitEachChild(node, renameShadowingVariables, context); |
| 347 | + }; |
| 348 | + } |
| 349 | + |
| 350 | + function visitForOfStatement(node: ForOfStatement) { |
| 351 | + if (isUsingVariableDeclarationList(node.initializer)) { |
| 352 | + // given: |
| 353 | + // |
| 354 | + // for (using x of y) { ... } |
| 355 | + // |
| 356 | + // produces a shallow transformation to: |
| 357 | + // |
| 358 | + // for (const x_1 of y) { |
| 359 | + // using x = x; |
| 360 | + // ... |
| 361 | + // } |
| 362 | + // |
| 363 | + // before handing the shallow transformation back to the visitor for an in-depth transformation. |
| 364 | + const forInitializer = node.initializer; |
| 365 | + const forDecl = firstOrUndefined(forInitializer.declarations) || factory.createVariableDeclaration(factory.createTempVariable(/*recordTempVariable*/ undefined)); |
| 366 | + |
| 367 | + const isAwaitUsing = getUsingKindOfVariableDeclarationList(forInitializer) === UsingKind.Async; |
| 368 | + const temp = factory.getGeneratedNameForNode(forDecl.name); |
| 369 | + const usingVar = factory.updateVariableDeclaration(forDecl, forDecl.name, /*exclamationToken*/ undefined, /*type*/ undefined, temp); |
| 370 | + const usingVarList = factory.createVariableDeclarationList([usingVar], isAwaitUsing ? NodeFlags.AwaitUsing : NodeFlags.Using); |
| 371 | + const usingVarStatement = factory.createVariableStatement(/*modifiers*/ undefined, usingVarList); |
| 372 | + |
| 373 | + // Check if the loop body contains shadowing variables and rename them if necessary |
| 374 | + const shadowedName = isIdentifier(forDecl.name) ? forDecl.name.escapedText as string : undefined; |
| 375 | + let transformedStatement = node.statement; |
| 376 | + |
| 377 | + if (shadowedName) { |
| 378 | + const shadowingVars = collectShadowingVariables(node.statement, shadowedName); |
| 379 | + if (shadowingVars.length > 0) { |
| 380 | + // Apply the renaming visitor to the loop body |
| 381 | + const renamer = createShadowingVariableRenamer(shadowedName); |
| 382 | + transformedStatement = visitNode(node.statement, renamer, isStatement); |
| 383 | + } |
| 384 | + } |
| 385 | + |
| 386 | + return visitNode( |
| 387 | + factory.updateForOfStatement( |
| 388 | + node, |
| 389 | + node.awaitModifier, |
| 390 | + factory.createVariableDeclarationList([ |
| 391 | + factory.createVariableDeclaration(temp), |
| 392 | + ], NodeFlags.Const), |
| 393 | + node.expression, |
| 394 | + isBlock(transformedStatement) ? |
| 395 | + factory.updateBlock(transformedStatement, [ |
| 396 | + usingVarStatement, |
| 397 | + ...transformedStatement.statements, |
| 398 | + ]) : |
| 399 | + factory.createBlock([ |
| 400 | + usingVarStatement, |
| 401 | + transformedStatement, |
| 402 | + ], /*multiLine*/ true), |
| 403 | + ), |
| 404 | + visitor, |
| 405 | + isStatement, |
| 406 | + ); |
| 407 | + } |
| 408 | + return visitEachChild(node, visitor, context); |
340 | 409 | }
|
341 | 410 |
|
342 | 411 | function visitCaseOrDefaultClause(node: CaseOrDefaultClause, envBinding: Identifier) {
|
|
0 commit comments