textChanges: Don't crash when inserting type annotation on function with no parens (#23034)

This commit is contained in:
Andy
2018-04-02 08:41:22 -07:00
committed by GitHub
parent 86a8e269fa
commit ea65f21677
4 changed files with 31 additions and 10 deletions

View File

@@ -50,19 +50,19 @@ namespace ts.codefix {
for (const param of decl.parameters) {
if (!param.type) {
const paramType = getJSDocType(param);
if (paramType) changes.insertTypeAnnotation(sourceFile, param, transformJSDocType(paramType));
if (paramType) changes.tryInsertTypeAnnotation(sourceFile, param, transformJSDocType(paramType));
}
}
if (needParens) changes.insertNodeAfter(sourceFile, last(decl.parameters), createToken(SyntaxKind.CloseParenToken));
if (!decl.type) {
const returnType = getJSDocReturnType(decl);
if (returnType) changes.insertTypeAnnotation(sourceFile, decl, transformJSDocType(returnType));
if (returnType) changes.tryInsertTypeAnnotation(sourceFile, decl, transformJSDocType(returnType));
}
}
else {
const jsdocType = Debug.assertDefined(getJSDocType(decl)); // If not defined, shouldn't have been an error to fix
Debug.assert(!decl.type); // If defined, shouldn't have been an error to fix.
changes.insertTypeAnnotation(sourceFile, decl, transformJSDocType(jsdocType));
changes.tryInsertTypeAnnotation(sourceFile, decl, transformJSDocType(jsdocType));
}
}

View File

@@ -173,7 +173,7 @@ namespace ts.codefix {
function annotate(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: textChanges.TypeAnnotatable, type: Type | undefined, program: Program): void {
const typeNode = type && getTypeNodeIfAccessible(type, declaration, program.getTypeChecker());
if (typeNode) changes.insertTypeAnnotation(sourceFile, declaration, typeNode);
if (typeNode) changes.tryInsertTypeAnnotation(sourceFile, declaration, typeNode);
}
function getTypeNodeIfAccessible(type: Type, enclosingScope: Node, checker: TypeChecker): TypeNode | undefined {

View File

@@ -360,12 +360,21 @@ namespace ts.textChanges {
}
/** Prefer this over replacing a node with another that has a type annotation, as it avoids reformatting the other parts of the node. */
public insertTypeAnnotation(sourceFile: SourceFile, node: TypeAnnotatable, type: TypeNode): void {
const end = (isFunctionLike(node)
// If no `)`, is an arrow function `x => x`, so use the end of the first parameter
? findChildOfKind(node, SyntaxKind.CloseParenToken, sourceFile) || first(node.parameters)
: node.kind !== SyntaxKind.VariableDeclaration && node.questionToken ? node.questionToken : node.name).end;
this.insertNodeAt(sourceFile, end, type, { prefix: ": " });
public tryInsertTypeAnnotation(sourceFile: SourceFile, node: TypeAnnotatable, type: TypeNode): void {
let endNode: Node;
if (isFunctionLike(node)) {
endNode = findChildOfKind(node, SyntaxKind.CloseParenToken, sourceFile);
if (!endNode) {
if (!isArrowFunction(node)) return; // Function missing parentheses, give up
// If no `)`, is an arrow function `x => x`, so use the end of the first parameter
endNode = first(node.parameters);
}
}
else {
endNode = node.kind !== SyntaxKind.VariableDeclaration && node.questionToken ? node.questionToken : node.name;
}
this.insertNodeAt(sourceFile, endNode.end, type, { prefix: ": " });
}
public insertTypeParameters(sourceFile: SourceFile, node: SignatureDeclaration, typeParameters: ReadonlyArray<TypeParameterDeclaration>): void {

View File

@@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />
// @noImplicitAny: true
// @target: esnext
////class C {
//// m() { this.x * 2; }
//// get x { return null; }
////}
// Just testing that we don't crash in `insertTypeAnnotation` from inferFromUsage
verify.not.codeFixAvailable();