[ty] Fix find references for type defined in stub (#21732)

This commit is contained in:
Micha Reiser 2025-12-01 17:53:45 +01:00 committed by GitHub
parent b4f618e180
commit 3738ab1c46
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 73 additions and 10 deletions

View File

@ -1656,4 +1656,70 @@ func<CURSOR>_alias()
| |
"); ");
} }
#[test]
fn stub_target() {
let test = CursorTest::builder()
.source(
"path.pyi",
r#"
class Path:
def __init__(self, path: str): ...
"#,
)
.source(
"path.py",
r#"
class Path:
def __init__(self, path: str):
self.path = path
"#,
)
.source(
"importer.py",
r#"
from path import Path<CURSOR>
a: Path = Path("test")
"#,
)
.build();
assert_snapshot!(test.references(), @r###"
info[references]: Reference 1
--> path.pyi:2:7
|
2 | class Path:
| ^^^^
3 | def __init__(self, path: str): ...
|
info[references]: Reference 2
--> importer.py:2:18
|
2 | from path import Path
| ^^^^
3 |
4 | a: Path = Path("test")
|
info[references]: Reference 3
--> importer.py:4:4
|
2 | from path import Path
3 |
4 | a: Path = Path("test")
| ^^^^
|
info[references]: Reference 4
--> importer.py:4:11
|
2 | from path import Path
3 |
4 | a: Path = Path("test")
| ^^^^
|
"###);
}
} }

View File

@ -12,7 +12,7 @@
use crate::find_node::CoveringNode; use crate::find_node::CoveringNode;
use crate::goto::GotoTarget; use crate::goto::GotoTarget;
use crate::{Db, NavigationTarget, ReferenceKind, ReferenceTarget}; use crate::{Db, NavigationTargets, ReferenceKind, ReferenceTarget};
use ruff_db::files::File; use ruff_db::files::File;
use ruff_python_ast::{ use ruff_python_ast::{
self as ast, AnyNodeRef, self as ast, AnyNodeRef,
@ -49,10 +49,9 @@ pub(crate) fn references(
// When finding references, do not resolve any local aliases. // When finding references, do not resolve any local aliases.
let model = SemanticModel::new(db, file); let model = SemanticModel::new(db, file);
let target_definitions_nav = goto_target let target_definitions = goto_target
.get_definition_targets(&model, ImportAliasResolution::PreserveAliases)? .get_definition_targets(&model, ImportAliasResolution::PreserveAliases)?
.definition_targets(db)?; .declaration_targets(db)?;
let target_definitions: Vec<NavigationTarget> = target_definitions_nav.into_iter().collect();
// Extract the target text from the goto target for fast comparison // Extract the target text from the goto target for fast comparison
let target_text = goto_target.to_string()?; let target_text = goto_target.to_string()?;
@ -115,7 +114,7 @@ pub(crate) fn references(
fn references_for_file( fn references_for_file(
db: &dyn Db, db: &dyn Db,
file: File, file: File,
target_definitions: &[NavigationTarget], target_definitions: &NavigationTargets,
target_text: &str, target_text: &str,
mode: ReferencesMode, mode: ReferencesMode,
references: &mut Vec<ReferenceTarget>, references: &mut Vec<ReferenceTarget>,
@ -159,7 +158,7 @@ fn is_symbol_externally_visible(goto_target: &GotoTarget<'_>) -> bool {
struct LocalReferencesFinder<'a> { struct LocalReferencesFinder<'a> {
model: &'a SemanticModel<'a>, model: &'a SemanticModel<'a>,
tokens: &'a Tokens, tokens: &'a Tokens,
target_definitions: &'a [NavigationTarget], target_definitions: &'a NavigationTargets,
references: &'a mut Vec<ReferenceTarget>, references: &'a mut Vec<ReferenceTarget>,
mode: ReferencesMode, mode: ReferencesMode,
target_text: &'a str, target_text: &'a str,
@ -318,12 +317,10 @@ impl LocalReferencesFinder<'_> {
GotoTarget::from_covering_node(self.model, covering_node, offset, self.tokens) GotoTarget::from_covering_node(self.model, covering_node, offset, self.tokens)
{ {
// Get the definitions for this goto target // Get the definitions for this goto target
if let Some(current_definitions_nav) = goto_target if let Some(current_definitions) = goto_target
.get_definition_targets(self.model, ImportAliasResolution::PreserveAliases) .get_definition_targets(self.model, ImportAliasResolution::PreserveAliases)
.and_then(|definitions| definitions.declaration_targets(self.model.db())) .and_then(|definitions| definitions.declaration_targets(self.model.db()))
{ {
let current_definitions: Vec<NavigationTarget> =
current_definitions_nav.into_iter().collect();
// Check if any of the current definitions match our target definitions // Check if any of the current definitions match our target definitions
if self.navigation_targets_match(&current_definitions) { if self.navigation_targets_match(&current_definitions) {
// Determine if this is a read or write reference // Determine if this is a read or write reference
@ -337,7 +334,7 @@ impl LocalReferencesFinder<'_> {
} }
/// Check if `Vec<NavigationTarget>` match our target definitions /// Check if `Vec<NavigationTarget>` match our target definitions
fn navigation_targets_match(&self, current_targets: &[NavigationTarget]) -> bool { fn navigation_targets_match(&self, current_targets: &NavigationTargets) -> bool {
// Since we're comparing the same symbol, all definitions should be equivalent // Since we're comparing the same symbol, all definitions should be equivalent
// We only need to check against the first target definition // We only need to check against the first target definition
if let Some(first_target) = self.target_definitions.iter().next() { if let Some(first_target) = self.target_definitions.iter().next() {