mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-25 09:28:14 +00:00
[ty] Fix panic when attempting to validate the members of a protocol that inherits from a protocol in another module (#20956)
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 (macos) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
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 / ty completion evaluation (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 (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks walltime (medium|multithreaded) (push) Blocked by required conditions
CI / benchmarks walltime (small|large) (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 (macos) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
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 / ty completion evaluation (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 (ruff) (push) Blocked by required conditions
CI / benchmarks instrumented (ty) (push) Blocked by required conditions
CI / benchmarks walltime (medium|multithreaded) (push) Blocked by required conditions
CI / benchmarks walltime (small|large) (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run
This commit is contained in:
parent
16efe53a72
commit
68c1fa86c8
4 changed files with 63 additions and 17 deletions
|
|
@ -1114,6 +1114,8 @@ it's a large section).
|
||||||
|
|
||||||
<!-- snapshot-diagnostics -->
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
|
`a.py`:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Protocol
|
from typing import Protocol
|
||||||
|
|
||||||
|
|
@ -1140,6 +1142,31 @@ class A(Protocol):
|
||||||
pass
|
pass
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Validation of protocols that had cross-module inheritance used to break, so we test that explicitly
|
||||||
|
here too:
|
||||||
|
|
||||||
|
`b.py`:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Protocol
|
||||||
|
|
||||||
|
# Ensure the number of scopes in `b.py` is greater than the number of scopes in `c.py`:
|
||||||
|
class SomethingUnrelated: ...
|
||||||
|
|
||||||
|
class A(Protocol):
|
||||||
|
x: int
|
||||||
|
```
|
||||||
|
|
||||||
|
`c.py`:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from b import A
|
||||||
|
from typing import Protocol
|
||||||
|
|
||||||
|
class C(A, Protocol):
|
||||||
|
x = 42 # fine, due to declaration in the base class
|
||||||
|
```
|
||||||
|
|
||||||
## Equivalence of protocols
|
## Equivalence of protocols
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/protocols.md
|
||||||
|
|
||||||
# Python source files
|
# Python source files
|
||||||
|
|
||||||
## mdtest_snippet.py
|
## a.py
|
||||||
|
|
||||||
```
|
```
|
||||||
1 | from typing import Protocol
|
1 | from typing import Protocol
|
||||||
|
|
@ -37,11 +37,33 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/protocols.md
|
||||||
23 | pass
|
23 | pass
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## b.py
|
||||||
|
|
||||||
|
```
|
||||||
|
1 | from typing import Protocol
|
||||||
|
2 |
|
||||||
|
3 | # Ensure the number of scopes in `b.py` is greater than the number of scopes in `c.py`:
|
||||||
|
4 | class SomethingUnrelated: ...
|
||||||
|
5 |
|
||||||
|
6 | class A(Protocol):
|
||||||
|
7 | x: int
|
||||||
|
```
|
||||||
|
|
||||||
|
## c.py
|
||||||
|
|
||||||
|
```
|
||||||
|
1 | from b import A
|
||||||
|
2 | from typing import Protocol
|
||||||
|
3 |
|
||||||
|
4 | class C(A, Protocol):
|
||||||
|
5 | x = 42 # fine, due to declaration in the base class
|
||||||
|
```
|
||||||
|
|
||||||
# Diagnostics
|
# Diagnostics
|
||||||
|
|
||||||
```
|
```
|
||||||
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
||||||
--> src/mdtest_snippet.py:12:5
|
--> src/a.py:12:5
|
||||||
|
|
|
|
||||||
11 | # error: [ambiguous-protocol-member]
|
11 | # error: [ambiguous-protocol-member]
|
||||||
12 | a = None # type: int
|
12 | a = None # type: int
|
||||||
|
|
@ -50,7 +72,7 @@ warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the
|
||||||
14 | b = ... # type: str
|
14 | b = ... # type: str
|
||||||
|
|
|
|
||||||
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
||||||
--> src/mdtest_snippet.py:6:7
|
--> src/a.py:6:7
|
||||||
|
|
|
|
||||||
4 | return True
|
4 | return True
|
||||||
5 |
|
5 |
|
||||||
|
|
@ -66,7 +88,7 @@ info: rule `ambiguous-protocol-member` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
||||||
--> src/mdtest_snippet.py:14:5
|
--> src/a.py:14:5
|
||||||
|
|
|
|
||||||
12 | a = None # type: int
|
12 | a = None # type: int
|
||||||
13 | # error: [ambiguous-protocol-member]
|
13 | # error: [ambiguous-protocol-member]
|
||||||
|
|
@ -76,7 +98,7 @@ warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the
|
||||||
16 | if coinflip():
|
16 | if coinflip():
|
||||||
|
|
|
|
||||||
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
||||||
--> src/mdtest_snippet.py:6:7
|
--> src/a.py:6:7
|
||||||
|
|
|
|
||||||
4 | return True
|
4 | return True
|
||||||
5 |
|
5 |
|
||||||
|
|
@ -92,7 +114,7 @@ info: rule `ambiguous-protocol-member` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
||||||
--> src/mdtest_snippet.py:17:9
|
--> src/a.py:17:9
|
||||||
|
|
|
|
||||||
16 | if coinflip():
|
16 | if coinflip():
|
||||||
17 | c = 1 # error: [ambiguous-protocol-member]
|
17 | c = 1 # error: [ambiguous-protocol-member]
|
||||||
|
|
@ -101,7 +123,7 @@ warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the
|
||||||
19 | c = 2
|
19 | c = 2
|
||||||
|
|
|
|
||||||
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
||||||
--> src/mdtest_snippet.py:6:7
|
--> src/a.py:6:7
|
||||||
|
|
|
|
||||||
4 | return True
|
4 | return True
|
||||||
5 |
|
5 |
|
||||||
|
|
@ -117,7 +139,7 @@ info: rule `ambiguous-protocol-member` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the body of a protocol class
|
||||||
--> src/mdtest_snippet.py:22:9
|
--> src/a.py:22:9
|
||||||
|
|
|
|
||||||
21 | # error: [ambiguous-protocol-member]
|
21 | # error: [ambiguous-protocol-member]
|
||||||
22 | for d in range(42):
|
22 | for d in range(42):
|
||||||
|
|
@ -125,7 +147,7 @@ warning[ambiguous-protocol-member]: Cannot assign to undeclared variable in the
|
||||||
23 | pass
|
23 | pass
|
||||||
|
|
|
|
||||||
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
info: Assigning to an undeclared variable in a protocol class leads to an ambiguous interface
|
||||||
--> src/mdtest_snippet.py:6:7
|
--> src/a.py:6:7
|
||||||
|
|
|
|
||||||
4 | return True
|
4 | return True
|
||||||
5 |
|
5 |
|
||||||
|
|
|
||||||
|
|
@ -932,7 +932,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(protocol) = class.into_protocol_class(self.db()) {
|
if let Some(protocol) = class.into_protocol_class(self.db()) {
|
||||||
protocol.validate_members(&self.context, self.index);
|
protocol.validate_members(&self.context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,7 @@ use crate::types::TypeContext;
|
||||||
use crate::{
|
use crate::{
|
||||||
Db, FxOrderSet,
|
Db, FxOrderSet,
|
||||||
place::{Definedness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
|
place::{Definedness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
|
||||||
semantic_index::{
|
semantic_index::{definition::Definition, place::ScopedPlaceId, place_table, use_def_map},
|
||||||
SemanticIndex, definition::Definition, place::ScopedPlaceId, place_table, use_def_map,
|
|
||||||
},
|
|
||||||
types::{
|
types::{
|
||||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral,
|
ApplyTypeMappingVisitor, BoundTypeVarInstance, CallableType, ClassBase, ClassLiteral,
|
||||||
ClassType, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
ClassType, FindLegacyTypeVarsVisitor, HasRelationToVisitor,
|
||||||
|
|
@ -77,11 +75,11 @@ impl<'db> ProtocolClass<'db> {
|
||||||
/// Iterate through the body of the protocol class. Check that all definitions
|
/// Iterate through the body of the protocol class. Check that all definitions
|
||||||
/// in the protocol class body are either explicitly declared directly in the
|
/// in the protocol class body are either explicitly declared directly in the
|
||||||
/// class body, or are declared in a superclass of the protocol class.
|
/// class body, or are declared in a superclass of the protocol class.
|
||||||
pub(super) fn validate_members(self, context: &InferContext, index: &SemanticIndex<'db>) {
|
pub(super) fn validate_members(self, context: &InferContext) {
|
||||||
let db = context.db();
|
let db = context.db();
|
||||||
let interface = self.interface(db);
|
let interface = self.interface(db);
|
||||||
let body_scope = self.class_literal(db).0.body_scope(db);
|
let body_scope = self.class_literal(db).0.body_scope(db);
|
||||||
let class_place_table = index.place_table(body_scope.file_scope_id(db));
|
let class_place_table = place_table(db, body_scope);
|
||||||
|
|
||||||
for (symbol_id, mut bindings_iterator) in
|
for (symbol_id, mut bindings_iterator) in
|
||||||
use_def_map(db, body_scope).all_end_of_scope_symbol_bindings()
|
use_def_map(db, body_scope).all_end_of_scope_symbol_bindings()
|
||||||
|
|
@ -104,8 +102,7 @@ impl<'db> ProtocolClass<'db> {
|
||||||
};
|
};
|
||||||
!place_from_declarations(
|
!place_from_declarations(
|
||||||
db,
|
db,
|
||||||
index
|
use_def_map(db, superclass_scope)
|
||||||
.use_def_map(superclass_scope.file_scope_id(db))
|
|
||||||
.end_of_scope_declarations(ScopedPlaceId::Symbol(scoped_symbol_id)),
|
.end_of_scope_declarations(ScopedPlaceId::Symbol(scoped_symbol_id)),
|
||||||
)
|
)
|
||||||
.into_place_and_conflicting_declarations()
|
.into_place_and_conflicting_declarations()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue