We now include unresolved references in the semantic output
Example after output:
```
⚠ References for symbol `I`
╭─[1:11]
1 │ interface I {
· ┬
· ╰── declared here
2 │ [Symbol.iterator]: number;
╰────
note: This symbol has no references.
⚠ Unresolved ident `Symbol`
╭─[2:6]
1 │ interface I {
2 │ [Symbol.iterator]: number;
· ───┬──
· ╰── referenced here: (ReferenceFlags(ValueAsType))
3 │ [Symbol.iterator]?: number;
· ───┬──
· ╰── referenced here: (ReferenceFlags(ValueAsType))
4 │ }
╰────
```
## Summary
Introduces `oxc_diagnostics::Diagnostics` — a `Vec<OxcDiagnostic>` newtype that knows the difference between errors and warnings — and makes it the standard diagnostics return value across oxc.
- **New type**: `Diagnostics` with `has_errors()`, `has_warnings()`, `errors()`, `warnings()`, plus `Vec`-like ergonomics (Deref, `IntoIterator`, `Extend`, `From`/`Into<Vec>`) so most call sites are unchanged.
- **Return types**: `ParserReturn`, `SemanticBuilderReturn`, `IsolatedDeclarationsReturn`, the React Compiler `TransformResult`/`LintResult`, and `TransformerReturn` now expose `diagnostics: Diagnostics` instead of `errors: Vec<OxcDiagnostic>` (the React Compiler's separate `errors`/`warnings` are merged into one severity-tagged list).
- **Behaviour**: a React Compiler **error** now stops the transform (no output), while **warnings** flow through and the transform continues. `compile()` aborts codegen only when `diagnostics.has_errors()` — warnings alone no longer abort (previously any diagnostic did).
## Breaking change
Consumers reading `*.errors` on these return values must read `*.diagnostics`. React Compiler consumers that read `.errors`/`.warnings` separately should use `diagnostics.has_errors()` / `diagnostics.errors()` / `diagnostics.warnings()`.
Parser/semantic/isolated-declarations fatality is otherwise unchanged. Builds on the React Compiler transform feature (#23201).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## What
Make the full `AstNodes` store **opt-in**, so the compiler pipeline (transformer / codegen / minifier) can run semantic analysis without building it at all.
Previously every `SemanticBuilder` run allocated and populated the full `AstNodes` store, even for consumers that only need scoping + the syntax checker. This separates the node-related builder state into three independent pieces:
- a standalone `NodeId` counter — the source of truth for node ids, valid in either mode;
- an **ancestry stack** — an upward-walk-only stack of the current node's ancestors, the only ancestry the syntax checker and binder actually read;
- the full **`AstNodes`** store — random access to every node, needed by the linter and mangler.
## How
- A new `AstNodeStore` (in `crates/oxc_semantic/src/node/store.rs`) bundles the node-id counter, the current-node cursor/flags, and the storage. The storage is an enum (`AstNodeStoreKind`) with two variants — `Full(AstNodes)` and `Ancestry(AncestryStack)` — so the full store and the ancestry stack are **mutually exclusive by construction**; they can never both be maintained at once.
- The checker and binder read ancestry through an `Ancestry` view (`node/ancestry.rs`) that dispatches between the full store (linter / mangler) and the stack (compiler pipeline). They no longer do random `NodeId` access into the store — all ancestry access is upward-walk-only.
- `SemanticBuilder` gains `with_build_nodes(bool)`. The default is **off** (ancestry stack only). `new_linter()` turns it on (the linter needs the full store); `new_compiler()` leaves it off.
- `CompilerInterface::build_semantic_nodes()` lets `oxc::Compiler` consumers opt back in if their `after_semantic` hook reads `Semantic::nodes`.
## Notes
- Behaviour-neutral: semantic conformance snapshots are unchanged.
- Allocation snapshots updated — the default (ancestry) path allocates slightly less (the growing `Stack` trades a couple of fixed allocations for a few reallocs).
- Builds on the already-merged `SymbolFlags`-based redeclaration checks (#23060) and the `new_linter`/`new_compiler` preset adoption (#23057), which removed the last random node reads from the redeclaration path so the checker works without the store.
Replaces ad-hoc `SemanticBuilder::new().with_*()` chains with the `new_linter()` / `new_compiler()` presets where the preset already expands to the exact same option set, so the config lives in one place.
- linter example + linter benchmark → `new_linter()` (cfg + class table + syntax)
- semantic benchmark, cfg example, integration test util → `new_compiler()` (syntax)
Behavior-neutral — each preset matches the options the callsite set by hand. Carved out of the larger store-optional refactor so it can land independently on `main` first.
## Summary
Adds two named constructor presets to `SemanticBuilder` that bake in the options each use case needs, so the configuration lives in one place instead of being repeated at call sites:
- **`SemanticBuilder::new_compiler()`** — compiler pipeline. Enables syntax-error checking; leaves linter-only analyses (control flow graph, class table) off.
- **`SemanticBuilder::new_linter()`** — linter. Enables syntax-error checking, the control flow graph, and the class table.
The presets are applied at the real pipeline entry points:
- `new_linter()` → the linter service build.
- `new_compiler()` → `oxc::Compiler`, and the napi parser/transform bindings.
Everywhere else keeps `SemanticBuilder::new()` and turns on only the features it needs (mangler, minifier, formatter, transformer plugins, examples, tests, ...). Individual options can still be overridden after a preset (e.g. the linter service overrides `check_syntax_error` with its runtime flag).
Behavior is preserved at every migrated site. Node-store access is intentionally untouched in this PR.
## Verified
- Builds across the affected crates + napi; clippy clean; `oxc_linter` tests pass; full `cargo coverage` with no snapshot changes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
This PR contains the following updates:
| Package | Update | Change |
|---|---|---|
| [rust](https://redirect.github.com/rust-lang/rust) | minor | `1.94.1` → `1.95.0` |
---
### Release Notes
<details>
<summary>rust-lang/rust (rust)</summary>
### [`v1.95.0`](https://redirect.github.com/rust-lang/rust/compare/1.94.1...1.95.0)
[Compare Source](https://redirect.github.com/rust-lang/rust/compare/1.94.1...1.95.0)
</details>
---
### Configuration
📅 **Schedule**: (in timezone Asia/Shanghai)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMjMuOCIsInVwZGF0ZWRJblZlciI6IjQzLjEyMy44IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

While possible with the current example infra, it's hard to understand what exactly points where, by reading the ids, and comparing it vs the ast.
This change makes it incredibly easy to identify what symbol a refernce points to.
part of #13646
## Summary
This PR converts the Control Flow Graph (CFG) functionality in `oxc_semantic` from runtime checks to a compile-time cargo feature, eliminating runtime overhead when CFG is not needed.
## Changes
- Added `cfg` cargo feature to `oxc_semantic/Cargo.toml` with `oxc_cfg` as optional dependency
- Feature-gated all CFG-related code using `#[cfg(feature = "cfg")]`
- Updated `oxc_linter` to enable the `cfg` feature (since it requires CFG)
- Made the `control_flow!` macro work correctly with and without the feature
## Benefits
- **Performance**: Eliminates runtime `Option<CFG>` checks when feature is disabled
- **Binary size**: Smaller binaries for crates that don't need CFG
- **Cleaner API**: CFG methods only available when feature is enabled
## Test Plan
- [x] All existing tests pass with `cargo test -p oxc_semantic --features cfg`
- [x] `oxc_linter` builds and works correctly (it enables the feature)
- [x] Feature is backward compatible - existing code continues to work
🤖 Generated with [Claude Code](https://claude.ai/code)
Replace `#[allow]` with `#[expect]` through the whole codebase.
This also surfaced many places where the `#[allow]` attributes were outdated/pointless and could be removed entirely.
Only places `#[allow]` remains are:
1. In generated code and macros, where lint errors may or may not be triggered depending on the generated code.
2. A few places where clippy malfunctions. These use e.g. `#[allow(unused, clippy::allow_attributes)]`.
This has the benefit of:
* expose dynamic import / import meta info from parser
* 1 less ast shallow in semantic builder
* no ast walk in oxc's module lexer
* some more benefits coming soon
Use an `IndexVec` when storing basic blocks. This makes the link between nodes in `.graph` and elements of `.basic_blocks` more clear. I had to rename `BasicBlockId` to `BlockNodeId` to avoid a name collision. I wasn't sure what else to name the `Idx` type for the basic blocks vec.
Using `FxHashMap` is faster than `HashMap` in many cases, especially for hashing-heavy workloads. This change improves the performance of the linter, prettier, and diagnostics crates by using `FxHashMap` instead of `std::collections::HashMap`.
Realized we can get the source type from the AST.
The next PR will introduce `unambiguous` to `SourceType` and directly set `Program::source_type` to either `script` or `module`.
For maximum backward compatibility, we generate CFG by default.
Note: It can't be done with a simple method since lifetimes make it impossible(at least without unsafe trickery) I've tried to do it without a macro but it was just unintuitive.
This PR aims to provide a more accurate error/finalization flow, I've nuked the old error path approach and rewrote it with more versatility in mind.
We used to visit the finalizer block twice and create 2 sets of AstNodes/Basic Blocks for them, This was there to differentiate between the error path finalizer and success path one. There is no longer a need for having 2 separate sets of nodes to do this differentiation.
As for the error path now we have 2 kinds of them, Everything is attached to an error block - even if it is not in a try-catch statement - this results in a lot of extra edges to keep track of these "Implicit" error blocks but I believe in future it can help us to track cross block error paths, For example, we can dynamically attach and cache the implicit error block of a function to its call site error path (either implicit or explicit).
I've replaced the `BasicBlockElement` with an `Instruction` type which would keep both the instruction kind and its associated AstNodeId.
I also removed the register scheme in the control flow in favor of a simpler approach using explicit enums.
https://github.com/oxc-project/oxc/pull/3381#issuecomment-2126622774
1. add a `test.js` file to the project root:
```js
class A extends B {
constructor() {
try {
super();
} finally {
this.a;
}
}
}
```
2. run:
```bash
$ cargo run -p oxc_semantic --example simple
Compiling oxc_semantic v0.5.0 (/home/tzvipm/src/github.com/tzvipm/oxc/crates/oxc_semantic)
Finished dev [unoptimized + debuginfo] target(s) in 32.07s
Running `target/debug/examples/simple`
Wrote AST to: test.ast.txt
Wrote CFG blocks to: test.cfg.txt
Wrote CFG dot diagram to: test.dot
```
3. resulting graph from .dot file:
