mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-10 13:48:35 +00:00
Introduce ruff_index
crate (#4597)
This commit is contained in:
parent
04d273bcc7
commit
652c644c2a
15 changed files with 681 additions and 149 deletions
|
@ -1,8 +1,8 @@
|
|||
use std::num::TryFromIntError;
|
||||
use std::ops::{Deref, Index, IndexMut};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::model::SemanticModel;
|
||||
use bitflags::bitflags;
|
||||
use ruff_index::{newtype_index, IndexSlice, IndexVec};
|
||||
use ruff_python_ast::helpers;
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_text_size::TextRange;
|
||||
|
@ -130,16 +130,8 @@ impl<'a> Binding<'a> {
|
|||
/// Using a `u32` to identify [Binding]s should is sufficient because Ruff only supports documents with a
|
||||
/// size smaller than or equal to `u32::max`. A document with the size of `u32::max` must have fewer than `u32::max`
|
||||
/// bindings because bindings must be separated by whitespace (and have an assignment).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct BindingId(u32);
|
||||
|
||||
impl TryFrom<usize> for BindingId {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
Ok(Self(u32::try_from(value)?))
|
||||
}
|
||||
}
|
||||
#[newtype_index]
|
||||
pub struct BindingId;
|
||||
|
||||
impl nohash_hasher::IsEnabled for BindingId {}
|
||||
|
||||
|
@ -147,53 +139,37 @@ impl nohash_hasher::IsEnabled for BindingId {}
|
|||
///
|
||||
/// Bindings are indexed by [`BindingId`]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Bindings<'a>(Vec<Binding<'a>>);
|
||||
pub struct Bindings<'a>(IndexVec<BindingId, Binding<'a>>);
|
||||
|
||||
impl<'a> Bindings<'a> {
|
||||
/// Pushes a new binding and returns its id
|
||||
pub fn push(&mut self, binding: Binding<'a>) -> BindingId {
|
||||
let id = self.next_id();
|
||||
self.0.push(binding);
|
||||
id
|
||||
self.0.push(binding)
|
||||
}
|
||||
|
||||
/// Returns the id that will be assigned when pushing the next binding
|
||||
pub fn next_id(&self) -> BindingId {
|
||||
BindingId::try_from(self.0.len()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<BindingId> for Bindings<'a> {
|
||||
type Output = Binding<'a>;
|
||||
|
||||
fn index(&self, index: BindingId) -> &Self::Output {
|
||||
&self.0[usize::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IndexMut<BindingId> for Bindings<'a> {
|
||||
fn index_mut(&mut self, index: BindingId) -> &mut Self::Output {
|
||||
&mut self.0[usize::from(index)]
|
||||
self.0.next_index()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Bindings<'a> {
|
||||
type Target = [Binding<'a>];
|
||||
type Target = IndexSlice<BindingId, Binding<'a>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<Binding<'a>> for Bindings<'a> {
|
||||
fn from_iter<T: IntoIterator<Item = Binding<'a>>>(iter: T) -> Self {
|
||||
Self(Vec::from_iter(iter))
|
||||
impl<'a> DerefMut for Bindings<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BindingId> for usize {
|
||||
fn from(value: BindingId) -> Self {
|
||||
value.0 as usize
|
||||
impl<'a> FromIterator<Binding<'a>> for Bindings<'a> {
|
||||
fn from_iter<T: IntoIterator<Item = Binding<'a>>>(iter: T) -> Self {
|
||||
Self(IndexVec::from_iter(iter))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
//! can be documented, such as a module, class, or function.
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::num::TryFromIntError;
|
||||
use std::ops::{Deref, Index};
|
||||
use std::ops::Deref;
|
||||
|
||||
use ruff_index::{newtype_index, IndexSlice, IndexVec};
|
||||
use rustpython_parser::ast::{self, Stmt};
|
||||
|
||||
use crate::analyze::visibility::{
|
||||
|
@ -12,28 +12,14 @@ use crate::analyze::visibility::{
|
|||
};
|
||||
|
||||
/// Id uniquely identifying a definition in a program.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct DefinitionId(u32);
|
||||
#[newtype_index]
|
||||
pub struct DefinitionId;
|
||||
|
||||
impl DefinitionId {
|
||||
/// Returns the ID for the module definition.
|
||||
#[inline]
|
||||
pub const fn module() -> Self {
|
||||
DefinitionId(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for DefinitionId {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
Ok(Self(u32::try_from(value)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DefinitionId> for usize {
|
||||
fn from(value: DefinitionId) -> Self {
|
||||
value.0 as usize
|
||||
DefinitionId::from_u32(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,11 +104,11 @@ impl Definition<'_> {
|
|||
|
||||
/// The definitions within a Python program indexed by [`DefinitionId`].
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Definitions<'a>(Vec<Definition<'a>>);
|
||||
pub struct Definitions<'a>(IndexVec<DefinitionId, Definition<'a>>);
|
||||
|
||||
impl<'a> Definitions<'a> {
|
||||
pub fn for_module(definition: Module<'a>) -> Self {
|
||||
Self(vec![Definition::Module(definition)])
|
||||
Self(IndexVec::from_raw(vec![Definition::Module(definition)]))
|
||||
}
|
||||
|
||||
/// Pushes a new member definition and returns its unique id.
|
||||
|
@ -130,14 +116,13 @@ impl<'a> Definitions<'a> {
|
|||
/// Members are assumed to be pushed in traversal order, such that parents are pushed before
|
||||
/// their children.
|
||||
pub fn push_member(&mut self, member: Member<'a>) -> DefinitionId {
|
||||
let next_id = DefinitionId::try_from(self.0.len()).unwrap();
|
||||
self.0.push(Definition::Member(member));
|
||||
next_id
|
||||
self.0.push(Definition::Member(member))
|
||||
}
|
||||
|
||||
/// Resolve the visibility of each definition in the collection.
|
||||
pub fn resolve(self, exports: Option<&[&str]>) -> ContextualizedDefinitions<'a> {
|
||||
let mut definitions: Vec<ContextualizedDefinition<'a>> = Vec::with_capacity(self.len());
|
||||
let mut definitions: IndexVec<DefinitionId, ContextualizedDefinition<'a>> =
|
||||
IndexVec::with_capacity(self.len());
|
||||
|
||||
for definition in self {
|
||||
// Determine the visibility of the next definition, taking into account its parent's
|
||||
|
@ -147,7 +132,7 @@ impl<'a> Definitions<'a> {
|
|||
Definition::Module(module) => module.source.to_visibility(),
|
||||
Definition::Member(member) => match member.kind {
|
||||
MemberKind::Class => {
|
||||
let parent = &definitions[usize::from(member.parent)];
|
||||
let parent = &definitions[member.parent];
|
||||
if parent.visibility.is_private()
|
||||
|| exports
|
||||
.map_or(false, |exports| !exports.contains(&member.name()))
|
||||
|
@ -158,7 +143,7 @@ impl<'a> Definitions<'a> {
|
|||
}
|
||||
}
|
||||
MemberKind::NestedClass => {
|
||||
let parent = &definitions[usize::from(member.parent)];
|
||||
let parent = &definitions[member.parent];
|
||||
if parent.visibility.is_private()
|
||||
|| matches!(
|
||||
parent.definition,
|
||||
|
@ -176,7 +161,7 @@ impl<'a> Definitions<'a> {
|
|||
}
|
||||
}
|
||||
MemberKind::Function => {
|
||||
let parent = &definitions[usize::from(member.parent)];
|
||||
let parent = &definitions[member.parent];
|
||||
if parent.visibility.is_private()
|
||||
|| exports
|
||||
.map_or(false, |exports| !exports.contains(&member.name()))
|
||||
|
@ -188,7 +173,7 @@ impl<'a> Definitions<'a> {
|
|||
}
|
||||
MemberKind::NestedFunction => Visibility::Private,
|
||||
MemberKind::Method => {
|
||||
let parent = &definitions[usize::from(member.parent)];
|
||||
let parent = &definitions[member.parent];
|
||||
if parent.visibility.is_private() {
|
||||
Visibility::Private
|
||||
} else {
|
||||
|
@ -204,20 +189,13 @@ impl<'a> Definitions<'a> {
|
|||
});
|
||||
}
|
||||
|
||||
ContextualizedDefinitions(definitions)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<DefinitionId> for Definitions<'a> {
|
||||
type Output = Definition<'a>;
|
||||
|
||||
fn index(&self, index: DefinitionId) -> &Self::Output {
|
||||
&self.0[usize::from(index)]
|
||||
ContextualizedDefinitions(definitions.raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Definitions<'a> {
|
||||
type Target = [Definition<'a>];
|
||||
type Target = IndexSlice<DefinitionId, Definition<'a>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::num::{NonZeroU32, TryFromIntError};
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use ruff_index::{newtype_index, IndexVec};
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_parser::ast::Stmt;
|
||||
|
||||
|
@ -11,24 +11,9 @@ use ruff_python_ast::types::RefEquality;
|
|||
/// Using a `u32` is sufficient because Ruff only supports parsing documents with a size of max `u32::max`
|
||||
/// and it is impossible to have more statements than characters in the file. We use a `NonZeroU32` to
|
||||
/// take advantage of memory layout optimizations.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct NodeId(NonZeroU32);
|
||||
|
||||
/// Convert a `usize` to a `NodeId` (by adding 1 to the value, and casting to `NonZeroU32`).
|
||||
impl TryFrom<usize> for NodeId {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
Ok(Self(NonZeroU32::try_from(u32::try_from(value)? + 1)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a `NodeId` to a `usize` (by subtracting 1 from the value, and casting to `usize`).
|
||||
impl From<NodeId> for usize {
|
||||
fn from(value: NodeId) -> Self {
|
||||
value.0.get() as usize - 1
|
||||
}
|
||||
}
|
||||
#[newtype_index]
|
||||
#[derive(Ord, PartialOrd)]
|
||||
pub struct NodeId;
|
||||
|
||||
/// A [`Node`] represents a statement in a program, along with a pointer to its parent (if any).
|
||||
#[derive(Debug)]
|
||||
|
@ -44,7 +29,7 @@ struct Node<'a> {
|
|||
/// The nodes of a program indexed by [`NodeId`]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Nodes<'a> {
|
||||
nodes: Vec<Node<'a>>,
|
||||
nodes: IndexVec<NodeId, Node<'a>>,
|
||||
node_to_id: FxHashMap<RefEquality<'a, Stmt>, NodeId>,
|
||||
}
|
||||
|
||||
|
@ -53,16 +38,15 @@ impl<'a> Nodes<'a> {
|
|||
///
|
||||
/// Panics if a node with the same pointer already exists.
|
||||
pub fn insert(&mut self, stmt: &'a Stmt, parent: Option<NodeId>) -> NodeId {
|
||||
let next_id = NodeId::try_from(self.nodes.len()).unwrap();
|
||||
let next_id = self.nodes.next_index();
|
||||
if let Some(existing_id) = self.node_to_id.insert(RefEquality(stmt), next_id) {
|
||||
panic!("Node already exists with id {existing_id:?}");
|
||||
}
|
||||
self.nodes.push(Node {
|
||||
stmt,
|
||||
parent,
|
||||
depth: parent.map_or(0, |parent| self.nodes[usize::from(parent)].depth + 1),
|
||||
});
|
||||
next_id
|
||||
depth: parent.map_or(0, |parent| self.nodes[parent].depth + 1),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the [`NodeId`] of the given node.
|
||||
|
@ -74,26 +58,24 @@ impl<'a> Nodes<'a> {
|
|||
/// Return the [`NodeId`] of the parent node.
|
||||
#[inline]
|
||||
pub fn parent_id(&self, node_id: NodeId) -> Option<NodeId> {
|
||||
self.nodes[usize::from(node_id)].parent
|
||||
self.nodes[node_id].parent
|
||||
}
|
||||
|
||||
/// Return the depth of the node.
|
||||
#[inline]
|
||||
pub fn depth(&self, node_id: NodeId) -> u32 {
|
||||
self.nodes[usize::from(node_id)].depth
|
||||
self.nodes[node_id].depth
|
||||
}
|
||||
|
||||
/// Returns an iterator over all [`NodeId`] ancestors, starting from the given [`NodeId`].
|
||||
pub fn ancestor_ids(&self, node_id: NodeId) -> impl Iterator<Item = NodeId> + '_ {
|
||||
std::iter::successors(Some(node_id), |&node_id| {
|
||||
self.nodes[usize::from(node_id)].parent
|
||||
})
|
||||
std::iter::successors(Some(node_id), |&node_id| self.nodes[node_id].parent)
|
||||
}
|
||||
|
||||
/// Return the parent of the given node.
|
||||
pub fn parent(&self, node: &'a Stmt) -> Option<&'a Stmt> {
|
||||
let node_id = self.node_to_id.get(&RefEquality(node))?;
|
||||
let parent_id = self.nodes[usize::from(*node_id)].parent?;
|
||||
let parent_id = self.nodes[*node_id].parent?;
|
||||
Some(self[parent_id])
|
||||
}
|
||||
}
|
||||
|
@ -101,13 +83,15 @@ impl<'a> Nodes<'a> {
|
|||
impl<'a> Index<NodeId> for Nodes<'a> {
|
||||
type Output = &'a Stmt;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, index: NodeId) -> &Self::Output {
|
||||
&self.nodes[usize::from(index)].stmt
|
||||
&self.nodes[index].stmt
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IndexMut<NodeId> for Nodes<'a> {
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: NodeId) -> &mut Self::Output {
|
||||
&mut self.nodes[usize::from(index)].stmt
|
||||
&mut self.nodes[index].stmt
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::num::TryFromIntError;
|
||||
use std::ops::{Deref, Index, IndexMut};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use ruff_index::{newtype_index, Idx, IndexSlice, IndexVec};
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_parser::ast::{Arguments, Expr, Keyword, Stmt};
|
||||
|
||||
|
@ -151,39 +151,25 @@ pub struct Lambda<'a> {
|
|||
/// Using a `u32` is sufficient because Ruff only supports parsing documents with a size of max `u32::max`
|
||||
/// and it is impossible to have more scopes than characters in the file (because defining a function or class
|
||||
/// requires more than one character).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct ScopeId(u32);
|
||||
#[newtype_index]
|
||||
pub struct ScopeId;
|
||||
|
||||
impl ScopeId {
|
||||
/// Returns the ID for the global scope
|
||||
#[inline]
|
||||
pub const fn global() -> Self {
|
||||
ScopeId(0)
|
||||
ScopeId::from_u32(0)
|
||||
}
|
||||
|
||||
/// Returns `true` if this is the id of the global scope
|
||||
pub const fn is_global(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for ScopeId {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
Ok(Self(u32::try_from(value)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ScopeId> for usize {
|
||||
fn from(value: ScopeId) -> Self {
|
||||
value.0 as usize
|
||||
self.index() == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// The scopes of a program indexed by [`ScopeId`]
|
||||
#[derive(Debug)]
|
||||
pub struct Scopes<'a>(Vec<Scope<'a>>);
|
||||
pub struct Scopes<'a>(IndexVec<ScopeId, Scope<'a>>);
|
||||
|
||||
impl<'a> Scopes<'a> {
|
||||
/// Returns a reference to the global scope
|
||||
|
@ -198,7 +184,7 @@ impl<'a> Scopes<'a> {
|
|||
|
||||
/// Pushes a new scope and returns its unique id
|
||||
pub fn push_scope(&mut self, kind: ScopeKind<'a>, parent: ScopeId) -> ScopeId {
|
||||
let next_id = ScopeId::try_from(self.0.len()).unwrap();
|
||||
let next_id = ScopeId::new(self.0.len());
|
||||
self.0.push(Scope::local(kind, parent));
|
||||
next_id
|
||||
}
|
||||
|
@ -218,27 +204,19 @@ impl<'a> Scopes<'a> {
|
|||
|
||||
impl Default for Scopes<'_> {
|
||||
fn default() -> Self {
|
||||
Self(vec![Scope::global()])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<ScopeId> for Scopes<'a> {
|
||||
type Output = Scope<'a>;
|
||||
|
||||
fn index(&self, index: ScopeId) -> &Self::Output {
|
||||
&self.0[usize::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IndexMut<ScopeId> for Scopes<'a> {
|
||||
fn index_mut(&mut self, index: ScopeId) -> &mut Self::Output {
|
||||
&mut self.0[usize::from(index)]
|
||||
Self(IndexVec::from_raw(vec![Scope::global()]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Scopes<'a> {
|
||||
type Target = [Scope<'a>];
|
||||
type Target = IndexSlice<ScopeId, Scope<'a>>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for Scopes<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue