Repository: astral-sh/ruff
File: crates/ty_python_semantic/src/ast_node_ref.rs (Rust)

Code before:
        debug_assert_eq!(module_ref.get_by_index(index).try_into().ok(), Some(node));

        Self {
            index,
            #[cfg(debug_assertions)]
            kind: AnyNodeRef::from(node).kind(),
            #[cfg(debug_assertions)]
            range: node.range(),
            _node: PhantomData,
        }
    }

    /// Returns a reference to the wrapped node.
    ///
    /// This method may panic or produce unspecified results if the provided module is from a
    /// different file or Salsa revision than the module to which the node belongs.
    #[track_caller]
    pub fn node<'ast>(&self, module_ref: &'ast ParsedModuleRef) -> &'ast T {
        // The user guarantees that the module is from the same file and Salsa
        // revision, so the file contents cannot have changed.
        module_ref
            .get_by_index(self.index)
            .try_into()
            .ok()
            .expect("AST indices should never change within the same revision")
    }
}

#[expect(unsafe_code)]
unsafe impl<T> salsa::Update for AstNodeRef<T> {
    unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
        let old_ref = unsafe { &mut (*old_pointer) };

        // The equality of an `AstNodeRef` depends on both the module address and the node index,
        // but the former is not stored in release builds to save memory. As such, AST nodes
        // are always considered change when the AST is reparsed, which is acceptable because
        // any change to the AST is likely to invalidate most node indices anyways.
        *old_ref = new_value;
        true
    }
}

impl<T> get_size2::GetSize for AstNodeRef<T> {}

#[allow(clippy::missing_fields_in_debug)]
impl<T> Debug for AstNodeRef<T>
where
    T: Debug,
    for<'ast> &'ast T: TryFrom<AnyRootNodeRef<'ast>>,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

Review comment (refactor):
I had to remove this assertion because the AST can now be collected so that it has a different module ref when dereferencing the node

Code after:
{
    /// Creates a new `AstNodeRef` that references `node`.
    ///
    /// This method may panic or produce unspecified results if the provided module is from a
    /// different file or Salsa revision than the module to which the node belongs.
    pub(super) fn new(module_ref: &ParsedModuleRef, node: &T) -> Self {
        let index = node.node_index().load();
        debug_assert_eq!(module_ref.get_by_index(index).try_into().ok(), Some(node));

        Self {
            index,
            #[cfg(debug_assertions)]
            file: module_ref.module().file(),
            #[cfg(debug_assertions)]
            kind: AnyNodeRef::from(node).kind(),
            #[cfg(debug_assertions)]
            range: node.range(),
            _node: PhantomData,
        }
    }

    /// Returns a reference to the wrapped node.
    ///
    /// This method may panic or produce unspecified results if the provided module is from a
    /// different file or Salsa revision than the module to which the node belongs.
    #[track_caller]
    pub fn node<'ast>(&self, module_ref: &'ast ParsedModuleRef) -> &'ast T {
        #[cfg(debug_assertions)]
        assert_eq!(module_ref.module().file(), self.file);
        // The user guarantees that the module is from the same file and Salsa
        // revision, so the file contents cannot have changed.
        module_ref
            .get_by_index(self.index)
            .try_into()
            .ok()
            .expect("AST indices should never change within the same revision")
    }
}

#[expect(unsafe_code)]
unsafe impl<T> salsa::Update for AstNodeRef<T> {
    unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
        let old_ref = unsafe { &mut (*old_pointer) };

        // The equality of an `AstNodeRef` depends on both the module address and the node index,
        // but the former is not stored in release builds to save memory. As such, AST nodes
        // are always considered change when the AST is reparsed, which is acceptable because
        // any change to the AST is likely to invalidate most node indices anyways.
        *old_ref = new_value;
        true
    }