@@ -83,19 +83,14 @@ namespace ts.codefix {
8383 }
8484
8585 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+ } ) ;
9994 }
10095 }
10196
@@ -167,6 +162,7 @@ namespace ts.codefix {
167162 function renameCollidingVarNames ( nodeToRename : FunctionLikeDeclaration , checker : TypeChecker , synthNamesMap : Map < SynthIdentifier > , context : CodeFixContextBase , setOfAllExpressionsToReturn : Map < true > , originalType : Map < Type > , allVarNames : SymbolAndIdentifier [ ] ) : FunctionLikeDeclaration {
168163
169164 const identsToRenameMap : Map < Identifier > = createMap ( ) ; // key is the symbol id
165+ const collidingSymbolMap : Map < Symbol [ ] > = createMap ( ) ;
170166 forEachChild ( nodeToRename , function visit ( node : Node ) {
171167 if ( ! isIdentifier ( node ) ) {
172168 forEachChild ( node , visit ) ;
@@ -184,26 +180,33 @@ namespace ts.codefix {
184180 // 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))
185181 // Note - the choice of the last call signature is arbitrary
186182 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 ) ;
188186 synthNamesMap . set ( symbolIdString , synthName ) ;
189187 allVarNames . push ( { identifier : synthName . identifier , symbol } ) ;
188+ addNameToFrequencyMap ( collidingSymbolMap , ident . text , symbol ) ;
190189 }
191190 // we only care about identifiers that are parameters and declarations (don't care about other uses)
192191 else if ( node . parent && ( isParameter ( node . parent ) || isVariableDeclaration ( node . parent ) ) ) {
192+ const originalName = node . text ;
193+ const collidingSymbols = collidingSymbolMap . get ( originalName ) ;
193194
194195 // 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 ) ;
197198 identsToRenameMap . set ( symbolIdString , newName . identifier ) ;
198199 synthNamesMap . set ( symbolIdString , newName ) ;
199200 allVarNames . push ( { identifier : newName . identifier , symbol } ) ;
201+ addNameToFrequencyMap ( collidingSymbolMap , originalName , symbol ) ;
200202 }
201203 else {
202204 const identifier = getSynthesizedDeepClone ( node ) ;
203205 identsToRenameMap . set ( symbolIdString , identifier ) ;
204206 synthNamesMap . set ( symbolIdString , { identifier, types : [ ] , numberOfAssignmentsOriginal : allVarNames . filter ( elem => elem . identifier . text === node . text ) . length /*, numberOfAssignmentsSynthesized: 0*/ } ) ;
205207 if ( ( isParameter ( node . parent ) && isExpressionOrCallOnTypePromise ( node . parent . parent ) ) || isVariableDeclaration ( node . parent ) ) {
206208 allVarNames . push ( { identifier, symbol } ) ;
209+ addNameToFrequencyMap ( collidingSymbolMap , originalName , symbol ) ;
207210 }
208211 }
209212 }
@@ -246,8 +249,17 @@ namespace ts.codefix {
246249
247250 }
248251
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 ;
251263 const numberOfAssignmentsOriginal = 0 ;
252264 const identifier = numVarsSameName === 0 ? name : createIdentifier ( name . text + "_" + numVarsSameName ) ;
253265 return { identifier, types : [ ] , numberOfAssignmentsOriginal } ;
@@ -294,13 +306,14 @@ namespace ts.codefix {
294306 prevArgName . numberOfAssignmentsOriginal = 2 ; // Try block and catch block
295307 transformer . synthNamesMap . forEach ( ( val , key ) => {
296308 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 ) ;
298311 }
299312 } ) ;
300313
301314 // update the constIdentifiers list
302315 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 ) ;
304317 }
305318 }
306319
@@ -326,6 +339,12 @@ namespace ts.codefix {
326339 return varDeclList ? [ varDeclList , tryStatement ] : [ tryStatement ] ;
327340 }
328341
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+
329348 function transformThen ( node : CallExpression , transformer : Transformer , outermostParent : CallExpression , prevArgName ?: SynthIdentifier ) : Statement [ ] {
330349 const [ res , rej ] = node . arguments ;
331350
@@ -348,11 +367,8 @@ namespace ts.codefix {
348367
349368 return [ createTry ( tryBlock , catchClause , /* finallyBlock */ undefined ) as Statement ] ;
350369 }
351- else {
352- return transformExpression ( node . expression , transformer , node , argNameRes ) . concat ( transformationBody ) ;
353- }
354370
355- return [ ] ;
371+ return transformExpression ( node . expression , transformer , node , argNameRes ) . concat ( transformationBody ) ;
356372 }
357373
358374 function getFlagOfIdentifier ( node : Identifier , constIdentifiers : Identifier [ ] ) : NodeFlags {
@@ -419,8 +435,13 @@ namespace ts.codefix {
419435 // Arrow functions with block bodies { } will enter this control flow
420436 if ( isFunctionLikeDeclaration ( func ) && func . body && isBlock ( func . body ) && func . body . statements ) {
421437 let refactoredStmts : Statement [ ] = [ ] ;
438+ let seenReturnStatement = false ;
422439
423440 for ( const statement of func . body . statements ) {
441+ if ( isReturnStatement ( statement ) ) {
442+ seenReturnStatement = true ;
443+ }
444+
424445 if ( getReturnStatementsWithPromiseHandlers ( statement ) . length ) {
425446 refactoredStmts = refactoredStmts . concat ( getInnerTransformationBody ( transformer , [ statement ] , prevArgName ) ) ;
426447 }
@@ -430,7 +451,7 @@ namespace ts.codefix {
430451 }
431452
432453 return shouldReturn ? getSynthesizedDeepClones ( createNodeArray ( refactoredStmts ) ) :
433- removeReturns ( createNodeArray ( refactoredStmts ) , prevArgName ! . identifier , transformer . constIdentifiers ) ;
454+ removeReturns ( createNodeArray ( refactoredStmts ) , prevArgName ! . identifier , transformer . constIdentifiers , seenReturnStatement ) ;
434455 }
435456 else {
436457 const funcBody = ( < ArrowFunction > func ) . body ;
@@ -443,7 +464,7 @@ namespace ts.codefix {
443464
444465 if ( hasPrevArgName && ! shouldReturn ) {
445466 const type = transformer . checker . getTypeAtLocation ( func ) ;
446- const returnType = getLastCallSignature ( type , transformer . checker ) . getReturnType ( ) ;
467+ const returnType = getLastCallSignature ( type , transformer . checker ) ! . getReturnType ( ) ;
447468 const varDeclOrAssignment = createVariableDeclarationOrAssignment ( prevArgName ! , getSynthesizedDeepClone ( funcBody ) as Expression , transformer ) ;
448469 prevArgName ! . types . push ( returnType ) ;
449470 return varDeclOrAssignment ;
@@ -460,13 +481,13 @@ namespace ts.codefix {
460481 return createNodeArray ( [ ] ) ;
461482 }
462483
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 ) ;
466487 }
467488
468489
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 > {
470491 const ret : Statement [ ] = [ ] ;
471492 for ( const stmt of stmts ) {
472493 if ( isReturnStatement ( stmt ) ) {
@@ -480,6 +501,12 @@ namespace ts.codefix {
480501 }
481502 }
482503
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+
483510 return createNodeArray ( ret ) ;
484511 }
485512
@@ -517,9 +544,6 @@ namespace ts.codefix {
517544 name = getMapEntryIfExists ( param ) ;
518545 }
519546 }
520- else if ( isCallExpression ( funcNode ) && funcNode . arguments . length > 0 && isIdentifier ( funcNode . arguments [ 0 ] ) ) {
521- name = { identifier : funcNode . arguments [ 0 ] as Identifier , types, numberOfAssignmentsOriginal } ;
522- }
523547 else if ( isIdentifier ( funcNode ) ) {
524548 name = getMapEntryIfExists ( funcNode ) ;
525549 }
0 commit comments