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_span::{Atom, SourceType, Span};
use oxc_syntax::operator::{
AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator, UpdateOperator,
use oxc_syntax::{
operator::{
AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator, UpdateOperator,
},
reference::ReferenceId,
symbol::SymbolId,
};
#[cfg(feature = "serde")]
use serde::Serialize;
@ -254,21 +258,51 @@ pub struct IdentifierName {
}
/// Identifier Reference
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
pub struct IdentifierReference {
#[cfg_attr(feature = "serde", serde(flatten))]
pub span: Span,
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
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
pub struct BindingIdentifier {
#[cfg_attr(feature = "serde", serde(flatten))]
pub span: Span,
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

View file

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

View file

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

View file

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

View file

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

View file

@ -151,7 +151,8 @@ impl<'a> CoverGrammar<'a, ObjectProperty<'a>> for AssignmentTargetProperty<'a> {
if property.shorthand {
let binding = match property.key {
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()),
};

View file

@ -431,7 +431,7 @@ impl<'a> Parser<'a> {
let span = self.start_span();
let (ident_span, name) = self.parse_identifier_kind(Kind::This);
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);
Ok(self.ast.formal_parameter(
self.end_span(span),

View file

@ -28,6 +28,7 @@ impl<'a> Binder for VariableDeclarator<'a> {
};
self.id.bound_names(&mut |ident| {
let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
ident.symbol_id.set(Some(symbol_id));
if self.kind == VariableDeclarationKind::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) {
let Some(ident) = &self.id else { return };
if !self.modifiers.contains(ModifierKind::Declare) {
builder.declare_symbol(
let symbol_id = builder.declare_symbol(
ident.span,
&ident.name,
SymbolFlags::Class,
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.name,
parent_scope_id,
includes,
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;
self.bound_names(&mut |ident| {
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
if let BindingPatternKind::BindingIdentifier(ident) = &param.kind {
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 {
param.bound_names(&mut |ident| {
builder.declare_symbol(
let symbol_id = builder.declare_symbol(
ident.span,
&ident.name,
SymbolFlags::BlockScopedVariable | SymbolFlags::CatchVariable,
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> {
fn bind(&self, builder: &mut SemanticBuilder) {
self.bound_names(&mut |ident| {
builder.declare_symbol(
let symbol_id = builder.declare_symbol(
ident.span,
&ident.name,
SymbolFlags::ImportBinding,
SymbolFlags::ImportBindingExcludes,
);
ident.symbol_id.set(Some(symbol_id));
});
}
}
impl<'a> Binder for TSTypeAliasDeclaration<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
builder.declare_symbol(
let symbol_id = builder.declare_symbol(
self.id.span,
&self.id.name,
SymbolFlags::TypeAlias,
SymbolFlags::TypeAliasExcludes,
);
self.id.symbol_id.set(Some(symbol_id));
}
}
impl<'a> Binder for TSInterfaceDeclaration<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
builder.declare_symbol(
let symbol_id = builder.declare_symbol(
self.id.span,
&self.id.name,
SymbolFlags::Interface,
SymbolFlags::InterfaceExcludes,
);
self.id.symbol_id.set(Some(symbol_id));
}
}
@ -226,7 +240,8 @@ impl<'a> Binder for TSEnumDeclaration<'a> {
} else {
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> {
fn bind(&self, builder: &mut SemanticBuilder) {
builder.declare_symbol(
let symbol_id = builder.declare_symbol(
self.name.span,
&self.name.name,
SymbolFlags::TypeParameter,
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) {
let flag = self.resolve_reference_usages();
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.

View file

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

View file

@ -4,6 +4,7 @@ pub mod identifier;
pub mod module_record;
pub mod operator;
pub mod precedence;
pub mod reference;
pub mod scope;
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_check_syntax_error(true)
.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 result = if errors.is_empty() {
@ -356,4 +359,8 @@ pub trait Case: Sized + Sync + Send + UnwindSafe {
}
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
}