Repository: astral-sh/ruff
File: crates/ty_ide/src/hover.rs (Rust)

Code before:
use std::fmt::Formatter;
use ty_python_semantic::types::{KnownInstanceType, Type, TypeVarVariance};
use ty_python_semantic::{DisplaySettings, SemanticModel};

pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Hover<'_>>> {
    let parsed = parsed_module(db, file).load(db);
    let goto_target = find_goto_target(&parsed, offset)?;

    if let GotoTarget::Expression(expr) = goto_target {
        if expr.is_literal_expr() {
            return None;
        }
    }

    let model = SemanticModel::new(db, file);
    let ty = goto_target.inferred_type(&model);
    let docs = goto_target
        .get_definition_targets(
            file,
            db,
            ty_python_semantic::ImportAliasResolution::ResolveAliases,
        )
        .and_then(|definitions| definitions.docstring(db))
        .map(HoverContent::Docstring);

    // TODO: Render the symbol's signature instead of just its type.
    let mut contents = Vec::new();
    if let Some(ty) = ty {
        tracing::debug!("Inferred type of covering node is {}", ty.display(db));
        contents.push(match ty {
            Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => typevar
                .bind_pep695(db)
                .map_or(HoverContent::Type(ty, None), |typevar| {
                    HoverContent::Type(Type::TypeVar(typevar), Some(typevar.variance(db)))
                }),
            Type::TypeVar(typevar) => HoverContent::Type(ty, Some(typevar.variance(db))),
            _ => HoverContent::Type(ty, None),
        });
    }
    contents.extend(docs);

    if contents.is_empty() {
        return None;
    }

    Some(RangedValue {
        range: FileRange::new(file, goto_target.range()),
        value: Hover { contents },
    })
}


Review comment (suggestion):
Is there a reason we only apply overload simplification for hover mode? For example, TypeScript jumps to the matching overload if you use "go to definition," which matches my expectation. https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9mABFApgZygCgIYC5FggC2ARigE4CU+hpFA3AFCiSwLLpZ4HFnkA0iEvgzkYYAObVEo8RKYto8JKgw4avCoJIB+EVDGTp2MAE9EAb0aMAvtcaqsARgAMggESP3le0A

I'm surprised that Pylance doesn't seem to do that

Code after:
use std::fmt::Formatter;
use ty_python_semantic::types::{KnownInstanceType, Type, TypeVarVariance};
use ty_python_semantic::{DisplaySettings, SemanticModel};

pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Hover<'_>>> {
    let parsed = parsed_module(db, file).load(db);
    let goto_target = find_goto_target(&parsed, offset)?;

    if let GotoTarget::Expression(expr) = goto_target {
        if expr.is_literal_expr() {
            return None;
        }
    }

    let model = SemanticModel::new(db, file);
    let docs = goto_target
        .get_definition_targets(
            file,
            db,
            ty_python_semantic::ImportAliasResolution::ResolveAliases,
        )
        .and_then(|definitions| definitions.docstring(db))
        .map(HoverContent::Docstring);

    let mut contents = Vec::new();
    if let Some(signature) = goto_target.call_type_simplified_by_overloads(&model) {
        contents.push(HoverContent::Signature(signature));
    } else if let Some(ty) = goto_target.inferred_type(&model) {
        tracing::debug!("Inferred type of covering node is {}", ty.display(db));
        contents.push(match ty {
            Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => typevar
                .bind_pep695(db)
                .map_or(HoverContent::Type(ty, None), |typevar| {
                    HoverContent::Type(Type::TypeVar(typevar), Some(typevar.variance(db)))
                }),
            Type::TypeVar(typevar) => HoverContent::Type(ty, Some(typevar.variance(db))),
            _ => HoverContent::Type(ty, None),
        });
    }
    contents.extend(docs);

    if contents.is_empty() {
        return None;
    }

    Some(RangedValue {
        range: FileRange::new(file, goto_target.range()),
        value: Hover { contents },
    })
}