Skip to content

Commit

Permalink
docs(semantic): document AstNode and AstNodes (#5872)
Browse files Browse the repository at this point in the history
Part of #5870
  • Loading branch information
DonIsaac committed Sep 19, 2024
1 parent 83ca7f5 commit 1ccf290
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 10 deletions.
83 changes: 73 additions & 10 deletions crates/oxc_semantic/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,56 @@ impl<'a> AstNode<'a> {
Self { id, kind, scope_id, cfg_id, flags }
}

/// This node's unique identifier.
#[inline]
pub fn id(&self) -> NodeId {
self.id
}

/// ID of the control flow graph node this node is in.
///
/// See [oxc_cfg::ControlFlowGraph] for more information.
#[inline]
pub fn cfg_id(&self) -> BasicBlockId {
self.cfg_id
}

/// Access the underlying struct from [`oxc_ast`].
///
/// # Examples
///
/// ```
/// use oxc_semantic::AstNode;
///
/// fn get_function_name<'a>(node: AstNode<'a>) -> Option<&'a str> {
/// match node.kind() {
/// AstKind::Function(func) => Some(func.name()),
/// _ => None,
/// }
/// }
/// ```
#[inline]
pub fn kind(&self) -> AstKind<'a> {
self.kind
}

/// The scope in which this node was declared.
///
/// It is important to note that this is _not_ the scope created _by_ the
/// node. For example, given a function declaration, this is the scope where
/// the function is declared, not the scope created by its body.
#[inline]
pub fn scope_id(&self) -> ScopeId {
self.scope_id
}

/// Flags providing additional information about the node.
#[inline]
pub fn flags(&self) -> NodeFlags {
self.flags
}

/// Get a mutable reference to this node's flags.
#[inline]
pub fn flags_mut(&mut self) -> &mut NodeFlags {
&mut self.flags
Expand All @@ -80,6 +105,7 @@ pub struct AstNodes<'a> {
/// users should beware.
root: Option<NodeId>,
nodes: IndexVec<NodeId, AstNode<'a>>,
/// `node` -> `parent`
parent_ids: IndexVec<NodeId, Option<NodeId>>,
}

Expand All @@ -88,11 +114,13 @@ impl<'a> AstNodes<'a> {
self.nodes.iter()
}

/// Returns the number of node in this AST.
#[inline]
pub fn len(&self) -> usize {
self.nodes.len()
}

/// Returns `true` if there are no nodes in this AST.
#[inline]
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
Expand All @@ -107,20 +135,37 @@ impl<'a> AstNodes<'a> {
AstNodeParentIter { current_node_id: Some(node_id), nodes: self }
}

/// Access the underlying struct from [`oxc_ast`].
///
/// ## Example
///
/// ```
/// use oxc_semantic::AstNodes;
/// use oxc_ast::AstKind;
///
/// let ast: AstNodes<'_> = get_ast_in_some_way();
/// assert!(matches!(
/// ast.kind(ast.root().unwrap()),
/// AstKind::Program(_)
/// ));
/// ```
#[inline]
pub fn kind(&self, ast_node_id: NodeId) -> AstKind<'a> {
self.nodes[ast_node_id].kind
}

/// Get id of this node's parent.
#[inline]
pub fn parent_id(&self, ast_node_id: NodeId) -> Option<NodeId> {
self.parent_ids[ast_node_id]
}

/// Get the kind of the parent node.
pub fn parent_kind(&self, ast_node_id: NodeId) -> Option<AstKind<'a>> {
self.parent_id(ast_node_id).map(|node_id| self.kind(node_id))
}

/// Get a reference to a node's parent.
pub fn parent_node(&self, ast_node_id: NodeId) -> Option<&AstNode<'a>> {
self.parent_id(ast_node_id).map(|node_id| self.get_node(node_id))
}
Expand All @@ -135,22 +180,34 @@ impl<'a> AstNodes<'a> {
&mut self.nodes[ast_node_id]
}

/// Get the root `NodeId`, It is always pointing to a `Program`.
/// Returns `None` if root node isn't set.
/// Get the root [`NodeId`]. This always points to a [`Program`] node.
///
/// Returns [`None`] if root node isn't set. This will never happen if you
/// are obtaining an [`AstNodes`] that has already been constructed.
///
/// [`Program`]: oxc_ast::ast::Program
#[inline]
pub fn root(&self) -> Option<NodeId> {
self.root
}

/// Get the root node as immutable reference, It is always guaranteed to be a `Program`.
/// Returns `None` if root node isn't set.
/// Get the root node as immutable reference, It is always guaranteed to be a [`Program`].
///
/// Returns [`None`] if root node isn't set. This will never happen if you
/// are obtaining an [`AstNodes`] that has already been constructed.
///
/// [`Program`]: oxc_ast::ast::Program
#[inline]
pub fn root_node(&self) -> Option<&AstNode<'a>> {
self.root().map(|id| self.get_node(id))
}

/// Get the root node as mutable reference, It is always guaranteed to be a `Program`.
/// Returns `None` if root node isn't set.
/// Get the root node as mutable reference, It is always guaranteed to be a [`Program`].
///
/// Returns [`None`] if root node isn't set. This will never happen if you
/// are obtaining an [`AstNodes`] that has already been constructed.
///
/// [`Program`]: oxc_ast::ast::Program
#[inline]
pub fn root_node_mut(&mut self) -> Option<&mut AstNode<'a>> {
self.root().map(|id| self.get_node_mut(id))
Expand All @@ -159,14 +216,19 @@ impl<'a> AstNodes<'a> {
/// Walk up the AST, iterating over each parent node.
///
/// The first node produced by this iterator is the first parent of the node
/// pointed to by `node_id`. The last node will usually be a `Program`.
/// pointed to by `node_id`. The last node will always be a [`Program`].
///
/// [`Program`]: oxc_ast::ast::Program
pub fn ancestors(&self, ast_node_id: NodeId) -> impl Iterator<Item = NodeId> + '_ {
let parent_ids = &self.parent_ids;
std::iter::successors(Some(ast_node_id), |node_id| parent_ids[*node_id])
}

/// Create and add an `AstNode` to the `AstNodes` tree and returns its `NodeId`.
/// Node must not be `Program`. Use `add_program_node` instead.
/// Create and add an [`AstNode`] to the [`AstNodes`] tree and get its [`NodeId`].
/// Node must not be [`Program`]; if it is, use [`add_program_node`] instead.
///
/// [`Program`]: oxc_ast::ast::Program
/// [`add_program_node`]: AstNodes::add_program_node
#[inline]
pub fn add_node(
&mut self,
Expand All @@ -182,7 +244,7 @@ impl<'a> AstNodes<'a> {
ast_node_id
}

/// Create and add an `AstNode` to the `AstNodes` tree and returns its `NodeId`.
/// Create and add an [`AstNode`] to the [`AstNodes`] tree and get its [`NodeId`].
pub fn add_program_node(
&mut self,
kind: AstKind<'a>,
Expand All @@ -197,6 +259,7 @@ impl<'a> AstNodes<'a> {
ast_node_id
}

/// Reserve space for at least `additional` more nodes.
pub fn reserve(&mut self, additional: usize) {
self.nodes.reserve(additional);
self.parent_ids.reserve(additional);
Expand Down
4 changes: 4 additions & 0 deletions crates/oxc_syntax/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use oxc_index::Idx;
#[cfg(feature = "serialize")]
use serde::{Serialize, Serializer};

/// AST Node ID
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct NodeId(NonMaxU32);

Expand Down Expand Up @@ -78,16 +79,19 @@ bitflags! {
}

impl NodeFlags {
/// Returns `true` if this node has a JSDoc comment attached to it.
#[inline]
pub fn has_jsdoc(&self) -> bool {
self.contains(Self::JSDoc)
}

/// Returns `true` if this node is inside a class.
#[inline]
pub fn has_class(&self) -> bool {
self.contains(Self::Class)
}

/// Returns `true` if this function has a yield statement.
#[inline]
pub fn has_yield(&self) -> bool {
self.contains(Self::HasYield)
Expand Down

0 comments on commit 1ccf290

Please sign in to comment.