[ty] add more lsp tests for overloads (#20148)

I decided to split out the addition of these tests from other PRs so
that it's easier to follow changes to the LSP's function call handling.
I'm not particularly concerned with whether the results produced by
these tests are "good" or "bad" in this PR, I'm just establishing a
baseline.
This commit is contained in:
Aria Desires 2025-09-02 10:38:26 -04:00 committed by GitHub
parent bbfcf6e111
commit f40a0b3800
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 1656 additions and 2 deletions

View file

@ -1355,6 +1355,462 @@ class MyClass:
"#); "#);
} }
#[test]
fn goto_declaration_overload_type_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1)
",
)
.source(
"mymodule.py",
r#"
def ab(a):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int): ...
@overload
def ab(a: str): ...
"#,
)
.build();
assert_snapshot!(test.goto_declaration(), @r"
info[goto-declaration]: Declaration
--> mymodule.pyi:5:5
|
4 | @overload
5 | def ab(a: int): ...
| ^^
6 |
7 | @overload
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1)
| ^^
|
info[goto-declaration]: Declaration
--> mymodule.pyi:8:5
|
7 | @overload
8 | def ab(a: str): ...
| ^^
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1)
| ^^
|
");
}
#[test]
fn goto_declaration_overload_type_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
r#"
from mymodule import ab
a<CURSOR>b("hello")
"#,
)
.source(
"mymodule.py",
r#"
def ab(a):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int): ...
@overload
def ab(a: str): ...
"#,
)
.build();
assert_snapshot!(test.goto_declaration(), @r#"
info[goto-declaration]: Declaration
--> mymodule.pyi:5:5
|
4 | @overload
5 | def ab(a: int): ...
| ^^
6 |
7 | @overload
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab("hello")
| ^^
|
info[goto-declaration]: Declaration
--> mymodule.pyi:8:5
|
7 | @overload
8 | def ab(a: str): ...
| ^^
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab("hello")
| ^^
|
"#);
}
#[test]
fn goto_declaration_overload_arity_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1, 2)
",
)
.source(
"mymodule.py",
r#"
def ab(a, b = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int, b: int): ...
@overload
def ab(a: int): ...
"#,
)
.build();
assert_snapshot!(test.goto_declaration(), @r"
info[goto-declaration]: Declaration
--> mymodule.pyi:5:5
|
4 | @overload
5 | def ab(a: int, b: int): ...
| ^^
6 |
7 | @overload
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, 2)
| ^^
|
info[goto-declaration]: Declaration
--> mymodule.pyi:8:5
|
7 | @overload
8 | def ab(a: int): ...
| ^^
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, 2)
| ^^
|
");
}
#[test]
fn goto_declaration_overload_arity_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1)
",
)
.source(
"mymodule.py",
r#"
def ab(a, b = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int, b: int): ...
@overload
def ab(a: int): ...
"#,
)
.build();
assert_snapshot!(test.goto_declaration(), @r"
info[goto-declaration]: Declaration
--> mymodule.pyi:5:5
|
4 | @overload
5 | def ab(a: int, b: int): ...
| ^^
6 |
7 | @overload
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1)
| ^^
|
info[goto-declaration]: Declaration
--> mymodule.pyi:8:5
|
7 | @overload
8 | def ab(a: int): ...
| ^^
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1)
| ^^
|
");
}
#[test]
fn goto_declaration_overload_keyword_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1, b=2)
",
)
.source(
"mymodule.py",
r#"
def ab(a, *, b = None, c = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int): ...
@overload
def ab(a: int, *, b: int): ...
@overload
def ab(a: int, *, c: int): ...
"#,
)
.build();
assert_snapshot!(test.goto_declaration(), @r"
info[goto-declaration]: Declaration
--> mymodule.pyi:5:5
|
4 | @overload
5 | def ab(a: int): ...
| ^^
6 |
7 | @overload
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, b=2)
| ^^
|
info[goto-declaration]: Declaration
--> mymodule.pyi:8:5
|
7 | @overload
8 | def ab(a: int, *, b: int): ...
| ^^
9 |
10 | @overload
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, b=2)
| ^^
|
info[goto-declaration]: Declaration
--> mymodule.pyi:11:5
|
10 | @overload
11 | def ab(a: int, *, c: int): ...
| ^^
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, b=2)
| ^^
|
");
}
#[test]
fn goto_declaration_overload_keyword_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1, c=2)
",
)
.source(
"mymodule.py",
r#"
def ab(a, *, b = None, c = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int): ...
@overload
def ab(a: int, *, b: int): ...
@overload
def ab(a: int, *, c: int): ...
"#,
)
.build();
assert_snapshot!(test.goto_declaration(), @r"
info[goto-declaration]: Declaration
--> mymodule.pyi:5:5
|
4 | @overload
5 | def ab(a: int): ...
| ^^
6 |
7 | @overload
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, c=2)
| ^^
|
info[goto-declaration]: Declaration
--> mymodule.pyi:8:5
|
7 | @overload
8 | def ab(a: int, *, b: int): ...
| ^^
9 |
10 | @overload
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, c=2)
| ^^
|
info[goto-declaration]: Declaration
--> mymodule.pyi:11:5
|
10 | @overload
11 | def ab(a: int, *, c: int): ...
| ^^
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, c=2)
| ^^
|
");
}
impl CursorTest { impl CursorTest {
fn goto_declaration(&self) -> String { fn goto_declaration(&self) -> String {
let Some(targets) = goto_declaration(&self.db, self.cursor.file, self.cursor.offset) let Some(targets) = goto_declaration(&self.db, self.cursor.file, self.cursor.offset)

View file

@ -802,6 +802,318 @@ my_func(my_other_func(a<CURSOR>b=5, y=2), 0)
} }
} }
#[test]
fn goto_definition_overload_type_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1)
",
)
.source(
"mymodule.py",
r#"
def ab(a):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int): ...
@overload
def ab(a: str): ...
"#,
)
.build();
assert_snapshot!(test.goto_definition(), @r#"
info[goto-definition]: Definition
--> mymodule.py:2:5
|
2 | def ab(a):
| ^^
3 | """the real implementation!"""
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1)
| ^^
|
"#);
}
#[test]
fn goto_definition_overload_type_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
r#"
from mymodule import ab
a<CURSOR>b("hello")
"#,
)
.source(
"mymodule.py",
r#"
def ab(a):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int): ...
@overload
def ab(a: str): ...
"#,
)
.build();
assert_snapshot!(test.goto_definition(), @r#"
info[goto-definition]: Definition
--> mymodule.py:2:5
|
2 | def ab(a):
| ^^
3 | """the real implementation!"""
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab("hello")
| ^^
|
"#);
}
#[test]
fn goto_definition_overload_arity_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1, 2)
",
)
.source(
"mymodule.py",
r#"
def ab(a, b = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int, b: int): ...
@overload
def ab(a: int): ...
"#,
)
.build();
assert_snapshot!(test.goto_definition(), @r#"
info[goto-definition]: Definition
--> mymodule.py:2:5
|
2 | def ab(a, b = None):
| ^^
3 | """the real implementation!"""
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, 2)
| ^^
|
"#);
}
#[test]
fn goto_definition_overload_arity_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1)
",
)
.source(
"mymodule.py",
r#"
def ab(a, b = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int, b: int): ...
@overload
def ab(a: int): ...
"#,
)
.build();
assert_snapshot!(test.goto_definition(), @r#"
info[goto-definition]: Definition
--> mymodule.py:2:5
|
2 | def ab(a, b = None):
| ^^
3 | """the real implementation!"""
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1)
| ^^
|
"#);
}
#[test]
fn goto_definition_overload_keyword_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1, b=2)
",
)
.source(
"mymodule.py",
r#"
def ab(a, *, b = None, c = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int): ...
@overload
def ab(a: int, *, b: int): ...
@overload
def ab(a: int, *, c: int): ...
"#,
)
.build();
assert_snapshot!(test.goto_definition(), @r#"
info[goto-definition]: Definition
--> mymodule.py:2:5
|
2 | def ab(a, *, b = None, c = None):
| ^^
3 | """the real implementation!"""
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, b=2)
| ^^
|
"#);
}
#[test]
fn goto_definition_overload_keyword_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1, c=2)
",
)
.source(
"mymodule.py",
r#"
def ab(a, *, b = None, c = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int): ...
@overload
def ab(a: int, *, b: int): ...
@overload
def ab(a: int, *, c: int): ...
"#,
)
.build();
assert_snapshot!(test.goto_definition(), @r#"
info[goto-definition]: Definition
--> mymodule.py:2:5
|
2 | def ab(a, *, b = None, c = None):
| ^^
3 | """the real implementation!"""
|
info: Source
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, c=2)
| ^^
|
"#);
}
struct GotoDefinitionDiagnostic { struct GotoDefinitionDiagnostic {
source: FileRange, source: FileRange,
target: FileRange, target: FileRange,

View file

@ -791,7 +791,453 @@ mod tests {
} }
#[test] #[test]
fn hover_overload() { fn hover_overload_type_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1)
",
)
.source(
"mymodule.py",
r#"
def ab(a):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int):
"""the int overload"""
@overload
def ab(a: str): ...
"""the str overload"""
"#,
)
.build();
assert_snapshot!(test.hover(), @r"
(a: int) -> Unknown
(a: str) -> Unknown
---------------------------------------------
the int overload
---------------------------------------------
```python
(a: int) -> Unknown
(a: str) -> Unknown
```
---
```text
the int overload
```
---------------------------------------------
info[hover]: Hovered content is
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1)
| ^-
| ||
| |Cursor offset
| source
|
");
}
#[test]
fn hover_overload_type_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
r#"
from mymodule import ab
a<CURSOR>b("hello")
"#,
)
.source(
"mymodule.py",
r#"
def ab(a):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int):
"""the int overload"""
@overload
def ab(a: str):
"""the str overload"""
"#,
)
.build();
assert_snapshot!(test.hover(), @r#"
(a: int) -> Unknown
(a: str) -> Unknown
---------------------------------------------
the int overload
---------------------------------------------
```python
(a: int) -> Unknown
(a: str) -> Unknown
```
---
```text
the int overload
```
---------------------------------------------
info[hover]: Hovered content is
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab("hello")
| ^-
| ||
| |Cursor offset
| source
|
"#);
}
#[test]
fn hover_overload_arity_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1, 2)
",
)
.source(
"mymodule.py",
r#"
def ab(a, b = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int, b: int):
"""the two arg overload"""
@overload
def ab(a: int):
"""the one arg overload"""
"#,
)
.build();
assert_snapshot!(test.hover(), @r"
(
a: int,
b: int
) -> Unknown
(a: int) -> Unknown
---------------------------------------------
the two arg overload
---------------------------------------------
```python
(
a: int,
b: int
) -> Unknown
(a: int) -> Unknown
```
---
```text
the two arg overload
```
---------------------------------------------
info[hover]: Hovered content is
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, 2)
| ^-
| ||
| |Cursor offset
| source
|
");
}
#[test]
fn hover_overload_arity_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1)
",
)
.source(
"mymodule.py",
r#"
def ab(a, b = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int, b: int):
"""the two arg overload"""
@overload
def ab(a: int):
"""the one arg overload"""
"#,
)
.build();
assert_snapshot!(test.hover(), @r"
(
a: int,
b: int
) -> Unknown
(a: int) -> Unknown
---------------------------------------------
the two arg overload
---------------------------------------------
```python
(
a: int,
b: int
) -> Unknown
(a: int) -> Unknown
```
---
```text
the two arg overload
```
---------------------------------------------
info[hover]: Hovered content is
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1)
| ^-
| ||
| |Cursor offset
| source
|
");
}
#[test]
fn hover_overload_keyword_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1, b=2)
",
)
.source(
"mymodule.py",
r#"
def ab(a, *, b = None, c = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int):
"""keywordless overload"""
@overload
def ab(a: int, *, b: int):
"""b overload"""
@overload
def ab(a: int, *, c: int):
"""c overload"""
"#,
)
.build();
assert_snapshot!(test.hover(), @r"
(a: int) -> Unknown
(
a: int,
*,
b: int
) -> Unknown
(
a: int,
*,
c: int
) -> Unknown
---------------------------------------------
keywordless overload
---------------------------------------------
```python
(a: int) -> Unknown
(
a: int,
*,
b: int
) -> Unknown
(
a: int,
*,
c: int
) -> Unknown
```
---
```text
keywordless overload
```
---------------------------------------------
info[hover]: Hovered content is
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, b=2)
| ^-
| ||
| |Cursor offset
| source
|
");
}
#[test]
fn hover_overload_keyword_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
a<CURSOR>b(1, c=2)
",
)
.source(
"mymodule.py",
r#"
def ab(a, *, b = None, c = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int):
"""keywordless overload"""
@overload
def ab(a: int, *, b: int):
"""b overload"""
@overload
def ab(a: int, *, c: int):
"""c overload"""
"#,
)
.build();
assert_snapshot!(test.hover(), @r"
(a: int) -> Unknown
(
a: int,
*,
b: int
) -> Unknown
(
a: int,
*,
c: int
) -> Unknown
---------------------------------------------
keywordless overload
---------------------------------------------
```python
(a: int) -> Unknown
(
a: int,
*,
b: int
) -> Unknown
(
a: int,
*,
c: int
) -> Unknown
```
---
```text
keywordless overload
```
---------------------------------------------
info[hover]: Hovered content is
--> main.py:4:1
|
2 | from mymodule import ab
3 |
4 | ab(1, c=2)
| ^-
| ||
| |Cursor offset
| source
|
");
}
#[test]
fn hover_overload_ambiguous() {
let test = cursor_test( let test = cursor_test(
r#" r#"
from typing import overload from typing import overload
@ -858,7 +1304,7 @@ mod tests {
} }
#[test] #[test]
fn hover_overload_compact() { fn hover_overload_ambiguous_compact() {
let test = cursor_test( let test = cursor_test(
r#" r#"
from typing import overload from typing import overload

View file

@ -258,6 +258,9 @@ fn create_parameters_from_offsets(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use insta::assert_snapshot;
use crate::MarkupKind;
use crate::docstring::Docstring; use crate::docstring::Docstring;
use crate::signature_help::SignatureHelpInfo; use crate::signature_help::SignatureHelpInfo;
use crate::tests::{CursorTest, cursor_test}; use crate::tests::{CursorTest, cursor_test};
@ -470,6 +473,354 @@ mod tests {
assert_eq!(param2.name, "value"); assert_eq!(param2.name, "value");
} }
#[test]
fn signature_help_overload_type_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
ab(1<CURSOR>)
",
)
.source(
"mymodule.py",
r#"
def ab(a):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int):
"""the int overload"""
@overload
def ab(a: str): ...
"""the str overload"""
"#,
)
.build();
assert_snapshot!(test.signature_help_render(), @r"
============== active signature =============
(a: int) -> Unknown
---------------------------------------------
the int overload
-------------- active parameter -------------
a: int
---------------------------------------------
=============== other signature =============
(a: str) -> Unknown
---------------------------------------------
the real implementation!
-------------- active parameter -------------
a: str
---------------------------------------------
");
}
#[test]
fn signature_help_overload_type_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
r#"
from mymodule import ab
ab("hello"<CURSOR>)
"#,
)
.source(
"mymodule.py",
r#"
def ab(a):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int):
"""the int overload"""
@overload
def ab(a: str):
"""the str overload"""
"#,
)
.build();
assert_snapshot!(test.signature_help_render(), @r"
============== active signature =============
(a: int) -> Unknown
---------------------------------------------
the int overload
-------------- active parameter -------------
a: int
---------------------------------------------
=============== other signature =============
(a: str) -> Unknown
---------------------------------------------
the str overload
-------------- active parameter -------------
a: str
---------------------------------------------
");
}
#[test]
fn signature_help_overload_arity_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
ab(1, 2<CURSOR>)
",
)
.source(
"mymodule.py",
r#"
def ab(a, b = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int, b: int):
"""the two arg overload"""
@overload
def ab(a: int):
"""the one arg overload"""
"#,
)
.build();
assert_snapshot!(test.signature_help_render(), @r"
============== active signature =============
(a: int, b: int) -> Unknown
---------------------------------------------
the two arg overload
-------------- active parameter -------------
b: int
---------------------------------------------
=============== other signature =============
(a: int) -> Unknown
---------------------------------------------
the one arg overload
(no active parameter specified)
");
}
#[test]
fn signature_help_overload_arity_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
ab(1<CURSOR>)
",
)
.source(
"mymodule.py",
r#"
def ab(a, b = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int, b: int):
"""the two arg overload"""
@overload
def ab(a: int):
"""the one arg overload"""
"#,
)
.build();
assert_snapshot!(test.signature_help_render(), @r"
============== active signature =============
(a: int, b: int) -> Unknown
---------------------------------------------
the two arg overload
-------------- active parameter -------------
a: int
---------------------------------------------
=============== other signature =============
(a: int) -> Unknown
---------------------------------------------
the one arg overload
-------------- active parameter -------------
a: int
---------------------------------------------
");
}
#[test]
fn signature_help_overload_keyword_disambiguated1() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
ab(1, b=2<CURSOR>)
",
)
.source(
"mymodule.py",
r#"
def ab(a, *, b = None, c = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int):
"""keywordless overload"""
@overload
def ab(a: int, *, b: int):
"""b overload"""
@overload
def ab(a: int, *, c: int):
"""c overload"""
"#,
)
.build();
assert_snapshot!(test.signature_help_render(), @r"
============== active signature =============
(a: int, *, b: int) -> Unknown
---------------------------------------------
b overload
-------------- active parameter -------------
b: int
---------------------------------------------
=============== other signature =============
(a: int) -> Unknown
---------------------------------------------
keywordless overload
(no active parameter specified)
=============== other signature =============
(a: int, *, c: int) -> Unknown
---------------------------------------------
c overload
-------------- active parameter -------------
c: int
---------------------------------------------
");
}
#[test]
fn signature_help_overload_keyword_disambiguated2() {
let test = CursorTest::builder()
.source(
"main.py",
"
from mymodule import ab
ab(1, c=2<CURSOR>)
",
)
.source(
"mymodule.py",
r#"
def ab(a, *, b = None, c = None):
"""the real implementation!"""
"#,
)
.source(
"mymodule.pyi",
r#"
from typing import overload
@overload
def ab(a: int):
"""keywordless overload"""
@overload
def ab(a: int, *, b: int):
"""b overload"""
@overload
def ab(a: int, *, c: int):
"""c overload"""
"#,
)
.build();
assert_snapshot!(test.signature_help_render(), @r"
============== active signature =============
(a: int, *, c: int) -> Unknown
---------------------------------------------
c overload
-------------- active parameter -------------
c: int
---------------------------------------------
=============== other signature =============
(a: int) -> Unknown
---------------------------------------------
keywordless overload
(no active parameter specified)
=============== other signature =============
(a: int, *, b: int) -> Unknown
---------------------------------------------
b overload
-------------- active parameter -------------
b: int
---------------------------------------------
");
}
#[test] #[test]
fn signature_help_class_constructor() { fn signature_help_class_constructor() {
let test = cursor_test( let test = cursor_test(
@ -826,5 +1177,94 @@ mod tests {
fn signature_help(&self) -> Option<SignatureHelpInfo> { fn signature_help(&self) -> Option<SignatureHelpInfo> {
crate::signature_help::signature_help(&self.db, self.cursor.file, self.cursor.offset) crate::signature_help::signature_help(&self.db, self.cursor.file, self.cursor.offset)
} }
fn signature_help_render(&self) -> String {
use std::fmt::Write;
let Some(signature_help) = self.signature_help() else {
return "Signature help found no signatures".to_string();
};
let active_sig_heading = "\n============== active signature =============\n";
let second_sig_heading = "\n=============== other signature =============\n";
let active_arg_heading = "\n-------------- active parameter -------------\n";
let mut buf = String::new();
if let Some(active_signature) = signature_help.active_signature {
let signature = signature_help
.signatures
.get(active_signature)
.expect("failed to find active signature!");
write!(
&mut buf,
"{heading}{label}{line}{docs}",
heading = active_sig_heading,
label = signature.label,
line = MarkupKind::PlainText.horizontal_line(),
docs = signature
.documentation
.as_ref()
.map(Docstring::render_plaintext)
.unwrap_or_default(),
)
.unwrap();
if let Some(active_parameter) = signature.active_parameter {
let parameter = signature
.parameters
.get(active_parameter)
.expect("failed to find active parameter!");
write!(
&mut buf,
"{heading}{label}{line}{docs}",
heading = active_arg_heading,
label = parameter.label,
line = MarkupKind::PlainText.horizontal_line(),
docs = parameter.documentation.as_deref().unwrap_or_default(),
)
.unwrap();
} else {
writeln!(&mut buf, "\n(no active parameter specified)").unwrap();
}
} else {
writeln!(&mut buf, "\n(no active signature specified)").unwrap();
}
for (idx, signature) in signature_help.signatures.iter().enumerate() {
if Some(idx) == signature_help.active_signature {
continue;
}
write!(
&mut buf,
"{heading}{label}{line}{docs}",
heading = second_sig_heading,
label = signature.label,
line = MarkupKind::PlainText.horizontal_line(),
docs = signature
.documentation
.as_ref()
.map(Docstring::render_plaintext)
.unwrap_or_default(),
)
.unwrap();
if let Some(active_parameter) = signature.active_parameter {
let parameter = signature
.parameters
.get(active_parameter)
.expect("failed to find active parameter!");
write!(
&mut buf,
"{heading}{label}{line}{docs}",
heading = active_arg_heading,
label = parameter.label,
line = MarkupKind::PlainText.horizontal_line(),
docs = parameter.documentation.as_deref().unwrap_or_default(),
)
.unwrap();
} else {
write!(&mut buf, "\n(no active parameter specified)").unwrap();
}
}
buf
}
} }
} }