Consider the following code:
```tsx
import { FC } from 'react'
import { SvgIcon } from '@mui/material'
const StyledIcon = <SvgIcon sx={{ padding: 1, color: '#ff0000' }} />
// reported violation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
export const MyComponent: FC = () => {
return (
<div>
{StyledIcon}
{/* ... */}
</div>
)
}
```
This should not be a violation since the JSX is pre-computed and re-used, which
does not break React's `Object.is()` checks.
related: #4394
The `UnresolvedReferencesStack` is only used for resolving references, and the `Atom` clone is cheap, So we can safely change `CompactStr`to `Atom`
`SemanticBuilder` keeps a stack of hashmaps to track unresolved references. This stack only grows, never shrinks. Use that invariant to remove bounds checks.
Similar to #4361.
`ScopeTree::add_scope` had a branch specifically to handle `Program`. Remove that by inlining the special logic for `Program` into `visit_program`.
Probably won't have much effect on benchmarks as the branch is easy to predict, but still removes a few instructions and makes `add_scope` easier for compiler to inline.
`SemanticBuilder::enter_scope` contained multiple checks purely to handle when called for `Program` scope (where scope has no parent). This is a very uncommon case as `Program` is visited only once, but these checks run for every scope.
So don't call `enter_scope` from `visit_program`, and inline the special logic for root scope there instead, to simplify `enter_scope`.
A branch in `leave_scope` also gets more predictable (always taken).
`AstNodes::add_node` had a branch specifically to handle `Program`. Remove that by inlining the special logic for `Program` into `visit_program`.
Probably won't have much effect on benchmarks as the branch is easy to predict, but still removes a few instructions and makes `add_node` easier for compiler to inline.
```ts
type A = any;
const B = 0;
export { A, B }
^^^^^^^^ ExportSpecifiers
export { A }
^^^^^ type-only ExportSpecifiers
```
non-type-only `ExportSpecifier` can reference value and type symbols. but currently, `IdentifierReference` in ExportSpecifier only has a `ReferenceFlags::Read`
We have a strange workaround for `visit_function` where we pass in `ScopeFlags`, to support creating the scope inside `Function`, but setting different flags for `MethodDefinition`s.
Previously `visit_function` took `Option<ScopeFlags>` and then did `flags.unwrap_or(ScopeFlags::empty()) | ScopeFlags::Function` to it. Personally, I found this confusing. When I was looking at `MethodDefinition`, I was wondering "It's a function, why doesn't it set Function flag too?"
This changes makes it more explicit and clear what `ScopeFlags` everything has.
Instead of calling `bind_function_or_class_expression` for every scope, and then branching on the AST node kind, insert the relevant code into the visitors for functions and classes. This reduces work on all other kinds of scopes e.g. block statements.
Previously:
```rs
let ix = control_flow!(|self, cfg| cfg.current_node_ix);
```
after this PR:
```rs
let ix = control_flow!(self, |cfg| cfg.current_node_ix);
```
It expands to:
```rs
let ix = if let Some(ref mut cfg) = self.cfg {
cfg.current_node_ix
} else {
Default::default()
};
```
So rationale for this change is that it makes it clearer that `self` is passed *in* and `cfg` comes *out* into the "closure".
Control flow graph builder records AST node IDs in a temp structure `ast_nodes_records`. Only the first AST node ID inserted into the `Vec` is ever used, so turn it into just a single value.
Using `AstNodeId::dummy()` as sentinel for "not set" instead of using `Option<AstNodeId>` to reduce size of the records stack. `Option<AstNodeId>` is 16 bytes.
```ts
export default class Binding {}
// ^^^^^^^ SymbolFlags::Export
export default function Binding () {}
// ^^^^^^^ SymbolFlags::Export
// No binding, so we should not have SymbolFlags::Export
export default function() {}
export default class {}
```
related: https://github.com/oxc-project/oxc/issues/4142#issuecomment-2219125356
Although we called `enter_node(Class)`, that doesn't mean we're in the `class` scope. It causes our must to visit decorators before `enter_node`.
Let's look at this case. It causes a syntax error if we don't visit decorators before `enter_node`
```js
// This file was procedurally generated from the following sources:
// - src/decorator/decorator-call-expr-identifier-reference-yield.case
// - src/decorator/syntax/valid/cls-expr-decorators-valid-syntax.template
/*---
description: Decorator @ DecoratorCallExpression (Valid syntax for decorator on class expression)
esid: prod-ClassExpression
features: [class, decorators]
flags: [generated, noStrict]
info: |
ClassExpression[Yield, Await] :
DecoratorList[?Yield, ?Await]opt class BindingIdentifier[?Yield, ?Await]opt ClassTail[?Yield, ?Await]
DecoratorList[Yield, Await] :
DecoratorList[?Yield, ?Await]opt Decorator[?Yield, ?Await]
Decorator[Yield, Await] :
@ DecoratorMemberExpression[?Yield, ?Await]
@ DecoratorParenthesizedExpression[?Yield, ?Await]
@ DecoratorCallExpression[?Yield, ?Await]
...
DecoratorCallExpression[Yield, Await] :
DecoratorMemberExpression[?Yield, ?Await] Arguments[?Yield, ?Await]
DecoratorMemberExpression[Yield, Await] :
IdentifierReference[?Yield, ?Await]
DecoratorMemberExpression[?Yield, ?Await] . IdentifierName
DecoratorMemberExpression[?Yield, ?Await] . PrivateIdentifier
IdentifierReference[Yield, Await] :
[~Yield] yield
...
---*/
function decorator() {
return () => {};
}
var yield = decorator;
var C = @yield() class {};
```
Errors:
```shell
× The keyword 'yield' is reserved
╭─[language/statements/class/decorator/syntax/valid/decorator-call-expr-identifier-reference-yield.js:45:2]
44 │
45 │ @yield() class C {}
· ─────
╰────
```
The changed code makes more sense. Only if we call `enter_scope` for class, the flags will contain `StrictMode`. Also, we can get the exact `flags` of the `scope` in the `class` at the transformer
For example:
```
class A {
B() {
// Before: flags is `Function`
// After: flags is `Function | StrictMode`
}
}
```
The regression tests will be fixed in follow-up PRs
```ts
class Klass <T> extends Root <R> {}
// ^^^^^ ^^^ ^^^^ ^^^ ^^
// id type_paramter super_class super_type_parameters body
```
I reorder fields according to the order above
The class scope is not defined in the spec. But we need to create a scope for `class` to store `TypeParamters`
Semantic resolves references when exiting `FormalParameters` to ensure references in param initializers don't get bound to bindings inside the function body.
However, it shouldn't do this for `FormalParameters` in TS types e.g. `TSTypeAnnotation` because they don't have their own scopes.
I'm going to be AFK today(till about 9 PM UTC). Meanwhile, I Didn't want to be a blocker so here we go.
It would fix the #4200 merge if you guys find it in the correct order otherwise feel free to close it.
Reduce memory copies when resolving references in `Semantic`.
If parent scope has no unresolved references for `name`, there is no need to generate a new `Vec<ReferenceId>` for `name` and copy in contents from current scope. Just move the existing `Vec` to the parent.
Closes#4169.
Reduce allocations while building `Semantic` by recycling hash maps used for tracking unresolved references, rather than creating a new one for every scope.