mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(semantic): check for abstract ClassElements in non-abstract classes (#4127)
feat(semantic): check for abstract ClassElements in non-abstract classes chore: update coverage snapshots
This commit is contained in:
parent
c5b4be021e
commit
2f53bdf72d
5 changed files with 119 additions and 9 deletions
|
|
@ -1269,6 +1269,23 @@ impl<'a> ClassElement<'a> {
|
|||
Self::StaticBlock(_) | Self::TSIndexSignature(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Has this property been marked as abstract?
|
||||
///
|
||||
/// ```ts
|
||||
/// abstract class Foo { // <-- not considered
|
||||
/// foo: string; // <-- false
|
||||
/// abstract bar: string; // <-- true
|
||||
/// }
|
||||
/// ```
|
||||
pub fn is_abstract(&self) -> bool {
|
||||
match self {
|
||||
Self::MethodDefinition(method) => method.r#type.is_abstract(),
|
||||
Self::AccessorProperty(accessor) => accessor.r#type.is_abstract(),
|
||||
Self::PropertyDefinition(property) => property.r#type.is_abstract(),
|
||||
Self::StaticBlock(_) | Self::TSIndexSignature(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PropertyDefinitionType {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ pub fn check<'a>(node: &AstNode<'a>, ctx: &SemanticBuilder<'a>) {
|
|||
}
|
||||
AstKind::Class(class) => {
|
||||
js::check_class(class, node, ctx);
|
||||
ts::check_class(class, ctx);
|
||||
}
|
||||
AstKind::MethodDefinition(method) => {
|
||||
js::check_method_definition(method, ctx);
|
||||
|
|
|
|||
|
|
@ -185,6 +185,27 @@ pub fn check_ts_import_equals_declaration<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
/// - Abstract properties can only appear within an abstract class. (1253)
|
||||
/// - Abstract methods can only appear within an abstract class. (1244)
|
||||
fn abstract_elem_in_concrete_class(is_property: bool, span: Span) -> OxcDiagnostic {
|
||||
let (code, elem_kind) = if is_property { (1253, "properties") } else { (1244, "methods") };
|
||||
OxcDiagnostic::error(format!(
|
||||
"TS({code}): Abstract {elem_kind} can only appear within an abstract class."
|
||||
))
|
||||
.with_label(span)
|
||||
}
|
||||
|
||||
pub fn check_class<'a>(class: &Class<'a>, ctx: &SemanticBuilder<'a>) {
|
||||
if !class.r#abstract {
|
||||
for elem in &class.body.body {
|
||||
if elem.is_abstract() {
|
||||
let span = elem.property_key().map_or_else(|| elem.span(), GetSpan::span);
|
||||
ctx.error(abstract_elem_in_concrete_class(elem.is_property(), span));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn abstract_element_cannot_have_initializer(
|
||||
code: u32,
|
||||
elem_name: &str,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ commit: 12619ffe
|
|||
parser_babel Summary:
|
||||
AST Parsed : 2093/2101 (99.62%)
|
||||
Positive Passed: 2083/2101 (99.14%)
|
||||
Negative Passed: 1377/1501 (91.74%)
|
||||
Negative Passed: 1380/1501 (91.94%)
|
||||
Expect Syntax Error: "annex-b/disabled/1.1-html-comments-close/input.js"
|
||||
Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions/input.js"
|
||||
Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-if-body/input.js"
|
||||
|
|
@ -42,9 +42,6 @@ Expect Syntax Error: "esprima/invalid-syntax/migrated_0276/input.js"
|
|||
Expect Syntax Error: "typescript/cast/satisfies-const-error/input.ts"
|
||||
Expect Syntax Error: "typescript/cast/unparenthesized-assert-and-assign/input.ts"
|
||||
Expect Syntax Error: "typescript/cast/unparenthesized-type-assertion-and-assign/input.ts"
|
||||
Expect Syntax Error: "typescript/class/abstract-method-in-non-abstract-class-1/input.ts"
|
||||
Expect Syntax Error: "typescript/class/abstract-method-in-non-abstract-class-2/input.ts"
|
||||
Expect Syntax Error: "typescript/class/abstract-method-in-non-abstract-class-3/input.ts"
|
||||
Expect Syntax Error: "typescript/class/constructor-with-invalid-order-modifiers-1/input.ts"
|
||||
Expect Syntax Error: "typescript/class/constructor-with-invalid-order-modifiers-2/input.ts"
|
||||
Expect Syntax Error: "typescript/class/constructor-with-invalid-order-modifiers-3/input.ts"
|
||||
|
|
@ -9971,6 +9968,28 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts"
|
|||
2 │ func<T>(a: T);
|
||||
╰────
|
||||
|
||||
× TS(1244): Abstract methods can only appear within an abstract class.
|
||||
╭─[typescript/class/abstract-method-in-non-abstract-class-1/input.ts:2:12]
|
||||
1 │ class Foo {
|
||||
2 │ abstract method();
|
||||
· ──────
|
||||
3 │ }
|
||||
╰────
|
||||
|
||||
× TS(1244): Abstract methods can only appear within an abstract class.
|
||||
╭─[typescript/class/abstract-method-in-non-abstract-class-2/input.ts:4:16]
|
||||
3 │ return class {
|
||||
4 │ abstract m();
|
||||
· ─
|
||||
5 │ }
|
||||
╰────
|
||||
|
||||
× TS(1244): Abstract methods can only appear within an abstract class.
|
||||
╭─[typescript/class/abstract-method-in-non-abstract-class-3/input.ts:1:41]
|
||||
1 │ abstract class C { p = class { abstract method() } }
|
||||
· ──────
|
||||
╰────
|
||||
|
||||
× TS(1245): Method 'method' cannot have an implementation because it is marked abstract.
|
||||
╭─[typescript/class/abstract-method-with-body/input.ts:2:12]
|
||||
1 │ abstract class Foo {
|
||||
|
|
@ -10115,6 +10134,14 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts"
|
|||
× TS(1245): Method 'd' cannot have an implementation because it is marked abstract.
|
||||
╭─[typescript/class/generator-method-with-modifiers/input.ts:5:13]
|
||||
4 │ static *c() {}
|
||||
5 │ abstract *d() {}
|
||||
· ─
|
||||
6 │ readonly *e() {}
|
||||
╰────
|
||||
|
||||
× TS(1244): Abstract methods can only appear within an abstract class.
|
||||
╭─[typescript/class/generator-method-with-modifiers/input.ts:5:13]
|
||||
4 │ static *c() {}
|
||||
5 │ abstract *d() {}
|
||||
· ─
|
||||
6 │ readonly *e() {}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ commit: d8086f14
|
|||
parser_typescript Summary:
|
||||
AST Parsed : 5279/5283 (99.92%)
|
||||
Positive Passed: 5272/5283 (99.79%)
|
||||
Negative Passed: 1090/4875 (22.36%)
|
||||
Negative Passed: 1094/4875 (22.44%)
|
||||
Expect Syntax Error: "compiler/ClassDeclaration10.ts"
|
||||
Expect Syntax Error: "compiler/ClassDeclaration11.ts"
|
||||
Expect Syntax Error: "compiler/ClassDeclaration13.ts"
|
||||
|
|
@ -27,7 +27,6 @@ Expect Syntax Error: "compiler/ParameterList8.ts"
|
|||
Expect Syntax Error: "compiler/abstractClassInLocalScopeIsAbstract.ts"
|
||||
Expect Syntax Error: "compiler/abstractClassUnionInstantiation.ts"
|
||||
Expect Syntax Error: "compiler/abstractPropertyInConstructor.ts"
|
||||
Expect Syntax Error: "compiler/abstractPropertyNegative.ts"
|
||||
Expect Syntax Error: "compiler/accessInstanceMemberFromStaticMethod01.ts"
|
||||
Expect Syntax Error: "compiler/accessStaticMemberFromInstanceMethod01.ts"
|
||||
Expect Syntax Error: "compiler/accessorAccidentalCallDiagnostic.ts"
|
||||
|
|
@ -100,7 +99,6 @@ Expect Syntax Error: "compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts"
|
|||
Expect Syntax Error: "compiler/arrayReferenceWithoutTypeArgs.ts"
|
||||
Expect Syntax Error: "compiler/arrayToLocaleStringES5.ts"
|
||||
Expect Syntax Error: "compiler/arrowFunctionInConstructorArgument1.ts"
|
||||
Expect Syntax Error: "compiler/asiAbstract.ts"
|
||||
Expect Syntax Error: "compiler/asiPublicPrivateProtected.ts"
|
||||
Expect Syntax Error: "compiler/assignLambdaToNominalSubtypeOfFunction.ts"
|
||||
Expect Syntax Error: "compiler/assignToEnum.ts"
|
||||
|
|
@ -1964,7 +1962,6 @@ Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword
|
|||
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInheritance1.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInheritance2.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMixedWithModifiers.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractOverloads.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractOverrideWithAbstract.ts"
|
||||
|
|
@ -1972,7 +1969,6 @@ Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword
|
|||
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractSingleLineDecl.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractSuperCalls.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractUsingAbstractMethod1.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractUsingAbstractMethods2.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classAndInterfaceMergeConflictingMembers.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classExtendingClassLikeType.ts"
|
||||
Expect Syntax Error: "conformance/classes/classDeclarations/classExtendingNonConstructor.ts"
|
||||
|
|
@ -4019,6 +4015,14 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
|
|||
╰────
|
||||
help: Try insert a semicolon here
|
||||
|
||||
× TS(1253): Abstract properties can only appear within an abstract class.
|
||||
╭─[compiler/abstractPropertyNegative.ts:15:14]
|
||||
14 │ readonly ro = "readonly please";
|
||||
15 │ abstract notAllowed: string;
|
||||
· ──────────
|
||||
16 │ get concreteWithNoBody(): string;
|
||||
╰────
|
||||
|
||||
× Unexpected token
|
||||
╭─[compiler/accessorBodyInTypeContext.ts:2:15]
|
||||
1 │ type A = {
|
||||
|
|
@ -4181,6 +4185,14 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
|
|||
3 │
|
||||
╰────
|
||||
|
||||
× TS(1244): Abstract methods can only appear within an abstract class.
|
||||
╭─[compiler/asiAbstract.ts:3:12]
|
||||
2 │ class NonAbstractClass {
|
||||
3 │ abstract s();
|
||||
· ─
|
||||
4 │ }
|
||||
╰────
|
||||
|
||||
× TS1108: A 'return' statement can only be used within a function body
|
||||
╭─[compiler/asiReturn.ts:2:1]
|
||||
1 │ // This should be an error for using a return outside a function, but ASI should work properly
|
||||
|
|
@ -11131,6 +11143,14 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
|
|||
9 │ }
|
||||
╰────
|
||||
|
||||
× TS(1244): Abstract methods can only appear within an abstract class.
|
||||
╭─[conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts:50:14]
|
||||
49 │ class H { // error -- not declared abstract
|
||||
50 │ abstract baz() : number;
|
||||
· ───
|
||||
51 │ }
|
||||
╰────
|
||||
|
||||
× Unexpected token
|
||||
╭─[conformance/classes/classDeclarations/classAbstractKeyword/classAbstractManyKeywords.ts:3:1]
|
||||
2 │ export abstract class B {}
|
||||
|
|
@ -11163,6 +11183,14 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
|
|||
18 │
|
||||
╰────
|
||||
|
||||
× TS(1244): Abstract methods can only appear within an abstract class.
|
||||
╭─[conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts:2:14]
|
||||
1 │ class A {
|
||||
2 │ abstract foo();
|
||||
· ───
|
||||
3 │ }
|
||||
╰────
|
||||
|
||||
× TS(1245): Method 'foo' cannot have an implementation because it is marked abstract.
|
||||
╭─[conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts:6:14]
|
||||
5 │ class B {
|
||||
|
|
@ -11174,6 +11202,14 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
|
|||
× TS(1245): Method 'foo' cannot have an implementation because it is marked abstract.
|
||||
╭─[conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts:6:14]
|
||||
5 │ class B {
|
||||
6 │ abstract foo() {}
|
||||
· ───
|
||||
7 │ }
|
||||
╰────
|
||||
|
||||
× TS(1244): Abstract methods can only appear within an abstract class.
|
||||
╭─[conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts:6:14]
|
||||
5 │ class B {
|
||||
6 │ abstract foo() {}
|
||||
· ───
|
||||
7 │ }
|
||||
|
|
@ -11195,6 +11231,14 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts"
|
|||
3 │ }
|
||||
╰────
|
||||
|
||||
× TS(1244): Abstract methods can only appear within an abstract class.
|
||||
╭─[conformance/classes/classDeclarations/classAbstractKeyword/classAbstractUsingAbstractMethods2.ts:2:14]
|
||||
1 │ class A {
|
||||
2 │ abstract foo();
|
||||
· ───
|
||||
3 │ }
|
||||
╰────
|
||||
|
||||
× 'abstract' modifier cannot be used here.
|
||||
╭─[conformance/classes/classDeclarations/classAbstractKeyword/classAbstractWithInterface.ts:1:1]
|
||||
1 │ abstract interface I {}
|
||||
|
|
|
|||
Loading…
Reference in a new issue