mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
fix(semantic): fix function redeclaration errors
The problem here we face here is that TypeScript does not consider Annex B.3.2, which makes implementation a bit more difficult. This implementation conforms to test262.
This commit is contained in:
parent
5b2474600f
commit
9ed682a38e
6 changed files with 23 additions and 241 deletions
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use oxc_ast::ast::*;
|
||||
use oxc_ast::{syntax_directed_operations::BoundNames, AstKind};
|
||||
use oxc_ast::{syntax_directed_operations::BoundNames, AstKind, SourceType};
|
||||
|
||||
use crate::{
|
||||
scope::{Scope, ScopeFlags, ScopeId},
|
||||
|
|
@ -80,9 +80,9 @@ impl<'a> Binder for Class<'a> {
|
|||
// and the duplicate entries are only bound by FunctionDeclarations.
|
||||
// https://tc39.es/ecma262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics
|
||||
#[must_use]
|
||||
fn function_as_var(scope: &Scope, is_script: bool) -> bool {
|
||||
fn function_as_var(scope: &Scope, source_type: SourceType) -> bool {
|
||||
scope.flags.intersects(ScopeFlags::Function)
|
||||
|| (is_script && scope.flags.intersects(ScopeFlags::Top))
|
||||
|| (source_type.is_script() && scope.flags.intersects(ScopeFlags::Top))
|
||||
}
|
||||
|
||||
impl<'a> Binder for Function<'a> {
|
||||
|
|
@ -93,35 +93,22 @@ impl<'a> Binder for Function<'a> {
|
|||
if !scope.strict_mode && matches!(builder.parent_kind(), AstKind::IfStatement(_)) {
|
||||
// Do not declare in if single statements,
|
||||
// if (false) function f() {} else function g() { }
|
||||
} else if matches!(self.r#type, FunctionType::FunctionDeclaration) {
|
||||
// the visitor is already inside the function scope,
|
||||
} else if self.r#type == FunctionType::FunctionDeclaration {
|
||||
// The visitor is already inside the function scope,
|
||||
// retrieve the parent scope for the function id to bind to.
|
||||
let parent_scope_id =
|
||||
builder.scope.scopes[*current_scope_id].parent().unwrap().into();
|
||||
let parent_scope: &Scope = &builder.scope.scopes[parent_scope_id];
|
||||
|
||||
let (includes, excludes) =
|
||||
if parent_scope.strict_mode || self.r#async || self.generator {
|
||||
if function_as_var(parent_scope, builder.source_type.is_script()) {
|
||||
(
|
||||
SymbolFlags::FunctionScopedVariable | SymbolFlags::Function,
|
||||
SymbolFlags::FunctionScopedVariableExcludes,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
SymbolFlags::BlockScopedVariable | SymbolFlags::Function,
|
||||
SymbolFlags::BlockScopedVariableExcludes,
|
||||
)
|
||||
}
|
||||
} else if function_as_var(parent_scope, builder.source_type.is_script()) {
|
||||
(
|
||||
SymbolFlags::Function,
|
||||
SymbolFlags::FunctionScopedVariableExcludes - SymbolFlags::Function,
|
||||
)
|
||||
if (parent_scope.strict_mode || self.r#async || self.generator)
|
||||
&& !function_as_var(parent_scope, builder.source_type)
|
||||
{
|
||||
(SymbolFlags::BlockScopedVariable, SymbolFlags::BlockScopedVariableExcludes)
|
||||
} else {
|
||||
(
|
||||
SymbolFlags::Function,
|
||||
SymbolFlags::BlockScopedVariableExcludes - SymbolFlags::Function,
|
||||
SymbolFlags::FunctionScopedVariable,
|
||||
SymbolFlags::FunctionScopedVariableExcludes,
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -39,10 +39,9 @@ bitflags! {
|
|||
const BlockScopedVariable = 1 << 1;
|
||||
const Class = 1 << 5;
|
||||
const CatchVariable = 1 << 6; // try {} catch(catch_variable) {}
|
||||
const Function = 1 << 7;
|
||||
|
||||
const Variable = Self::FunctionScopedVariable.bits | Self::BlockScopedVariable.bits;
|
||||
const Value = Self::Variable.bits | Self::Function.bits | Self::Class.bits;
|
||||
const Value = Self::Variable.bits | Self::Class.bits;
|
||||
|
||||
/// Variables can be redeclared, but can not redeclare a block-scoped declaration with the
|
||||
/// same name, or any other value that is not a variable, e.g. ValueModule or Class
|
||||
|
|
@ -52,8 +51,7 @@ bitflags! {
|
|||
/// they can not merge with anything in the value space
|
||||
const BlockScopedVariableExcludes = Self::Value.bits;
|
||||
|
||||
const FunctionExcludes = Self::Value.bits - (Self::Function.bits | Self::Class.bits);
|
||||
const ClassExcludes = Self::Value.bits - Self::Function.bits;
|
||||
const ClassExcludes = Self::Value.bits;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Babel Summary:
|
||||
AST Parsed : 2052/2071 (99.08%)
|
||||
Positive Passed: 2052/2071 (99.08%)
|
||||
Negative Passed: 1335/1502 (88.88%)
|
||||
AST Parsed : 2054/2071 (99.18%)
|
||||
Positive Passed: 2054/2071 (99.18%)
|
||||
Negative Passed: 1333/1502 (88.75%)
|
||||
Expect Syntax Error: "annex-b/disabled/1.1-html-comments-close/input.js"
|
||||
Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-if-body/input.js"
|
||||
Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-multiple-labels/input.js"
|
||||
|
|
@ -13,6 +13,8 @@ Expect Syntax Error: "annex-b/disabled/3.5-for-in-initializer/input.js"
|
|||
Expect Syntax Error: "annex-b/enabled/3.1-sloppy-labeled-functions-if-body/input.js"
|
||||
Expect Syntax Error: "core/categorized/invalid-fn-decl-labeled-inside-if/input.js"
|
||||
Expect Syntax Error: "core/categorized/invalid-fn-decl-labeled-inside-loop/input.js"
|
||||
Expect Syntax Error: "core/scope/dupl-bind-catch-func/input.js"
|
||||
Expect Syntax Error: "core/scope/dupl-bind-func-var-sloppy/input.js"
|
||||
Expect Syntax Error: "es2015/class-methods/direct-super-in-object-method/input.js"
|
||||
Expect Syntax Error: "es2015/destructuring/error-operator-for-default/input.js"
|
||||
Expect Syntax Error: "es2015/for-of/invalid-let-as-identifier/input.js"
|
||||
|
|
@ -218,28 +220,6 @@ Expect to Parse: "core/opts/allowNewTargetOutsideFunction-true/input.js"
|
|||
· ──────────
|
||||
╰────
|
||||
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
|
||||
Expect to Parse: "core/scope/dupl-bind-func-var/input.js"
|
||||
|
||||
× Identifier `foo` has already been declared
|
||||
╭─[core/scope/dupl-bind-func-var/input.js:1:1]
|
||||
1 │ function foo() {}
|
||||
· ─┬─
|
||||
· ╰── `foo` has already been declared here
|
||||
2 │ var foo = 1;
|
||||
· ─┬─
|
||||
· ╰── It can not be redeclared here
|
||||
╰────
|
||||
Expect to Parse: "core/scope/dupl-bind-gen-gen-script/input.js"
|
||||
|
||||
× Identifier `foo` has already been declared
|
||||
╭─[core/scope/dupl-bind-gen-gen-script/input.js:1:1]
|
||||
1 │ function *foo() {};
|
||||
· ─┬─
|
||||
· ╰── `foo` has already been declared here
|
||||
2 │ function *foo() {};
|
||||
· ─┬─
|
||||
· ╰── It can not be redeclared here
|
||||
╰────
|
||||
Expect to Parse: "core/uncategorised/327/input.js"
|
||||
|
||||
× TS1108: A 'return' statement can only be used within a function body
|
||||
|
|
@ -904,18 +884,6 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
|
|||
· ╰── `foo` has already been declared here
|
||||
╰────
|
||||
|
||||
× Identifier `foo` has already been declared
|
||||
╭─[core/scope/dupl-bind-catch-func/input.js:1:1]
|
||||
1 │ try {
|
||||
2 │ } catch (foo) {
|
||||
· ─┬─
|
||||
· ╰── `foo` has already been declared here
|
||||
3 │ function foo() {}
|
||||
· ─┬─
|
||||
· ╰── It can not be redeclared here
|
||||
4 │ }
|
||||
╰────
|
||||
|
||||
× Identifier `foo` has already been declared
|
||||
╭─[core/scope/dupl-bind-catch-let/input.js:1:1]
|
||||
1 │ try {
|
||||
|
|
@ -1046,18 +1014,6 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
|
|||
· ╰── It can not be redeclared here
|
||||
╰────
|
||||
|
||||
× Identifier `foo` has already been declared
|
||||
╭─[core/scope/dupl-bind-func-var-sloppy/input.js:1:1]
|
||||
1 │ {
|
||||
2 │ function foo() {}
|
||||
· ─┬─
|
||||
· ╰── `foo` has already been declared here
|
||||
3 │ var foo = 1;
|
||||
· ─┬─
|
||||
· ╰── It can not be redeclared here
|
||||
4 │ }
|
||||
╰────
|
||||
|
||||
× Identifier `f` has already been declared
|
||||
╭─[core/scope/dupl-bind-gen-func/input.js:1:1]
|
||||
1 │ { function* f() {} function f() {} }
|
||||
|
|
|
|||
|
|
@ -1,31 +1,7 @@
|
|||
Test262 Summary:
|
||||
AST Parsed : 44015/44024 (99.98%)
|
||||
Positive Passed: 44015/44024 (99.98%)
|
||||
AST Parsed : 44022/44024 (100.00%)
|
||||
Positive Passed: 44022/44024 (100.00%)
|
||||
Negative Passed: 3915/3915 (100.00%)
|
||||
Expect to Parse: "language/block-scope/syntax/redeclaration-global/allowed-to-redeclare-function-declaration-with-var.js"
|
||||
|
||||
× Identifier `f` has already been declared
|
||||
╭─[language/block-scope/syntax/redeclaration-global/allowed-to-redeclare-function-declaration-with-var.js:8:1]
|
||||
8 │ ---*/
|
||||
9 │ function f() {} var f;
|
||||
· ┬ ┬
|
||||
· │ ╰── It can not be redeclared here
|
||||
· ╰── `f` has already been declared here
|
||||
10 │
|
||||
╰────
|
||||
Expect to Parse: "language/destructuring/binding/syntax/recursive-array-and-object-patterns.js"
|
||||
|
||||
× Identifier `fn4` has already been declared
|
||||
╭─[language/destructuring/binding/syntax/recursive-array-and-object-patterns.js:22:1]
|
||||
22 │
|
||||
23 │ function fn4([], [[]], [[[[[[[[[x]]]]]]]]]) {}
|
||||
· ─┬─
|
||||
· ╰── `fn4` has already been declared here
|
||||
24 │
|
||||
25 │ function fn4([[x, y, ...z]]) {}
|
||||
· ─┬─
|
||||
· ╰── It can not be redeclared here
|
||||
╰────
|
||||
Expect to Parse: "language/expressions/class/decorator/syntax/class-valid/decorator-member-expr-private-identifier.js"
|
||||
|
||||
× Unexpected token
|
||||
|
|
@ -35,17 +11,6 @@ Expect to Parse: "language/expressions/class/decorator/syntax/class-valid/decora
|
|||
· ──
|
||||
45 │ @#_
|
||||
╰────
|
||||
Expect to Parse: "language/global-code/decl-func-dup.js"
|
||||
|
||||
× Identifier `f` has already been declared
|
||||
╭─[language/global-code/decl-func-dup.js:8:1]
|
||||
8 │ ---*/
|
||||
9 │ function f() { return 1; } function f() { return 2; }
|
||||
· ┬ ┬
|
||||
· │ ╰── It can not be redeclared here
|
||||
· ╰── `f` has already been declared here
|
||||
10 │
|
||||
╰────
|
||||
Expect to Parse: "language/statements/class/decorator/syntax/class-valid/decorator-member-expr-private-identifier.js"
|
||||
|
||||
× Unexpected token
|
||||
|
|
@ -55,83 +20,6 @@ Expect to Parse: "language/statements/class/decorator/syntax/class-valid/decorat
|
|||
· ──
|
||||
46 │ @#_
|
||||
╰────
|
||||
Expect to Parse: "language/statements/function/S13_A6_T1.js"
|
||||
|
||||
× Identifier `__func` has already been declared
|
||||
╭─[language/statements/function/S13_A6_T1.js:11:1]
|
||||
11 │
|
||||
12 │ function __func(){return 1};
|
||||
· ───┬──
|
||||
· ╰── `__func` has already been declared here
|
||||
13 │
|
||||
14 │ var __store__func = __func;
|
||||
15 │
|
||||
16 │ var __1 = __func();
|
||||
17 │
|
||||
18 │ function __func(){return 'A'};
|
||||
· ───┬──
|
||||
· ╰── It can not be redeclared here
|
||||
19 │
|
||||
╰────
|
||||
Expect to Parse: "language/statements/function/S13_A6_T2.js"
|
||||
|
||||
× Identifier `__func` has already been declared
|
||||
╭─[language/statements/function/S13_A6_T2.js:24:1]
|
||||
24 │
|
||||
25 │ function __func(){return "FIRST";};
|
||||
· ───┬──
|
||||
· ╰── `__func` has already been declared here
|
||||
26 │
|
||||
27 │ //////////////////////////////////////////////////////////////////////////////
|
||||
28 │ //CHECK#2
|
||||
29 │ __result = __func();
|
||||
30 │ if (__result !== "SECOND") {
|
||||
31 │ throw new Test262Error('#2: __result === "SECOND". Actual: __result ==='+__result);
|
||||
32 │ }
|
||||
33 │ //
|
||||
34 │ //////////////////////////////////////////////////////////////////////////////
|
||||
35 │
|
||||
36 │ function __func(){return "SECOND";};
|
||||
· ───┬──
|
||||
· ╰── It can not be redeclared here
|
||||
╰────
|
||||
Expect to Parse: "language/statements/function/S14_A5_T1.js"
|
||||
|
||||
× Identifier `__func` has already been declared
|
||||
╭─[language/statements/function/S14_A5_T1.js:21:1]
|
||||
21 │
|
||||
22 │ function __func(){return "ascii"};
|
||||
· ───┬──
|
||||
· ╰── `__func` has already been declared here
|
||||
23 │ function \u005f\u005f\u0066\u0075\u006e\u0063(){return "unicode"};//__func in unicode
|
||||
· ──────────────────┬─────────────────
|
||||
· ╰── It can not be redeclared here
|
||||
24 │ function __\u0066\u0075\u006e\u0063(){return "both"};//__func in unicode
|
||||
╰────
|
||||
|
||||
× Identifier `__func` has already been declared
|
||||
╭─[language/statements/function/S14_A5_T1.js:21:1]
|
||||
21 │
|
||||
22 │ function __func(){return "ascii"};
|
||||
· ───┬──
|
||||
· ╰── `__func` has already been declared here
|
||||
23 │ function \u005f\u005f\u0066\u0075\u006e\u0063(){return "unicode"};//__func in unicode
|
||||
24 │ function __\u0066\u0075\u006e\u0063(){return "both"};//__func in unicode
|
||||
· ─────────────┬────────────
|
||||
· ╰── It can not be redeclared here
|
||||
╰────
|
||||
Expect to Parse: "language/statements/function/S14_A5_T2.js"
|
||||
|
||||
× Identifier `__func` has already been declared
|
||||
╭─[language/statements/function/S14_A5_T2.js:21:1]
|
||||
21 │
|
||||
22 │ function __func(){return "ascii"};
|
||||
· ───┬──
|
||||
· ╰── `__func` has already been declared here
|
||||
23 │ function \u005f\u005f\u0066\u0075\u006e\u0063(){return "unicode"};//__func in unicode
|
||||
· ──────────────────┬─────────────────
|
||||
· ╰── It can not be redeclared here
|
||||
╰────
|
||||
|
||||
× '0'-prefixed octal literals and octal escape sequences are deprecated
|
||||
╭─[annexB/language/expressions/template-literal/legacy-octal-escape-sequence-strict.js:17:1]
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 7f292bf2a19aa14ed69a55e646111af9533d8f1c
|
||||
Subproject commit b8c71685f910230a9dca864d403e370a27ea1041
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
TypeScript Summary:
|
||||
AST Parsed : 2323/2340 (99.27%)
|
||||
Positive Passed: 2323/2340 (99.27%)
|
||||
Negative Passed: 681/2532 (26.90%)
|
||||
Negative Passed: 680/2532 (26.86%)
|
||||
Expect Syntax Error: "Symbols/ES5SymbolProperty2.ts"
|
||||
Expect Syntax Error: "Symbols/ES5SymbolProperty6.ts"
|
||||
Expect Syntax Error: "additionalChecks/noPropertyAccessFromIndexSignature1.ts"
|
||||
|
|
@ -829,6 +829,7 @@ Expect Syntax Error: "externalModules/verbatimModuleSyntaxInternalImportEquals.t
|
|||
Expect Syntax Error: "fixSignatureCaching.ts"
|
||||
Expect Syntax Error: "functions/functionImplementationErrors.ts"
|
||||
Expect Syntax Error: "functions/functionImplementations.ts"
|
||||
Expect Syntax Error: "functions/functionNameConflicts.ts"
|
||||
Expect Syntax Error: "functions/functionOverloadCompatibilityWithVoid01.ts"
|
||||
Expect Syntax Error: "functions/functionOverloadErrors.ts"
|
||||
Expect Syntax Error: "functions/parameterInitializersForwardReferencing.2.ts"
|
||||
|
|
@ -8267,54 +8268,6 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts"
|
|||
15 │ }
|
||||
╰────
|
||||
|
||||
× Identifier `fn1` has already been declared
|
||||
╭─[functions/functionNameConflicts.ts:4:1]
|
||||
4 │ module M {
|
||||
5 │ function fn1() { }
|
||||
· ─┬─
|
||||
· ╰── `fn1` has already been declared here
|
||||
6 │ var fn1;
|
||||
· ─┬─
|
||||
· ╰── It can not be redeclared here
|
||||
7 │
|
||||
╰────
|
||||
|
||||
× Identifier `fn2` has already been declared
|
||||
╭─[functions/functionNameConflicts.ts:7:1]
|
||||
7 │
|
||||
8 │ var fn2;
|
||||
· ─┬─
|
||||
· ╰── `fn2` has already been declared here
|
||||
9 │ function fn2() { }
|
||||
· ─┬─
|
||||
· ╰── It can not be redeclared here
|
||||
10 │ }
|
||||
╰────
|
||||
|
||||
× Identifier `fn3` has already been declared
|
||||
╭─[functions/functionNameConflicts.ts:11:1]
|
||||
11 │
|
||||
12 │ function fn3() { }
|
||||
· ─┬─
|
||||
· ╰── `fn3` has already been declared here
|
||||
13 │ var fn3;
|
||||
· ─┬─
|
||||
· ╰── It can not be redeclared here
|
||||
14 │
|
||||
╰────
|
||||
|
||||
× Identifier `fn5` has already been declared
|
||||
╭─[functions/functionNameConflicts.ts:18:1]
|
||||
18 │
|
||||
19 │ function fn5() { }
|
||||
· ─┬─
|
||||
· ╰── `fn5` has already been declared here
|
||||
20 │ var fn5;
|
||||
· ─┬─
|
||||
· ╰── It can not be redeclared here
|
||||
21 │ }
|
||||
╰────
|
||||
|
||||
× Rest element must be last element
|
||||
╭─[functions/functionOverloadErrorsSyntax.ts:8:1]
|
||||
8 │ //Function overload signature with rest param followed by non-optional parameter
|
||||
|
|
|
|||
Loading…
Reference in a new issue