diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index af4168ac4483c..66fa710b24418 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -237,6 +237,20 @@ macro_rules! match_ts_type { pub use match_ts_type; impl<'a> TSType<'a> { + pub fn get_identifier_reference(&self) -> Option> { + match self { + TSType::TSTypeReference(reference) => { + Some(TSTypeName::get_first_name(&reference.type_name)) + } + TSType::TSQualifiedName(qualified) => Some(TSTypeName::get_first_name(&qualified.left)), + TSType::TSTypeQuery(query) => match &query.expr_name { + TSTypeQueryExprName::IdentifierReference(ident) => Some((*ident).clone()), + _ => None, + }, + _ => None, + } + } + pub fn is_const_type_reference(&self) -> bool { matches!(self, TSType::TSTypeReference(reference) if reference.type_name.is_const()) } diff --git a/crates/oxc_transformer_dts/src/diagnostics.rs b/crates/oxc_transformer_dts/src/diagnostics.rs new file mode 100644 index 0000000000000..3d586e59270db --- /dev/null +++ b/crates/oxc_transformer_dts/src/diagnostics.rs @@ -0,0 +1,23 @@ +use oxc_ast::ast::Function; +use oxc_diagnostics::OxcDiagnostic; +use oxc_span::{Atom, Span}; + +pub fn function_must_have_explicit_return_type(func: &Function<'_>) -> OxcDiagnostic { + OxcDiagnostic::error( + "Function must have an explicit return type annotation with --isolatedDeclarations.", + ) + .with_label(func.id.as_ref().map_or_else( + || { + let start = func.params.span.start; + Span::new(start, start) + }, + |id| id.span, + )) +} + +pub fn type_containing_private_name(name: &Atom<'_>, span: Span) -> OxcDiagnostic { + OxcDiagnostic::error(format!( + "Type containing private name '{name}' can't be used with --isolatedDeclarations." + )) + .with_label(span) +} diff --git a/crates/oxc_transformer_dts/src/inferrer.rs b/crates/oxc_transformer_dts/src/inferrer.rs index 622990874c9af..8bdbed7fa2e3e 100644 --- a/crates/oxc_transformer_dts/src/inferrer.rs +++ b/crates/oxc_transformer_dts/src/inferrer.rs @@ -6,7 +6,10 @@ use oxc_ast::ast::{ use oxc_diagnostics::OxcDiagnostic; use oxc_span::{GetSpan, SPAN}; -use crate::{return_type::FunctionReturnType, TransformerDts}; +use crate::{ + diagnostics::function_must_have_explicit_return_type, return_type::FunctionReturnType, + TransformerDts, +}; impl<'a> TransformerDts<'a> { pub fn infer_type_from_expression(&self, expr: &Expression<'a>) -> Option> { @@ -100,19 +103,21 @@ impl<'a> TransformerDts<'a> { return self.ctx.ast.copy(&function.return_type); } + if function.r#async || function.generator { + self.ctx.error(function_must_have_explicit_return_type(function)); + } + let return_type = FunctionReturnType::infer( self, function .body .as_ref() - .unwrap_or_else(|| unreachable!("declare function can not have body")), + .unwrap_or_else(|| unreachable!("Only declare function can have no body")), ) .map(|type_annotation| self.ctx.ast.ts_type_annotation(SPAN, type_annotation)); if return_type.is_none() { - self.ctx.error(OxcDiagnostic::error( - "Function must have an explicit return type annotation with --isolatedDeclarations.", - ).with_label(function.span)); + self.ctx.error(function_must_have_explicit_return_type(function)); Some(self.ctx.ast.ts_type_annotation(SPAN, self.ctx.ast.ts_unknown_keyword(SPAN))) } else { diff --git a/crates/oxc_transformer_dts/src/lib.rs b/crates/oxc_transformer_dts/src/lib.rs index 58ee3e3333ce3..ac8eb359a491a 100644 --- a/crates/oxc_transformer_dts/src/lib.rs +++ b/crates/oxc_transformer_dts/src/lib.rs @@ -8,6 +8,7 @@ mod class; mod context; mod declaration; +mod diagnostics; mod function; mod inferrer; mod module; diff --git a/crates/oxc_transformer_dts/src/return_type.rs b/crates/oxc_transformer_dts/src/return_type.rs index aa7e9f13e37d7..cd761ac1156a8 100644 --- a/crates/oxc_transformer_dts/src/return_type.rs +++ b/crates/oxc_transformer_dts/src/return_type.rs @@ -7,11 +7,10 @@ use oxc_ast::{ }, Visit, }; -use oxc_diagnostics::OxcDiagnostic; use oxc_span::{Atom, GetSpan}; use oxc_syntax::scope::ScopeFlags; -use crate::{context::Ctx, TransformerDts}; +use crate::{context::Ctx, diagnostics::type_containing_private_name, TransformerDts}; /// Infer return type from return statement. Does not support multiple return statements. pub struct FunctionReturnType<'a> { @@ -67,11 +66,12 @@ impl<'a> FunctionReturnType<'a> { }; if is_defined_in_current_scope { - transformer.ctx.error( - OxcDiagnostic::error(format!("Type containing private name '{reference_name}' can't be used with --isolatedDeclarations.")).with_label( - expr.span() - ) - ); + transformer.ctx.error(type_containing_private_name( + &reference_name, + expr_type + .get_identifier_reference() + .map_or_else(|| expr_type.span(), |ident| ident.span), + )); } } diff --git a/crates/oxc_transformer_dts/src/scope.rs b/crates/oxc_transformer_dts/src/scope.rs index d40c9e3683750..d8180d3c0b723 100644 --- a/crates/oxc_transformer_dts/src/scope.rs +++ b/crates/oxc_transformer_dts/src/scope.rs @@ -51,11 +51,11 @@ impl<'a> ScopeTree<'a> { self.type_bindings.last_mut().unwrap().insert(ident.clone()); } - fn add_unresolved_value_reference(&mut self, ident: &Atom<'a>) { + fn add_value_reference(&mut self, ident: &Atom<'a>) { self.value_references.last_mut().unwrap().insert(ident.clone()); } - fn add_unresolved_type_reference(&mut self, ident: &Atom<'a>) { + fn add_type_reference(&mut self, ident: &Atom<'a>) { self.type_references.last_mut().unwrap().insert(ident.clone()); } @@ -65,7 +65,6 @@ impl<'a> ScopeTree<'a> { fn resolve_references(&mut self) { let current_value_bindings = self.value_bindings.pop().unwrap_or_default(); let current_value_references = self.value_references.pop().unwrap_or_default(); - self.type_references .last_mut() .unwrap() @@ -95,7 +94,7 @@ impl<'a> Visit<'a> for ScopeTree<'a> { } fn visit_identifier_reference(&mut self, ident: &IdentifierReference<'a>) { - self.add_unresolved_value_reference(&ident.name); + self.add_value_reference(&ident.name); } fn visit_binding_pattern(&mut self, pattern: &BindingPattern<'a>) { @@ -107,7 +106,7 @@ impl<'a> Visit<'a> for ScopeTree<'a> { fn visit_ts_type_name(&mut self, name: &TSTypeName<'a>) { if let TSTypeName::IdentifierReference(ident) = name { - self.add_unresolved_type_reference(&ident.name); + self.add_type_reference(&ident.name); } else { walk_ts_type_name(self, name); } @@ -116,7 +115,7 @@ impl<'a> Visit<'a> for ScopeTree<'a> { fn visit_ts_type_query(&mut self, ty: &TSTypeQuery<'a>) { if let Some(type_name) = ty.expr_name.as_ts_type_name() { let ident = TSTypeName::get_first_name(type_name); - self.add_unresolved_value_reference(&ident.name); + self.add_value_reference(&ident.name); } else { walk_ts_type_query(self, ty); } @@ -125,8 +124,8 @@ impl<'a> Visit<'a> for ScopeTree<'a> { fn visit_export_named_declaration(&mut self, decl: &ExportNamedDeclaration<'a>) { for specifier in &decl.specifiers { if let ModuleExportName::Identifier(ident) = &specifier.local { - self.add_type_binding(&ident.name); - self.add_value_binding(&ident.name); + self.add_type_reference(&ident.name); + self.add_value_reference(&ident.name); } } } diff --git a/tasks/coverage/transpile.snap b/tasks/coverage/transpile.snap index e0850b563aa3f..abf6b7bd7c909 100644 --- a/tasks/coverage/transpile.snap +++ b/tasks/coverage/transpile.snap @@ -2,14 +2,13 @@ commit: d8086f14 transpile Summary: AST Parsed : 20/20 (100.00%) -Positive Passed: 7/20 (35.00%) +Positive Passed: 8/20 (40.00%) Mismatch: "declarationAsyncAndGeneratorFunctions.ts" Mismatch: "declarationBasicSyntax.ts" Mismatch: "declarationComputedPropertyNames.ts" Mismatch: "declarationCrossFileInferences.ts" Mismatch: "declarationEmitPartialNodeReuse.ts" Mismatch: "declarationFunctionDeclarations.ts" -Mismatch: "declarationLinkedAliases.ts" Mismatch: "declarationNotInScopeTypes.ts" Mismatch: "declarationPartialNodeReuseTypeOf.ts" Mismatch: "declarationRestParameters.ts"