Skip to content

Commit 9c3c18b

Browse files
committed
Remove 'is_read_only' check
1 parent 4807564 commit 9c3c18b

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
@@ -3442,20 +3442,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
34423442
| Type::AlwaysTruthy
34433443
| Type::AlwaysFalsy
34443444
| Type::TypeIs(_) => {
3445-
let is_read_only = || {
3446-
let dataclass_params = match object_ty {
3447-
Type::NominalInstance(instance) => match instance.class {
3448-
ClassType::NonGeneric(cls) => cls.dataclass_params(self.db()),
3449-
ClassType::Generic(cls) => {
3450-
cls.origin(self.db()).dataclass_params(self.db())
3451-
}
3452-
},
3453-
_ => None,
3454-
};
3455-
3456-
dataclass_params.is_some_and(|params| params.contains(DataclassParams::FROZEN))
3457-
};
3458-
34593445
// First, try to call the `__setattr__` dunder method. If this is present/defined, overrides
34603446
// assigning the attributed by the normal mechanism.
34613447
let setattr_dunder_call_result = object_ty.try_call_dunder_with_policy(
@@ -3493,7 +3479,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
34933479

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

3604-
successful_call
3605-
} else {
3606-
ensure_assignable_to(meta_attr_ty)
3607-
};
3577+
successful_call
3578+
} else {
3579+
ensure_assignable_to(meta_attr_ty)
3580+
};
36083581

3609-
let assignable_to_instance_attribute =
3610-
if meta_attr_boundness == Boundness::PossiblyUnbound {
3611-
let (assignable, boundness) = if let Place::Type(
3612-
instance_attr_ty,
3582+
let assignable_to_instance_attribute =
3583+
if meta_attr_boundness == Boundness::PossiblyUnbound {
3584+
let (assignable, boundness) = if let Place::Type(
3585+
instance_attr_ty,
3586+
instance_attr_boundness,
3587+
) =
3588+
object_ty.instance_member(db, attribute).place
3589+
{
3590+
(
3591+
ensure_assignable_to(instance_attr_ty),
36133592
instance_attr_boundness,
3614-
) =
3615-
object_ty.instance_member(db, attribute).place
3616-
{
3617-
(
3618-
ensure_assignable_to(instance_attr_ty),
3619-
instance_attr_boundness,
3620-
)
3621-
} else {
3622-
(true, Boundness::PossiblyUnbound)
3623-
};
3624-
3625-
if boundness == Boundness::PossiblyUnbound {
3626-
report_possibly_unbound_attribute(
3627-
&self.context,
3628-
target,
3629-
attribute,
3630-
object_ty,
3631-
);
3632-
}
3633-
3634-
assignable
3593+
)
36353594
} else {
3636-
true
3595+
(true, Boundness::PossiblyUnbound)
36373596
};
36383597

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

36433615
PlaceAndQualifiers {
@@ -3656,22 +3628,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
36563628
);
36573629
}
36583630

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

0 commit comments

Comments
 (0)