ruff/crates/ty_ide/src/find_references.rs

1726 lines
42 KiB
Rust

use crate::goto::find_goto_target;
use crate::references::{ReferencesMode, references};
use crate::{Db, ReferenceTarget};
use ruff_db::files::File;
use ruff_text_size::TextSize;
use ty_python_semantic::SemanticModel;
/// Find all references to a symbol at the given position.
/// Search for references across all files in the project.
pub fn find_references(
db: &dyn Db,
file: File,
offset: TextSize,
include_declaration: bool,
) -> Option<Vec<ReferenceTarget>> {
let parsed = ruff_db::parsed::parsed_module(db, file);
let module = parsed.load(db);
let model = SemanticModel::new(db, file);
// Get the definitions for the symbol at the cursor position
let goto_target = find_goto_target(&model, &module, offset)?;
let mode = if include_declaration {
ReferencesMode::References
} else {
ReferencesMode::ReferencesSkipDeclaration
};
references(db, file, &goto_target, mode)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{CursorTest, IntoDiagnostic, cursor_test};
use insta::assert_snapshot;
use ruff_db::diagnostic::{Annotation, Diagnostic, DiagnosticId, LintName, Severity, Span};
use ruff_db::files::FileRange;
use ruff_text_size::Ranged;
impl CursorTest {
fn references(&self) -> String {
let Some(mut reference_results) =
find_references(&self.db, self.cursor.file, self.cursor.offset, true)
else {
return "No references found".to_string();
};
if reference_results.is_empty() {
return "No references found".to_string();
}
reference_results.sort_by_key(ReferenceTarget::file);
self.render_diagnostics(reference_results.into_iter().enumerate().map(
|(i, ref_item)| -> ReferenceResult {
ReferenceResult {
index: i,
file_range: FileRange::new(ref_item.file(), ref_item.range()),
}
},
))
}
}
struct ReferenceResult {
index: usize,
file_range: FileRange,
}
impl IntoDiagnostic for ReferenceResult {
fn into_diagnostic(self) -> Diagnostic {
let mut main = Diagnostic::new(
DiagnosticId::Lint(LintName::of("references")),
Severity::Info,
format!("Reference {}", self.index + 1),
);
main.annotate(Annotation::primary(
Span::from(self.file_range.file()).with_range(self.file_range.range()),
));
main
}
}
#[test]
fn parameter_references_in_function() {
let test = cursor_test(
"
def calculate_sum(<CURSOR>value: int) -> int:
doubled = value * 2
result = value + doubled
return value
# Call with keyword argument
result = calculate_sum(value=42)
",
);
assert_snapshot!(test.references(), @r###"
info[references]: Reference 1
--> main.py:2:19
|
2 | def calculate_sum(value: int) -> int:
| ^^^^^
3 | doubled = value * 2
4 | result = value + doubled
|
info[references]: Reference 2
--> main.py:3:15
|
2 | def calculate_sum(value: int) -> int:
3 | doubled = value * 2
| ^^^^^
4 | result = value + doubled
5 | return value
|
info[references]: Reference 3
--> main.py:4:14
|
2 | def calculate_sum(value: int) -> int:
3 | doubled = value * 2
4 | result = value + doubled
| ^^^^^
5 | return value
|
info[references]: Reference 4
--> main.py:5:12
|
3 | doubled = value * 2
4 | result = value + doubled
5 | return value
| ^^^^^
6 |
7 | # Call with keyword argument
|
info[references]: Reference 5
--> main.py:8:24
|
7 | # Call with keyword argument
8 | result = calculate_sum(value=42)
| ^^^^^
|
"###);
}
#[test]
fn nonlocal_variable_references() {
let test = cursor_test(
"
def outer_function():
coun<CURSOR>ter = 0
def increment():
nonlocal counter
counter += 1
return counter
def decrement():
nonlocal counter
counter -= 1
return counter
# Use counter in outer scope
initial = counter
increment()
decrement()
final = counter
return increment, decrement
",
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:3:5
|
2 | def outer_function():
3 | counter = 0
| ^^^^^^^
4 |
5 | def increment():
|
info[references]: Reference 2
--> main.py:6:18
|
5 | def increment():
6 | nonlocal counter
| ^^^^^^^
7 | counter += 1
8 | return counter
|
info[references]: Reference 3
--> main.py:7:9
|
5 | def increment():
6 | nonlocal counter
7 | counter += 1
| ^^^^^^^
8 | return counter
|
info[references]: Reference 4
--> main.py:8:16
|
6 | nonlocal counter
7 | counter += 1
8 | return counter
| ^^^^^^^
9 |
10 | def decrement():
|
info[references]: Reference 5
--> main.py:11:18
|
10 | def decrement():
11 | nonlocal counter
| ^^^^^^^
12 | counter -= 1
13 | return counter
|
info[references]: Reference 6
--> main.py:12:9
|
10 | def decrement():
11 | nonlocal counter
12 | counter -= 1
| ^^^^^^^
13 | return counter
|
info[references]: Reference 7
--> main.py:13:16
|
11 | nonlocal counter
12 | counter -= 1
13 | return counter
| ^^^^^^^
14 |
15 | # Use counter in outer scope
|
info[references]: Reference 8
--> main.py:16:15
|
15 | # Use counter in outer scope
16 | initial = counter
| ^^^^^^^
17 | increment()
18 | decrement()
|
info[references]: Reference 9
--> main.py:19:13
|
17 | increment()
18 | decrement()
19 | final = counter
| ^^^^^^^
20 |
21 | return increment, decrement
|
");
}
#[test]
fn global_variable_references() {
let test = cursor_test(
"
glo<CURSOR>bal_counter = 0
def increment_global():
global global_counter
global_counter += 1
return global_counter
def decrement_global():
global global_counter
global_counter -= 1
return global_counter
# Use global_counter at module level
initial_value = global_counter
increment_global()
decrement_global()
final_value = global_counter
",
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:2:1
|
2 | global_counter = 0
| ^^^^^^^^^^^^^^
3 |
4 | def increment_global():
|
info[references]: Reference 2
--> main.py:5:12
|
4 | def increment_global():
5 | global global_counter
| ^^^^^^^^^^^^^^
6 | global_counter += 1
7 | return global_counter
|
info[references]: Reference 3
--> main.py:6:5
|
4 | def increment_global():
5 | global global_counter
6 | global_counter += 1
| ^^^^^^^^^^^^^^
7 | return global_counter
|
info[references]: Reference 4
--> main.py:7:12
|
5 | global global_counter
6 | global_counter += 1
7 | return global_counter
| ^^^^^^^^^^^^^^
8 |
9 | def decrement_global():
|
info[references]: Reference 5
--> main.py:10:12
|
9 | def decrement_global():
10 | global global_counter
| ^^^^^^^^^^^^^^
11 | global_counter -= 1
12 | return global_counter
|
info[references]: Reference 6
--> main.py:11:5
|
9 | def decrement_global():
10 | global global_counter
11 | global_counter -= 1
| ^^^^^^^^^^^^^^
12 | return global_counter
|
info[references]: Reference 7
--> main.py:12:12
|
10 | global global_counter
11 | global_counter -= 1
12 | return global_counter
| ^^^^^^^^^^^^^^
13 |
14 | # Use global_counter at module level
|
info[references]: Reference 8
--> main.py:15:17
|
14 | # Use global_counter at module level
15 | initial_value = global_counter
| ^^^^^^^^^^^^^^
16 | increment_global()
17 | decrement_global()
|
info[references]: Reference 9
--> main.py:18:15
|
16 | increment_global()
17 | decrement_global()
18 | final_value = global_counter
| ^^^^^^^^^^^^^^
|
");
}
#[test]
fn except_handler_variable_references() {
let test = cursor_test(
"
try:
x = 1 / 0
except ZeroDivisionError as e<CURSOR>rr:
print(f'Error: {err}')
return err
try:
y = 2 / 0
except ValueError as err:
print(f'Different error: {err}')
",
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:4:29
|
2 | try:
3 | x = 1 / 0
4 | except ZeroDivisionError as err:
| ^^^
5 | print(f'Error: {err}')
6 | return err
|
info[references]: Reference 2
--> main.py:5:21
|
3 | x = 1 / 0
4 | except ZeroDivisionError as err:
5 | print(f'Error: {err}')
| ^^^
6 | return err
|
info[references]: Reference 3
--> main.py:6:12
|
4 | except ZeroDivisionError as err:
5 | print(f'Error: {err}')
6 | return err
| ^^^
7 |
8 | try:
|
info[references]: Reference 4
--> main.py:11:31
|
9 | y = 2 / 0
10 | except ValueError as err:
11 | print(f'Different error: {err}')
| ^^^
|
");
}
#[test]
fn pattern_match_as_references() {
let test = cursor_test(
"
match x:
case [a, b] as patter<CURSOR>n:
print(f'Matched: {pattern}')
return pattern
case _:
pass
",
);
assert_snapshot!(test.references(), @r###"
info[references]: Reference 1
--> main.py:3:20
|
2 | match x:
3 | case [a, b] as pattern:
| ^^^^^^^
4 | print(f'Matched: {pattern}')
5 | return pattern
|
info[references]: Reference 2
--> main.py:4:27
|
2 | match x:
3 | case [a, b] as pattern:
4 | print(f'Matched: {pattern}')
| ^^^^^^^
5 | return pattern
6 | case _:
|
info[references]: Reference 3
--> main.py:5:16
|
3 | case [a, b] as pattern:
4 | print(f'Matched: {pattern}')
5 | return pattern
| ^^^^^^^
6 | case _:
7 | pass
|
"###);
}
#[test]
fn pattern_match_mapping_rest_references() {
let test = cursor_test(
"
match data:
case {'a': a, 'b': b, **re<CURSOR>st}:
print(f'Rest data: {rest}')
process(rest)
return rest
",
);
assert_snapshot!(test.references(), @r###"
info[references]: Reference 1
--> main.py:3:29
|
2 | match data:
3 | case {'a': a, 'b': b, **rest}:
| ^^^^
4 | print(f'Rest data: {rest}')
5 | process(rest)
|
info[references]: Reference 2
--> main.py:4:29
|
2 | match data:
3 | case {'a': a, 'b': b, **rest}:
4 | print(f'Rest data: {rest}')
| ^^^^
5 | process(rest)
6 | return rest
|
info[references]: Reference 3
--> main.py:5:17
|
3 | case {'a': a, 'b': b, **rest}:
4 | print(f'Rest data: {rest}')
5 | process(rest)
| ^^^^
6 | return rest
|
info[references]: Reference 4
--> main.py:6:16
|
4 | print(f'Rest data: {rest}')
5 | process(rest)
6 | return rest
| ^^^^
|
"###);
}
#[test]
fn function_definition_references() {
let test = cursor_test(
"
def my_func<CURSOR>tion():
return 42
# Call the function multiple times
result1 = my_function()
result2 = my_function()
# Function passed as an argument
callback = my_function
# Function used in different contexts
print(my_function())
value = my_function
",
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:2:5
|
2 | def my_function():
| ^^^^^^^^^^^
3 | return 42
|
info[references]: Reference 2
--> main.py:6:11
|
5 | # Call the function multiple times
6 | result1 = my_function()
| ^^^^^^^^^^^
7 | result2 = my_function()
|
info[references]: Reference 3
--> main.py:7:11
|
5 | # Call the function multiple times
6 | result1 = my_function()
7 | result2 = my_function()
| ^^^^^^^^^^^
8 |
9 | # Function passed as an argument
|
info[references]: Reference 4
--> main.py:10:12
|
9 | # Function passed as an argument
10 | callback = my_function
| ^^^^^^^^^^^
11 |
12 | # Function used in different contexts
|
info[references]: Reference 5
--> main.py:13:7
|
12 | # Function used in different contexts
13 | print(my_function())
| ^^^^^^^^^^^
14 | value = my_function
|
info[references]: Reference 6
--> main.py:14:9
|
12 | # Function used in different contexts
13 | print(my_function())
14 | value = my_function
| ^^^^^^^^^^^
|
");
}
#[test]
fn class_definition_references() {
let test = cursor_test(
"
class My<CURSOR>Class:
def __init__(self):
pass
# Create instances
obj1 = MyClass()
obj2 = MyClass()
# Use in type annotations
def process(instance: MyClass) -> MyClass:
return instance
# Reference the class itself
cls = MyClass
",
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:2:7
|
2 | class MyClass:
| ^^^^^^^
3 | def __init__(self):
4 | pass
|
info[references]: Reference 2
--> main.py:7:8
|
6 | # Create instances
7 | obj1 = MyClass()
| ^^^^^^^
8 | obj2 = MyClass()
|
info[references]: Reference 3
--> main.py:8:8
|
6 | # Create instances
7 | obj1 = MyClass()
8 | obj2 = MyClass()
| ^^^^^^^
9 |
10 | # Use in type annotations
|
info[references]: Reference 4
--> main.py:11:23
|
10 | # Use in type annotations
11 | def process(instance: MyClass) -> MyClass:
| ^^^^^^^
12 | return instance
|
info[references]: Reference 5
--> main.py:11:35
|
10 | # Use in type annotations
11 | def process(instance: MyClass) -> MyClass:
| ^^^^^^^
12 | return instance
|
info[references]: Reference 6
--> main.py:15:7
|
14 | # Reference the class itself
15 | cls = MyClass
| ^^^^^^^
|
");
}
#[test]
fn references_string_annotation1() {
let test = cursor_test(
r#"
a: "MyCla<CURSOR>ss" = 1
class MyClass:
"""some docs"""
"#,
);
assert_snapshot!(test.references(), @r#"
info[references]: Reference 1
--> main.py:2:5
|
2 | a: "MyClass" = 1
| ^^^^^^^
3 |
4 | class MyClass:
|
info[references]: Reference 2
--> main.py:4:7
|
2 | a: "MyClass" = 1
3 |
4 | class MyClass:
| ^^^^^^^
5 | """some docs"""
|
"#);
}
#[test]
fn references_string_annotation2() {
let test = cursor_test(
r#"
a: "None | MyCl<CURSOR>ass" = 1
class MyClass:
"""some docs"""
"#,
);
assert_snapshot!(test.references(), @r#"
info[references]: Reference 1
--> main.py:2:12
|
2 | a: "None | MyClass" = 1
| ^^^^^^^
3 |
4 | class MyClass:
|
info[references]: Reference 2
--> main.py:4:7
|
2 | a: "None | MyClass" = 1
3 |
4 | class MyClass:
| ^^^^^^^
5 | """some docs"""
|
"#);
}
#[test]
fn references_string_annotation3() {
let test = cursor_test(
r#"
a: "None |<CURSOR> MyClass" = 1
class MyClass:
"""some docs"""
"#,
);
assert_snapshot!(test.references(), @"No references found");
}
#[test]
fn references_string_annotation4() {
let test = cursor_test(
r#"
a: "None | MyClass<CURSOR>" = 1
class MyClass:
"""some docs"""
"#,
);
assert_snapshot!(test.references(), @r#"
info[references]: Reference 1
--> main.py:2:12
|
2 | a: "None | MyClass" = 1
| ^^^^^^^
3 |
4 | class MyClass:
|
info[references]: Reference 2
--> main.py:4:7
|
2 | a: "None | MyClass" = 1
3 |
4 | class MyClass:
| ^^^^^^^
5 | """some docs"""
|
"#);
}
#[test]
fn references_string_annotation5() {
let test = cursor_test(
r#"
a: "None | MyClass"<CURSOR> = 1
class MyClass:
"""some docs"""
"#,
);
assert_snapshot!(test.references(), @"No references found");
}
#[test]
fn references_string_annotation_dangling1() {
let test = cursor_test(
r#"
a: "MyCl<CURSOR>ass |" = 1
class MyClass:
"""some docs"""
"#,
);
assert_snapshot!(test.references(), @"No references found");
}
#[test]
fn references_string_annotation_dangling2() {
let test = cursor_test(
r#"
a: "MyCl<CURSOR>ass | No" = 1
class MyClass:
"""some docs"""
"#,
);
assert_snapshot!(test.references(), @r#"
info[references]: Reference 1
--> main.py:2:5
|
2 | a: "MyClass | No" = 1
| ^^^^^^^
3 |
4 | class MyClass:
|
info[references]: Reference 2
--> main.py:4:7
|
2 | a: "MyClass | No" = 1
3 |
4 | class MyClass:
| ^^^^^^^
5 | """some docs"""
|
"#);
}
#[test]
fn references_string_annotation_dangling3() {
let test = cursor_test(
r#"
a: "MyClass | N<CURSOR>o" = 1
class MyClass:
"""some docs"""
"#,
);
assert_snapshot!(test.references(), @"No references found");
}
#[test]
fn references_match_name_stmt() {
let test = cursor_test(
r#"
def my_func(command: str):
match command.split():
case ["get", a<CURSOR>b]:
x = ab
"#,
);
assert_snapshot!(test.references(), @r#"
info[references]: Reference 1
--> main.py:4:22
|
2 | def my_func(command: str):
3 | match command.split():
4 | case ["get", ab]:
| ^^
5 | x = ab
|
info[references]: Reference 2
--> main.py:5:17
|
3 | match command.split():
4 | case ["get", ab]:
5 | x = ab
| ^^
|
"#);
}
#[test]
fn references_match_name_binding() {
let test = cursor_test(
r#"
def my_func(command: str):
match command.split():
case ["get", ab]:
x = a<CURSOR>b
"#,
);
assert_snapshot!(test.references(), @r#"
info[references]: Reference 1
--> main.py:4:22
|
2 | def my_func(command: str):
3 | match command.split():
4 | case ["get", ab]:
| ^^
5 | x = ab
|
info[references]: Reference 2
--> main.py:5:17
|
3 | match command.split():
4 | case ["get", ab]:
5 | x = ab
| ^^
|
"#);
}
#[test]
fn references_match_rest_stmt() {
let test = cursor_test(
r#"
def my_func(command: str):
match command.split():
case ["get", *a<CURSOR>b]:
x = ab
"#,
);
assert_snapshot!(test.references(), @r#"
info[references]: Reference 1
--> main.py:4:23
|
2 | def my_func(command: str):
3 | match command.split():
4 | case ["get", *ab]:
| ^^
5 | x = ab
|
info[references]: Reference 2
--> main.py:5:17
|
3 | match command.split():
4 | case ["get", *ab]:
5 | x = ab
| ^^
|
"#);
}
#[test]
fn references_match_rest_binding() {
let test = cursor_test(
r#"
def my_func(command: str):
match command.split():
case ["get", *ab]:
x = a<CURSOR>b
"#,
);
assert_snapshot!(test.references(), @r#"
info[references]: Reference 1
--> main.py:4:23
|
2 | def my_func(command: str):
3 | match command.split():
4 | case ["get", *ab]:
| ^^
5 | x = ab
|
info[references]: Reference 2
--> main.py:5:17
|
3 | match command.split():
4 | case ["get", *ab]:
5 | x = ab
| ^^
|
"#);
}
#[test]
fn references_match_as_stmt() {
let test = cursor_test(
r#"
def my_func(command: str):
match command.split():
case ["get", ("a" | "b") as a<CURSOR>b]:
x = ab
"#,
);
assert_snapshot!(test.references(), @r#"
info[references]: Reference 1
--> main.py:4:37
|
2 | def my_func(command: str):
3 | match command.split():
4 | case ["get", ("a" | "b") as ab]:
| ^^
5 | x = ab
|
info[references]: Reference 2
--> main.py:5:17
|
3 | match command.split():
4 | case ["get", ("a" | "b") as ab]:
5 | x = ab
| ^^
|
"#);
}
#[test]
fn references_match_as_binding() {
let test = cursor_test(
r#"
def my_func(command: str):
match command.split():
case ["get", ("a" | "b") as ab]:
x = a<CURSOR>b
"#,
);
assert_snapshot!(test.references(), @r#"
info[references]: Reference 1
--> main.py:4:37
|
2 | def my_func(command: str):
3 | match command.split():
4 | case ["get", ("a" | "b") as ab]:
| ^^
5 | x = ab
|
info[references]: Reference 2
--> main.py:5:17
|
3 | match command.split():
4 | case ["get", ("a" | "b") as ab]:
5 | x = ab
| ^^
|
"#);
}
#[test]
fn references_match_keyword_stmt() {
let test = cursor_test(
r#"
class Click:
__match_args__ = ("position", "button")
def __init__(self, pos, btn):
self.position: int = pos
self.button: str = btn
def my_func(event: Click):
match event:
case Click(x, button=a<CURSOR>b):
x = ab
"#,
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:10:30
|
8 | def my_func(event: Click):
9 | match event:
10 | case Click(x, button=ab):
| ^^
11 | x = ab
|
info[references]: Reference 2
--> main.py:11:17
|
9 | match event:
10 | case Click(x, button=ab):
11 | x = ab
| ^^
|
");
}
#[test]
fn references_match_keyword_binding() {
let test = cursor_test(
r#"
class Click:
__match_args__ = ("position", "button")
def __init__(self, pos, btn):
self.position: int = pos
self.button: str = btn
def my_func(event: Click):
match event:
case Click(x, button=ab):
x = a<CURSOR>b
"#,
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:10:30
|
8 | def my_func(event: Click):
9 | match event:
10 | case Click(x, button=ab):
| ^^
11 | x = ab
|
info[references]: Reference 2
--> main.py:11:17
|
9 | match event:
10 | case Click(x, button=ab):
11 | x = ab
| ^^
|
");
}
#[test]
fn references_match_class_name() {
let test = cursor_test(
r#"
class Click:
__match_args__ = ("position", "button")
def __init__(self, pos, btn):
self.position: int = pos
self.button: str = btn
def my_func(event: Click):
match event:
case Cl<CURSOR>ick(x, button=ab):
x = ab
"#,
);
assert_snapshot!(test.references(), @r#"
info[references]: Reference 1
--> main.py:2:7
|
2 | class Click:
| ^^^^^
3 | __match_args__ = ("position", "button")
4 | def __init__(self, pos, btn):
|
info[references]: Reference 2
--> main.py:8:20
|
6 | self.button: str = btn
7 |
8 | def my_func(event: Click):
| ^^^^^
9 | match event:
10 | case Click(x, button=ab):
|
info[references]: Reference 3
--> main.py:10:14
|
8 | def my_func(event: Click):
9 | match event:
10 | case Click(x, button=ab):
| ^^^^^
11 | x = ab
|
"#);
}
#[test]
fn references_match_class_field_name() {
let test = cursor_test(
r#"
class Click:
__match_args__ = ("position", "button")
def __init__(self, pos, btn):
self.position: int = pos
self.button: str = btn
def my_func(event: Click):
match event:
case Click(x, but<CURSOR>ton=ab):
x = ab
"#,
);
assert_snapshot!(test.references(), @"No references found");
}
#[test]
fn references_typevar_name_stmt() {
let test = cursor_test(
r#"
type Alias1[A<CURSOR>B: int = bool] = tuple[AB, list[AB]]
"#,
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:2:13
|
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
| ^^
|
info[references]: Reference 2
--> main.py:2:37
|
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
| ^^
|
info[references]: Reference 3
--> main.py:2:46
|
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
| ^^
|
");
}
#[test]
fn references_typevar_name_binding() {
let test = cursor_test(
r#"
type Alias1[AB: int = bool] = tuple[A<CURSOR>B, list[AB]]
"#,
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:2:13
|
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
| ^^
|
info[references]: Reference 2
--> main.py:2:37
|
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
| ^^
|
info[references]: Reference 3
--> main.py:2:46
|
2 | type Alias1[AB: int = bool] = tuple[AB, list[AB]]
| ^^
|
");
}
#[test]
fn references_typevar_spec_stmt() {
let test = cursor_test(
r#"
from typing import Callable
type Alias2[**A<CURSOR>B = [int, str]] = Callable[AB, tuple[AB]]
"#,
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:3:15
|
2 | from typing import Callable
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
| ^^
|
info[references]: Reference 2
--> main.py:3:43
|
2 | from typing import Callable
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
| ^^
|
info[references]: Reference 3
--> main.py:3:53
|
2 | from typing import Callable
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
| ^^
|
");
}
#[test]
fn references_typevar_spec_binding() {
let test = cursor_test(
r#"
from typing import Callable
type Alias2[**AB = [int, str]] = Callable[A<CURSOR>B, tuple[AB]]
"#,
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:3:15
|
2 | from typing import Callable
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
| ^^
|
info[references]: Reference 2
--> main.py:3:43
|
2 | from typing import Callable
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
| ^^
|
info[references]: Reference 3
--> main.py:3:53
|
2 | from typing import Callable
3 | type Alias2[**AB = [int, str]] = Callable[AB, tuple[AB]]
| ^^
|
");
}
#[test]
fn references_typevar_tuple_stmt() {
let test = cursor_test(
r#"
type Alias3[*A<CURSOR>B = ()] = tuple[tuple[*AB], tuple[*AB]]
"#,
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:2:14
|
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
| ^^
|
info[references]: Reference 2
--> main.py:2:38
|
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
| ^^
|
info[references]: Reference 3
--> main.py:2:50
|
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
| ^^
|
");
}
#[test]
fn references_typevar_tuple_binding() {
let test = cursor_test(
r#"
type Alias3[*AB = ()] = tuple[tuple[*A<CURSOR>B], tuple[*AB]]
"#,
);
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> main.py:2:14
|
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
| ^^
|
info[references]: Reference 2
--> main.py:2:38
|
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
| ^^
|
info[references]: Reference 3
--> main.py:2:50
|
2 | type Alias3[*AB = ()] = tuple[tuple[*AB], tuple[*AB]]
| ^^
|
");
}
#[test]
fn multi_file_function_references() {
let test = CursorTest::builder()
.source(
"utils.py",
"
def fun<CURSOR>c(x):
return x * 2
",
)
.source(
"module.py",
"
from utils import func
def process_data(data):
return func(data)
",
)
.source(
"app.py",
"
from utils import func
class DataProcessor:
def __init__(self):
self.multiplier = func
def process(self, value):
return func(value)
",
)
.build();
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> utils.py:2:5
|
2 | def func(x):
| ^^^^
3 | return x * 2
|
info[references]: Reference 2
--> module.py:2:19
|
2 | from utils import func
| ^^^^
3 |
4 | def process_data(data):
|
info[references]: Reference 3
--> module.py:5:12
|
4 | def process_data(data):
5 | return func(data)
| ^^^^
|
info[references]: Reference 4
--> app.py:2:19
|
2 | from utils import func
| ^^^^
3 |
4 | class DataProcessor:
|
info[references]: Reference 5
--> app.py:6:27
|
4 | class DataProcessor:
5 | def __init__(self):
6 | self.multiplier = func
| ^^^^
7 |
8 | def process(self, value):
|
info[references]: Reference 6
--> app.py:9:16
|
8 | def process(self, value):
9 | return func(value)
| ^^^^
|
");
}
#[test]
fn multi_file_class_attribute_references() {
let test = CursorTest::builder()
.source(
"models.py",
"
class MyModel:
a<CURSOR>ttr = 42
def get_attribute(self):
return MyModel.attr
",
)
.source(
"main.py",
"
from models import MyModel
def process_model():
model = MyModel()
value = model.attr
model.attr = 100
return model.attr
",
)
.build();
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> models.py:3:5
|
2 | class MyModel:
3 | attr = 42
| ^^^^
4 |
5 | def get_attribute(self):
|
info[references]: Reference 2
--> models.py:6:24
|
5 | def get_attribute(self):
6 | return MyModel.attr
| ^^^^
|
info[references]: Reference 3
--> main.py:6:19
|
4 | def process_model():
5 | model = MyModel()
6 | value = model.attr
| ^^^^
7 | model.attr = 100
8 | return model.attr
|
info[references]: Reference 4
--> main.py:7:11
|
5 | model = MyModel()
6 | value = model.attr
7 | model.attr = 100
| ^^^^
8 | return model.attr
|
info[references]: Reference 5
--> main.py:8:18
|
6 | value = model.attr
7 | model.attr = 100
8 | return model.attr
| ^^^^
|
");
}
#[test]
fn import_alias_references_should_not_resolve_to_original() {
let test = CursorTest::builder()
.source(
"original.py",
"
def func():
pass
func()
",
)
.source(
"importer.py",
"
from original import func as func_alias
func<CURSOR>_alias()
",
)
.build();
// When finding references to the alias, we should NOT find references
// to the original function in the original module
assert_snapshot!(test.references(), @r"
info[references]: Reference 1
--> importer.py:2:30
|
2 | from original import func as func_alias
| ^^^^^^^^^^
3 |
4 | func_alias()
|
info[references]: Reference 2
--> importer.py:4:1
|
2 | from original import func as func_alias
3 |
4 | func_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")
| ^^^^
|
"###);
}
}