@@ -15,24 +15,24 @@ namespace ts.codefix {
15
15
} ) ;
16
16
17
17
interface SynthIdentifier {
18
- identifier : Identifier ;
19
- types : Type [ ] ;
18
+ readonly identifier : Identifier ;
19
+ readonly types : Type [ ] ;
20
20
numberOfAssignmentsOriginal : number ; // number of times the variable should be assigned in the refactor
21
21
}
22
22
23
23
interface SymbolAndIdentifier {
24
- identifier : Identifier ;
25
- symbol : Symbol ;
24
+ readonly identifier : Identifier ;
25
+ readonly symbol : Symbol ;
26
26
}
27
27
28
28
interface Transformer {
29
- checker : TypeChecker ;
30
- synthNamesMap : Map < SynthIdentifier > ; // keys are the symbol id of the identifier
31
- allVarNames : SymbolAndIdentifier [ ] ;
32
- setOfExpressionsToReturn : Map < true > ; // keys are the node ids of the expressions
33
- constIdentifiers : Identifier [ ] ;
34
- originalTypeMap : Map < Type > ; // keys are the node id of the identifier
35
- isInJSFile : boolean ;
29
+ readonly checker : TypeChecker ;
30
+ readonly synthNamesMap : Map < SynthIdentifier > ; // keys are the symbol id of the identifier
31
+ readonly allVarNames : ReadonlyArray < SymbolAndIdentifier > ;
32
+ readonly setOfExpressionsToReturn : ReadonlyMap < true > ; // keys are the node ids of the expressions
33
+ readonly constIdentifiers : Identifier [ ] ;
34
+ readonly originalTypeMap : ReadonlyMap < Type > ; // keys are the node id of the identifier
35
+ readonly isInJSFile : boolean ;
36
36
}
37
37
38
38
function convertToAsyncFunction ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , position : number , checker : TypeChecker , context : CodeFixContextBase ) : void {
@@ -61,14 +61,14 @@ namespace ts.codefix {
61
61
const functionToConvertRenamed : FunctionLikeDeclaration = renameCollidingVarNames ( functionToConvert , checker , synthNamesMap , context , setOfExpressionsToReturn , originalTypeMap , allVarNames ) ;
62
62
const constIdentifiers = getConstIdentifiers ( synthNamesMap ) ;
63
63
const returnStatements = getReturnStatementsWithPromiseHandlers ( functionToConvertRenamed ) ;
64
- const transformer = { checker, synthNamesMap, allVarNames, setOfExpressionsToReturn, constIdentifiers, originalTypeMap, isInJSFile : isInJavascript } ;
64
+ const transformer : Transformer = { checker, synthNamesMap, allVarNames, setOfExpressionsToReturn, constIdentifiers, originalTypeMap, isInJSFile : isInJavascript } ;
65
65
66
66
if ( ! returnStatements . length ) {
67
67
return ;
68
68
}
69
69
70
70
// add the async keyword
71
- changes . insertModifierBefore ( sourceFile , SyntaxKind . AsyncKeyword , functionToConvert ) ;
71
+ changes . insertLastModifierBefore ( sourceFile , SyntaxKind . AsyncKeyword , functionToConvert ) ;
72
72
73
73
function startTransformation ( node : CallExpression , nodeToReplace : Node ) {
74
74
const newNodes = transformExpression ( node , transformer , node ) ;
@@ -88,7 +88,7 @@ namespace ts.codefix {
88
88
}
89
89
90
90
// Returns the identifiers that are never reassigned in the refactor
91
- function getConstIdentifiers ( synthNamesMap : Map < SynthIdentifier > ) : Identifier [ ] {
91
+ function getConstIdentifiers ( synthNamesMap : ReadonlyMap < SynthIdentifier > ) : Identifier [ ] {
92
92
const constIdentifiers : Identifier [ ] = [ ] ;
93
93
synthNamesMap . forEach ( ( val ) => {
94
94
if ( val . numberOfAssignmentsOriginal === 0 ) {
@@ -249,18 +249,18 @@ namespace ts.codefix {
249
249
}
250
250
}
251
251
252
- function getNewNameIfConflict ( name : Identifier , originalNames : Map < Symbol [ ] > ) : SynthIdentifier {
253
- const numVarsSameName = ( originalNames . get ( name . text ) || [ ] ) . length ;
252
+ function getNewNameIfConflict ( name : Identifier , originalNames : ReadonlyMap < Symbol [ ] > ) : SynthIdentifier {
253
+ const numVarsSameName = ( originalNames . get ( name . text ) || emptyArray ) . length ;
254
254
const numberOfAssignmentsOriginal = 0 ;
255
255
const identifier = numVarsSameName === 0 ? name : createIdentifier ( name . text + "_" + numVarsSameName ) ;
256
256
return { identifier, types : [ ] , numberOfAssignmentsOriginal } ;
257
257
}
258
258
259
259
// dispatch function to recursively build the refactoring
260
260
// should be kept up to date with isFixablePromiseHandler in suggestionDiagnostics.ts
261
- function transformExpression ( node : Expression , transformer : Transformer , outermostParent : CallExpression , prevArgName ?: SynthIdentifier ) : Statement [ ] {
261
+ function transformExpression ( node : Expression , transformer : Transformer , outermostParent : CallExpression , prevArgName ?: SynthIdentifier ) : ReadonlyArray < Statement > {
262
262
if ( ! node ) {
263
- return [ ] ;
263
+ return emptyArray ;
264
264
}
265
265
266
266
const originalType = isIdentifier ( node ) && transformer . originalTypeMap . get ( getNodeId ( node ) . toString ( ) ) ;
@@ -280,10 +280,10 @@ namespace ts.codefix {
280
280
}
281
281
282
282
codeActionSucceeded = false ;
283
- return [ ] ;
283
+ return emptyArray ;
284
284
}
285
285
286
- function transformCatch ( node : CallExpression , transformer : Transformer , prevArgName ?: SynthIdentifier ) : Statement [ ] {
286
+ function transformCatch ( node : CallExpression , transformer : Transformer , prevArgName ?: SynthIdentifier ) : ReadonlyArray < Statement > {
287
287
const func = node . arguments [ 0 ] ;
288
288
const argName = getArgName ( func , transformer ) ;
289
289
const shouldReturn = transformer . setOfExpressionsToReturn . get ( getNodeId ( node ) . toString ( ) ) ;
@@ -336,7 +336,7 @@ namespace ts.codefix {
336
336
return newSynthName ;
337
337
}
338
338
339
- function transformThen ( node : CallExpression , transformer : Transformer , outermostParent : CallExpression , prevArgName ?: SynthIdentifier ) : Statement [ ] {
339
+ function transformThen ( node : CallExpression , transformer : Transformer , outermostParent : CallExpression , prevArgName ?: SynthIdentifier ) : ReadonlyArray < Statement > {
340
340
const [ res , rej ] = node . arguments ;
341
341
342
342
if ( ! res ) {
@@ -356,18 +356,18 @@ namespace ts.codefix {
356
356
const catchArg = argNameRej ? argNameRej . identifier . text : "e" ;
357
357
const catchClause = createCatchClause ( catchArg , createBlock ( transformationBody2 ) ) ;
358
358
359
- return [ createTry ( tryBlock , catchClause , /* finallyBlock */ undefined ) as Statement ] ;
359
+ return [ createTry ( tryBlock , catchClause , /* finallyBlock */ undefined ) ] ;
360
360
}
361
361
362
362
return transformExpression ( node . expression , transformer , node , argNameRes ) . concat ( transformationBody ) ;
363
363
}
364
364
365
- function getFlagOfIdentifier ( node : Identifier , constIdentifiers : Identifier [ ] ) : NodeFlags {
365
+ function getFlagOfIdentifier ( node : Identifier , constIdentifiers : ReadonlyArray < Identifier > ) : NodeFlags {
366
366
const inArr : boolean = constIdentifiers . some ( elem => elem . text === node . text ) ;
367
367
return inArr ? NodeFlags . Const : NodeFlags . Let ;
368
368
}
369
369
370
- function transformPromiseCall ( node : Expression , transformer : Transformer , prevArgName ?: SynthIdentifier ) : Statement [ ] {
370
+ function transformPromiseCall ( node : Expression , transformer : Transformer , prevArgName ?: SynthIdentifier ) : ReadonlyArray < Statement > {
371
371
const shouldReturn = transformer . setOfExpressionsToReturn . get ( getNodeId ( node ) . toString ( ) ) ;
372
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
373
373
const originalNodeParent = node . original ? node . original . parent : node . parent ;
@@ -381,23 +381,23 @@ namespace ts.codefix {
381
381
return [ createReturn ( getSynthesizedDeepClone ( node ) ) ] ;
382
382
}
383
383
384
- function createTransformedStatement ( prevArgName : SynthIdentifier | undefined , rightHandSide : Expression , transformer : Transformer ) : MutableNodeArray < Statement > {
384
+ function createTransformedStatement ( prevArgName : SynthIdentifier | undefined , rightHandSide : Expression , transformer : Transformer ) : ReadonlyArray < Statement > {
385
385
if ( ! prevArgName || prevArgName . identifier . text . length === 0 ) {
386
386
// if there's no argName to assign to, there still might be side effects
387
- return createNodeArray ( [ createStatement ( rightHandSide ) ] ) ;
387
+ return [ createStatement ( rightHandSide ) ] ;
388
388
}
389
389
390
390
if ( prevArgName . types . length < prevArgName . numberOfAssignmentsOriginal ) {
391
391
// if the variable has already been declared, we don't need "let" or "const"
392
- return createNodeArray ( [ createStatement ( createAssignment ( getSynthesizedDeepClone ( prevArgName . identifier ) , rightHandSide ) ) ] ) ;
392
+ return [ createStatement ( createAssignment ( getSynthesizedDeepClone ( prevArgName . identifier ) , rightHandSide ) ) ] ;
393
393
}
394
394
395
- return createNodeArray ( [ createVariableStatement ( /*modifiers*/ undefined ,
396
- ( createVariableDeclarationList ( [ createVariableDeclaration ( getSynthesizedDeepClone ( prevArgName . identifier ) , /*type*/ undefined , rightHandSide ) ] , getFlagOfIdentifier ( prevArgName . identifier , transformer . constIdentifiers ) ) ) ) ] ) ;
395
+ return [ createVariableStatement ( /*modifiers*/ undefined ,
396
+ ( createVariableDeclarationList ( [ createVariableDeclaration ( getSynthesizedDeepClone ( prevArgName . identifier ) , /*type*/ undefined , rightHandSide ) ] , getFlagOfIdentifier ( prevArgName . identifier , transformer . constIdentifiers ) ) ) ) ] ;
397
397
}
398
398
399
399
// should be kept up to date with isFixablePromiseArgument in suggestionDiagnostics.ts
400
- function getTransformationBody ( func : Expression , prevArgName : SynthIdentifier | undefined , argName : SynthIdentifier | undefined , parent : CallExpression , transformer : Transformer ) : NodeArray < Statement > {
400
+ function getTransformationBody ( func : Expression , prevArgName : SynthIdentifier | undefined , argName : SynthIdentifier | undefined , parent : CallExpression , transformer : Transformer ) : ReadonlyArray < Statement > {
401
401
402
402
const shouldReturn = transformer . setOfExpressionsToReturn . get ( getNodeId ( parent ) . toString ( ) ) ;
403
403
switch ( func . kind ) {
@@ -410,9 +410,9 @@ namespace ts.codefix {
410
410
break ;
411
411
}
412
412
413
- const synthCall = createCall ( getSynthesizedDeepClone ( func ) as Identifier , /*typeArguments*/ undefined , argName ? [ argName . identifier ] : [ ] ) ;
413
+ const synthCall = createCall ( getSynthesizedDeepClone ( func as Identifier ) , /*typeArguments*/ undefined , argName ? [ argName . identifier ] : emptyArray ) ;
414
414
if ( shouldReturn ) {
415
- return createNodeArray ( [ createReturn ( synthCall ) ] ) ;
415
+ return [ createReturn ( synthCall ) ] ;
416
416
}
417
417
418
418
const type = transformer . originalTypeMap . get ( getNodeId ( func ) . toString ( ) ) || transformer . checker . getTypeAtLocation ( func ) ;
@@ -450,15 +450,19 @@ namespace ts.codefix {
450
450
}
451
451
}
452
452
453
- return shouldReturn ? getSynthesizedDeepClones ( createNodeArray ( refactoredStmts ) ) :
454
- removeReturns ( createNodeArray ( refactoredStmts ) , prevArgName ! . identifier , transformer , seenReturnStatement ) ;
453
+ return shouldReturn ? refactoredStmts . map ( s => getSynthesizedDeepClone ( s ) ) :
454
+ removeReturns (
455
+ refactoredStmts ,
456
+ prevArgName === undefined ? undefined : prevArgName . identifier ,
457
+ transformer ,
458
+ seenReturnStatement ) ;
455
459
}
456
460
else {
457
461
const innerRetStmts = getReturnStatementsWithPromiseHandlers ( createReturn ( funcBody ) ) ;
458
462
const innerCbBody = getInnerTransformationBody ( transformer , innerRetStmts , prevArgName ) ;
459
463
460
464
if ( innerCbBody . length > 0 ) {
461
- return createNodeArray ( innerCbBody ) ;
465
+ return innerCbBody ;
462
466
}
463
467
464
468
if ( ! shouldReturn ) {
@@ -473,7 +477,7 @@ namespace ts.codefix {
473
477
return transformedStatement ;
474
478
}
475
479
else {
476
- return createNodeArray ( [ createReturn ( getSynthesizedDeepClone ( funcBody ) ) ] ) ;
480
+ return [ createReturn ( getSynthesizedDeepClone ( funcBody ) ) ] ;
477
481
}
478
482
}
479
483
}
@@ -482,7 +486,7 @@ namespace ts.codefix {
482
486
codeActionSucceeded = false ;
483
487
break ;
484
488
}
485
- return createNodeArray ( [ ] ) ;
489
+ return emptyArray ;
486
490
}
487
491
488
492
function getLastCallSignature ( type : Type , checker : TypeChecker ) : Signature | undefined {
@@ -491,14 +495,19 @@ namespace ts.codefix {
491
495
}
492
496
493
497
494
- function removeReturns ( stmts : NodeArray < Statement > , prevArgName : Identifier , transformer : Transformer , seenReturnStatement : boolean ) : NodeArray < Statement > {
498
+ function removeReturns ( stmts : ReadonlyArray < Statement > , prevArgName : Identifier | undefined , transformer : Transformer , seenReturnStatement : boolean ) : ReadonlyArray < Statement > {
495
499
const ret : Statement [ ] = [ ] ;
496
500
for ( const stmt of stmts ) {
497
501
if ( isReturnStatement ( stmt ) ) {
498
502
if ( stmt . expression ) {
499
503
const possiblyAwaitedExpression = isPromiseReturningExpression ( stmt . expression , transformer . checker ) ? createAwait ( stmt . expression ) : stmt . expression ;
500
- ret . push ( createVariableStatement ( /*modifiers*/ undefined ,
501
- ( createVariableDeclarationList ( [ createVariableDeclaration ( prevArgName , /*type*/ undefined , possiblyAwaitedExpression ) ] , getFlagOfIdentifier ( prevArgName , transformer . constIdentifiers ) ) ) ) ) ;
504
+ if ( prevArgName === undefined ) {
505
+ ret . push ( createExpressionStatement ( possiblyAwaitedExpression ) ) ;
506
+ }
507
+ else {
508
+ ret . push ( createVariableStatement ( /*modifiers*/ undefined ,
509
+ ( createVariableDeclarationList ( [ createVariableDeclaration ( prevArgName , /*type*/ undefined , possiblyAwaitedExpression ) ] , getFlagOfIdentifier ( prevArgName , transformer . constIdentifiers ) ) ) ) ) ;
510
+ }
502
511
}
503
512
}
504
513
else {
@@ -507,20 +516,20 @@ namespace ts.codefix {
507
516
}
508
517
509
518
// if block has no return statement, need to define prevArgName as undefined to prevent undeclared variables
510
- if ( ! seenReturnStatement ) {
519
+ if ( ! seenReturnStatement && prevArgName !== undefined ) {
511
520
ret . push ( createVariableStatement ( /*modifiers*/ undefined ,
512
521
( createVariableDeclarationList ( [ createVariableDeclaration ( prevArgName , /*type*/ undefined , createIdentifier ( "undefined" ) ) ] , getFlagOfIdentifier ( prevArgName , transformer . constIdentifiers ) ) ) ) ) ;
513
522
}
514
523
515
- return createNodeArray ( ret ) ;
524
+ return ret ;
516
525
}
517
526
518
527
519
- function getInnerTransformationBody ( transformer : Transformer , innerRetStmts : Node [ ] , prevArgName ?: SynthIdentifier ) {
528
+ function getInnerTransformationBody ( transformer : Transformer , innerRetStmts : ReadonlyArray < Node > , prevArgName ?: SynthIdentifier ) {
520
529
521
530
let innerCbBody : Statement [ ] = [ ] ;
522
531
for ( const stmt of innerRetStmts ) {
523
- forEachChild ( stmt , function visit ( node : Node ) {
532
+ forEachChild ( stmt , function visit ( node ) {
524
533
if ( isCallExpression ( node ) ) {
525
534
const temp = transformExpression ( node , transformer , node , prevArgName ) ;
526
535
innerCbBody = innerCbBody . concat ( temp ) ;
0 commit comments