mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
Closes #5736. Introduce a `NONE` type which can be used for any `AstBuilder` method call param which expects an `IntoIn<'a, Something<'a>>`, where otherwise you have to provide a verbose type annotation. Before: ```rs ast.arrow_function_expression( SPAN, is_expression, is_async, None::<TSTypeParameterDeclaration>, params, None::<TSTypeAnnotation>, body, ) ``` After: ```rs ast.arrow_function_expression(SPAN, is_expression, is_async, NONE, params, NONE, body) ```
169 lines
6.5 KiB
Rust
169 lines
6.5 KiB
Rust
use oxc_allocator::Box;
|
|
#[allow(clippy::wildcard_imports)]
|
|
use oxc_ast::{ast::*, NONE};
|
|
use oxc_span::{Span, SPAN};
|
|
|
|
use crate::{
|
|
diagnostics::{
|
|
function_must_have_explicit_return_type, implicitly_adding_undefined_to_type,
|
|
parameter_must_have_explicit_type,
|
|
},
|
|
formal_parameter_binding_pattern::FormalParameterBindingPattern,
|
|
IsolatedDeclarations,
|
|
};
|
|
|
|
impl<'a> IsolatedDeclarations<'a> {
|
|
pub fn transform_function(
|
|
&mut self,
|
|
func: &Function<'a>,
|
|
declare: Option<bool>,
|
|
) -> Option<Box<'a, Function<'a>>> {
|
|
if func.declare {
|
|
None
|
|
} else {
|
|
let return_type = self.infer_function_return_type(func);
|
|
if return_type.is_none() {
|
|
self.error(function_must_have_explicit_return_type(get_function_span(func)));
|
|
}
|
|
let params = self.transform_formal_parameters(&func.params);
|
|
Some(self.ast.alloc_function(
|
|
func.r#type,
|
|
func.span,
|
|
// SAFETY: `ast.copy` is unsound! We need to fix.
|
|
unsafe { self.ast.copy(&func.id) },
|
|
false,
|
|
false,
|
|
declare.unwrap_or_else(|| self.is_declare()),
|
|
// SAFETY: `ast.copy` is unsound! We need to fix.
|
|
unsafe { self.ast.copy(&func.type_parameters) },
|
|
// SAFETY: `ast.copy` is unsound! We need to fix.
|
|
unsafe { self.ast.copy(&func.this_param) },
|
|
params,
|
|
return_type,
|
|
NONE,
|
|
))
|
|
}
|
|
}
|
|
|
|
pub fn transform_formal_parameter(
|
|
&self,
|
|
param: &FormalParameter<'a>,
|
|
is_remaining_params_have_required: bool,
|
|
) -> Option<FormalParameter<'a>> {
|
|
let pattern = ¶m.pattern;
|
|
if let BindingPatternKind::AssignmentPattern(pattern) = &pattern.kind {
|
|
if pattern.left.kind.is_destructuring_pattern()
|
|
&& pattern.left.type_annotation.is_none()
|
|
{
|
|
self.error(parameter_must_have_explicit_type(param.span));
|
|
return None;
|
|
}
|
|
}
|
|
|
|
let is_assignment_pattern = pattern.kind.is_assignment_pattern();
|
|
let mut pattern =
|
|
if let BindingPatternKind::AssignmentPattern(pattern) = ¶m.pattern.kind {
|
|
// SAFETY: `ast.copy` is unsound! We need to fix.
|
|
unsafe { self.ast.copy(&pattern.left) }
|
|
} else {
|
|
// SAFETY: `ast.copy` is unsound! We need to fix.
|
|
unsafe { self.ast.copy(¶m.pattern) }
|
|
};
|
|
|
|
FormalParameterBindingPattern::remove_assignments_from_kind(self.ast, &mut pattern.kind);
|
|
|
|
if is_assignment_pattern || pattern.type_annotation.is_none() {
|
|
let type_annotation = pattern
|
|
.type_annotation
|
|
.as_ref()
|
|
.map(|type_annotation| {
|
|
// SAFETY: `ast.copy` is unsound! We need to fix.
|
|
unsafe { self.ast.copy(&type_annotation.type_annotation) }
|
|
})
|
|
.or_else(|| {
|
|
// report error for has no type annotation
|
|
let new_type = self.infer_type_from_formal_parameter(param);
|
|
if new_type.is_none() {
|
|
self.error(parameter_must_have_explicit_type(param.span));
|
|
}
|
|
new_type
|
|
})
|
|
.map(|ts_type| {
|
|
// jf next param is not optional and current param is assignment pattern
|
|
// we need to add undefined to it's type
|
|
if is_remaining_params_have_required {
|
|
if matches!(ts_type, TSType::TSTypeReference(_)) {
|
|
self.error(implicitly_adding_undefined_to_type(param.span));
|
|
} else if !ts_type.is_maybe_undefined() {
|
|
// union with undefined
|
|
return self.ast.ts_type_annotation(
|
|
SPAN,
|
|
self.ast.ts_type_union_type(
|
|
SPAN,
|
|
self.ast.vec_from_iter([
|
|
ts_type,
|
|
self.ast.ts_type_undefined_keyword(SPAN),
|
|
]),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
self.ast.ts_type_annotation(SPAN, ts_type)
|
|
});
|
|
|
|
pattern = self.ast.binding_pattern(
|
|
// SAFETY: `ast.copy` is unsound! We need to fix.
|
|
unsafe { self.ast.copy(&pattern.kind) },
|
|
type_annotation,
|
|
// if it's assignment pattern, it's optional
|
|
pattern.optional || (!is_remaining_params_have_required && is_assignment_pattern),
|
|
);
|
|
}
|
|
|
|
Some(self.ast.formal_parameter(param.span, self.ast.vec(), pattern, None, false, false))
|
|
}
|
|
|
|
pub fn transform_formal_parameters(
|
|
&self,
|
|
params: &FormalParameters<'a>,
|
|
) -> Box<'a, FormalParameters<'a>> {
|
|
if params.kind.is_signature() || (params.rest.is_none() && params.items.is_empty()) {
|
|
// SAFETY: `ast.copy` is unsound! We need to fix.
|
|
return self.ast.alloc(unsafe { self.ast.copy(params) });
|
|
}
|
|
|
|
let items =
|
|
self.ast.vec_from_iter(params.items.iter().enumerate().filter_map(|(index, item)| {
|
|
let is_remaining_params_have_required =
|
|
params.items.iter().skip(index).any(|item| {
|
|
!(item.pattern.optional || item.pattern.kind.is_assignment_pattern())
|
|
});
|
|
self.transform_formal_parameter(item, is_remaining_params_have_required)
|
|
}));
|
|
|
|
if let Some(rest) = ¶ms.rest {
|
|
if rest.argument.type_annotation.is_none() {
|
|
self.error(parameter_must_have_explicit_type(rest.span));
|
|
}
|
|
}
|
|
|
|
self.ast.alloc_formal_parameters(
|
|
params.span,
|
|
FormalParameterKind::Signature,
|
|
items,
|
|
// SAFETY: `ast.copy` is unsound! We need to fix.
|
|
unsafe { self.ast.copy(¶ms.rest) },
|
|
)
|
|
}
|
|
}
|
|
|
|
pub fn get_function_span(func: &Function<'_>) -> Span {
|
|
func.id.as_ref().map_or_else(
|
|
|| {
|
|
let start = func.params.span.start;
|
|
Span::new(start, start)
|
|
},
|
|
|id| id.span,
|
|
)
|
|
}
|