docs(semantic): document SymbolTable (#5998)

Re-creation of #5978, per @overlookmotel's request
This commit is contained in:
DonIsaac 2024-09-23 23:19:41 +00:00
parent 02d5637dbc
commit 1abfe8f02c
4 changed files with 151 additions and 29 deletions

View file

@ -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)]

View file

@ -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,

View file

@ -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()
}

View file

@ -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,