@@ -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