mirror of
https://github.com/astral-sh/ruff
synced 2026-01-11 00:24:13 -05:00
Implement goto-definition and find-references for global/nonlocal statements (#21616)
## Summary The implementation here is to just record the idents of these statements in `scopes_by_expression` (which already supported idents but only ones that happened to appear in expressions), so that `definitions_for_name` Just Works. goto-type (and therefore hover) notably does not work on these statements because the typechecker does not record info for them. I am tempted to just introduce `type_for_name` which runs `definitions_for_name` to find other expressions and queries the inferred type... but that's a bit whack because it won't be the computed type at the right point in the code. It probably wouldn't be particularly expensive to just compute/record the type at those nodes, as if they were a load, because global/nonlocal is so scarce? ## Test Plan Snapshot tests added/re-enabled.
This commit is contained in:
@@ -2255,6 +2255,8 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||
names,
|
||||
}) => {
|
||||
for name in names {
|
||||
self.scopes_by_expression
|
||||
.record_expression(name, self.current_scope());
|
||||
let symbol_id = self.add_symbol(name.id.clone());
|
||||
let symbol = self.current_place_table().symbol(symbol_id);
|
||||
// Check whether the variable has already been accessed in this scope.
|
||||
@@ -2290,6 +2292,8 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
||||
names,
|
||||
}) => {
|
||||
for name in names {
|
||||
self.scopes_by_expression
|
||||
.record_expression(name, self.current_scope());
|
||||
let symbol_id = self.add_symbol(name.id.clone());
|
||||
let symbol = self.current_place_table().symbol(symbol_id);
|
||||
// Check whether the variable has already been accessed in this scope.
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::{Db, DisplaySettings, HasType, NameKind, SemanticModel};
|
||||
use ruff_db::files::FileRange;
|
||||
use ruff_db::parsed::parsed_module;
|
||||
use ruff_python_ast::name::Name;
|
||||
use ruff_python_ast::{self as ast};
|
||||
use ruff_python_ast::{self as ast, AnyNodeRef};
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
@@ -515,7 +515,7 @@ pub fn definition_for_name<'db>(
|
||||
model: &SemanticModel<'db>,
|
||||
name: &ast::ExprName,
|
||||
) -> Option<Definition<'db>> {
|
||||
let definitions = definitions_for_name(model, name);
|
||||
let definitions = definitions_for_name(model, name.id.as_str(), name.into());
|
||||
|
||||
// Find the first valid definition and return its kind
|
||||
for declaration in definitions {
|
||||
@@ -531,15 +531,15 @@ pub fn definition_for_name<'db>(
|
||||
/// are resolved (recursively) to the original definitions or module files.
|
||||
pub fn definitions_for_name<'db>(
|
||||
model: &SemanticModel<'db>,
|
||||
name: &ast::ExprName,
|
||||
name_str: &str,
|
||||
node: AnyNodeRef<'_>,
|
||||
) -> Vec<ResolvedDefinition<'db>> {
|
||||
let db = model.db();
|
||||
let file = model.file();
|
||||
let index = semantic_index(db, file);
|
||||
let name_str = name.id.as_str();
|
||||
|
||||
// Get the scope for this name expression
|
||||
let Some(file_scope) = model.scope(name.into()) else {
|
||||
let Some(file_scope) = model.scope(node) else {
|
||||
return vec![];
|
||||
};
|
||||
|
||||
@@ -648,7 +648,8 @@ pub fn definitions_for_name<'db>(
|
||||
//
|
||||
// https://typing.python.org/en/latest/spec/special-types.html#special-cases-for-float-and-complex
|
||||
if matches!(name_str, "float" | "complex")
|
||||
&& let Some(union) = name.inferred_type(&SemanticModel::new(db, file)).as_union()
|
||||
&& let Some(expr) = node.expr_name()
|
||||
&& let Some(union) = expr.inferred_type(&SemanticModel::new(db, file)).as_union()
|
||||
&& is_float_or_complex_annotation(db, union, name_str)
|
||||
{
|
||||
return union
|
||||
|
||||
Reference in New Issue
Block a user