Skip to content

Ensure &Node<T> is not removed#198

Open
russellbanks wants to merge 2 commits into
saschagrunert:mainfrom
russellbanks:validate-nodes
Open

Ensure &Node<T> is not removed#198
russellbanks wants to merge 2 commits into
saschagrunert:mainfrom
russellbanks:validate-nodes

Conversation

@russellbanks
Copy link
Copy Markdown
Contributor

This pull request modifies the design of the API so that an &Node<T> to a consumer will never be a removed node.

Resolves #195

@codecov
Copy link
Copy Markdown

codecov Bot commented May 8, 2026

Codecov Report

❌ Patch coverage is 95.32710% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.7%. Comparing base (44ec276) to head (d0a082e).
⚠️ Report is 13 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##            main    #198     +/-   ##
=======================================
- Coverage   97.5%   96.7%   -0.8%     
=======================================
  Files         10      10             
  Lines       1127    1212     +85     
=======================================
+ Hits        1099    1173     +74     
- Misses        28      39     +11     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Owner

@saschagrunert saschagrunert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good API improvement that addresses the inconsistency in #195. A few suggestions below.

Comment thread indextree/src/id.rs Outdated
Comment thread indextree/src/traverse.rs
Comment thread indextree/src/id.rs Outdated
Comment thread indextree/src/id.rs Outdated
Co-Authored-By: Sascha Grunert <sgrunert@redhat.com>
@russellbanks
Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review @saschagrunert!

Copy link
Copy Markdown
Owner

@saschagrunert saschagrunert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second pass review, five inline comments.

}
debug_assert!(!previous.is_some_and(|id| arena[id].is_removed()));
debug_assert!(!next.is_some_and(|id| arena[id].is_removed()));
}
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two debug_assert! lines that checked previous and next are not removed nodes were deleted here (old lines 81-82). Only the parent check survived. Consider restoring them:

debug_assert!(!previous.is_some_and(|id| id.is_removed(arena)));
debug_assert!(!next.is_some_and(|id| id.is_removed(arena)));

Comment thread indextree/src/node.rs
}
}

/// Returns a mutable reference to the node data.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since nodes is pub(crate), internal code can still construct &Node<T> for a removed node via &arena.nodes[id.index0()]. If that happens, this unreachable! would be misleading. Consider keeping panic!("attempted to access a freed node") as a safety net.

}
};
let is_last_sibling = traverser.arena()[id].next_sibling().is_none();
let is_last_sibling = traverser.arena().nodes[id.index0()]
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id comes from a live traversal, so traverser.arena().get(id).and_then(Node::next_sibling).is_none() would work here and stay consistent with the rest of the PR.

Comment thread indextree/src/arena.rs
/// ```
pub fn count(&self) -> usize {
self.nodes.len()
self.iter().count()
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes count() from O(1) to O(n) since iter() now filters removed nodes. Worth a doc note about the linear cost, since the old count() was just Vec::len().

Comment thread indextree/tests/lib.rs

#[test]
#[should_panic(expected = "index out of bounds")]
fn inaccessible_node() {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No replacement test covers arena.get(id) / id.is_removed(&arena) behavior after arena.clear(). The new is_removed handles out-of-bounds gracefully, but a test would confirm that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API node validation inconsistency

2 participants