namespace ts . codefix { const fixId = "convertToAsyncFunction" ; const errorCodes = [ Diagnostics . This_may_be_converted_to_an_async_function . code ] ; let codeActionSucceeded = true ; registerCodeFix ( { errorCodes , getCodeActions ( context ) { codeActionSucceeded = true ; const changes = textChanges . ChangeTracker . with ( context , ( t ) => convertToAsyncFunction ( t , context . sourceFile , context . span . start , context . program . getTypeChecker ( ) ) ) ; return codeActionSucceeded ? [ createCodeFixAction ( fixId , changes , Diagnostics . Convert_to_async_function , fixId , Diagnostics . Convert_all_to_async_functions ) ] : [ ] ; } , fixIds : [ fixId ] , getAllCodeActions : context => codeFixAll ( context , errorCodes , ( changes , err ) => convertToAsyncFunction ( changes , err . file , err . start , context . program . getTypeChecker ( ) ) ) , } ) ; const enum SynthBindingNameKind { Identifier , BindingPattern } type SynthBindingName = SynthBindingPattern | SynthIdentifier ; interface SynthBindingPattern { readonly kind : SynthBindingNameKind . BindingPattern ; readonly elements : readonly SynthBindingName [ ] ; readonly bindingPattern : BindingPattern ; readonly types : Type [ ] ; } interface SynthIdentifier { readonly kind : SynthBindingNameKind . Identifier ; readonly identifier : Identifier ; readonly types : Type [ ] ; /** A declaration for this identifier has already been generated */ hasBeenDeclared : boolean ; hasBeenReferenced : boolean ; } interface Transformer { readonly checker : TypeChecker ; readonly synthNamesMap : ESMap < string , SynthIdentifier > ; readonly setOfExpressionsToReturn : ReadonlySet < number > ; readonly isInJSFile : boolean ; } interface PromiseReturningCallExpression < Name extends string > extends CallExpression { readonly expression : PropertyAccessExpression & { readonly escapedText : Name ; } ; } function convertToAsyncFunction ( changes , sourceFile , position , checker ) { const tokenAtPosition = getTokenAtPosition ( sourceFile , position ) ; let functionToConvert ; if ( isIdentifier ( tokenAtPosition ) && isVariableDeclaration ( tokenAtPosition . parent ) && tokenAtPosition . parent . initializer && isFunctionLikeDeclaration ( tokenAtPosition . parent . initializer ) ) { functionToConvert = tokenAtPosition . parent . initializer ; } else { functionToConvert = tryCast ( getContainingFunction ( getTokenAtPosition ( sourceFile , position ) ) , canBeConvertedToAsync ) ; } if ( ! functionToConvert ) { return ; } const synthNamesMap = new Map < string , SynthIdentifier > ( ) ; const isInJavascript = isInJSFile ( functionToConvert ) ; const setOfExpressionsToReturn = getAllPromiseExpressionsToReturn ( functionToConvert , checker ) ; const functionToConvertRenamed = renameCollidingVarNames ( functionToConvert , checker , synthNamesMap ) ; if ( ! returnsPromise ( functionToConvertRenamed , checker ) ) { return ; } const returnStatements = functionToConvertRenamed . body && isBlock ( functionToConvertRenamed . body ) ? getReturnStatementsWithPromiseHandlers ( functionToConvertRenamed . body , checker ) : emptyArray ; const transformer = { checker , synthNamesMap , setOfExpressionsToReturn , isInJSFile : isInJavascript } ; if ( ! returnStatements . length ) { return ; } const pos = functionToConvert . modifiers ? functionToConvert . modifiers . end : functionToConvert . decorators ? skipTrivia ( sourceFile . text , functionToConvert . decorators . end ) : functionToConvert . getStart ( sourceFile ) ; const options = functionToConvert . modifiers ? { prefix : " " } : { suffix : " " } ; changes . insertModifierAt ( sourceFile , pos , SyntaxKind . AsyncKeyword , options ) ; for ( const returnStatement of returnStatements ) { forEachChild ( returnStatement , function visit ( node ) { if ( isCallExpression ( node ) ) { const newNodes = transformExpression ( node , node , transformer , false ) ; if ( hasFailed ( ) ) { return true ; } changes . replaceNodeWithNodes ( sourceFile , returnStatement , newNodes ) ; } else if ( ! isFunctionLike ( node ) ) { forEachChild ( node , visit ) ; if ( hasFailed ( ) ) { return true ; } } } ) ; if ( hasFailed ( ) ) { return ; } } } function getReturnStatementsWithPromiseHandlers ( body , checker ) { const res = [ ] ; forEachReturnStatement ( body , ret => { if ( isReturnStatementWithFixablePromiseHandler ( ret , checker ) ) res . push ( ret ) ; } ) ; return res ; } function getAllPromiseExpressionsToReturn ( func , checker ) { if ( ! func . body ) { return new Set ( ) ; } const setOfExpressionsToReturn = new Set < number > ( ) ; forEachChild ( func . body , function visit ( node ) { if ( isPromiseReturningCallExpression ( node , checker , "then" ) ) { setOfExpressionsToReturn . add ( getNodeId ( node ) ) ; forEach ( node . arguments , visit ) ; } else if ( isPromiseReturningCallExpression ( node , checker , "catch" ) || isPromiseReturningCallExpression ( node , checker , "finally" ) ) { setOfExpressionsToReturn . add ( getNodeId ( node ) ) ; forEachChild ( node , visit ) ; } else if ( isPromiseTypedExpression ( node , checker ) ) { setOfExpressionsToReturn . add ( getNodeId ( node ) ) ; } else { forEachChild ( node , visit ) ; } } ) ; return setOfExpressionsToReturn ; } function isPromiseReturningCallExpression < Name extends string > ( node , checker , name ) { if ( ! isCallExpression ( node ) ) return false ; const isExpressionOfName = hasPropertyAccessExpressionWithName ( node , name ) ; const nodeType = isExpressionOfName && checker . getTypeAtLocation ( node ) ; return ! ! ( nodeType && checker . getPromisedTypeOfPromise ( nodeType ) ) ; } function isReferenceToType ( type , target ) { return ( getObjectFlags ( type ) & ObjectFlags . Reference ) !== 0 && ( type as TypeReference ) . target === target ; } function getExplicitPromisedTypeOfPromiseReturningCallExpression ( node , callback , checker ) { if ( node . expression . name . escapedText === "finally" ) { return undefined ; } const promiseType = checker . getTypeAtLocation ( node . expression . expression ) ; if ( isReferenceToType ( promiseType , checker . getPromiseType ( ) ) || isReferenceToType ( promiseType , checker . getPromiseLikeType ( ) ) ) { if ( node . expression . name . escapedText === "then" ) { if ( callback === elementAt ( node . arguments , 0 ) ) { return elementAt ( node . typeArguments , 0 ) ; } else if ( callback === elementAt ( node . arguments , 1 ) ) { return elementAt ( node . typeArguments , 1 ) ; } } else { return elementAt ( node . typeArguments , 0 ) ; } } } function isPromiseTypedExpression ( node , checker ) { if ( ! isExpression ( node ) ) return false ; return ! ! checker . getPromisedTypeOfPromise ( checker . getTypeAtLocation ( node ) ) ; } function renameCollidingVarNames ( nodeToRename , checker , synthNamesMap ) { const identsToRenameMap = new Map < string , Identifier > ( ) ; const collidingSymbolMap = createMultiMap < Symbol > ( ) ; forEachChild ( nodeToRename , function visit ( node ) { if ( ! isIdentifier ( node ) ) { forEachChild ( node , visit ) ; return ; } const symbol = checker . getSymbolAtLocation ( node ) ; if ( symbol ) { const type = checker . getTypeAtLocation ( node ) ; const lastCallSignature = getLastCallSignature ( type , checker ) ; const symbolIdString = getSymbolId ( symbol ) . toString ( ) ; if ( lastCallSignature && ! isParameter ( node . parent ) && ! isFunctionLikeDeclaration ( node . parent ) && ! synthNamesMap . has ( symbolIdString ) ) { const firstParameter = firstOrUndefined ( lastCallSignature . parameters ) ; const ident = firstParameter ?. valueDeclaration && isParameter ( firstParameter . valueDeclaration ) && tryCast ( firstParameter . valueDeclaration . name , isIdentifier ) || factory . createUniqueName ( "result" , GeneratedIdentifierFlags . Optimistic ) ; const synthName = getNewNameIfConflict ( ident , collidingSymbolMap ) ; synthNamesMap . set ( symbolIdString , synthName ) ; collidingSymbolMap . add ( ident . text , symbol ) ; } else if ( node . parent && ( isParameter ( node . parent ) || isVariableDeclaration ( node . parent ) || isBindingElement ( node . parent ) ) ) { const originalName = node . text ; const collidingSymbols = collidingSymbolMap . get ( originalName ) ; if ( collidingSymbols && collidingSymbols . some ( prevSymbol => prevSymbol !== symbol ) ) { const newName = getNewNameIfConflict ( node , collidingSymbolMap ) ; identsToRenameMap . set ( symbolIdString , newName . identifier ) ; synthNamesMap . set ( symbolIdString , newName ) ; collidingSymbolMap . add ( originalName , symbol ) ; } else { const identifier = getSynthesizedDeepClone ( node ) ; synthNamesMap . set ( symbolIdString , createSynthIdentifier ( identifier ) ) ; collidingSymbolMap . add ( originalName , symbol ) ; } } } } ) ; return getSynthesizedDeepCloneWithReplacements ( nodeToRename , true , original => { if ( isBindingElement ( original ) && isIdentifier ( original . name ) && isObjectBindingPattern ( original . parent ) ) { const symbol = checker . getSymbolAtLocation ( original . name ) ; const renameInfo = symbol && identsToRenameMap . get ( String ( getSymbolId ( symbol ) ) ) ; if ( renameInfo && renameInfo . text !== ( original . name || original . propertyName ) . getText ( ) ) { return factory . createBindingElement ( original . dotDotDotToken , original . propertyName || original . name , renameInfo , original . initializer ) ; } } else if ( isIdentifier ( original ) ) { const symbol = checker . getSymbolAtLocation ( original ) ; const renameInfo = symbol && identsToRenameMap . get ( String ( getSymbolId ( symbol ) ) ) ; if ( renameInfo ) { return factory . createIdentifier ( renameInfo . text ) ; } } } ) ; } function getNewNameIfConflict ( name , originalNames ) { const numVarsSameName = ( originalNames . get ( name . text ) || emptyArray ) . length ; const identifier = numVarsSameName === 0 ? name : factory . createIdentifier ( name . text + "_" + numVarsSameName ) ; return createSynthIdentifier ( identifier ) ; } function hasFailed ( ) { return ! codeActionSucceeded ; } function silentFail ( ) { codeActionSucceeded = false ; return emptyArray ; } param hasContinuation param continuationArgName function transformExpression ( returnContextNode , node , transformer , hasContinuation , continuationArgName ? ) { if ( isPromiseReturningCallExpression ( node , transformer . checker , "then" ) ) { return transformThen ( node , elementAt ( node . arguments , 0 ) , elementAt ( node . arguments , 1 ) , transformer , hasContinuation , continuationArgName ) ; } if ( isPromiseReturningCallExpression ( node , transformer . checker , "catch" ) ) { return transformCatch ( node , elementAt ( node . arguments , 0 ) , transformer , hasContinuation , continuationArgName ) ; } if ( isPromiseReturningCallExpression ( node , transformer . checker , "finally" ) ) { return transformFinally ( node , elementAt ( node . arguments , 0 ) , transformer , hasContinuation , continuationArgName ) ; } if ( isPropertyAccessExpression ( node ) ) { return transformExpression ( returnContextNode , node . expression , transformer , hasContinuation , continuationArgName ) ; } const nodeType = transformer . checker . getTypeAtLocation ( node ) ; if ( nodeType && transformer . checker . getPromisedTypeOfPromise ( nodeType ) ) { Debug . assertNode ( getOriginalNode ( node ) . parent , isPropertyAccessExpression ) ; return transformPromiseExpressionOfPropertyAccess ( returnContextNode , node , transformer , hasContinuation , continuationArgName ) ; } return silentFail ( ) ; } function isNullOrUndefined ( { checker } , node ) { if ( node . kind === SyntaxKind . NullKeyword ) return true ; if ( isIdentifier ( node ) && ! isGeneratedIdentifier ( node ) && idText ( node ) === "undefined" ) { const symbol = checker . getSymbolAtLocation ( node ) ; return ! symbol || checker . isUndefinedSymbol ( symbol ) ; } return false ; } function createUniqueSynthName ( prevArgName ) { const renamedPrevArg = factory . createUniqueName ( prevArgName . identifier . text , GeneratedIdentifierFlags . Optimistic ) ; return createSynthIdentifier ( renamedPrevArg ) ; } function getPossibleNameForVarDecl ( node , transformer , continuationArgName ? ) { let possibleNameForVarDecl ; if ( continuationArgName && ! shouldReturn ( node , transformer ) ) { if ( isSynthIdentifier ( continuationArgName ) ) { possibleNameForVarDecl = continuationArgName ; transformer . synthNamesMap . forEach ( ( val , key ) => { if ( val . identifier . text === continuationArgName . identifier . text ) { const newSynthName = createUniqueSynthName ( continuationArgName ) ; transformer . synthNamesMap . set ( key , newSynthName ) ; } } ) ; } else { possibleNameForVarDecl = createSynthIdentifier ( factory . createUniqueName ( "result" , GeneratedIdentifierFlags . Optimistic ) , continuationArgName . types ) ; } declareSynthIdentifier ( possibleNameForVarDecl ) ; } return possibleNameForVarDecl ; } function finishCatchOrFinallyTransform ( node , transformer , tryStatement , possibleNameForVarDecl , continuationArgName ? ) { const statements = [ ] ; let varDeclIdentifier ; if ( possibleNameForVarDecl && ! shouldReturn ( node , transformer ) ) { varDeclIdentifier = getSynthesizedDeepClone ( declareSynthIdentifier ( possibleNameForVarDecl ) ) ; const typeArray = possibleNameForVarDecl . types ; const unionType = transformer . checker . getUnionType ( typeArray , UnionReduction . Subtype ) ; const unionTypeNode = transformer . isInJSFile ? undefined : transformer . checker . typeToTypeNode ( unionType , undefined , undefined ) ; const varDecl = [ factory . createVariableDeclaration ( varDeclIdentifier , undefined , unionTypeNode ) ] ; const varDeclList = factory . createVariableStatement ( undefined , factory . createVariableDeclarationList ( varDecl , NodeFlags . Let ) ) ; statements . push ( varDeclList ) ; } statements . push ( tryStatement ) ; if ( continuationArgName && varDeclIdentifier && isSynthBindingPattern ( continuationArgName ) ) { statements . push ( factory . createVariableStatement ( undefined , factory . createVariableDeclarationList ( [ factory . createVariableDeclaration ( getSynthesizedDeepClone ( declareSynthBindingPattern ( continuationArgName ) ) , undefined , undefined , varDeclIdentifier ) ] , NodeFlags . Const ) ) ) ; } return statements ; } param hasContinuation param continuationArgName function transformFinally ( node , onFinally , transformer , hasContinuation , continuationArgName ? ) { if ( ! onFinally || isNullOrUndefined ( transformer , onFinally ) ) { return transformExpression ( node , node . expression . expression , transformer , hasContinuation , continuationArgName ) ; } const possibleNameForVarDecl = getPossibleNameForVarDecl ( node , transformer , continuationArgName ) ; const inlinedLeftHandSide = transformExpression ( node , node . expression . expression , transformer , true , possibleNameForVarDecl ) ; if ( hasFailed ( ) ) return silentFail ( ) ; const inlinedCallback = transformCallbackArgument ( onFinally , hasContinuation , undefined , undefined , node , transformer ) ; if ( hasFailed ( ) ) return silentFail ( ) ; const tryBlock = factory . createBlock ( inlinedLeftHandSide ) ; const finallyBlock = factory . createBlock ( inlinedCallback ) ; const tryStatement = factory . createTryStatement ( tryBlock , undefined , finallyBlock ) ; return finishCatchOrFinallyTransform ( node , transformer , tryStatement , possibleNameForVarDecl , continuationArgName ) ; } param hasContinuation param continuationArgName function transformCatch ( node , onRejected , transformer , hasContinuation , continuationArgName ? ) { if ( ! onRejected || isNullOrUndefined ( transformer , onRejected ) ) { return transformExpression ( node , node . expression . expression , transformer , hasContinuation , continuationArgName ) ; } const inputArgName = getArgBindingName ( onRejected , transformer ) ; const possibleNameForVarDecl = getPossibleNameForVarDecl ( node , transformer , continuationArgName ) ; const inlinedLeftHandSide = transformExpression ( node , node . expression . expression , transformer , true , possibleNameForVarDecl ) ; if ( hasFailed ( ) ) return silentFail ( ) ; const inlinedCallback = transformCallbackArgument ( onRejected , hasContinuation , possibleNameForVarDecl , inputArgName , node , transformer ) ; if ( hasFailed ( ) ) return silentFail ( ) ; const tryBlock = factory . createBlock ( inlinedLeftHandSide ) ; const catchClause = factory . createCatchClause ( inputArgName && getSynthesizedDeepClone ( declareSynthBindingName ( inputArgName ) ) , factory . createBlock ( inlinedCallback ) ) ; const tryStatement = factory . createTryStatement ( tryBlock , catchClause , undefined ) ; return finishCatchOrFinallyTransform ( node , transformer , tryStatement , possibleNameForVarDecl , continuationArgName ) ; } param hasContinuation param continuationArgName function transformThen ( node , onFulfilled , onRejected , transformer , hasContinuation , continuationArgName ? ) { if ( ! onFulfilled || isNullOrUndefined ( transformer , onFulfilled ) ) { return transformCatch ( node , onRejected , transformer , hasContinuation , continuationArgName ) ; } if ( onRejected && ! isNullOrUndefined ( transformer , onRejected ) ) { return silentFail ( ) ; } const inputArgName = getArgBindingName ( onFulfilled , transformer ) ; const inlinedLeftHandSide = transformExpression ( node . expression . expression , node . expression . expression , transformer , true , inputArgName ) ; if ( hasFailed ( ) ) return silentFail ( ) ; const inlinedCallback = transformCallbackArgument ( onFulfilled , hasContinuation , continuationArgName , inputArgName , node , transformer ) ; if ( hasFailed ( ) ) return silentFail ( ) ; return concatenate ( inlinedLeftHandSide , inlinedCallback ) ; } /**
     * Transforms the 'x' part of `x.then(...)`, or the 'y()' part of `y().catch(...)`, where 'x' and 'y()' are Promises.
     */ function transformPromiseExpressionOfPropertyAccess ( returnContextNode , node , transformer , hasContinuation , continuationArgName ? ) { if ( shouldReturn ( returnContextNode , transformer ) ) { let returnValue = getSynthesizedDeepClone ( node ) ; if ( hasContinuation ) { returnValue = factory . createAwaitExpression ( returnValue ) ; } return [ factory . createReturnStatement ( returnValue ) ] ; } return createVariableOrAssignmentOrExpressionStatement ( continuationArgName , factory . createAwaitExpression ( node ) , undefined ) ; } function createVariableOrAssignmentOrExpressionStatement ( variableName , rightHandSide , typeAnnotation ) { if ( ! variableName || isEmptyBindingName ( variableName ) ) { return [ factory . createExpressionStatement ( rightHandSide ) ] ; } if ( isSynthIdentifier ( variableName ) && variableName . hasBeenDeclared ) { return [ factory . createExpressionStatement ( factory . createAssignment ( getSynthesizedDeepClone ( referenceSynthIdentifier ( variableName ) ) , rightHandSide ) ) ] ; } return [ factory . createVariableStatement ( undefined , factory . createVariableDeclarationList ( [ factory . createVariableDeclaration ( getSynthesizedDeepClone ( declareSynthBindingName ( variableName ) ) , undefined , typeAnnotation , rightHandSide ) ] , NodeFlags . Const ) ) ] ; } function maybeAnnotateAndReturn ( expressionToReturn , typeAnnotation ) { if ( typeAnnotation && expressionToReturn ) { const name = factory . createUniqueName ( "result" , GeneratedIdentifierFlags . Optimistic ) ; return [ ... createVariableOrAssignmentOrExpressionStatement ( createSynthIdentifier ( name ) , expressionToReturn , typeAnnotation ) , factory . createReturnStatement ( name ) ] ; } return [ factory . createReturnStatement ( expressionToReturn ) ] ; } param hasContinuation param continuationArgName param inputArgName function transformCallbackArgument ( func , hasContinuation , continuationArgName , inputArgName , parent , transformer ) { switch ( func . kind ) { case SyntaxKind . NullKeyword : break ; case SyntaxKind . PropertyAccessExpression : case SyntaxKind . Identifier : if ( ! inputArgName ) { break ; } const synthCall = factory . createCallExpression ( getSynthesizedDeepClone ( func as Identifier | PropertyAccessExpression ) , undefined , isSynthIdentifier ( inputArgName ) ? [ referenceSynthIdentifier ( inputArgName ) ] : [ ] ) ; if ( shouldReturn ( parent , transformer ) ) { return maybeAnnotateAndReturn ( synthCall , getExplicitPromisedTypeOfPromiseReturningCallExpression ( parent , func , transformer . checker ) ) ; } const type = transformer . checker . getTypeAtLocation ( func ) ; const callSignatures = transformer . checker . getSignaturesOfType ( type , SignatureKind . Call ) ; if ( ! callSignatures . length ) { return silentFail ( ) ; } const returnType = callSignatures [ 0 ] . getReturnType ( ) ; const varDeclOrAssignment = createVariableOrAssignmentOrExpressionStatement ( continuationArgName , factory . createAwaitExpression ( synthCall ) , getExplicitPromisedTypeOfPromiseReturningCallExpression ( parent , func , transformer . checker ) ) ; if ( continuationArgName ) { continuationArgName . types . push ( transformer . checker . getAwaitedType ( returnType ) || returnType ) ; } return varDeclOrAssignment ; case SyntaxKind . FunctionExpression : case SyntaxKind . ArrowFunction : { const funcBody = ( func as FunctionExpression | ArrowFunction ) . body ; const returnType = getLastCallSignature ( transformer . checker . getTypeAtLocation ( func ) , transformer . checker ) ?. getReturnType ( ) ; if ( isBlock ( funcBody ) ) { let refactoredStmts = [ ] ; let seenReturnStatement = false ; for ( const statement of funcBody . statements ) { if ( isReturnStatement ( statement ) ) { seenReturnStatement = true ; if ( isReturnStatementWithFixablePromiseHandler ( statement , transformer . checker ) ) { refactoredStmts = refactoredStmts . concat ( transformReturnStatementWithFixablePromiseHandler ( transformer , statement , hasContinuation , continuationArgName ) ) ; } else { const possiblyAwaitedRightHandSide = returnType && statement . expression ? getPossiblyAwaitedRightHandSide ( transformer . checker , returnType , statement . expression ) : statement . expression ; refactoredStmts . push ( ... maybeAnnotateAndReturn ( possiblyAwaitedRightHandSide , getExplicitPromisedTypeOfPromiseReturningCallExpression ( parent , func , transformer . checker ) ) ) ; } } else if ( hasContinuation && forEachReturnStatement ( statement , returnTrue ) ) { return silentFail ( ) ; } else { refactoredStmts . push ( statement ) ; } } return shouldReturn ( parent , transformer ) ? refactoredStmts . map ( s => getSynthesizedDeepClone ( s ) ) : removeReturns ( refactoredStmts , continuationArgName , transformer , seenReturnStatement ) ; } else { const inlinedStatements = isFixablePromiseHandler ( funcBody , transformer . checker ) ? transformReturnStatementWithFixablePromiseHandler ( transformer , factory . createReturnStatement ( funcBody ) , hasContinuation , continuationArgName ) : emptyArray ; if ( inlinedStatements . length > 0 ) { return inlinedStatements ; } if ( returnType ) { const possiblyAwaitedRightHandSide = getPossiblyAwaitedRightHandSide ( transformer . checker , returnType , funcBody ) ; if ( ! shouldReturn ( parent , transformer ) ) { const transformedStatement = createVariableOrAssignmentOrExpressionStatement ( continuationArgName , possiblyAwaitedRightHandSide , undefined ) ; if ( continuationArgName ) { continuationArgName . types . push ( transformer . checker . getAwaitedType ( returnType ) || returnType ) ; } return transformedStatement ; } else { return maybeAnnotateAndReturn ( possiblyAwaitedRightHandSide , getExplicitPromisedTypeOfPromiseReturningCallExpression ( parent , func , transformer . checker ) ) ; } } else { return silentFail ( ) ; } } } default : return silentFail ( ) ; } return emptyArray ; } function getPossiblyAwaitedRightHandSide ( checker , type , expr ) { const rightHandSide = getSynthesizedDeepClone ( expr ) ; return ! ! checker . getPromisedTypeOfPromise ( type ) ? factory . createAwaitExpression ( rightHandSide ) : rightHandSide ; } function getLastCallSignature ( type , checker ) { const callSignatures = checker . getSignaturesOfType ( type , SignatureKind . Call ) ; return lastOrUndefined ( callSignatures ) ; } function removeReturns ( stmts , prevArgName , transformer , seenReturnStatement ) { const ret = [ ] ; for ( const stmt of stmts ) { if ( isReturnStatement ( stmt ) ) { if ( stmt . expression ) { const possiblyAwaitedExpression = isPromiseTypedExpression ( stmt . expression , transformer . checker ) ? factory . createAwaitExpression ( stmt . expression ) : stmt . expression ; if ( prevArgName === undefined ) { ret . push ( factory . createExpressionStatement ( possiblyAwaitedExpression ) ) ; } else if ( isSynthIdentifier ( prevArgName ) && prevArgName . hasBeenDeclared ) { ret . push ( factory . createExpressionStatement ( factory . createAssignment ( referenceSynthIdentifier ( prevArgName ) , possiblyAwaitedExpression ) ) ) ; } else { ret . push ( factory . createVariableStatement ( undefined , ( factory . createVariableDeclarationList ( [ factory . createVariableDeclaration ( declareSynthBindingName ( prevArgName ) , undefined , undefined , possiblyAwaitedExpression ) ] , NodeFlags . Const ) ) ) ) ; } } } else { ret . push ( getSynthesizedDeepClone ( stmt ) ) ; } } if ( ! seenReturnStatement && prevArgName !== undefined ) { ret . push ( factory . createVariableStatement ( undefined , ( factory . createVariableDeclarationList ( [ factory . createVariableDeclaration ( declareSynthBindingName ( prevArgName ) , undefined , undefined , factory . createIdentifier ( "undefined" ) ) ] , NodeFlags . Const ) ) ) ) ; } return ret ; } param hasContinuation param continuationArgName function transformReturnStatementWithFixablePromiseHandler ( transformer , innerRetStmt , hasContinuation , continuationArgName ? ) { let innerCbBody = [ ] ; forEachChild ( innerRetStmt , function visit ( node ) { if ( isCallExpression ( node ) ) { const temp = transformExpression ( node , node , transformer , hasContinuation , continuationArgName ) ; innerCbBody = innerCbBody . concat ( temp ) ; if ( innerCbBody . length > 0 ) { return ; } } else if ( ! isFunctionLike ( node ) ) { forEachChild ( node , visit ) ; } } ) ; return innerCbBody ; } function getArgBindingName ( funcNode , transformer ) { const types = [ ] ; let name ; if ( isFunctionLikeDeclaration ( funcNode ) ) { if ( funcNode . parameters . length > 0 ) { const param = funcNode . parameters [ 0 ] . name ; name = getMappedBindingNameOrDefault ( param ) ; } } else if ( isIdentifier ( funcNode ) ) { name = getMapEntryOrDefault ( funcNode ) ; } else if ( isPropertyAccessExpression ( funcNode ) && isIdentifier ( funcNode . name ) ) { name = getMapEntryOrDefault ( funcNode . name ) ; } if ( ! name || "identifier" in name && name . identifier . text === "undefined" ) { return undefined ; } return name ; function getMappedBindingNameOrDefault ( bindingName ) { if ( isIdentifier ( bindingName ) ) return getMapEntryOrDefault ( bindingName ) ; const elements = flatMap ( bindingName . elements , element => { if ( isOmittedExpression ( element ) ) return [ ] ; return [ getMappedBindingNameOrDefault ( element . name ) ] ; } ) ; return createSynthBindingPattern ( bindingName , elements ) ; } function getMapEntryOrDefault ( identifier ) { const originalNode = getOriginalNode ( identifier ) ; const symbol = getSymbol ( originalNode ) ; if ( ! symbol ) { return createSynthIdentifier ( identifier , types ) ; } const mapEntry = transformer . synthNamesMap . get ( getSymbolId ( symbol ) . toString ( ) ) ; return mapEntry || createSynthIdentifier ( identifier , types ) ; } function getSymbol ( node ) { return node . symbol ? node . symbol : transformer . checker . getSymbolAtLocation ( node ) ; } function getOriginalNode ( node ) { return node . original ? node . original : node ; } } function isEmptyBindingName ( bindingName ) { if ( ! bindingName ) { return true ; } if ( isSynthIdentifier ( bindingName ) ) { return ! bindingName . identifier . text ; } return every ( bindingName . elements , isEmptyBindingName ) ; } function createSynthIdentifier ( identifier , types = [ ] ) { return { kind : SynthBindingNameKind . Identifier , identifier , types , hasBeenDeclared : false , hasBeenReferenced : false } ; } function createSynthBindingPattern ( bindingPattern , elements = emptyArray , types = [ ] ) { return { kind : SynthBindingNameKind . BindingPattern , bindingPattern , elements , types } ; } function referenceSynthIdentifier ( synthId ) { synthId . hasBeenReferenced = true ; return synthId . identifier ; } function declareSynthBindingName ( synthName ) { return isSynthIdentifier ( synthName ) ? declareSynthIdentifier ( synthName ) : declareSynthBindingPattern ( synthName ) ; } function declareSynthBindingPattern ( synthPattern ) { for ( const element of synthPattern . elements ) { declareSynthBindingName ( element ) ; } return synthPattern . bindingPattern ; } function declareSynthIdentifier ( synthId ) { synthId . hasBeenDeclared = true ; return synthId . identifier ; } function isSynthIdentifier ( bindingName ) { return bindingName . kind === SynthBindingNameKind . Identifier ; } function isSynthBindingPattern ( bindingName ) { return bindingName . kind === SynthBindingNameKind . BindingPattern ; } function shouldReturn ( expression , transformer ) { return ! ! expression . original && transformer . setOfExpressionsToReturn . has ( getNodeId ( expression . original ) ) ; } }