feat(ast)!: narrow TSInterfaceHeritage::expression to TSTypeName (#24360)

## Summary

Narrow `TSInterfaceHeritage` from a general JavaScript `Expression` to `TSTypeName`.

```rust
pub struct TSInterfaceHeritage<'a> {
    pub type_name: TSTypeName<'a>,
    pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
```

## Breaking Change

The Rust AST field changes from:

```rust
expression: Expression<'a>
```

to:

```rust
type_name: TSTypeName<'a>
```

## Why?

The current AST can represent shapes such as:

```ts
interface A extends foo() {}
interface A extends A + B {}
interface A extends new Foo() {}
interface A extends true {}
```

Which is an overly wide type since all of the above variants are invalid.

This PR tightens up the AST, making these invalid variants irrepresentable.

## How to migrate?

This should be a trivial migration:
1. instead of accessing `expression` on `TSInterfaceHeritage`, now access `type_name`
2. change any pattern matching on the `type_name` field to use `TypeName` (this is a smaller subset so should allow deleting code!)

closes https://github.com/oxc-project/backlog/issues/215
This commit is contained in:
camc314 committed 2026-08-07 06:00:42 +00:00
1 parent 9323be7d32
commit 5c5cdcd522
48 files changed
+634 -149

No files matched your search

+43 -2
View File
@@ -5159,8 +5159,49 @@ function deserializeTSInterfaceHeritage(pos) {
end: (end = deserializeI32(pos + 4)),
range: [start, end],
parent,
});
node.expression = deserializeExpression(pos + 16);
}),
expression = deserializeTSTypeName(pos + 16);
if (expression.type === "TSQualifiedName") {
let object = expression.left,
{ right } = expression,
start,
end,
previous = (expression = {
__proto__: NodeProto,
type: "MemberExpression",
object,
property: right,
optional: false,
computed: false,
start: (start = expression.start),
end: (end = expression.end),
range: [start, end],
parent,
});
right.parent = previous;
for (;;) {
if (object.type !== "TSQualifiedName") {
object.parent = previous;
break;
}
let { left, right } = object;
previous = previous.object = {
__proto__: NodeProto,
type: "MemberExpression",
object: left,
property: right,
optional: false,
computed: false,
start: (start = object.start),
end: (end = object.end),
range: [start, end],
parent: previous,
};
right.parent = previous;
object = left;
}
}
node.expression = expression;
node.typeArguments = deserializeOptionBoxTSTypeParameterInstantiation(pos + 32);
parent = previousParent;
return node;
+2 -1
View File
@@ -1159,7 +1159,8 @@ pub struct TSIndexSignatureName<'a> {
pub struct TSInterfaceHeritage<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub expression: Expression<'a>,
#[estree(rename = "expression", via = TSInterfaceHeritageExpression)]
pub type_name: TSTypeName<'a>,
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
@@ -1564,7 +1564,7 @@ const _: () = {
assert!(align_of::<TSInterfaceHeritage>() == 8);
assert!(offset_of!(TSInterfaceHeritage, span) == 0);
assert!(offset_of!(TSInterfaceHeritage, node_id) == 8);
assert!(offset_of!(TSInterfaceHeritage, expression) == 16);
assert!(offset_of!(TSInterfaceHeritage, type_name) == 16);
assert!(offset_of!(TSInterfaceHeritage, type_arguments) == 32);
// Padding: 3 bytes
@@ -3401,7 +3401,7 @@ const _: () = if cfg!(target_family = "wasm") || align_of::<u64>() == 8 {
assert!(align_of::<TSInterfaceHeritage>() == 4);
assert!(offset_of!(TSInterfaceHeritage, span) == 0);
assert!(offset_of!(TSInterfaceHeritage, node_id) == 8);
assert!(offset_of!(TSInterfaceHeritage, expression) == 12);
assert!(offset_of!(TSInterfaceHeritage, type_name) == 12);
assert!(offset_of!(TSInterfaceHeritage, type_arguments) == 20);
// Padding: 3 bytes
+3 -3
View File
@@ -25377,12 +25377,12 @@ impl<'a> TSInterfaceHeritage<'a> {
///
/// ## Parameters
/// * `span`: The [`Span`] covering this node
/// * `expression`
/// * `type_name`
/// * `type_arguments`
#[inline]
pub fn new(
span: Span,
expression: Expression<'a>,
type_name: TSTypeName<'a>,
type_arguments: Option<ArenaBox<'a, TSTypeParameterInstantiation<'a>>>,
builder: &impl GetAstBuilder<'a>,
) -> Self {
@@ -25390,7 +25390,7 @@ impl<'a> TSInterfaceHeritage<'a> {
TSInterfaceHeritage {
node_id: Cell::new(builder.node_id()),
span,
expression,
type_name,
type_arguments,
}
}
@@ -5022,7 +5022,7 @@ impl<'new_alloc> CloneIn<'new_alloc> for TSInterfaceHeritage<'_> {
TSInterfaceHeritage {
node_id: CloneIn::clone_in_impl(&self.node_id, with_semantic_ids, allocator),
span: CloneIn::clone_in_impl(&self.span, with_semantic_ids, allocator),
expression: CloneIn::clone_in_impl(&self.expression, with_semantic_ids, allocator),
type_name: CloneIn::clone_in_impl(&self.type_name, with_semantic_ids, allocator),
type_arguments: CloneIn::clone_in_impl(
&self.type_arguments,
with_semantic_ids,
@@ -2320,7 +2320,7 @@ impl ContentEq for TSIndexSignatureName<'_> {
impl ContentEq for TSInterfaceHeritage<'_> {
fn content_eq(&self, other: &Self) -> bool {
ContentEq::content_eq(&self.expression, &other.expression)
ContentEq::content_eq(&self.type_name, &other.type_name)
&& ContentEq::content_eq(&self.type_arguments, &other.type_arguments)
}
}
+1 -1
View File
@@ -2804,7 +2804,7 @@ impl<'a> Dummy<'a> for TSInterfaceHeritage<'a> {
Self {
node_id: Dummy::dummy(allocator),
span: Dummy::dummy(allocator),
expression: Dummy::dummy(allocator),
type_name: Dummy::dummy(allocator),
type_arguments: Dummy::dummy(allocator),
}
}
@@ -2973,7 +2973,10 @@ impl ESTree for TSInterfaceHeritage<'_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("type", &JsonSafeString("TSInterfaceHeritage"));
state.serialize_field("expression", &self.expression);
state.serialize_field(
"expression",
&crate::serialize::ts::TSInterfaceHeritageExpression(self),
);
state.serialize_field("typeArguments", &self.type_arguments);
state.serialize_span(self.span);
state.end();
+66
View File
@@ -384,6 +384,72 @@ impl ESTree for TSClassImplementsExpression<'_, '_> {
}
}
/// Serializer for `expression` field of `TSInterfaceHeritage`.
///
/// Our AST represents `X.Y` in `interface I extends X.Y {}` as a `TSQualifiedName`.
/// TS-ESTree represents `X.Y` as a `MemberExpression`.
///
/// Where there are more parts e.g. `interface I extends X.Y.Z {}`, the `TSQualifiedName`s (Oxc)
/// or `MemberExpression`s (TS-ESTree) are nested.
#[ast_meta]
#[estree(
ts_type = "Expression",
raw_deser = "
let expression = DESER[TSTypeName](POS_OFFSET.type_name);
if (expression.type === 'TSQualifiedName') {
let object = expression.left;
const { right } = expression;
let start, end;
let previous = expression = {
type: 'MemberExpression',
object,
property: right,
optional: false,
computed: false,
start: start = expression.start,
end: end = expression.end,
...(RANGE && { range: [start, end] }),
...(PARENT && { parent }),
};
if (PARENT) right.parent = previous;
while (true) {
if (object.type !== 'TSQualifiedName') {
if (PARENT) object.parent = previous;
break;
}
const { left, right } = object;
previous = previous.object = {
type: 'MemberExpression',
object: left,
property: right,
optional: false,
computed: false,
start: start = object.start,
end: end = object.end,
...(RANGE && { range: [start, end] }),
...(PARENT && { parent: previous }),
};
if (PARENT) right.parent = previous;
object = left;
}
}
expression
"
)]
pub struct TSInterfaceHeritageExpression<'a, 'b>(pub &'b TSInterfaceHeritage<'a>);
impl ESTree for TSInterfaceHeritageExpression<'_, '_> {
#[inline] // Because it just delegates
fn serialize<S: Serializer>(&self, serializer: S) {
TSTypeNameAsMemberExpression(&self.0.type_name).serialize(serializer);
}
}
struct TSTypeNameAsMemberExpression<'a, 'b>(&'b TSTypeName<'a>);
impl ESTree for TSTypeNameAsMemberExpression<'_, '_> {
+1 -1
View File
@@ -3871,7 +3871,7 @@ pub mod walk {
let kind = AstKind::TSInterfaceHeritage(visitor.alloc(it));
visitor.enter_node(kind);
visitor.visit_span(&it.span);
visitor.visit_expression(&it.expression);
visitor.visit_ts_type_name(&it.type_name);
if let Some(type_arguments) = &it.type_arguments {
visitor.visit_ts_type_parameter_instantiation(type_arguments);
}
@@ -4085,7 +4085,7 @@ pub mod walk_mut {
let kind = AstType::TSInterfaceHeritage;
visitor.enter_node(kind);
visitor.visit_span(&mut it.span);
visitor.visit_expression(&mut it.expression);
visitor.visit_ts_type_name(&mut it.type_name);
if let Some(type_arguments) = &mut it.type_arguments {
visitor.visit_ts_type_parameter_instantiation(type_arguments);
}
+1 -1
View File
@@ -4134,7 +4134,7 @@ impl Gen for TSInterfaceDeclaration<'_> {
impl Gen for TSInterfaceHeritage<'_> {
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
self.expression.print_expr(p, Precedence::Call, ctx);
self.type_name.print(p, ctx);
if let Some(type_parameters) = &self.type_arguments {
type_parameters.print(p, ctx);
}
@@ -9408,7 +9408,7 @@ impl<'a> AstNode<'a, TSInterfaceHeritage<'a>> {
}
#[inline]
pub fn expression(&self) -> &AstNode<'a, Expression<'a>> {
pub fn type_name(&self) -> &AstNode<'a, TSTypeName<'a>> {
let following_span_start = self
.inner
.type_arguments
@@ -9417,7 +9417,7 @@ impl<'a> AstNode<'a, TSInterfaceHeritage<'a>> {
.or(Some(self.following_span_start))
.unwrap_or(0);
self.allocator.alloc(AstNode {
inner: &self.inner.expression,
inner: &self.inner.type_name,
allocator: self.allocator,
parent: AstNodes::TSInterfaceHeritage(transmute_self(self)),
following_span_start,
+2 -2
View File
@@ -1681,7 +1681,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, TSInterfaceDeclaration<'a>> {
// 3. If there are comments between the `id` and the `extends`, we use group mode.
let group_mode = extends.len() > 1
|| extends.as_ref().first().is_some_and(|first| {
(first.expression.is_member_expression() && first.type_arguments.is_none()) || {
(first.type_name.is_qualified_name() && first.type_arguments.is_none()) || {
let prev_span = type_parameters.as_ref().map_or(id.span(), GetSpan::span);
f.comments().has_comment_in_range(prev_span.end, first.span().start)
}
@@ -1925,7 +1925,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for AstNode<'a, ArenaVec<'a, TSInterfac
impl<'a> FormatWrite<'a> for AstNode<'a, TSInterfaceHeritage<'a>> {
fn write(&self, f: &mut JsFormatter<'_, 'a>) {
write!(f, [self.expression(), self.type_arguments()]);
write!(f, [self.type_name(), self.type_arguments()]);
}
}
@@ -280,7 +280,7 @@ fn get_generic_type_argument_name<'a>(
Some(ctx.source_range(class_implements.expression.span()))
}
AstKind::TSInterfaceHeritage(interface_heritage) => {
Some(ctx.source_range(interface_heritage.expression.span()))
Some(ctx.source_range(interface_heritage.type_name.span()))
}
AstKind::Class(class) => class
.super_type_arguments
@@ -1,6 +1,6 @@
use oxc_ast::{
AstKind,
ast::{Expression, IdentifierReference, TSTypeName},
ast::{IdentifierReference, TSTypeName},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
@@ -73,8 +73,8 @@ impl Rule for NoUnsafeFunctionType {
}
}
AstKind::TSInterfaceHeritage(heritage) => {
if let Expression::Identifier(ident) = &heritage.expression {
handle_function_type(ident, ctx);
if let TSTypeName::IdentifierReference(ident_ref) = &heritage.type_name {
handle_function_type(ident_ref, ctx);
}
}
_ => {}
@@ -1,8 +1,5 @@
use cow_utils::CowUtils;
use oxc_ast::{
AstKind,
ast::{Expression, TSTypeName},
};
use oxc_ast::{AstKind, ast::TSTypeName};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
@@ -80,8 +77,9 @@ impl Rule for NoWrapperObjectTypes {
}
}
AstKind::TSInterfaceHeritage(ts_interface_heritage) => {
if let Expression::Identifier(extends) = &ts_interface_heritage.expression {
(extends.name.as_str(), extends.span, extends.reference_id())
if let TSTypeName::IdentifierReference(type_name) = &ts_interface_heritage.type_name
{
(type_name.name.as_str(), type_name.span, type_name.reference_id())
} else {
return;
}
@@ -1,6 +1,6 @@
use oxc_ast::{
AstKind,
ast::{ExportDefaultDeclarationKind, Expression, TSInterfaceDeclaration, TSSignature, TSType},
ast::{ExportDefaultDeclarationKind, TSInterfaceDeclaration, TSSignature, TSType, TSTypeName},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
@@ -97,8 +97,8 @@ fn has_one_super_type(decl: &TSInterfaceDeclaration) -> bool {
_ => return true,
}
let expr = &decl.extends[0].expression;
if let Expression::Identifier(identifier) = expr {
let expr = &decl.extends[0].type_name;
if let TSTypeName::IdentifierReference(identifier) = expr {
return &identifier.name != "Function";
}
@@ -9,9 +9,10 @@ source: crates/oxc_linter/src/tester.rs
╰────
help: Add members to this interface, or use a type alias if it is intentionally empty.
× Expected `{` but found `EOF`
╭─[no_empty_interface.tsx:1:25]
× TS(1097): 'extends' list cannot be empty.
╭─[no_empty_interface.tsx:1:15]
1 │ interface Foo extends {}
· ───────
╰────
⚠ typescript(no-empty-interface): an interface declaring no members is equivalent to its supertype
+14 -14
View File
@@ -273,7 +273,7 @@ pub(crate) enum AncestorType {
TSConstructSignatureDeclarationParams = 249,
TSConstructSignatureDeclarationReturnType = 250,
TSIndexSignatureNameTypeAnnotation = 251,
TSInterfaceHeritageExpression = 252,
TSInterfaceHeritageTypeName = 252,
TSInterfaceHeritageTypeArguments = 253,
TSTypePredicateParameterName = 254,
TSTypePredicateTypeAnnotation = 255,
@@ -811,8 +811,8 @@ pub enum Ancestor<'a, 't> {
) = AncestorType::TSConstructSignatureDeclarationReturnType as u16,
TSIndexSignatureNameTypeAnnotation(TSIndexSignatureNameWithoutTypeAnnotation<'a, 't>) =
AncestorType::TSIndexSignatureNameTypeAnnotation as u16,
TSInterfaceHeritageExpression(TSInterfaceHeritageWithoutExpression<'a, 't>) =
AncestorType::TSInterfaceHeritageExpression as u16,
TSInterfaceHeritageTypeName(TSInterfaceHeritageWithoutTypeName<'a, 't>) =
AncestorType::TSInterfaceHeritageTypeName as u16,
TSInterfaceHeritageTypeArguments(TSInterfaceHeritageWithoutTypeArguments<'a, 't>) =
AncestorType::TSInterfaceHeritageTypeArguments as u16,
TSTypePredicateParameterName(TSTypePredicateWithoutParameterName<'a, 't>) =
@@ -1741,7 +1741,7 @@ impl<'a, 't> Ancestor<'a, 't> {
pub fn is_ts_interface_heritage(self) -> bool {
matches!(
self,
Self::TSInterfaceHeritageExpression(_) | Self::TSInterfaceHeritageTypeArguments(_)
Self::TSInterfaceHeritageTypeName(_) | Self::TSInterfaceHeritageTypeArguments(_)
)
}
@@ -2019,7 +2019,6 @@ impl<'a, 't> Ancestor<'a, 't> {
| Self::JSXSpreadAttributeArgument(_)
| Self::JSXSpreadChildExpression(_)
| Self::TSEnumMemberInitializer(_)
| Self::TSInterfaceHeritageExpression(_)
| Self::TSAsExpressionExpression(_)
| Self::TSSatisfiesExpressionExpression(_)
| Self::TSTypeAssertionExpression(_)
@@ -2230,6 +2229,7 @@ impl<'a, 't> Ancestor<'a, 't> {
Self::TSTypeReferenceTypeName(_)
| Self::TSQualifiedNameLeft(_)
| Self::TSClassImplementsExpression(_)
| Self::TSInterfaceHeritageTypeName(_)
)
}
@@ -2522,7 +2522,7 @@ impl<'a, 't> GetAddress for Ancestor<'a, 't> {
Self::TSConstructSignatureDeclarationParams(a) => a.address(),
Self::TSConstructSignatureDeclarationReturnType(a) => a.address(),
Self::TSIndexSignatureNameTypeAnnotation(a) => a.address(),
Self::TSInterfaceHeritageExpression(a) => a.address(),
Self::TSInterfaceHeritageTypeName(a) => a.address(),
Self::TSInterfaceHeritageTypeArguments(a) => a.address(),
Self::TSTypePredicateParameterName(a) => a.address(),
Self::TSTypePredicateTypeAnnotation(a) => a.address(),
@@ -16062,19 +16062,19 @@ impl<'a, 't> GetAddress for TSIndexSignatureNameWithoutTypeAnnotation<'a, 't> {
pub(crate) const OFFSET_TS_INTERFACE_HERITAGE_NODE_ID: usize =
offset_of!(TSInterfaceHeritage, node_id);
pub(crate) const OFFSET_TS_INTERFACE_HERITAGE_SPAN: usize = offset_of!(TSInterfaceHeritage, span);
pub(crate) const OFFSET_TS_INTERFACE_HERITAGE_EXPRESSION: usize =
offset_of!(TSInterfaceHeritage, expression);
pub(crate) const OFFSET_TS_INTERFACE_HERITAGE_TYPE_NAME: usize =
offset_of!(TSInterfaceHeritage, type_name);
pub(crate) const OFFSET_TS_INTERFACE_HERITAGE_TYPE_ARGUMENTS: usize =
offset_of!(TSInterfaceHeritage, type_arguments);
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct TSInterfaceHeritageWithoutExpression<'a, 't>(
pub struct TSInterfaceHeritageWithoutTypeName<'a, 't>(
pub(crate) *const TSInterfaceHeritage<'a>,
pub(crate) PhantomData<&'t ()>,
);
impl<'a, 't> TSInterfaceHeritageWithoutExpression<'a, 't> {
impl<'a, 't> TSInterfaceHeritageWithoutTypeName<'a, 't> {
#[inline]
pub fn node_id(self) -> &'t Cell<NodeId> {
unsafe {
@@ -16097,7 +16097,7 @@ impl<'a, 't> TSInterfaceHeritageWithoutExpression<'a, 't> {
}
}
impl<'a, 't> GetAddress for TSInterfaceHeritageWithoutExpression<'a, 't> {
impl<'a, 't> GetAddress for TSInterfaceHeritageWithoutTypeName<'a, 't> {
#[inline]
fn address(&self) -> Address {
unsafe { Address::from_ptr(self.0) }
@@ -16126,10 +16126,10 @@ impl<'a, 't> TSInterfaceHeritageWithoutTypeArguments<'a, 't> {
}
#[inline]
pub fn expression(self) -> &'t Expression<'a> {
pub fn type_name(self) -> &'t TSTypeName<'a> {
unsafe {
&*((self.0 as *const u8).add(OFFSET_TS_INTERFACE_HERITAGE_EXPRESSION)
as *const Expression<'a>)
&*((self.0 as *const u8).add(OFFSET_TS_INTERFACE_HERITAGE_TYPE_NAME)
as *const TSTypeName<'a>)
}
}
}
+4 -4
View File
@@ -5044,12 +5044,12 @@ unsafe fn walk_ts_interface_heritage<'a, Tr: Traverse<'a>>(
ctx: &mut TraverseCtx<'a>,
) {
traverser.enter_ts_interface_heritage(&mut *node, ctx);
let pop_token = ctx.push_stack(Ancestor::TSInterfaceHeritageExpression(
ancestor::TSInterfaceHeritageWithoutExpression(node, PhantomData),
let pop_token = ctx.push_stack(Ancestor::TSInterfaceHeritageTypeName(
ancestor::TSInterfaceHeritageWithoutTypeName(node, PhantomData),
));
walk_expression(
walk_ts_type_name(
traverser,
(node as *mut u8).add(ancestor::OFFSET_TS_INTERFACE_HERITAGE_EXPRESSION) as *mut Expression,
(node as *mut u8).add(ancestor::OFFSET_TS_INTERFACE_HERITAGE_TYPE_NAME) as *mut TSTypeName,
ctx,
);
if let Some(field) = &mut *((node as *mut u8)
@@ -1935,7 +1935,6 @@ impl<'a> Visit<'a> for ChildScopeCollector {
#[inline]
fn visit_ts_interface_heritage(&mut self, it: &TSInterfaceHeritage<'a>) {
self.visit_expression(&it.expression);
if let Some(type_arguments) = &it.type_arguments {
self.visit_ts_type_parameter_instantiation(type_arguments);
}
+8
View File
@@ -1214,6 +1214,14 @@ parser_diagnostics! {
.with_label(span)
};
empty_extends_clause(span: Span) => {
ts_error("1097", "'extends' list cannot be empty.").with_label(span)
};
trailing_comma_not_allowed(span: Span) => {
ts_error("1009", "Trailing comma not allowed.").with_label(span)
};
reg_exp_flag_u_and_v(span: Span) => {
ts_error("1502", "The 'u' and 'v' regular expression flags cannot be enabled at the same time")
.with_label(span)
+19 -3
View File
@@ -91,7 +91,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
let type_parameters =
if self.is_ts { self.parse_ts_type_parameters_with_variance() } else { None };
let (extends, implements) = self.parse_heritage_clause(Self::parse_class_extends_clause);
let (extends, implements) = self.parse_class_heritage_clause();
let mut super_class = None;
let mut super_type_parameters = None;
if let Some(mut extends) = extends
@@ -146,7 +146,8 @@ impl<'a, C: Config> ParserImpl<'a, C> {
loop {
match self.cur_kind() {
Kind::Extends => {
if extends.is_some() {
let duplicate_extends = extends.is_some();
if duplicate_extends {
self.error(diagnostics::extends_clause_already_seen(
self.cur_token().span(),
));
@@ -156,7 +157,10 @@ impl<'a, C: Config> ParserImpl<'a, C> {
implements_span,
));
}
extends = Some(parse_extends_clause(self));
let parsed_extends = parse_extends_clause(self);
if !duplicate_extends {
extends = Some(parsed_extends);
}
}
Kind::Implements => {
if let Some((implements_span, _)) = implements {
@@ -182,6 +186,18 @@ impl<'a, C: Config> ParserImpl<'a, C> {
(extends, implements)
}
#[expect(clippy::type_complexity)]
fn parse_class_heritage_clause(
&mut self,
) -> (
Option<
ArenaVec<'a, (Expression<'a>, Option<ArenaBox<'a, TSTypeParameterInstantiation<'a>>>)>,
>,
Option<ImplementsWithKeywordSpan<'a>>,
) {
self.parse_heritage_clause(Self::parse_class_extends_clause)
}
/// `ClassHeritage`
/// extends `LeftHandSideExpression`[?Yield, ?Await]
fn parse_class_extends_clause(
+71 -27
View File
@@ -1,4 +1,4 @@
use oxc_allocator::{ArenaBox, ArenaVec};
use oxc_allocator::{ArenaBox, ArenaVec, Dummy, GetAllocator};
use oxc_ast::ast::*;
use oxc_span::{FileExtension, GetSpan};
@@ -236,8 +236,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
}
}
}
let (extends, implements) =
self.parse_heritage_clause(Self::parse_ts_interface_extends_clause);
let (extends, implements) = self.parse_ts_interface_heritage_clause();
let body = self.parse_ts_interface_body();
let extends = extends.unwrap_or_else(|| ArenaVec::new_in(self));
self.verify_modifiers(
@@ -249,14 +248,6 @@ impl<'a, C: Config> ParserImpl<'a, C> {
if let Some((implements_kw_span, _)) = implements {
self.error(diagnostics::interface_implements(implements_kw_span));
}
for extend in &extends {
if self.fatal_error.is_some() {
break;
}
if !extend.expression.is_entity_name_expression() {
self.error(diagnostics::interface_extend(extend.span));
}
}
Declaration::new_ts_interface_declaration(
self.end_span(start),
id,
@@ -268,30 +259,65 @@ impl<'a, C: Config> ParserImpl<'a, C> {
)
}
#[expect(clippy::type_complexity)]
fn parse_ts_interface_heritage_clause(
&mut self,
) -> (
Option<ArenaVec<'a, TSInterfaceHeritage<'a>>>,
Option<(Span, ArenaVec<'a, TSClassImplements<'a>>)>,
) {
self.parse_heritage_clause(Self::parse_ts_interface_extends_clause)
}
fn parse_ts_interface_extends_clause(&mut self) -> ArenaVec<'a, TSInterfaceHeritage<'a>> {
let extends_span = self.cur_token().span();
self.bump_any(); // bump `extends`
let mut extends = ArenaVec::with_capacity_in(1, self);
if self.at(Kind::LCurly) {
self.error(diagnostics::empty_extends_clause(extends_span));
return extends;
}
loop {
let start = self.cur_start();
let mut extend = self.parse_lhs_expression_or_higher();
if self.fatal_error.is_some() {
let (type_name, type_argument) = if self.at(Kind::This)
|| self
.cur_kind()
.is_identifier_reference(self.ctx.has_yield(), self.ctx.has_await())
{
let checkpoint = self.checkpoint();
let has_this = self.at(Kind::This);
let type_name = self.parse_ts_interface_heritage_type_name(start);
let type_argument = self.try_parse_type_arguments();
if matches!(
self.cur_kind(),
Kind::Comma | Kind::LCurly | Kind::Extends | Kind::Implements | Kind::Eof
) {
if has_this {
self.error(diagnostics::interface_extend(self.end_span(start)));
}
(type_name, type_argument)
} else {
self.rewind(checkpoint);
(self.parse_invalid_ts_interface_heritage_type_name(), None)
}
} else {
(self.parse_invalid_ts_interface_heritage_type_name(), None)
};
extends.push(TSInterfaceHeritage::new(
self.end_span(start),
type_name,
type_argument,
self,
));
if !self.at(Kind::Comma) {
break;
}
let type_argument;
if let Expression::TSInstantiationExpression(expr) = extend {
let expr = expr.unbox();
extend = expr.expression;
type_argument = Some(expr.type_arguments);
} else {
type_argument = self.try_parse_type_arguments();
}
let heritage =
TSInterfaceHeritage::new(self.end_span(start), extend, type_argument, self);
extends.push(heritage);
if !self.eat(Kind::Comma) {
let comma_span = self.cur_token().span();
self.bump_any();
if self.at(Kind::LCurly) {
self.error(diagnostics::trailing_comma_not_allowed(comma_span));
break;
}
}
@@ -299,6 +325,24 @@ impl<'a, C: Config> ParserImpl<'a, C> {
extends
}
fn parse_ts_interface_heritage_type_name(&mut self, start: u32) -> TSTypeName<'a> {
let left = if self.at(Kind::This) {
self.bump_any();
TSTypeName::new_this_expression(self.end_span(start), self)
} else {
let ident = self.parse_identifier_reference();
TSTypeName::new_identifier_reference(ident.span, ident.name, self)
};
if self.at(Kind::Dot) { self.parse_ts_qualified_type_name(start, left) } else { left }
}
fn parse_invalid_ts_interface_heritage_type_name(&mut self) -> TSTypeName<'a> {
let expression = self.parse_assignment_expression_or_higher();
let expression_span = expression.span();
self.error(diagnostics::interface_extend(expression_span));
TSTypeName::dummy(self.allocator())
}
fn parse_ts_interface_body(&mut self) -> ArenaBox<'a, TSInterfaceBody<'a>> {
let start = self.cur_start();
let body_list =
+1 -1
View File
@@ -2841,7 +2841,7 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
// ^^^^^^^^^
self.current_reference_flags = ReferenceFlags::Type;
self.visit_span(&heritage.span);
self.visit_expression(&heritage.expression);
self.visit_ts_type_name(&heritage.type_name);
if let Some(type_arguments) = &heritage.type_arguments {
self.visit_ts_type_parameter_instantiation(type_arguments);
}
+14 -14
View File
@@ -273,7 +273,7 @@ pub(crate) enum AncestorType {
TSConstructSignatureDeclarationParams = 249,
TSConstructSignatureDeclarationReturnType = 250,
TSIndexSignatureNameTypeAnnotation = 251,
TSInterfaceHeritageExpression = 252,
TSInterfaceHeritageTypeName = 252,
TSInterfaceHeritageTypeArguments = 253,
TSTypePredicateParameterName = 254,
TSTypePredicateTypeAnnotation = 255,
@@ -811,8 +811,8 @@ pub enum Ancestor<'a, 't> {
) = AncestorType::TSConstructSignatureDeclarationReturnType as u16,
TSIndexSignatureNameTypeAnnotation(TSIndexSignatureNameWithoutTypeAnnotation<'a, 't>) =
AncestorType::TSIndexSignatureNameTypeAnnotation as u16,
TSInterfaceHeritageExpression(TSInterfaceHeritageWithoutExpression<'a, 't>) =
AncestorType::TSInterfaceHeritageExpression as u16,
TSInterfaceHeritageTypeName(TSInterfaceHeritageWithoutTypeName<'a, 't>) =
AncestorType::TSInterfaceHeritageTypeName as u16,
TSInterfaceHeritageTypeArguments(TSInterfaceHeritageWithoutTypeArguments<'a, 't>) =
AncestorType::TSInterfaceHeritageTypeArguments as u16,
TSTypePredicateParameterName(TSTypePredicateWithoutParameterName<'a, 't>) =
@@ -1741,7 +1741,7 @@ impl<'a, 't> Ancestor<'a, 't> {
pub fn is_ts_interface_heritage(self) -> bool {
matches!(
self,
Self::TSInterfaceHeritageExpression(_) | Self::TSInterfaceHeritageTypeArguments(_)
Self::TSInterfaceHeritageTypeName(_) | Self::TSInterfaceHeritageTypeArguments(_)
)
}
@@ -2019,7 +2019,6 @@ impl<'a, 't> Ancestor<'a, 't> {
| Self::JSXSpreadAttributeArgument(_)
| Self::JSXSpreadChildExpression(_)
| Self::TSEnumMemberInitializer(_)
| Self::TSInterfaceHeritageExpression(_)
| Self::TSAsExpressionExpression(_)
| Self::TSSatisfiesExpressionExpression(_)
| Self::TSTypeAssertionExpression(_)
@@ -2230,6 +2229,7 @@ impl<'a, 't> Ancestor<'a, 't> {
Self::TSTypeReferenceTypeName(_)
| Self::TSQualifiedNameLeft(_)
| Self::TSClassImplementsExpression(_)
| Self::TSInterfaceHeritageTypeName(_)
)
}
@@ -2522,7 +2522,7 @@ impl<'a, 't> GetAddress for Ancestor<'a, 't> {
Self::TSConstructSignatureDeclarationParams(a) => a.address(),
Self::TSConstructSignatureDeclarationReturnType(a) => a.address(),
Self::TSIndexSignatureNameTypeAnnotation(a) => a.address(),
Self::TSInterfaceHeritageExpression(a) => a.address(),
Self::TSInterfaceHeritageTypeName(a) => a.address(),
Self::TSInterfaceHeritageTypeArguments(a) => a.address(),
Self::TSTypePredicateParameterName(a) => a.address(),
Self::TSTypePredicateTypeAnnotation(a) => a.address(),
@@ -16062,19 +16062,19 @@ impl<'a, 't> GetAddress for TSIndexSignatureNameWithoutTypeAnnotation<'a, 't> {
pub(crate) const OFFSET_TS_INTERFACE_HERITAGE_NODE_ID: usize =
offset_of!(TSInterfaceHeritage, node_id);
pub(crate) const OFFSET_TS_INTERFACE_HERITAGE_SPAN: usize = offset_of!(TSInterfaceHeritage, span);
pub(crate) const OFFSET_TS_INTERFACE_HERITAGE_EXPRESSION: usize =
offset_of!(TSInterfaceHeritage, expression);
pub(crate) const OFFSET_TS_INTERFACE_HERITAGE_TYPE_NAME: usize =
offset_of!(TSInterfaceHeritage, type_name);
pub(crate) const OFFSET_TS_INTERFACE_HERITAGE_TYPE_ARGUMENTS: usize =
offset_of!(TSInterfaceHeritage, type_arguments);
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct TSInterfaceHeritageWithoutExpression<'a, 't>(
pub struct TSInterfaceHeritageWithoutTypeName<'a, 't>(
pub(crate) *const TSInterfaceHeritage<'a>,
pub(crate) PhantomData<&'t ()>,
);
impl<'a, 't> TSInterfaceHeritageWithoutExpression<'a, 't> {
impl<'a, 't> TSInterfaceHeritageWithoutTypeName<'a, 't> {
#[inline]
pub fn node_id(self) -> &'t Cell<NodeId> {
unsafe {
@@ -16097,7 +16097,7 @@ impl<'a, 't> TSInterfaceHeritageWithoutExpression<'a, 't> {
}
}
impl<'a, 't> GetAddress for TSInterfaceHeritageWithoutExpression<'a, 't> {
impl<'a, 't> GetAddress for TSInterfaceHeritageWithoutTypeName<'a, 't> {
#[inline]
fn address(&self) -> Address {
unsafe { Address::from_ptr(self.0) }
@@ -16126,10 +16126,10 @@ impl<'a, 't> TSInterfaceHeritageWithoutTypeArguments<'a, 't> {
}
#[inline]
pub fn expression(self) -> &'t Expression<'a> {
pub fn type_name(self) -> &'t TSTypeName<'a> {
unsafe {
&*((self.0 as *const u8).add(OFFSET_TS_INTERFACE_HERITAGE_EXPRESSION)
as *const Expression<'a>)
&*((self.0 as *const u8).add(OFFSET_TS_INTERFACE_HERITAGE_TYPE_NAME)
as *const TSTypeName<'a>)
}
}
}
@@ -1935,7 +1935,6 @@ impl<'a> Visit<'a> for ChildScopeCollector {
#[inline]
fn visit_ts_interface_heritage(&mut self, it: &TSInterfaceHeritage<'a>) {
self.visit_expression(&it.expression);
if let Some(type_arguments) = &it.type_arguments {
self.visit_ts_type_parameter_instantiation(type_arguments);
}
+4 -4
View File
@@ -5044,12 +5044,12 @@ unsafe fn walk_ts_interface_heritage<'a, State, Tr: Traverse<'a, State>>(
ctx: &mut TraverseCtx<'a, State>,
) {
traverser.enter_ts_interface_heritage(&mut *node, ctx);
let pop_token = ctx.push_stack(Ancestor::TSInterfaceHeritageExpression(
ancestor::TSInterfaceHeritageWithoutExpression(node, PhantomData),
let pop_token = ctx.push_stack(Ancestor::TSInterfaceHeritageTypeName(
ancestor::TSInterfaceHeritageWithoutTypeName(node, PhantomData),
));
walk_expression(
walk_ts_type_name(
traverser,
(node as *mut u8).add(ancestor::OFFSET_TS_INTERFACE_HERITAGE_EXPRESSION) as *mut Expression,
(node as *mut u8).add(ancestor::OFFSET_TS_INTERFACE_HERITAGE_TYPE_NAME) as *mut TSTypeName,
ctx,
);
if let Some(field) = &mut *((node as *mut u8)
+34 -7
View File
@@ -3778,13 +3778,40 @@ function deserializeTSIndexSignatureName(pos) {
function deserializeTSInterfaceHeritage(pos) {
let node = {
type: "TSInterfaceHeritage",
expression: null,
typeArguments: null,
start: deserializeI32(pos),
end: deserializeI32(pos + 4),
};
node.expression = deserializeExpression(pos + 16);
type: "TSInterfaceHeritage",
expression: null,
typeArguments: null,
start: deserializeI32(pos),
end: deserializeI32(pos + 4),
},
expression = deserializeTSTypeName(pos + 16);
if (expression.type === "TSQualifiedName") {
let object = expression.left,
{ right } = expression,
previous = (expression = {
type: "MemberExpression",
object,
property: right,
optional: false,
computed: false,
start: expression.start,
end: expression.end,
});
for (; object.type === "TSQualifiedName";) {
let { left, right } = object;
previous = previous.object = {
type: "MemberExpression",
object: left,
property: right,
optional: false,
computed: false,
start: object.start,
end: object.end,
};
object = left;
}
}
node.expression = expression;
node.typeArguments = deserializeOptionBoxTSTypeParameterInstantiation(pos + 32);
return node;
}
@@ -4214,8 +4214,43 @@ function deserializeTSInterfaceHeritage(pos) {
start: deserializeI32(pos),
end: deserializeI32(pos + 4),
parent,
});
node.expression = deserializeExpression(pos + 16);
}),
expression = deserializeTSTypeName(pos + 16);
if (expression.type === "TSQualifiedName") {
let object = expression.left,
{ right } = expression,
previous = (expression = {
type: "MemberExpression",
object,
property: right,
optional: false,
computed: false,
start: expression.start,
end: expression.end,
parent,
});
right.parent = previous;
for (;;) {
if (object.type !== "TSQualifiedName") {
object.parent = previous;
break;
}
let { left, right } = object;
previous = previous.object = {
type: "MemberExpression",
object: left,
property: right,
optional: false,
computed: false,
start: object.start,
end: object.end,
parent: previous,
};
right.parent = previous;
object = left;
}
}
node.expression = expression;
node.typeArguments = deserializeOptionBoxTSTypeParameterInstantiation(pos + 32);
parent = previousParent;
return node;
@@ -4232,8 +4232,39 @@ function deserializeTSInterfaceHeritage(pos) {
start: (start = deserializeI32(pos)),
end: (end = deserializeI32(pos + 4)),
range: [start, end],
};
node.expression = deserializeExpression(pos + 16);
},
expression = deserializeTSTypeName(pos + 16);
if (expression.type === "TSQualifiedName") {
let object = expression.left,
{ right } = expression,
start,
end,
previous = (expression = {
type: "MemberExpression",
object,
property: right,
optional: false,
computed: false,
start: (start = expression.start),
end: (end = expression.end),
range: [start, end],
});
for (; object.type === "TSQualifiedName";) {
let { left, right } = object;
previous = previous.object = {
type: "MemberExpression",
object: left,
property: right,
optional: false,
computed: false,
start: (start = object.start),
end: (end = object.end),
range: [start, end],
};
object = left;
}
}
node.expression = expression;
node.typeArguments = deserializeOptionBoxTSTypeParameterInstantiation(pos + 32);
return node;
}
@@ -4661,8 +4661,47 @@ function deserializeTSInterfaceHeritage(pos) {
end: (end = deserializeI32(pos + 4)),
range: [start, end],
parent,
});
node.expression = deserializeExpression(pos + 16);
}),
expression = deserializeTSTypeName(pos + 16);
if (expression.type === "TSQualifiedName") {
let object = expression.left,
{ right } = expression,
start,
end,
previous = (expression = {
type: "MemberExpression",
object,
property: right,
optional: false,
computed: false,
start: (start = expression.start),
end: (end = expression.end),
range: [start, end],
parent,
});
right.parent = previous;
for (;;) {
if (object.type !== "TSQualifiedName") {
object.parent = previous;
break;
}
let { left, right } = object;
previous = previous.object = {
type: "MemberExpression",
object: left,
property: right,
optional: false,
computed: false,
start: (start = object.start),
end: (end = object.end),
range: [start, end],
parent: previous,
};
right.parent = previous;
object = left;
}
}
node.expression = expression;
node.typeArguments = deserializeOptionBoxTSTypeParameterInstantiation(pos + 32);
parent = previousParent;
return node;
+34 -7
View File
@@ -4072,13 +4072,40 @@ function deserializeTSIndexSignatureName(pos) {
function deserializeTSInterfaceHeritage(pos) {
let node = {
type: "TSInterfaceHeritage",
expression: null,
typeArguments: null,
start: deserializeI32(pos),
end: deserializeI32(pos + 4),
};
node.expression = deserializeExpression(pos + 16);
type: "TSInterfaceHeritage",
expression: null,
typeArguments: null,
start: deserializeI32(pos),
end: deserializeI32(pos + 4),
},
expression = deserializeTSTypeName(pos + 16);
if (expression.type === "TSQualifiedName") {
let object = expression.left,
{ right } = expression,
previous = (expression = {
type: "MemberExpression",
object,
property: right,
optional: false,
computed: false,
start: expression.start,
end: expression.end,
});
for (; object.type === "TSQualifiedName";) {
let { left, right } = object;
previous = previous.object = {
type: "MemberExpression",
object: left,
property: right,
optional: false,
computed: false,
start: object.start,
end: object.end,
};
object = left;
}
}
node.expression = expression;
node.typeArguments = deserializeOptionBoxTSTypeParameterInstantiation(pos + 32);
return node;
}
@@ -4538,8 +4538,43 @@ function deserializeTSInterfaceHeritage(pos) {
start: deserializeI32(pos),
end: deserializeI32(pos + 4),
parent,
});
node.expression = deserializeExpression(pos + 16);
}),
expression = deserializeTSTypeName(pos + 16);
if (expression.type === "TSQualifiedName") {
let object = expression.left,
{ right } = expression,
previous = (expression = {
type: "MemberExpression",
object,
property: right,
optional: false,
computed: false,
start: expression.start,
end: expression.end,
parent,
});
right.parent = previous;
for (;;) {
if (object.type !== "TSQualifiedName") {
object.parent = previous;
break;
}
let { left, right } = object;
previous = previous.object = {
type: "MemberExpression",
object: left,
property: right,
optional: false,
computed: false,
start: object.start,
end: object.end,
parent: previous,
};
right.parent = previous;
object = left;
}
}
node.expression = expression;
node.typeArguments = deserializeOptionBoxTSTypeParameterInstantiation(pos + 32);
parent = previousParent;
return node;
@@ -4554,8 +4554,39 @@ function deserializeTSInterfaceHeritage(pos) {
start: (start = deserializeI32(pos)),
end: (end = deserializeI32(pos + 4)),
range: [start, end],
};
node.expression = deserializeExpression(pos + 16);
},
expression = deserializeTSTypeName(pos + 16);
if (expression.type === "TSQualifiedName") {
let object = expression.left,
{ right } = expression,
start,
end,
previous = (expression = {
type: "MemberExpression",
object,
property: right,
optional: false,
computed: false,
start: (start = expression.start),
end: (end = expression.end),
range: [start, end],
});
for (; object.type === "TSQualifiedName";) {
let { left, right } = object;
previous = previous.object = {
type: "MemberExpression",
object: left,
property: right,
optional: false,
computed: false,
start: (start = object.start),
end: (end = object.end),
range: [start, end],
};
object = left;
}
}
node.expression = expression;
node.typeArguments = deserializeOptionBoxTSTypeParameterInstantiation(pos + 32);
return node;
}
@@ -5013,8 +5013,47 @@ function deserializeTSInterfaceHeritage(pos) {
end: (end = deserializeI32(pos + 4)),
range: [start, end],
parent,
});
node.expression = deserializeExpression(pos + 16);
}),
expression = deserializeTSTypeName(pos + 16);
if (expression.type === "TSQualifiedName") {
let object = expression.left,
{ right } = expression,
start,
end,
previous = (expression = {
type: "MemberExpression",
object,
property: right,
optional: false,
computed: false,
start: (start = expression.start),
end: (end = expression.end),
range: [start, end],
parent,
});
right.parent = previous;
for (;;) {
if (object.type !== "TSQualifiedName") {
object.parent = previous;
break;
}
let { left, right } = object;
previous = previous.object = {
type: "MemberExpression",
object: left,
property: right,
optional: false,
computed: false,
start: (start = object.start),
end: (end = object.end),
range: [start, end],
parent: previous,
};
right.parent = previous;
object = left;
}
}
node.expression = expression;
node.typeArguments = deserializeOptionBoxTSTypeParameterInstantiation(pos + 32);
parent = previousParent;
return node;
@@ -10467,7 +10467,7 @@ export class TSInterfaceHeritage {
get expression() {
const internal = this.#internal;
return constructExpression(internal.pos + 16, internal.ast);
return constructTSTypeName(internal.pos + 16, internal.ast);
}
get typeArguments() {
+1 -1
View File
@@ -4404,7 +4404,7 @@ function walkTSInterfaceHeritage(pos, ast, visitors) {
if (enter !== null) enter(node);
}
walkExpression(pos + 16, ast, visitors);
walkTSTypeName(pos + 16, ast, visitors);
walkOptionBoxTSTypeParameterInstantiation(pos + 32, ast, visitors);
if (exit !== null) exit(node);
@@ -0,0 +1,3 @@
interface A extends B, {}
interface B extends this {}
interface C extends this.D {}
@@ -0,0 +1,4 @@
interface Base<T> {}
interface Derived extends Base
<string> {}
+2 -2
View File
@@ -1,3 +1,3 @@
codegen_misc Summary:
AST Parsed : 74/74 (100.00%)
Positive Passed: 74/74 (100.00%)
AST Parsed : 75/75 (100.00%)
Positive Passed: 75/75 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
formatter_misc Summary:
AST Parsed : 74/74 (100.00%)
Positive Passed: 74/74 (100.00%)
AST Parsed : 75/75 (100.00%)
Positive Passed: 75/75 (100.00%)
+20 -5
View File
@@ -3,7 +3,7 @@ commit: 06b6eae3
parser_babel Summary:
AST Parsed : 2234/2234 (100.00%)
Positive Passed: 2218/2234 (99.28%)
Negative Passed: 1727/1742 (99.14%)
Negative Passed: 1728/1742 (99.20%)
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-assert-and-assign/input.ts
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-type-assertion-and-assign/input.ts
@@ -32,8 +32,6 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/ty
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/module-namespace/module-identifier-invalid/input.ts
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/regression/keyword-qualified-type-2/input.ts
Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/estree/class-private-method/typescript-invalid-abstract/input.ts
× TS(18019): 'abstract' modifier cannot be used with a private identifier.
@@ -12970,8 +12968,10 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ──
╰────
× Expected `{` but found `EOF`
╭─[babel/packages/babel-parser/test/fixtures/typescript/class/extends-empty/input.ts:3:1]
× TS(1097): 'extends' list cannot be empty.
╭─[babel/packages/babel-parser/test/fixtures/typescript/class/extends-empty/input.ts:1:15]
1 │ interface foo extends {
· ───────
2 │ }
╰────
@@ -14261,6 +14261,13 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ─────────
╰────
× TS(2499): An interface can only extend an identifier/qualified-name with optional type arguments.
╭─[babel/packages/babel-parser/test/fixtures/typescript/interface/invalid-extends/input.ts:2:38]
1 │ // Every extend element here is invalid
2 │ interface Invalid extends (foo.bar), (foo).bar, foo[bar], foo?.bar, foo!.bar, foo(), import.meta {}
· ─────────
╰────
× TS(2499): An interface can only extend an identifier/qualified-name with optional type arguments.
╭─[babel/packages/babel-parser/test/fixtures/typescript/interface/invalid-extends/input.ts:2:49]
1 │ // Every extend element here is invalid
@@ -14702,6 +14709,14 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Either remove this `await` or add the `async` keyword to the enclosing function
× TS(2499): An interface can only extend an identifier/qualified-name with optional type arguments.
╭─[babel/packages/babel-parser/test/fixtures/typescript/regression/keyword-qualified-type-2/input.ts:3:21]
2 │ // but they are always type-checking errors.
3 │ interface A extends this.B {}
· ──────
4 │ type T = typeof var.bar;
╰────
× Identifier expected. 'this' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/typescript/regression/keyword-qualified-type-disallowed/input.ts:2:12]
1 │ import X = Y.if;
+25 -3
View File
@@ -1,7 +1,7 @@
parser_misc Summary:
AST Parsed : 74/74 (100.00%)
Positive Passed: 74/74 (100.00%)
Negative Passed: 155/155 (100.00%)
AST Parsed : 75/75 (100.00%)
Positive Passed: 75/75 (100.00%)
Negative Passed: 156/156 (100.00%)
× Cannot assign to 'arguments' in strict mode
╭─[misc/fail/arguments-eval-ambient.ts:2:13]
@@ -4182,6 +4182,28 @@ Negative Passed: 155/155 (100.00%)
╰────
help: If this is intended to be the condition for the switch statement, add `case` before it.
× TS(1009): Trailing comma not allowed.
╭─[misc/fail/ts-interface-invalid-heritage.ts:1:22]
1 │ interface A extends B, {}
· ─
2 │ interface B extends this {}
╰────
× TS(2499): An interface can only extend an identifier/qualified-name with optional type arguments.
╭─[misc/fail/ts-interface-invalid-heritage.ts:2:21]
1 │ interface A extends B, {}
2 │ interface B extends this {}
· ────
3 │ interface C extends this.D {}
╰────
× TS(2499): An interface can only extend an identifier/qualified-name with optional type arguments.
╭─[misc/fail/ts-interface-invalid-heritage.ts:3:21]
2 │ interface B extends this {}
3 │ interface C extends this.D {}
· ──────
╰────
× Expected a semicolon or an implicit semicolon after a statement, but found none
╭─[misc/fail/ts-unerasable-as-chained.ts:4:33]
3 │ // so the trailing `*` still makes the assertion impossible to erase. Syntax error.
@@ -25732,9 +25732,10 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
2 │ }
╰────
× Expected `{` but found `EOF`
╭─[typescript/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ExtendsOrImplementsClauses/parserErrorRecovery_ExtendsOrImplementsClause6.ts:1:24]
× TS(1097): 'extends' list cannot be empty.
╭─[typescript/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ExtendsOrImplementsClauses/parserErrorRecovery_ExtendsOrImplementsClause6.ts:1:13]
1 │ interface I extends { }
· ───────
╰────
× Unexpected token
+2 -2
View File
@@ -1,6 +1,6 @@
semantic_misc Summary:
AST Parsed : 74/74 (100.00%)
Positive Passed: 70/74 (94.59%)
AST Parsed : 75/75 (100.00%)
Positive Passed: 71/75 (94.67%)
semantic Error: tasks/coverage/misc/pass/declare-let-private.ts
Bindings mismatch:
after transform: ScopeId(0): ["private"]
@@ -1,3 +1,3 @@
transformer_misc Summary:
AST Parsed : 74/74 (100.00%)
Positive Passed: 74/74 (100.00%)
AST Parsed : 75/75 (100.00%)
Positive Passed: 75/75 (100.00%)