mirror of
https://github.com/astral-sh/ruff.git
synced 2025-12-09 03:40:36 +00:00
[ty] add cycle handling for FunctionType::signature query (#17833)
This fixes cycle panics in several ecosystem projects (moved to `good.txt` in a following PR https://github.com/astral-sh/ruff/pull/17834 because our mypy-primer job doesn't handle it well if we move projects to `good.txt` in the same PR that fixes `ty` to handle them), as well as in the minimal case in the added mdtest. It also fixes a number of panicking fuzzer seeds. It doesn't appear to cause any regression in any ecosystem project or any fuzzer seed.
This commit is contained in:
parent
3f32446e16
commit
4850c187ea
3 changed files with 43 additions and 6 deletions
15
crates/ty_python_semantic/resources/mdtest/cycle.md
Normal file
15
crates/ty_python_semantic/resources/mdtest/cycle.md
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Cycles
|
||||||
|
|
||||||
|
## Function signature
|
||||||
|
|
||||||
|
Deferred annotations can result in cycles in resolving a function signature:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
# error: [invalid-type-form]
|
||||||
|
def f(x: f):
|
||||||
|
pass
|
||||||
|
|
||||||
|
reveal_type(f) # revealed: def f(x: Unknown) -> Unknown
|
||||||
|
```
|
||||||
|
|
@ -6495,6 +6495,11 @@ impl<'db> FunctionSignature<'db> {
|
||||||
pub(crate) fn iter(&self) -> Iter<Signature<'db>> {
|
pub(crate) fn iter(&self) -> Iter<Signature<'db>> {
|
||||||
self.as_slice().iter()
|
self.as_slice().iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the "bottom" signature (subtype of all fully-static signatures.)
|
||||||
|
pub(crate) fn bottom(db: &'db dyn Db) -> Self {
|
||||||
|
Self::Single(Signature::bottom(db))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> IntoIterator for &'db FunctionSignature<'db> {
|
impl<'db> IntoIterator for &'db FunctionSignature<'db> {
|
||||||
|
|
@ -6639,7 +6644,7 @@ impl<'db> FunctionType<'db> {
|
||||||
///
|
///
|
||||||
/// Were this not a salsa query, then the calling query
|
/// Were this not a salsa query, then the calling query
|
||||||
/// would depend on the function's AST and rerun for every change in that file.
|
/// would depend on the function's AST and rerun for every change in that file.
|
||||||
#[salsa::tracked(return_ref)]
|
#[salsa::tracked(return_ref, cycle_fn=signature_cycle_recover, cycle_initial=signature_cycle_initial)]
|
||||||
pub(crate) fn signature(self, db: &'db dyn Db) -> FunctionSignature<'db> {
|
pub(crate) fn signature(self, db: &'db dyn Db) -> FunctionSignature<'db> {
|
||||||
if let Some(overloaded) = self.to_overloaded(db) {
|
if let Some(overloaded) = self.to_overloaded(db) {
|
||||||
FunctionSignature::Overloaded(
|
FunctionSignature::Overloaded(
|
||||||
|
|
@ -6849,6 +6854,22 @@ impl<'db> FunctionType<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn signature_cycle_recover<'db>(
|
||||||
|
_db: &'db dyn Db,
|
||||||
|
_value: &FunctionSignature<'db>,
|
||||||
|
_count: u32,
|
||||||
|
_function: FunctionType<'db>,
|
||||||
|
) -> salsa::CycleRecoveryAction<FunctionSignature<'db>> {
|
||||||
|
salsa::CycleRecoveryAction::Iterate
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature_cycle_initial<'db>(
|
||||||
|
db: &'db dyn Db,
|
||||||
|
_function: FunctionType<'db>,
|
||||||
|
) -> FunctionSignature<'db> {
|
||||||
|
FunctionSignature::bottom(db)
|
||||||
|
}
|
||||||
|
|
||||||
/// Non-exhaustive enumeration of known functions (e.g. `builtins.reveal_type`, ...) that might
|
/// Non-exhaustive enumeration of known functions (e.g. `builtins.reveal_type`, ...) that might
|
||||||
/// have special behavior.
|
/// have special behavior.
|
||||||
#[derive(
|
#[derive(
|
||||||
|
|
@ -7060,10 +7081,7 @@ impl<'db> CallableType<'db> {
|
||||||
/// `(*args: object, **kwargs: object) -> Never`.
|
/// `(*args: object, **kwargs: object) -> Never`.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn bottom(db: &'db dyn Db) -> Type<'db> {
|
pub(crate) fn bottom(db: &'db dyn Db) -> Type<'db> {
|
||||||
Type::Callable(CallableType::single(
|
Type::Callable(CallableType::single(db, Signature::bottom(db)))
|
||||||
db,
|
|
||||||
Signature::new(Parameters::object(db), Some(Type::Never)),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a "normalized" version of this `Callable` type.
|
/// Return a "normalized" version of this `Callable` type.
|
||||||
|
|
|
||||||
|
|
@ -291,6 +291,11 @@ impl<'db> Signature<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the "bottom" signature, subtype of all other fully-static signatures.
|
||||||
|
pub(crate) fn bottom(db: &'db dyn Db) -> Self {
|
||||||
|
Self::new(Parameters::object(db), Some(Type::Never))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn normalized(&self, db: &'db dyn Db) -> Self {
|
pub(crate) fn normalized(&self, db: &'db dyn Db) -> Self {
|
||||||
Self {
|
Self {
|
||||||
generic_context: self.generic_context,
|
generic_context: self.generic_context,
|
||||||
|
|
@ -957,7 +962,6 @@ impl<'db> Parameters<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return parameters that represents `(*args: object, **kwargs: object)`.
|
/// Return parameters that represents `(*args: object, **kwargs: object)`.
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn object(db: &'db dyn Db) -> Self {
|
pub(crate) fn object(db: &'db dyn Db) -> Self {
|
||||||
Self {
|
Self {
|
||||||
value: vec![
|
value: vec![
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue