mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
refactor(traverse): indicate scope entry point with scope(enter_before) attr (#3882)
Improve annotation of AST types for codegen.
Currently:
```rs
#[visited_node(
scope(ScopeFlags::empty()),
enter_scope_before(cases),
)]
pub struct SwitchStatement<'a> {
pub span: Span,
pub discriminant: Expression<'a>,
pub cases: Vec<'a, SwitchCase<'a>>,
pub scope_id: Cell<Option<ScopeId>>,
}
```
After this PR:
```rs
#[visited_node(scope(ScopeFlags::empty()))]
pub struct SwitchStatement<'a> {
pub span: Span,
pub discriminant: Expression<'a>,
#[scope(enter_before)]
pub cases: Vec<'a, SwitchCase<'a>>,
pub scope_id: Cell<Option<ScopeId>>,
}
```
I think this is easier to read.
In order to enable use of `#[scope]` attr, this introduces a dummy `VisitedNode` derive macro. Like the `visited_node` macro, `VisitedNode` derive macro is designed to do very minimal work and have no heavy dependencies, so it should be almost 0 cost in terms of compile time.
This commit is contained in:
parent
24979c98b2
commit
fcd21a6a75
4 changed files with 37 additions and 9 deletions
|
|
@ -1228,7 +1228,7 @@ pub struct WithStatement<'a> {
|
|||
}
|
||||
|
||||
/// Switch Statement
|
||||
#[visited_node(scope(ScopeFlags::empty()), enter_scope_before(cases))]
|
||||
#[visited_node(scope(ScopeFlags::empty()))]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
|
||||
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
|
||||
|
|
@ -1236,6 +1236,7 @@ pub struct SwitchStatement<'a> {
|
|||
#[cfg_attr(feature = "serialize", serde(flatten))]
|
||||
pub span: Span,
|
||||
pub discriminant: Expression<'a>,
|
||||
#[scope(enter_before)]
|
||||
pub cases: Vec<'a, SwitchCase<'a>>,
|
||||
pub scope_id: Cell<Option<ScopeId>>,
|
||||
}
|
||||
|
|
@ -1566,7 +1567,7 @@ pub struct YieldExpression<'a> {
|
|||
}
|
||||
|
||||
/// Class Definitions
|
||||
#[visited_node(scope(ScopeFlags::StrictMode), enter_scope_before(id))]
|
||||
#[visited_node(scope(ScopeFlags::StrictMode))]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
|
||||
#[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
|
||||
|
|
@ -1575,6 +1576,7 @@ pub struct Class<'a> {
|
|||
#[cfg_attr(feature = "serialize", serde(flatten))]
|
||||
pub span: Span,
|
||||
pub decorators: Vec<'a, Decorator<'a>>,
|
||||
#[scope(enter_before)]
|
||||
pub id: Option<BindingIdentifier<'a>>,
|
||||
pub super_class: Option<Expression<'a>>,
|
||||
pub body: Box<'a, ClassBody<'a>>,
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ pub struct TSThisParameter<'a> {
|
|||
/// Enum Declaration
|
||||
///
|
||||
/// `const_opt` enum `BindingIdentifier` { `EnumBody_opt` }
|
||||
#[visited_node(scope(ScopeFlags::empty()), enter_scope_before(members))]
|
||||
#[visited_node(scope(ScopeFlags::empty()))]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
|
||||
#[cfg_attr(feature = "serialize", serde(tag = "type"))]
|
||||
|
|
@ -54,6 +54,7 @@ pub struct TSEnumDeclaration<'a> {
|
|||
#[cfg_attr(feature = "serialize", serde(flatten))]
|
||||
pub span: Span,
|
||||
pub id: BindingIdentifier<'a>,
|
||||
#[scope(enter_before)]
|
||||
pub members: Vec<'a, TSEnumMember<'a>>,
|
||||
pub r#const: bool,
|
||||
pub declare: bool,
|
||||
|
|
@ -784,7 +785,6 @@ pub enum TSTypePredicateName<'a> {
|
|||
|
||||
#[visited_node(
|
||||
scope(ScopeFlags::TsModuleBlock),
|
||||
enter_scope_before(body),
|
||||
strict_if(self.body.as_ref().is_some_and(|body| body.is_strict())),
|
||||
)]
|
||||
#[derive(Debug)]
|
||||
|
|
@ -794,6 +794,7 @@ pub struct TSModuleDeclaration<'a> {
|
|||
#[cfg_attr(feature = "serialize", serde(flatten))]
|
||||
pub span: Span,
|
||||
pub id: TSModuleDeclarationName<'a>,
|
||||
#[scope(enter_before)]
|
||||
pub body: Option<TSModuleDeclarationBody<'a>>,
|
||||
/// The keyword used to define this module declaration
|
||||
/// ```text
|
||||
|
|
|
|||
|
|
@ -1,8 +1,28 @@
|
|||
use proc_macro::TokenStream;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Attach to AST node type (struct or enum), to signal to codegen to create visitor for this type.
|
||||
/// Macro itself does nothing - just passes through the token stream unchanged.
|
||||
///
|
||||
/// Macro does not generate any code - it's purely a means to communicate information to the codegen.
|
||||
///
|
||||
/// Only thing macro does is add `#[derive(VisitedNode)]` to the item.
|
||||
/// Deriving `VisitedNode` does nothing, but supports the `#[scope]` attr on struct fields.
|
||||
/// This is a workaround for Rust not supporting helper attributes for `proc_macro_attribute` macros,
|
||||
/// so we need to use a derive macro to get that support.
|
||||
///
|
||||
/// Use native Rust `TokenStream`, to avoid dependency on slow-compiling crates like `syn` and `quote`.
|
||||
#[proc_macro_attribute]
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn visited_node(_args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
input
|
||||
let mut stream = TokenStream::from_str("#[derive(::oxc_ast_macros::VisitedNode)]").unwrap();
|
||||
stream.extend(input);
|
||||
stream
|
||||
}
|
||||
|
||||
/// Dummy derive macro for a non-existent trait `VisitedNode`.
|
||||
///
|
||||
/// Does not generate any code, only purpose is to allow using `#[scope]` attr in the type def.
|
||||
#[proc_macro_derive(VisitedNode, attributes(scope))]
|
||||
pub fn visited_node_derive(_item: TokenStream) -> TokenStream {
|
||||
TokenStream::new()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,8 +68,11 @@ function parseFile(code, filename, types) {
|
|||
function parseStruct(name, rawName, lines, scopeArgs, filename, startLineIndex) {
|
||||
const fields = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (line.startsWith('#[')) {
|
||||
let line = lines[i];
|
||||
const isScopeEntry = line === '#[scope(enter_before)]';
|
||||
if (isScopeEntry) {
|
||||
line = lines[++i];
|
||||
} else if (line.startsWith('#[')) {
|
||||
while (!lines[i].endsWith(']')) {
|
||||
i++;
|
||||
}
|
||||
|
|
@ -86,6 +89,8 @@ function parseStruct(name, rawName, lines, scopeArgs, filename, startLineIndex)
|
|||
{name: innerTypeName, wrappers} = typeAndWrappers(typeName);
|
||||
|
||||
fields.push({name, typeName, rawName, rawTypeName, innerTypeName, wrappers});
|
||||
|
||||
if (isScopeEntry) scopeArgs.enterScopeBefore = name;
|
||||
}
|
||||
return {kind: 'struct', name, rawName, fields, scopeArgs};
|
||||
}
|
||||
|
|
@ -128,7 +133,7 @@ function parseScopeArgs(argsStr, filename, lineIndex) {
|
|||
while (true) {
|
||||
const [key] = matchAndConsume(/^([a-z_]+)\(/);
|
||||
assert(
|
||||
['scope', 'scope_if', 'strict_if', 'enter_scope_before'].includes(key),
|
||||
['scope', 'scope_if', 'strict_if'].includes(key),
|
||||
`Unexpected visited_node macro arg: ${key}`
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue