mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 22:01:18 +00:00
[ty] Added support for "go to definition" for attribute accesses and keyword arguments (#19417)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
This PR builds upon #19371. It addresses a few additional code review suggestions and adds support for attribute accesses (expressions of the form `x.y`) and keyword arguments within call expressions. --------- Co-authored-by: UnboundVariable <unbound@gmail.com>
This commit is contained in:
parent
630c7a3152
commit
360eb7005f
5 changed files with 603 additions and 64 deletions
|
@ -611,7 +611,107 @@ def another_helper():
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_builtin_type() {
|
||||
fn goto_declaration_instance_attribute() {
|
||||
let test = cursor_test(
|
||||
"
|
||||
class C:
|
||||
def __init__(self):
|
||||
self.x: int = 1
|
||||
|
||||
c = C()
|
||||
y = c.x<CURSOR>
|
||||
",
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:4:21
|
||||
|
|
||||
2 | class C:
|
||||
3 | def __init__(self):
|
||||
4 | self.x: int = 1
|
||||
| ^^^^^^
|
||||
5 |
|
||||
6 | c = C()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:7:17
|
||||
|
|
||||
6 | c = C()
|
||||
7 | y = c.x
|
||||
| ^^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_instance_attribute_no_annotation() {
|
||||
let test = cursor_test(
|
||||
"
|
||||
class C:
|
||||
def __init__(self):
|
||||
self.x = 1
|
||||
|
||||
c = C()
|
||||
y = c.x<CURSOR>
|
||||
",
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:4:21
|
||||
|
|
||||
2 | class C:
|
||||
3 | def __init__(self):
|
||||
4 | self.x = 1
|
||||
| ^^^^^^
|
||||
5 |
|
||||
6 | c = C()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:7:17
|
||||
|
|
||||
6 | c = C()
|
||||
7 | y = c.x
|
||||
| ^^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_method_call_to_definition() {
|
||||
let test = cursor_test(
|
||||
"
|
||||
class C:
|
||||
def foo(self):
|
||||
return 42
|
||||
|
||||
c = C()
|
||||
res = c.foo<CURSOR>()
|
||||
",
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:3:21
|
||||
|
|
||||
2 | class C:
|
||||
3 | def foo(self):
|
||||
| ^^^
|
||||
4 | return 42
|
||||
|
|
||||
info: Source
|
||||
--> main.py:7:19
|
||||
|
|
||||
6 | c = C()
|
||||
7 | res = c.foo()
|
||||
| ^^^^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_module_attribute() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
x: i<CURSOR>nt = 42
|
||||
|
@ -721,6 +821,152 @@ def function():
|
|||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_inherited_attribute() {
|
||||
let test = cursor_test(
|
||||
"
|
||||
class A:
|
||||
x = 10
|
||||
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
b = B()
|
||||
y = b.x<CURSOR>
|
||||
",
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:3:17
|
||||
|
|
||||
2 | class A:
|
||||
3 | x = 10
|
||||
| ^
|
||||
4 |
|
||||
5 | class B(A):
|
||||
|
|
||||
info: Source
|
||||
--> main.py:9:17
|
||||
|
|
||||
8 | b = B()
|
||||
9 | y = b.x
|
||||
| ^^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_property_getter_setter() {
|
||||
let test = cursor_test(
|
||||
"
|
||||
class C:
|
||||
def __init__(self):
|
||||
self._value = 0
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self._value
|
||||
|
||||
c = C()
|
||||
c.value<CURSOR> = 42
|
||||
",
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:7:21
|
||||
|
|
||||
6 | @property
|
||||
7 | def value(self):
|
||||
| ^^^^^
|
||||
8 | return self._value
|
||||
|
|
||||
info: Source
|
||||
--> main.py:11:13
|
||||
|
|
||||
10 | c = C()
|
||||
11 | c.value = 42
|
||||
| ^^^^^^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_function_doc_attribute() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
def my_function():
|
||||
"""This is a docstring."""
|
||||
return 42
|
||||
|
||||
doc = my_function.__doc<CURSOR>__
|
||||
"#,
|
||||
);
|
||||
|
||||
// Should navigate to the __doc__ property in the FunctionType class in typeshed
|
||||
let result = test.goto_declaration();
|
||||
|
||||
assert!(
|
||||
!result.contains("No goto target found"),
|
||||
"Should find builtin __doc__ attribute"
|
||||
);
|
||||
assert!(
|
||||
!result.contains("No declarations found"),
|
||||
"Should find builtin __doc__ declarations"
|
||||
);
|
||||
|
||||
// Should navigate to a typeshed file containing the __doc__ attribute
|
||||
assert!(
|
||||
result.contains("types.pyi") || result.contains("builtins.pyi"),
|
||||
"Should navigate to typeshed file with __doc__ definition"
|
||||
);
|
||||
assert!(
|
||||
result.contains("__doc__"),
|
||||
"Should find the __doc__ attribute definition"
|
||||
);
|
||||
assert!(
|
||||
result.contains("info[goto-declaration]: Declaration"),
|
||||
"Should be a goto-declaration result"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_protocol_instance_attribute() {
|
||||
let test = cursor_test(
|
||||
"
|
||||
from typing import Protocol
|
||||
|
||||
class Drawable(Protocol):
|
||||
def draw(self) -> None: ...
|
||||
name: str
|
||||
|
||||
def use_drawable(obj: Drawable):
|
||||
obj.na<CURSOR>me
|
||||
",
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:6:17
|
||||
|
|
||||
4 | class Drawable(Protocol):
|
||||
5 | def draw(self) -> None: ...
|
||||
6 | name: str
|
||||
| ^^^^
|
||||
7 |
|
||||
8 | def use_drawable(obj: Drawable):
|
||||
|
|
||||
info: Source
|
||||
--> main.py:9:17
|
||||
|
|
||||
8 | def use_drawable(obj: Drawable):
|
||||
9 | obj.name
|
||||
| ^^^^^^^^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_generic_method_class_type() {
|
||||
let test = cursor_test(
|
||||
|
@ -756,6 +1002,94 @@ class MyClass:
|
|||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_keyword_argument_simple() {
|
||||
let test = cursor_test(
|
||||
"
|
||||
def my_function(x, y, z=10):
|
||||
return x + y + z
|
||||
|
||||
result = my_function(1, y<CURSOR>=2, z=3)
|
||||
",
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_declaration(), @r"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:2:32
|
||||
|
|
||||
2 | def my_function(x, y, z=10):
|
||||
| ^
|
||||
3 | return x + y + z
|
||||
|
|
||||
info: Source
|
||||
--> main.py:5:37
|
||||
|
|
||||
3 | return x + y + z
|
||||
4 |
|
||||
5 | result = my_function(1, y=2, z=3)
|
||||
| ^
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_declaration_keyword_argument_overloaded() {
|
||||
let test = cursor_test(
|
||||
r#"
|
||||
from typing import overload
|
||||
|
||||
@overload
|
||||
def process(data: str, format: str) -> str: ...
|
||||
|
||||
@overload
|
||||
def process(data: int, format: int) -> int: ...
|
||||
|
||||
def process(data, format):
|
||||
return data
|
||||
|
||||
# Call the overloaded function
|
||||
result = process("hello", format<CURSOR>="json")
|
||||
"#,
|
||||
);
|
||||
|
||||
// Should navigate to the parameter in both matching overloads
|
||||
assert_snapshot!(test.goto_declaration(), @r#"
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:5:36
|
||||
|
|
||||
4 | @overload
|
||||
5 | def process(data: str, format: str) -> str: ...
|
||||
| ^^^^^^
|
||||
6 |
|
||||
7 | @overload
|
||||
|
|
||||
info: Source
|
||||
--> main.py:14:39
|
||||
|
|
||||
13 | # Call the overloaded function
|
||||
14 | result = process("hello", format="json")
|
||||
| ^^^^^^
|
||||
|
|
||||
|
||||
info[goto-declaration]: Declaration
|
||||
--> main.py:8:36
|
||||
|
|
||||
7 | @overload
|
||||
8 | def process(data: int, format: int) -> int: ...
|
||||
| ^^^^^^
|
||||
9 |
|
||||
10 | def process(data, format):
|
||||
|
|
||||
info: Source
|
||||
--> main.py:14:39
|
||||
|
|
||||
13 | # Call the overloaded function
|
||||
14 | result = process("hello", format="json")
|
||||
| ^^^^^^
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
impl CursorTest {
|
||||
fn goto_declaration(&self) -> String {
|
||||
let Some(targets) = goto_declaration(&self.db, self.cursor.file, self.cursor.offset)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue