|
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