Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions crates/oxc_ast/src/ast_impl/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_semantic/src/checker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
21 changes: 21 additions & 0 deletions crates/oxc_semantic/src/checker/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
35 changes: 31 additions & 4 deletions tasks/coverage/parser_babel.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {}
Expand Down
54 changes: 49 additions & 5 deletions tasks/coverage/parser_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -1964,15 +1962,13 @@ 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"
Expect Syntax Error: "conformance/classes/classDeclarations/classAbstractKeyword/classAbstractProperties.ts"
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"
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {}
Expand Down Expand Up @@ -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 {
Expand All @@ -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 │ }
Expand All @@ -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 {}
Expand Down