mirror of https://github.com/astral-sh/ruff
Update tests
This commit is contained in:
parent
8d53802bc7
commit
7b7c8425b8
|
|
@ -380,6 +380,11 @@ reveal_type(c_instance.y) # revealed: Unknown | int
|
|||
|
||||
#### Attributes defined in comprehensions
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
class IntIterator:
|
||||
def __next__(self) -> int:
|
||||
|
|
@ -405,13 +410,6 @@ class C:
|
|||
[[... for self.f in IntIterable()] for _ in IntIterable()]
|
||||
[[... for self.g in IntIterable()] for self in [D()]]
|
||||
|
||||
def f():
|
||||
[... for self.h in IntIterable()]
|
||||
|
||||
def g():
|
||||
[... for self.i in IntIterable()]
|
||||
g()
|
||||
|
||||
class D:
|
||||
g: int
|
||||
|
||||
|
|
@ -432,14 +430,56 @@ reveal_type(c_instance.f) # revealed: Unknown | int
|
|||
# This one is correctly not resolved as an attribute:
|
||||
# error: [unresolved-attribute]
|
||||
reveal_type(c_instance.g) # revealed: Unknown
|
||||
```
|
||||
|
||||
It does not matter how much the comprehension is nested.
|
||||
|
||||
Similarly attributes defined by the comprehension in a generic method are recognized.
|
||||
|
||||
```py
|
||||
class C:
|
||||
def f[T](self):
|
||||
[... for self.a in [1]]
|
||||
[[... for self.b in [1]] for _ in [1]]
|
||||
|
||||
c_instance = C()
|
||||
|
||||
reveal_type(c_instance.a) # revealed: Unknown | int
|
||||
reveal_type(c_instance.b) # revealed: Unknown | int
|
||||
```
|
||||
|
||||
If the comprehension is inside another scope like function then that attribute is not inferred.
|
||||
|
||||
```py
|
||||
class C:
|
||||
def __init__(self):
|
||||
def f():
|
||||
[... for self.a in IntIterable()]
|
||||
|
||||
def g():
|
||||
[... for self.b in IntIterable()]
|
||||
g()
|
||||
|
||||
c_instance = C()
|
||||
|
||||
# This attribute is in the function f and is not reachable
|
||||
# error: [unresolved-attribute]
|
||||
reveal_type(c_instance.h) # revealed: Unknown
|
||||
reveal_type(c_instance.a) # revealed: Unknown
|
||||
|
||||
# TODO: Even though g method is called and is reachable we do not record this attribute assignment
|
||||
# error: [unresolved-attribute]
|
||||
reveal_type(c_instance.i) # revealed: Unknown
|
||||
reveal_type(c_instance.b) # revealed: Unknown
|
||||
```
|
||||
|
||||
If the comprehension is nested in any other eager scope it still can assign attributes.
|
||||
|
||||
```py
|
||||
class C:
|
||||
def __init__(self):
|
||||
class D:
|
||||
[[... for self.a in IntIterable()] for _ in IntIterable()]
|
||||
|
||||
reveal_type(C().a) # revealed: Unknown | int
|
||||
```
|
||||
|
||||
#### Conditionally declared / bound attributes
|
||||
|
|
|
|||
|
|
@ -1167,7 +1167,6 @@ def f(a: str, /, b: str, c: int = 1, *args, d: int = 2, **kwargs):
|
|||
"
|
||||
class C:
|
||||
def __init__(self):
|
||||
self.x = 1
|
||||
[None for self.z in range(1)]
|
||||
",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -185,10 +185,10 @@ impl<'db, 'ast> SemanticIndexBuilder<'db, 'ast> {
|
|||
}
|
||||
|
||||
/// Returns the scope ID of the method scope if the current scope
|
||||
/// is a method inside a class body. Returns `None` otherwise, e.g. if the current
|
||||
/// scope is a function body outside of a class, or if the current scope is not a
|
||||
/// is a method inside a class body or current scope is in eagerly executed scope in a method.
|
||||
/// Returns `None` otherwise, e.g. if the current scope is a function body outside of a class, or if the current scope is not a
|
||||
/// function body.
|
||||
fn is_method_of_class(&self) -> Option<FileScopeId> {
|
||||
fn is_method_or_eagerly_executed_in_method(&self) -> Option<FileScopeId> {
|
||||
let mut scopes_rev = self
|
||||
.scope_stack
|
||||
.iter()
|
||||
|
|
@ -1678,7 +1678,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
|||
self.visit_expr(&node.annotation);
|
||||
if let Some(value) = &node.value {
|
||||
self.visit_expr(value);
|
||||
if self.is_method_of_class().is_some() {
|
||||
if self.is_method_or_eagerly_executed_in_method().is_some() {
|
||||
// Record the right-hand side of the assignment as a standalone expression
|
||||
// if we're inside a method. This allows type inference to infer the type
|
||||
// of the value for annotated assignments like `self.CONSTANT: Final = 1`,
|
||||
|
|
@ -2350,7 +2350,7 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
|
|||
| ast::Expr::Attribute(ast::ExprAttribute { ctx, .. })
|
||||
| ast::Expr::Subscript(ast::ExprSubscript { ctx, .. }) => {
|
||||
if let Some(mut place_expr) = PlaceExpr::try_from_expr(expr) {
|
||||
if let Some(method_scope_id) = self.is_method_of_class() {
|
||||
if let Some(method_scope_id) = self.is_method_or_eagerly_executed_in_method() {
|
||||
if let PlaceExpr::Member(member) = &mut place_expr {
|
||||
if member.is_instance_attribute_candidate() {
|
||||
// We specifically mark attribute assignments to the first parameter of a method,
|
||||
|
|
|
|||
Loading…
Reference in New Issue