refactor: Move scope and binding types to scope.rs (#3573)

This commit is contained in:
Micha Reiser 2023-03-17 17:31:33 +01:00 committed by GitHub
parent 92179e6369
commit dedf4cbdeb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 535 additions and 507 deletions

View file

@ -1,4 +1,3 @@
use std::ops::{Deref, Index, IndexMut};
use std::path::Path;
use nohash_hasher::{BuildNoHashHasher, IntMap};
@ -10,10 +9,11 @@ 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, Exceptions};
use crate::types::{
Binding, BindingId, BindingKind, CallPath, ExecutionContext, RefEquality, Scope, ScopeId,
ScopeKind,
use crate::scope::{
Binding, BindingId, BindingKind, Bindings, ExecutionContext, Scope, ScopeId, ScopeKind,
ScopeStack, Scopes,
};
use crate::types::{CallPath, RefEquality};
use crate::visibility::{module_visibility, Modifier, VisibleScope};
#[allow(clippy::struct_excessive_bools)]
@ -125,7 +125,7 @@ impl<'a> Context<'a> {
/// Return the current `Binding` for a given `name`.
pub fn find_binding(&self, member: &str) -> Option<&Binding> {
self.scopes()
.find_map(|scope| scope.bindings.get(member))
.find_map(|scope| scope.get(member))
.map(|index| &self.bindings[*index])
}
@ -319,132 +319,3 @@ impl<'a> Context<'a> {
}
}
}
/// The scopes of a program indexed by [`ScopeId`]
#[derive(Debug)]
pub struct Scopes<'a>(Vec<Scope<'a>>);
impl<'a> Scopes<'a> {
/// Returns a reference to the global scope
pub fn global(&self) -> &Scope<'a> {
&self[ScopeId::global()]
}
/// Returns a mutable reference to the global scope
pub fn global_mut(&mut self) -> &mut Scope<'a> {
&mut self[ScopeId::global()]
}
/// Pushes a new scope and returns its unique id
fn push_scope(&mut self, kind: ScopeKind<'a>) -> ScopeId {
let next_id = ScopeId::try_from(self.0.len()).unwrap();
self.0.push(Scope::local(next_id, kind));
next_id
}
}
impl Default for Scopes<'_> {
fn default() -> Self {
Self(vec![Scope::global(ScopeKind::Module)])
}
}
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)]
}
}
impl<'a> Deref for Scopes<'a> {
type Target = [Scope<'a>];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone)]
pub struct ScopeStack(Vec<ScopeId>);
impl ScopeStack {
/// Pushes a new scope on the stack
pub fn push(&mut self, id: ScopeId) {
self.0.push(id);
}
/// Pops the top most scope
pub fn pop(&mut self) -> Option<ScopeId> {
self.0.pop()
}
/// Returns the id of the top-most
pub fn top(&self) -> Option<ScopeId> {
self.0.last().copied()
}
/// Returns an iterator from the current scope to the top scope (reverse iterator)
pub fn iter(&self) -> std::iter::Rev<std::slice::Iter<ScopeId>> {
self.0.iter().rev()
}
}
impl Default for ScopeStack {
fn default() -> Self {
Self(vec![ScopeId::global()])
}
}
/// 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))
}
}

View file

@ -2,7 +2,7 @@ use rustpython_parser::ast::Expr;
use crate::context::Context;
use crate::helpers::{map_callable, to_call_path};
use crate::types::{Scope, ScopeKind};
use crate::scope::{Scope, ScopeKind};
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")];

View file

@ -15,8 +15,9 @@ use smallvec::{smallvec, SmallVec};
use crate::context::Context;
use crate::newlines::StrExt;
use crate::scope::{Binding, BindingKind};
use crate::source_code::{Generator, Indexer, Locator, Stylist};
use crate::types::{Binding, BindingKind, CallPath, Range};
use crate::types::{CallPath, Range};
use crate::visitor;
use crate::visitor::Visitor;

View file

@ -9,6 +9,7 @@ pub mod logging;
pub mod newlines;
pub mod operations;
pub mod relocate;
pub mod scope;
pub mod source_code;
pub mod str;
pub mod types;

View file

@ -5,7 +5,7 @@ use rustpython_parser::{lexer, Mode, Tok};
use crate::context::Context;
use crate::helpers::any_over_expr;
use crate::types::{BindingKind, Scope};
use crate::scope::{BindingKind, Scope};
use crate::visitor;
use crate::visitor::Visitor;
@ -95,7 +95,7 @@ pub fn extract_all_names(
// Grab the existing bound __all__ values.
if let StmtKind::AugAssign { .. } = &stmt.node {
if let Some(index) = scope.bindings.get("__all__") {
if let Some(index) = scope.get("__all__") {
if let BindingKind::Export(existing) = &ctx.bindings[*index].kind {
names.extend_from_slice(existing);
}

View file

@ -0,0 +1,424 @@
use crate::types::{Range, RefEquality};
use rustc_hash::FxHashMap;
use rustpython_parser::ast::{Arguments, Expr, Keyword, Stmt};
use std::num::TryFromIntError;
use std::ops::{Deref, Index, IndexMut};
#[derive(Debug)]
pub struct Scope<'a> {
pub id: ScopeId,
pub kind: ScopeKind<'a>,
pub import_starred: bool,
pub uses_locals: bool,
/// A map from bound name to binding index, for live bindings.
bindings: FxHashMap<&'a str, BindingId>,
/// A map from bound name to binding index, for bindings that were created
/// in the scope but rebound (and thus overridden) later on in the same
/// scope.
pub rebounds: FxHashMap<&'a str, Vec<BindingId>>,
}
impl<'a> Scope<'a> {
pub fn global() -> Self {
Scope::local(ScopeId::global(), ScopeKind::Module)
}
pub fn local(id: ScopeId, kind: ScopeKind<'a>) -> Self {
Scope {
id,
kind,
import_starred: false,
uses_locals: false,
bindings: FxHashMap::default(),
rebounds: FxHashMap::default(),
}
}
/// Returns the [id](BindingId) of the binding with the given name.
pub fn get(&self, name: &str) -> Option<&BindingId> {
self.bindings.get(name)
}
/// Adds a new binding with the given name to this scope.
pub fn add(&mut self, name: &'a str, id: BindingId) -> Option<BindingId> {
self.bindings.insert(name, id)
}
/// Returns `true` if this scope defines a binding with the given name.
pub fn defines(&self, name: &str) -> bool {
self.bindings.contains_key(name)
}
/// Removes the binding with the given name
pub fn remove(&mut self, name: &str) -> Option<BindingId> {
self.bindings.remove(name)
}
/// Returns the ids of all bindings defined in this scope.
pub fn binding_ids(&self) -> std::collections::hash_map::Values<&str, BindingId> {
self.bindings.values()
}
pub fn bindings(&self) -> std::collections::hash_map::Iter<&'a str, BindingId> {
self.bindings.iter()
}
}
/// Id uniquely identifying a scope in a program.
///
/// 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);
impl ScopeId {
/// Returns the ID for the global scope
#[inline]
pub const fn global() -> Self {
ScopeId(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
}
}
#[derive(Debug)]
pub enum ScopeKind<'a> {
Class(ClassDef<'a>),
Function(FunctionDef<'a>),
Generator,
Module,
Lambda(Lambda<'a>),
}
#[derive(Debug)]
pub struct FunctionDef<'a> {
// Properties derived from StmtKind::FunctionDef.
pub name: &'a str,
pub args: &'a Arguments,
pub body: &'a [Stmt],
pub decorator_list: &'a [Expr],
// pub returns: Option<&'a Expr>,
// pub type_comment: Option<&'a str>,
// Scope-specific properties.
// TODO(charlie): Create AsyncFunctionDef to mirror the AST.
pub async_: bool,
pub globals: FxHashMap<&'a str, &'a Stmt>,
}
#[derive(Debug)]
pub struct ClassDef<'a> {
// Properties derived from StmtKind::ClassDef.
pub name: &'a str,
pub bases: &'a [Expr],
pub keywords: &'a [Keyword],
// pub body: &'a [Stmt],
pub decorator_list: &'a [Expr],
// Scope-specific properties.
pub globals: FxHashMap<&'a str, &'a Stmt>,
}
#[derive(Debug)]
pub struct Lambda<'a> {
pub args: &'a Arguments,
pub body: &'a Expr,
}
/// The scopes of a program indexed by [`ScopeId`]
#[derive(Debug)]
pub struct Scopes<'a>(Vec<Scope<'a>>);
impl<'a> Scopes<'a> {
/// Returns a reference to the global scope
pub fn global(&self) -> &Scope<'a> {
&self[ScopeId::global()]
}
/// Returns a mutable reference to the global scope
pub fn global_mut(&mut self) -> &mut Scope<'a> {
&mut self[ScopeId::global()]
}
/// Pushes a new scope and returns its unique id
pub(crate) fn push_scope(&mut self, kind: ScopeKind<'a>) -> ScopeId {
let next_id = ScopeId::try_from(self.0.len()).unwrap();
self.0.push(Scope::local(next_id, kind));
next_id
}
}
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)]
}
}
impl<'a> Deref for Scopes<'a> {
type Target = [Scope<'a>];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone)]
pub struct ScopeStack(Vec<ScopeId>);
impl ScopeStack {
/// Pushes a new scope on the stack
pub fn push(&mut self, id: ScopeId) {
self.0.push(id);
}
/// Pops the top most scope
pub fn pop(&mut self) -> Option<ScopeId> {
self.0.pop()
}
/// Returns the id of the top-most
pub fn top(&self) -> Option<ScopeId> {
self.0.last().copied()
}
/// Returns an iterator from the current scope to the top scope (reverse iterator)
pub fn iter(&self) -> std::iter::Rev<std::slice::Iter<ScopeId>> {
self.0.iter().rev()
}
}
impl Default for ScopeStack {
fn default() -> Self {
Self(vec![ScopeId::global()])
}
}
#[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)>,
}
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::StarImportation(..)
| BindingKind::Importation(..)
| BindingKind::FromImportation(..)
| BindingKind::SubmoduleImportation(..)
)
}
pub fn redefines(&self, existing: &'a Binding) -> bool {
match &self.kind {
BindingKind::Importation(.., full_name) => {
if let BindingKind::SubmoduleImportation(.., existing) = &existing.kind {
return full_name == existing;
}
}
BindingKind::FromImportation(.., full_name) => {
if let BindingKind::SubmoduleImportation(.., existing) = &existing.kind {
return full_name == existing;
}
}
BindingKind::SubmoduleImportation(.., full_name) => match &existing.kind {
BindingKind::Importation(.., existing)
| BindingKind::SubmoduleImportation(.., existing) => {
return full_name == existing;
}
BindingKind::FromImportation(.., existing) => {
return full_name == existing;
}
_ => {}
},
BindingKind::Annotation => {
return false;
}
BindingKind::FutureImportation => {
return false;
}
BindingKind::StarImportation(..) => {
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 {}
// Pyflakes defines the following binding hierarchy (via inheritance):
// Binding
// ExportBinding
// Annotation
// Argument
// Assignment
// NamedExprAssignment
// Definition
// FunctionDefinition
// ClassDefinition
// Builtin
// Importation
// SubmoduleImportation
// ImportationFrom
// StarImportation
// FutureImportation
#[derive(Clone, Debug, is_macro::Is)]
pub enum BindingKind<'a> {
Annotation,
Argument,
Assignment,
Binding,
LoopVar,
Global,
Nonlocal,
Builtin,
ClassDefinition,
FunctionDefinition,
Export(Vec<String>),
FutureImportation,
StarImportation(Option<usize>, Option<String>),
Importation(&'a str, &'a str),
FromImportation(&'a str, String),
SubmoduleImportation(&'a str, &'a str),
}
/// 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))
}
}

View file

@ -1,8 +1,6 @@
use std::num::TryFromIntError;
use std::ops::Deref;
use rustc_hash::FxHashMap;
use rustpython_parser::ast::{Arguments, Expr, Keyword, Located, Location, Stmt};
use rustpython_parser::ast::{Expr, Located, Location, Stmt};
#[derive(Clone)]
pub enum Node<'a> {
@ -37,267 +35,6 @@ impl<T> From<&Box<Located<T>>> for Range {
}
}
#[derive(Debug)]
pub struct FunctionDef<'a> {
// Properties derived from StmtKind::FunctionDef.
pub name: &'a str,
pub args: &'a Arguments,
pub body: &'a [Stmt],
pub decorator_list: &'a [Expr],
// pub returns: Option<&'a Expr>,
// pub type_comment: Option<&'a str>,
// Scope-specific properties.
// TODO(charlie): Create AsyncFunctionDef to mirror the AST.
pub async_: bool,
pub globals: FxHashMap<&'a str, &'a Stmt>,
}
#[derive(Debug)]
pub struct ClassDef<'a> {
// Properties derived from StmtKind::ClassDef.
pub name: &'a str,
pub bases: &'a [Expr],
pub keywords: &'a [Keyword],
// pub body: &'a [Stmt],
pub decorator_list: &'a [Expr],
// Scope-specific properties.
pub globals: FxHashMap<&'a str, &'a Stmt>,
}
#[derive(Debug)]
pub struct Lambda<'a> {
pub args: &'a Arguments,
pub body: &'a Expr,
}
#[derive(Debug)]
pub enum ScopeKind<'a> {
Class(ClassDef<'a>),
Function(FunctionDef<'a>),
Generator,
Module,
Lambda(Lambda<'a>),
}
/// Id uniquely identifying a scope in a program.
///
/// 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);
impl ScopeId {
/// Returns the ID for the global scope
#[inline]
pub const fn global() -> Self {
ScopeId(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
}
}
#[derive(Debug)]
pub struct Scope<'a> {
pub id: ScopeId,
pub kind: ScopeKind<'a>,
pub import_starred: bool,
pub uses_locals: bool,
/// A map from bound name to binding index, for live bindings.
pub bindings: FxHashMap<&'a str, BindingId>,
/// A map from bound name to binding index, for bindings that were created
/// in the scope but rebound (and thus overridden) later on in the same
/// scope.
pub rebounds: FxHashMap<&'a str, Vec<BindingId>>,
}
impl<'a> Scope<'a> {
pub fn global(kind: ScopeKind<'a>) -> Self {
Self::local(ScopeId::global(), kind)
}
pub fn local(id: ScopeId, kind: ScopeKind<'a>) -> Self {
Scope {
id,
kind,
import_starred: false,
uses_locals: false,
bindings: FxHashMap::default(),
rebounds: FxHashMap::default(),
}
}
}
// Pyflakes defines the following binding hierarchy (via inheritance):
// Binding
// ExportBinding
// Annotation
// Argument
// Assignment
// NamedExprAssignment
// Definition
// FunctionDefinition
// ClassDefinition
// Builtin
// Importation
// SubmoduleImportation
// ImportationFrom
// StarImportation
// FutureImportation
#[derive(Clone, Debug, is_macro::Is)]
pub enum BindingKind<'a> {
Annotation,
Argument,
Assignment,
Binding,
LoopVar,
Global,
Nonlocal,
Builtin,
ClassDefinition,
FunctionDefinition,
Export(Vec<String>),
FutureImportation,
StarImportation(Option<usize>, Option<String>),
Importation(&'a str, &'a str),
FromImportation(&'a str, String),
SubmoduleImportation(&'a str, &'a str),
}
/// 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 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)>,
}
#[derive(Copy, Debug, Clone)]
pub enum ExecutionContext {
Runtime,
Typing,
}
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::StarImportation(..)
| BindingKind::Importation(..)
| BindingKind::FromImportation(..)
| BindingKind::SubmoduleImportation(..)
)
}
pub fn redefines(&self, existing: &'a Binding) -> bool {
match &self.kind {
BindingKind::Importation(.., full_name) => {
if let BindingKind::SubmoduleImportation(.., existing) = &existing.kind {
return full_name == existing;
}
}
BindingKind::FromImportation(.., full_name) => {
if let BindingKind::SubmoduleImportation(.., existing) = &existing.kind {
return full_name == existing;
}
}
BindingKind::SubmoduleImportation(.., full_name) => match &existing.kind {
BindingKind::Importation(.., existing)
| BindingKind::SubmoduleImportation(.., existing) => {
return full_name == existing;
}
BindingKind::FromImportation(.., existing) => {
return full_name == existing;
}
_ => {}
},
BindingKind::Annotation => {
return false;
}
BindingKind::FutureImportation => {
return false;
}
BindingKind::StarImportation(..) => {
return false;
}
_ => {}
}
existing.is_definition()
}
}
#[derive(Debug, Copy, Clone)]
pub struct RefEquality<'a, T>(pub &'a T);