refactor(semantic): add SemanticIds to transformer checker (#5048)

Transformer checker use `SemanticIds` to store collected IDs. `SemanticIds` only contains the IDs, without the `Vec` of errors which is only used temporarily.
This commit is contained in:
overlookmotel 2024-08-21 15:49:07 +00:00
parent c1da5741f4
commit 4e1f4abf89
2 changed files with 53 additions and 51 deletions

View file

@ -103,7 +103,7 @@ use crate::{ScopeTree, SemanticBuilder, SymbolTable};
#[derive(Default)] #[derive(Default)]
pub struct PostTransformChecker { pub struct PostTransformChecker {
errors: Vec<OxcDiagnostic>, errors: Vec<OxcDiagnostic>,
collector_transformer: SemanticCollector, ids_transformer: SemanticIds,
} }
impl PostTransformChecker { impl PostTransformChecker {
@ -114,8 +114,8 @@ impl PostTransformChecker {
program: &Program<'_>, program: &Program<'_>,
) -> Option<Vec<OxcDiagnostic>> { ) -> Option<Vec<OxcDiagnostic>> {
// Collect `ScopeId`s, `SymbolId`s and `ReferenceId`s from AST after transformer // Collect `ScopeId`s, `SymbolId`s and `ReferenceId`s from AST after transformer
self.collector_transformer = SemanticCollector::default(); self.ids_transformer = SemanticIds::default();
if let Some(errors) = self.collector_transformer.check(program) { if let Some(errors) = self.ids_transformer.check(program) {
self.errors.push(OxcDiagnostic::error("Semantic Collector failed after transform")); self.errors.push(OxcDiagnostic::error("Semantic Collector failed after transform"));
self.errors.extend(errors); self.errors.extend(errors);
return Some(mem::take(&mut self.errors)); return Some(mem::take(&mut self.errors));
@ -132,8 +132,8 @@ impl PostTransformChecker {
.semantic .semantic
.into_symbol_table_and_scope_tree(); .into_symbol_table_and_scope_tree();
let mut collector_rebuild = SemanticCollector::default(); let mut ids_rebuild = SemanticIds::default();
if let Some(errors) = collector_rebuild.check(&program) { if let Some(errors) = ids_rebuild.check(&program) {
self.errors.push(OxcDiagnostic::error("Semantic Collector failed after rebuild")); self.errors.push(OxcDiagnostic::error("Semantic Collector failed after rebuild"));
self.errors.extend(errors); self.errors.extend(errors);
return Some(mem::take(&mut self.errors)); return Some(mem::take(&mut self.errors));
@ -142,15 +142,10 @@ impl PostTransformChecker {
let errors_count = self.errors.len(); let errors_count = self.errors.len();
// Compare post-transform semantic data to semantic data from fresh semantic analysis // Compare post-transform semantic data to semantic data from fresh semantic analysis
self.check_bindings(scopes_transformer, &scopes_rebuild, &collector_rebuild); self.check_bindings(scopes_transformer, &scopes_rebuild, &ids_rebuild);
self.check_symbols( self.check_symbols(symbols_transformer, &symbols_rebuild, &scopes_rebuild, &ids_rebuild);
symbols_transformer, self.check_references(symbols_transformer, &symbols_rebuild, &ids_rebuild);
&symbols_rebuild,
&scopes_rebuild,
&collector_rebuild,
);
self.check_references(symbols_transformer, &symbols_rebuild, &collector_rebuild);
(errors_count != self.errors.len()).then(|| mem::take(&mut self.errors)) (errors_count != self.errors.len()).then(|| mem::take(&mut self.errors))
} }
@ -159,16 +154,16 @@ impl PostTransformChecker {
&mut self, &mut self,
scopes_transformer: &ScopeTree, scopes_transformer: &ScopeTree,
scopes_rebuild: &ScopeTree, scopes_rebuild: &ScopeTree,
collector_rebuild: &SemanticCollector, ids_rebuild: &SemanticIds,
) { ) {
if self.collector_transformer.scope_ids.len() != collector_rebuild.scope_ids.len() { if self.ids_transformer.scope_ids.len() != ids_rebuild.scope_ids.len() {
self.errors.push(OxcDiagnostic::error("Scopes mismatch after transform")); self.errors.push(OxcDiagnostic::error("Scopes mismatch after transform"));
return; return;
} }
// Check whether bindings are the same for scopes in the same visitation order. // Check whether bindings are the same for scopes in the same visitation order.
for (&scope_id_transformer, &scope_id_rebuild) in for (&scope_id_transformer, &scope_id_rebuild) in
self.collector_transformer.scope_ids.iter().zip(collector_rebuild.scope_ids.iter()) self.ids_transformer.scope_ids.iter().zip(ids_rebuild.scope_ids.iter())
{ {
fn get_sorted_bindings(scopes: &ScopeTree, scope_id: ScopeId) -> Vec<CompactStr> { fn get_sorted_bindings(scopes: &ScopeTree, scope_id: ScopeId) -> Vec<CompactStr> {
let mut bindings = let mut bindings =
@ -227,10 +222,10 @@ rebuilt : {result_rebuild}
symbols_transformer: &SymbolTable, symbols_transformer: &SymbolTable,
symbols_rebuild: &SymbolTable, symbols_rebuild: &SymbolTable,
scopes_rebuild: &ScopeTree, scopes_rebuild: &ScopeTree,
collector_rebuild: &SemanticCollector, ids_rebuild: &SemanticIds,
) { ) {
// Check whether symbols are valid // Check whether symbols are valid
for symbol_id in collector_rebuild.symbol_ids.iter().copied() { for symbol_id in ids_rebuild.symbol_ids.iter().copied() {
if symbols_rebuild.get_flags(symbol_id).is_empty() { if symbols_rebuild.get_flags(symbol_id).is_empty() {
let name = symbols_rebuild.get_name(symbol_id); let name = symbols_rebuild.get_name(symbol_id);
self.errors.push(OxcDiagnostic::error(format!( self.errors.push(OxcDiagnostic::error(format!(
@ -244,14 +239,14 @@ rebuilt : {result_rebuild}
} }
} }
if self.collector_transformer.symbol_ids.len() != collector_rebuild.symbol_ids.len() { if self.ids_transformer.symbol_ids.len() != ids_rebuild.symbol_ids.len() {
self.errors.push(OxcDiagnostic::error("Symbols mismatch after transform")); self.errors.push(OxcDiagnostic::error("Symbols mismatch after transform"));
return; return;
} }
// Check whether symbols match // Check whether symbols match
for (symbol_id_transformer, symbol_id_rebuild) in for (symbol_id_transformer, symbol_id_rebuild) in
self.collector_transformer.symbol_ids.iter().zip(collector_rebuild.symbol_ids.iter()) self.ids_transformer.symbol_ids.iter().zip(ids_rebuild.symbol_ids.iter())
{ {
let symbol_name_transformer = &symbols_transformer.names[*symbol_id_transformer]; let symbol_name_transformer = &symbols_transformer.names[*symbol_id_transformer];
let symbol_name_rebuild = &symbols_rebuild.names[*symbol_id_rebuild]; let symbol_name_rebuild = &symbols_rebuild.names[*symbol_id_rebuild];
@ -272,10 +267,10 @@ rebuilt : {symbol_id_rebuild:?}: {symbol_name_rebuild:?}
&mut self, &mut self,
symbols_transformer: &SymbolTable, symbols_transformer: &SymbolTable,
symbols_rebuild: &SymbolTable, symbols_rebuild: &SymbolTable,
collector_rebuild: &SemanticCollector, ids_rebuild: &SemanticIds,
) { ) {
// Check whether references are valid // Check whether references are valid
for reference_id in collector_rebuild.reference_ids.iter().copied() { for reference_id in ids_rebuild.reference_ids.iter().copied() {
let reference = symbols_rebuild.get_reference(reference_id); let reference = symbols_rebuild.get_reference(reference_id);
if reference.flags().is_empty() { if reference.flags().is_empty() {
self.errors.push(OxcDiagnostic::error(format!( self.errors.push(OxcDiagnostic::error(format!(
@ -284,17 +279,14 @@ rebuilt : {symbol_id_rebuild:?}: {symbol_name_rebuild:?}
} }
} }
if self.collector_transformer.reference_ids.len() != collector_rebuild.reference_ids.len() { if self.ids_transformer.reference_ids.len() != ids_rebuild.reference_ids.len() {
self.errors.push(OxcDiagnostic::error("ReferenceId mismatch after transform")); self.errors.push(OxcDiagnostic::error("ReferenceId mismatch after transform"));
return; return;
} }
// Check whether symbols match // Check whether symbols match
for (reference_id_transformer, reference_id_rebuild) in self for (reference_id_transformer, reference_id_rebuild) in
.collector_transformer self.ids_transformer.reference_ids.iter().zip(ids_rebuild.reference_ids.iter())
.reference_ids
.iter()
.zip(collector_rebuild.reference_ids.iter())
{ {
let symbol_id_transformer = let symbol_id_transformer =
symbols_transformer.references[*reference_id_transformer].symbol_id(); symbols_transformer.references[*reference_id_transformer].symbol_id();
@ -316,26 +308,50 @@ rebuilt : {reference_id_rebuild:?}: {symbol_name_rebuild:?}
} }
} }
/// Collector of `ScopeId`s, `SymbolId`s and `ReferenceId`s from an AST. /// Collection of `ScopeId`s, `SymbolId`s and `ReferenceId`s from an AST.
/// ///
/// `scope_ids`, `symbol_ids` and `reference_ids` lists are filled in visitation order. /// `scope_ids`, `symbol_ids` and `reference_ids` lists are filled in visitation order.
#[derive(Default)] #[derive(Default)]
pub struct SemanticCollector { pub struct SemanticIds {
scope_ids: Vec<Option<ScopeId>>, scope_ids: Vec<Option<ScopeId>>,
symbol_ids: Vec<SymbolId>, symbol_ids: Vec<SymbolId>,
reference_ids: Vec<ReferenceId>, reference_ids: Vec<ReferenceId>,
}
impl SemanticIds {
/// Collect IDs and check for errors
pub fn check(&mut self, program: &Program<'_>) -> Option<Vec<OxcDiagnostic>> {
if program.source_type.is_typescript_definition() {
return None;
}
let mut collector = SemanticIdsCollector::new(self);
collector.visit_program(program);
let errors = collector.errors;
(!errors.is_empty()).then_some(errors)
}
}
struct SemanticIdsCollector<'c> {
ids: &'c mut SemanticIds,
errors: Vec<OxcDiagnostic>, errors: Vec<OxcDiagnostic>,
} }
impl<'a> Visit<'a> for SemanticCollector { impl<'c> SemanticIdsCollector<'c> {
fn new(ids: &'c mut SemanticIds) -> Self {
Self { ids, errors: vec![] }
}
}
impl<'a, 'c> Visit<'a> for SemanticIdsCollector<'c> {
fn enter_scope(&mut self, _flags: ScopeFlags, scope_id: &Cell<Option<ScopeId>>) { fn enter_scope(&mut self, _flags: ScopeFlags, scope_id: &Cell<Option<ScopeId>>) {
self.scope_ids.push(scope_id.get()); self.ids.scope_ids.push(scope_id.get());
} }
fn visit_identifier_reference(&mut self, ident: &IdentifierReference<'a>) { fn visit_identifier_reference(&mut self, ident: &IdentifierReference<'a>) {
if let Some(reference_id) = ident.reference_id.get() { if let Some(reference_id) = ident.reference_id.get() {
self.reference_ids.push(reference_id); self.ids.reference_ids.push(reference_id);
} else { } else {
let message = format!("Missing ReferenceId: {}", ident.name); let message = format!("Missing ReferenceId: {}", ident.name);
self.errors.push(OxcDiagnostic::error(message).with_label(ident.span)); self.errors.push(OxcDiagnostic::error(message).with_label(ident.span));
@ -344,7 +360,7 @@ impl<'a> Visit<'a> for SemanticCollector {
fn visit_binding_identifier(&mut self, ident: &BindingIdentifier<'a>) { fn visit_binding_identifier(&mut self, ident: &BindingIdentifier<'a>) {
if let Some(symbol_id) = ident.symbol_id.get() { if let Some(symbol_id) = ident.symbol_id.get() {
self.symbol_ids.push(symbol_id); self.ids.symbol_ids.push(symbol_id);
} else { } else {
let message = format!("Missing SymbolId: {}", ident.name); let message = format!("Missing SymbolId: {}", ident.name);
self.errors.push(OxcDiagnostic::error(message).with_label(ident.span)); self.errors.push(OxcDiagnostic::error(message).with_label(ident.span));
@ -385,18 +401,3 @@ impl<'a> Visit<'a> for SemanticCollector {
/* noop */ /* noop */
} }
} }
impl SemanticCollector {
pub fn check(&mut self, program: &Program<'_>) -> Option<Vec<OxcDiagnostic>> {
if program.source_type.is_typescript_definition() {
return None;
}
self.check_ast(program)
}
fn check_ast(&mut self, program: &Program<'_>) -> Option<Vec<OxcDiagnostic>> {
self.visit_program(program);
(!self.errors.is_empty()).then(|| mem::take(&mut self.errors))
}
}

View file

@ -9,7 +9,7 @@ use oxc::diagnostics::OxcDiagnostic;
use oxc::minifier::CompressOptions; use oxc::minifier::CompressOptions;
use oxc::parser::{ParseOptions, ParserReturn}; use oxc::parser::{ParseOptions, ParserReturn};
use oxc::semantic::{ use oxc::semantic::{
post_transform_checker::{PostTransformChecker, SemanticCollector}, post_transform_checker::{PostTransformChecker, SemanticIds},
SemanticBuilderReturn, SemanticBuilderReturn,
}; };
use oxc::span::{SourceType, Span}; use oxc::span::{SourceType, Span};
@ -78,7 +78,8 @@ impl CompilerInterface for Driver {
_semantic_return: &mut SemanticBuilderReturn, _semantic_return: &mut SemanticBuilderReturn,
) -> ControlFlow<()> { ) -> ControlFlow<()> {
if self.check_semantic { if self.check_semantic {
if let Some(errors) = SemanticCollector::default().check(program) { let mut ids = SemanticIds::default();
if let Some(errors) = ids.check(program) {
self.errors.extend(errors); self.errors.extend(errors);
return ControlFlow::Break(()); return ControlFlow::Break(());
} }