[ty] Fix hover to prefer expression nodes over identifiers

When hovering on an attribute name like 'value' in 'instance.value',
the minimal covering node is the Identifier, but we need the Attribute
expression to get the type. Update find_covering_node to track both
the minimal node and minimal expression, preferring the expression.

This fixes hover on attribute accesses. All hover tests now pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Douglas Creager 2025-10-08 15:54:27 -04:00
parent 6ddf729864
commit c08a2d6d65
1 changed files with 18 additions and 1 deletions

View File

@ -26,10 +26,12 @@ pub(crate) struct HoverOutput {
}
/// Find the AST node with minimal range that fully contains the given offset.
/// Returns the smallest expression node if possible, skipping over identifier-only nodes.
fn find_covering_node(root: AnyNodeRef<'_>, offset: TextSize) -> Option<AnyNodeRef<'_>> {
struct Visitor<'a> {
offset: TextSize,
minimal_node: Option<AnyNodeRef<'a>>,
minimal_expr: Option<AnyNodeRef<'a>>,
}
impl<'a> SourceOrderVisitor<'a> for Visitor<'a> {
@ -43,6 +45,18 @@ fn find_covering_node(root: AnyNodeRef<'_>, offset: TextSize) -> Option<AnyNodeR
} else {
self.minimal_node = Some(node);
}
// Also track the smallest expression node
if node.as_expr_ref().is_some() {
if let Some(current) = self.minimal_expr {
if node.range().len() < current.range().len() {
self.minimal_expr = Some(node);
}
} else {
self.minimal_expr = Some(node);
}
}
TraversalSignal::Traverse
} else {
TraversalSignal::Skip
@ -53,10 +67,13 @@ fn find_covering_node(root: AnyNodeRef<'_>, offset: TextSize) -> Option<AnyNodeR
let mut visitor = Visitor {
offset,
minimal_node: None,
minimal_expr: None,
};
root.visit_source_order(&mut visitor);
visitor.minimal_node
// Prefer the minimal expression node if we found one, otherwise return the minimal node
visitor.minimal_expr.or(visitor.minimal_node)
}
/// Get the inferred type at a given position in a file.