## 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
Follow-up to #19138 after merge.
This PR fixes panic-safety in `multi_index_vec!` clone and keeps strict lint clean for `oxc_semantic` tests.
### Changes
- Make macro-generated `Clone` panic-safe by cloning row-by-row through `push`.
- Avoids setting `len` before all elements are initialized.
- Prevents dropping uninitialized elements if a field `clone()` panics.
- Add `#[expect(clippy::unused_self)]` to cfg-disabled integration helper methods to keep `cargo clippy -p oxc_semantic --lib --tests -D warnings` green.
## Validation
- `cargo clippy -p oxc_semantic --lib --tests -- -D warnings`
- `just lint`
## AI disclosure
AI-assisted: I used Codex to draft and apply this patch, then ran lint checks and reviewed the result before opening this PR.
## Summary
- Add `SourceType::script()` constructor for creating script source types
- `with_module(bool)` now only sets module mode when `true`, does nothing when `false`
- This aligns with the pattern of other `with_*` methods like `with_script()`, `with_jsx()`, etc.
### Migration
Before:
```rust
// This used to set script mode when is_module is false
let source_type = SourceType::default().with_module(is_module);
```
After:
```rust
// Start from script and upgrade to module when needed
let source_type = SourceType::script().with_module(is_module);
```
🤖 Generated with [Claude Code](https://claude.ai/code)
Remove `Scoping::scope_build_child_ids` and all related APIs to make the semantics cleaner. This could also make it easier for us to sync Scoping data since we don't need to worry about child scope IDs.
This is a breaking change, but it is okay because all use of `scope_build_child_ids` in the Oxc and Rolldown has been removed.
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)
Where a type being returned by a function has lifetimes, specify them with `'_` instead of omitting them entirely. `Cow<str>` -> `Cow<'_, str>`.
This is split out from Rust upgrade PR #12873, so can see the diff in that PR more clearly.
Note: This is very slightly different from what was in #12873. Clippy auto-fix introduced a couple of unnecessary and weird-looking anonymous lifetimes in function params, which this PR skips.
Remove an unnecessary `clone` from `SymbolTester`. Don't need to clone the `Reference` only to pass a reference to it to `filter`. Just use the original `&Reference`.
```js
(function n(n) {})()
```
The function `n` symbol will insert into the function itself scope, which causes param `n` to be considered a redeclaration, but it is not.
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)]`.
The previous implementation is a little weird, I think it should be a shortcut of `bindings.iter`, It is infrequent we need to iter all symbols and know its scope ID. Only two cases in tests, and no use case in `Rolldown`
fixes: #8437
In semantic builder make sure `Program` reference has a lifetime of the
Arena.
---------
Co-authored-by: overlookmotel <theoverlookmotel@gmail.com>
close: #7338close: #7344
The `SymbolFlags::Export` is Initially used to solve `ExportSpecifier` that is not `IdentifierReference` that causes we cannot determine whether a Binding is not used everywhere by `Semantic`.
Since #3820 this problem is solved, so we don't need `SymbolFlags::Export` no longer. Also, removing this can help us easier to pass the `Semantic` check in `Transformer`
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`.
close: #3969close: #2023
We need to add scope according to [this](https://github.com/microsoft/TypeScript/blob/d8086f14b6b97c0df34a0cc2f56d4b5926a0c299/src/compiler/binder.ts#L3883-L3962). There are still some ASTs that need to be added to the scope.
---
Context from @Boshen:
Before this whole journey of fixing symbols and scopes I asked @Dunqing to debug through binder.ts via a debugger to fully understand how it does resolution.
We then agreed to align the implementation so that when a problem occurs, we can debug through both implementations and find where our problem is.
tsc doesn't have a specification, so we need to align with the reference implementation instead.
Reverts oxc-project/oxc#3972
@DonIsaac As it turns out we can't have lifetimes on these structures because semantic data is exposed to downstream users to use freely, detached from the ast and allocator.
`Atom` is just a wrapper around `&str`, so better not to pass `&Atom` to functions, as that's a double-reference. Prefer `Atom` or `&str` instead to avoid indirection.
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.
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