@@ -19,7 +19,7 @@ use crate::semantic_index::{
1919use crate :: types:: bound_super:: BoundSuperError ;
2020use crate :: types:: constraints:: { ConstraintSet , IteratorConstraintsExtension } ;
2121use crate :: types:: context:: InferContext ;
22- use crate :: types:: diagnostic:: INVALID_TYPE_ALIAS_TYPE ;
22+ use crate :: types:: diagnostic:: { CANNOT_OVERWRITE_ATTRIBUTE , INVALID_TYPE_ALIAS_TYPE } ;
2323use crate :: types:: enums:: enum_metadata;
2424use crate :: types:: function:: { DataclassTransformerParams , KnownFunction } ;
2525use crate :: types:: generics:: {
@@ -2285,9 +2285,8 @@ impl<'db> ClassLiteral<'db> {
22852285 inherited_generic_context : Option < GenericContext < ' db > > ,
22862286 name : & str ,
22872287 ) -> Option < Type < ' db > > {
2288- let dataclass_params = self . dataclass_params ( db) ;
2289-
22902288 let field_policy = CodeGeneratorKind :: from_class ( db, self , specialization) ?;
2289+ let dataclass_params = self . dataclass_params ( db) ;
22912290
22922291 let mut transformer_params =
22932292 if let CodeGeneratorKind :: DataclassLike ( Some ( transformer_params) ) = field_policy {
@@ -2989,6 +2988,85 @@ impl<'db> ClassLiteral<'db> {
29892988 . collect ( )
29902989 }
29912990
2991+ pub ( crate ) fn validate_members ( self , context : & InferContext < ' db , ' _ > ) {
2992+ let db = context. db ( ) ;
2993+ let Some ( field_policy) = CodeGeneratorKind :: from_class ( db, self , None ) else {
2994+ return ;
2995+ } ;
2996+ let dataclass_params = self . dataclass_params ( db) ;
2997+ let mut transformer_params =
2998+ if let CodeGeneratorKind :: DataclassLike ( Some ( transformer_params) ) = field_policy {
2999+ Some ( DataclassParams :: from_transformer_params (
3000+ db,
3001+ transformer_params,
3002+ ) )
3003+ } else {
3004+ None
3005+ } ;
3006+
3007+ // Dataclass transformer flags can be overwritten using class arguments.
3008+ if let Some ( transformer_params) = transformer_params. as_mut ( ) {
3009+ if let Some ( class_def) = self . definition ( db) . kind ( db) . as_class ( ) {
3010+ let module = parsed_module ( db, self . file ( db) ) . load ( db) ;
3011+
3012+ if let Some ( arguments) = & class_def. node ( & module) . arguments {
3013+ let mut flags = transformer_params. flags ( db) ;
3014+
3015+ for keyword in & arguments. keywords {
3016+ if let Some ( arg_name) = & keyword. arg {
3017+ if let Some ( is_set) =
3018+ keyword. value . as_boolean_literal_expr ( ) . map ( |b| b. value )
3019+ {
3020+ for ( flag_name, flag) in DATACLASS_FLAGS {
3021+ if arg_name. as_str ( ) == * flag_name {
3022+ flags. set ( * flag, is_set) ;
3023+ }
3024+ }
3025+ }
3026+ }
3027+ }
3028+
3029+ * transformer_params =
3030+ DataclassParams :: new ( db, flags, transformer_params. field_specifiers ( db) ) ;
3031+ }
3032+ }
3033+ }
3034+
3035+ let has_dataclass_param = |param| {
3036+ dataclass_params. is_some_and ( |params| params. flags ( db) . contains ( param) )
3037+ || transformer_params. is_some_and ( |params| params. flags ( db) . contains ( param) )
3038+ } ;
3039+ let db = context. db ( ) ;
3040+ let class_body_scope = self . body_scope ( db) ;
3041+ let table = place_table ( db, class_body_scope) ;
3042+ let use_def = use_def_map ( db, class_body_scope) ;
3043+ for ( symbol_id, declarations) in use_def. all_end_of_scope_symbol_declarations ( ) {
3044+ let result = place_from_declarations ( db, declarations. clone ( ) ) ;
3045+ let attr = result. ignore_conflicting_declarations ( ) ;
3046+ let symbol = table. symbol ( symbol_id) ;
3047+ let name = symbol. name ( ) ;
3048+ if let Some ( Type :: FunctionLiteral ( literal) ) = attr. place . ignore_possibly_undefined ( )
3049+ && name == "__setattr__"
3050+ {
3051+ if let Some ( CodeGeneratorKind :: DataclassLike ( _) ) =
3052+ CodeGeneratorKind :: from_class ( db, self , None )
3053+ && has_dataclass_param ( DataclassFlags :: FROZEN )
3054+ {
3055+ if let Some ( builder) = context. report_lint (
3056+ & CANNOT_OVERWRITE_ATTRIBUTE ,
3057+ literal. node ( db, context. file ( ) , context. module ( ) ) ,
3058+ ) {
3059+ let mut diagnostic = builder. into_diagnostic ( format_args ! (
3060+ "Cannot overwrite attribute __setattr__ in class {}" ,
3061+ self . name( db)
3062+ ) ) ;
3063+ diagnostic. info ( "__setattr__" ) ;
3064+ }
3065+ }
3066+ }
3067+ }
3068+ }
3069+
29923070 /// Returns a list of all annotated attributes defined in the body of this class. This is similar
29933071 /// to the `__annotations__` attribute at runtime, but also contains default values.
29943072 ///
0 commit comments