refactor(ast): introduce ThisParameter (#1728)

Most TypeScript types can be eliminated during the code generation phase
by not printing the corresponding AST nodes.
The changes in this PR enable applying a similar technique to the `this`
parameter.
This commit is contained in:
magic-akari 2023-12-19 13:20:33 +08:00 committed by GitHub
parent f8b386ef58
commit a2858ed452
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 96 additions and 51 deletions

View file

@ -1530,6 +1530,22 @@ pub struct Function<'a> {
pub expression: bool,
pub generator: bool,
pub r#async: bool,
/// Declaring `this` in a Function <https://www.typescriptlang.org/docs/handbook/2/functions.html#declaring-this-in-a-function>
///
/// The JavaScript specification states that you cannot have a parameter called `this`,
/// and so TypeScript uses that syntax space to let you declare the type for `this` in the function body.
///
/// ```TypeScript
/// interface DB {
/// filterUsers(filter: (this: User) => boolean): User[];
/// }
///
/// const db = getDB();
/// const admins = db.filterUsers(function (this: User) {
/// return this.admin;
/// });
/// ```
pub this_param: Option<TSThisParameter<'a>>,
pub params: Box<'a, FormalParameters<'a>>,
pub body: Option<Box<'a, FunctionBody<'a>>>,
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
@ -1590,10 +1606,6 @@ impl<'a> FormalParameters<'a> {
pub fn parameters_count(&self) -> usize {
self.items.len() + self.rest.as_ref().map_or(0, |_| 1)
}
pub fn this_parameter(&self) -> Option<&FormalParameter<'a>> {
self.items.first().filter(|item| matches!(&item.pattern.kind, BindingPatternKind::BindingIdentifier(ident) if ident.name == "this"))
}
}
#[derive(Debug, Hash)]

View file

@ -11,6 +11,15 @@ use serde::Serialize;
#[allow(clippy::wildcard_imports)]
use crate::ast::*;
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
pub struct TSThisParameter<'a> {
#[cfg_attr(feature = "serde", serde(flatten))]
pub span: Span,
pub this: IdentifierName,
pub type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
}
/// Enum Declaration
///
/// `const_opt`enum`BindingIdentifier`{`EnumBody_opt`}
@ -548,6 +557,7 @@ pub struct TSIndexSignature<'a> {
pub struct TSCallSignatureDeclaration<'a> {
#[cfg_attr(feature = "serde", serde(flatten))]
pub span: Span,
pub this_param: Option<TSThisParameter<'a>>,
pub params: Box<'a, FormalParameters<'a>>,
pub return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
@ -570,6 +580,7 @@ pub struct TSMethodSignature<'a> {
pub computed: bool,
pub optional: bool,
pub kind: TSMethodSignatureKind,
pub this_param: Option<TSThisParameter<'a>>,
pub params: Box<'a, FormalParameters<'a>>,
pub return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
@ -703,6 +714,7 @@ pub struct TSImportType<'a> {
pub struct TSFunctionType<'a> {
#[cfg_attr(feature = "serde", serde(flatten))]
pub span: Span,
pub this_param: Option<TSThisParameter<'a>>,
pub params: Box<'a, FormalParameters<'a>>,
pub return_type: Box<'a, TSTypeAnnotation<'a>>,
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,

View file

@ -742,6 +742,15 @@ impl<'a> AstBuilder<'a> {
FormalParameter { span, pattern, accessibility, readonly, decorators }
}
pub fn ts_this_parameter(
&self,
span: Span,
this: IdentifierName,
type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
) -> TSThisParameter<'a> {
TSThisParameter { span, this, type_annotation }
}
pub fn function(
&self,
r#type: FunctionType,
@ -750,6 +759,7 @@ impl<'a> AstBuilder<'a> {
expression: bool,
generator: bool,
r#async: bool,
this_param: Option<TSThisParameter<'a>>,
params: Box<'a, FormalParameters<'a>>,
body: Option<Box<'a, FunctionBody<'a>>>,
type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
@ -763,6 +773,7 @@ impl<'a> AstBuilder<'a> {
expression,
generator,
r#async,
this_param,
params,
body,
type_parameters,
@ -1307,12 +1318,14 @@ impl<'a> AstBuilder<'a> {
pub fn ts_call_signature_declaration(
&self,
span: Span,
this_param: Option<TSThisParameter<'a>>,
params: Box<'a, FormalParameters<'a>>,
return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
) -> TSSignature<'a> {
TSSignature::TSCallSignatureDeclaration(self.alloc(TSCallSignatureDeclaration {
span,
this_param,
params,
return_type,
type_parameters,
@ -1341,6 +1354,7 @@ impl<'a> AstBuilder<'a> {
computed: bool,
optional: bool,
kind: TSMethodSignatureKind,
this_param: Option<TSThisParameter<'a>>,
params: Box<'a, FormalParameters<'a>>,
return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
@ -1351,6 +1365,7 @@ impl<'a> AstBuilder<'a> {
computed,
optional,
kind,
this_param,
params,
return_type,
type_parameters,
@ -1627,12 +1642,14 @@ impl<'a> AstBuilder<'a> {
pub fn ts_function_type(
&self,
span: Span,
this_param: Option<TSThisParameter<'a>>,
params: Box<'a, FormalParameters<'a>>,
return_type: Box<'a, TSTypeAnnotation<'a>>,
type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
) -> TSType<'a> {
TSType::TSFunctionType(self.alloc(TSFunctionType {
span,
this_param,
params,
return_type,
type_parameters,

View file

@ -357,15 +357,11 @@ impl<'a> Parser<'a> {
let value = self.parse_method(r#async, generator)?;
if kind == MethodDefinitionKind::Get
&& value.params.parameters_count() != value.params.this_parameter().map_or(0, |_| 1)
{
if kind == MethodDefinitionKind::Get && value.params.parameters_count() != 0 {
self.error(diagnostics::GetterParameters(value.params.span));
}
if kind == MethodDefinitionKind::Set
&& value.params.parameters_count() != value.params.this_parameter().map_or(1, |_| 2)
{
if kind == MethodDefinitionKind::Set && value.params.parameters_count() != 1 {
self.error(diagnostics::SetterParameters(value.params.span));
}

View file

@ -74,6 +74,18 @@ impl<'a> Parser<'a> {
Ok(self.ast.formal_parameters(self.end_span(span), params_kind, list.elements, list.rest))
}
pub(crate) fn parse_formal_parameters_with_ts_this(
&mut self,
params_kind: FormalParameterKind,
) -> Result<(Option<TSThisParameter<'a>>, Box<'a, FormalParameters<'a>>)> {
let span = self.start_span();
let list: FormalParameterList<'_> = FormalParameterList::parse(self)?;
let formal_parameters =
self.ast.formal_parameters(self.end_span(span), params_kind, list.elements, list.rest);
let this_param = list.this_param;
Ok((this_param, formal_parameters))
}
pub(crate) fn parse_function(
&mut self,
span: Span,
@ -88,7 +100,8 @@ impl<'a> Parser<'a> {
let type_parameters = self.parse_ts_type_parameters()?;
let params = self.parse_formal_parameters(FormalParameterKind::FormalParameter)?;
let (this_param, params) =
self.parse_formal_parameters_with_ts_this(FormalParameterKind::FormalParameter)?;
let return_type = self.parse_ts_return_type_annotation()?;
@ -124,6 +137,7 @@ impl<'a> Parser<'a> {
false, // expression
generator,
r#async,
this_param,
params,
body,
type_parameters,

View file

@ -234,11 +234,12 @@ impl<'a> SeparatedList<'a> for SequenceExpressionList<'a> {
pub struct FormalParameterList<'a> {
pub elements: Vec<'a, FormalParameter<'a>>,
pub rest: Option<oxc_allocator::Box<'a, RestElement<'a>>>,
pub this_param: Option<TSThisParameter<'a>>,
}
impl<'a> SeparatedList<'a> for FormalParameterList<'a> {
fn new(p: &Parser<'a>) -> Self {
Self { elements: p.ast.new_vec(), rest: None }
Self { elements: p.ast.new_vec(), rest: None, this_param: None }
}
fn open(&self) -> Kind {
@ -260,8 +261,8 @@ impl<'a> SeparatedList<'a> for FormalParameterList<'a> {
match p.cur_kind() {
Kind::This if p.ts_enabled() => {
let formal_parameter = p.parse_ts_this_parameter()?;
self.elements.push(formal_parameter);
let this_parameter = p.parse_ts_this_parameter()?;
self.this_param.replace(this_parameter);
}
Kind::Dot3 => {
let rest = p.parse_rest_element()?;

View file

@ -217,7 +217,7 @@ impl<'a> Parser<'a> {
self.expect(Kind::Get)?;
let (key, computed) = self.parse_property_name()?;
let method = self.parse_method(false, false)?;
if method.params.parameters_count() != method.params.this_parameter().map_or(0, |_| 1) {
if method.params.parameters_count() != 0 {
self.error(diagnostics::GetterParameters(method.params.span));
}
let value = self.ast.function_expression(method);
@ -241,7 +241,7 @@ impl<'a> Parser<'a> {
let (key, computed) = self.parse_property_name()?;
let method = self.parse_method(false, false)?;
if method.params.parameters_count() != method.params.this_parameter().map_or(1, |_| 2) {
if method.params.parameters_count() != 1 {
self.error(diagnostics::SetterParameters(method.params.span));
}

View file

@ -422,19 +422,16 @@ impl<'a> Parser<'a> {
))
}
pub(crate) fn parse_ts_this_parameter(&mut self) -> Result<FormalParameter<'a>> {
pub(crate) fn parse_ts_this_parameter(&mut self) -> Result<TSThisParameter<'a>> {
let span = self.start_span();
let (ident_span, name) = self.parse_identifier_kind(Kind::This);
let this = {
let (span, name) = self.parse_identifier_kind(Kind::This);
IdentifierName { span, name }
};
let type_annotation = self.parse_ts_type_annotation()?;
let kind = self.ast.binding_pattern_identifier(BindingIdentifier::new(ident_span, name));
let binding = self.ast.binding_pattern(kind, type_annotation, /* optional */ false);
Ok(self.ast.formal_parameter(
self.end_span(span),
binding,
/* accessibility */ None,
/* readonly */ false,
/* decorators */ self.ast.new_vec(),
))
Ok(self.ast.ts_this_parameter(self.end_span(span), this, type_annotation))
}
pub(crate) fn eat_decorators(&mut self) -> Result<()> {

View file

@ -802,12 +802,19 @@ impl<'a> Parser<'a> {
fn parse_ts_function_type(&mut self) -> Result<TSType<'a>> {
let span = self.start_span();
let type_parameters = self.parse_ts_type_parameters()?;
let params = self.parse_formal_parameters(FormalParameterKind::Signature)?;
let (this_param, params) =
self.parse_formal_parameters_with_ts_this(FormalParameterKind::Signature)?;
self.expect(Kind::Arrow)?;
let return_type_span = self.start_span();
let return_type = self.parse_ts_return_type()?;
let return_type = self.ast.ts_type_annotation(self.end_span(return_type_span), return_type);
Ok(self.ast.ts_function_type(self.end_span(span), params, return_type, type_parameters))
Ok(self.ast.ts_function_type(
self.end_span(span),
this_param,
params,
return_type,
type_parameters,
))
}
fn parse_ts_infer_type(&mut self) -> Result<TSType<'a>> {
@ -913,12 +920,14 @@ impl<'a> Parser<'a> {
pub(crate) fn parse_ts_call_signature_member(&mut self) -> Result<TSSignature<'a>> {
let span = self.start_span();
let type_parameters = self.parse_ts_type_parameters()?;
let params = self.parse_formal_parameters(FormalParameterKind::Signature)?;
let (this_patam, params) =
self.parse_formal_parameters_with_ts_this(FormalParameterKind::Signature)?;
let return_type = self.parse_ts_return_type_annotation()?;
self.bump(Kind::Comma);
self.bump(Kind::Semicolon);
Ok(self.ast.ts_call_signature_declaration(
self.end_span(span),
this_patam,
params,
return_type,
type_parameters,
@ -929,7 +938,8 @@ impl<'a> Parser<'a> {
let span = self.start_span();
self.expect(Kind::Get)?;
let (key, computed) = self.parse_property_name()?;
let params = self.parse_formal_parameters(FormalParameterKind::Signature)?;
let (this_param, params) =
self.parse_formal_parameters_with_ts_this(FormalParameterKind::Signature)?;
let return_type = self.parse_ts_return_type_annotation()?;
self.bump(Kind::Comma);
self.bump(Kind::Semicolon);
@ -939,6 +949,7 @@ impl<'a> Parser<'a> {
computed,
/* optional */ false,
TSMethodSignatureKind::Get,
this_param,
params,
return_type,
None,
@ -949,7 +960,8 @@ impl<'a> Parser<'a> {
let span = self.start_span();
self.expect(Kind::Set)?;
let (key, computed) = self.parse_property_name()?;
let params = self.parse_formal_parameters(FormalParameterKind::Signature)?;
let (this_param, params) =
self.parse_formal_parameters_with_ts_this(FormalParameterKind::Signature)?;
let return_type = self.parse_ts_return_type_annotation()?;
self.bump(Kind::Comma);
self.bump(Kind::Semicolon);
@ -962,6 +974,7 @@ impl<'a> Parser<'a> {
computed,
/* optional */ false,
TSMethodSignatureKind::Set,
this_param,
params,
return_type,
None,
@ -996,6 +1009,7 @@ impl<'a> Parser<'a> {
computed,
optional,
TSMethodSignatureKind::Method,
call_signature.this_param,
call_signature.params,
call_signature.return_type,
call_signature.type_parameters,

View file

@ -130,6 +130,7 @@ impl<'a> ArrowFunctions<'a> {
false,
arrow_expr.generator,
arrow_expr.r#async,
None,
self.ast.copy(&arrow_expr.params),
Some(body),
self.ast.copy(&arrow_expr.type_parameters),

View file

@ -223,18 +223,6 @@ impl<'a> VisitMut<'a> for Transformer<'a> {
});
}
fn visit_formal_parameters(&mut self, params: &mut FormalParameters<'a>) {
self.typescript.as_mut().map(|t| t.transform_formal_parameters(params));
for param in params.items.iter_mut() {
self.visit_formal_parameter(param);
}
if let Some(rest) = &mut params.rest {
self.visit_rest_element(rest);
}
}
fn visit_variable_declarator(&mut self, declarator: &mut VariableDeclarator<'a>) {
self.es2015_function_name.as_mut().map(|t| t.transform_variable_declarator(declarator));

View file

@ -33,13 +33,6 @@ impl<'a> TypeScript<'a> {
Self { ast, ctx, verbatim_module_syntax, export_name_set: FxHashSet::default() }
}
#[allow(clippy::unused_self)]
pub fn transform_formal_parameters(&self, params: &mut FormalParameters<'a>) {
if params.items.get(0).is_some_and(|param| matches!(&param.pattern.kind, BindingPatternKind::BindingIdentifier(ident) if ident.name =="this")) {
params.items.remove(0);
}
}
/// ```TypeScript
/// enum Foo {
/// X