This commit is contained in:
Alex Waygood
2025-08-16 17:05:54 +01:00
parent 386b0116a6
commit 8379673369
3 changed files with 39 additions and 19 deletions

View File

@@ -389,7 +389,7 @@ To see the kinds and types of the protocol members, you can use the debugging ai
```py
from ty_extensions import reveal_protocol_interface
from typing import SupportsIndex, SupportsAbs
from typing import SupportsIndex, SupportsAbs, ClassVar
# error: [revealed-type] "Revealed protocol interface: `{"method_member": MethodMember(`(self) -> bytes`), "x": AttributeMember(`int`), "y": PropertyMember { get_type: `str` }, "z": PropertyMember { get_type: `int`, set_type: `int` }}`"
reveal_protocol_interface(Foo)
@@ -419,6 +419,12 @@ reveal_protocol_interface(BaseProto)
# error: [revealed-type] "Revealed protocol interface: `{"member": MethodMember(`(self) -> bool`)}`"
reveal_protocol_interface(SubProto)
class ProtoWithClassVar(Protocol):
x: ClassVar[int]
# error: [revealed-type] "Revealed protocol interface: `{"x": AttributeMember(`int`; ClassVar)}`"
reveal_protocol_interface(ProtoWithClassVar)
```
Certain special attributes and methods are not considered protocol members at runtime, and should

View File

@@ -8,8 +8,9 @@ use rustc_hash::FxHashMap;
use super::TypeVarVariance;
use crate::semantic_index::place_table;
use crate::types::signatures::ParameterForm;
use crate::types::{AttributeAssignmentError, CallArguments, HasRelationToVisitor, UnionType};
use crate::types::{
AttributeAssignmentError, CallArguments, HasRelationToVisitor, Signature, UnionType,
};
use crate::{
Db, FxOrderSet,
place::{Boundness, Place, PlaceAndQualifiers, place_from_bindings, place_from_declarations},
@@ -294,6 +295,7 @@ impl<'db> ProtocolMemberData<'db> {
struct ProtocolMemberDataDisplay<'a, 'db> {
db: &'db dyn Db,
data: &'a ProtocolMemberKind<'db>,
qualifiers: TypeQualifiers,
}
impl std::fmt::Display for ProtocolMemberDataDisplay<'_, '_> {
@@ -312,8 +314,16 @@ impl<'db> ProtocolMemberData<'db> {
}
d.finish()
}
ProtocolMemberKind::Attribute(attribute) => {
write!(f, "AttributeMember(`{}`)", attribute.ty.display(self.db))
ProtocolMemberKind::Attribute(AttributeMember {
ty,
bound_on_class: _,
}) => {
f.write_str("AttributeMember(")?;
write!(f, "`{}`", ty.display(self.db))?;
if self.qualifiers.contains(TypeQualifiers::CLASS_VAR) {
f.write_str("; ClassVar")?;
}
f.write_char(')')
}
}
}
@@ -322,6 +332,7 @@ impl<'db> ProtocolMemberData<'db> {
ProtocolMemberDataDisplay {
db,
data: &self.kind,
qualifiers: self.qualifiers,
}
}
}
@@ -354,21 +365,17 @@ impl<'db> PropertyMember<'db> {
_ => return None,
};
let set_type_from_signature = |sig: &Signature<'db>| match sig.parameters().as_slice() {
[_, parameter] if parameter.is_positional() && parameter.form.is_value() => {
Some(parameter.annotated_type().unwrap_or_else(Type::unknown))
}
_ => None,
};
let set_type = if let Some(signature) = setter_signature {
if let Some(ty) = UnionType::try_from_elements(
db,
signature
.iter()
.map(|sig| match sig.parameters().as_slice() {
[_, parameter]
if parameter.is_positional()
&& parameter.form == ParameterForm::Value =>
{
Some(parameter.annotated_type().unwrap_or_else(Type::unknown))
}
_ => None,
}),
) {
if let Some(ty) =
UnionType::try_from_elements(db, signature.iter().map(set_type_from_signature))
{
Some(ty)
} else {
return None;

View File

@@ -1594,6 +1594,13 @@ pub(crate) enum ParameterForm {
Type,
}
impl ParameterForm {
/// Returns `true` if this is a value form.
pub(crate) const fn is_value(self) -> bool {
matches!(self, Self::Value)
}
}
#[cfg(test)]
mod tests {
use super::*;