mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:45:24 +00:00

<!-- Thank you for contributing to Ruff! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? - Does this pull request include references to any relevant issues? --> ## Summary - Remove `Type::Unbound` - Handle (potential) unboundness as a concept orthogonal to the type system (see new `Symbol` type) - Improve existing and add new diagnostics related to (potential) unboundness closes #13671 ## Test Plan - Update existing markdown-based tests - Add new tests for added/modified functionality
92 lines
2.4 KiB
Rust
92 lines
2.4 KiB
Rust
use crate::{
|
|
types::{Type, UnionType},
|
|
Db,
|
|
};
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub(crate) enum Boundness {
|
|
Bound,
|
|
MayBeUnbound,
|
|
}
|
|
|
|
/// The result of a symbol lookup, which can either be a (possibly unbound) type
|
|
/// or a completely unbound symbol.
|
|
///
|
|
/// Consider this example:
|
|
/// ```py
|
|
/// bound = 1
|
|
///
|
|
/// if flag:
|
|
/// maybe_unbound = 2
|
|
/// ```
|
|
///
|
|
/// If we look up symbols in this scope, we would get the following results:
|
|
/// ```rs
|
|
/// bound: Symbol::Type(Type::IntLiteral(1), Boundness::Bound),
|
|
/// maybe_unbound: Symbol::Type(Type::IntLiteral(2), Boundness::MayBeUnbound),
|
|
/// non_existent: Symbol::Unbound,
|
|
/// ```
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub(crate) enum Symbol<'db> {
|
|
Type(Type<'db>, Boundness),
|
|
Unbound,
|
|
}
|
|
|
|
impl<'db> Symbol<'db> {
|
|
pub(crate) fn is_unbound(&self) -> bool {
|
|
matches!(self, Symbol::Unbound)
|
|
}
|
|
|
|
pub(crate) fn may_be_unbound(&self) -> bool {
|
|
match self {
|
|
Symbol::Type(_, Boundness::MayBeUnbound) | Symbol::Unbound => true,
|
|
Symbol::Type(_, Boundness::Bound) => false,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn unwrap_or(&self, other: Type<'db>) -> Type<'db> {
|
|
match self {
|
|
Symbol::Type(ty, _) => *ty,
|
|
Symbol::Unbound => other,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn unwrap_or_unknown(&self) -> Type<'db> {
|
|
self.unwrap_or(Type::Unknown)
|
|
}
|
|
|
|
pub(crate) fn as_type(&self) -> Option<Type<'db>> {
|
|
match self {
|
|
Symbol::Type(ty, _) => Some(*ty),
|
|
Symbol::Unbound => None,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[track_caller]
|
|
pub(crate) fn expect_type(self) -> Type<'db> {
|
|
self.as_type()
|
|
.expect("Expected a (possibly unbound) type, not an unbound symbol")
|
|
}
|
|
|
|
#[must_use]
|
|
pub(crate) fn replace_unbound_with(
|
|
self,
|
|
db: &'db dyn Db,
|
|
replacement: &Symbol<'db>,
|
|
) -> Symbol<'db> {
|
|
match replacement {
|
|
Symbol::Type(replacement, _) => Symbol::Type(
|
|
match self {
|
|
Symbol::Type(ty, Boundness::Bound) => ty,
|
|
Symbol::Type(ty, Boundness::MayBeUnbound) => {
|
|
UnionType::from_elements(db, [*replacement, ty])
|
|
}
|
|
Symbol::Unbound => *replacement,
|
|
},
|
|
Boundness::Bound,
|
|
),
|
|
Symbol::Unbound => self,
|
|
}
|
|
}
|
|
}
|