mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(semantic): bind Class
This commit is contained in:
parent
5c8670d1c8
commit
b1e802cecc
7 changed files with 153 additions and 46 deletions
25
crates/oxc_semantic/src/binder.rs
Normal file
25
crates/oxc_semantic/src/binder.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
#![feature(is_some_and)]
|
||||
#![feature(let_chains)]
|
||||
|
||||
mod binder;
|
||||
mod builder;
|
||||
mod node;
|
||||
mod scope;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue