mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
parent
fd3fa6ca84
commit
e7c2313817
14 changed files with 145 additions and 30 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)?
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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()),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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) = ¶m.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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
5
crates/oxc_syntax/src/reference.rs
Normal file
5
crates/oxc_syntax/src/reference.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
use oxc_index::define_index_type;
|
||||
|
||||
define_index_type! {
|
||||
pub struct ReferenceId = u32;
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue