Skip to content

Commit f5bb268

Browse files
committed
Remove 'is_read_only' check
1 parent 4626c63 commit f5bb268

File tree

3 files changed

+58
-101
lines changed

3 files changed

+58
-101
lines changed

crates/ty_python_semantic/resources/mdtest/attributes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1806,7 +1806,7 @@ class Frozen:
18061806
raise AttributeError("Attributes can not be modified")
18071807

18081808
instance = Frozen()
1809-
instance.non_existing = 2 # error: [invalid-assignment] "Cannot assign to attribute `non_existing` on type `Frozen` whose `__setattr__` method returns `Never`/`NoReturn`"
1809+
instance.non_existing = 2 # error: [invalid-assignment] "Can not assign to unresolved attribute `non_existing` on type `Frozen`"
18101810
instance.existing = 2 # error: [invalid-assignment] "Cannot assign to attribute `existing` on type `Frozen` whose `__setattr__` method returns `Never`/`NoReturn`"
18111811
```
18121812

crates/ty_python_semantic/resources/mdtest/dataclasses/dataclasses.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ from dataclasses import dataclass
424424
class MyFrozenClass: ...
425425

426426
frozen = MyFrozenClass()
427-
frozen.x = 2 # error: [invalid-assignment] "Unresolved attribute `x` on type `MyFrozenClass"
427+
frozen.x = 2 # error: [invalid-assignment] "Can not assign to unresolved attribute `x` on type `MyFrozenClass`"
428428
```
429429

430430
A diagnostic is also emitted if a frozen dataclass is inherited, and an attempt is made to mutate an

crates/ty_python_semantic/src/types/infer.rs

Lines changed: 56 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -3446,20 +3446,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
34463446
| Type::AlwaysTruthy
34473447
| Type::AlwaysFalsy
34483448
| Type::TypeIs(_) => {
3449-
let is_read_only = || {
3450-
let dataclass_params = match object_ty {
3451-
Type::NominalInstance(instance) => match instance.class {
3452-
ClassType::NonGeneric(cls) => cls.dataclass_params(self.db()),
3453-
ClassType::Generic(cls) => {
3454-
cls.origin(self.db()).dataclass_params(self.db())
3455-
}
3456-
},
3457-
_ => None,
3458-
};
3459-
3460-
dataclass_params.is_some_and(|params| params.contains(DataclassParams::FROZEN))
3461-
};
3462-
34633449
// First, try to call the `__setattr__` dunder method. If this is present/defined, overrides
34643450
// assigning the attributed by the normal mechanism.
34653451
let setattr_dunder_call_result = object_ty.try_call_dunder_with_policy(
@@ -3494,7 +3480,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
34943480

34953481
let msg = if !member_exists {
34963482
format!(
3497-
"Unresolved attribute `{attribute}` on type `{}`",
3483+
"Can not assign to unresolved attribute `{attribute}` on type `{}`",
34983484
object_ty.display(db)
34993485
)
35003486
} else if is_setattr_synthesized {
@@ -3560,85 +3546,71 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
35603546
place: Place::Type(meta_attr_ty, meta_attr_boundness),
35613547
qualifiers: _,
35623548
} => {
3563-
if is_read_only() {
3564-
if emit_diagnostics {
3565-
if let Some(builder) =
3566-
self.context.report_lint(&INVALID_ASSIGNMENT, target)
3567-
{
3568-
builder.into_diagnostic(format_args!(
3569-
"Property `{attribute}` defined in `{ty}` is read-only",
3570-
ty = object_ty.display(self.db()),
3571-
));
3572-
}
3573-
}
3574-
false
3575-
} else {
3576-
let assignable_to_meta_attr =
3577-
if let Place::Type(meta_dunder_set, _) =
3578-
meta_attr_ty.class_member(db, "__set__".into()).place
3579-
{
3580-
let successful_call = meta_dunder_set
3581-
.try_call(
3582-
db,
3583-
&CallArguments::positional([
3584-
meta_attr_ty,
3585-
object_ty,
3586-
value_ty,
3587-
]),
3588-
)
3589-
.is_ok();
3590-
3591-
if !successful_call && emit_diagnostics {
3592-
if let Some(builder) = self
3593-
.context
3594-
.report_lint(&INVALID_ASSIGNMENT, target)
3595-
{
3596-
// TODO: Here, it would be nice to emit an additional diagnostic that explains why the call failed
3597-
builder.into_diagnostic(format_args!(
3549+
let assignable_to_meta_attr =
3550+
if let Place::Type(meta_dunder_set, _) =
3551+
meta_attr_ty.class_member(db, "__set__".into()).place
3552+
{
3553+
let successful_call = meta_dunder_set
3554+
.try_call(
3555+
db,
3556+
&CallArguments::positional([
3557+
meta_attr_ty,
3558+
object_ty,
3559+
value_ty,
3560+
]),
3561+
)
3562+
.is_ok();
3563+
3564+
if !successful_call && emit_diagnostics {
3565+
if let Some(builder) = self
3566+
.context
3567+
.report_lint(&INVALID_ASSIGNMENT, target)
3568+
{
3569+
// TODO: Here, it would be nice to emit an additional diagnostic that explains why the call failed
3570+
builder.into_diagnostic(format_args!(
35983571
"Invalid assignment to data descriptor attribute \
35993572
`{attribute}` on type `{}` with custom `__set__` method",
36003573
object_ty.display(db)
36013574
));
3602-
}
36033575
}
3576+
}
36043577

3605-
successful_call
3606-
} else {
3607-
ensure_assignable_to(meta_attr_ty)
3608-
};
3578+
successful_call
3579+
} else {
3580+
ensure_assignable_to(meta_attr_ty)
3581+
};
36093582

3610-
let assignable_to_instance_attribute =
3611-
if meta_attr_boundness == Boundness::PossiblyUnbound {
3612-
let (assignable, boundness) = if let Place::Type(
3613-
instance_attr_ty,
3583+
let assignable_to_instance_attribute =
3584+
if meta_attr_boundness == Boundness::PossiblyUnbound {
3585+
let (assignable, boundness) = if let Place::Type(
3586+
instance_attr_ty,
3587+
instance_attr_boundness,
3588+
) =
3589+
object_ty.instance_member(db, attribute).place
3590+
{
3591+
(
3592+
ensure_assignable_to(instance_attr_ty),
36143593
instance_attr_boundness,
3615-
) =
3616-
object_ty.instance_member(db, attribute).place
3617-
{
3618-
(
3619-
ensure_assignable_to(instance_attr_ty),
3620-
instance_attr_boundness,
3621-
)
3622-
} else {
3623-
(true, Boundness::PossiblyUnbound)
3624-
};
3625-
3626-
if boundness == Boundness::PossiblyUnbound {
3627-
report_possibly_unbound_attribute(
3628-
&self.context,
3629-
target,
3630-
attribute,
3631-
object_ty,
3632-
);
3633-
}
3634-
3635-
assignable
3594+
)
36363595
} else {
3637-
true
3596+
(true, Boundness::PossiblyUnbound)
36383597
};
36393598

3640-
assignable_to_meta_attr && assignable_to_instance_attribute
3641-
}
3599+
if boundness == Boundness::PossiblyUnbound {
3600+
report_possibly_unbound_attribute(
3601+
&self.context,
3602+
target,
3603+
attribute,
3604+
object_ty,
3605+
);
3606+
}
3607+
3608+
assignable
3609+
} else {
3610+
true
3611+
};
3612+
3613+
assignable_to_meta_attr && assignable_to_instance_attribute
36423614
}
36433615

36443616
PlaceAndQualifiers {
@@ -3657,22 +3629,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
36573629
);
36583630
}
36593631

3660-
if is_read_only() {
3661-
if emit_diagnostics {
3662-
if let Some(builder) = self
3663-
.context
3664-
.report_lint(&INVALID_ASSIGNMENT, target)
3665-
{
3666-
builder.into_diagnostic(format_args!(
3667-
"Property `{attribute}` defined in `{ty}` is read-only",
3668-
ty = object_ty.display(self.db()),
3669-
));
3670-
}
3671-
}
3672-
false
3673-
} else {
3674-
ensure_assignable_to(instance_attr_ty)
3675-
}
3632+
ensure_assignable_to(instance_attr_ty)
36763633
} else {
36773634
if emit_diagnostics {
36783635
if let Some(builder) =

0 commit comments

Comments
 (0)