mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(isolated-declarations): report errors that are consistent with typescript. (#3720)
This commit is contained in:
parent
3c597356e4
commit
77d553364d
7 changed files with 152 additions and 44 deletions
|
|
@ -5,7 +5,10 @@ use oxc_allocator::Box;
|
||||||
use oxc_span::{GetSpan, SPAN};
|
use oxc_span::{GetSpan, SPAN};
|
||||||
|
|
||||||
use crate::{
|
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,
|
IsolatedDeclarations,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -59,16 +62,17 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|type_annotation| self.ast.copy(type_annotation))
|
.map(|type_annotation| self.ast.copy(type_annotation))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let new_type = property
|
property
|
||||||
.value
|
.value
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|expr| self.infer_type_from_expression(expr))
|
.and_then(|expr| {
|
||||||
.unwrap_or_else(|| {
|
let ts_type = self.infer_type_from_expression(expr);
|
||||||
// report error for has no type annotation
|
if ts_type.is_none() {
|
||||||
self.ast.ts_unknown_keyword(property.span)
|
self.error(property_must_have_explicit_type(property.key.span()));
|
||||||
});
|
}
|
||||||
|
ts_type
|
||||||
Some(self.ast.ts_type_annotation(SPAN, new_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);
|
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(
|
let value = self.ast.function(
|
||||||
FunctionType::TSEmptyBodyFunctionExpression,
|
FunctionType::TSEmptyBodyFunctionExpression,
|
||||||
function.span,
|
function.span,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,10 @@ use oxc_diagnostics::OxcDiagnostic;
|
||||||
use oxc_span::{GetSpan, SPAN};
|
use oxc_span::{GetSpan, SPAN};
|
||||||
use oxc_syntax::scope::ScopeFlags;
|
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> {
|
impl<'a> IsolatedDeclarations<'a> {
|
||||||
pub fn transform_variable_declaration(
|
pub fn transform_variable_declaration(
|
||||||
|
|
@ -264,6 +267,9 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
|
|
||||||
impl<'a> Visit<'a> for IsolatedDeclarations<'a> {
|
impl<'a> Visit<'a> for IsolatedDeclarations<'a> {
|
||||||
fn visit_ts_method_signature(&mut self, signature: &TSMethodSignature<'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);
|
self.report_signature_property_key(&signature.key, signature.computed);
|
||||||
}
|
}
|
||||||
fn visit_ts_property_signature(&mut self, signature: &TSPropertySignature<'a>) {
|
fn visit_ts_property_signature(&mut self, signature: &TSPropertySignature<'a>) {
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,32 @@
|
||||||
use oxc_ast::ast::Function;
|
|
||||||
use oxc_diagnostics::OxcDiagnostic;
|
use oxc_diagnostics::OxcDiagnostic;
|
||||||
use oxc_span::{Atom, Span};
|
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(
|
OxcDiagnostic::error(
|
||||||
"Function must have an explicit return type annotation with --isolatedDeclarations.",
|
"Function must have an explicit return type annotation with --isolatedDeclarations.",
|
||||||
)
|
)
|
||||||
.with_label(func.id.as_ref().map_or_else(
|
.with_label(span)
|
||||||
|| {
|
}
|
||||||
let start = func.params.span.start;
|
|
||||||
Span::new(start, start)
|
pub fn accessor_must_have_explicit_return_type(span: Span) -> OxcDiagnostic {
|
||||||
},
|
OxcDiagnostic::error(
|
||||||
|id| id.span,
|
"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 {
|
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.")
|
OxcDiagnostic::error("Extends clause can't contain an expression with --isolatedDeclarations.")
|
||||||
.with_label(span)
|
.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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ use oxc_ast::ast::*;
|
||||||
use oxc_allocator::Box;
|
use oxc_allocator::Box;
|
||||||
use oxc_ast::ast::Function;
|
use oxc_ast::ast::Function;
|
||||||
use oxc_diagnostics::OxcDiagnostic;
|
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> {
|
impl<'a> IsolatedDeclarations<'a> {
|
||||||
pub fn transform_function(&mut self, func: &Function<'a>) -> Option<Box<'a, Function<'a>>> {
|
pub fn transform_function(&mut self, func: &Function<'a>) -> Option<Box<'a, Function<'a>>> {
|
||||||
|
|
@ -14,6 +14,9 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let return_type = self.infer_function_return_type(func);
|
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);
|
let params = self.transform_formal_parameters(&func.params);
|
||||||
Some(self.ast.function(
|
Some(self.ast.function(
|
||||||
func.r#type,
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@ use oxc_diagnostics::OxcDiagnostic;
|
||||||
use oxc_span::{GetSpan, SPAN};
|
use oxc_span::{GetSpan, SPAN};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diagnostics::function_must_have_explicit_return_type, return_type::FunctionReturnType,
|
diagnostics::{array_inferred, inferred_type_of_class_expression},
|
||||||
|
return_type::FunctionReturnType,
|
||||||
IsolatedDeclarations,
|
IsolatedDeclarations,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -35,6 +36,10 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
Expression::ObjectExpression(expr) => {
|
Expression::ObjectExpression(expr) => {
|
||||||
Some(self.transform_object_expression_to_ts_type(expr, false))
|
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) => {
|
Expression::TSAsExpression(expr) => {
|
||||||
if expr.type_annotation.is_const_type_reference() {
|
if expr.type_annotation.is_const_type_reference() {
|
||||||
Some(self.transform_expression_to_ts_type(&expr.expression))
|
Some(self.transform_expression_to_ts_type(&expr.expression))
|
||||||
|
|
@ -43,14 +48,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::ClassExpression(expr) => {
|
Expression::ClassExpression(expr) => {
|
||||||
self.error(
|
self.error(inferred_type_of_class_expression(expr.span));
|
||||||
OxcDiagnostic::error(
|
|
||||||
"
|
|
||||||
Inference from class expressions is not supported with --isolatedDeclarations.
|
|
||||||
",
|
|
||||||
)
|
|
||||||
.with_label(expr.span),
|
|
||||||
);
|
|
||||||
Some(self.ast.ts_unknown_keyword(SPAN))
|
Some(self.ast.ts_unknown_keyword(SPAN))
|
||||||
}
|
}
|
||||||
Expression::TSNonNullExpression(expr) => {
|
Expression::TSNonNullExpression(expr) => {
|
||||||
|
|
@ -104,25 +102,17 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if function.r#async || function.generator {
|
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,
|
self,
|
||||||
function
|
function
|
||||||
.body
|
.body
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap_or_else(|| unreachable!("Only declare function can have no body")),
|
.unwrap_or_else(|| unreachable!("Only declare function can have no body")),
|
||||||
)
|
)
|
||||||
.map(|type_annotation| self.ast.ts_type_annotation(SPAN, type_annotation));
|
.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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn infer_arrow_function_return_type(
|
pub fn infer_arrow_function_return_type(
|
||||||
|
|
@ -133,6 +123,14 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
return self.ast.copy(&function.return_type);
|
return self.ast.copy(&function.return_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if function.r#async {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if function.r#async {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if function.expression {
|
if function.expression {
|
||||||
if let Some(Statement::ExpressionStatement(stmt)) = function.body.statements.first() {
|
if let Some(Statement::ExpressionStatement(stmt)) = function.body.statements.first() {
|
||||||
return self
|
return self
|
||||||
|
|
@ -140,6 +138,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
.map(|type_annotation| self.ast.ts_type_annotation(SPAN, type_annotation));
|
.map(|type_annotation| self.ast.ts_type_annotation(SPAN, type_annotation));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionReturnType::infer(self, &function.body)
|
FunctionReturnType::infer(self, &function.body)
|
||||||
.map(|type_annotation| self.ast.ts_type_annotation(SPAN, type_annotation))
|
.map(|type_annotation| self.ast.ts_type_annotation(SPAN, type_annotation))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use oxc_allocator::Box;
|
||||||
use oxc_ast::Visit;
|
use oxc_ast::Visit;
|
||||||
use oxc_span::{GetSpan, SPAN};
|
use oxc_span::{GetSpan, SPAN};
|
||||||
|
|
||||||
use crate::IsolatedDeclarations;
|
use crate::{diagnostics::default_export_inferred, IsolatedDeclarations};
|
||||||
|
|
||||||
impl<'a> IsolatedDeclarations<'a> {
|
impl<'a> IsolatedDeclarations<'a> {
|
||||||
pub fn transform_export_named_declaration(
|
pub fn transform_export_named_declaration(
|
||||||
|
|
@ -46,6 +46,7 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
} else {
|
} else {
|
||||||
// declare const _default: Type
|
// declare const _default: Type
|
||||||
let kind = VariableDeclarationKind::Const;
|
let kind = VariableDeclarationKind::Const;
|
||||||
|
// TODO: create unique name for this
|
||||||
let name = self.ast.new_atom("_default");
|
let name = self.ast.new_atom("_default");
|
||||||
let id = self
|
let id = self
|
||||||
.ast
|
.ast
|
||||||
|
|
@ -54,6 +55,10 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
.infer_type_from_expression(expr)
|
.infer_type_from_expression(expr)
|
||||||
.map(|ts_type| self.ast.ts_type_annotation(SPAN, ts_type));
|
.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 id = BindingPattern { kind: id, type_annotation, optional: false };
|
||||||
let declarations = self
|
let declarations = self
|
||||||
.ast
|
.ast
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,23 @@ use oxc_ast::ast::{
|
||||||
TSTypeOperatorOperator,
|
TSTypeOperatorOperator,
|
||||||
};
|
};
|
||||||
use oxc_diagnostics::OxcDiagnostic;
|
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> {
|
impl<'a> IsolatedDeclarations<'a> {
|
||||||
pub fn transform_function_to_ts_type(&self, func: &Function<'a>) -> Option<TSType<'a>> {
|
pub fn transform_function_to_ts_type(&self, func: &Function<'a>) -> Option<TSType<'a>> {
|
||||||
let return_type = self.infer_function_return_type(func);
|
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);
|
let params = self.transform_formal_parameters(&func.params);
|
||||||
|
|
||||||
return_type.map(|return_type| {
|
return_type.map(|return_type| {
|
||||||
|
|
@ -29,6 +39,11 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
func: &ArrowFunctionExpression<'a>,
|
func: &ArrowFunctionExpression<'a>,
|
||||||
) -> Option<TSType<'a>> {
|
) -> Option<TSType<'a>> {
|
||||||
let return_type = self.infer_arrow_function_return_type(func);
|
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);
|
let params = self.transform_formal_parameters(&func.params);
|
||||||
|
|
||||||
return_type.map(|return_type| {
|
return_type.map(|return_type| {
|
||||||
|
|
@ -66,6 +81,11 @@ impl<'a> IsolatedDeclarations<'a> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if object.shorthand {
|
||||||
|
self.error(shorthand_property(object.span));
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if let Expression::FunctionExpression(function) = &object.value {
|
if let Expression::FunctionExpression(function) = &object.value {
|
||||||
if !is_const && object.method {
|
if !is_const && object.method {
|
||||||
let return_type = self.infer_function_return_type(function);
|
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);
|
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(
|
let property_signature = self.ast.ts_property_signature(
|
||||||
object.span,
|
object.span,
|
||||||
false,
|
false,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue