Function parameters, TypeScript signatures, and function bodies currently share one Oxc semantic scope. A reference in a parameter can therefore bind to a declaration inside the body, even though JavaScript does not make body declarations visible there. Rolldown may then treat a global as local, skip a configured define, or remove live code.
## Example
```js
export function load(value = typeof window !== "undefined") {
var window;
return value;
}
```
The body `var window` is not visible to the default value. Before this change, Oxc resolved `window` in the default value to that body declaration. It should remain a global reference.
The workaround resolves parameter and signature references before visiting the body, while only parameter bindings are present. If a reference is still unresolved, its `lookup_scope_id` advances to the function's parent. Final resolution resumes there, so declarations added while visiting the body are skipped. References created in the body still begin at the function scope and resolve normally.
```text
Before: parameter reference -> completed function scope -> body binding (wrong)
After: parameter reference -> parameter bindings -> parent scopes
body reference -> completed function scope -> parent scopes
```
Parameter decorators already begin in the enclosing class scope, so they are left for normal final resolution. This emulates separate parameter and body scopes until https://github.com/oxc-project/backlog/issues/176 is implemented.
The corrected semantic data also exposes the transformer case covered by #26105.
Verified with semantic and transformer-plugin tests, Clippy, coverage, allocation snapshots, and diff checks. `just ready` completed workspace checks but stopped at a codegen snapshot recorded with Node.js v26.5.0; the local version is v25.8.1.
Closes#20427Closes#22783
Downstream reports:
- https://github.com/rolldown/rolldown/issues/10722
- https://github.com/rolldown/rolldown/issues/10779
AI assistance: OpenAI Codex was used to investigate, implement, and test this change. The contributor is responsible for the submitted change.
## Summary
- report TS1234 for function-local ambient module declarations
- preserve TS1235 for function-local namespace and internal module declarations
- report TS2435 for ambient modules nested in namespaces while allowing module augmentation inside ambient external modules
## Babel Conformance Snapshot Parse Failure
This intentionally introduces one known Babel conformance false positive: `typescript/scope/redeclaration-in-nested-module/input.ts`. The Babel harness supplies `sourceType: "module"`, so Oxc reports TS2435 for its nested ambient module. This affects one unique fixture in both the parser and semantic Babel summaries. This change aligns with TS, and TS expects this error.
## Summary
- Introduces `ArrowFunctionBody` to represent concise expression and block bodies explicitly.
- Removes the `ArrowFunctionExpression::expression` boolean.
- Makes invalid arrow body combinations unrepresentable.
## Breaking Change
The Rust AST changes from:
```rust
pub struct ArrowFunctionExpression<'a> {
pub expression: bool,
pub body: Box<'a, FunctionBody<'a>>,
}
```
to:
```rust
pub enum ArrowFunctionBody<'a> {
FunctionBody(Box<'a, FunctionBody<'a>>) = 64,
INHERIT(Expression<'a>),
}
pub struct ArrowFunctionExpression<'a> {
pub body: ArrowFunctionBody<'a>,
}
```
Concise arrow bodies are now stored directly as expressions instead of synthetic `FunctionBody` and `ExpressionStatement` nodes. Block bodies continue to use `FunctionBody`.
## Why?
The previous representation required consumers to maintain this implicit invariant:
```text
expression == true
→ no directives
→ exactly one statement
→ that statement is an ExpressionStatement
```
Invalid combinations could cause panics, skipped work, or fallback behavior. The enum encodes the grammar directly and makes those invalid states unrepresentable.
Concise expressions also now have `ArrowFunctionExpression` as their direct traversal ancestor, without synthetic `FunctionBody` or `ExpressionStatement` nodes.
## How to migrate
Use the arrow body accessors or match on `ArrowFunctionBody` rather than inspecting `expression` and synthetic statements.
Common field migrations:
| Previous representation | New representation |
| --- | --- |
| `arrow.expression` | `arrow.is_expression()` |
| `arrow.get_expression()` | `arrow.get_expression()` |
| `&arrow.body.statements` | `arrow.get_function_body().map(\|body\| &body.statements)` |
| `&mut arrow.body.statements` | `arrow.get_function_body_mut().map(\|body\| &mut body.statements)` |
| Synthetic expression statement in `arrow.body` | Direct `ArrowFunctionBody` expression variant |
| `arrow.body = function_body` | `arrow.body = ArrowFunctionBody::FunctionBody(function_body)` |
| Expression-to-block conversion | Create a `FunctionBody` containing a return statement |
| Block-to-expression conversion | Assign the expression directly to `arrow.body` |
| Builder `(expression, body)` arguments | Pass a single `ArrowFunctionBody` |
closes https://github.com/oxc-project/backlog/issues/211
## Summary
Correct the CFG generated for `for` statements whose update expression spans multiple basic blocks, such as conditional or logical expressions.
This addresses the malformed CFG underlying #23789.
## Problem
`update_graph_ix` identifies the entry of the update expression. After visiting the expression, `cfg.current_node_ix` identifies its exit block.
Previously, the loop backedge to the test originated from the update entry. This allowed control flow to bypass the update expression and left its exit block disconnected.
### Before
```mermaid
flowchart LR
Test["Loop test"] -->|truthy| Body["Loop body"]
Test -->|falsy| After["After loop"]
Body -->|backedge| Entry["Update entry"]
Entry --> Update["Update expression blocks"]
Update --> Exit["Update exit"]
Entry -->|backedge| Test
```
### After
```mermaid
flowchart LR
Test["Loop test"] -->|truthy| Body["Loop body"]
Test -->|falsy| After["After loop"]
Body -->|backedge| Entry["Update entry"]
Entry --> Update["Update expression blocks"]
Update --> Exit["Update exit"]
Exit -->|backedge| Test
```
## Implementation
Record `cfg.current_node_ix` after visiting the update expression as `after_update_graph_ix`. The backedge to the loop test now originates from that exit block.
The loop body backedge and `continue` target still point to `update_graph_ix`, ensuring the complete update expression runs before the next test.
A dedicated CFG fixture covers a conditional update expression:
```js
forward ? index++ : index--
```
Correct the CFG generated for `while` statements whose test expression creates multiple basic blocks, such as logical or conditional expressions.
Previously, the loop body and exit edges originated from the test expression's entry block. They now originate from its exit block after the complete expression has been evaluated.
## Before
The body and exit edges could bypass the test expression, leaving its exit block disconnected.
```mermaid
flowchart LR
Entry["Test entry"] --> Test["Test expression"]
Test --> Exit["Test exit"]
Entry -->|true| Body["Loop body"]
Entry -->|false| After["After while"]
Body -->|backedge| Entry
```
## After
The complete test expression is evaluated before control branches to the loop body or exit.
```mermaid
flowchart LR
Entry["Test entry"] --> Test["Test expression"]
Test --> Exit["Test exit"]
Exit -->|true| Body["Loop body"]
Exit -->|false| After["After while"]
Body -->|backedge| Entry
```
The implementation records cfg.current_node_ix after visiting the test expression as after_test_graph_ix, then uses that block for the body and exit edges. The loop backedge and continue target remain connected to the test entry so each iteration evaluates the expression again.
## 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.
Encode whether a function binding is a named function expression or an `async`/generator function as two internal `SymbolFlags` bits (`FunctionExpression`, `AsyncOrGeneratorFunction`), set at bind time.
The redeclaration checks now read these flags instead of the AST node:
- `check_redeclaration` checks `FunctionExpression` rather than fetching the declaration node to ask `Function::is_expression`.
- `check_redeclared_function` (Annex B.3.3) checks `AsyncOrGeneratorFunction` on each previous declaration, replacing the build-only `async_or_generator_function_node_ids` Vec and its linear scan.
The transformer keeps the flags accurate when it rewrites functions, via a new `sync_function_symbol_flags` helper applied at the function-rewriting sites in `async_to_generator` and class private-method extraction.
Behaviour-neutral: semantic conformance snapshots are unchanged.
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
- When a TypeScript enum member with no initializer follows one whose initializer could not be evaluated to a constant, the auto-increment value previously fell back to `0`. It now propagates "unresolved" (`None`) like the existing `Some(String) => None` arm, matching tsc 6.0.3 (which errors on this construct and emits `void 0`, never `0`).
- The bug surfaced via rolldown's cross-module enum inliner (rolldown/rolldown#9494). Rolldown reads `Scoping::get_enum_member_value` to populate its `enum_member_value_map`; once the wrong `0` landed in the map, every call-site reference to the affected member was inlined to `0`. After this fix the map omits the entry and rolldown leaves the reference alone — matching tsc's call-site behavior.
- Adopts the existing `prev = Some(Number(-1.0))` sentinel pattern from `oxc_isolated_declarations/src/enum.rs` and `oxc_transformer/src/typescript/enum.rs`, so the first-member-defaults-to-0 case still resolves naturally and the auto-increment branch shrinks to two arms.
## Example
Input:
```ts
import { External } from './external'; // External.X = 0, External.Y = 1
enum Local { A = External.X, B = External.Y, C }
```
`Scoping::get_enum_member_value` before:
- A → None, B → None, C → Some(Number(0.0)) (wrong)
After:
- A → None, B → None, C → None
Covered by new test `crates/oxc_semantic/tests/integration/enum_values.rs::auto_increment_after_unresolvable_fails`; existing `auto_increment` (first member defaults to 0) and `auto_increment_after_string_fails` continue to pass.
AI disclosure: drafted with Claude Code, reviewed manually.
## Summary
Part of https://github.com/oxc-project/oxc/issues/6073 and #11844
- Add `ConstantValue` type in `oxc_syntax` for representing enum member values (number/string)
- Implement enum member value evaluation in `oxc_semantic` supporting numeric auto-increment, string values, binary/unary expressions, cross-member and cross-enum references, template literals, and merged enum declarations
- Store evaluated enum member values in `Scoping` via `EnumData` struct with getter/setter APIs and enum body scope tracking
- All enum evaluation code is gated behind the `enum_eval` Cargo feature (enabled by `oxc_transformer`)
### Feature gate
The `enum_eval` feature controls:
- `ts_enum` module (evaluator + data types)
- `EnumData` field and enum-related methods on `Scoping`
- `oxc_syntax/to_js_string` dependency
Consumers that don't need enum values (linter, language server) pay zero cost.
### Babel alignment
Unary operators on strings follow Babel's JS-coercion semantics (`+"s"` → `"s"`, `-"s"` → `NaN`, `~"s"` → `-1`), which differs from TypeScript (unevaluated). This is documented in the source.
## Test plan
- [x] Integration tests in `crates/oxc_semantic/tests/integration/enum_values.rs`
- [x] Semantic TypeScript conformance snapshot updated
- [x] `cargo check -p oxc_semantic` passes (without feature)
- [x] `cargo check -p oxc_semantic --features enum_eval` passes (with feature)
- [x] `cargo check -p oxc_transformer` passes (enables feature transitively)
## 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
- In JavaScript modules, `import { Foo } from './foo.js'; var Foo;` (and `let`, `const`, `class`, `function`) should report `"Identifier 'Foo' has already been declared"` per the [ES spec](https://tc39.es/ecma262/#sec-imports-static-semantics-early-errors)
- The existing excludes-based conflict detection in the binder doesn't catch this because `Import` is not in the `Value` bitset
- Added a checker function that runs at `Program` leave, scanning root scope bindings for symbols with both `Import` and a value flag, and reports a redeclaration error
- TypeScript is excluded since it has its own declaration merging rules
Closes#15961🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
Fixes#18719.
- In module mode, function declarations are lexically scoped (part of `LexicallyDeclaredNames`), so `function a() {} var a = ''` is a redeclaration error per the ECMAScript spec
- The checker was only reporting the error when the `var` appeared **before** the function (`var a = ''; function a() {}`) but not the other way around, due to AST visit ordering
- `check_variable_declarator_redeclaration` unconditionally skipped the check at `Top | Function` scopes — now it falls through to the redeclaration check when in module mode at the top scope
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## 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.
## Summary
- Move `scope_id: Cell<Option<ScopeId>>` from `TSEnumDeclaration` to `TSEnumBody`
- Since `TSEnumBody` was introduced as a dedicated type in PR #10284, the scope logically belongs on the body rather than on the parent declaration node
Closes#10331
## Test plan
- [x] Unit tests pass (`just test`)
- [x] Conformance tests pass (`just conformance`)
- [x] Updated semantic test snapshots reflect scope now on `TSEnumBody`
🤖 Generated with [Claude Code](https://claude.ai/code)
Add explicit condition basic blocks in the control flow graph for logical
expressions and logical assignment operators. This improves CFG accuracy by
representing condition evaluation as distinct nodes.
Changes:
- Create condition basic blocks before evaluating left operands of logical expressions (&&, ||, ??)
- Record and attach AST nodes to condition blocks for better traceability
- Add edges from preceding blocks to condition blocks
- Apply same treatment to logical assignment operators (&&=, ||=, ??=)
This affects CFG generation for:
- LogicalExpression nodes
- AssignmentExpression nodes with logical operators
Fixes#8365
## Summary
The `with` statement was not creating a scope in the semantic analysis, causing `is_global_reference` to incorrectly return `true` for references inside `with` blocks. This could lead to incorrect minification.
## Problem
According to the ECMAScript specification, `with` statements should create an Object Environment Record that extends the lexical environment. Without proper scope tracking:
- `is_global_reference()` incorrectly returns `true` for identifiers inside `with` blocks
- This can cause minification to incorrectly treat local references as global
- The scope tree doesn't accurately represent the program structure
## Solution
### Changes Made
1. **AST Definition** (`crates/oxc_ast/src/ast/js.rs`):
- Added `#[scope]` attribute to `WithStatement` struct
- Added `scope_id: Cell<Option<ScopeId>>` field
2. **Semantic Builder** (`crates/oxc_semantic/src/builder.rs`):
- Modified `visit_with_statement` to call `enter_scope()` before visiting the body
- Added `leave_scope()` call after visiting the body
- Uses `ScopeFlags::empty()` similar to block statements
3. **Test Coverage** (`crates/oxc_semantic/tests/integration/scopes.rs`):
- Added `test_with_statement` to verify scope creation
- Tests both simple expressions and block statement bodies
- Verifies proper scope tree structure
### Example
```javascript
const foo = {
Object: class {
constructor() { console.log('Constructor') }
}
}
with (foo) {
console.log(new Object()) // should print "Constructor"
}
```
Before this fix, `Object` inside the `with` block would be incorrectly identified as a global reference, potentially causing minification errors.
## Implementation Notes
While this implementation doesn't fully model the complex object environment semantics (where property lookups can dynamically affect identifier resolution at runtime), it ensures:
- References inside `with` blocks are no longer incorrectly marked as global
- The scope tree properly represents the lexical structure
- Provides a foundation for future improvements to object environment handling
## Testing
All existing tests pass, plus the new test verifies:
- `with` statements create scopes
- Scopes are properly nested in the scope tree
- Works correctly with both simple and block statement bodies
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`.
Based on TypeScript's [implementation](https://github.com/microsoft/TypeScript/blob/15392346d05045742e653eab5c87538ff2a3c863/src/compiler/binder.ts#L2384-L2393) to correct `SymbolFlags` of `TSModuleDeclaration`.
The `SymbolFlags::NamespaceModule` and `SymbolFlags::ValueModule` have a significant difference,
`NamespaceModule`: can only be referenced as a type.
`ValueModule`: can only be referenced as a value.
Let's take an example to see:
```ts
namespace NamespaceModule {
export type A = string
}
namespace ValueModule {
export const A = 0;
}
```
The following code is the JS output of the above example.
```js
"use strict";
var ValueModule;
(function (ValueModule) {
ValueModule.A = 0;
})(ValueModule || (ValueModule = {}));
```
Only `ValueModule` will be preserved and transformed.
That means whether a `TSModuleDeclaration` needs to be transformed or removed directly, we can determine by its `SymbolFlags`. We can use it to simplify the current `TSModuleDeclaration` transformation later.
In previous implementations, a symbol was declared for `ModuleDeclaration` but only inserted when `name` was a `BindingIdentifier`. This is incorrect and also causes a potential performance impact. See https://github.com/oxc-project/oxc/blob/bae53b9606b62fce58b4101497849997b0dc07b3/crates/oxc_semantic/src/stats.rs.
So we should only declare a symbol when its name is a `BindingIdentifier` because only `BindingIdentifier` can be inserted with a symbol ID.
Fixes#10087
---
Follow up for #10017 .
This PR eliminates the part where nodes were manually generated for
JS-land AST.
NOTE: This node was originally present, but was removed at #2509 and is
now being reintroduced to get the correct `Span` position for #9705 .
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
```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.
This PR fixes the construction of the cfg within `oxc_semantic`, by marking the `test` of `IfStatement` as unconditionally visited.
Given the following:
```ts
if (foo) {
bar();
} else {
baz();
}
```
Would produce the following graph:
```mermaid
graph TD
0["bb0"]
1["bb1 IfStatement"]
2["bb2 Condition(IdentifierReference(foo))"]
3["bb3 BlockStatement ExpressionStatement"]
4["bb4 BlockStatement ExpressionStatement"]
5["bb5"]
1 -- "Error(Implicit)" --> 0
2 -- "Error(Implicit)" --> 0
3 -- "Error(Implicit)" --> 0
4 -- "Error(Implicit)" --> 0
5 -- "Error(Implicit)" --> 0
1 -- "Normal" --> 2
1 -- "Normal" --> 4
3 -- "Normal" --> 5
2 -- "Jump" --> 3
4 -- "Normal" --> 5
```
After this change, it produces the following graph:
```mermaid
graph TD
0["bb0"]
1["bb1 IfStatement"]
2["bb2 Condition(IdentifierReference(foo))"]
3["bb3 BlockStatement ExpressionStatement"]
4["bb4 BlockStatement ExpressionStatement"]
5["bb5"]
1 -- "Error(Implicit)" --> 0
2 -- "Error(Implicit)" --> 0
3 -- "Error(Implicit)" --> 0
4 -- "Error(Implicit)" --> 0
5 -- "Error(Implicit)" --> 0
1 -- "Normal" --> 2
3 -- "Normal" --> 5
2 -- "Jump" --> 3
2 -- "Jump" --> 4
4 -- "Normal" --> 5
```
Rather than jumping from the if statment (`bb1\nIfStatement`) directly to the consequent (`bb4 BlockStatement ExpressionStatement`), it now unconditionally visits the `test` of the if statement. This can be seen in the after graph as the jump to `bb4 BlockStatement ExpressionStatement"` comes **from** `bb2 Condition(IdentifierReference(foo))` rather than from `bb1 IfStatement`.
As a result of this change, `rules_of_hooks`, does not have false positives when a hook is in a position such as:
```ts
if (useMe) {
```
As the cfg would previously think that `useMe` was called conditionally when in fact it was not
fixes https://github.com/oxc-project/oxc/issues/9795
related #9807
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>