#![expect(clippy::print_stdout)]
use std::{env, path::Path, sync::Arc};
use itertools::Itertools;
use oxc_allocator::Allocator;
use oxc_diagnostics::{GraphicalReportHandler, OxcDiagnostic};
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use oxc_span::{GetSpan, SourceType};
fn main() -> std::io::Result<()> {
let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string());
let show_symbols = env::args().skip(1).any(|arg| arg == "--symbols");
let show_symbol_references = env::args().skip(1).any(|arg| arg == "--symbol-references");
let path = Path::new(&name);
let source_text = Arc::new(std::fs::read_to_string(path)?);
let source_type = SourceType::from_path(path).unwrap();
let allocator = Allocator::default();
let parser_ret = Parser::new(&allocator, &source_text, source_type).parse();
if !parser_ret.errors.is_empty() {
let error_message: String = parser_ret
.errors
.into_iter()
.map(|error| format!("{:?}", error.with_source_code(Arc::clone(&source_text))))
.join("\n");
println!("Parsing failed:\n\n{error_message}");
return Ok(());
}
let program = parser_ret.program;
let semantic = SemanticBuilder::new()
.with_check_syntax_error(true)
.build(&program);
if !semantic.errors.is_empty() {
let error_message: String = semantic
.errors
.into_iter()
.map(|error| format!("{:?}", error.with_source_code(Arc::clone(&source_text))))
.join("\n");
println!("Semantic analysis failed:\n\n{error_message}");
}
if show_symbols {
let scoping = semantic.semantic.scoping();
for symbol_id in scoping.symbol_ids() {
let name = scoping.symbol_name(symbol_id);
let flags = scoping.symbol_flags(symbol_id);
println!("Symbol: {name}, ID: {symbol_id:?}, Flags: {flags:?}");
for reference_id in scoping.get_resolved_reference_ids(symbol_id) {
let reference = scoping.get_reference(*reference_id);
println!(" Reference ID: {reference_id:?}, Flags: {:?}", reference.flags());
}
}
}
if show_symbol_references {
let reporter = GraphicalReportHandler::new();
for sym in semantic.semantic.scoping().symbol_ids() {
let symbol_name = semantic.semantic.scoping().symbol_name(sym);
let declaration_span = semantic.semantic.scoping().symbol_span(sym);
let reference_spans = semantic.semantic.symbol_references(sym).map(|reference| {
(
semantic.semantic.nodes().get_node(reference.node_id()).kind().span(),
reference.flags(),
)
});
let has_zero_references = semantic.semantic.symbol_references(sym).next().is_none();
let mut info = OxcDiagnostic::warn(format!("References for symbol `{symbol_name}`"))
.with_label(declaration_span.primary_label("declared here"))
.and_labels(
reference_spans
.map(|(span, flags)| span.label(format!("referenced here: ({flags:?})"))),
);
if has_zero_references {
info = info.with_note("This symbol has no references.");
}
let info = info.with_source_code(Arc::clone(&source_text));
let mut s = String::new();
reporter.render_report(&mut s, info.as_ref()).unwrap();
println!("{s}");
}
}
Ok(())
}