feat(es/parser): Report errors for non-abstract members in an abstract class (#3917)

This commit is contained in:
Austaras 2022-03-08 15:38:12 +08:00 committed by GitHub
parent dbdfb3a449
commit 16182d586f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 121 additions and 56 deletions

View File

@ -227,6 +227,9 @@ pub enum SyntaxError {
TS1196,
TS1242,
TS1243(JsWord, JsWord),
TS1244,
TS1245,
TS1267,
TS1383,
TS2206,
TS2207,
@ -561,11 +564,16 @@ impl SyntaxError {
SyntaxError::TS1242 => {
"`abstract` modifier can only appear on a class or method declaration".into()
}
SyntaxError::TS1244 => {
"Abstract methods can only appear within an abstract class.".into()
}
SyntaxError::TS1243(left, right) => format!(
"'{}' modifier cannot be used with '{}' modifier.",
left, right
)
.into(),
SyntaxError::TS1245 => "Abstract method cannot have an implementation.".into(),
SyntaxError::TS1267 => "Abstract property cannot have an initializer.".into(),
SyntaxError::TS1383 => "Only named exports may use 'export type'.".into(),
SyntaxError::TS2206 => "The 'type' modifier cannot be used on a named import when \
'import type' is used on its import statement."

View File

@ -51,8 +51,9 @@ impl<'a, I: Tokens> Parser<I> {
start: BytePos,
class_start: BytePos,
decorators: Vec<Decorator>,
is_abstract: bool,
) -> PResult<Decl> {
self.parse_class(start, class_start, decorators)
self.parse_class(start, class_start, decorators, is_abstract)
}
pub(super) fn parse_class_expr(
@ -60,7 +61,7 @@ impl<'a, I: Tokens> Parser<I> {
start: BytePos,
decorators: Vec<Decorator>,
) -> PResult<Box<Expr>> {
self.parse_class(start, start, decorators)
self.parse_class(start, start, decorators, false)
}
pub(super) fn parse_default_class(
@ -68,8 +69,9 @@ impl<'a, I: Tokens> Parser<I> {
start: BytePos,
class_start: BytePos,
decorators: Vec<Decorator>,
is_abstract: bool,
) -> PResult<ExportDefaultDecl> {
self.parse_class(start, class_start, decorators)
self.parse_class(start, class_start, decorators, is_abstract)
}
/// Not generic
@ -179,13 +181,34 @@ impl<'a, I: Tokens> Parser<I> {
start: BytePos,
class_start: BytePos,
decorators: Vec<Decorator>,
is_abstract: bool,
) -> PResult<T>
where
T: OutputType,
{
let (ident, class) =
let (ident, mut class) =
self.parse_class_inner(start, class_start, decorators, T::IS_IDENT_REQUIRED)?;
if is_abstract {
class.is_abstract = true
} else {
for member in class.body.iter() {
match member {
ClassMember::ClassProp(ClassProp {
is_abstract: true,
span,
..
})
| ClassMember::Method(ClassMethod {
span,
is_abstract: true,
..
}) => self.emit_err(*span, SyntaxError::TS1244),
_ => (),
}
}
}
match T::finish_class(span!(self, start), ident, class) {
Ok(v) => Ok(v),
Err(kind) => syntax_error!(self, kind),
@ -955,22 +978,28 @@ impl<'a, I: Tokens> Parser<I> {
type_ann,
}
.into(),
Either::Right(key) => ClassProp {
span: span!(p, start),
key,
value,
is_static,
decorators,
accessibility,
is_abstract,
is_optional,
is_override,
readonly,
declare,
definite,
type_ann,
Either::Right(key) => {
let span = span!(p, start);
if is_abstract && value.is_some() {
p.emit_err(span, SyntaxError::TS1267)
}
ClassProp {
span,
key,
value,
is_static,
decorators,
accessibility,
is_abstract,
is_optional,
is_override,
readonly,
declare,
definite,
type_ann,
}
.into()
}
.into(),
})
})
}
@ -1281,20 +1310,26 @@ impl<'a, I: Tokens> Parser<I> {
kind,
}
.into()),
Either::Right(key) => Ok(ClassMethod {
span: span!(self, start),
Either::Right(key) => {
let span = span!(self, start);
if is_abstract && function.body.is_some() {
self.emit_err(span, SyntaxError::TS1245)
}
Ok(ClassMethod {
span,
accessibility,
is_abstract,
is_optional,
is_override,
accessibility,
is_abstract,
is_optional,
is_override,
is_static,
key,
function,
kind,
is_static,
key,
function,
kind,
}
.into())
}
.into()),
}
}
}

View File

@ -198,7 +198,7 @@ impl<'a, I: Tokens> Parser<I> {
self.emit_err(self.input.cur_span(), SyntaxError::DeclNotAllowed);
}
return self
.parse_class_decl(start, start, decorators)
.parse_class_decl(start, start, decorators, false)
.map(Stmt::from);
}

View File

@ -457,15 +457,9 @@ impl<'a, I: Tokens> Parser<I> {
assert_and_bump!(self, "abstract");
let _ = cur!(self, true);
let mut class = self.parse_default_class(start, class_start, decorators)?;
match class {
ExportDefaultDecl {
decl: DefaultDecl::Class(ClassExpr { ref mut class, .. }),
..
} => class.is_abstract = true,
_ => unreachable!(),
}
return Ok(class.into());
return self
.parse_default_class(start, class_start, decorators, true)
.map(ModuleDecl::ExportDefaultDecl);
}
if is!(self, "abstract") && peeked_is!(self, "interface") {
self.emit_err(self.input.cur_span(), SyntaxError::TS1242);
@ -488,7 +482,7 @@ impl<'a, I: Tokens> Parser<I> {
if is!(self, "class") {
let class_start = cur_pos!(self);
let decl = self.parse_default_class(start, class_start, decorators)?;
let decl = self.parse_default_class(start, class_start, decorators, false)?;
return Ok(ModuleDecl::ExportDefaultDecl(decl));
} else if is!(self, "async")
&& peeked_is!(self, "function")
@ -515,7 +509,7 @@ impl<'a, I: Tokens> Parser<I> {
let decl = if !type_only && is!(self, "class") {
let class_start = cur_pos!(self);
self.parse_class_decl(start, class_start, decorators)?
self.parse_class_decl(start, class_start, decorators, false)?
} else if !type_only
&& is!(self, "async")
&& peeked_is!(self, "function")

View File

@ -2282,7 +2282,7 @@ impl<I: Tokens> Parser<I> {
if is!(p, "class") {
return p
.parse_class_decl(start, start, decorators)
.parse_class_decl(start, start, decorators, false)
.map(|decl| match decl {
Decl::Class(c) => Decl::Class(ClassDecl {
declare: true,
@ -2394,19 +2394,7 @@ impl<I: Tokens> Parser<I> {
if next {
bump!(self);
}
let mut decl = self.parse_class_decl(start, start, decorators)?;
match decl {
Decl::Class(ClassDecl {
class:
Class {
ref mut is_abstract,
..
},
..
}) => *is_abstract = true,
_ => unreachable!(),
}
return Ok(Some(decl));
return Ok(Some(self.parse_class_decl(start, start, decorators, true)?));
}
}

View File

@ -0,0 +1,4 @@
class A {
abstract a = 123;
abstract foo() {};
}

View File

@ -0,0 +1,24 @@
error: Abstract property cannot have an initializer.
--> $DIR/tests/typescript-errors/class/abstract/input.ts:2:5
|
2 | abstract a = 123;
| ^^^^^^^^^^^^^^^^^
error: Abstract method cannot have an implementation.
--> $DIR/tests/typescript-errors/class/abstract/input.ts:3:5
|
3 | abstract foo() {};
| ^^^^^^^^^^^^^^^^^
error: Abstract methods can only appear within an abstract class.
--> $DIR/tests/typescript-errors/class/abstract/input.ts:2:5
|
2 | abstract a = 123;
| ^^^^^^^^^^^^^^^^^
error: Abstract methods can only appear within an abstract class.
--> $DIR/tests/typescript-errors/class/abstract/input.ts:3:5
|
3 | abstract foo() {};
| ^^^^^^^^^^^^^^^^^

View File

@ -10,9 +10,21 @@ error: 'abstract' modifier already seen.
3 | abstract abstract t() {}
| ^^^^^^^^
error: Abstract method cannot have an implementation.
--> $DIR/tests/typescript-errors/class/duplicated-modifiers/input.ts:3:3
|
3 | abstract abstract t() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: 'override' modifier already seen.
--> $DIR/tests/typescript-errors/class/duplicated-modifiers/input.ts:4:12
|
4 | override override e() {}
| ^^^^^^^^
error: Abstract methods can only appear within an abstract class.
--> $DIR/tests/typescript-errors/class/duplicated-modifiers/input.ts:3:3
|
3 | abstract abstract t() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^