feat(semantic): bind Class

This commit is contained in:
Boshen 2023-03-11 12:50:03 +08:00
parent 5c8670d1c8
commit b1e802cecc
7 changed files with 153 additions and 46 deletions

View file

@ -0,0 +1,25 @@
//! Declare symbol for `BindingIdentifier`s
#[allow(clippy::wildcard_imports)]
use oxc_ast::ast::*;
use crate::{symbol::SymbolFlags, SemanticBuilder};
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,
);
}
}
}

View file

@ -9,6 +9,7 @@ use oxc_ast::{ast::*, visit::Visit, AstKind, Atom, GetSpan, SourceType, Span, Tr
use oxc_diagnostics::{Error, Redeclaration};
use crate::{
binder::Binder,
node::{AstNodeId, AstNodes, NodeFlags, SemanticNode},
scope::{ScopeBuilder, ScopeId},
symbol::{Reference, ReferenceFlag, SymbolFlags, SymbolId, SymbolTable},
@ -16,19 +17,19 @@ use crate::{
};
pub struct SemanticBuilder<'a> {
source_type: SourceType,
pub source_type: SourceType,
/// Semantic early errors such as redeclaration errors.
errors: Vec<Error>,
// states
current_node_id: AstNodeId,
current_node_flags: NodeFlags,
pub current_node_id: AstNodeId,
pub current_node_flags: NodeFlags,
// builders
nodes: AstNodes<'a>,
scope: ScopeBuilder,
symbols: SymbolTable,
pub nodes: AstNodes<'a>,
pub scope: ScopeBuilder,
pub symbols: SymbolTable,
}
pub struct ScopeBuilderReturn<'a> {
@ -172,19 +173,18 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
}
impl<'a> SemanticBuilder<'a> {
#[allow(clippy::single_match)]
fn enter_kind(&mut self, kind: AstKind<'a>) {
match kind {
AstKind::BindingIdentifier(ident) => {
self.enter_binding_identifier(ident);
}
AstKind::IdentifierReference(ident) => {
self.enter_identifier_reference(ident);
self.reference_identifier(ident);
}
AstKind::JSXElementName(elem) => {
self.enter_jsx_element_name(elem);
self.reference_jsx_element_name(elem);
}
AstKind::Class(class) => {
self.current_node_flags |= NodeFlags::Class;
class.bind(self);
}
AstKind::Class(class) => self.enter_class(class),
_ => {}
}
}
@ -192,22 +192,14 @@ impl<'a> SemanticBuilder<'a> {
#[allow(clippy::single_match)]
fn leave_kind(&mut self, kind: AstKind<'a>) {
match kind {
AstKind::Class(class) => self.leave_class(class),
AstKind::Class(_) => {
self.current_node_flags -= NodeFlags::Class;
}
_ => {}
}
}
fn enter_binding_identifier(&mut self, ident: &BindingIdentifier) {
self.declare_symbol(
&ident.name,
ident.span,
self.scope.current_scope_id,
SymbolFlags::empty(),
SymbolFlags::empty(),
);
}
fn enter_identifier_reference(&mut self, ident: &IdentifierReference) {
fn reference_identifier(&mut self, ident: &IdentifierReference) {
let flag = if matches!(
self.parent_kind(),
AstKind::SimpleAssignmentTarget(_) | AstKind::AssignmentTarget(_)
@ -220,7 +212,7 @@ impl<'a> SemanticBuilder<'a> {
self.scope.reference_identifier(&ident.name, reference);
}
fn enter_jsx_element_name(&mut self, elem: &JSXElementName) {
fn reference_jsx_element_name(&mut self, elem: &JSXElementName) {
if matches!(self.parent_kind(), AstKind::JSXOpeningElement(_)) {
if let Some(ident) = match elem {
JSXElementName::Identifier(ident)
@ -237,12 +229,4 @@ impl<'a> SemanticBuilder<'a> {
}
}
}
fn enter_class(&mut self, _: &Class<'a>) {
self.current_node_flags |= NodeFlags::Class;
}
fn leave_class(&mut self, _: &Class<'a>) {
self.current_node_flags -= NodeFlags::Class;
}
}

View file

@ -1,5 +1,7 @@
#![feature(is_some_and)]
#![feature(let_chains)]
mod binder;
mod builder;
mod node;
mod scope;

View file

@ -31,8 +31,13 @@ fn symbol_size() {
bitflags! {
#[derive(Default)]
pub struct SymbolFlags: u32 {
pub struct SymbolFlags: u16 {
const None = 0;
const Class = 1 << 5;
const Value = Self::Class.bits;
const ClassExcludes = Self::Value.bits;
}
}

View file

@ -1,7 +1,7 @@
Babel Summary:
AST Parsed : 2056/2069 (99.37%)
Positive Passed: 2056/2069 (99.37%)
Negative Passed: 936/1502 (62.32%)
Negative Passed: 938/1502 (62.45%)
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"
@ -37,7 +37,6 @@ Expect Syntax Error: "core/scope/dupl-bind-catch-let/input.js"
Expect Syntax Error: "core/scope/dupl-bind-catch-obj-destr/input.js"
Expect Syntax Error: "core/scope/dupl-bind-catch-var-arr-destr/input.js"
Expect Syntax Error: "core/scope/dupl-bind-catch-var-obj-destr/input.js"
Expect Syntax Error: "core/scope/dupl-bind-class-class/input.js"
Expect Syntax Error: "core/scope/dupl-bind-class-const/input.js"
Expect Syntax Error: "core/scope/dupl-bind-class-func/input.js"
Expect Syntax Error: "core/scope/dupl-bind-class-let/input.js"
@ -510,7 +509,6 @@ Expect Syntax Error: "typescript/interface/invalid-modifiers-method/input.ts"
Expect Syntax Error: "typescript/interface/invalid-modifiers-property/input.ts"
Expect Syntax Error: "typescript/module-namespace/top-level-await/input.ts"
Expect Syntax Error: "typescript/regression/keyword-qualified-type-disallowed/input.ts"
Expect Syntax Error: "typescript/scope/redeclaration-class-class/input.ts"
Expect Syntax Error: "typescript/scope/redeclaration-class-enum/input.ts"
Expect Syntax Error: "typescript/scope/redeclaration-class-type/input.ts"
Expect Syntax Error: "typescript/scope/redeclaration-constenum-enum/input.ts"
@ -898,6 +896,16 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
╰────
help: Try insert a semicolon here
× Identifier `"foo"` has already been declared
╭─[core/scope/dupl-bind-class-class/input.js:1:1]
1 │ class foo {};
· ─┬─
· ╰── `foo` has already been declared here
2 │ class foo {};
· ─┬─
· ╰── It can not be redeclared here
╰────
× A 'get' accessor must not have any formal parameters.
╭─[core/uncategorised/.542/input.js:1:1]
1 │ ({ get prop(x) {} })
@ -7695,6 +7703,16 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
╰────
help: Try insert a semicolon here
× Identifier `"A"` has already been declared
╭─[typescript/scope/redeclaration-class-class/input.ts:1:1]
1 │ class A {}
· ┬
· ╰── `A` has already been declared here
2 │ class A {}
· ┬
· ╰── It can not be redeclared here
╰────
× Automatic Semicolon Insertion
╭─[typescript/static-blocks/invalid-static-block-with-accessibility-private-02/input.ts:1:1]
1 │ class Foo {

View file

@ -1,7 +1,7 @@
Test262 Summary:
AST Parsed : 44022/44034 (99.97%)
Positive Passed: 44022/44034 (99.97%)
Negative Passed: 2626/3917 (67.04%)
Negative Passed: 2630/3917 (67.14%)
Expect Syntax Error: "annexB/language/statements/for-in/const-initializer.js"
Expect Syntax Error: "annexB/language/statements/for-in/let-initializer.js"
Expect Syntax Error: "annexB/language/statements/for-in/strict-initializer.js"
@ -35,7 +35,6 @@ Expect Syntax Error: "language/block-scope/syntax/redeclaration/async-generator-
Expect Syntax Error: "language/block-scope/syntax/redeclaration/async-generator-name-redeclaration-attempt-with-var.js"
Expect Syntax Error: "language/block-scope/syntax/redeclaration/class-name-redeclaration-attempt-with-async-function.js"
Expect Syntax Error: "language/block-scope/syntax/redeclaration/class-name-redeclaration-attempt-with-async-generator.js"
Expect Syntax Error: "language/block-scope/syntax/redeclaration/class-name-redeclaration-attempt-with-class.js"
Expect Syntax Error: "language/block-scope/syntax/redeclaration/class-name-redeclaration-attempt-with-const.js"
Expect Syntax Error: "language/block-scope/syntax/redeclaration/class-name-redeclaration-attempt-with-function.js"
Expect Syntax Error: "language/block-scope/syntax/redeclaration/class-name-redeclaration-attempt-with-generator.js"
@ -985,8 +984,6 @@ Expect Syntax Error: "language/statements/class/static-init-invalid-lex-dup.js"
Expect Syntax Error: "language/statements/class/static-init-invalid-lex-var.js"
Expect Syntax Error: "language/statements/class/static-init-invalid-return.js"
Expect Syntax Error: "language/statements/class/static-init-invalid-super-call.js"
Expect Syntax Error: "language/statements/class/syntax/early-errors/class-definition-evaluation-block-duplicate-binding.js"
Expect Syntax Error: "language/statements/class/syntax/early-errors/class-definition-evaluation-scriptbody-duplicate-binding.js"
Expect Syntax Error: "language/statements/const/dstr/ary-ptrn-rest-init-ary.js"
Expect Syntax Error: "language/statements/const/dstr/ary-ptrn-rest-init-id.js"
Expect Syntax Error: "language/statements/const/dstr/ary-ptrn-rest-init-obj.js"
@ -1196,7 +1193,6 @@ Expect Syntax Error: "language/statements/switch/syntax/redeclaration/async-gene
Expect Syntax Error: "language/statements/switch/syntax/redeclaration/async-generator-name-redeclaration-attempt-with-var.js"
Expect Syntax Error: "language/statements/switch/syntax/redeclaration/class-name-redeclaration-attempt-with-async-function.js"
Expect Syntax Error: "language/statements/switch/syntax/redeclaration/class-name-redeclaration-attempt-with-async-generator.js"
Expect Syntax Error: "language/statements/switch/syntax/redeclaration/class-name-redeclaration-attempt-with-class.js"
Expect Syntax Error: "language/statements/switch/syntax/redeclaration/class-name-redeclaration-attempt-with-const.js"
Expect Syntax Error: "language/statements/switch/syntax/redeclaration/class-name-redeclaration-attempt-with-function.js"
Expect Syntax Error: "language/statements/switch/syntax/redeclaration/class-name-redeclaration-attempt-with-generator.js"
@ -1722,6 +1718,15 @@ Expect to Parse: "language/statements/class/decorator/syntax/valid/decorator-par
17 │ while (false)
╰────
× Identifier `"f"` has already been declared
╭─[language/block-scope/syntax/redeclaration/class-name-redeclaration-attempt-with-class.js:21:1]
21 │
22 │ { class f {} class f {} }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `f` has already been declared here
╰────
× Unterminated multiline comment
╭─[language/comments/S7.4_A2_T2.js:14:1]
14 │
@ -20662,6 +20667,29 @@ Expect to Parse: "language/statements/class/decorator/syntax/valid/decorator-par
15 │
╰────
× Identifier `"A"` has already been declared
╭─[language/statements/class/syntax/early-errors/class-definition-evaluation-block-duplicate-binding.js:15:1]
15 │ {
16 │ class A {}
· ┬
· ╰── `A` has already been declared here
17 │ class A {}
· ┬
· ╰── It can not be redeclared here
18 │ }
╰────
× Identifier `"A"` has already been declared
╭─[language/statements/class/syntax/early-errors/class-definition-evaluation-scriptbody-duplicate-binding.js:14:1]
14 │ $DONOTEVALUATE();
15 │ class A {}
· ┬
· ╰── `A` has already been declared here
16 │ class A {}
· ┬
· ╰── It can not be redeclared here
╰────
× Keywords cannot contain escape characters
╭─[language/statements/class/syntax/escaped-static.js:23:1]
23 │ class C {
@ -23662,6 +23690,15 @@ Expect to Parse: "language/statements/class/decorator/syntax/valid/decorator-par
20 │ case 0:
╰────
× Identifier `"f"` has already been declared
╭─[language/statements/switch/syntax/redeclaration/class-name-redeclaration-attempt-with-class.js:21:1]
21 │
22 │ switch (0) { case 1: class f {} default: class f {} }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `f` has already been declared here
╰────
× Expect token
╭─[language/statements/try/S12.14_A16_T1.js:17:1]
17 │ // CHECK#1

View file

@ -1,7 +1,7 @@
TypeScript Summary:
AST Parsed : 2308/2338 (98.72%)
Positive Passed: 2308/2338 (98.72%)
Negative Passed: 578/2532 (22.83%)
Negative Passed: 580/2532 (22.91%)
Expect Syntax Error: "Symbols/ES5SymbolProperty2.ts"
Expect Syntax Error: "Symbols/ES5SymbolProperty6.ts"
Expect Syntax Error: "additionalChecks/noPropertyAccessFromIndexSignature1.ts"
@ -80,7 +80,6 @@ Expect Syntax Error: "classes/classDeclarations/classAbstractKeyword/classAbstra
Expect Syntax Error: "classes/classDeclarations/classAbstractKeyword/classAbstractInheritance.ts"
Expect Syntax Error: "classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts"
Expect Syntax Error: "classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts"
Expect Syntax Error: "classes/classDeclarations/classAbstractKeyword/classAbstractMergedDeclaration.ts"
Expect Syntax Error: "classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts"
Expect Syntax Error: "classes/classDeclarations/classAbstractKeyword/classAbstractMethodWithImplementation.ts"
Expect Syntax Error: "classes/classDeclarations/classAbstractKeyword/classAbstractOverloads.ts"
@ -588,7 +587,6 @@ Expect Syntax Error: "es6/functionPropertyAssignments/FunctionPropertyAssignment
Expect Syntax Error: "es6/memberFunctionDeclarations/MemberFunctionDeclaration3_es6.ts"
Expect Syntax Error: "es6/modules/defaultExportsCannotMerge04.ts"
Expect Syntax Error: "es6/modules/importEmptyFromModuleNotExisted.ts"
Expect Syntax Error: "es6/modules/multipleDefaultExports03.ts"
Expect Syntax Error: "es6/modules/multipleDefaultExports04.ts"
Expect Syntax Error: "es6/modules/multipleDefaultExports05.ts"
Expect Syntax Error: "es6/newTarget/invalidNewTarget.es5.ts"
@ -2499,6 +2497,30 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts"
4 │ import abstract class D {}
╰────
× Identifier `"CC1"` has already been declared
╭─[classes/classDeclarations/classAbstractKeyword/classAbstractMergedDeclaration.ts:12:1]
12 │
13 │ abstract class CC1 {}
· ─┬─
· ╰── `CC1` has already been declared here
14 │ class CC1 {}
· ─┬─
· ╰── It can not be redeclared here
15 │
╰────
× Identifier `"CC2"` has already been declared
╭─[classes/classDeclarations/classAbstractKeyword/classAbstractMergedDeclaration.ts:15:1]
15 │
16 │ class CC2 {}
· ─┬─
· ╰── `CC2` has already been declared here
17 │ abstract class CC2 {}
· ─┬─
· ╰── It can not be redeclared here
18 │
╰────
× Expect token
╭─[classes/classDeclarations/classAbstractKeyword/classAbstractMixedWithModifiers.ts:15:1]
15 │ abstract async foo_f();
@ -4718,6 +4740,20 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts"
6 │ return bar;
╰────
× Identifier `"C"` has already been declared
╭─[es6/modules/multipleDefaultExports03.ts:3:1]
3 │
4 │ export default class C {
· ┬
· ╰── `C` has already been declared here
5 │ }
6 │
7 │ export default class C {
· ┬
· ╰── It can not be redeclared here
8 │ }
╰────
× Unexpected token
╭─[es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts:2:1]
2 │ var y = {