Compare commits
2 Commits
jack/loop-
...
gankra/stm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0c31d0c59 | ||
|
|
7ca384d0de |
@@ -334,15 +334,24 @@ impl GotoTarget<'_> {
|
||||
let (_, ty) = ty_python_semantic::definitions_for_unary_op(model, expression)?;
|
||||
ty
|
||||
}
|
||||
// TODO: Support identifier targets
|
||||
GotoTarget::PatternMatchRest(_)
|
||||
| GotoTarget::PatternKeywordArgument(_)
|
||||
| GotoTarget::PatternMatchStarName(_)
|
||||
| GotoTarget::PatternMatchAsName(_)
|
||||
| GotoTarget::TypeParamParamSpecName(_)
|
||||
| GotoTarget::TypeParamTypeVarTupleName(_)
|
||||
| GotoTarget::NonLocal { .. }
|
||||
| GotoTarget::Globals { .. } => return None,
|
||||
GotoTarget::PatternMatchRest(pattern) => {
|
||||
model.inferred_type_for_identifier(pattern.rest.as_ref()?)
|
||||
}
|
||||
GotoTarget::PatternKeywordArgument(pattern) => {
|
||||
model.inferred_type_for_identifier(&pattern.attr)
|
||||
}
|
||||
GotoTarget::PatternMatchStarName(pattern) => {
|
||||
model.inferred_type_for_identifier(pattern.name.as_ref()?)
|
||||
}
|
||||
GotoTarget::PatternMatchAsName(pattern) => {
|
||||
model.inferred_type_for_identifier(pattern.name.as_ref()?)
|
||||
}
|
||||
GotoTarget::NonLocal { identifier } => model.inferred_type_for_identifier(identifier),
|
||||
GotoTarget::Globals { identifier } => model.inferred_type_for_identifier(identifier),
|
||||
// These don't really... *have* a type?
|
||||
GotoTarget::TypeParamParamSpecName(_) | GotoTarget::TypeParamTypeVarTupleName(_) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(ty)
|
||||
|
||||
@@ -975,7 +975,26 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
assert_snapshot!(test.goto_type_definition(), @r#"
|
||||
info[goto-type-definition]: Type definition
|
||||
--> stdlib/ty_extensions.pyi:20:1
|
||||
|
|
||||
19 | # Types
|
||||
20 | Unknown = object()
|
||||
| ^^^^^^^
|
||||
21 | AlwaysTruthy = object()
|
||||
22 | AlwaysFalsy = object()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:22
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1003,7 +1022,26 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
assert_snapshot!(test.goto_type_definition(), @r#"
|
||||
info[goto-type-definition]: Type definition
|
||||
--> stdlib/ty_extensions.pyi:20:1
|
||||
|
|
||||
19 | # Types
|
||||
20 | Unknown = object()
|
||||
| ^^^^^^^
|
||||
21 | AlwaysTruthy = object()
|
||||
22 | AlwaysFalsy = object()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:23
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1031,7 +1069,26 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
assert_snapshot!(test.goto_type_definition(), @r#"
|
||||
info[goto-type-definition]: Type definition
|
||||
--> stdlib/ty_extensions.pyi:20:1
|
||||
|
|
||||
19 | # Types
|
||||
20 | Unknown = object()
|
||||
| ^^^^^^^
|
||||
21 | AlwaysTruthy = object()
|
||||
22 | AlwaysFalsy = object()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:4:37
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
| ^^
|
||||
5 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1065,7 +1122,26 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
assert_snapshot!(test.goto_type_definition(), @r"
|
||||
info[goto-type-definition]: Type definition
|
||||
--> stdlib/ty_extensions.pyi:20:1
|
||||
|
|
||||
19 | # Types
|
||||
20 | Unknown = object()
|
||||
| ^^^^^^^
|
||||
21 | AlwaysTruthy = object()
|
||||
22 | AlwaysFalsy = object()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:10:30
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^
|
||||
11 | x = ab
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1143,7 +1219,26 @@ mod tests {
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
assert_snapshot!(test.goto_type_definition(), @r"
|
||||
info[goto-type-definition]: Type definition
|
||||
--> stdlib/ty_extensions.pyi:20:1
|
||||
|
|
||||
19 | # Types
|
||||
20 | Unknown = object()
|
||||
| ^^^^^^^
|
||||
21 | AlwaysTruthy = object()
|
||||
22 | AlwaysFalsy = object()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:10:23
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^^^^^
|
||||
11 | x = ab
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1395,7 +1490,26 @@ def outer():
|
||||
);
|
||||
|
||||
// Should find the variable declaration in the outer scope, not the nonlocal statement
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
assert_snapshot!(test.goto_type_definition(), @r#"
|
||||
info[goto-type-definition]: Type definition
|
||||
--> stdlib/ty_extensions.pyi:20:1
|
||||
|
|
||||
19 | # Types
|
||||
20 | Unknown = object()
|
||||
| ^^^^^^^
|
||||
21 | AlwaysTruthy = object()
|
||||
22 | AlwaysFalsy = object()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:6:18
|
||||
|
|
||||
5 | def inner():
|
||||
6 | nonlocal xy
|
||||
| ^^
|
||||
7 | xy = "modified"
|
||||
8 | return x # Should find the nonlocal x declaration in outer scope
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1447,7 +1561,26 @@ def function():
|
||||
);
|
||||
|
||||
// Should find the global variable declaration, not the global statement
|
||||
assert_snapshot!(test.goto_type_definition(), @"No goto target found");
|
||||
assert_snapshot!(test.goto_type_definition(), @r#"
|
||||
info[goto-type-definition]: Type definition
|
||||
--> stdlib/ty_extensions.pyi:20:1
|
||||
|
|
||||
19 | # Types
|
||||
20 | Unknown = object()
|
||||
| ^^^^^^^
|
||||
21 | AlwaysTruthy = object()
|
||||
22 | AlwaysFalsy = object()
|
||||
|
|
||||
info: Source
|
||||
--> main.py:5:12
|
||||
|
|
||||
4 | def function():
|
||||
5 | global global_var
|
||||
| ^^^^^^^^^^
|
||||
6 | global_var = "modified"
|
||||
7 | return global_var # Should find the global variable declaration
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1704,7 +1704,26 @@ def outer():
|
||||
);
|
||||
|
||||
// Should find the variable declaration in the outer scope, not the nonlocal statement
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
assert_snapshot!(test.hover(), @r#"
|
||||
Unknown
|
||||
---------------------------------------------
|
||||
```python
|
||||
Unknown
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:6:18
|
||||
|
|
||||
5 | def inner():
|
||||
6 | nonlocal xy
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
7 | xy = "modified"
|
||||
8 | return x # Should find the nonlocal x declaration in outer scope
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1756,7 +1775,26 @@ def function():
|
||||
);
|
||||
|
||||
// Should find the global variable declaration, not the global statement
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
assert_snapshot!(test.hover(), @r#"
|
||||
Unknown
|
||||
---------------------------------------------
|
||||
```python
|
||||
Unknown
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:5:12
|
||||
|
|
||||
4 | def function():
|
||||
5 | global global_var
|
||||
| ^^^^^^^-^^
|
||||
| | |
|
||||
| | Cursor offset
|
||||
| source
|
||||
6 | global_var = "modified"
|
||||
7 | return global_var # Should find the global variable declaration
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1770,7 +1808,26 @@ def function():
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
assert_snapshot!(test.hover(), @r#"
|
||||
Unknown
|
||||
---------------------------------------------
|
||||
```python
|
||||
Unknown
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:4:22
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ab]:
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
5 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1816,7 +1873,26 @@ def function():
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
assert_snapshot!(test.hover(), @r#"
|
||||
Unknown
|
||||
---------------------------------------------
|
||||
```python
|
||||
Unknown
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:4:23
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", *ab]:
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
5 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1862,7 +1938,26 @@ def function():
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
assert_snapshot!(test.hover(), @r#"
|
||||
Unknown
|
||||
---------------------------------------------
|
||||
```python
|
||||
Unknown
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:4:37
|
||||
|
|
||||
2 | def my_func(command: str):
|
||||
3 | match command.split():
|
||||
4 | case ["get", ("a" | "b") as ab]:
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
5 | x = ab
|
||||
|
|
||||
"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1914,7 +2009,26 @@ def function():
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
Unknown
|
||||
---------------------------------------------
|
||||
```python
|
||||
Unknown
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:10:30
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^-
|
||||
| ||
|
||||
| |Cursor offset
|
||||
| source
|
||||
11 | x = ab
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2011,7 +2125,26 @@ def function():
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||
assert_snapshot!(test.hover(), @r"
|
||||
Unknown
|
||||
---------------------------------------------
|
||||
```python
|
||||
Unknown
|
||||
```
|
||||
---------------------------------------------
|
||||
info[hover]: Hovered content is
|
||||
--> main.py:10:23
|
||||
|
|
||||
8 | def my_func(event: Click):
|
||||
9 | match event:
|
||||
10 | case Click(x, button=ab):
|
||||
| ^^^-^^
|
||||
| | |
|
||||
| | Cursor offset
|
||||
| source
|
||||
11 | x = ab
|
||||
|
|
||||
");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ruff_db::files::{File, FilePath};
|
||||
use ruff_db::source::{line_index, source_text};
|
||||
use ruff_python_ast::{self as ast, ExprStringLiteral, ModExpression};
|
||||
use ruff_python_ast::{self as ast, ExprStringLiteral, Identifier, ModExpression};
|
||||
use ruff_python_ast::{Expr, ExprRef, HasNodeIndex, name::Name};
|
||||
use ruff_python_parser::Parsed;
|
||||
use ruff_source_file::LineIndex;
|
||||
@@ -90,6 +90,19 @@ impl<'db> SemanticModel<'db> {
|
||||
members
|
||||
}
|
||||
|
||||
pub fn inferred_type_for_identifier(&self, identifier: &Identifier) -> Type<'db> {
|
||||
// TODO(#1637): semantic tokens is making this crash even with
|
||||
// `try_expr_ref_in_ast` guarding this, for now just use `try_expression_scope_id`.
|
||||
// The problematic input is `x: "float` (with a dangling quote). I imagine the issue
|
||||
// is we're too eagerly setting `is_string_annotation` in inference.
|
||||
let Some(file_scope) = self.scope(identifier.into()) else {
|
||||
return Type::unknown();
|
||||
};
|
||||
let scope = file_scope.to_scope_id(self.db, self.file);
|
||||
|
||||
infer_scope_types(self.db, scope).expression_type(identifier)
|
||||
}
|
||||
|
||||
/// Resolve the given import made in this file to a Type
|
||||
pub fn resolve_module_type(&self, module: Option<&str>, level: u32) -> Option<Type<'db>> {
|
||||
let module = self.resolve_module(module, level)?;
|
||||
|
||||
@@ -6507,6 +6507,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
if symbol.is_bound() || symbol.is_declared() {
|
||||
// This name is explicitly defined in the global scope (not just in function
|
||||
// bodies that mark it `global`).
|
||||
//
|
||||
// TODO: call `self.store_expression_type(name)` here with the computed type of
|
||||
// the variable (requires `infer_load_name` but for `Identifier`). This would
|
||||
// empower displaying the type on-hover in the LSP.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -6588,6 +6592,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
// but it's ok if it's `nonlocal`. If a "chain" of `nonlocal` statements fails to
|
||||
// lead to a valid binding, the outermost one will be an error; we don't need to
|
||||
// walk the whole chain for each one.
|
||||
//
|
||||
// TODO: call `self.store_expression_type(name)` here with the computed type of
|
||||
// the variable (requires `infer_load_name` but for `Identifier`). This would
|
||||
// empower displaying the type on-hover in the LSP.
|
||||
continue 'names;
|
||||
}
|
||||
// There's no matching binding in an enclosing scope. This `nonlocal` statement is
|
||||
|
||||
Reference in New Issue
Block a user