Codefixes for implement interface and change extends to implements.

This commit is contained in:
Paul van Brenk 2016-10-11 14:09:21 -07:00
parent 7c96c28863
commit 1438f9a015
44 changed files with 934 additions and 1 deletions

View File

@ -0,0 +1,33 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements.code],
getCodeActions: (context: CodeFixContext) => {
const sourceFile = context.sourceFile;
const start = context.span.start;
const token = getTokenAtPosition(sourceFile, start);
const textChanges: TextChange[] = [];
if (token.kind === SyntaxKind.Identifier && token.parent.parent.kind === SyntaxKind.HeritageClause) {
const children = (<HeritageClause>token.parent.parent).getChildren();
ts.forEach(children, child => {
if (child.kind === SyntaxKind.ExtendsKeyword) {
textChanges.push({ newText: " implements", span: { start: child.pos, length: child.end - child.pos } });
}
});
}
if (textChanges.length > 0) {
return [{
description: getLocaleSpecificMessage(Diagnostics.Change_extends_to_implements),
changes: [{
fileName: sourceFile.fileName,
textChanges: textChanges
}]
}];
}
return undefined;
}
});
}

View File

@ -1 +1,3 @@
///<reference path='superFixes.ts' />
///<reference path='superFixes.ts' />
///<reference path='interfaceFixes.ts' />
///<reference path='changeExtendsToImplementsFix.ts' />

View File

@ -0,0 +1,268 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [Diagnostics.Type_0_is_not_assignable_to_type_1.code],
getCodeActions: (context: CodeFixContext) => {
const sourceFile = context.sourceFile;
const start = context.span.start;
const token = getTokenAtPosition(sourceFile, start);
const checker = context.program.getTypeChecker();
let textChanges: TextChange[] = [];
if (token.kind === SyntaxKind.Identifier && token.parent.kind === SyntaxKind.VariableDeclaration) {
const variableDeclaration = <VariableDeclaration>token.parent;
const membersAndStartPosObject = getMembersAndStartPosFromReference(variableDeclaration);
const variableMembers = membersAndStartPosObject.members;
const trackingAddedMembers: string[] = [];
const startPos: number = membersAndStartPosObject.startPos;
if (variableDeclaration.type.kind === SyntaxKind.TypeReference) {
textChanges = textChanges.concat(getChanges(variableDeclaration.type, variableMembers, startPos, checker, /*reference*/ true, trackingAddedMembers, context.newLineCharacter));
}
else if (variableDeclaration.type.kind === SyntaxKind.UnionType) {
const types = (<UnionTypeNode>variableDeclaration.type).types;
ts.forEach(types, t => {
textChanges = textChanges.concat(getChanges(t, variableMembers, startPos, checker, /*reference*/ true, trackingAddedMembers, context.newLineCharacter));
});
}
}
if (textChanges.length > 0) {
return [{
description: getLocaleSpecificMessage(Diagnostics.Implement_interface_on_reference),
changes: [{
fileName: sourceFile.fileName,
textChanges: textChanges
}]
}];
}
return undefined;
}
});
registerCodeFix({
errorCodes: [Diagnostics.Class_0_incorrectly_implements_interface_1.code],
getCodeActions: (context: CodeFixContext) => {
const sourceFile = context.sourceFile;
const start = context.span.start;
const token = getTokenAtPosition(sourceFile, start);
const checker = context.program.getTypeChecker();
let textChanges: TextChange[] = [];
if (token.kind === SyntaxKind.Identifier && token.parent.kind === SyntaxKind.ClassDeclaration) {
const classDeclaration = <ClassDeclaration>token.parent;
const startPos: number = classDeclaration.members.pos;
const classMembers = getClassMembers(classDeclaration);
const trackingAddedMembers: string[] = [];
const interfaceClauses = ts.getClassImplementsHeritageClauseElements(classDeclaration);
for (let i = 0; interfaceClauses && i < interfaceClauses.length; i++) {
textChanges = textChanges.concat(getChanges(interfaceClauses[i], classMembers, startPos, checker, /*reference*/ false, trackingAddedMembers, context.newLineCharacter));
}
}
if (textChanges.length > 0) {
return [{
description: getLocaleSpecificMessage(Diagnostics.Implement_interface_on_class),
changes: [{
fileName: sourceFile.fileName,
textChanges: textChanges
}]
}];
}
return undefined;
}
});
registerCodeFix({
errorCodes: [Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2.code],
getCodeActions: (context: CodeFixContext) => {
const sourceFile = context.sourceFile;
const start = context.span.start;
const token = getTokenAtPosition(sourceFile, start);
const checker = context.program.getTypeChecker();
let textChanges: TextChange[] = [];
if (token.kind === SyntaxKind.Identifier && token.parent.kind === SyntaxKind.ClassDeclaration) {
const classDeclaration = <ClassDeclaration>token.parent;
const startPos = classDeclaration.members.pos;
const classMembers = getClassMembers(classDeclaration);
const trackingAddedMembers: string[] = [];
const extendsClause = ts.getClassExtendsHeritageClauseElement(classDeclaration);
textChanges = textChanges.concat(getChanges(extendsClause, classMembers, startPos, checker, /*reference*/ false, trackingAddedMembers, context.newLineCharacter));
}
if (textChanges.length > 0) {
return [{
description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class),
changes: [{
fileName: sourceFile.fileName,
textChanges: textChanges
}]
}];
}
return undefined;
}
});
function getChanges(interfaceClause: Node, existingMembers: string[], startPos: number, checker: TypeChecker, reference: boolean, trackingAddedMembers: string[], newLineCharacter: string): TextChange[] {
const type = checker.getTypeAtLocation(interfaceClause);
const changesArray: TextChange[] = [];
if (type && type.symbol && type.symbol.declarations) {
const interfaceMembers = getMembers(<InterfaceDeclaration>type.symbol.declarations[0], checker);
for (let j = 0; interfaceMembers && j < interfaceMembers.length; j++) {
if (interfaceMembers[j].name && existingMembers.indexOf(interfaceMembers[j].name.getText()) === -1) {
if (interfaceMembers[j].kind === SyntaxKind.PropertySignature) {
const interfaceProperty = <PropertySignature>interfaceMembers[j];
if (trackingAddedMembers.indexOf(interfaceProperty.name.getText()) === -1) {
let propertyText = "";
if (reference) {
propertyText = `${interfaceProperty.name.getText()} : ${getDefaultValue(interfaceProperty.type.kind)},${newLineCharacter}`;
}
else {
propertyText = interfaceProperty.getText();
const stringToAdd = !(propertyText.match(/;$/)) ? `;${newLineCharacter}` : newLineCharacter;
propertyText += stringToAdd;
}
changesArray.push({ newText: propertyText, span: { start: startPos, length: 0 } });
trackingAddedMembers.push(interfaceProperty.name.getText());
}
}
else if (interfaceMembers[j].kind === SyntaxKind.MethodSignature || interfaceMembers[j].kind === SyntaxKind.MethodDeclaration) {
const interfaceMethod = <MethodSignature>interfaceMembers[j];
handleMethods(interfaceMethod, startPos, reference, trackingAddedMembers, changesArray, newLineCharacter);
}
}
}
}
if (reference && existingMembers.length === 0 && changesArray.length > 0) {
let lastValue = changesArray[changesArray.length - 1].newText;
lastValue = `${lastValue.substr(0, lastValue.length - (newLineCharacter.length + 1))} ${newLineCharacter}`;
changesArray[changesArray.length - 1].newText = lastValue;
}
return changesArray;
}
function getMembers(declaration: InterfaceDeclaration, checker: TypeChecker): TypeElement[] {
const clauses = getInterfaceBaseTypeNodes(declaration);
let result: TypeElement[] = [];
for (let i = 0; clauses && i < clauses.length; i++) {
const type = checker.getTypeAtLocation(clauses[i]);
if (type && type.symbol && type.symbol.declarations) {
result = result.concat(getMembers(<InterfaceDeclaration>type.symbol.declarations[0], checker));
}
}
if (declaration.members) {
result = result.concat(declaration.members);
}
return result;
}
function getClassMembers(classDeclaration: ClassDeclaration): string[] {
const classMembers: string[] = [];
for (let i = 0; classDeclaration.members && i < classDeclaration.members.length; i++) {
if (classDeclaration.members[i].name) {
classMembers.push(classDeclaration.members[i].name.getText());
}
}
return classMembers;
}
function getMembersAndStartPosFromReference(variableDeclaration: VariableDeclaration): { startPos: number, members: string[] } {
const children = variableDeclaration.getChildren();
const variableMembers: string[] = [];
let startPos = 0;
ts.forEach(children, child => {
if (child.kind === SyntaxKind.ObjectLiteralExpression) {
const properties = (<ObjectLiteralExpression>child).properties;
if (properties) {
startPos = properties.pos;
}
for (let j = 0; properties && j < properties.length; j++) {
if (properties[j].name) {
variableMembers.push(properties[j].name.getText());
}
}
}
});
return { startPos: startPos, members: variableMembers };
}
function getDefaultValue(kind: SyntaxKind): string {
switch (kind) {
case SyntaxKind.StringKeyword:
return '""';
case SyntaxKind.BooleanKeyword:
return "false";
case SyntaxKind.NumberKeyword:
return "0";
default:
return "null";
}
}
function handleMethods(interfaceMethod: MethodSignature, startPos: number, isReference: boolean, trackingAddedMembers: string[], textChanges: TextChange[], newLineCharacter: string) {
const methodBody = "throw new Error('Method not Implemented');";
if (trackingAddedMembers.indexOf(interfaceMethod.name.getText())) {
const methodName = interfaceMethod.name.getText();
const typeParameterArray: string[] = [];
for (let i = 0; interfaceMethod.typeParameters && i < interfaceMethod.typeParameters.length; i++) {
typeParameterArray.push(interfaceMethod.typeParameters[i].getText());
}
const parameterArray: string[] = [];
for (let j = 0; interfaceMethod.parameters && j < interfaceMethod.parameters.length; j++) {
parameterArray.push(interfaceMethod.parameters[j].getText());
}
let methodText = methodName;
if (typeParameterArray.length > 0) {
methodText += "<";
}
for (let k = 0; k < typeParameterArray.length; k++) {
methodText += typeParameterArray[k];
if (k !== typeParameterArray.length - 1) {
methodText += ",";
}
}
if (typeParameterArray.length > 0) {
methodText += ">";
}
methodText += "(";
for (let k = 0; k < parameterArray.length; k++) {
methodText += parameterArray[k];
if (k !== parameterArray.length - 1) {
methodText += ",";
}
}
methodText += `)`;
if (interfaceMethod.type) {
methodText += ":" + interfaceMethod.type.getText();
}
methodText += `{${newLineCharacter}${methodBody}${newLineCharacter}`;
methodText = isReference ? methodText.concat(`},${newLineCharacter}`) : methodText.concat(`}${newLineCharacter}`);
textChanges.push({ newText: methodText, span: { start: startPos, length: 0 } });
trackingAddedMembers.push(interfaceMethod.name.getText());
}
}
}

View File

@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// interface I1 {}
//// [|class c1 extends I1|]{}
verify.codeFixAtPosition("class c1 implements I1");

View File

@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
////interface I1 {}
////[|class c1<T extends string , U> extends I1|]{}
verify.codeFixAtPosition("class c1<T extends string , U> implements I1");

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
//// namespace N1 {
//// export interface I1 {
//// f1();
//// }
//// }
//// interface I1 {
//// f1();
//// }
////
//// class C1 implements N1.I1 {[|
//// |]}
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// f1()
//// }
////
//// interface I2 extends I1 {
////
//// }
////
////
//// class C1 implements I2 {[|
//// |]}
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,19 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
////
//// }
////
//// interface I2 extends I1 {
//// f1();
//// }
////
//// interface I3 extends I2 {}
////
//// class C1 implements I3 {[|
//// |]}
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,19 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
////
//// }
////
//// interface I2 {
//// f1();
//// }
////
//// interface I3 extends I2, I1 {}
////
//// class C1 implements I3 {[|
//// |]}
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,19 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// f1();
//// }
////
//// interface I2 {
//// f1();
//// }
////
//// interface I3 extends I2, I1 {}
////
//// class C1 implements I3 {[|
//// |]}
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,16 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// f1();
//// f2();
//// }
////
////
//// var x: I1 ={[|
//// |]f2() {}
//// }
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
},
`);

View File

@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// f1();
//// }
////
//// var x: I1 = {[|
////
//// |]}
verify.codeFixAtPosition(`
f1(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:string;
//// }
////
////
//// var x: I1 ={[|
//// |]}
verify.codeFixAtPosition(`x : ""
`);

View File

@ -0,0 +1,11 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:number;
//// }
////
//// var x: I1 ={[|
//// |]}
verify.codeFixAtPosition(`x : 0
`);

View File

@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:boolean;
//// }
////
//// var x: I1 ={[|
////
//// |]}
verify.codeFixAtPosition(`x : false
`);

View File

@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:string;
//// f1();
//// }
////
//// var x: I1 ={[|
//// |]f1(){}
//// }
verify.codeFixAtPosition(`x : "",
`);

View File

@ -0,0 +1,16 @@
/// <reference path='fourslash.ts' />
//// namespace N1 {
//// export interface I1 {
//// x: number;
//// }
//// }
//// interface I1 {
//// f1();
//// }
////
//// class C1 implements N1.I1 {[|
//// |]}
verify.codeFixAtPosition(`x: number;
`);

View File

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:number;
//// f1();
//// }
////
////
//// var x: I1 ={[|
//// |]f1(){}
//// }
verify.codeFixAtPosition(`x : 0,
`);

View File

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:boolean;
//// f1();
//// }
////
////
//// var x: I1 ={[|
//// |]f1(){}
//// }
verify.codeFixAtPosition(`x : false,
`);

View File

@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:[string];
//// }
////
////
//// var x: I1 ={[|
//// |]}
verify.codeFixAtPosition(`x : null
`);

View File

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:[string];
//// f1();
//// }
////
////
//// var x: I1 ={[|
//// |]f1(){}
//// }
verify.codeFixAtPosition(`x : null,
`);

View File

@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:Array<Number>;
//// }
////
////
//// var x: I1 ={[|
//// |]}
verify.codeFixAtPosition(`x : null
`);

View File

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:Array<Number>;
//// f1();
//// }
////
////
//// var x: I1 ={[|
//// |]f1(){}
//// }
verify.codeFixAtPosition(`x : null,
`);

View File

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:T;
//// }
////
//// class T {}
////
////
//// var x: I1 ={[|
//// |]}
verify.codeFixAtPosition(`x : null
`);

View File

@ -0,0 +1,16 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// x:T;
//// f1();
//// }
////
//// class T {}
////
////
//// var x: I1 ={[|
//// |]f1(){}
//// }
verify.codeFixAtPosition(`x : null,
`);

View File

@ -0,0 +1,21 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// f1();
//// }
////
//// interface I2 {
//// f2();
//// }
////
//// var x: I1|I2 ={[|
////
//// |]}
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
}
f2(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// abstract class C1 {
//// f1(){}
//// }
////
//// class C2 implements C1 {[|
//// |]f2(){}
//// }
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,16 @@
/// <reference path='fourslash.ts' />
//// namespace N1 {
//// export interface I1 {
//// x: number
//// }
//// }
//// interface I1 {
//// f1();
//// }
////
//// class C1 implements N1.I1 {[|
//// |]}
verify.codeFixAtPosition(`x: number;
`);

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
//// abstract class C1 {
//// f1(){}
//// }
////
//// abstract class C2 extends C1 {
////
//// }
////
//// class C3 implements C2 {[|
//// |]f2(){}
//// }
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
//// abstract class C1 {
//// abstract f1();
//// }
////
//// abstract class C2 extends C1 {
////
//// }
////
//// class C3 implements C2 {[|
//// |]f2(){}
//// }
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
//// abstract class C1 {
//// abstract f1<T>();
//// }
////
//// abstract class C2 extends C1 {
////
//// }
////
//// class C3 implements C2 {[|
//// |]f2(){}
//// }
verify.codeFixAtPosition(`f1<T>(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
//// abstract class C1 {
//// abstract f1<T extends number>();
//// }
////
//// abstract class C2 extends C1 {
////
//// }
////
//// class C3 implements C2 {[|
//// |]f2(){}
//// }
verify.codeFixAtPosition(`f1<T extends number>(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// abstract class C1 {
//// abstract f1<T extends number>();
//// }
////
//// class C2 extends C1 {[|
////
//// |]}
verify.codeFixAtPosition(`f1<T extends number>(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,16 @@
/// <reference path='fourslash.ts' />
//// abstract class C1 {
//// abstract f1<T extends number>();
//// }
////
//// interface I1 extends C1 {}
////
//// class C2 implements I1 {[|
////
//// |]}
verify.codeFixAtPosition(`f1<T extends number>(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
//// abstract class C1 {
////
//// }
////
//// abstract class C2 {
//// abstract f1<T extends number>();
//// }
////
//// interface I1 extends C1, C2 {}
////
//// class C3 implements I1 {[|
////
//// |]}
verify.codeFixAtPosition(`f1<T extends number>(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
//// abstract class C1 {
//// abstract f1<T extends number>();
//// }
////
//// abstract class C2 extends C1{
////
//// }
////
//// interface I1 extends C2 {}
////
//// class C3 implements I1 {[|
////
//// |]}
verify.codeFixAtPosition(`f1<T extends number>(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,11 @@
/// <reference path='fourslash.ts' />
//// abstract class C2 {
//// abstract f1<T extends number>();
//// }
////
//// var x: C2 = {[| |]}
verify.codeFixAtPosition(`f1<T extends number>(){
throw new Error('Method not Implemented');
}`);

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
//// namespace N1 {
//// export interface I1 {
//// f1():string;
//// }
//// }
//// interface I1 {
//// f1();
//// }
////
//// class C1 implements N1.I1 {[|
//// |]}
verify.codeFixAtPosition(`f1():string{
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
//// namespace N1 {
//// export interface I1 {
//// f1()
//// }
//// }
//// interface I1 {
//// f1();
//// }
////
//// class C1 implements N1.I1 {[|
//// |]}
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
//// namespace N1 {
//// export interface I1 {
//// f1(x: number, y: string)
//// }
//// }
//// interface I1 {
//// f1();
//// }
////
//// class C1 implements N1.I1 {[|
//// |]}
verify.codeFixAtPosition(`f1(x: number,y: string){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// f1(x: number, y: T);
//// }
////
//// class T {}
////
//// class C1 implements I1 {[|
//// |]}
verify.codeFixAtPosition(`f1(x: number,y: T){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// f1<T>(x: number, y: C2);
//// }
////
//// class C2 {}
////
//// class C1 implements I1 {[|
//// |]}
verify.codeFixAtPosition(`f1<T>(x: number,y: C2){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
//// f1<T extends string>(x: number, y: C2);
//// }
////
//// class C2 {}
////
//// class C1 implements I1 {[|
//// |]}
verify.codeFixAtPosition(`f1<T extends string>(x: number,y: C2){
throw new Error('Method not Implemented');
}
`);

View File

@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
//// interface I1 {
////
//// }
////
//// interface I2 extends I1 {
//// f1();
//// }
////
//// class C1 implements I2 {[|
//// |]}
verify.codeFixAtPosition(`f1(){
throw new Error('Method not Implemented');
}
`);