Skip to content

Commit 93a9fab

Browse files
authored
[ty] Avoid secondary tree traversal to get call expression for keyword arguments (#19429)
1 parent 98d1811 commit 93a9fab

File tree

2 files changed

+31
-22
lines changed

2 files changed

+31
-22
lines changed

crates/ty_ide/src/find_node.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ impl<'a> CoveringNode<'a> {
112112
Ok(self)
113113
}
114114

115+
/// Returns an iterator over the ancestor nodes, starting from the root
116+
/// and ending with the covering node.
117+
pub(crate) fn ancestors(&self) -> impl Iterator<Item = AnyNodeRef<'a>> + '_ {
118+
self.nodes.iter().copied()
119+
}
120+
115121
/// Finds the index of the node that fully covers the range and
116122
/// fulfills the given predicate.
117123
///

crates/ty_ide/src/goto.rs

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pub use crate::goto_type_definition::goto_type_definition;
44

55
use crate::find_node::covering_node;
66
use crate::stub_mapping::StubMapper;
7-
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
7+
use ruff_db::parsed::ParsedModuleRef;
88
use ruff_python_ast::{self as ast, AnyNodeRef};
99
use ruff_python_parser::TokenKind;
1010
use ruff_text_size::{Ranged, TextRange, TextSize};
@@ -40,7 +40,10 @@ pub(crate) enum GotoTarget<'a> {
4040
/// test(a = 1)
4141
/// ^
4242
/// ```
43-
KeywordArgument(&'a ast::Keyword),
43+
KeywordArgument {
44+
keyword: &'a ast::Keyword,
45+
call_expression: &'a ast::ExprCall,
46+
},
4447

4548
/// Go to on the rest parameter of a pattern match
4649
///
@@ -117,10 +120,10 @@ impl GotoTarget<'_> {
117120
GotoTarget::Parameter(parameter) => parameter.inferred_type(model),
118121
GotoTarget::Alias(alias) => alias.inferred_type(model),
119122
GotoTarget::ExceptVariable(except) => except.inferred_type(model),
120-
GotoTarget::KeywordArgument(argument) => {
123+
GotoTarget::KeywordArgument { keyword, .. } => {
121124
// TODO: Pyright resolves the declared type of the matching parameter. This seems more accurate
122125
// than using the inferred value.
123-
argument.value.inferred_type(model)
126+
keyword.value.inferred_type(model)
124127
}
125128
// TODO: Support identifier targets
126129
GotoTarget::PatternMatchRest(_)
@@ -201,22 +204,13 @@ impl GotoTarget<'_> {
201204
}
202205

203206
// Handle keyword arguments in call expressions
204-
GotoTarget::KeywordArgument(keyword) => {
205-
// Find the call expression that contains this keyword
206-
let module = parsed_module(db, file).load(db);
207-
208-
// Use the keyword's range to find the containing call expression
209-
let covering_node = covering_node(module.syntax().into(), keyword.range())
210-
.find_first(|node| matches!(node, AnyNodeRef::ExprCall(_)))
211-
.ok()?;
212-
213-
if let AnyNodeRef::ExprCall(call_expr) = covering_node.node() {
214-
let definitions =
215-
definitions_for_keyword_argument(db, file, keyword, call_expr);
216-
return definitions_to_navigation_targets(db, stub_mapper, definitions);
217-
}
218-
219-
None
207+
GotoTarget::KeywordArgument {
208+
keyword,
209+
call_expression,
210+
} => {
211+
let definitions =
212+
definitions_for_keyword_argument(db, file, keyword, call_expression);
213+
definitions_to_navigation_targets(db, stub_mapper, definitions)
220214
}
221215

222216
// TODO: Handle multi-part module names in import statements
@@ -237,7 +231,7 @@ impl Ranged for GotoTarget<'_> {
237231
GotoTarget::Alias(alias) => alias.name.range,
238232
GotoTarget::ImportedModule(module) => module.module.as_ref().unwrap().range,
239233
GotoTarget::ExceptVariable(except) => except.name.as_ref().unwrap().range,
240-
GotoTarget::KeywordArgument(keyword) => keyword.arg.as_ref().unwrap().range,
234+
GotoTarget::KeywordArgument { keyword, .. } => keyword.arg.as_ref().unwrap().range,
241235
GotoTarget::PatternMatchRest(rest) => rest.rest.as_ref().unwrap().range,
242236
GotoTarget::PatternKeywordArgument(keyword) => keyword.attr.range,
243237
GotoTarget::PatternMatchStarName(star) => star.name.as_ref().unwrap().range,
@@ -335,7 +329,16 @@ pub(crate) fn find_goto_target(
335329
Some(AnyNodeRef::ExceptHandlerExceptHandler(handler)) => {
336330
Some(GotoTarget::ExceptVariable(handler))
337331
}
338-
Some(AnyNodeRef::Keyword(keyword)) => Some(GotoTarget::KeywordArgument(keyword)),
332+
Some(AnyNodeRef::Keyword(keyword)) => {
333+
// Find the containing call expression from the ancestor chain
334+
let call_expression = covering_node
335+
.ancestors()
336+
.find_map(ruff_python_ast::AnyNodeRef::expr_call)?;
337+
Some(GotoTarget::KeywordArgument {
338+
keyword,
339+
call_expression,
340+
})
341+
}
339342
Some(AnyNodeRef::PatternMatchMapping(mapping)) => {
340343
Some(GotoTarget::PatternMatchRest(mapping))
341344
}

0 commit comments

Comments
 (0)