Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/pyrefly_config/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ impl Preset {
let errors = HashMap::from([
(ErrorKind::DirectAbstractBaseInstantiation, Severity::Error),
(ErrorKind::ImplicitAny, Severity::Error),
(ErrorKind::InstanceVarAssign, Severity::Error),
(ErrorKind::MethodAssign, Severity::Error),
(ErrorKind::MissingOverrideDecorator, Severity::Error),
(ErrorKind::OpenUnpacking, Severity::Error),
Expand Down
3 changes: 3 additions & 0 deletions crates/pyrefly_config/src/error_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ pub enum ErrorKind {
/// An inconsistency between a function parameter's type in an overload signature and its
/// default value in the implementation.
InconsistentOverloadDefault,
/// Cannot assign to a class-body-declared instance variable via the class (e.g. `C.instance_var = ...`).
InstanceVarAssign,
/// Internal Pyrefly error.
InternalError,
/// An `@abstractmethod` is defined in a class that is not abstract.
Expand Down Expand Up @@ -638,6 +640,7 @@ impl ErrorKind {
ErrorKind::ImplicitReexport => Severity::Ignore,
ErrorKind::ImplicitlyDefinedAttribute => Severity::Ignore,
ErrorKind::IncompatibleComparison => Severity::Ignore,
ErrorKind::InstanceVarAssign => Severity::Ignore,
ErrorKind::InvalidAbstractMethod => Severity::Ignore,
ErrorKind::InvalidCast => Severity::Ignore,
ErrorKind::InvalidDecorator => Severity::Warn,
Expand Down
24 changes: 17 additions & 7 deletions pyrefly/lib/alt/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1463,7 +1463,7 @@ impl<'ctx, 'answer, Ans: LookupAnswer> AnswersSolver<'ctx, 'answer, Ans> {
return None;
};
let ty = match class_attr {
ClassAttribute::ReadWrite(ty, _) | ClassAttribute::ReadOnly(ty, _) => ty,
ClassAttribute::ReadWrite(ty, ..) | ClassAttribute::ReadOnly(ty, _) => ty,
_ => return None,
};
let Type::BoundMethod(bound_method) = ty else {
Expand All @@ -1486,8 +1486,8 @@ impl<'ctx, 'answer, Ans: LookupAnswer> AnswersSolver<'ctx, 'answer, Ans> {
new_bound_method.func = BoundMethodType::Overload(filtered_overload);
let new_method = self.heap.mk_bound_method(new_bound_method);
Some(Attribute::ClassAttribute(match class_attr {
ClassAttribute::ReadWrite(_, is_method) => {
ClassAttribute::ReadWrite(new_method, *is_method)
ClassAttribute::ReadWrite(_, is_method, is_instance_var) => {
ClassAttribute::ReadWrite(new_method, *is_method, *is_instance_var)
}
ClassAttribute::ReadOnly(_, reason) => {
ClassAttribute::ReadOnly(new_method, reason.clone())
Expand Down Expand Up @@ -1536,7 +1536,7 @@ impl<'ctx, 'answer, Ans: LookupAnswer> AnswersSolver<'ctx, 'answer, Ans> {
{
let want_is_dunder_method = attr_name.starts_with("__")
&& attr_name.ends_with("__")
&& matches!(want, ClassAttribute::ReadWrite(_, IsMethod::Yes));
&& matches!(want, ClassAttribute::ReadWrite(_, IsMethod::Yes, _));
for (got_attr, _) in got_attrs.iter() {
// A `__getattr__` fallback is not evidence that the missing member exists
// when Python would not consult `__getattr__` for it: `__getattr__` governs
Expand Down Expand Up @@ -1742,17 +1742,23 @@ impl<'ctx, 'answer, Ans: LookupAnswer> AnswersSolver<'ctx, 'answer, Ans> {
let mut read_only_reason = None;
let mut is_class_attribute = false;
let mut is_method = IsMethod::No;
let mut is_instance_var = false;
for base_results in candidates {
let [(attribute, _)] = base_results.as_slice() else {
return None;
};
let ty = match attribute {
Attribute::Simple(ty) => ty,
Attribute::ClassAttribute(ClassAttribute::ReadWrite(ty, base_is_method)) => {
Attribute::ClassAttribute(ClassAttribute::ReadWrite(
ty,
base_is_method,
base_is_instance_var,
)) => {
is_class_attribute = true;
if *base_is_method == IsMethod::Yes {
is_method = IsMethod::Yes;
}
is_instance_var |= *base_is_instance_var;
ty
}
Attribute::ClassAttribute(ClassAttribute::ReadOnly(ty, reason)) => {
Expand All @@ -1775,7 +1781,11 @@ impl<'ctx, 'answer, Ans: LookupAnswer> AnswersSolver<'ctx, 'answer, Ans> {
Some(reason) => {
Attribute::class_attribute(ClassAttribute::read_only(combined, reason))
}
None => Attribute::class_attribute(ClassAttribute::ReadWrite(combined, is_method)),
None => Attribute::class_attribute(ClassAttribute::ReadWrite(
combined,
is_method,
is_instance_var,
)),
}
} else {
Attribute::simple(combined)
Expand Down Expand Up @@ -3574,7 +3584,7 @@ impl<'ctx, 'answer, Ans: LookupAnswer> AnswersSolver<'ctx, 'answer, Ans> {
.into_iter()
.filter_map(|(attr, _)| {
match &attr {
Attribute::ClassAttribute(ClassAttribute::ReadWrite(ty, _))
Attribute::ClassAttribute(ClassAttribute::ReadWrite(ty, ..))
| Attribute::ClassAttribute(ClassAttribute::ReadOnly(ty, _))
| Attribute::Simple(ty)
| Attribute::ClassAttribute(ClassAttribute::Property(ty, _, _))
Expand Down
Loading
Loading