mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-31 07:37:38 +00:00
Move Binding
structs out of scope.rs
(#3842)
This commit is contained in:
parent
6d80c79bac
commit
88308ef9cc
14 changed files with 309 additions and 291 deletions
|
@ -14,12 +14,14 @@ use rustpython_parser::ast::{
|
||||||
|
|
||||||
use ruff_diagnostics::Diagnostic;
|
use ruff_diagnostics::Diagnostic;
|
||||||
use ruff_python_ast::all::{extract_all_names, AllNamesFlags};
|
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::context::Context;
|
||||||
use ruff_python_ast::helpers::{extract_handled_exceptions, to_module_path};
|
use ruff_python_ast::helpers::{extract_handled_exceptions, to_module_path};
|
||||||
use ruff_python_ast::scope::{
|
use ruff_python_ast::scope::{
|
||||||
Binding, BindingId, BindingKind, ClassDef, Exceptions, ExecutionContext, Export,
|
ClassDef, FunctionDef, Lambda, Scope, ScopeId, ScopeKind, ScopeStack,
|
||||||
FromImportation, FunctionDef, Importation, Lambda, Scope, ScopeId, ScopeKind, ScopeStack,
|
|
||||||
StarImportation, SubmoduleImportation,
|
|
||||||
};
|
};
|
||||||
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
|
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
|
||||||
use ruff_python_ast::types::{Node, Range, RefEquality};
|
use ruff_python_ast::types::{Node, Range, RefEquality};
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use num_traits::Zero;
|
use num_traits::Zero;
|
||||||
use rustpython_parser::ast::{Constant, Expr, ExprKind};
|
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::context::Context;
|
||||||
use ruff_python_ast::helpers::{map_callable, to_call_path};
|
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.
|
/// Return `true` if [`Expr`] is a guard for a type-checking block.
|
||||||
pub fn is_type_checking_block(context: &Context, test: &Expr) -> bool {
|
pub fn is_type_checking_block(context: &Context, test: &Expr) -> bool {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::scope::{
|
use ruff_python_ast::binding::{
|
||||||
Binding, BindingKind, ExecutionContext, FromImportation, Importation, SubmoduleImportation,
|
Binding, BindingKind, ExecutionContext, FromImportation, Importation, SubmoduleImportation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::scope::{
|
use ruff_python_ast::binding::{
|
||||||
Binding, BindingKind, ExecutionContext, FromImportation, Importation, SubmoduleImportation,
|
Binding, BindingKind, ExecutionContext, FromImportation, Importation, SubmoduleImportation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,10 @@ use rustpython_parser::ast::{Arg, Arguments};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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;
|
||||||
use ruff_python_ast::function_type::FunctionType;
|
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 ruff_python_ast::visibility;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
|
@ -3,7 +3,7 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
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 ruff_python_ast::types::Range;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
|
@ -3,7 +3,7 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
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 ruff_python_ast::types::Range;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
|
@ -2,7 +2,8 @@ use std::string::ToString;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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]
|
#[violation]
|
||||||
pub struct UndefinedLocal {
|
pub struct UndefinedLocal {
|
||||||
|
|
|
@ -2,7 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind, Keyword, Stmt};
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
|
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
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 ruff_python_ast::types::Range;
|
||||||
|
|
||||||
use crate::autofix::helpers::remove_argument;
|
use crate::autofix::helpers::remove_argument;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
|
use rustpython_parser::ast::{Constant, Expr, ExprKind, Stmt, StmtKind};
|
||||||
|
|
||||||
|
use crate::binding::{BindingKind, Export};
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::scope::{BindingKind, Export, Scope};
|
use crate::scope::Scope;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
280
crates/ruff_python_ast/src/binding.rs
Normal file
280
crates/ruff_python_ast/src/binding.rs
Normal file
|
@ -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<RefEquality<'a, Stmt>>,
|
||||||
|
/// 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<usize> for BindingId {
|
||||||
|
type Error = TryFromIntError;
|
||||||
|
|
||||||
|
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||||
|
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<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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for Bindings<'a> {
|
||||||
|
type Target = [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 From<BindingId> 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<usize>,
|
||||||
|
/// 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<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
}
|
|
@ -8,11 +8,12 @@ use smallvec::smallvec;
|
||||||
use ruff_python_stdlib::path::is_python_stub_file;
|
use ruff_python_stdlib::path::is_python_stub_file;
|
||||||
use ruff_python_stdlib::typing::TYPING_EXTENSIONS;
|
use ruff_python_stdlib::typing::TYPING_EXTENSIONS;
|
||||||
|
|
||||||
use crate::helpers::{collect_call_path, from_relative_import};
|
use crate::binding::{
|
||||||
use crate::scope::{
|
|
||||||
Binding, BindingId, BindingKind, Bindings, Exceptions, ExecutionContext, FromImportation,
|
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::types::{CallPath, RefEquality};
|
||||||
use crate::typing::AnnotationKind;
|
use crate::typing::AnnotationKind;
|
||||||
use crate::visibility::{module_visibility, Modifier, VisibleScope};
|
use crate::visibility::{module_visibility, Modifier, VisibleScope};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod all;
|
pub mod all;
|
||||||
|
pub mod binding;
|
||||||
pub mod branch_detection;
|
pub mod branch_detection;
|
||||||
pub mod cast;
|
pub mod cast;
|
||||||
pub mod comparable;
|
pub mod comparable;
|
||||||
|
|
|
@ -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::num::TryFromIntError;
|
||||||
use std::ops::{Deref, Index, IndexMut};
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Scope<'a> {
|
pub struct Scope<'a> {
|
||||||
pub id: ScopeId,
|
pub id: ScopeId,
|
||||||
|
@ -251,275 +252,3 @@ impl Default for ScopeStack {
|
||||||
Self(vec![ScopeId::global()])
|
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<RefEquality<'a, Stmt>>,
|
|
||||||
/// 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<BindingId> for usize {
|
|
||||||
fn from(value: BindingId) -> Self {
|
|
||||||
value.0 as usize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<usize> for BindingId {
|
|
||||||
type Error = TryFromIntError;
|
|
||||||
|
|
||||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
|
||||||
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<usize>,
|
|
||||||
/// 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<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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<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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Deref for Bindings<'a> {
|
|
||||||
type Target = [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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue