Skip to content

Commit 3cfac08

Browse files
author
Arthur Ozga
committed
abstract class expr instantiation and abstract fix works with class expr
1 parent ad01110 commit 3cfac08

File tree

4 files changed

+81
-38
lines changed

4 files changed

+81
-38
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19710,6 +19710,9 @@ namespace ts {
1971019710
// This is a declaration, call getSymbolOfNode
1971119711
return getSymbolOfNode(node.parent);
1971219712
}
19713+
else if (node.kind === SyntaxKind.ClassKeyword && node.parent.kind === SyntaxKind.ClassExpression) {
19714+
return getSymbolOfNode(node.parent);
19715+
}
1971319716
else if (isLiteralComputedPropertyDeclarationName(node)) {
1971419717
return getSymbolOfNode(node.parent.parent);
1971519718
}

src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,58 @@
22
namespace ts.codefix {
33
registerCodeFix({
44
errorCodes: [Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2.code],
5-
getCodeActions: (context: CodeFixContext) => {
6-
const sourceFile = context.sourceFile;
7-
const start = context.span.start;
8-
const token = getTokenAtPosition(sourceFile, start);
9-
const checker = context.program.getTypeChecker();
10-
11-
if (token.kind === SyntaxKind.Identifier && isClassLike(token.parent)) {
12-
const classDecl = <ClassDeclaration>token.parent;
13-
const startPos = classDecl.members.pos;
14-
15-
const InstantiatedExtendsType = checker.getTypeFromTypeReference(getClassExtendsHeritageClauseElement(classDecl)) as InterfaceType;
16-
// Note that this is ultimately derived from a map indexed by symbol names,
17-
// so duplicates cannot occur.
18-
const extendsSymbols = checker.getPropertiesOfType(InstantiatedExtendsType);
19-
const abstractAndNonPrivateExtendsSymbols = extendsSymbols.filter(symbolPointsToNonPrivateAndAbstractMember);
20-
21-
const insertion = getMissingMembersInsertion(classDecl, abstractAndNonPrivateExtendsSymbols, checker, context.newLineCharacter);
22-
23-
if (insertion.length) {
24-
return [{
25-
description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class),
26-
changes: [{
27-
fileName: sourceFile.fileName,
28-
textChanges: [{
29-
span: { start: startPos, length: 0 },
30-
newText: insertion
31-
}]
32-
}]
33-
}];
34-
}
35-
}
5+
getCodeActions: getActionForClassLikeMissingAbstractMember
6+
});
7+
8+
registerCodeFix({
9+
errorCodes: [Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1.code],
10+
getCodeActions: getActionForClassLikeMissingAbstractMember
11+
});
12+
13+
function getActionForClassLikeMissingAbstractMember(context: CodeFixContext): CodeAction[] | undefined {
14+
const sourceFile = context.sourceFile;
15+
const start = context.span.start;
16+
// This is the identifier in the case of a class declaration
17+
// or the class keyword token in the case of a class expression.
18+
const token = getTokenAtPosition(sourceFile, start);
19+
const checker = context.program.getTypeChecker();
20+
21+
if (isClassLike(token.parent)) {
22+
const classDecl = token.parent as ClassLikeDeclaration;
23+
const startPos = classDecl.members.pos;
3624

37-
return undefined;
25+
const classType = checker.getTypeAtLocation(classDecl) as InterfaceType;
26+
const instantiatedExtendsType = checker.getBaseTypes(classType)[0];
3827

39-
function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean {
40-
const decls = symbol.getDeclarations();
41-
Debug.assert(!!(decls && decls.length > 0));
42-
const flags = getModifierFlags(decls[0]);
43-
return !(flags & ModifierFlags.Private) && !!(flags & ModifierFlags.Abstract);
28+
// Note that this is ultimately derived from a map indexed by symbol names,
29+
// so duplicates cannot occur.
30+
const extendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType);
31+
const abstractAndNonPrivateExtendsSymbols = extendsSymbols.filter(symbolPointsToNonPrivateAndAbstractMember);
32+
33+
const insertion = getMissingMembersInsertion(classDecl, abstractAndNonPrivateExtendsSymbols, checker, context.newLineCharacter);
34+
35+
if (insertion.length) {
36+
return [{
37+
description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class),
38+
changes: [{
39+
fileName: sourceFile.fileName,
40+
textChanges: [{
41+
span: { start: startPos, length: 0 },
42+
newText: insertion
43+
}]
44+
}]
45+
}];
4446
}
4547
}
46-
});
48+
49+
return undefined;
50+
51+
}
52+
53+
function symbolPointsToNonPrivateAndAbstractMember(symbol: Symbol): boolean {
54+
const decls = symbol.getDeclarations();
55+
Debug.assert(!!(decls && decls.length > 0));
56+
const flags = getModifierFlags(decls[0]);
57+
return !(flags & ModifierFlags.Private) && !!(flags & ModifierFlags.Abstract);
58+
}
4759
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
//// function foo<T>(a: T) {
4+
//// abstract class C<U> {
5+
//// abstract a: T | U;
6+
//// }
7+
//// return C;
8+
//// }
9+
////
10+
//// let B = class extends foo("s")<number> {[| |]}
11+
12+
verify.rangeAfterCodeFix(`
13+
a: string | number;
14+
`);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
//// function foo<T>(a: T) {
4+
//// abstract class C<U> {
5+
//// abstract a: T | U;
6+
//// }
7+
//// return C;
8+
//// }
9+
////
10+
//// class B extends foo("s")<number> {[| |]}
11+
12+
verify.rangeAfterCodeFix(`
13+
a: string | number;
14+
`);

0 commit comments

Comments
 (0)