mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-01 09:22:19 +00:00
[red-knot] Infer unary not operation for instances (#13827)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz (push) Blocked by required conditions
CI / Fuzz the parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz (push) Blocked by required conditions
CI / Fuzz the parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
Handle unary `not` on instances by calling the `__bool__` dunder. ## Test Plan Added a new test case with some examples from these resources: - https://docs.python.org/3/library/stdtypes.html#truth-value-testing - <https://docs.python.org/3/reference/datamodel.html#object.__len__> - <https://docs.python.org/3/reference/datamodel.html#object.__bool__> --------- Co-authored-by: Carl Meyer <carl@astral.sh>
This commit is contained in:
parent
77e8da7497
commit
924741cb11
3 changed files with 132 additions and 6 deletions
|
@ -1197,14 +1197,40 @@ impl<'db> Type<'db> {
|
|||
// TODO: see above
|
||||
Truthiness::Ambiguous
|
||||
}
|
||||
Type::Instance(InstanceType { class }) => {
|
||||
// TODO: lookup `__bool__` and `__len__` methods on the instance's class
|
||||
// More info in https://docs.python.org/3/library/stdtypes.html#truth-value-testing
|
||||
// For now, we only special-case some builtin classes
|
||||
instance_ty @ Type::Instance(InstanceType { class }) => {
|
||||
if class.is_known(db, KnownClass::NoneType) {
|
||||
Truthiness::AlwaysFalse
|
||||
} else {
|
||||
Truthiness::Ambiguous
|
||||
// We only check the `__bool__` method for truth testing, even though at
|
||||
// runtime there is a fallback to `__len__`, since `__bool__` takes precedence
|
||||
// and a subclass could add a `__bool__` method. We don't use
|
||||
// `Type::call_dunder` here because of the need to check for `__bool__ = bool`.
|
||||
|
||||
// Don't trust a maybe-unbound `__bool__` method.
|
||||
let Symbol::Type(bool_method, Boundness::Bound) =
|
||||
instance_ty.to_meta_type(db).member(db, "__bool__")
|
||||
else {
|
||||
return Truthiness::Ambiguous;
|
||||
};
|
||||
|
||||
// Check if the class has `__bool__ = bool` and avoid infinite recursion, since
|
||||
// `Type::call` on `bool` will call `Type::bool` on the argument.
|
||||
if bool_method
|
||||
.into_class_literal()
|
||||
.is_some_and(|ClassLiteralType { class }| {
|
||||
class.is_known(db, KnownClass::Bool)
|
||||
})
|
||||
{
|
||||
return Truthiness::Ambiguous;
|
||||
}
|
||||
|
||||
if let Some(Type::BooleanLiteral(bool_val)) =
|
||||
bool_method.call(db, &[*instance_ty]).return_ty(db)
|
||||
{
|
||||
bool_val.into()
|
||||
} else {
|
||||
Truthiness::Ambiguous
|
||||
}
|
||||
}
|
||||
}
|
||||
Type::KnownInstance(known_instance) => known_instance.bool(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue