@@ -14,17 +14,10 @@ namespace ts.codefix {
14
14
getAllCodeActions : context => codeFixAll ( context , errorCodes , ( changes , err ) => convertToAsyncFunction ( changes , err . file , err . start , context . program . getTypeChecker ( ) , context ) ) ,
15
15
} ) ;
16
16
17
-
18
- /*
19
- custom type to encapsulate information for variable declarations synthesized in the refactor
20
- numberOfUsesOriginal - number of times the variable should be assigned in the refactor
21
- numberOfUsesSynthesized - count of how many times the variable has been assigned so far
22
- At the end of the refactor, numberOfUsesOriginal should === numberOfUsesSynthesized
23
- */
24
17
interface SynthIdentifier {
25
18
identifier : Identifier ;
26
19
types : Type [ ] ;
27
- numberOfAssignmentsOriginal : number ;
20
+ numberOfAssignmentsOriginal : number ; // number of times the variable should be assigned in the refactor
28
21
}
29
22
30
23
interface SymbolAndIdentifier {
@@ -179,9 +172,9 @@ namespace ts.codefix {
179
172
180
173
// 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))
181
174
// Note - the choice of the last call signature is arbitrary
182
- if ( lastCallSignature && lastCallSignature . parameters . length && ! synthNamesMap . has ( symbolIdString ) ) {
183
- const firstParameter = lastCallSignature . parameters [ 0 ] ;
184
- const ident = isParameter ( firstParameter . valueDeclaration ) && tryCast ( firstParameter . valueDeclaration . name , isIdentifier ) || createOptimisticUniqueName ( "result" ) ;
175
+ if ( lastCallSignature && ! isFunctionLikeDeclaration ( node . parent ) && ! synthNamesMap . has ( symbolIdString ) ) {
176
+ const firstParameter = firstOrUndefined ( lastCallSignature . parameters ) ;
177
+ const ident = firstParameter && isParameter ( firstParameter . valueDeclaration ) && tryCast ( firstParameter . valueDeclaration . name , isIdentifier ) || createOptimisticUniqueName ( "result" ) ;
185
178
const synthName = getNewNameIfConflict ( ident , collidingSymbolMap ) ;
186
179
synthNamesMap . set ( symbolIdString , synthName ) ;
187
180
allVarNames . push ( { identifier : synthName . identifier , symbol } ) ;
@@ -234,9 +227,7 @@ namespace ts.codefix {
234
227
235
228
if ( renameInfo ) {
236
229
const type = checker . getTypeAtLocation ( node ) ;
237
- if ( type ) {
238
- originalType . set ( getNodeId ( clone ) . toString ( ) , type ) ;
239
- }
230
+ originalType . set ( getNodeId ( clone ) . toString ( ) , type ) ;
240
231
}
241
232
}
242
233
@@ -320,7 +311,7 @@ namespace ts.codefix {
320
311
const tryBlock = createBlock ( transformExpression ( node . expression , transformer , node , prevArgName ) ) ;
321
312
322
313
const transformationBody = getTransformationBody ( func , prevArgName , argName , node , transformer ) ;
323
- const catchArg = argName . identifier . text . length > 0 ? argName . identifier . text : "e" ;
314
+ const catchArg = argName ? argName . identifier . text : "e" ;
324
315
const catchClause = createCatchClause ( catchArg , createBlock ( transformationBody ) ) ;
325
316
326
317
/*
@@ -362,7 +353,7 @@ namespace ts.codefix {
362
353
363
354
const transformationBody2 = getTransformationBody ( rej , prevArgName , argNameRej , node , transformer ) ;
364
355
365
- const catchArg = argNameRej . identifier . text . length > 0 ? argNameRej . identifier . text : "e" ;
356
+ const catchArg = argNameRej ? argNameRej . identifier . text : "e" ;
366
357
const catchClause = createCatchClause ( catchArg , createBlock ( transformationBody2 ) ) ;
367
358
368
359
return [ createTry ( tryBlock , catchClause , /* finallyBlock */ undefined ) as Statement ] ;
@@ -379,21 +370,25 @@ namespace ts.codefix {
379
370
function transformPromiseCall ( node : Expression , transformer : Transformer , prevArgName ?: SynthIdentifier ) : Statement [ ] {
380
371
const shouldReturn = transformer . setOfExpressionsToReturn . get ( getNodeId ( node ) . toString ( ) ) ;
381
372
// the identifier is empty when the handler (.then()) ignores the argument - In this situation we do not need to save the result of the promise returning call
382
- const hasPrevArgName = prevArgName && prevArgName . identifier . text . length > 0 ;
383
373
const originalNodeParent = node . original ? node . original . parent : node . parent ;
384
- if ( hasPrevArgName && ! shouldReturn && ( ! originalNodeParent || isPropertyAccessExpression ( originalNodeParent ) ) ) {
385
- return createVariableDeclarationOrAssignment ( prevArgName ! , createAwait ( node ) , transformer ) . concat ( ) ; // hack to make the types match
374
+ if ( prevArgName && ! shouldReturn && ( ! originalNodeParent || isPropertyAccessExpression ( originalNodeParent ) ) ) {
375
+ return createTransformedStatement ( prevArgName , createAwait ( node ) , transformer ) . concat ( ) ; // hack to make the types match
386
376
}
387
- else if ( ! hasPrevArgName && ! shouldReturn && ( ! originalNodeParent || isPropertyAccessExpression ( originalNodeParent ) ) ) {
377
+ else if ( ! prevArgName && ! shouldReturn && ( ! originalNodeParent || isPropertyAccessExpression ( originalNodeParent ) ) ) {
388
378
return [ createStatement ( createAwait ( node ) ) ] ;
389
379
}
390
380
391
381
return [ createReturn ( getSynthesizedDeepClone ( node ) ) ] ;
392
382
}
393
383
394
- function createVariableDeclarationOrAssignment ( prevArgName : SynthIdentifier , rightHandSide : Expression , transformer : Transformer ) : NodeArray < Statement > {
384
+ function createTransformedStatement ( prevArgName : SynthIdentifier | undefined , rightHandSide : Expression , transformer : Transformer ) : NodeArray < Statement > {
385
+ if ( ! prevArgName || prevArgName . identifier . text . length === 0 ) {
386
+ // if there's no argName to assign to, there still might be side effects
387
+ return createNodeArray ( [ createStatement ( rightHandSide ) ] ) ;
388
+ }
395
389
396
390
if ( prevArgName . types . length < prevArgName . numberOfAssignmentsOriginal ) {
391
+ // if the variable has already been declared, we don't need "let" or "const"
397
392
return createNodeArray ( [ createStatement ( createAssignment ( getSynthesizedDeepClone ( prevArgName . identifier ) , rightHandSide ) ) ] ) ;
398
393
}
399
394
@@ -402,31 +397,33 @@ namespace ts.codefix {
402
397
}
403
398
404
399
// should be kept up to date with isFixablePromiseArgument in suggestionDiagnostics.ts
405
- function getTransformationBody ( func : Expression , prevArgName : SynthIdentifier | undefined , argName : SynthIdentifier , parent : CallExpression , transformer : Transformer ) : NodeArray < Statement > {
400
+ function getTransformationBody ( func : Expression , prevArgName : SynthIdentifier | undefined , argName : SynthIdentifier | undefined , parent : CallExpression , transformer : Transformer ) : NodeArray < Statement > {
406
401
407
- const hasPrevArgName = prevArgName && prevArgName . identifier . text . length > 0 ;
408
- const hasArgName = argName && argName . identifier . text . length > 0 ;
409
402
const shouldReturn = transformer . setOfExpressionsToReturn . get ( getNodeId ( parent ) . toString ( ) ) ;
410
403
switch ( func . kind ) {
411
404
case SyntaxKind . NullKeyword :
412
405
// do not produce a transformed statement for a null argument
413
406
break ;
414
- case SyntaxKind . Identifier :
415
- // identifier includes undefined
416
- if ( ! hasArgName ) break ;
407
+ case SyntaxKind . Identifier : // identifier includes undefined
408
+ if ( ! argName ) break ;
417
409
418
- const synthCall = createCall ( getSynthesizedDeepClone ( func ) as Identifier , /*typeArguments*/ undefined , [ argName . identifier ] ) ;
410
+ const synthCall = createCall ( getSynthesizedDeepClone ( func ) as Identifier , /*typeArguments*/ undefined , argName ? [ argName . identifier ] : [ ] ) ;
419
411
if ( shouldReturn ) {
420
412
return createNodeArray ( [ createReturn ( synthCall ) ] ) ;
421
413
}
422
414
423
- if ( ! hasPrevArgName ) break ;
424
-
425
- const type = transformer . originalTypeMap . get ( getNodeId ( func ) . toString ( ) ) ;
426
- const callSignatures = type && transformer . checker . getSignaturesOfType ( type , SignatureKind . Call ) ;
427
- const returnType = callSignatures && callSignatures [ 0 ] . getReturnType ( ) ;
428
- const varDeclOrAssignment = createVariableDeclarationOrAssignment ( prevArgName ! , createAwait ( synthCall ) , transformer ) ;
429
- prevArgName ! . types . push ( returnType ! ) ;
415
+ const type = transformer . originalTypeMap . get ( getNodeId ( func ) . toString ( ) ) || transformer . checker . getTypeAtLocation ( func ) ;
416
+ const callSignatures = transformer . checker . getSignaturesOfType ( type , SignatureKind . Call ) ;
417
+ if ( ! callSignatures . length ) {
418
+ // if identifier in handler has no call signatures, it's invalid
419
+ codeActionSucceeded = false ;
420
+ break ;
421
+ }
422
+ const returnType = callSignatures [ 0 ] . getReturnType ( ) ;
423
+ const varDeclOrAssignment = createTransformedStatement ( prevArgName , createAwait ( synthCall ) , transformer ) ;
424
+ if ( prevArgName ) {
425
+ prevArgName . types . push ( returnType ) ;
426
+ }
430
427
return varDeclOrAssignment ;
431
428
432
429
case SyntaxKind . FunctionExpression :
@@ -451,7 +448,7 @@ namespace ts.codefix {
451
448
}
452
449
453
450
return shouldReturn ? getSynthesizedDeepClones ( createNodeArray ( refactoredStmts ) ) :
454
- removeReturns ( createNodeArray ( refactoredStmts ) , prevArgName ! . identifier , transformer . constIdentifiers , seenReturnStatement ) ;
451
+ removeReturns ( createNodeArray ( refactoredStmts ) , prevArgName ! . identifier , transformer , seenReturnStatement ) ;
455
452
}
456
453
else {
457
454
const innerRetStmts = getReturnStatementsWithPromiseHandlers ( createReturn ( funcBody ) ) ;
@@ -461,20 +458,24 @@ namespace ts.codefix {
461
458
return createNodeArray ( innerCbBody ) ;
462
459
}
463
460
464
- if ( hasPrevArgName && ! shouldReturn ) {
461
+ if ( ! shouldReturn ) {
465
462
const type = transformer . checker . getTypeAtLocation ( func ) ;
466
463
const returnType = getLastCallSignature ( type , transformer . checker ) ! . getReturnType ( ) ;
467
- const varDeclOrAssignment = createVariableDeclarationOrAssignment ( prevArgName ! , getSynthesizedDeepClone ( funcBody ) , transformer ) ;
468
- prevArgName ! . types . push ( returnType ) ;
469
- return varDeclOrAssignment ;
464
+ const rightHandSide = getSynthesizedDeepClone ( funcBody ) ;
465
+ const possiblyAwaitedRightHandSide = ! ! transformer . checker . getPromisedTypeOfPromise ( returnType ) ? createAwait ( rightHandSide ) : rightHandSide ;
466
+ const transformedStatement = createTransformedStatement ( prevArgName , possiblyAwaitedRightHandSide , transformer ) ;
467
+ if ( prevArgName ) {
468
+ prevArgName . types . push ( returnType ) ;
469
+ }
470
+ return transformedStatement ;
470
471
}
471
472
else {
472
473
return createNodeArray ( [ createReturn ( getSynthesizedDeepClone ( funcBody ) ) ] ) ;
473
474
}
474
475
}
475
476
}
476
477
default :
477
- // We 've found a transformation body we don't know how to handle, so the refactoring should no-op to avoid deleting code.
478
+ // If no cases apply, we 've found a transformation body we don't know how to handle, so the refactoring should no-op to avoid deleting code.
478
479
codeActionSucceeded = false ;
479
480
break ;
480
481
}
@@ -487,13 +488,14 @@ namespace ts.codefix {
487
488
}
488
489
489
490
490
- function removeReturns ( stmts : NodeArray < Statement > , prevArgName : Identifier , constIdentifiers : Identifier [ ] , seenReturnStatement : boolean ) : NodeArray < Statement > {
491
+ function removeReturns ( stmts : NodeArray < Statement > , prevArgName : Identifier , transformer : Transformer , seenReturnStatement : boolean ) : NodeArray < Statement > {
491
492
const ret : Statement [ ] = [ ] ;
492
493
for ( const stmt of stmts ) {
493
494
if ( isReturnStatement ( stmt ) ) {
494
495
if ( stmt . expression ) {
496
+ const possiblyAwaitedExpression = isPromiseReturningExpression ( stmt . expression , transformer . checker ) ? createAwait ( stmt . expression ) : stmt . expression ;
495
497
ret . push ( createVariableStatement ( /*modifiers*/ undefined ,
496
- ( createVariableDeclarationList ( [ createVariableDeclaration ( prevArgName , /*type*/ undefined , stmt . expression ) ] , getFlagOfIdentifier ( prevArgName , constIdentifiers ) ) ) ) ) ;
498
+ ( createVariableDeclarationList ( [ createVariableDeclaration ( prevArgName , /*type*/ undefined , possiblyAwaitedExpression ) ] , getFlagOfIdentifier ( prevArgName , transformer . constIdentifiers ) ) ) ) ) ;
497
499
}
498
500
}
499
501
else {
@@ -504,7 +506,7 @@ namespace ts.codefix {
504
506
// if block has no return statement, need to define prevArgName as undefined to prevent undeclared variables
505
507
if ( ! seenReturnStatement ) {
506
508
ret . push ( createVariableStatement ( /*modifiers*/ undefined ,
507
- ( createVariableDeclarationList ( [ createVariableDeclaration ( prevArgName , /*type*/ undefined , createIdentifier ( "undefined" ) ) ] , getFlagOfIdentifier ( prevArgName , constIdentifiers ) ) ) ) ) ;
509
+ ( createVariableDeclarationList ( [ createVariableDeclaration ( prevArgName , /*type*/ undefined , createIdentifier ( "undefined" ) ) ] , getFlagOfIdentifier ( prevArgName , transformer . constIdentifiers ) ) ) ) ) ;
508
510
}
509
511
510
512
return createNodeArray ( ret ) ;
@@ -531,7 +533,7 @@ namespace ts.codefix {
531
533
return innerCbBody ;
532
534
}
533
535
534
- function getArgName ( funcNode : Node , transformer : Transformer ) : SynthIdentifier {
536
+ function getArgName ( funcNode : Node , transformer : Transformer ) : SynthIdentifier | undefined {
535
537
536
538
const numberOfAssignmentsOriginal = 0 ;
537
539
const types : Type [ ] = [ ] ;
@@ -548,8 +550,8 @@ namespace ts.codefix {
548
550
name = getMapEntryIfExists ( funcNode ) ;
549
551
}
550
552
551
- if ( ! name || name . identifier === undefined || name . identifier . text === "_" || name . identifier . text === " undefined") {
552
- return { identifier : createIdentifier ( "" ) , types , numberOfAssignmentsOriginal } ;
553
+ if ( ! name || name . identifier === undefined || name . identifier . text === "undefined" ) {
554
+ return undefined ;
553
555
}
554
556
555
557
return name ;
0 commit comments