mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-29 11:07:54 +00:00
[ty] display variance on hover over type variables (#20900)
This commit is contained in:
parent
0520d11a66
commit
a7c38eb122
6 changed files with 528 additions and 48 deletions
|
|
@ -284,13 +284,13 @@ impl GotoTarget<'_> {
|
||||||
// When asking the type of a callable, usually you want the callable itself?
|
// When asking the type of a callable, usually you want the callable itself?
|
||||||
// (i.e. the type of `MyClass` in `MyClass()` is `<class MyClass>` and not `() -> MyClass`)
|
// (i.e. the type of `MyClass` in `MyClass()` is `<class MyClass>` and not `() -> MyClass`)
|
||||||
GotoTarget::Call { callable, .. } => callable.inferred_type(model),
|
GotoTarget::Call { callable, .. } => callable.inferred_type(model),
|
||||||
|
GotoTarget::TypeParamTypeVarName(typevar) => typevar.inferred_type(model),
|
||||||
// TODO: Support identifier targets
|
// TODO: Support identifier targets
|
||||||
GotoTarget::PatternMatchRest(_)
|
GotoTarget::PatternMatchRest(_)
|
||||||
| GotoTarget::PatternKeywordArgument(_)
|
| GotoTarget::PatternKeywordArgument(_)
|
||||||
| GotoTarget::PatternMatchStarName(_)
|
| GotoTarget::PatternMatchStarName(_)
|
||||||
| GotoTarget::PatternMatchAsName(_)
|
| GotoTarget::PatternMatchAsName(_)
|
||||||
| GotoTarget::ImportModuleComponent { .. }
|
| GotoTarget::ImportModuleComponent { .. }
|
||||||
| GotoTarget::TypeParamTypeVarName(_)
|
|
||||||
| GotoTarget::TypeParamParamSpecName(_)
|
| GotoTarget::TypeParamParamSpecName(_)
|
||||||
| GotoTarget::TypeParamTypeVarTupleName(_)
|
| GotoTarget::TypeParamTypeVarTupleName(_)
|
||||||
| GotoTarget::NonLocal { .. }
|
| GotoTarget::NonLocal { .. }
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use ruff_db::parsed::parsed_module;
|
||||||
use ruff_text_size::{Ranged, TextSize};
|
use ruff_text_size::{Ranged, TextSize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use ty_python_semantic::types::Type;
|
use ty_python_semantic::types::{KnownInstanceType, Type, TypeVarVariance};
|
||||||
use ty_python_semantic::{DisplaySettings, SemanticModel};
|
use ty_python_semantic::{DisplaySettings, SemanticModel};
|
||||||
|
|
||||||
pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Hover<'_>>> {
|
pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Hover<'_>>> {
|
||||||
|
|
@ -20,7 +20,7 @@ pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Ho
|
||||||
}
|
}
|
||||||
|
|
||||||
let model = SemanticModel::new(db, file);
|
let model = SemanticModel::new(db, file);
|
||||||
let ty = goto_target.inferred_type(&model).map(HoverContent::Type);
|
let ty = goto_target.inferred_type(&model);
|
||||||
let docs = goto_target
|
let docs = goto_target
|
||||||
.get_definition_targets(
|
.get_definition_targets(
|
||||||
file,
|
file,
|
||||||
|
|
@ -30,13 +30,20 @@ pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Ho
|
||||||
.and_then(|definitions| definitions.docstring(db))
|
.and_then(|definitions| definitions.docstring(db))
|
||||||
.map(HoverContent::Docstring);
|
.map(HoverContent::Docstring);
|
||||||
|
|
||||||
if let Some(HoverContent::Type(ty)) = ty {
|
|
||||||
tracing::debug!("Inferred type of covering node is {}", ty.display(db));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Render the symbol's signature instead of just its type.
|
// TODO: Render the symbol's signature instead of just its type.
|
||||||
let mut contents = Vec::new();
|
let mut contents = Vec::new();
|
||||||
contents.extend(ty);
|
if let Some(ty) = ty {
|
||||||
|
tracing::debug!("Inferred type of covering node is {}", ty.display(db));
|
||||||
|
contents.push(match ty {
|
||||||
|
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => typevar
|
||||||
|
.bind_pep695(db)
|
||||||
|
.map_or(HoverContent::Type(ty, None), |typevar| {
|
||||||
|
HoverContent::Type(Type::TypeVar(typevar), Some(typevar.variance(db)))
|
||||||
|
}),
|
||||||
|
Type::TypeVar(typevar) => HoverContent::Type(ty, Some(typevar.variance(db))),
|
||||||
|
_ => HoverContent::Type(ty, None),
|
||||||
|
});
|
||||||
|
}
|
||||||
contents.extend(docs);
|
contents.extend(docs);
|
||||||
|
|
||||||
if contents.is_empty() {
|
if contents.is_empty() {
|
||||||
|
|
@ -110,7 +117,7 @@ impl fmt::Display for DisplayHover<'_> {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum HoverContent<'db> {
|
pub enum HoverContent<'db> {
|
||||||
Type(Type<'db>),
|
Type(Type<'db>, Option<TypeVarVariance>),
|
||||||
Docstring(Docstring),
|
Docstring(Docstring),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,13 +140,24 @@ pub(crate) struct DisplayHoverContent<'a, 'db> {
|
||||||
impl fmt::Display for DisplayHoverContent<'_, '_> {
|
impl fmt::Display for DisplayHoverContent<'_, '_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self.content {
|
match self.content {
|
||||||
HoverContent::Type(ty) => self
|
HoverContent::Type(ty, variance) => {
|
||||||
.kind
|
let variance = match variance {
|
||||||
.fenced_code_block(
|
Some(TypeVarVariance::Covariant) => " (covariant)",
|
||||||
ty.display_with(self.db, DisplaySettings::default().multiline()),
|
Some(TypeVarVariance::Contravariant) => " (contravariant)",
|
||||||
"python",
|
Some(TypeVarVariance::Invariant) => " (invariant)",
|
||||||
)
|
Some(TypeVarVariance::Bivariant) => " (bivariant)",
|
||||||
.fmt(f),
|
None => "",
|
||||||
|
};
|
||||||
|
self.kind
|
||||||
|
.fenced_code_block(
|
||||||
|
format!(
|
||||||
|
"{}{variance}",
|
||||||
|
ty.display_with(self.db, DisplaySettings::default().multiline())
|
||||||
|
),
|
||||||
|
"python",
|
||||||
|
)
|
||||||
|
.fmt(f)
|
||||||
|
}
|
||||||
HoverContent::Docstring(docstring) => docstring.render(self.kind).fmt(f),
|
HoverContent::Docstring(docstring) => docstring.render(self.kind).fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1590,10 +1608,10 @@ def ab(a: int, *, c: int):
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_snapshot!(test.hover(), @r"
|
assert_snapshot!(test.hover(), @r"
|
||||||
T@Alias
|
T@Alias (invariant)
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
```python
|
```python
|
||||||
T@Alias
|
T@Alias (invariant)
|
||||||
```
|
```
|
||||||
---------------------------------------------
|
---------------------------------------------
|
||||||
info[hover]: Hovered content is
|
info[hover]: Hovered content is
|
||||||
|
|
@ -2058,6 +2076,444 @@ def ab(a: int, *, c: int):
|
||||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_class_typevar_variance() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
class Covariant[T<CURSOR>]:
|
||||||
|
def get(self) -> T:
|
||||||
|
raise ValueError
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@Covariant (covariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@Covariant (covariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:2:17
|
||||||
|
|
|
||||||
|
2 | class Covariant[T]:
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
3 | def get(self) -> T:
|
||||||
|
4 | raise ValueError
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
class Covariant[T]:
|
||||||
|
def get(self) -> T<CURSOR>:
|
||||||
|
raise ValueError
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@Covariant (covariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@Covariant (covariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:3:22
|
||||||
|
|
|
||||||
|
2 | class Covariant[T]:
|
||||||
|
3 | def get(self) -> T:
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
4 | raise ValueError
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
class Contravariant[T<CURSOR>]:
|
||||||
|
def set(self, x: T):
|
||||||
|
pass
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@Contravariant (contravariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@Contravariant (contravariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:2:21
|
||||||
|
|
|
||||||
|
2 | class Contravariant[T]:
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
3 | def set(self, x: T):
|
||||||
|
4 | pass
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
class Contravariant[T]:
|
||||||
|
def set(self, x: T<CURSOR>):
|
||||||
|
pass
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@Contravariant (contravariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@Contravariant (contravariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:3:22
|
||||||
|
|
|
||||||
|
2 | class Contravariant[T]:
|
||||||
|
3 | def set(self, x: T):
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
4 | pass
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_function_typevar_variance() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
def covariant[T<CURSOR>]() -> T:
|
||||||
|
raise ValueError
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@covariant (covariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@covariant (covariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:2:15
|
||||||
|
|
|
||||||
|
2 | def covariant[T]() -> T:
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
3 | raise ValueError
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
def covariant[T]() -> T<CURSOR>:
|
||||||
|
raise ValueError
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@covariant (covariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@covariant (covariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:2:23
|
||||||
|
|
|
||||||
|
2 | def covariant[T]() -> T:
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
3 | raise ValueError
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
def contravariant[T<CURSOR>](x: T):
|
||||||
|
pass
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@contravariant (contravariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@contravariant (contravariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:2:19
|
||||||
|
|
|
||||||
|
2 | def contravariant[T](x: T):
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
3 | pass
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
def contravariant[T](x: T<CURSOR>):
|
||||||
|
pass
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@contravariant (contravariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@contravariant (contravariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:2:25
|
||||||
|
|
|
||||||
|
2 | def contravariant[T](x: T):
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
3 | pass
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_type_alias_typevar_variance() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
type List[T<CURSOR>] = list[T]
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@List (invariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@List (invariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:2:11
|
||||||
|
|
|
||||||
|
2 | type List[T] = list[T]
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
type List[T] = list[T<CURSOR>]
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@List (invariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@List (invariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:2:21
|
||||||
|
|
|
||||||
|
2 | type List[T] = list[T]
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
type Tuple[T<CURSOR>] = tuple[T]
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@Tuple (covariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@Tuple (covariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:2:12
|
||||||
|
|
|
||||||
|
2 | type Tuple[T] = tuple[T]
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
type Tuple[T] = tuple[T<CURSOR>]
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@Tuple (covariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@Tuple (covariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:2:23
|
||||||
|
|
|
||||||
|
2 | type Tuple[T] = tuple[T]
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_legacy_typevar_variance() {
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
T<CURSOR> = TypeVar('T', covariant=True)
|
||||||
|
|
||||||
|
def covariant() -> T:
|
||||||
|
raise ValueError
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
typing.TypeVar
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
typing.TypeVar
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:4:1
|
||||||
|
|
|
||||||
|
2 | from typing import TypeVar
|
||||||
|
3 |
|
||||||
|
4 | T = TypeVar('T', covariant=True)
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
5 |
|
||||||
|
6 | def covariant() -> T:
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
T = TypeVar('T', covariant=True)
|
||||||
|
|
||||||
|
def covariant() -> T<CURSOR>:
|
||||||
|
raise ValueError
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@covariant (covariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@covariant (covariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:6:20
|
||||||
|
|
|
||||||
|
4 | T = TypeVar('T', covariant=True)
|
||||||
|
5 |
|
||||||
|
6 | def covariant() -> T:
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
7 | raise ValueError
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
T<CURSOR> = TypeVar('T', contravariant=True)
|
||||||
|
|
||||||
|
def contravariant(x: T):
|
||||||
|
pass
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
typing.TypeVar
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
typing.TypeVar
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:4:1
|
||||||
|
|
|
||||||
|
2 | from typing import TypeVar
|
||||||
|
3 |
|
||||||
|
4 | T = TypeVar('T', contravariant=True)
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
5 |
|
||||||
|
6 | def contravariant(x: T):
|
||||||
|
|
|
||||||
|
");
|
||||||
|
|
||||||
|
let test = cursor_test(
|
||||||
|
r#"
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
T = TypeVar('T', contravariant=True)
|
||||||
|
|
||||||
|
def contravariant(x: T<CURSOR>):
|
||||||
|
pass
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @r"
|
||||||
|
T@contravariant (contravariant)
|
||||||
|
---------------------------------------------
|
||||||
|
```python
|
||||||
|
T@contravariant (contravariant)
|
||||||
|
```
|
||||||
|
---------------------------------------------
|
||||||
|
info[hover]: Hovered content is
|
||||||
|
--> main.py:6:22
|
||||||
|
|
|
||||||
|
4 | T = TypeVar('T', contravariant=True)
|
||||||
|
5 |
|
||||||
|
6 | def contravariant(x: T):
|
||||||
|
| ^- Cursor offset
|
||||||
|
| |
|
||||||
|
| source
|
||||||
|
7 | pass
|
||||||
|
|
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
impl CursorTest {
|
impl CursorTest {
|
||||||
fn hover(&self) -> String {
|
fn hover(&self) -> String {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use crate::{
|
||||||
semantic_index::{
|
semantic_index::{
|
||||||
SemanticIndex, reachability_constraints::ScopedReachabilityConstraintId, semantic_index,
|
SemanticIndex, reachability_constraints::ScopedReachabilityConstraintId, semantic_index,
|
||||||
},
|
},
|
||||||
|
types::{GenericContext, binding_type, infer_definition_types},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A cross-module identifier of a scope that can be used as a salsa query parameter.
|
/// A cross-module identifier of a scope that can be used as a salsa query parameter.
|
||||||
|
|
@ -430,6 +431,38 @@ impl NodeWithScopeKind {
|
||||||
pub(crate) fn expect_type_alias(&self) -> &AstNodeRef<ast::StmtTypeAlias> {
|
pub(crate) fn expect_type_alias(&self) -> &AstNodeRef<ast::StmtTypeAlias> {
|
||||||
self.as_type_alias().expect("expected type alias")
|
self.as_type_alias().expect("expected type alias")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generic_context<'db>(
|
||||||
|
&self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
index: &SemanticIndex<'db>,
|
||||||
|
) -> Option<GenericContext<'db>> {
|
||||||
|
match self {
|
||||||
|
NodeWithScopeKind::Class(class) => {
|
||||||
|
let definition = index.expect_single_definition(class);
|
||||||
|
binding_type(db, definition)
|
||||||
|
.as_class_literal()?
|
||||||
|
.generic_context(db)
|
||||||
|
}
|
||||||
|
NodeWithScopeKind::Function(function) => {
|
||||||
|
let definition = index.expect_single_definition(function);
|
||||||
|
infer_definition_types(db, definition)
|
||||||
|
.undecorated_type()
|
||||||
|
.expect("function should have undecorated type")
|
||||||
|
.as_function_literal()?
|
||||||
|
.last_definition_signature(db)
|
||||||
|
.generic_context
|
||||||
|
}
|
||||||
|
NodeWithScopeKind::TypeAlias(type_alias) => {
|
||||||
|
let definition = index.expect_single_definition(type_alias);
|
||||||
|
binding_type(db, definition)
|
||||||
|
.as_type_alias()?
|
||||||
|
.as_pep_695_type_alias()?
|
||||||
|
.generic_context(db)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, get_size2::GetSize)]
|
||||||
|
|
|
||||||
|
|
@ -479,6 +479,7 @@ impl_binding_has_ty_def!(ast::StmtClassDef);
|
||||||
impl_binding_has_ty_def!(ast::Parameter);
|
impl_binding_has_ty_def!(ast::Parameter);
|
||||||
impl_binding_has_ty_def!(ast::ParameterWithDefault);
|
impl_binding_has_ty_def!(ast::ParameterWithDefault);
|
||||||
impl_binding_has_ty_def!(ast::ExceptHandlerExceptHandler);
|
impl_binding_has_ty_def!(ast::ExceptHandlerExceptHandler);
|
||||||
|
impl_binding_has_ty_def!(ast::TypeParamTypeVar);
|
||||||
|
|
||||||
impl HasType for ast::Alias {
|
impl HasType for ast::Alias {
|
||||||
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Type<'db> {
|
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Type<'db> {
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,8 @@ pub(crate) use crate::types::narrow::infer_narrowing_constraint;
|
||||||
use crate::types::signatures::{ParameterForm, walk_signature};
|
use crate::types::signatures::{ParameterForm, walk_signature};
|
||||||
use crate::types::tuple::{TupleSpec, TupleSpecBuilder};
|
use crate::types::tuple::{TupleSpec, TupleSpecBuilder};
|
||||||
pub(crate) use crate::types::typed_dict::{TypedDictParams, TypedDictType, walk_typed_dict_type};
|
pub(crate) use crate::types::typed_dict::{TypedDictParams, TypedDictType, walk_typed_dict_type};
|
||||||
use crate::types::variance::{TypeVarVariance, VarianceInferable};
|
pub use crate::types::variance::TypeVarVariance;
|
||||||
|
use crate::types::variance::VarianceInferable;
|
||||||
use crate::types::visitor::any_over_type;
|
use crate::types::visitor::any_over_type;
|
||||||
use crate::unpack::EvaluationMode;
|
use crate::unpack::EvaluationMode;
|
||||||
use crate::{Db, FxOrderSet, Module, Program};
|
use crate::{Db, FxOrderSet, Module, Program};
|
||||||
|
|
@ -8394,6 +8395,21 @@ impl<'db> TypeVarInstance<'db> {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bind_pep695(self, db: &'db dyn Db) -> Option<BoundTypeVarInstance<'db>> {
|
||||||
|
if self.identity(db).kind(db) != TypeVarKind::Pep695 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let typevar_definition = self.definition(db)?;
|
||||||
|
let index = semantic_index(db, typevar_definition.file(db));
|
||||||
|
let (_, child) = index
|
||||||
|
.child_scopes(typevar_definition.file_scope(db))
|
||||||
|
.next()?;
|
||||||
|
child
|
||||||
|
.node()
|
||||||
|
.generic_context(db, index)?
|
||||||
|
.binds_typevar(db, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::ref_option)]
|
#[allow(clippy::ref_option)]
|
||||||
|
|
@ -8544,7 +8560,7 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn variance(self, db: &'db dyn Db) -> TypeVarVariance {
|
pub fn variance(self, db: &'db dyn Db) -> TypeVarVariance {
|
||||||
self.variance_with_polarity(db, TypeVarVariance::Covariant)
|
self.variance_with_polarity(db, TypeVarVariance::Covariant)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ use crate::semantic_index::{SemanticIndex, semantic_index};
|
||||||
use crate::types::class::ClassType;
|
use crate::types::class::ClassType;
|
||||||
use crate::types::class_base::ClassBase;
|
use crate::types::class_base::ClassBase;
|
||||||
use crate::types::constraints::ConstraintSet;
|
use crate::types::constraints::ConstraintSet;
|
||||||
use crate::types::infer::infer_definition_types;
|
|
||||||
use crate::types::instance::{Protocol, ProtocolInstanceType};
|
use crate::types::instance::{Protocol, ProtocolInstanceType};
|
||||||
use crate::types::signatures::{Parameter, Parameters, Signature};
|
use crate::types::signatures::{Parameter, Parameters, Signature};
|
||||||
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
use crate::types::tuple::{TupleSpec, TupleType, walk_tuple_type};
|
||||||
|
|
@ -21,8 +20,7 @@ use crate::types::{
|
||||||
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
|
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
|
||||||
KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor, Type, TypeContext,
|
KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor, Type, TypeContext,
|
||||||
TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarIdentity, TypeVarInstance,
|
TypeMapping, TypeRelation, TypeVarBoundOrConstraints, TypeVarIdentity, TypeVarInstance,
|
||||||
TypeVarKind, TypeVarVariance, UnionType, binding_type, declaration_type,
|
TypeVarKind, TypeVarVariance, UnionType, declaration_type, walk_bound_type_var_type,
|
||||||
walk_bound_type_var_type,
|
|
||||||
};
|
};
|
||||||
use crate::{Db, FxIndexSet, FxOrderMap, FxOrderSet};
|
use crate::{Db, FxIndexSet, FxOrderMap, FxOrderSet};
|
||||||
|
|
||||||
|
|
@ -35,31 +33,7 @@ pub(crate) fn enclosing_generic_contexts<'db>(
|
||||||
) -> impl Iterator<Item = GenericContext<'db>> {
|
) -> impl Iterator<Item = GenericContext<'db>> {
|
||||||
index
|
index
|
||||||
.ancestor_scopes(scope)
|
.ancestor_scopes(scope)
|
||||||
.filter_map(|(_, ancestor_scope)| match ancestor_scope.node() {
|
.filter_map(|(_, ancestor_scope)| ancestor_scope.node().generic_context(db, index))
|
||||||
NodeWithScopeKind::Class(class) => {
|
|
||||||
let definition = index.expect_single_definition(class);
|
|
||||||
binding_type(db, definition)
|
|
||||||
.as_class_literal()?
|
|
||||||
.generic_context(db)
|
|
||||||
}
|
|
||||||
NodeWithScopeKind::Function(function) => {
|
|
||||||
let definition = index.expect_single_definition(function);
|
|
||||||
infer_definition_types(db, definition)
|
|
||||||
.undecorated_type()
|
|
||||||
.expect("function should have undecorated type")
|
|
||||||
.as_function_literal()?
|
|
||||||
.last_definition_signature(db)
|
|
||||||
.generic_context
|
|
||||||
}
|
|
||||||
NodeWithScopeKind::TypeAlias(type_alias) => {
|
|
||||||
let definition = index.expect_single_definition(type_alias);
|
|
||||||
binding_type(db, definition)
|
|
||||||
.as_type_alias()?
|
|
||||||
.as_pep_695_type_alias()?
|
|
||||||
.generic_context(db)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Binds an unbound typevar.
|
/// Binds an unbound typevar.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue