mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
docs(semantic): document SymbolTable (#5998)
Re-creation of #5978, per @overlookmotel's request
This commit is contained in:
parent
02d5637dbc
commit
1abfe8f02c
4 changed files with 151 additions and 29 deletions
|
|
@ -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)]
|
||||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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]
|
||||
|
|
@ -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]
|
||||
|
|
@ -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]
|
||||
|
|
@ -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]
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue