mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 22:01:47 +00:00
[red knot] Minor follow-up tasks regarding singleton types (#13769)
## Summary - Do not treat empty tuples as singletons after discussion [1] - Improve comment regarding intersection types - Resolve unnecessary TODO in Markdown test [1] https://discuss.python.org/t/should-we-specify-in-the-language-reference-that-the-empty-tuple-is-a-singleton/67957 ## Test Plan —
This commit is contained in:
parent
fb1d1e3241
commit
b85be6297e
3 changed files with 20 additions and 23 deletions
|
@ -31,10 +31,9 @@ non-singleton class may occupy different addresses in memory even if
|
||||||
they compare equal.
|
they compare equal.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
x = [1]
|
x = 345
|
||||||
y = [1]
|
y = 345
|
||||||
|
|
||||||
if x is not y:
|
if x is not y:
|
||||||
# TODO: should include type parameter: list[int]
|
reveal_type(x) # revealed: Literal[345]
|
||||||
reveal_type(x) # revealed: list
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -467,7 +467,7 @@ impl<'db> Type<'db> {
|
||||||
///
|
///
|
||||||
/// Note: This function aims to have no false positives, but might return `false`
|
/// Note: This function aims to have no false positives, but might return `false`
|
||||||
/// for more complicated types that are actually singletons.
|
/// for more complicated types that are actually singletons.
|
||||||
pub(crate) fn is_singleton(self, db: &'db dyn Db) -> bool {
|
pub(crate) fn is_singleton(self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Type::Any
|
Type::Any
|
||||||
| Type::Never
|
| Type::Never
|
||||||
|
@ -485,14 +485,13 @@ impl<'db> Type<'db> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
Type::None | Type::BooleanLiteral(_) | Type::Function(..) | Type::Class(..) | Type::Module(..) => true,
|
Type::None | Type::BooleanLiteral(_) | Type::Function(..) | Type::Class(..) | Type::Module(..) => true,
|
||||||
Type::Tuple(tuple) => {
|
Type::Tuple(..) => {
|
||||||
// We deliberately deviate from the language specification [1] here and claim
|
// The empty tuple is a singleton on CPython and PyPy, but not on other Python
|
||||||
// that the empty tuple type is a singleton type. The reasoning is that `()`
|
// implementations such as GraalPy. Its *use* as a singleton is discouraged and
|
||||||
// is often used as a sentinel value in user code. Declaring the empty tuple to
|
// should not be relied on for type narrowing, so we do not treat it as one.
|
||||||
// be of singleton type allows us to narrow types in `is not ()` conditionals.
|
// See:
|
||||||
//
|
// https://docs.python.org/3/reference/expressions.html#parenthesized-forms
|
||||||
// [1] https://docs.python.org/3/reference/expressions.html#parenthesized-forms
|
false
|
||||||
tuple.elements(db).is_empty()
|
|
||||||
}
|
}
|
||||||
Type::Union(..) => {
|
Type::Union(..) => {
|
||||||
// A single-element union, where the sole element was a singleton, would itself
|
// A single-element union, where the sole element was a singleton, would itself
|
||||||
|
@ -501,13 +500,12 @@ impl<'db> Type<'db> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
Type::Intersection(..) => {
|
Type::Intersection(..) => {
|
||||||
// Intersection types are hard to analyze. The following types are technically
|
// Here, we assume that all intersection types that are singletons would have
|
||||||
// all singleton types, but it is not straightforward to compute this. Again,
|
// been reduced to a different form via [`IntersectionBuilder::build`] by now.
|
||||||
// we simply return false.
|
// For example:
|
||||||
//
|
//
|
||||||
// bool & ~Literal[False]`
|
// bool & ~Literal[False] = Literal[True]
|
||||||
// None & (None | int)
|
// None & (None | int) = None | None & int = None
|
||||||
// (A | B) & (B | C) with A, B, C disjunct and B a singleton
|
|
||||||
//
|
//
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -1682,23 +1680,23 @@ mod tests {
|
||||||
#[test_case(Ty::None)]
|
#[test_case(Ty::None)]
|
||||||
#[test_case(Ty::BoolLiteral(true))]
|
#[test_case(Ty::BoolLiteral(true))]
|
||||||
#[test_case(Ty::BoolLiteral(false))]
|
#[test_case(Ty::BoolLiteral(false))]
|
||||||
#[test_case(Ty::Tuple(vec![]))]
|
|
||||||
fn is_singleton(from: Ty) {
|
fn is_singleton(from: Ty) {
|
||||||
let db = setup_db();
|
let db = setup_db();
|
||||||
|
|
||||||
assert!(from.into_type(&db).is_singleton(&db));
|
assert!(from.into_type(&db).is_singleton());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case(Ty::Never)]
|
#[test_case(Ty::Never)]
|
||||||
#[test_case(Ty::IntLiteral(345))]
|
#[test_case(Ty::IntLiteral(345))]
|
||||||
#[test_case(Ty::BuiltinInstance("str"))]
|
#[test_case(Ty::BuiltinInstance("str"))]
|
||||||
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]))]
|
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]))]
|
||||||
|
#[test_case(Ty::Tuple(vec![]))]
|
||||||
#[test_case(Ty::Tuple(vec![Ty::None]))]
|
#[test_case(Ty::Tuple(vec![Ty::None]))]
|
||||||
#[test_case(Ty::Tuple(vec![Ty::None, Ty::BoolLiteral(true)]))]
|
#[test_case(Ty::Tuple(vec![Ty::None, Ty::BoolLiteral(true)]))]
|
||||||
fn is_not_singleton(from: Ty) {
|
fn is_not_singleton(from: Ty) {
|
||||||
let db = setup_db();
|
let db = setup_db();
|
||||||
|
|
||||||
assert!(!from.into_type(&db).is_singleton(&db));
|
assert!(!from.into_type(&db).is_singleton());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case(Ty::IntLiteral(1); "is_int_literal_truthy")]
|
#[test_case(Ty::IntLiteral(1); "is_int_literal_truthy")]
|
||||||
|
|
|
@ -157,7 +157,7 @@ impl<'db> NarrowingConstraintsBuilder<'db> {
|
||||||
let comp_ty = inference.expression_ty(comparator.scoped_ast_id(self.db, scope));
|
let comp_ty = inference.expression_ty(comparator.scoped_ast_id(self.db, scope));
|
||||||
match op {
|
match op {
|
||||||
ast::CmpOp::IsNot => {
|
ast::CmpOp::IsNot => {
|
||||||
if comp_ty.is_singleton(self.db) {
|
if comp_ty.is_singleton() {
|
||||||
let ty = IntersectionBuilder::new(self.db)
|
let ty = IntersectionBuilder::new(self.db)
|
||||||
.add_negative(comp_ty)
|
.add_negative(comp_ty)
|
||||||
.build();
|
.build();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue