Use a single node hierarchy to track statements and expressions (#6709)

## Summary

This PR is a follow-up to the suggestion in
https://github.com/astral-sh/ruff/pull/6345#discussion_r1285470953 to
use a single stack to store all statements and expressions, rather than
using separate vectors for each, which gives us something closer to a
full-fidelity chain. (We can then generalize this concept to include all
other AST nodes too.)

This is in part made possible by the removal of the hash map from
`&Stmt` to `StatementId` (#6694), which makes it much cheaper to store
these using a single interface (since doing so no longer introduces the
requirement that we hash all expressions).

I'll follow-up with some profiling, but a few notes on how the data
requirements have changed:

- We now store a `BranchId` for every expression, not just every
statement, so that's an extra `u32`.
- We now store a single `NodeId` on every snapshot, rather than separate
`StatementId` and `ExpressionId` IDs, so that's one fewer `u32` for each
snapshot.
- We're probably doing a few more lookups in general, since any calls to
`current_statement()` etc. now have to iterate up the node hierarchy
until they identify the first statement.

## Test Plan

`cargo test`
This commit is contained in:
Charlie Marsh 2023-08-21 21:32:57 -04:00 committed by GitHub
parent abc5065fc7
commit 424b8d4ad2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 268 additions and 299 deletions

View file

@ -11,8 +11,8 @@ use ruff_text_size::TextRange;
use crate::context::ExecutionContext;
use crate::model::SemanticModel;
use crate::nodes::NodeId;
use crate::reference::ResolvedReferenceId;
use crate::statements::StatementId;
use crate::ScopeId;
#[derive(Debug, Clone)]
@ -24,7 +24,7 @@ pub struct Binding<'a> {
/// The context in which the [`Binding`] was created.
pub context: ExecutionContext,
/// The statement in which the [`Binding`] was defined.
pub source: Option<StatementId>,
pub source: Option<NodeId>,
/// The references to the [`Binding`].
pub references: Vec<ResolvedReferenceId>,
/// The exceptions that were handled when the [`Binding`] was defined.
@ -185,7 +185,7 @@ impl<'a> Binding<'a> {
/// Returns the range of the binding's parent.
pub fn parent_range(&self, semantic: &SemanticModel) -> Option<TextRange> {
self.source
.map(|statement_id| semantic.statement(statement_id))
.map(|id| semantic.statement(id))
.and_then(|parent| {
if parent.is_import_from_stmt() {
Some(parent.range())