Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 71 additions & 4 deletions crates/oxc_ast/src/ast/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,15 @@ pub struct ArrayPattern<'a> {
pub rest: Option<Box<'a, BindingRestElement<'a>>>,
}

/// A `...rest` binding in an [array](ArrayPattern) or [object](ObjectPattern) destructure.
///
/// ## Examples
/// ```ts
/// const [a, ...rest] = [1, 2, 3];
/// // ^^^^ argument
/// const { x, y, ...others} = foo.bar();
/// // ^^^^^^ argument
/// ```
#[ast(visit)]
#[derive(Debug)]
#[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ContentHash)]
Expand All @@ -1736,7 +1745,41 @@ pub struct BindingRestElement<'a> {
pub argument: BindingPattern<'a>,
}

/// Function Definitions
/// Function Statement or Expression
///
/// Includes generator functions and function-valued class properties.
/// Arrow functions are represented by [`ArrowFunctionExpression`].
///
/// # Examples
/// ```ts
/// // id ___ ____ return_type
/// function foo(a: number): void {
/// // ^^^^^^^^^ params
/// console.log(a);
/// }
/// ```
///
/// ```ts
/// // `async` and `generator` are true
/// async function* foo() {
/// yield 1;
/// }
/// ```
///
/// ```js
/// // function.id is None
/// // use function.r#type to check if a node is a function expression.
/// const foo = function() { }
/// ```
///
/// ```ts
/// // Function overloads will not have a body
/// function add(a: number, b: number): number; // <-- No body
/// function add(a: string, b: string): string; // <-- No body
/// function add(a: any, b: any): any { // <-- Body is between `{}`, inclusive.
/// return a + b;
/// }
/// ```
#[ast(visit)]
#[scope(
// `flags` passed in to visitor via parameter defined by `#[visit(args(flags = ...))]` on parents
Expand All @@ -1751,7 +1794,14 @@ pub struct Function<'a> {
pub r#type: FunctionType,
#[serde(flatten)]
pub span: Span,
/// The function identifier. [`None`] for anonymous function expressions.
pub id: Option<BindingIdentifier<'a>>,
/// Is this a generator function?
///
/// ```ts
/// function* foo() { } // <- generator: true
/// function bar() { } // <- generator: false
/// ```
pub generator: bool,
pub r#async: bool,
pub declare: bool,
Expand All @@ -1761,19 +1811,36 @@ pub struct Function<'a> {
/// The JavaScript specification states that you cannot have a parameter called `this`,
/// and so TypeScript uses that syntax space to let you declare the type for `this` in the function body.
///
/// ```TypeScript
/// ```ts
/// interface DB {
/// filterUsers(filter: (this: User) => boolean): User[];
/// filterUsers(filter: (this: User) => boolean): User[];
/// // ^^^^
/// }
///
/// const db = getDB();
/// const admins = db.filterUsers(function (this: User) {
/// return this.admin;
/// return this.admin;
/// });
/// ```
pub this_param: Option<Box<'a, TSThisParameter<'a>>>,
/// Function parameters.
///
/// Does not include `this` parameters used by some TypeScript functions.
pub params: Box<'a, FormalParameters<'a>>,
/// The TypeScript return type annotation.
pub return_type: Option<Box<'a, TSTypeAnnotation<'a>>>,
/// The function body.
///
/// [`None`] for function declarations, e.g.
/// ```ts
/// // TypeScript function declarations have no body
/// declare function foo(a: number): number;
///
/// function bar(a: number): number; // <- overloads have no body
/// function bar(a: number): number {
/// return a;
/// }
/// ```
pub body: Option<Box<'a, FunctionBody<'a>>>,
#[serde(skip)]
#[clone_in(default)]
Expand Down
50 changes: 25 additions & 25 deletions crates/oxc_ast/src/generated/ast_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -832,15 +832,15 @@ impl<'a> AstBuilder<'a> {
/// ## Parameters
/// - r#type
/// - span: The [`Span`] covering this node
/// - id
/// - generator
/// - id: The function identifier. [`None`] for anonymous function expressions.
/// - generator: Is this a generator function?
/// - r#async
/// - declare
/// - type_parameters
/// - this_param: Declaring `this` in a Function <https://www.typescriptlang.org/docs/handbook/2/functions.html#declaring-this-in-a-function>
/// - params
/// - return_type
/// - body
/// - params: Function parameters.
/// - return_type: The TypeScript return type annotation.
/// - body: The function body.
#[inline]
pub fn expression_function<T1, T2, T3, T4, T5>(
self,
Expand Down Expand Up @@ -4179,15 +4179,15 @@ impl<'a> AstBuilder<'a> {
/// ## Parameters
/// - r#type
/// - span: The [`Span`] covering this node
/// - id
/// - generator
/// - id: The function identifier. [`None`] for anonymous function expressions.
/// - generator: Is this a generator function?
/// - r#async
/// - declare
/// - type_parameters
/// - this_param: Declaring `this` in a Function <https://www.typescriptlang.org/docs/handbook/2/functions.html#declaring-this-in-a-function>
/// - params
/// - return_type
/// - body
/// - params: Function parameters.
/// - return_type: The TypeScript return type annotation.
/// - body: The function body.
#[inline]
pub fn declaration_function<T1, T2, T3, T4, T5>(
self,
Expand Down Expand Up @@ -5719,15 +5719,15 @@ impl<'a> AstBuilder<'a> {
/// ## Parameters
/// - r#type
/// - span: The [`Span`] covering this node
/// - id
/// - generator
/// - id: The function identifier. [`None`] for anonymous function expressions.
/// - generator: Is this a generator function?
/// - r#async
/// - declare
/// - type_parameters
/// - this_param: Declaring `this` in a Function <https://www.typescriptlang.org/docs/handbook/2/functions.html#declaring-this-in-a-function>
/// - params
/// - return_type
/// - body
/// - params: Function parameters.
/// - return_type: The TypeScript return type annotation.
/// - body: The function body.
#[inline]
pub fn function<T1, T2, T3, T4, T5>(
self,
Expand Down Expand Up @@ -5773,15 +5773,15 @@ impl<'a> AstBuilder<'a> {
/// ## Parameters
/// - r#type
/// - span: The [`Span`] covering this node
/// - id
/// - generator
/// - id: The function identifier. [`None`] for anonymous function expressions.
/// - generator: Is this a generator function?
/// - r#async
/// - declare
/// - type_parameters
/// - this_param: Declaring `this` in a Function <https://www.typescriptlang.org/docs/handbook/2/functions.html#declaring-this-in-a-function>
/// - params
/// - return_type
/// - body
/// - params: Function parameters.
/// - return_type: The TypeScript return type annotation.
/// - body: The function body.
#[inline]
pub fn alloc_function<T1, T2, T3, T4, T5>(
self,
Expand Down Expand Up @@ -7677,15 +7677,15 @@ impl<'a> AstBuilder<'a> {
/// ## Parameters
/// - r#type
/// - span: The [`Span`] covering this node
/// - id
/// - generator
/// - id: The function identifier. [`None`] for anonymous function expressions.
/// - generator: Is this a generator function?
/// - r#async
/// - declare
/// - type_parameters
/// - this_param: Declaring `this` in a Function <https://www.typescriptlang.org/docs/handbook/2/functions.html#declaring-this-in-a-function>
/// - params
/// - return_type
/// - body
/// - params: Function parameters.
/// - return_type: The TypeScript return type annotation.
/// - body: The function body.
#[inline]
pub fn export_default_declaration_kind_function<T1, T2, T3, T4, T5>(
self,
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_semantic/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ pub struct AstNodes<'a> {
}

impl<'a> AstNodes<'a> {
/// Iterate over all [`AstNode`]s in this AST.
pub fn iter(&self) -> impl Iterator<Item = &AstNode<'a>> + '_ {
self.nodes.iter()
}
Expand Down
54 changes: 54 additions & 0 deletions crates/oxc_semantic/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,40 @@ pub struct SymbolTable {
}

impl SymbolTable {
/// Returns the number of symbols in this table.
#[inline]
pub fn len(&self) -> usize {
self.spans.len()
}

/// Returns `true` if this table contains no symbols.
#[inline]
pub fn is_empty(&self) -> bool {
self.spans.is_empty()
}

/// Iterate over all symbol IDs in this table.
///
/// Use [`ScopeTree::iter_bindings_in`] to only iterate over symbols declared in a specific
/// scope.
///
/// [`ScopeTree::iter_bindings_in`]: crate::scope::ScopeTree::iter_bindings_in
///
/// ## Example
///
/// ```
/// use oxc_semantic::Semantic;
/// let semantic: Semantic<'_> = parse_and_analyze("./foo.js");
///
/// let classes = semantic
/// .scopes()
/// .symbol_ids()
/// .filter(|symbol_id| {
/// let flags = semantic.symbols().get_flags(*symbol_id);
/// flags.is_class()
/// })
/// .collect::<Vec<_>>();
/// ```
pub fn symbol_ids(&self) -> impl Iterator<Item = SymbolId> + '_ {
self.spans.iter_enumerated().map(|(symbol_id, _)| symbol_id)
}
Expand All @@ -69,11 +93,15 @@ impl SymbolTable {
.find_map(|(symbol, &inner_span)| if inner_span == span { Some(symbol) } else { None })
}

/// Get the [`Span`] of the [`AstNode`] declaring a symbol.
///
/// [`AstNode`]: crate::node::AstNode
#[inline]
pub fn get_span(&self, symbol_id: SymbolId) -> Span {
self.spans[symbol_id]
}

/// Get the identifier name a symbol is bound to.
#[inline]
pub fn get_name(&self, symbol_id: SymbolId) -> &str {
&self.names[symbol_id]
Expand All @@ -84,11 +112,15 @@ impl SymbolTable {
self.names[symbol_id] = name;
}

/// Get the [`SymbolFlags`] for a symbol, which describe how the symbol is declared.
///
/// To find how a symbol is used, use [`SymbolTable::get_resolved_references`].
#[inline]
pub fn get_flags(&self, symbol_id: SymbolId) -> SymbolFlags {
self.flags[symbol_id]
}

/// Get a mutable reference to a symbol's [flags](SymbolFlags).
#[inline]
pub fn get_flags_mut(&mut self, symbol_id: SymbolId) -> &mut SymbolFlags {
&mut self.flags[symbol_id]
Expand Down Expand Up @@ -123,6 +155,16 @@ impl SymbolTable {
self.get_symbol_id_from_span(span).map(|symbol_id| self.get_scope_id(symbol_id))
}

/// Get the ID of the AST node declaring a symbol.
///
/// This node will be a [`VariableDeclaration`], [`Function`], or some other AST node
/// that _has_ a [`BindingIdentifier`] or a [`BindingPattern`]. It will not point to the
/// binding pattern or identifier node itself.
///
/// [`VariableDeclaration`]: oxc_ast::ast::VariableDeclaration
/// [`Function`]: oxc_ast::ast::Function
/// [`BindingIdentifier`]: oxc_ast::ast::BindingIdentifier
/// [`BindingPattern`]: oxc_ast::ast::BindingPattern
#[inline]
pub fn get_declaration(&self, symbol_id: SymbolId) -> NodeId {
self.declarations[symbol_id]
Expand Down Expand Up @@ -158,6 +200,9 @@ impl SymbolTable {
self.references.push(reference)
}

/// Get a resolved or unresolved reference.
///
/// [`ReferenceId`]s can be found in [`IdentifierReference`] and similar nodes.
#[inline]
pub fn get_reference(&self, reference_id: ReferenceId) -> &Reference {
&self.references[reference_id]
Expand All @@ -168,16 +213,25 @@ impl SymbolTable {
&mut self.references[reference_id]
}

/// Returns `true` if the corresponding [`Reference`] is resolved to a symbol.
///
/// When `false`, this could either be a reference to a global value or an identifier that does
/// not exist.
#[inline]
pub fn has_binding(&self, reference_id: ReferenceId) -> bool {
self.references[reference_id].symbol_id().is_some()
}

/// Find [`Reference`] ids resolved to a symbol.
///
/// If you want direct access to a symbol's [`Reference`]s, use
/// [`SymbolTable::get_resolved_references`].
#[inline]
pub fn get_resolved_reference_ids(&self, symbol_id: SymbolId) -> &Vec<ReferenceId> {
&self.resolved_references[symbol_id]
}

/// Find [`Reference`]s resolved to a symbol.
pub fn get_resolved_references(
&self,
symbol_id: SymbolId,
Expand Down