@@ -83,19 +83,14 @@ namespace ts.codefix {
83
83
}
84
84
85
85
for ( const statement of returnStatements ) {
86
- if ( isCallExpression ( statement ) ) {
87
- startTransformation ( statement , statement ) ;
88
- }
89
- else {
90
- forEachChild ( statement , function visit ( node : Node ) {
91
- if ( isCallExpression ( node ) ) {
92
- startTransformation ( node , statement ) ;
93
- }
94
- else if ( ! isFunctionLike ( node ) ) {
95
- forEachChild ( node , visit ) ;
96
- }
97
- } ) ;
98
- }
86
+ forEachChild ( statement , function visit ( node ) {
87
+ if ( isCallExpression ( node ) ) {
88
+ startTransformation ( node , statement ) ;
89
+ }
90
+ else if ( ! isFunctionLike ( node ) ) {
91
+ forEachChild ( node , visit ) ;
92
+ }
93
+ } ) ;
99
94
}
100
95
}
101
96
@@ -167,6 +162,7 @@ namespace ts.codefix {
167
162
function renameCollidingVarNames ( nodeToRename : FunctionLikeDeclaration , checker : TypeChecker , synthNamesMap : Map < SynthIdentifier > , context : CodeFixContextBase , setOfAllExpressionsToReturn : Map < true > , originalType : Map < Type > , allVarNames : SymbolAndIdentifier [ ] ) : FunctionLikeDeclaration {
168
163
169
164
const identsToRenameMap : Map < Identifier > = createMap ( ) ; // key is the symbol id
165
+ const collidingSymbolMap : Map < Symbol [ ] > = createMap ( ) ;
170
166
forEachChild ( nodeToRename , function visit ( node : Node ) {
171
167
if ( ! isIdentifier ( node ) ) {
172
168
forEachChild ( node , visit ) ;
@@ -184,26 +180,33 @@ namespace ts.codefix {
184
180
// if the identifier refers to a function we want to add the new synthesized variable for the declaration (ex. blob in let blob = res(arg))
185
181
// Note - the choice of the last call signature is arbitrary
186
182
if ( lastCallSignature && lastCallSignature . parameters . length && ! synthNamesMap . has ( symbolIdString ) ) {
187
- const synthName = getNewNameIfConflict ( createIdentifier ( lastCallSignature . parameters [ 0 ] . name ) , allVarNames ) ;
183
+ const firstParameter = lastCallSignature . parameters [ 0 ] ;
184
+ const ident = isParameter ( firstParameter . valueDeclaration ) && tryCast ( firstParameter . valueDeclaration . name , isIdentifier ) || createOptimisticUniqueName ( "result" ) ;
185
+ const synthName = getNewNameIfConflict ( ident , collidingSymbolMap ) ;
188
186
synthNamesMap . set ( symbolIdString , synthName ) ;
189
187
allVarNames . push ( { identifier : synthName . identifier , symbol } ) ;
188
+ addNameToFrequencyMap ( collidingSymbolMap , ident . text , symbol ) ;
190
189
}
191
190
// we only care about identifiers that are parameters and declarations (don't care about other uses)
192
191
else if ( node . parent && ( isParameter ( node . parent ) || isVariableDeclaration ( node . parent ) ) ) {
192
+ const originalName = node . text ;
193
+ const collidingSymbols = collidingSymbolMap . get ( originalName ) ;
193
194
194
195
// if the identifier name conflicts with a different identifier that we've already seen
195
- if ( allVarNames . some ( ident => ident . identifier . text === node . text && ident . symbol !== symbol ) ) {
196
- const newName = getNewNameIfConflict ( node , allVarNames ) ;
196
+ if ( collidingSymbols && collidingSymbols . some ( prevSymbol => prevSymbol !== symbol ) ) {
197
+ const newName = getNewNameIfConflict ( node , collidingSymbolMap ) ;
197
198
identsToRenameMap . set ( symbolIdString , newName . identifier ) ;
198
199
synthNamesMap . set ( symbolIdString , newName ) ;
199
200
allVarNames . push ( { identifier : newName . identifier , symbol } ) ;
201
+ addNameToFrequencyMap ( collidingSymbolMap , originalName , symbol ) ;
200
202
}
201
203
else {
202
204
const identifier = getSynthesizedDeepClone ( node ) ;
203
205
identsToRenameMap . set ( symbolIdString , identifier ) ;
204
206
synthNamesMap . set ( symbolIdString , { identifier, types : [ ] , numberOfAssignmentsOriginal : allVarNames . filter ( elem => elem . identifier . text === node . text ) . length /*, numberOfAssignmentsSynthesized: 0*/ } ) ;
205
207
if ( ( isParameter ( node . parent ) && isExpressionOrCallOnTypePromise ( node . parent . parent ) ) || isVariableDeclaration ( node . parent ) ) {
206
208
allVarNames . push ( { identifier, symbol } ) ;
209
+ addNameToFrequencyMap ( collidingSymbolMap , originalName , symbol ) ;
207
210
}
208
211
}
209
212
}
@@ -246,8 +249,17 @@ namespace ts.codefix {
246
249
247
250
}
248
251
249
- function getNewNameIfConflict ( name : Identifier , allVarNames : SymbolAndIdentifier [ ] ) : SynthIdentifier {
250
- const numVarsSameName = allVarNames . filter ( elem => elem . identifier . text === name . text ) . length ;
252
+ function addNameToFrequencyMap ( renamedVarNameFrequencyMap : Map < Symbol [ ] > , originalName : string , symbol : Symbol ) {
253
+ if ( renamedVarNameFrequencyMap . has ( originalName ) ) {
254
+ renamedVarNameFrequencyMap . get ( originalName ) ! . push ( symbol ) ;
255
+ }
256
+ else {
257
+ renamedVarNameFrequencyMap . set ( originalName , [ symbol ] ) ;
258
+ }
259
+ }
260
+
261
+ function getNewNameIfConflict ( name : Identifier , originalNames : Map < Symbol [ ] > ) : SynthIdentifier {
262
+ const numVarsSameName = ( originalNames . get ( name . text ) || [ ] ) . length ;
251
263
const numberOfAssignmentsOriginal = 0 ;
252
264
const identifier = numVarsSameName === 0 ? name : createIdentifier ( name . text + "_" + numVarsSameName ) ;
253
265
return { identifier, types : [ ] , numberOfAssignmentsOriginal } ;
@@ -294,13 +306,14 @@ namespace ts.codefix {
294
306
prevArgName . numberOfAssignmentsOriginal = 2 ; // Try block and catch block
295
307
transformer . synthNamesMap . forEach ( ( val , key ) => {
296
308
if ( val . identifier . text === prevArgName . identifier . text ) {
297
- transformer . synthNamesMap . set ( key , getNewNameIfConflict ( prevArgName . identifier , transformer . allVarNames ) ) ;
309
+ const newSynthName = createUniqueSynthName ( prevArgName ) ;
310
+ transformer . synthNamesMap . set ( key , newSynthName ) ;
298
311
}
299
312
} ) ;
300
313
301
314
// update the constIdentifiers list
302
315
if ( transformer . constIdentifiers . some ( elem => elem . text === prevArgName . identifier . text ) ) {
303
- transformer . constIdentifiers . push ( getNewNameIfConflict ( prevArgName . identifier , transformer . allVarNames ) . identifier ) ;
316
+ transformer . constIdentifiers . push ( createUniqueSynthName ( prevArgName ) . identifier ) ;
304
317
}
305
318
}
306
319
@@ -326,6 +339,12 @@ namespace ts.codefix {
326
339
return varDeclList ? [ varDeclList , tryStatement ] : [ tryStatement ] ;
327
340
}
328
341
342
+ function createUniqueSynthName ( prevArgName : SynthIdentifier ) {
343
+ const renamedPrevArg = createOptimisticUniqueName ( prevArgName . identifier . text ) ;
344
+ const newSynthName = { identifier : renamedPrevArg , types : [ ] , numberOfAssignmentsOriginal : 0 } ;
345
+ return newSynthName ;
346
+ }
347
+
329
348
function transformThen ( node : CallExpression , transformer : Transformer , outermostParent : CallExpression , prevArgName ?: SynthIdentifier ) : Statement [ ] {
330
349
const [ res , rej ] = node . arguments ;
331
350
@@ -348,11 +367,8 @@ namespace ts.codefix {
348
367
349
368
return [ createTry ( tryBlock , catchClause , /* finallyBlock */ undefined ) as Statement ] ;
350
369
}
351
- else {
352
- return transformExpression ( node . expression , transformer , node , argNameRes ) . concat ( transformationBody ) ;
353
- }
354
370
355
- return [ ] ;
371
+ return transformExpression ( node . expression , transformer , node , argNameRes ) . concat ( transformationBody ) ;
356
372
}
357
373
358
374
function getFlagOfIdentifier ( node : Identifier , constIdentifiers : Identifier [ ] ) : NodeFlags {
@@ -419,8 +435,13 @@ namespace ts.codefix {
419
435
// Arrow functions with block bodies { } will enter this control flow
420
436
if ( isFunctionLikeDeclaration ( func ) && func . body && isBlock ( func . body ) && func . body . statements ) {
421
437
let refactoredStmts : Statement [ ] = [ ] ;
438
+ let seenReturnStatement = false ;
422
439
423
440
for ( const statement of func . body . statements ) {
441
+ if ( isReturnStatement ( statement ) ) {
442
+ seenReturnStatement = true ;
443
+ }
444
+
424
445
if ( getReturnStatementsWithPromiseHandlers ( statement ) . length ) {
425
446
refactoredStmts = refactoredStmts . concat ( getInnerTransformationBody ( transformer , [ statement ] , prevArgName ) ) ;
426
447
}
@@ -430,7 +451,7 @@ namespace ts.codefix {
430
451
}
431
452
432
453
return shouldReturn ? getSynthesizedDeepClones ( createNodeArray ( refactoredStmts ) ) :
433
- removeReturns ( createNodeArray ( refactoredStmts ) , prevArgName ! . identifier , transformer . constIdentifiers ) ;
454
+ removeReturns ( createNodeArray ( refactoredStmts ) , prevArgName ! . identifier , transformer . constIdentifiers , seenReturnStatement ) ;
434
455
}
435
456
else {
436
457
const funcBody = ( < ArrowFunction > func ) . body ;
@@ -443,7 +464,7 @@ namespace ts.codefix {
443
464
444
465
if ( hasPrevArgName && ! shouldReturn ) {
445
466
const type = transformer . checker . getTypeAtLocation ( func ) ;
446
- const returnType = getLastCallSignature ( type , transformer . checker ) . getReturnType ( ) ;
467
+ const returnType = getLastCallSignature ( type , transformer . checker ) ! . getReturnType ( ) ;
447
468
const varDeclOrAssignment = createVariableDeclarationOrAssignment ( prevArgName ! , getSynthesizedDeepClone ( funcBody ) as Expression , transformer ) ;
448
469
prevArgName ! . types . push ( returnType ) ;
449
470
return varDeclOrAssignment ;
@@ -460,13 +481,13 @@ namespace ts.codefix {
460
481
return createNodeArray ( [ ] ) ;
461
482
}
462
483
463
- function getLastCallSignature ( type : Type , checker : TypeChecker ) : Signature {
464
- const callSignatures = type && checker . getSignaturesOfType ( type , SignatureKind . Call ) ;
465
- return callSignatures && callSignatures [ callSignatures . length - 1 ] ;
484
+ function getLastCallSignature ( type : Type , checker : TypeChecker ) : Signature | undefined {
485
+ const callSignatures = checker . getSignaturesOfType ( type , SignatureKind . Call ) ;
486
+ return lastOrUndefined ( callSignatures ) ;
466
487
}
467
488
468
489
469
- function removeReturns ( stmts : NodeArray < Statement > , prevArgName : Identifier , constIdentifiers : Identifier [ ] ) : NodeArray < Statement > {
490
+ function removeReturns ( stmts : NodeArray < Statement > , prevArgName : Identifier , constIdentifiers : Identifier [ ] , seenReturnStatement : boolean ) : NodeArray < Statement > {
470
491
const ret : Statement [ ] = [ ] ;
471
492
for ( const stmt of stmts ) {
472
493
if ( isReturnStatement ( stmt ) ) {
@@ -480,6 +501,12 @@ namespace ts.codefix {
480
501
}
481
502
}
482
503
504
+ // if block has no return statement, need to define prevArgName as undefined to prevent undeclared variables
505
+ if ( ! seenReturnStatement ) {
506
+ ret . push ( createVariableStatement ( /*modifiers*/ undefined ,
507
+ ( createVariableDeclarationList ( [ createVariableDeclaration ( prevArgName , /*type*/ undefined , createIdentifier ( "undefined" ) ) ] , getFlagOfIdentifier ( prevArgName , constIdentifiers ) ) ) ) ) ;
508
+ }
509
+
483
510
return createNodeArray ( ret ) ;
484
511
}
485
512
@@ -517,9 +544,6 @@ namespace ts.codefix {
517
544
name = getMapEntryIfExists ( param ) ;
518
545
}
519
546
}
520
- else if ( isCallExpression ( funcNode ) && funcNode . arguments . length > 0 && isIdentifier ( funcNode . arguments [ 0 ] ) ) {
521
- name = { identifier : funcNode . arguments [ 0 ] as Identifier , types, numberOfAssignmentsOriginal } ;
522
- }
523
547
else if ( isIdentifier ( funcNode ) ) {
524
548
name = getMapEntryIfExists ( funcNode ) ;
525
549
}
0 commit comments