From f3850eb53b8930b40bced12441e72707d68441e3 Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:43:34 +0000 Subject: [PATCH] fix(semantic): correctly resolve binding for return type of functions (#6388) Fixes #6387. --- crates/oxc_semantic/src/builder.rs | 32 ++++-- .../oxc/ts/functions/return-type.snap | 102 ++++++++++++++++++ .../fixtures/oxc/ts/functions/return-type.ts | 12 +++ 3 files changed, 137 insertions(+), 9 deletions(-) create mode 100644 crates/oxc_semantic/tests/fixtures/oxc/ts/functions/return-type.snap create mode 100644 crates/oxc_semantic/tests/fixtures/oxc/ts/functions/return-type.ts diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 0e101f82c..acc682b5c 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -1655,6 +1655,18 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { if let Some(return_type) = &func.return_type { self.visit_ts_type_annotation(return_type); } + + if func.params.has_parameter() || func.return_type.is_some() { + // `function foo({bar: identifier_reference}) {}` + // ^^^^^^^^^^^^^^^^^^^^ + // `function foo(v: SomeType): SomeType { return v; }` + // ^^^^^^^^ ^^^^^^^^ + // Parameter initializers must be resolved after all parameters have been declared. + // Param types and return type must be resolved after type parameters have been declared. + // In both cases, need to avoid binding to variables/types declared inside the function body. + self.resolve_references_for_current_scope(); + } + if let Some(body) = &func.body { self.visit_function_body(body); } @@ -1719,6 +1731,17 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { self.visit_ts_type_annotation(return_type); } + if expr.params.has_parameter() || expr.return_type.is_some() { + // `let foo = ({bar: identifier_reference}) => {};` + // ^^^^^^^^^^^^^^^^^^^^ + // `let foo = (v: SomeType): SomeType => v;` + // ^^^^^^^^ ^^^^^^^^ + // Parameter initializers must be resolved after all parameters have been declared. + // Param types and return type must be resolved after type parameters have been declared. + // In both cases, need to avoid binding to variables/types declared inside the function body. + self.resolve_references_for_current_scope(); + } + self.visit_function_body(&expr.body); /* cfg */ @@ -2021,15 +2044,6 @@ impl<'a> SemanticBuilder<'a> { AstKind::Function(_) | AstKind::ArrowFunctionExpression(_) => { self.function_stack.pop(); } - AstKind::FormalParameters(parameters) => { - if parameters.kind != FormalParameterKind::Signature && parameters.has_parameter() { - // `function foo({bar: identifier_reference}) {}` - // ^^^^^^^^^^^^^^^^^^^^ Parameter initializer must be resolved - // after all parameters have been declared - // to avoid binding to variables inside the scope - self.resolve_references_for_current_scope(); - } - } AstKind::CatchParameter(_) => { self.resolve_references_for_current_scope(); } diff --git a/crates/oxc_semantic/tests/fixtures/oxc/ts/functions/return-type.snap b/crates/oxc_semantic/tests/fixtures/oxc/ts/functions/return-type.snap new file mode 100644 index 000000000..0652b3d37 --- /dev/null +++ b/crates/oxc_semantic/tests/fixtures/oxc/ts/functions/return-type.snap @@ -0,0 +1,102 @@ +--- +source: crates/oxc_semantic/tests/main.rs +input_file: crates/oxc_semantic/tests/fixtures/oxc/ts/functions/return-type.ts +--- +[ + { + "children": [ + { + "children": [], + "flags": "ScopeFlags(StrictMode)", + "id": 1, + "node": "TSTypeAliasDeclaration", + "symbols": [] + }, + { + "children": [ + { + "children": [], + "flags": "ScopeFlags(StrictMode)", + "id": 3, + "node": "TSTypeAliasDeclaration", + "symbols": [] + } + ], + "flags": "ScopeFlags(StrictMode | Function)", + "id": 2, + "node": "Function(Foo)", + "symbols": [ + { + "flags": "SymbolFlags(TypeAlias)", + "id": 2, + "name": "T", + "node": "TSTypeAliasDeclaration", + "references": [] + } + ] + }, + { + "children": [ + { + "children": [], + "flags": "ScopeFlags(StrictMode)", + "id": 5, + "node": "TSTypeAliasDeclaration", + "symbols": [] + } + ], + "flags": "ScopeFlags(StrictMode | Function | Arrow)", + "id": 4, + "node": "ArrowFunctionExpression", + "symbols": [ + { + "flags": "SymbolFlags(TypeAlias)", + "id": 4, + "name": "T", + "node": "TSTypeAliasDeclaration", + "references": [] + } + ] + } + ], + "flags": "ScopeFlags(StrictMode | Top)", + "id": 0, + "node": "Program", + "symbols": [ + { + "flags": "SymbolFlags(TypeAlias)", + "id": 0, + "name": "T", + "node": "TSTypeAliasDeclaration", + "references": [ + { + "flags": "ReferenceFlags(Type)", + "id": 0, + "name": "T", + "node_id": 12 + }, + { + "flags": "ReferenceFlags(Type)", + "id": 1, + "name": "T", + "node_id": 27 + } + ] + }, + { + "flags": "SymbolFlags(BlockScopedVariable | Function)", + "id": 1, + "name": "Foo", + "node": "Function(Foo)", + "references": [] + }, + { + "flags": "SymbolFlags(BlockScopedVariable | ConstVariable)", + "id": 3, + "name": "Bar", + "node": "VariableDeclarator(Bar)", + "references": [] + } + ] + } +] diff --git a/crates/oxc_semantic/tests/fixtures/oxc/ts/functions/return-type.ts b/crates/oxc_semantic/tests/fixtures/oxc/ts/functions/return-type.ts new file mode 100644 index 000000000..2b4d48cad --- /dev/null +++ b/crates/oxc_semantic/tests/fixtures/oxc/ts/functions/return-type.ts @@ -0,0 +1,12 @@ +export type T = number; + +function Foo(): T { + type T = string; + return 0; +} + +const Bar = (): T => { + type T = string; + return 0; +} +