mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-14 22:29:17 +00:00
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:
1 parent
9323be7d32
commit
5c5cdcd522
48 files changed
+634
-149
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<'_, '_> {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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> {}
|
||||
@@ -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%)
|
||||
@@ -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%)
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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%)
|
||||
Reference in new issue
Block a user