abstract class expr instantiation and abstract fix works with class expr

This commit is contained in:
Arthur Ozga 2016-12-14 08:14:06 -08:00
parent ad011108e6
commit 3cfac081d5
4 changed files with 78 additions and 35 deletions

View file

@ -19710,6 +19710,9 @@ namespace ts {
// This is a declaration, call getSymbolOfNode
return getSymbolOfNode(node.parent);
}
else if (node.kind === SyntaxKind.ClassKeyword && node.parent.kind === SyntaxKind.ClassExpression) {
return getSymbolOfNode(node.parent);
}
else if (isLiteralComputedPropertyDeclarationName(node)) {
return getSymbolOfNode(node.parent.parent);
}

View file

@ -2,46 +2,58 @@
namespace ts.codefix {
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();
getCodeActions: getActionForClassLikeMissingAbstractMember
});
if (token.kind === SyntaxKind.Identifier && isClassLike(token.parent)) {
const classDecl = <ClassDeclaration>token.parent;
const startPos = classDecl.members.pos;
registerCodeFix({
errorCodes: [Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1.code],
getCodeActions: getActionForClassLikeMissingAbstractMember
});
const InstantiatedExtendsType = checker.getTypeFromTypeReference(getClassExtendsHeritageClauseElement(classDecl)) as InterfaceType;
// Note that this is ultimately derived from a map indexed by symbol names,
// so duplicates cannot occur.
const extendsSymbols = checker.getPropertiesOfType(InstantiatedExtendsType);
const abstractAndNonPrivateExtendsSymbols = extendsSymbols.filter(symbolPointsToNonPrivateAndAbstractMember);
function getActionForClassLikeMissingAbstractMember(context: CodeFixContext): CodeAction[] | undefined {
const sourceFile = context.sourceFile;
const start = context.span.start;
// This is the identifier in the case of a class declaration
// or the class keyword token in the case of a class expression.
const token = getTokenAtPosition(sourceFile, start);
const checker = context.program.getTypeChecker();
const insertion = getMissingMembersInsertion(classDecl, abstractAndNonPrivateExtendsSymbols, checker, context.newLineCharacter);
if (isClassLike(token.parent)) {
const classDecl = token.parent as ClassLikeDeclaration;
const startPos = classDecl.members.pos;
if (insertion.length) {
return [{
description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: startPos, length: 0 },
newText: insertion
}]
const classType = checker.getTypeAtLocation(classDecl) as InterfaceType;
const instantiatedExtendsType = checker.getBaseTypes(classType)[0];
// Note that this is ultimately derived from a map indexed by symbol names,
// so duplicates cannot occur.
const extendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType);
const abstractAndNonPrivateExtendsSymbols = extendsSymbols.filter(symbolPointsToNonPrivateAndAbstractMember);
const insertion = getMissingMembersInsertion(classDecl, abstractAndNonPrivateExtendsSymbols, checker, context.newLineCharacter);
if (insertion.length) {
return [{
description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class),
changes: [{
fileName: sourceFile.fileName,
textChanges: [{
span: { start: startPos, length: 0 },
newText: insertion
}]
}];
}
}
return undefined;
function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean {
const decls = symbol.getDeclarations();
Debug.assert(!!(decls && decls.length > 0));
const flags = getModifierFlags(decls[0]);
return !(flags & ModifierFlags.Private) && !!(flags & ModifierFlags.Abstract);
}]
}];
}
}
});
return undefined;
}
function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean {
const decls = symbol.getDeclarations();
Debug.assert(!!(decls && decls.length > 0));
const flags = getModifierFlags(decls[0]);
return !(flags & ModifierFlags.Private) && !!(flags & ModifierFlags.Abstract);
}
}

View file

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// function foo<T>(a: T) {
//// abstract class C<U> {
//// abstract a: T | U;
//// }
//// return C;
//// }
////
//// let B = class extends foo("s")<number> {[| |]}
verify.rangeAfterCodeFix(`
a: string | number;
`);

View file

@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// function foo<T>(a: T) {
//// abstract class C<U> {
//// abstract a: T | U;
//// }
//// return C;
//// }
////
//// class B extends foo("s")<number> {[| |]}
verify.rangeAfterCodeFix(`
a: string | number;
`);