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

Code before:
                }
            }
            ty_python_semantic::ResolvedDefinition::Module(file) => {
                // For modules, navigate to the start of the file
                crate::NavigationTarget::new(file, TextRange::default())
            }
            ty_python_semantic::ResolvedDefinition::FileWithRange(file_range) => {
                // For file ranges, navigate to the specific range within the file
                crate::NavigationTarget::from(file_range)
            }
        })
        .collect()
}

/// If a function is a property setter or deleter (e.g., decorated with
/// `@my_property.setter`), return the definitions for the property getter.
/// This ensures that the setter/deleter function name is recognized as a
/// co-definition of the same logical symbol as the getter.
fn property_getter_definitions<'db>(
    function: &ast::StmtFunctionDef,
    model: &SemanticModel<'db>,
    alias_resolution: ImportAliasResolution,
) -> Option<Vec<ResolvedDefinition<'db>>> {
    for decorator in &function.decorator_list {
        if let Some(attr_expr) = decorator.expression.as_attribute_expr()
            && matches!(attr_expr.attr.as_str(), "setter" | "deleter")
        {
            return definitions_for_expression(model, (&*attr_expr.value).into(), alias_resolution);
        }
    }
    None
}

/// Shared helper to get definitions for an expr (that is presumably a name/attr)
fn definitions_for_expression<'db>(
    model: &SemanticModel<'db>,
    expression: ruff_python_ast::ExprRef<'_>,
    alias_resolution: ImportAliasResolution,
) -> Option<Vec<ResolvedDefinition<'db>>> {
    match expression {
        ast::ExprRef::Name(name) => Some(definitions_for_name(
            model,
            name.id.as_str(),
            expression.into(),
            alias_resolution,
        )),
        ast::ExprRef::Attribute(attribute) => Some(ty_python_semantic::definitions_for_attribute(
            model, attribute,
        )),
        _ => None,
    }

Review comment (suggestion):
I think if we want to make the "doesn't apply to non-properties" test pass for the right reason, then this code should check the type of the decorator, not do this syntactic check on the attribute name.

Code after:
                }
            }
            ty_python_semantic::ResolvedDefinition::Module(file) => {
                // For modules, navigate to the start of the file
                crate::NavigationTarget::new(file, TextRange::default())
            }
            ty_python_semantic::ResolvedDefinition::FileWithRange(file_range) => {
                // For file ranges, navigate to the specific range within the file
                crate::NavigationTarget::from(file_range)
            }
        })
        .collect()
}

/// If a function is a property setter or deleter (e.g., decorated with
/// `@my_property.setter`), return the definitions for the property getter.
/// This ensures that the setter/deleter function name is recognized as a
/// co-definition of the same logical symbol as the getter.
fn property_getter_definitions<'db>(
    function: &ast::StmtFunctionDef,
    model: &SemanticModel<'db>,
    alias_resolution: ImportAliasResolution,
) -> Option<Vec<ResolvedDefinition<'db>>> {
    for decorator in &function.decorator_list {
        if let Some(attr_expr) = decorator.expression.as_attribute_expr()
            && matches!(attr_expr.attr.as_str(), "setter" | "deleter")
            && matches!(
                attr_expr.value.inferred_type(model),
                Some(Type::PropertyInstance(_))
            )
        {
            return definitions_for_expression(model, (&*attr_expr.value).into(), alias_resolution);
        }
    }
    None
}

/// Shared helper to get definitions for an expr (that is presumably a name/attr)
fn definitions_for_expression<'db>(
    model: &SemanticModel<'db>,
    expression: ruff_python_ast::ExprRef<'_>,
    alias_resolution: ImportAliasResolution,
) -> Option<Vec<ResolvedDefinition<'db>>> {
    match expression {
        ast::ExprRef::Name(name) => Some(definitions_for_name(
            model,
            name.id.as_str(),
            expression.into(),
            alias_resolution,
        )),
        ast::ExprRef::Attribute(attribute) => Some(ty_python_semantic::definitions_for_attribute(