Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,18 @@ def _(a: Foo):
reveal_type(a) # revealed: Foo & Bar
```

Type guard narrowing also works when the callee has a `Callable` type:

```py
from typing import Callable

def _(x: Foo | Bar, is_bar: Callable[[object], TypeIs[Bar]]):
if is_bar(x):
reveal_type(x) # revealed: Bar
else:
reveal_type(x) # revealed: Foo & ~Bar
```

For generics, we transform the argument passed into `TypeIs[]` from `X` to `Top[X]`. This helps
especially when using various functions from typeshed that are annotated as returning
`TypeIs[SomeCovariantGeneric[Any]]` to avoid false positives in other type checkers. For ty's
Expand Down
19 changes: 7 additions & 12 deletions crates/ty_python_semantic/src/types/narrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1363,20 +1363,15 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
) -> Option<NarrowingConstraints<'db>> {
let inference = infer_expression_types(self.db, expression, TypeContext::default());

if let Some(type_guard_call_constraints) =
self.evaluate_type_guard_call(inference, expr_call, is_positive)
{
return Some(type_guard_call_constraints);
}

let callable_ty = inference.expression_type(&*expr_call.func);

match callable_ty {
Type::FunctionLiteral(function_type)
if matches!(
function_type.known(self.db),
None | Some(KnownFunction::RevealType)
) =>
{
self.evaluate_type_guard_call(inference, expr_call, is_positive)
}
Type::BoundMethod(_) => {
self.evaluate_type_guard_call(inference, expr_call, is_positive)
}
// For the expression `len(E)`, we narrow the type based on whether len(E) is truthy
// (i.e., whether E is non-empty). We only narrow the parts of the type where we know
// `__bool__` and `__len__` are consistent (literals, tuples). Non-narrowable parts
Expand Down Expand Up @@ -1464,7 +1459,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
}

// Helper to evaluate TypeGuard/TypeIs narrowing for a call expression.
// Used for both direct function calls and bound method calls.
// This is based on the call expression's return type, so it applies to any callable type.
fn evaluate_type_guard_call(
&mut self,
inference: &ExpressionInference<'db>,
Expand Down