Repository: astral-sh/ruff
File: crates/ty_python_semantic/src/types/infer.rs (Rust)

Code before:
    definition: Definition<'db>,
) -> DefinitionInference<'db> {
    let file = definition.file(db);
    let module = parsed_module(db, file).load(db);
    let _span = tracing::trace_span!(
        "infer_definition_types",
        range = ?definition.kind(db).target_range(&module),
        ?file
    )
    .entered();

    let index = semantic_index(db, file);

    TypeInferenceBuilder::new(db, InferenceRegion::Definition(definition), index, &module)
        .finish_definition()
}

fn definition_cycle_recover<'db>(
    db: &'db dyn Db,
    _id: salsa::Id,
    _last_provisional_value: &DefinitionInference<'db>,
    _value: &DefinitionInference<'db>,
    count: u32,
    definition: Definition<'db>,
) -> salsa::CycleRecoveryAction<DefinitionInference<'db>> {
    if count == ITERATIONS_BEFORE_FALLBACK {
        salsa::CycleRecoveryAction::Fallback(DefinitionInference::cycle_fallback(
            definition.scope(db),
        ))
    } else {
        salsa::CycleRecoveryAction::Iterate
    }
}

fn definition_cycle_initial<'db>(
    db: &'db dyn Db,
    _id: salsa::Id,
    definition: Definition<'db>,
) -> DefinitionInference<'db> {
    DefinitionInference::cycle_initial(definition.scope(db))
}

/// Infer types for all deferred type expressions in a [`Definition`].
///
/// Deferred expressions are type expressions (annotations, base classes, aliases...) in a stub
/// file, or in a file with `from __future__ import annotations`, or stringified annotations.
#[salsa::tracked(returns(ref), cycle_initial=deferred_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn infer_deferred_types<'db>(
    db: &'db dyn Db,
    definition: Definition<'db>,
) -> DefinitionInference<'db> {

Review comment (bug):
It would be correcter for us to use `count < ITERATIONS_BEFORE_FALLBACK` but one existing mdtest fails if we do so (but it fixes a corpus test panic).

I decided not to spend any time on debugging this further, given that https://github.com/astral-sh/ruff/pull/20566 is about to solve this holistically

Code after:
    definition: Definition<'db>,
) -> DefinitionInference<'db> {
    let file = definition.file(db);
    let module = parsed_module(db, file).load(db);
    let _span = tracing::trace_span!(
        "infer_definition_types",
        range = ?definition.kind(db).target_range(&module),
        ?file
    )
    .entered();

    let index = semantic_index(db, file);

    TypeInferenceBuilder::new(db, InferenceRegion::Definition(definition), index, &module)
        .finish_definition()
}

fn definition_cycle_recover<'db>(
    db: &'db dyn Db,
    _id: salsa::Id,
    last_provisional_value: &DefinitionInference<'db>,
    value: DefinitionInference<'db>,
    count: u32,
    definition: Definition<'db>,
) -> DefinitionInference<'db> {
    if &value == last_provisional_value || count != ITERATIONS_BEFORE_FALLBACK {
        value
    } else {
        DefinitionInference::cycle_fallback(definition.scope(db))
    }
}

fn definition_cycle_initial<'db>(
    db: &'db dyn Db,
    _id: salsa::Id,
    definition: Definition<'db>,
) -> DefinitionInference<'db> {
    DefinitionInference::cycle_initial(definition.scope(db))
}

/// Infer types for all deferred type expressions in a [`Definition`].
///
/// Deferred expressions are type expressions (annotations, base classes, aliases...) in a stub
/// file, or in a file with `from __future__ import annotations`, or stringified annotations.
#[salsa::tracked(returns(ref), cycle_initial=deferred_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn infer_deferred_types<'db>(
    db: &'db dyn Db,
    definition: Definition<'db>,
) -> DefinitionInference<'db> {
    let file = definition.file(db);
    let module = parsed_module(db, file).load(db);