diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 913f651668..63f4bac476 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -14,12 +14,14 @@ use rustpython_parser::ast::{ use ruff_diagnostics::Diagnostic; use ruff_python_ast::all::{extract_all_names, AllNamesFlags}; +use ruff_python_ast::binding::{ + Binding, BindingId, BindingKind, Exceptions, ExecutionContext, Export, FromImportation, + Importation, StarImportation, SubmoduleImportation, +}; use ruff_python_ast::context::Context; use ruff_python_ast::helpers::{extract_handled_exceptions, to_module_path}; use ruff_python_ast::scope::{ - Binding, BindingId, BindingKind, ClassDef, Exceptions, ExecutionContext, Export, - FromImportation, FunctionDef, Importation, Lambda, Scope, ScopeId, ScopeKind, ScopeStack, - StarImportation, SubmoduleImportation, + ClassDef, FunctionDef, Lambda, Scope, ScopeId, ScopeKind, ScopeStack, }; use ruff_python_ast::source_code::{Indexer, Locator, Stylist}; use ruff_python_ast::types::{Node, Range, RefEquality}; diff --git a/crates/ruff/src/rules/flake8_type_checking/helpers.rs b/crates/ruff/src/rules/flake8_type_checking/helpers.rs index a03950d3d0..3d43af3ae9 100644 --- a/crates/ruff/src/rules/flake8_type_checking/helpers.rs +++ b/crates/ruff/src/rules/flake8_type_checking/helpers.rs @@ -1,9 +1,10 @@ use num_traits::Zero; use rustpython_parser::ast::{Constant, Expr, ExprKind}; +use ruff_python_ast::binding::{Binding, BindingKind, ExecutionContext}; use ruff_python_ast::context::Context; use ruff_python_ast::helpers::{map_callable, to_call_path}; -use ruff_python_ast::scope::{Binding, BindingKind, ExecutionContext, ScopeKind}; +use ruff_python_ast::scope::ScopeKind; /// Return `true` if [`Expr`] is a guard for a type-checking block. pub fn is_type_checking_block(context: &Context, test: &Expr) -> bool { diff --git a/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs b/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs index 6cec22746d..3a56d5b060 100644 --- a/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs +++ b/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs @@ -1,6 +1,6 @@ use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::scope::{ +use ruff_python_ast::binding::{ Binding, BindingKind, ExecutionContext, FromImportation, Importation, SubmoduleImportation, }; diff --git a/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs b/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs index b303db32d0..ab3b92dd22 100644 --- a/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs +++ b/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs @@ -2,7 +2,7 @@ use std::path::Path; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::scope::{ +use ruff_python_ast::binding::{ Binding, BindingKind, ExecutionContext, FromImportation, Importation, SubmoduleImportation, }; diff --git a/crates/ruff/src/rules/flake8_unused_arguments/rules.rs b/crates/ruff/src/rules/flake8_unused_arguments/rules.rs index 009db5cc77..6d9ee58237 100644 --- a/crates/ruff/src/rules/flake8_unused_arguments/rules.rs +++ b/crates/ruff/src/rules/flake8_unused_arguments/rules.rs @@ -5,9 +5,10 @@ use rustpython_parser::ast::{Arg, Arguments}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::binding::Bindings; use ruff_python_ast::function_type; use ruff_python_ast::function_type::FunctionType; -use ruff_python_ast::scope::{Bindings, FunctionDef, Lambda, Scope, ScopeKind}; +use ruff_python_ast::scope::{FunctionDef, Lambda, Scope, ScopeKind}; use ruff_python_ast::visibility; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs b/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs index aee0237b9b..45e94ad41f 100644 --- a/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs +++ b/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs @@ -3,7 +3,7 @@ use rustpython_parser::ast::{Expr, ExprKind}; use ruff_diagnostics::Violation; use ruff_diagnostics::{Diagnostic, DiagnosticKind}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::scope::BindingKind; +use ruff_python_ast::binding::BindingKind; use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pandas_vet/rules/check_call.rs b/crates/ruff/src/rules/pandas_vet/rules/check_call.rs index 4c90fc6cc9..963c008356 100644 --- a/crates/ruff/src/rules/pandas_vet/rules/check_call.rs +++ b/crates/ruff/src/rules/pandas_vet/rules/check_call.rs @@ -3,7 +3,7 @@ use rustpython_parser::ast::{Expr, ExprKind}; use ruff_diagnostics::Violation; use ruff_diagnostics::{Diagnostic, DiagnosticKind}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::scope::{BindingKind, Importation}; +use ruff_python_ast::binding::{BindingKind, Importation}; use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs b/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs index b0f7d5a410..b8d5d90c16 100644 --- a/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs +++ b/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs @@ -2,7 +2,8 @@ use std::string::ToString; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::scope::{Bindings, Scope, ScopeKind}; +use ruff_python_ast::binding::Bindings; +use ruff_python_ast::scope::{Scope, ScopeKind}; #[violation] pub struct UndefinedLocal { diff --git a/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs b/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs index d46263de8d..cdd37811e6 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs @@ -2,7 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind, Keyword, Stmt}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::scope::{Binding, BindingKind, Bindings, Scope}; +use ruff_python_ast::binding::{Binding, BindingKind, Bindings}; +use ruff_python_ast::scope::Scope; use ruff_python_ast::types::Range; use crate::autofix::helpers::remove_argument; diff --git a/crates/ruff_python_ast/src/all.rs b/crates/ruff_python_ast/src/all.rs index 998ab314d8..f61a4f3077 100644 --- a/crates/ruff_python_ast/src/all.rs +++ b/crates/ruff_python_ast/src/all.rs @@ -1,8 +1,9 @@ use bitflags::bitflags; use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind}; +use crate::binding::{BindingKind, Export}; use crate::context::Context; -use crate::scope::{BindingKind, Export, Scope}; +use crate::scope::Scope; bitflags! { #[derive(Default)] diff --git a/crates/ruff_python_ast/src/binding.rs b/crates/ruff_python_ast/src/binding.rs new file mode 100644 index 0000000000..95b4c0c29e --- /dev/null +++ b/crates/ruff_python_ast/src/binding.rs @@ -0,0 +1,280 @@ +use std::num::TryFromIntError; +use std::ops::{Deref, Index, IndexMut}; + +use bitflags::bitflags; +use rustpython_parser::ast::Stmt; + +use crate::scope::ScopeId; +use crate::types::{Range, RefEquality}; + +#[derive(Debug, Clone)] +pub struct Binding<'a> { + pub kind: BindingKind<'a>, + pub range: Range, + /// The context in which the binding was created. + pub context: ExecutionContext, + /// The statement in which the [`Binding`] was defined. + pub source: Option>, + /// Tuple of (scope index, range) indicating the scope and range at which + /// the binding was last used in a runtime context. + pub runtime_usage: Option<(ScopeId, Range)>, + /// Tuple of (scope index, range) indicating the scope and range at which + /// the binding was last used in a typing-time context. + pub typing_usage: Option<(ScopeId, Range)>, + /// Tuple of (scope index, range) indicating the scope and range at which + /// the binding was last used in a synthetic context. This is used for + /// (e.g.) `__future__` imports, explicit re-exports, and other bindings + /// that should be considered used even if they're never referenced. + pub synthetic_usage: Option<(ScopeId, Range)>, + /// The exceptions that were handled when the binding was defined. + pub exceptions: Exceptions, +} + +impl<'a> Binding<'a> { + pub fn mark_used(&mut self, scope: ScopeId, range: Range, context: ExecutionContext) { + match context { + ExecutionContext::Runtime => self.runtime_usage = Some((scope, range)), + ExecutionContext::Typing => self.typing_usage = Some((scope, range)), + } + } + + pub const fn used(&self) -> bool { + self.runtime_usage.is_some() + || self.synthetic_usage.is_some() + || self.typing_usage.is_some() + } + + pub const fn is_definition(&self) -> bool { + matches!( + self.kind, + BindingKind::ClassDefinition + | BindingKind::FunctionDefinition + | BindingKind::Builtin + | BindingKind::FutureImportation + | BindingKind::Importation(..) + | BindingKind::FromImportation(..) + | BindingKind::SubmoduleImportation(..) + ) + } + + pub fn redefines(&self, existing: &'a Binding) -> bool { + match &self.kind { + BindingKind::Importation(Importation { full_name, .. }) => { + if let BindingKind::SubmoduleImportation(SubmoduleImportation { + full_name: existing, + .. + }) = &existing.kind + { + return full_name == existing; + } + } + BindingKind::FromImportation(FromImportation { full_name, .. }) => { + if let BindingKind::SubmoduleImportation(SubmoduleImportation { + full_name: existing, + .. + }) = &existing.kind + { + return full_name == existing; + } + } + BindingKind::SubmoduleImportation(SubmoduleImportation { full_name, .. }) => { + match &existing.kind { + BindingKind::Importation(Importation { + full_name: existing, + .. + }) + | BindingKind::SubmoduleImportation(SubmoduleImportation { + full_name: existing, + .. + }) => { + return full_name == existing; + } + BindingKind::FromImportation(FromImportation { + full_name: existing, + .. + }) => { + return full_name == existing; + } + _ => {} + } + } + BindingKind::Annotation => { + return false; + } + BindingKind::FutureImportation => { + return false; + } + _ => {} + } + existing.is_definition() + } +} + +/// ID uniquely identifying a [Binding] in a program. +/// +/// 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 for BindingId { + type Error = TryFromIntError; + + fn try_from(value: usize) -> Result { + Ok(Self(u32::try_from(value)?)) + } +} + +impl nohash_hasher::IsEnabled for BindingId {} + +/// The bindings in a program. +/// +/// Bindings are indexed by [`BindingId`] +#[derive(Debug, Clone, Default)] +pub struct Bindings<'a>(Vec>); + +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 + } + + /// 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 for Bindings<'a> { + type Output = Binding<'a>; + + fn index(&self, index: BindingId) -> &Self::Output { + &self.0[usize::from(index)] + } +} + +impl<'a> IndexMut for Bindings<'a> { + fn index_mut(&mut self, index: BindingId) -> &mut Self::Output { + &mut self.0[usize::from(index)] + } +} + +impl<'a> Deref for Bindings<'a> { + type Target = [Binding<'a>]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a> FromIterator> for Bindings<'a> { + fn from_iter>>(iter: T) -> Self { + Self(Vec::from_iter(iter)) + } +} + +impl From for usize { + fn from(value: BindingId) -> Self { + value.0 as usize + } +} + +#[derive(Debug, Clone)] +pub struct StarImportation<'a> { + /// The level of the import. `None` or `Some(0)` indicate an absolute import. + pub level: Option, + /// The module being imported. `None` indicates a wildcard import. + pub module: Option<&'a str>, +} + +// Pyflakes defines the following binding hierarchy (via inheritance): +// Binding +// ExportBinding +// Annotation +// Argument +// Assignment +// NamedExprAssignment +// Definition +// FunctionDefinition +// ClassDefinition +// Builtin +// Importation +// SubmoduleImportation +// ImportationFrom +// FutureImportation + +#[derive(Clone, Debug)] +pub struct Export { + /// The names of the bindings exported via `__all__`. + pub names: Vec, +} + +#[derive(Clone, Debug)] +pub struct Importation<'a> { + /// The name to which the import is bound. + /// Given `import foo`, `name` would be "foo". + /// Given `import foo as bar`, `name` would be "bar". + pub name: &'a str, + /// The full name of the module being imported. + /// Given `import foo`, `full_name` would be "foo". + /// Given `import foo as bar`, `full_name` would be "foo". + pub full_name: &'a str, +} + +#[derive(Clone, Debug)] +pub struct FromImportation<'a> { + /// The name to which the import is bound. + /// Given `from foo import bar`, `name` would be "bar". + /// Given `from foo import bar as baz`, `name` would be "baz". + pub name: &'a str, + /// The full name of the module being imported. + /// Given `from foo import bar`, `full_name` would be "foo.bar". + /// Given `from foo import bar as baz`, `full_name` would be "foo.bar". + pub full_name: String, +} + +#[derive(Clone, Debug)] +pub struct SubmoduleImportation<'a> { + /// The parent module imported by the submodule import. + /// Given `import foo.bar`, `module` would be "foo". + pub name: &'a str, + /// The full name of the submodule being imported. + /// Given `import foo.bar`, `full_name` would be "foo.bar". + pub full_name: &'a str, +} + +#[derive(Clone, Debug, is_macro::Is)] +pub enum BindingKind<'a> { + Annotation, + Argument, + Assignment, + Binding, + LoopVar, + Global, + Nonlocal, + Builtin, + ClassDefinition, + FunctionDefinition, + Export(Export), + FutureImportation, + Importation(Importation<'a>), + FromImportation(FromImportation<'a>), + SubmoduleImportation(SubmoduleImportation<'a>), +} + +bitflags! { + pub struct Exceptions: u32 { + const NAME_ERROR = 0b0000_0001; + const MODULE_NOT_FOUND_ERROR = 0b0000_0010; + const IMPORT_ERROR = 0b0000_0100; + } +} + +#[derive(Copy, Debug, Clone)] +pub enum ExecutionContext { + Runtime, + Typing, +} diff --git a/crates/ruff_python_ast/src/context.rs b/crates/ruff_python_ast/src/context.rs index e0e4f8e7f9..49f79ebd37 100644 --- a/crates/ruff_python_ast/src/context.rs +++ b/crates/ruff_python_ast/src/context.rs @@ -8,11 +8,12 @@ use smallvec::smallvec; use ruff_python_stdlib::path::is_python_stub_file; use ruff_python_stdlib::typing::TYPING_EXTENSIONS; -use crate::helpers::{collect_call_path, from_relative_import}; -use crate::scope::{ +use crate::binding::{ Binding, BindingId, BindingKind, Bindings, Exceptions, ExecutionContext, FromImportation, - Importation, Scope, ScopeId, ScopeKind, ScopeStack, Scopes, SubmoduleImportation, + Importation, SubmoduleImportation, }; +use crate::helpers::{collect_call_path, from_relative_import}; +use crate::scope::{Scope, ScopeId, ScopeKind, ScopeStack, Scopes}; use crate::types::{CallPath, RefEquality}; use crate::typing::AnnotationKind; use crate::visibility::{module_visibility, Modifier, VisibleScope}; diff --git a/crates/ruff_python_ast/src/lib.rs b/crates/ruff_python_ast/src/lib.rs index ecf1416075..07f0505658 100644 --- a/crates/ruff_python_ast/src/lib.rs +++ b/crates/ruff_python_ast/src/lib.rs @@ -1,4 +1,5 @@ pub mod all; +pub mod binding; pub mod branch_detection; pub mod cast; pub mod comparable; diff --git a/crates/ruff_python_ast/src/scope.rs b/crates/ruff_python_ast/src/scope.rs index 4d5893f8ff..2224221eb8 100644 --- a/crates/ruff_python_ast/src/scope.rs +++ b/crates/ruff_python_ast/src/scope.rs @@ -1,10 +1,11 @@ -use crate::types::{Range, RefEquality}; -use bitflags::bitflags; -use rustc_hash::FxHashMap; -use rustpython_parser::ast::{Arguments, Expr, Keyword, Stmt}; use std::num::TryFromIntError; use std::ops::{Deref, Index, IndexMut}; +use rustc_hash::FxHashMap; +use rustpython_parser::ast::{Arguments, Expr, Keyword, Stmt}; + +use crate::binding::{BindingId, StarImportation}; + #[derive(Debug)] pub struct Scope<'a> { pub id: ScopeId, @@ -251,275 +252,3 @@ impl Default for ScopeStack { Self(vec![ScopeId::global()]) } } - -bitflags! { - pub struct Exceptions: u32 { - const NAME_ERROR = 0b0000_0001; - const MODULE_NOT_FOUND_ERROR = 0b0000_0010; - const IMPORT_ERROR = 0b0000_0100; - } -} - -#[derive(Debug, Clone)] -pub struct Binding<'a> { - pub kind: BindingKind<'a>, - pub range: Range, - /// The context in which the binding was created. - pub context: ExecutionContext, - /// The statement in which the [`Binding`] was defined. - pub source: Option>, - /// Tuple of (scope index, range) indicating the scope and range at which - /// the binding was last used in a runtime context. - pub runtime_usage: Option<(ScopeId, Range)>, - /// Tuple of (scope index, range) indicating the scope and range at which - /// the binding was last used in a typing-time context. - pub typing_usage: Option<(ScopeId, Range)>, - /// Tuple of (scope index, range) indicating the scope and range at which - /// the binding was last used in a synthetic context. This is used for - /// (e.g.) `__future__` imports, explicit re-exports, and other bindings - /// that should be considered used even if they're never referenced. - pub synthetic_usage: Option<(ScopeId, Range)>, - /// The exceptions that were handled when the binding was defined. - pub exceptions: Exceptions, -} - -impl<'a> Binding<'a> { - pub fn mark_used(&mut self, scope: ScopeId, range: Range, context: ExecutionContext) { - match context { - ExecutionContext::Runtime => self.runtime_usage = Some((scope, range)), - ExecutionContext::Typing => self.typing_usage = Some((scope, range)), - } - } - - pub const fn used(&self) -> bool { - self.runtime_usage.is_some() - || self.synthetic_usage.is_some() - || self.typing_usage.is_some() - } - - pub const fn is_definition(&self) -> bool { - matches!( - self.kind, - BindingKind::ClassDefinition - | BindingKind::FunctionDefinition - | BindingKind::Builtin - | BindingKind::FutureImportation - | BindingKind::Importation(..) - | BindingKind::FromImportation(..) - | BindingKind::SubmoduleImportation(..) - ) - } - - pub fn redefines(&self, existing: &'a Binding) -> bool { - match &self.kind { - BindingKind::Importation(Importation { full_name, .. }) => { - if let BindingKind::SubmoduleImportation(SubmoduleImportation { - full_name: existing, - .. - }) = &existing.kind - { - return full_name == existing; - } - } - BindingKind::FromImportation(FromImportation { full_name, .. }) => { - if let BindingKind::SubmoduleImportation(SubmoduleImportation { - full_name: existing, - .. - }) = &existing.kind - { - return full_name == existing; - } - } - BindingKind::SubmoduleImportation(SubmoduleImportation { full_name, .. }) => { - match &existing.kind { - BindingKind::Importation(Importation { - full_name: existing, - .. - }) - | BindingKind::SubmoduleImportation(SubmoduleImportation { - full_name: existing, - .. - }) => { - return full_name == existing; - } - BindingKind::FromImportation(FromImportation { - full_name: existing, - .. - }) => { - return full_name == existing; - } - _ => {} - } - } - BindingKind::Annotation => { - return false; - } - BindingKind::FutureImportation => { - return false; - } - _ => {} - } - existing.is_definition() - } -} - -#[derive(Copy, Debug, Clone)] -pub enum ExecutionContext { - Runtime, - Typing, -} - -/// ID uniquely identifying a [Binding] in a program. -/// -/// 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 From for usize { - fn from(value: BindingId) -> Self { - value.0 as usize - } -} - -impl TryFrom for BindingId { - type Error = TryFromIntError; - - fn try_from(value: usize) -> Result { - Ok(Self(u32::try_from(value)?)) - } -} - -impl nohash_hasher::IsEnabled for BindingId {} - -#[derive(Debug, Clone)] -pub struct StarImportation<'a> { - /// The level of the import. `None` or `Some(0)` indicate an absolute import. - pub level: Option, - /// The module being imported. `None` indicates a wildcard import. - pub module: Option<&'a str>, -} - -// Pyflakes defines the following binding hierarchy (via inheritance): -// Binding -// ExportBinding -// Annotation -// Argument -// Assignment -// NamedExprAssignment -// Definition -// FunctionDefinition -// ClassDefinition -// Builtin -// Importation -// SubmoduleImportation -// ImportationFrom -// FutureImportation - -#[derive(Clone, Debug)] -pub struct Export { - /// The names of the bindings exported via `__all__`. - pub names: Vec, -} - -#[derive(Clone, Debug)] -pub struct Importation<'a> { - /// The name to which the import is bound. - /// Given `import foo`, `name` would be "foo". - /// Given `import foo as bar`, `name` would be "bar". - pub name: &'a str, - /// The full name of the module being imported. - /// Given `import foo`, `full_name` would be "foo". - /// Given `import foo as bar`, `full_name` would be "foo". - pub full_name: &'a str, -} - -#[derive(Clone, Debug)] -pub struct FromImportation<'a> { - /// The name to which the import is bound. - /// Given `from foo import bar`, `name` would be "bar". - /// Given `from foo import bar as baz`, `name` would be "baz". - pub name: &'a str, - /// The full name of the module being imported. - /// Given `from foo import bar`, `full_name` would be "foo.bar". - /// Given `from foo import bar as baz`, `full_name` would be "foo.bar". - pub full_name: String, -} - -#[derive(Clone, Debug)] -pub struct SubmoduleImportation<'a> { - /// The parent module imported by the submodule import. - /// Given `import foo.bar`, `module` would be "foo". - pub name: &'a str, - /// The full name of the submodule being imported. - /// Given `import foo.bar`, `full_name` would be "foo.bar". - pub full_name: &'a str, -} - -#[derive(Clone, Debug, is_macro::Is)] -pub enum BindingKind<'a> { - Annotation, - Argument, - Assignment, - Binding, - LoopVar, - Global, - Nonlocal, - Builtin, - ClassDefinition, - FunctionDefinition, - Export(Export), - FutureImportation, - Importation(Importation<'a>), - FromImportation(FromImportation<'a>), - SubmoduleImportation(SubmoduleImportation<'a>), -} - -/// The bindings in a program. -/// -/// Bindings are indexed by [`BindingId`] -#[derive(Debug, Clone, Default)] -pub struct Bindings<'a>(Vec>); - -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 - } - - /// 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 for Bindings<'a> { - type Output = Binding<'a>; - - fn index(&self, index: BindingId) -> &Self::Output { - &self.0[usize::from(index)] - } -} - -impl<'a> IndexMut for Bindings<'a> { - fn index_mut(&mut self, index: BindingId) -> &mut Self::Output { - &mut self.0[usize::from(index)] - } -} - -impl<'a> Deref for Bindings<'a> { - type Target = [Binding<'a>]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a> FromIterator> for Bindings<'a> { - fn from_iter>>(iter: T) -> Self { - Self(Vec::from_iter(iter)) - } -}