mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-24 05:26:23 +00:00
[red-knot] Fix more redundant-cast
false positives (#17119)
## Summary
There are quite a few places we infer `Todo` types currently, and some
of them are nested somewhat deeply in type expressions. These can cause
spurious issues for the new `redundant-cast` diagnostics. We fixed all
the false positives we saw in the mypy_primer report before merging
https://github.com/astral-sh/ruff/pull/17100, but I think there are
still lots of places where we'd emit false positives due to this check
-- we currently don't run on that many projects at all in our
mypy_primer check:
d0c8eaa092/.github/workflows/mypy_primer.yaml (L71)
This PR fixes some more false positives from this diagnostic by making
the `Type::contains_todo()` method more expansive.
## Test Plan
I added a regression test which causes us to emit a spurious diagnostic
on `main`, but does not with this PR.
This commit is contained in:
parent
a15404a5c1
commit
c74ba00219
2 changed files with 65 additions and 2 deletions
|
@ -38,3 +38,14 @@ def function_returning_any() -> Any:
|
|||
# error: [redundant-cast] "Value is already of type `Any`"
|
||||
cast(Any, function_returning_any())
|
||||
```
|
||||
|
||||
Complex type expressions (which may be unsupported) do not lead to spurious `[redundant-cast]`
|
||||
diagnostics.
|
||||
|
||||
```py
|
||||
from typing import Callable
|
||||
|
||||
def f(x: Callable[[dict[str, int]], None], y: tuple[dict[str, int]]):
|
||||
a = cast(Callable[[list[bytes]], None], x)
|
||||
b = cast(tuple[list[bytes]], y)
|
||||
```
|
||||
|
|
|
@ -321,8 +321,60 @@ impl<'db> Type<'db> {
|
|||
}
|
||||
|
||||
pub fn contains_todo(&self, db: &'db dyn Db) -> bool {
|
||||
self.is_todo()
|
||||
|| matches!(self, Type::Union(union) if union.elements(db).iter().any(Type::is_todo))
|
||||
match self {
|
||||
Self::Dynamic(DynamicType::Todo(_) | DynamicType::TodoProtocol) => true,
|
||||
|
||||
Self::AlwaysFalsy
|
||||
| Self::AlwaysTruthy
|
||||
| Self::Never
|
||||
| Self::BooleanLiteral(_)
|
||||
| Self::BytesLiteral(_)
|
||||
| Self::FunctionLiteral(_)
|
||||
| Self::Instance(_)
|
||||
| Self::ModuleLiteral(_)
|
||||
| Self::ClassLiteral(_)
|
||||
| Self::KnownInstance(_)
|
||||
| Self::StringLiteral(_)
|
||||
| Self::IntLiteral(_)
|
||||
| Self::LiteralString
|
||||
| Self::SliceLiteral(_)
|
||||
| Self::Dynamic(DynamicType::Unknown | DynamicType::Any)
|
||||
| Self::Callable(
|
||||
CallableType::BoundMethod(_)
|
||||
| CallableType::WrapperDescriptorDunderGet
|
||||
| CallableType::MethodWrapperDunderGet(_),
|
||||
) => false,
|
||||
|
||||
Self::Callable(CallableType::General(callable)) => {
|
||||
let signature = callable.signature(db);
|
||||
signature.parameters().iter().any(|param| {
|
||||
param
|
||||
.annotated_type()
|
||||
.is_some_and(|ty| ty.contains_todo(db))
|
||||
}) || signature.return_ty.is_some_and(|ty| ty.contains_todo(db))
|
||||
}
|
||||
|
||||
Self::SubclassOf(subclass_of) => match subclass_of.subclass_of() {
|
||||
ClassBase::Dynamic(DynamicType::Todo(_) | DynamicType::TodoProtocol) => true,
|
||||
ClassBase::Dynamic(DynamicType::Unknown | DynamicType::Any) => false,
|
||||
ClassBase::Class(_) => false,
|
||||
},
|
||||
|
||||
Self::Tuple(tuple) => tuple.elements(db).iter().any(|ty| ty.contains_todo(db)),
|
||||
|
||||
Self::Union(union) => union.elements(db).iter().any(|ty| ty.contains_todo(db)),
|
||||
|
||||
Self::Intersection(intersection) => {
|
||||
intersection
|
||||
.positive(db)
|
||||
.iter()
|
||||
.any(|ty| ty.contains_todo(db))
|
||||
|| intersection
|
||||
.negative(db)
|
||||
.iter()
|
||||
.any(|ty| ty.contains_todo(db))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn class_literal(class: Class<'db>) -> Self {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue