feat(ast): add SymbolId and ReferenceId (#755)

Closes #510
This commit is contained in:
Yunfei He 2023-08-19 18:09:47 +08:00 committed by GitHub
parent fd3fa6ca84
commit e7c2313817
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 145 additions and 30 deletions

View file

@ -1,9 +1,13 @@
use std::fmt; use std::{cell::Cell, fmt, hash::Hash};
use oxc_allocator::{Box, Vec}; use oxc_allocator::{Box, Vec};
use oxc_span::{Atom, SourceType, Span}; use oxc_span::{Atom, SourceType, Span};
use oxc_syntax::operator::{ use oxc_syntax::{
AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator, UpdateOperator, operator::{
AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator, UpdateOperator,
},
reference::ReferenceId,
symbol::SymbolId,
}; };
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::Serialize; use serde::Serialize;
@ -254,21 +258,51 @@ pub struct IdentifierName {
} }
/// Identifier Reference /// Identifier Reference
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))] #[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
pub struct IdentifierReference { pub struct IdentifierReference {
#[cfg_attr(feature = "serde", serde(flatten))] #[cfg_attr(feature = "serde", serde(flatten))]
pub span: Span, pub span: Span,
pub name: Atom, pub name: Atom,
#[cfg_attr(feature = "serde", serde(skip))]
pub reference_id: Cell<Option<ReferenceId>>,
}
impl Hash for IdentifierReference {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.span.hash(state);
self.name.hash(state);
}
}
impl IdentifierReference {
pub fn new(name: Atom, span: Span) -> Self {
Self { name, span, reference_id: Cell::default() }
}
} }
/// Binding Identifier /// Binding Identifier
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))] #[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
pub struct BindingIdentifier { pub struct BindingIdentifier {
#[cfg_attr(feature = "serde", serde(flatten))] #[cfg_attr(feature = "serde", serde(flatten))]
pub span: Span, pub span: Span,
pub name: Atom, pub name: Atom,
#[cfg_attr(feature = "serde", serde(skip))]
pub symbol_id: Cell<Option<SymbolId>>,
}
impl Hash for BindingIdentifier {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.span.hash(state);
self.name.hash(state);
}
}
impl BindingIdentifier {
pub fn new(name: Atom, span: Span) -> Self {
Self { name, span, symbol_id: Cell::default() }
}
} }
/// Label Identifier /// Label Identifier

View file

@ -371,6 +371,7 @@ impl<'a> AstLower<'a> {
self.hir.with_statement(stmt.span, object, body) self.hir.with_statement(stmt.span, object, body)
} }
#[allow(clippy::too_many_lines)]
fn lower_expression(&mut self, expr: &ast::Expression<'a>) -> hir::Expression<'a> { fn lower_expression(&mut self, expr: &ast::Expression<'a>) -> hir::Expression<'a> {
ensure_sufficient_stack(|| { ensure_sufficient_stack(|| {
match expr { match expr {
@ -444,7 +445,7 @@ impl<'a> AstLower<'a> {
ast::Expression::JSXElement(elem) => { ast::Expression::JSXElement(elem) => {
// TODO: implement JSX // TODO: implement JSX
let ident = self.lower_identifier_reference( let ident = self.lower_identifier_reference(
&ast::IdentifierReference { span: elem.span, name: "undefined".into() }, &ast::IdentifierReference::new("undefined".into(), elem.span),
ReferenceFlag::Read, ReferenceFlag::Read,
); );
self.hir.identifier_reference_expression(ident) self.hir.identifier_reference_expression(ident)
@ -452,7 +453,7 @@ impl<'a> AstLower<'a> {
ast::Expression::JSXFragment(elem) => { ast::Expression::JSXFragment(elem) => {
// TODO: implement JSX // TODO: implement JSX
let ident = self.lower_identifier_reference( let ident = self.lower_identifier_reference(
&ast::IdentifierReference { span: elem.span, name: "undefined".into() }, &ast::IdentifierReference::new("undefined".into(), elem.span),
ReferenceFlag::Read, ReferenceFlag::Read,
); );
self.hir.identifier_reference_expression(ident) self.hir.identifier_reference_expression(ident)
@ -850,7 +851,7 @@ impl<'a> AstLower<'a> {
expr => { expr => {
// return undefined because this is invalid syntax // return undefined because this is invalid syntax
let ident = self.lower_identifier_reference( let ident = self.lower_identifier_reference(
&ast::IdentifierReference { span: expr.span(), name: "undefined".into() }, &ast::IdentifierReference::new("undefined".into(), expr.span()),
ReferenceFlag::Write, ReferenceFlag::Write,
); );
self.hir.assignment_target_identifier(ident) self.hir.assignment_target_identifier(ident)

View file

@ -90,8 +90,7 @@ impl<'a> Parser<'a> {
// ^ BindingIdentifier // ^ BindingIdentifier
if let PropertyKey::Identifier(ident) = &key { if let PropertyKey::Identifier(ident) = &key {
shorthand = true; shorthand = true;
let binding_identifier = let binding_identifier = BindingIdentifier::new(ident.name.clone(), ident.span);
BindingIdentifier { span: ident.span, name: ident.name.clone() };
let identifier = self.ast.binding_identifier(binding_identifier); let identifier = self.ast.binding_identifier(binding_identifier);
let left = self.ast.binding_pattern(identifier, None, false); let left = self.ast.binding_pattern(identifier, None, false);
self.parse_initializer(span, left)? self.parse_initializer(span, left)?

View file

@ -1,3 +1,5 @@
use std::cell::Cell;
use oxc_allocator::Box; use oxc_allocator::Box;
use oxc_ast::ast::*; use oxc_ast::ast::*;
use oxc_diagnostics::Result; use oxc_diagnostics::Result;
@ -55,7 +57,7 @@ impl<'a> Parser<'a> {
} }
let (span, name) = self.parse_identifier_kind(Kind::Ident); let (span, name) = self.parse_identifier_kind(Kind::Ident);
self.check_identifier(span, &name); self.check_identifier(span, &name);
Ok(IdentifierReference { span, name }) Ok(IdentifierReference { span, name, reference_id: Cell::default() })
} }
/// `BindingIdentifier` : Identifier /// `BindingIdentifier` : Identifier
@ -65,7 +67,7 @@ impl<'a> Parser<'a> {
} }
let (span, name) = self.parse_identifier_kind(Kind::Ident); let (span, name) = self.parse_identifier_kind(Kind::Ident);
self.check_identifier(span, &name); self.check_identifier(span, &name);
Ok(BindingIdentifier { span, name }) Ok(BindingIdentifier { span, name, symbol_id: Cell::default() })
} }
pub(crate) fn parse_label_identifier(&mut self) -> Result<LabelIdentifier> { pub(crate) fn parse_label_identifier(&mut self) -> Result<LabelIdentifier> {

View file

@ -1,3 +1,5 @@
use std::cell::Cell;
use oxc_allocator::Box; use oxc_allocator::Box;
use oxc_ast::{ast::*, AstBuilder}; use oxc_ast::{ast::*, AstBuilder};
use oxc_diagnostics::Result; use oxc_diagnostics::Result;
@ -332,7 +334,7 @@ impl<'a> Parser<'a> {
let id = self.cur_kind().is_binding_identifier().then(|| { let id = self.cur_kind().is_binding_identifier().then(|| {
let (span, name) = self.parse_identifier_kind(Kind::Ident); let (span, name) = self.parse_identifier_kind(Kind::Ident);
self.check_identifier(span, &name); self.check_identifier(span, &name);
BindingIdentifier { span, name } BindingIdentifier { span, name, symbol_id: Cell::default() }
}); });
self.ctx = ctx; self.ctx = ctx;

View file

@ -151,7 +151,8 @@ impl<'a> CoverGrammar<'a, ObjectProperty<'a>> for AssignmentTargetProperty<'a> {
if property.shorthand { if property.shorthand {
let binding = match property.key { let binding = match property.key {
PropertyKey::Identifier(ident) => { PropertyKey::Identifier(ident) => {
IdentifierReference { span: ident.span, name: ident.unbox().name } let ident = ident.unbox();
IdentifierReference::new(ident.name, ident.span)
} }
_ => return Err(p.unexpected()), _ => return Err(p.unexpected()),
}; };

View file

@ -431,7 +431,7 @@ impl<'a> Parser<'a> {
let span = self.start_span(); let span = self.start_span();
let (ident_span, name) = self.parse_identifier_kind(Kind::This); let (ident_span, name) = self.parse_identifier_kind(Kind::This);
let type_annotation = self.parse_ts_type_annotation()?; let type_annotation = self.parse_ts_type_annotation()?;
let kind = self.ast.binding_identifier(BindingIdentifier { span: ident_span, name }); let kind = self.ast.binding_identifier(BindingIdentifier::new(name, ident_span));
let binding = self.ast.binding_pattern(kind, type_annotation, /* optional */ false); let binding = self.ast.binding_pattern(kind, type_annotation, /* optional */ false);
Ok(self.ast.formal_parameter( Ok(self.ast.formal_parameter(
self.end_span(span), self.end_span(span),

View file

@ -28,6 +28,7 @@ impl<'a> Binder for VariableDeclarator<'a> {
}; };
self.id.bound_names(&mut |ident| { self.id.bound_names(&mut |ident| {
let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes); let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
ident.symbol_id.set(Some(symbol_id));
if self.kind == VariableDeclarationKind::Var if self.kind == VariableDeclarationKind::Var
&& !builder.scope.get_flags(current_scope_id).is_var() && !builder.scope.get_flags(current_scope_id).is_var()
{ {
@ -59,12 +60,13 @@ impl<'a> Binder for Class<'a> {
fn bind(&self, builder: &mut SemanticBuilder) { fn bind(&self, builder: &mut SemanticBuilder) {
let Some(ident) = &self.id else { return }; let Some(ident) = &self.id else { return };
if !self.modifiers.contains(ModifierKind::Declare) { if !self.modifiers.contains(ModifierKind::Declare) {
builder.declare_symbol( let symbol_id = builder.declare_symbol(
ident.span, ident.span,
&ident.name, &ident.name,
SymbolFlags::Class, SymbolFlags::Class,
SymbolFlags::ClassExcludes, SymbolFlags::ClassExcludes,
); );
ident.symbol_id.set(Some(symbol_id));
} }
} }
} }
@ -108,13 +110,14 @@ impl<'a> Binder for Function<'a> {
) )
}; };
builder.declare_symbol_on_scope( let symbol_id = builder.declare_symbol_on_scope(
ident.span, ident.span,
&ident.name, &ident.name,
parent_scope_id, parent_scope_id,
includes, includes,
excludes, excludes,
); );
ident.symbol_id.set(Some(symbol_id));
} }
} }
@ -152,7 +155,8 @@ impl<'a> Binder for FormalParameters<'a> {
let is_signature = self.kind == FormalParameterKind::Signature; let is_signature = self.kind == FormalParameterKind::Signature;
self.bound_names(&mut |ident| { self.bound_names(&mut |ident| {
if !is_signature { if !is_signature {
builder.declare_symbol(ident.span, &ident.name, includes, excludes); let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
ident.symbol_id.set(Some(symbol_id));
} }
}); });
} }
@ -167,15 +171,22 @@ impl<'a> Binder for CatchClause<'a> {
// unless CatchParameter is CatchParameter : BindingIdentifier // unless CatchParameter is CatchParameter : BindingIdentifier
if let BindingPatternKind::BindingIdentifier(ident) = &param.kind { if let BindingPatternKind::BindingIdentifier(ident) = &param.kind {
let includes = SymbolFlags::FunctionScopedVariable | SymbolFlags::CatchVariable; let includes = SymbolFlags::FunctionScopedVariable | SymbolFlags::CatchVariable;
builder.declare_shadow_symbol(&ident.name, ident.span, current_scope_id, includes); let symbol_id = builder.declare_shadow_symbol(
&ident.name,
ident.span,
current_scope_id,
includes,
);
ident.symbol_id.set(Some(symbol_id));
} else { } else {
param.bound_names(&mut |ident| { param.bound_names(&mut |ident| {
builder.declare_symbol( let symbol_id = builder.declare_symbol(
ident.span, ident.span,
&ident.name, &ident.name,
SymbolFlags::BlockScopedVariable | SymbolFlags::CatchVariable, SymbolFlags::BlockScopedVariable | SymbolFlags::CatchVariable,
SymbolFlags::BlockScopedVariableExcludes, SymbolFlags::BlockScopedVariableExcludes,
); );
ident.symbol_id.set(Some(symbol_id));
}); });
} }
} }
@ -185,35 +196,38 @@ impl<'a> Binder for CatchClause<'a> {
impl<'a> Binder for ModuleDeclaration<'a> { impl<'a> Binder for ModuleDeclaration<'a> {
fn bind(&self, builder: &mut SemanticBuilder) { fn bind(&self, builder: &mut SemanticBuilder) {
self.bound_names(&mut |ident| { self.bound_names(&mut |ident| {
builder.declare_symbol( let symbol_id = builder.declare_symbol(
ident.span, ident.span,
&ident.name, &ident.name,
SymbolFlags::ImportBinding, SymbolFlags::ImportBinding,
SymbolFlags::ImportBindingExcludes, SymbolFlags::ImportBindingExcludes,
); );
ident.symbol_id.set(Some(symbol_id));
}); });
} }
} }
impl<'a> Binder for TSTypeAliasDeclaration<'a> { impl<'a> Binder for TSTypeAliasDeclaration<'a> {
fn bind(&self, builder: &mut SemanticBuilder) { fn bind(&self, builder: &mut SemanticBuilder) {
builder.declare_symbol( let symbol_id = builder.declare_symbol(
self.id.span, self.id.span,
&self.id.name, &self.id.name,
SymbolFlags::TypeAlias, SymbolFlags::TypeAlias,
SymbolFlags::TypeAliasExcludes, SymbolFlags::TypeAliasExcludes,
); );
self.id.symbol_id.set(Some(symbol_id));
} }
} }
impl<'a> Binder for TSInterfaceDeclaration<'a> { impl<'a> Binder for TSInterfaceDeclaration<'a> {
fn bind(&self, builder: &mut SemanticBuilder) { fn bind(&self, builder: &mut SemanticBuilder) {
builder.declare_symbol( let symbol_id = builder.declare_symbol(
self.id.span, self.id.span,
&self.id.name, &self.id.name,
SymbolFlags::Interface, SymbolFlags::Interface,
SymbolFlags::InterfaceExcludes, SymbolFlags::InterfaceExcludes,
); );
self.id.symbol_id.set(Some(symbol_id));
} }
} }
@ -226,7 +240,8 @@ impl<'a> Binder for TSEnumDeclaration<'a> {
} else { } else {
SymbolFlags::RegularEnumExcludes SymbolFlags::RegularEnumExcludes
}; };
builder.declare_symbol(self.id.span, &self.id.name, includes, excludes); let symbol_id = builder.declare_symbol(self.id.span, &self.id.name, includes, excludes);
self.id.symbol_id.set(Some(symbol_id));
} }
} }
@ -271,11 +286,12 @@ impl<'a> Binder for TSModuleDeclaration<'a> {
impl<'a> Binder for TSTypeParameter<'a> { impl<'a> Binder for TSTypeParameter<'a> {
fn bind(&self, builder: &mut SemanticBuilder) { fn bind(&self, builder: &mut SemanticBuilder) {
builder.declare_symbol( let symbol_id = builder.declare_symbol(
self.name.span, self.name.span,
&self.name.name, &self.name.name,
SymbolFlags::TypeParameter, SymbolFlags::TypeParameter,
SymbolFlags::TypeParameterExcludes, SymbolFlags::TypeParameterExcludes,
); );
self.name.symbol_id.set(Some(symbol_id));
} }
} }

View file

@ -573,7 +573,8 @@ impl<'a> SemanticBuilder<'a> {
fn reference_identifier(&mut self, ident: &IdentifierReference) { fn reference_identifier(&mut self, ident: &IdentifierReference) {
let flag = self.resolve_reference_usages(); let flag = self.resolve_reference_usages();
let reference = Reference::new(ident.span, ident.name.clone(), self.current_node_id, flag); let reference = Reference::new(ident.span, ident.name.clone(), self.current_node_id, flag);
self.declare_reference(reference); let reference_id = self.declare_reference(reference);
ident.reference_id.set(Some(reference_id));
} }
/// Resolve reference flags for the current ast node. /// Resolve reference flags for the current ast node.

View file

@ -1,12 +1,9 @@
use bitflags::bitflags; use bitflags::bitflags;
use oxc_index::define_index_type;
use oxc_span::{Atom, Span}; use oxc_span::{Atom, Span};
use crate::{symbol::SymbolId, AstNodeId}; use crate::{symbol::SymbolId, AstNodeId};
define_index_type! { pub use oxc_syntax::reference::ReferenceId;
pub struct ReferenceId = u32;
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Reference { pub struct Reference {

View file

@ -4,6 +4,7 @@ pub mod identifier;
pub mod module_record; pub mod module_record;
pub mod operator; pub mod operator;
pub mod precedence; pub mod precedence;
pub mod reference;
pub mod scope; pub mod scope;
pub mod symbol; pub mod symbol;

View file

@ -0,0 +1,5 @@
use oxc_index::define_index_type;
define_index_type! {
pub struct ReferenceId = u32;
}

View file

@ -285,6 +285,9 @@ pub trait Case: Sized + Sync + Send + UnwindSafe {
.with_module_record_builder(true) .with_module_record_builder(true)
.with_check_syntax_error(true) .with_check_syntax_error(true)
.build(program); .build(program);
if let Some(res) = self.check_semantic(&semantic_ret.semantic) {
return res;
}
let errors = parser_ret.errors.into_iter().chain(semantic_ret.errors).collect::<Vec<_>>(); let errors = parser_ret.errors.into_iter().chain(semantic_ret.errors).collect::<Vec<_>>();
let result = if errors.is_empty() { let result = if errors.is_empty() {
@ -356,4 +359,8 @@ pub trait Case: Sized + Sync + Send + UnwindSafe {
} }
Ok(()) Ok(())
} }
fn check_semantic(&self, _semantic: &oxc_semantic::Semantic<'_>) -> Option<TestResult> {
None
}
} }

View file

@ -190,4 +190,53 @@ impl Case for Test262Case {
} }
}; };
} }
fn check_semantic(&self, semantic: &oxc_semantic::Semantic<'_>) -> Option<TestResult> {
if are_all_identifiers_resolved(semantic) {
None
} else {
Some(TestResult::ParseError("Unset symbol / reference".to_string(), true))
}
}
}
fn are_all_identifiers_resolved(semantic: &oxc_semantic::Semantic<'_>) -> bool {
use oxc_ast::{ast, AstKind};
use oxc_semantic::AstNode;
let ast_nodes = semantic.nodes();
let has_non_resolved = ast_nodes.iter().any(|node| {
match node.kind() {
AstKind::BindingIdentifier(id) => {
let mut parents = ast_nodes.iter_parents(node.id()).map(AstNode::kind);
parents.next(); // Exclude BindingIdentifier itself
match parents.next() {
Some(AstKind::Function(func))
if func.r#type == ast::FunctionType::FunctionExpression =>
{
// FIXME: Currently, the name of `FunctionExpression` won't be assigned a `SymbolId`
return false;
}
_ => {}
}
let mut parents = ast_nodes.iter_parents(node.id()).map(AstNode::kind);
parents.next(); // Exclude BindingIdentifier itself
match (parents.next(), parents.next()) {
// FIXME: case like `if (xx) ; else function test() {}`
(Some(AstKind::Function(func)), Some(AstKind::IfStatement(_)))
if func.r#type == ast::FunctionType::FunctionDeclaration =>
{
return false;
}
_ => {}
}
id.symbol_id.get().is_none()
}
AstKind::IdentifierReference(ref_id) => ref_id.reference_id.get().is_none(),
_ => false,
}
});
!has_non_resolved
} }