mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 17:54:15 +03:00
feat(es/parser): Report errors for non-abstract members in an abstract class (#3917)
This commit is contained in:
parent
dbdfb3a449
commit
16182d586f
@ -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."
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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)?));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,4 @@
|
||||
class A {
|
||||
abstract a = 123;
|
||||
abstract foo() {};
|
||||
}
|
@ -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() {};
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
@ -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() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user