Files
oxc-project__oxc/crates/oxc_semantic/examples/semantic.rs
Boshen 030857b509 refactor(diagnostics): update oxc-miette to v4 (#25395)
Remove the miette `Report` wrapper and update oxc-miette to v4's narrowed diagnostic and source protocol.

AI-assisted by OpenAI Codex.
2026-08-10 07:48:02 +00:00

146 lines
5.5 KiB
Rust

#![expect(clippy::print_stdout)]
//! # Semantic Analysis Example
//!
//! This example demonstrates how to use Oxc's semantic analyzer to perform symbol analysis
//! and scope resolution on JavaScript and TypeScript code.
//!
//! ## Usage
//!
//! Create a `test.js` file and run:
//! ```bash
//! cargo run -p oxc_semantic --example semantic [filename] [--symbols] [--symbol-references]
//! ```
//!
//! ## Options
//!
//! - `--symbols`: Display symbol table and reference information
//! - `--symbol-references`: Display detailed reference information for each symbol
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};
// Instruction:
// create a `test.js`,
// run `cargo run -p oxc_semantic --example semantic`
// or `just watch "run -p oxc_semantic --example semantic"`
/// Perform semantic analysis on a JavaScript or TypeScript file
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();
// Memory arena where Semantic and Parser allocate objects
let allocator = Allocator::default();
// Parse the source text into an AST
let parser_ret = Parser::new(&allocator, &source_text, source_type).parse();
if !parser_ret.diagnostics.is_empty() {
let error_message: String = parser_ret
.diagnostics
.into_iter()
.map(|error| error.render_with_source_code(Arc::clone(&source_text)))
.join("\n");
println!("Parsing failed:\n\n{error_message}");
return Ok(());
}
let program = parser_ret.program;
// Compiler-pipeline defaults, plus the full `AstNodes` store since this
// example reads `semantic.nodes()`.
let semantic = SemanticBuilder::new_compiler().with_build_nodes(true).build(&program);
// Report semantic analysis errors
if !semantic.diagnostics.is_empty() {
let error_message: String = semantic
.diagnostics
.into_iter()
.map(|error| error.render_with_source_code(Arc::clone(&source_text)))
.join("\n");
println!("Semantic analysis failed:\n\n{error_message}");
}
// Display symbol information if requested
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}");
}
for (ident, references) in semantic.semantic.scoping().root_unresolved_references() {
let info = OxcDiagnostic::warn(format!("Unresolved ident `{ident}`")).and_labels(
references
.iter()
.map(|reference_id| semantic.semantic.scoping().get_reference(*reference_id))
.map(|reference| {
semantic
.semantic
.nodes()
.get_node(reference.node_id())
.span()
.label(format!("referenced here: ({:?})", reference.flags()))
}),
);
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(())
}