feat(semantic): bind Function name

This commit is contained in:
Boshen 2023-03-11 15:22:01 +08:00
parent 995e7c1767
commit 58e2741ec9
6 changed files with 1589 additions and 172 deletions

View file

@ -2,10 +2,10 @@
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
use oxc_ast::syntax_directed_operations::BoundNames;
use oxc_ast::{syntax_directed_operations::BoundNames, AstKind};
use crate::{
scope::{ScopeFlags, ScopeId},
scope::{Scope, ScopeFlags, ScopeId},
symbol::SymbolFlags,
SemanticBuilder,
};
@ -14,21 +14,6 @@ pub trait Binder {
fn bind(&self, _builder: &mut SemanticBuilder) {}
}
impl<'a> Binder for Class<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
if let Some(ident) = self.id.as_ref()
&& self.r#type == ClassType::ClassDeclaration && !self.modifiers.contains(ModifierKind::Declare) {
builder.declare_symbol(
&ident.name,
ident.span,
builder.scope.current_scope_id,
SymbolFlags::Class ,
SymbolFlags::ClassExcludes,
);
}
}
}
impl<'a> Binder for VariableDeclarator<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
let current_scope_id = builder.scope.current_scope_id;
@ -75,6 +60,83 @@ impl<'a> Binder for VariableDeclarator<'a> {
}
}
impl<'a> Binder for Class<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
if let Some(ident) = &self.id
&& self.r#type == ClassType::ClassDeclaration && !self.modifiers.contains(ModifierKind::Declare) {
builder.declare_symbol(
&ident.name,
ident.span,
builder.scope.current_scope_id,
SymbolFlags::Class,
SymbolFlags::ClassExcludes,
);
}
}
}
// It is a Syntax Error if the LexicallyDeclaredNames of StatementList contains any duplicate entries,
// unless the source text matched by this production is not strict mode code
// 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 {
scope.flags.intersects(ScopeFlags::Function)
|| (is_script && scope.flags.intersects(ScopeFlags::Top))
}
impl<'a> Binder for Function<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
if let Some(ident) = &self.id {
let current_scope_id = builder.scope.current_scope_id;
let scope = builder.scope.current_scope();
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,
// 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,
)
} else {
(
SymbolFlags::Function,
SymbolFlags::BlockScopedVariableExcludes - SymbolFlags::Function,
)
};
builder.declare_symbol(
&ident.name,
ident.span,
parent_scope_id,
includes,
excludes,
);
};
}
}
}
impl<'a> Binder for FormalParameters<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
let includes = SymbolFlags::FunctionScopedVariable;

View file

@ -178,6 +178,13 @@ impl<'a> SemanticBuilder<'a> {
AstKind::VariableDeclarator(decl) => {
decl.bind(self);
}
AstKind::Function(func) => {
func.bind(self);
}
AstKind::Class(class) => {
self.current_node_flags |= NodeFlags::Class;
class.bind(self);
}
AstKind::FormalParameters(params) => {
params.bind(self);
}
@ -190,10 +197,6 @@ impl<'a> SemanticBuilder<'a> {
AstKind::JSXElementName(elem) => {
self.reference_jsx_element_name(elem);
}
AstKind::Class(class) => {
self.current_node_flags |= NodeFlags::Class;
class.bind(self);
}
_ => {}
}
}

View file

@ -39,11 +39,10 @@ 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::Class.bits;
const ClassExcludes = Self::Value.bits;
const Value = Self::Variable.bits | Self::Function.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,6 +51,9 @@ bitflags! {
/// Block-scoped declarations are not allowed to be re-declared
/// 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;
}
}

View file

@ -1,7 +1,7 @@
Babel Summary:
AST Parsed : 2056/2069 (99.37%)
Positive Passed: 2056/2069 (99.37%)
Negative Passed: 972/1502 (64.71%)
AST Parsed : 2054/2069 (99.28%)
Positive Passed: 2054/2069 (99.28%)
Negative Passed: 987/1502 (65.71%)
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"
@ -24,13 +24,6 @@ Expect Syntax Error: "core/legacy-octal/legacy-octal-after-use-strict-function/i
Expect Syntax Error: "core/legacy-octal/legacy-octal-after-use-strict/input.js"
Expect Syntax Error: "core/opts/allowNewTargetOutsideFunction-false-2/input.js"
Expect Syntax Error: "core/opts/allowNewTargetOutsideFunction-false/input.js"
Expect Syntax Error: "core/scope/dupl-bind-catch-func/input.js"
Expect Syntax Error: "core/scope/dupl-bind-class-func/input.js"
Expect Syntax Error: "core/scope/dupl-bind-func-gen/input.js"
Expect Syntax Error: "core/scope/dupl-bind-func-module-sloppy/input.js"
Expect Syntax Error: "core/scope/dupl-bind-func-module/input.js"
Expect Syntax Error: "core/scope/dupl-bind-func-var-sloppy/input.js"
Expect Syntax Error: "core/scope/dupl-bind-gen-func/input.js"
Expect Syntax Error: "core/scope/undecl-export-as-default/input.js"
Expect Syntax Error: "core/scope/undecl-export-as/input.js"
Expect Syntax Error: "core/scope/undecl-export-block/input.js"
@ -122,14 +115,6 @@ Expect Syntax Error: "es2015/modules/duplicate-export-default-and-export-as-defa
Expect Syntax Error: "es2015/modules/duplicate-export-default/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-class-declaration/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-destructuring-assignment/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-destructuring10/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-destructuring11/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-destructuring12/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-destructuring13/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-destructuring2/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-destructuring3/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-destructuring4/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-destructuring5/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-function-declaration/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-variable-declaration/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export/input.js"
@ -532,6 +517,28 @@ Expect Syntax Error: "typescript/types/tuple-optional-invalid/input.ts"
Expect Syntax Error: "typescript/types/tuple-required-after-labeled-optional/input.ts"
Expect Syntax Error: "typescript/types/tuple-unlabeled-spread-after-labeled/input.ts"
Expect Syntax Error: "typescript/types/tuple-unlabeled-spread-before-labeled/input.ts"
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: "typescript/arrow-function/generic-tsx-babel-7/input.ts"
× Expect token
@ -944,6 +951,18 @@ 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 {
@ -1010,6 +1029,16 @@ 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-class-func/input.js:1:1]
1 │ class foo {};
· ─┬─
· ╰── `foo` has already been declared here
2 │ function foo () {};
· ─┬─
· ╰── It can not be redeclared here
╰────
× Identifier `"foo"` has already been declared
╭─[core/scope/dupl-bind-class-let/input.js:1:1]
1 │ class foo {};
@ -1038,6 +1067,52 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ╰── `foo` has already been declared here
╰────
× Identifier `"f"` has already been declared
╭─[core/scope/dupl-bind-func-gen/input.js:1:1]
1 │ { function f() {} function* f() {} }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `f` has already been declared here
╰────
× Identifier `"foo"` has already been declared
╭─[core/scope/dupl-bind-func-module-sloppy/input.js:1:1]
1 │ { function foo() {} function foo() {} }
· ─┬─ ─┬─
· │ ╰── It can not be redeclared here
· ╰── `foo` has already been declared here
╰────
× Identifier `"foo"` has already been declared
╭─[core/scope/dupl-bind-func-module/input.js:1:1]
1 │ function foo() {}
· ─┬─
· ╰── `foo` has already been declared here
2 │ function foo() {}
· ─┬─
· ╰── 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() {} }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `f` has already been declared here
╰────
× Identifier `"foo"` has already been declared
╭─[core/scope/dupl-bind-let-let/input.js:1:1]
1 │ let foo, foo;
@ -2454,6 +2529,46 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ╰── keyword cannot contain escape characters
╰────
× Identifier `"foo"` has already been declared
╭─[es2015/modules/duplicate-named-export-destructuring10/input.js:1:1]
1 │ export function foo() {};
· ─┬─
· ╰── `foo` has already been declared here
2 │ export const { a: [{foo}] } = bar;
· ─┬─
· ╰── It can not be redeclared here
╰────
× Identifier `"foo4"` has already been declared
╭─[es2015/modules/duplicate-named-export-destructuring11/input.js:1:1]
1 │ export function foo4() {};
· ──┬─
· ╰── `foo4` has already been declared here
2 │ export const [{ a: [{ foo }], b: { foo2: [{ foo3: foo4 }] } }] = bar;
· ──┬─
· ╰── It can not be redeclared here
╰────
× Identifier `"foo4"` has already been declared
╭─[es2015/modules/duplicate-named-export-destructuring12/input.js:1:1]
1 │ export function foo4() {};
· ──┬─
· ╰── `foo4` has already been declared here
2 │ export const { a: [{ foo }], b: { foo2: [{ foo3: foo4 }] } } = bar;
· ──┬─
· ╰── It can not be redeclared here
╰────
× Identifier `"foo4"` has already been declared
╭─[es2015/modules/duplicate-named-export-destructuring13/input.js:1:1]
1 │ export function foo4() {};
· ──┬─
· ╰── `foo4` has already been declared here
2 │ export const { a: [{ foo4: foo }], b, c: { foo2: [{ foo3: foo4 }] } } = bar;
· ──┬─
· ╰── It can not be redeclared here
╰────
× Identifier `"foo"` has already been declared
╭─[es2015/modules/duplicate-named-export-destructuring14/input.js:1:1]
1 │ export const foo = 1;
@ -2520,6 +2635,46 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
3 │
╰────
× Identifier `"foo"` has already been declared
╭─[es2015/modules/duplicate-named-export-destructuring2/input.js:1:1]
1 │ export function foo() {};
· ─┬─
· ╰── `foo` has already been declared here
2 │ export const { foo } = bar;
· ─┬─
· ╰── It can not be redeclared here
╰────
× Identifier `"foo"` has already been declared
╭─[es2015/modules/duplicate-named-export-destructuring3/input.js:1:1]
1 │ export const { foo } = bar;
· ─┬─
· ╰── `foo` has already been declared here
2 │ export function foo() {};
· ─┬─
· ╰── It can not be redeclared here
╰────
× Identifier `"foo"` has already been declared
╭─[es2015/modules/duplicate-named-export-destructuring4/input.js:1:1]
1 │ export function foo() {};
· ─┬─
· ╰── `foo` has already been declared here
2 │ export const [foo] = bar;
· ─┬─
· ╰── It can not be redeclared here
╰────
× Identifier `"foo"` has already been declared
╭─[es2015/modules/duplicate-named-export-destructuring5/input.js:1:1]
1 │ export const [foo] = bar;
· ─┬─
· ╰── `foo` has already been declared here
2 │ export function foo() {};
· ─┬─
· ╰── It can not be redeclared here
╰────
× Identifier `"foo"` has already been declared
╭─[es2015/modules/duplicate-named-export-destructuring6/input.js:1:1]
1 │ export const { foo } = bar;

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
TypeScript Summary:
AST Parsed : 2305/2338 (98.59%)
Positive Passed: 2305/2338 (98.59%)
Negative Passed: 589/2532 (23.26%)
Negative Passed: 590/2532 (23.30%)
Expect Syntax Error: "Symbols/ES5SymbolProperty2.ts"
Expect Syntax Error: "Symbols/ES5SymbolProperty6.ts"
Expect Syntax Error: "additionalChecks/noPropertyAccessFromIndexSignature1.ts"
@ -882,7 +882,6 @@ 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/functionWithUseStrictAndSimpleParameterList_es2016.ts"
@ -6054,6 +6053,54 @@ 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