From 77d553364d7522b6d111068f54aee904cc8df04e Mon Sep 17 00:00:00 2001 From: Dunqing <29533304+Dunqing@users.noreply.github.com> Date: Mon, 17 Jun 2024 13:53:19 +0000 Subject: [PATCH] feat(isolated-declarations): report errors that are consistent with typescript. (#3720) --- crates/oxc_isolated_declarations/src/class.rs | 36 ++++++++--- .../src/declaration.rs | 8 ++- .../src/diagnostics.rs | 60 ++++++++++++++++--- .../oxc_isolated_declarations/src/function.rs | 17 +++++- .../oxc_isolated_declarations/src/inferrer.rs | 39 ++++++------ .../oxc_isolated_declarations/src/module.rs | 7 ++- crates/oxc_isolated_declarations/src/types.rs | 29 ++++++++- 7 files changed, 152 insertions(+), 44 deletions(-) diff --git a/crates/oxc_isolated_declarations/src/class.rs b/crates/oxc_isolated_declarations/src/class.rs index afbc4da2d1d28..99b25d5c2aeb2 100644 --- a/crates/oxc_isolated_declarations/src/class.rs +++ b/crates/oxc_isolated_declarations/src/class.rs @@ -5,7 +5,10 @@ use oxc_allocator::Box; use oxc_span::{GetSpan, SPAN}; use crate::{ - diagnostics::{computed_property_name, extends_clause_expression}, + diagnostics::{ + accessor_must_have_explicit_return_type, computed_property_name, extends_clause_expression, + method_must_have_explicit_return_type, property_must_have_explicit_type, + }, IsolatedDeclarations, }; @@ -59,16 +62,17 @@ impl<'a> IsolatedDeclarations<'a> { .as_ref() .map(|type_annotation| self.ast.copy(type_annotation)) .or_else(|| { - let new_type = property + property .value .as_ref() - .and_then(|expr| self.infer_type_from_expression(expr)) - .unwrap_or_else(|| { - // report error for has no type annotation - self.ast.ts_unknown_keyword(property.span) - }); - - Some(self.ast.ts_type_annotation(SPAN, new_type)) + .and_then(|expr| { + let ts_type = self.infer_type_from_expression(expr); + if ts_type.is_none() { + self.error(property_must_have_explicit_type(property.key.span())); + } + ts_type + }) + .map(|ts_type| self.ast.ts_type_annotation(SPAN, ts_type)) }) }; @@ -115,6 +119,20 @@ impl<'a> IsolatedDeclarations<'a> { let type_annotation = self.infer_function_return_type(function); + if type_annotation.is_none() { + match definition.kind { + MethodDefinitionKind::Method => { + self.error(method_must_have_explicit_return_type(definition.key.span())); + } + MethodDefinitionKind::Get => { + self.error(accessor_must_have_explicit_return_type(definition.key.span())); + } + MethodDefinitionKind::Constructor | MethodDefinitionKind::Set => {} + } + } + + // TODO: Infer the parameter type of the `set` method from the `get` method + let value = self.ast.function( FunctionType::TSEmptyBodyFunctionExpression, function.span, diff --git a/crates/oxc_isolated_declarations/src/declaration.rs b/crates/oxc_isolated_declarations/src/declaration.rs index 7e6725bb75a84..d62db78ccef27 100644 --- a/crates/oxc_isolated_declarations/src/declaration.rs +++ b/crates/oxc_isolated_declarations/src/declaration.rs @@ -7,7 +7,10 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_span::{GetSpan, SPAN}; use oxc_syntax::scope::ScopeFlags; -use crate::{diagnostics::signature_computed_property_name, IsolatedDeclarations}; +use crate::{ + diagnostics::{inferred_type_of_expression, signature_computed_property_name}, + IsolatedDeclarations, +}; impl<'a> IsolatedDeclarations<'a> { pub fn transform_variable_declaration( @@ -264,6 +267,9 @@ impl<'a> IsolatedDeclarations<'a> { impl<'a> Visit<'a> for IsolatedDeclarations<'a> { fn visit_ts_method_signature(&mut self, signature: &TSMethodSignature<'a>) { + if signature.return_type.is_none() { + self.error(inferred_type_of_expression(signature.span)); + } self.report_signature_property_key(&signature.key, signature.computed); } fn visit_ts_property_signature(&mut self, signature: &TSPropertySignature<'a>) { diff --git a/crates/oxc_isolated_declarations/src/diagnostics.rs b/crates/oxc_isolated_declarations/src/diagnostics.rs index 4ea844f08540f..c4dc308093000 100644 --- a/crates/oxc_isolated_declarations/src/diagnostics.rs +++ b/crates/oxc_isolated_declarations/src/diagnostics.rs @@ -1,18 +1,32 @@ -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 { +pub fn method_must_have_explicit_return_type(span: Span) -> OxcDiagnostic { + OxcDiagnostic::error( + "Method must have an explicit return type annotation with --isolatedDeclarations.", + ) + .with_label(span) +} + +pub fn function_must_have_explicit_return_type(span: Span) -> 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, - )) + .with_label(span) +} + +pub fn accessor_must_have_explicit_return_type(span: Span) -> OxcDiagnostic { + OxcDiagnostic::error( + "At least one accessor must have an explicit return type annotation with --isolatedDeclarations.", + ) + .with_label(span) +} + +pub fn property_must_have_explicit_type(span: Span) -> OxcDiagnostic { + OxcDiagnostic::error( + "Property must have an explicit type annotation with --isolatedDeclarations.", + ) + .with_label(span) } pub fn type_containing_private_name(name: &Atom<'_>, span: Span) -> OxcDiagnostic { @@ -41,3 +55,31 @@ pub fn extends_clause_expression(span: Span) -> OxcDiagnostic { OxcDiagnostic::error("Extends clause can't contain an expression with --isolatedDeclarations.") .with_label(span) } + +pub fn default_export_inferred(span: Span) -> OxcDiagnostic { + OxcDiagnostic::error("Default exports can't be inferred with --isolatedDeclarations.") + .with_label(span) +} + +pub fn array_inferred(span: Span) -> OxcDiagnostic { + OxcDiagnostic::error("Arrays can't be inferred with --isolatedDeclarations.").with_label(span) +} + +pub fn shorthand_property(span: Span) -> OxcDiagnostic { + OxcDiagnostic::error( + "Objects that contain shorthand properties can't be inferred with --isolatedDeclarations.", + ) + .with_label(span) +} + +pub fn inferred_type_of_expression(span: Span) -> OxcDiagnostic { + OxcDiagnostic::error("Expression type can't be inferred with --isolatedDeclarations.") + .with_label(span) +} + +// Inference from class expressions is not supported with --isolatedDeclarations. + +pub fn inferred_type_of_class_expression(span: Span) -> OxcDiagnostic { + OxcDiagnostic::error("Class expression type can't be inferred with --isolatedDeclarations.") + .with_label(span) +} diff --git a/crates/oxc_isolated_declarations/src/function.rs b/crates/oxc_isolated_declarations/src/function.rs index ebd012e900684..bd975a154d297 100644 --- a/crates/oxc_isolated_declarations/src/function.rs +++ b/crates/oxc_isolated_declarations/src/function.rs @@ -4,9 +4,9 @@ use oxc_ast::ast::*; use oxc_allocator::Box; use oxc_ast::ast::Function; use oxc_diagnostics::OxcDiagnostic; -use oxc_span::SPAN; +use oxc_span::{Span, SPAN}; -use crate::IsolatedDeclarations; +use crate::{diagnostics::function_must_have_explicit_return_type, IsolatedDeclarations}; impl<'a> IsolatedDeclarations<'a> { pub fn transform_function(&mut self, func: &Function<'a>) -> Option>> { @@ -14,6 +14,9 @@ impl<'a> IsolatedDeclarations<'a> { 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.function( func.r#type, @@ -126,3 +129,13 @@ impl<'a> IsolatedDeclarations<'a> { ) } } + +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, + ) +} diff --git a/crates/oxc_isolated_declarations/src/inferrer.rs b/crates/oxc_isolated_declarations/src/inferrer.rs index 034ee23a17bc4..7b4ed9e4f056e 100644 --- a/crates/oxc_isolated_declarations/src/inferrer.rs +++ b/crates/oxc_isolated_declarations/src/inferrer.rs @@ -7,7 +7,8 @@ use oxc_diagnostics::OxcDiagnostic; use oxc_span::{GetSpan, SPAN}; use crate::{ - diagnostics::function_must_have_explicit_return_type, return_type::FunctionReturnType, + diagnostics::{array_inferred, inferred_type_of_class_expression}, + return_type::FunctionReturnType, IsolatedDeclarations, }; @@ -35,6 +36,10 @@ impl<'a> IsolatedDeclarations<'a> { Expression::ObjectExpression(expr) => { Some(self.transform_object_expression_to_ts_type(expr, false)) } + Expression::ArrayExpression(expr) => { + self.error(array_inferred(expr.span)); + Some(self.ast.ts_unknown_keyword(expr.span)) + } Expression::TSAsExpression(expr) => { if expr.type_annotation.is_const_type_reference() { Some(self.transform_expression_to_ts_type(&expr.expression)) @@ -43,14 +48,7 @@ impl<'a> IsolatedDeclarations<'a> { } } Expression::ClassExpression(expr) => { - self.error( - OxcDiagnostic::error( - " - Inference from class expressions is not supported with --isolatedDeclarations. - ", - ) - .with_label(expr.span), - ); + self.error(inferred_type_of_class_expression(expr.span)); Some(self.ast.ts_unknown_keyword(SPAN)) } Expression::TSNonNullExpression(expr) => { @@ -104,25 +102,17 @@ impl<'a> IsolatedDeclarations<'a> { } if function.r#async || function.generator { - self.error(function_must_have_explicit_return_type(function)); + return None; } - let return_type = FunctionReturnType::infer( + FunctionReturnType::infer( self, function .body .as_ref() .unwrap_or_else(|| unreachable!("Only declare function can have no body")), ) - .map(|type_annotation| self.ast.ts_type_annotation(SPAN, type_annotation)); - - if return_type.is_none() { - self.error(function_must_have_explicit_return_type(function)); - - Some(self.ast.ts_type_annotation(SPAN, self.ast.ts_unknown_keyword(SPAN))) - } else { - return_type - } + .map(|type_annotation| self.ast.ts_type_annotation(SPAN, type_annotation)) } pub fn infer_arrow_function_return_type( @@ -133,6 +123,14 @@ impl<'a> IsolatedDeclarations<'a> { return self.ast.copy(&function.return_type); } + if function.r#async { + return None; + } + + if function.r#async { + return None; + } + if function.expression { if let Some(Statement::ExpressionStatement(stmt)) = function.body.statements.first() { return self @@ -140,6 +138,7 @@ impl<'a> IsolatedDeclarations<'a> { .map(|type_annotation| self.ast.ts_type_annotation(SPAN, type_annotation)); } } + FunctionReturnType::infer(self, &function.body) .map(|type_annotation| self.ast.ts_type_annotation(SPAN, type_annotation)) } diff --git a/crates/oxc_isolated_declarations/src/module.rs b/crates/oxc_isolated_declarations/src/module.rs index 197512cb162f2..e45db55c1087e 100644 --- a/crates/oxc_isolated_declarations/src/module.rs +++ b/crates/oxc_isolated_declarations/src/module.rs @@ -5,7 +5,7 @@ use oxc_allocator::Box; use oxc_ast::Visit; use oxc_span::{GetSpan, SPAN}; -use crate::IsolatedDeclarations; +use crate::{diagnostics::default_export_inferred, IsolatedDeclarations}; impl<'a> IsolatedDeclarations<'a> { pub fn transform_export_named_declaration( @@ -46,6 +46,7 @@ impl<'a> IsolatedDeclarations<'a> { } else { // declare const _default: Type let kind = VariableDeclarationKind::Const; + // TODO: create unique name for this let name = self.ast.new_atom("_default"); let id = self .ast @@ -54,6 +55,10 @@ impl<'a> IsolatedDeclarations<'a> { .infer_type_from_expression(expr) .map(|ts_type| self.ast.ts_type_annotation(SPAN, ts_type)); + if type_annotation.is_none() { + self.error(default_export_inferred(expr.span())); + } + let id = BindingPattern { kind: id, type_annotation, optional: false }; let declarations = self .ast diff --git a/crates/oxc_isolated_declarations/src/types.rs b/crates/oxc_isolated_declarations/src/types.rs index bbe8f0fdfc15e..513fb28fb9561 100644 --- a/crates/oxc_isolated_declarations/src/types.rs +++ b/crates/oxc_isolated_declarations/src/types.rs @@ -4,13 +4,23 @@ use oxc_ast::ast::{ TSTypeOperatorOperator, }; use oxc_diagnostics::OxcDiagnostic; -use oxc_span::SPAN; +use oxc_span::{GetSpan, SPAN}; -use crate::IsolatedDeclarations; +use crate::{ + diagnostics::{ + function_must_have_explicit_return_type, inferred_type_of_expression, shorthand_property, + }, + function::get_function_span, + IsolatedDeclarations, +}; impl<'a> IsolatedDeclarations<'a> { pub fn transform_function_to_ts_type(&self, func: &Function<'a>) -> Option> { 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); return_type.map(|return_type| { @@ -29,6 +39,11 @@ impl<'a> IsolatedDeclarations<'a> { func: &ArrowFunctionExpression<'a>, ) -> Option> { let return_type = self.infer_arrow_function_return_type(func); + + if return_type.is_none() { + self.error(function_must_have_explicit_return_type(func.span)); + } + let params = self.transform_formal_parameters(&func.params); return_type.map(|return_type| { @@ -66,6 +81,11 @@ impl<'a> IsolatedDeclarations<'a> { return None; } + if object.shorthand { + self.error(shorthand_property(object.span)); + return None; + } + if let Expression::FunctionExpression(function) = &object.value { if !is_const && object.method { let return_type = self.infer_function_return_type(function); @@ -86,6 +106,11 @@ impl<'a> IsolatedDeclarations<'a> { let type_annotation = self.infer_type_from_expression(&object.value); + if type_annotation.is_none() { + self.error(inferred_type_of_expression(object.value.span())); + return None; + } + let property_signature = self.ast.ts_property_signature( object.span, false,