mirror of
				https://github.com/astral-sh/ruff.git
				synced 2025-11-03 21:24:29 +00:00 
			
		
		
		
	[ty] Avoid secondary tree traversal to get call expression for keyword arguments (#19429)
This commit is contained in:
		
							parent
							
								
									98d1811dd1
								
							
						
					
					
						commit
						93a9fabb26
					
				
					 2 changed files with 31 additions and 22 deletions
				
			
		| 
						 | 
					@ -112,6 +112,12 @@ impl<'a> CoveringNode<'a> {
 | 
				
			||||||
        Ok(self)
 | 
					        Ok(self)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns an iterator over the ancestor nodes, starting from the root
 | 
				
			||||||
 | 
					    /// and ending with the covering node.
 | 
				
			||||||
 | 
					    pub(crate) fn ancestors(&self) -> impl Iterator<Item = AnyNodeRef<'a>> + '_ {
 | 
				
			||||||
 | 
					        self.nodes.iter().copied()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Finds the index of the node that fully covers the range and
 | 
					    /// Finds the index of the node that fully covers the range and
 | 
				
			||||||
    /// fulfills the given predicate.
 | 
					    /// fulfills the given predicate.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ pub use crate::goto_type_definition::goto_type_definition;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::find_node::covering_node;
 | 
					use crate::find_node::covering_node;
 | 
				
			||||||
use crate::stub_mapping::StubMapper;
 | 
					use crate::stub_mapping::StubMapper;
 | 
				
			||||||
use ruff_db::parsed::{ParsedModuleRef, parsed_module};
 | 
					use ruff_db::parsed::ParsedModuleRef;
 | 
				
			||||||
use ruff_python_ast::{self as ast, AnyNodeRef};
 | 
					use ruff_python_ast::{self as ast, AnyNodeRef};
 | 
				
			||||||
use ruff_python_parser::TokenKind;
 | 
					use ruff_python_parser::TokenKind;
 | 
				
			||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
 | 
					use ruff_text_size::{Ranged, TextRange, TextSize};
 | 
				
			||||||
| 
						 | 
					@ -40,7 +40,10 @@ pub(crate) enum GotoTarget<'a> {
 | 
				
			||||||
    /// test(a = 1)
 | 
					    /// test(a = 1)
 | 
				
			||||||
    ///      ^
 | 
					    ///      ^
 | 
				
			||||||
    /// ```
 | 
					    /// ```
 | 
				
			||||||
    KeywordArgument(&'a ast::Keyword),
 | 
					    KeywordArgument {
 | 
				
			||||||
 | 
					        keyword: &'a ast::Keyword,
 | 
				
			||||||
 | 
					        call_expression: &'a ast::ExprCall,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Go to on the rest parameter of a pattern match
 | 
					    /// Go to on the rest parameter of a pattern match
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
| 
						 | 
					@ -117,10 +120,10 @@ impl GotoTarget<'_> {
 | 
				
			||||||
            GotoTarget::Parameter(parameter) => parameter.inferred_type(model),
 | 
					            GotoTarget::Parameter(parameter) => parameter.inferred_type(model),
 | 
				
			||||||
            GotoTarget::Alias(alias) => alias.inferred_type(model),
 | 
					            GotoTarget::Alias(alias) => alias.inferred_type(model),
 | 
				
			||||||
            GotoTarget::ExceptVariable(except) => except.inferred_type(model),
 | 
					            GotoTarget::ExceptVariable(except) => except.inferred_type(model),
 | 
				
			||||||
            GotoTarget::KeywordArgument(argument) => {
 | 
					            GotoTarget::KeywordArgument { keyword, .. } => {
 | 
				
			||||||
                // TODO: Pyright resolves the declared type of the matching parameter. This seems more accurate
 | 
					                // TODO: Pyright resolves the declared type of the matching parameter. This seems more accurate
 | 
				
			||||||
                // than using the inferred value.
 | 
					                // than using the inferred value.
 | 
				
			||||||
                argument.value.inferred_type(model)
 | 
					                keyword.value.inferred_type(model)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            // TODO: Support identifier targets
 | 
					            // TODO: Support identifier targets
 | 
				
			||||||
            GotoTarget::PatternMatchRest(_)
 | 
					            GotoTarget::PatternMatchRest(_)
 | 
				
			||||||
| 
						 | 
					@ -201,22 +204,13 @@ impl GotoTarget<'_> {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Handle keyword arguments in call expressions
 | 
					            // Handle keyword arguments in call expressions
 | 
				
			||||||
            GotoTarget::KeywordArgument(keyword) => {
 | 
					            GotoTarget::KeywordArgument {
 | 
				
			||||||
                // Find the call expression that contains this keyword
 | 
					                keyword,
 | 
				
			||||||
                let module = parsed_module(db, file).load(db);
 | 
					                call_expression,
 | 
				
			||||||
 | 
					            } => {
 | 
				
			||||||
                // Use the keyword's range to find the containing call expression
 | 
					                let definitions =
 | 
				
			||||||
                let covering_node = covering_node(module.syntax().into(), keyword.range())
 | 
					                    definitions_for_keyword_argument(db, file, keyword, call_expression);
 | 
				
			||||||
                    .find_first(|node| matches!(node, AnyNodeRef::ExprCall(_)))
 | 
					                definitions_to_navigation_targets(db, stub_mapper, definitions)
 | 
				
			||||||
                    .ok()?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if let AnyNodeRef::ExprCall(call_expr) = covering_node.node() {
 | 
					 | 
				
			||||||
                    let definitions =
 | 
					 | 
				
			||||||
                        definitions_for_keyword_argument(db, file, keyword, call_expr);
 | 
					 | 
				
			||||||
                    return definitions_to_navigation_targets(db, stub_mapper, definitions);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                None
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // TODO: Handle multi-part module names in import statements
 | 
					            // TODO: Handle multi-part module names in import statements
 | 
				
			||||||
| 
						 | 
					@ -237,7 +231,7 @@ impl Ranged for GotoTarget<'_> {
 | 
				
			||||||
            GotoTarget::Alias(alias) => alias.name.range,
 | 
					            GotoTarget::Alias(alias) => alias.name.range,
 | 
				
			||||||
            GotoTarget::ImportedModule(module) => module.module.as_ref().unwrap().range,
 | 
					            GotoTarget::ImportedModule(module) => module.module.as_ref().unwrap().range,
 | 
				
			||||||
            GotoTarget::ExceptVariable(except) => except.name.as_ref().unwrap().range,
 | 
					            GotoTarget::ExceptVariable(except) => except.name.as_ref().unwrap().range,
 | 
				
			||||||
            GotoTarget::KeywordArgument(keyword) => keyword.arg.as_ref().unwrap().range,
 | 
					            GotoTarget::KeywordArgument { keyword, .. } => keyword.arg.as_ref().unwrap().range,
 | 
				
			||||||
            GotoTarget::PatternMatchRest(rest) => rest.rest.as_ref().unwrap().range,
 | 
					            GotoTarget::PatternMatchRest(rest) => rest.rest.as_ref().unwrap().range,
 | 
				
			||||||
            GotoTarget::PatternKeywordArgument(keyword) => keyword.attr.range,
 | 
					            GotoTarget::PatternKeywordArgument(keyword) => keyword.attr.range,
 | 
				
			||||||
            GotoTarget::PatternMatchStarName(star) => star.name.as_ref().unwrap().range,
 | 
					            GotoTarget::PatternMatchStarName(star) => star.name.as_ref().unwrap().range,
 | 
				
			||||||
| 
						 | 
					@ -335,7 +329,16 @@ pub(crate) fn find_goto_target(
 | 
				
			||||||
            Some(AnyNodeRef::ExceptHandlerExceptHandler(handler)) => {
 | 
					            Some(AnyNodeRef::ExceptHandlerExceptHandler(handler)) => {
 | 
				
			||||||
                Some(GotoTarget::ExceptVariable(handler))
 | 
					                Some(GotoTarget::ExceptVariable(handler))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Some(AnyNodeRef::Keyword(keyword)) => Some(GotoTarget::KeywordArgument(keyword)),
 | 
					            Some(AnyNodeRef::Keyword(keyword)) => {
 | 
				
			||||||
 | 
					                // Find the containing call expression from the ancestor chain
 | 
				
			||||||
 | 
					                let call_expression = covering_node
 | 
				
			||||||
 | 
					                    .ancestors()
 | 
				
			||||||
 | 
					                    .find_map(ruff_python_ast::AnyNodeRef::expr_call)?;
 | 
				
			||||||
 | 
					                Some(GotoTarget::KeywordArgument {
 | 
				
			||||||
 | 
					                    keyword,
 | 
				
			||||||
 | 
					                    call_expression,
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            Some(AnyNodeRef::PatternMatchMapping(mapping)) => {
 | 
					            Some(AnyNodeRef::PatternMatchMapping(mapping)) => {
 | 
				
			||||||
                Some(GotoTarget::PatternMatchRest(mapping))
 | 
					                Some(GotoTarget::PatternMatchRest(mapping))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue