mirror of https://github.com/astral-sh/ruff
Add tests, address review
This commit is contained in:
parent
6368fd0ea5
commit
d35e7f313d
|
|
@ -521,6 +521,36 @@ frozen = MyFrozenChildClass()
|
||||||
del frozen.x # TODO this should emit an [invalid-assignment]
|
del frozen.x # TODO this should emit an [invalid-assignment]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A diagnostic is emitted if a non-frozen dataclass inherits from a frozen dataclass:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class FrozenBase:
|
||||||
|
x: int
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Child(FrozenBase): # error: [non-frozen-subclass-of-frozen-dataclass] "A non-frozen class `Child` cannot inherit from a class `FrozenBase` that is frozen"
|
||||||
|
|
||||||
|
y: int
|
||||||
|
```
|
||||||
|
|
||||||
|
A diagnostic is emitted if a frozen dataclass inherits from a non-frozen dataclass:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Base:
|
||||||
|
x: int
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class FrozenChild(Base): # error: [frozen-subclass-of-non-frozen-dataclass] "A frozen class `FrozenChild` cannot inherit from a class `Base` that is not frozen"
|
||||||
|
|
||||||
|
y: int
|
||||||
|
```
|
||||||
|
|
||||||
### `match_args`
|
### `match_args`
|
||||||
|
|
||||||
If `match_args` is set to `True` (the default), the `__match_args__` attribute is a tuple created
|
If `match_args` is set to `True` (the default), the `__match_args__` attribute is a tuple created
|
||||||
|
|
|
||||||
|
|
@ -2227,8 +2227,8 @@ declare_lint! {
|
||||||
/// Checks for frozen dataclasses that inherit from non-frozen dataclasses.
|
/// Checks for frozen dataclasses that inherit from non-frozen dataclasses.
|
||||||
///
|
///
|
||||||
/// ## Why is this bad?
|
/// ## Why is this bad?
|
||||||
/// A frozen dataclass promises immutability. Inheriting from a non-frozen
|
/// Python raises a `TypeError` at runtime when a frozen dataclass
|
||||||
/// dataclass breaks that guarantee because the base class allows mutation.
|
/// inherits from a non-frozen dataclass.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
|
|
@ -2255,8 +2255,8 @@ declare_lint! {
|
||||||
/// Checks for non-frozen dataclasses that inherit from frozen dataclasses.
|
/// Checks for non-frozen dataclasses that inherit from frozen dataclasses.
|
||||||
///
|
///
|
||||||
/// ## Why is this bad?
|
/// ## Why is this bad?
|
||||||
/// A frozen dataclass enforces immutability. Allowing a non-frozen subclass
|
/// Python raises a `TypeError` at runtime when a non-frozen dataclass
|
||||||
/// would reintroduce mutability and violate the base class contract.
|
/// inherits from a frozen dataclass.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
|
|
@ -2278,8 +2278,6 @@ declare_lint! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// A collection of type check diagnostics.
|
/// A collection of type check diagnostics.
|
||||||
#[derive(Default, Eq, PartialEq, get_size2::GetSize)]
|
#[derive(Default, Eq, PartialEq, get_size2::GetSize)]
|
||||||
pub struct TypeCheckDiagnostics {
|
pub struct TypeCheckDiagnostics {
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,35 @@ use crate::types::call::{Binding, Bindings, CallArguments, CallError, CallErrorK
|
||||||
use crate::types::class::{CodeGeneratorKind, FieldKind, MetaclassErrorKind, MethodDecorator};
|
use crate::types::class::{CodeGeneratorKind, FieldKind, MetaclassErrorKind, MethodDecorator};
|
||||||
use crate::types::context::{InNoTypeCheck, InferContext};
|
use crate::types::context::{InNoTypeCheck, InferContext};
|
||||||
use crate::types::cyclic::CycleDetector;
|
use crate::types::cyclic::CycleDetector;
|
||||||
use crate::types::diagnostic::{self, CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS, CYCLIC_CLASS_DEFINITION, CYCLIC_TYPE_ALIAS_DEFINITION, DIVISION_BY_ZERO, DUPLICATE_KW_ONLY, INCONSISTENT_MRO, INVALID_ARGUMENT_TYPE, INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, INVALID_DECLARATION, INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_LEGACY_TYPE_VARIABLE, INVALID_METACLASS, INVALID_NAMED_TUPLE, INVALID_NEWTYPE, INVALID_OVERLOAD, INVALID_PARAMETER_DEFAULT, INVALID_PARAMSPEC, INVALID_PROTOCOL, INVALID_TYPE_ARGUMENTS, INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL, INVALID_TYPE_VARIABLE_CONSTRAINTS, IncompatibleBases, NON_SUBSCRIPTABLE, POSSIBLY_MISSING_ATTRIBUTE, POSSIBLY_MISSING_IMPLICIT_CALL, POSSIBLY_MISSING_IMPORT, SUBCLASS_OF_FINAL_CLASS, UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL, UNRESOLVED_IMPORT, UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, USELESS_OVERLOAD_BODY, hint_if_stdlib_attribute_exists_on_other_versions, hint_if_stdlib_submodule_exists_on_other_versions, report_attempted_protocol_instantiation, report_bad_dunder_set_call, report_cannot_pop_required_field_on_typed_dict, report_duplicate_bases, report_implicit_return_type, report_index_out_of_bounds, report_instance_layout_conflict, report_invalid_arguments_to_annotated, report_invalid_assignment, report_invalid_attribute_assignment, report_invalid_exception_caught, report_invalid_exception_cause, report_invalid_exception_raised, report_invalid_exception_tuple_caught, report_invalid_generator_function_return_type, report_invalid_key_on_typed_dict, report_invalid_or_unsupported_base, report_invalid_return_type, report_invalid_type_checking_constant, report_named_tuple_field_with_leading_underscore, report_namedtuple_field_without_default_after_field_with_default, report_non_subscriptable, report_possibly_missing_attribute, report_possibly_unresolved_reference, report_rebound_typevar, report_slice_step_size_zero, report_unsupported_augmented_assignment, report_unsupported_binary_operation, report_unsupported_comparison, FROZEN_SUBCLASS_OF_NON_FROZEN_DATACLASS, NON_FROZEN_SUBCLASS_OF_FROZEN_DATACLASS};
|
use crate::types::diagnostic::{
|
||||||
|
self, CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS,
|
||||||
|
CYCLIC_CLASS_DEFINITION, CYCLIC_TYPE_ALIAS_DEFINITION, DIVISION_BY_ZERO, DUPLICATE_KW_ONLY,
|
||||||
|
FROZEN_SUBCLASS_OF_NON_FROZEN_DATACLASS, INCONSISTENT_MRO, INVALID_ARGUMENT_TYPE,
|
||||||
|
INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, INVALID_DECLARATION,
|
||||||
|
INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_LEGACY_TYPE_VARIABLE, INVALID_METACLASS,
|
||||||
|
INVALID_NAMED_TUPLE, INVALID_NEWTYPE, INVALID_OVERLOAD, INVALID_PARAMETER_DEFAULT,
|
||||||
|
INVALID_PARAMSPEC, INVALID_PROTOCOL, INVALID_TYPE_ARGUMENTS, INVALID_TYPE_FORM,
|
||||||
|
INVALID_TYPE_GUARD_CALL, INVALID_TYPE_VARIABLE_CONSTRAINTS, IncompatibleBases,
|
||||||
|
NON_FROZEN_SUBCLASS_OF_FROZEN_DATACLASS, NON_SUBSCRIPTABLE, POSSIBLY_MISSING_ATTRIBUTE,
|
||||||
|
POSSIBLY_MISSING_IMPLICIT_CALL, POSSIBLY_MISSING_IMPORT, SUBCLASS_OF_FINAL_CLASS,
|
||||||
|
UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL, UNRESOLVED_IMPORT,
|
||||||
|
UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, USELESS_OVERLOAD_BODY,
|
||||||
|
hint_if_stdlib_attribute_exists_on_other_versions,
|
||||||
|
hint_if_stdlib_submodule_exists_on_other_versions, report_attempted_protocol_instantiation,
|
||||||
|
report_bad_dunder_set_call, report_cannot_pop_required_field_on_typed_dict,
|
||||||
|
report_duplicate_bases, report_implicit_return_type, report_index_out_of_bounds,
|
||||||
|
report_instance_layout_conflict, report_invalid_arguments_to_annotated,
|
||||||
|
report_invalid_assignment, report_invalid_attribute_assignment,
|
||||||
|
report_invalid_exception_caught, report_invalid_exception_cause,
|
||||||
|
report_invalid_exception_raised, report_invalid_exception_tuple_caught,
|
||||||
|
report_invalid_generator_function_return_type, report_invalid_key_on_typed_dict,
|
||||||
|
report_invalid_or_unsupported_base, report_invalid_return_type,
|
||||||
|
report_invalid_type_checking_constant, report_named_tuple_field_with_leading_underscore,
|
||||||
|
report_namedtuple_field_without_default_after_field_with_default, report_non_subscriptable,
|
||||||
|
report_possibly_missing_attribute, report_possibly_unresolved_reference,
|
||||||
|
report_rebound_typevar, report_slice_step_size_zero, report_unsupported_augmented_assignment,
|
||||||
|
report_unsupported_binary_operation, report_unsupported_comparison,
|
||||||
|
};
|
||||||
use crate::types::function::{
|
use crate::types::function::{
|
||||||
FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral,
|
FunctionDecorators, FunctionLiteral, FunctionType, KnownFunction, OverloadLiteral,
|
||||||
is_implicit_classmethod, is_implicit_staticmethod,
|
is_implicit_classmethod, is_implicit_staticmethod,
|
||||||
|
|
@ -77,14 +105,15 @@ use crate::types::typed_dict::{
|
||||||
use crate::types::visitor::any_over_type;
|
use crate::types::visitor::any_over_type;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BoundTypeVarInstance, CallDunderError, CallableBinding, CallableType, CallableTypeKind,
|
BoundTypeVarInstance, CallDunderError, CallableBinding, CallableType, CallableTypeKind,
|
||||||
ClassLiteral, ClassType, DataclassFlags, DataclassParams, DynamicType, InternedType, IntersectionBuilder,
|
ClassLiteral, ClassType, DataclassFlags, DataclassParams, DynamicType, InternedType,
|
||||||
IntersectionType, KnownClass, KnownInstanceType, KnownUnion, LintDiagnosticGuard,
|
IntersectionBuilder, IntersectionType, KnownClass, KnownInstanceType, KnownUnion,
|
||||||
MemberLookupPolicy, MetaclassCandidate, PEP695TypeAliasType, ParamSpecAttrKind, Parameter,
|
LintDiagnosticGuard, MemberLookupPolicy, MetaclassCandidate, PEP695TypeAliasType,
|
||||||
ParameterForm, Parameters, Signature, SpecialFormType, SubclassOfType, TrackedConstraintSet,
|
ParamSpecAttrKind, Parameter, ParameterForm, Parameters, Signature, SpecialFormType,
|
||||||
Truthiness, Type, TypeAliasType, TypeAndQualifiers, TypeContext, TypeQualifiers,
|
SubclassOfType, TrackedConstraintSet, Truthiness, Type, TypeAliasType, TypeAndQualifiers,
|
||||||
TypeVarBoundOrConstraints, TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation,
|
TypeContext, TypeQualifiers, TypeVarBoundOrConstraints, TypeVarBoundOrConstraintsEvaluation,
|
||||||
TypeVarIdentity, TypeVarInstance, TypeVarKind, TypeVarVariance, TypedDictType, UnionBuilder,
|
TypeVarDefaultEvaluation, TypeVarIdentity, TypeVarInstance, TypeVarKind, TypeVarVariance,
|
||||||
UnionType, UnionTypeInstance, binding_type, infer_scope_types, todo_type,
|
TypedDictType, UnionBuilder, UnionType, UnionTypeInstance, binding_type, infer_scope_types,
|
||||||
|
todo_type,
|
||||||
};
|
};
|
||||||
use crate::types::{CallableTypes, overrides};
|
use crate::types::{CallableTypes, overrides};
|
||||||
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
|
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
|
||||||
|
|
@ -735,10 +764,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
base_class_literal.dataclass_params(self.db()),
|
base_class_literal.dataclass_params(self.db()),
|
||||||
class.dataclass_params(self.db()),
|
class.dataclass_params(self.db()),
|
||||||
) {
|
) {
|
||||||
let base_is_frozen = base_params.flags(self.db())
|
let base_is_frozen = base_params
|
||||||
|
.flags(self.db())
|
||||||
.contains(DataclassFlags::FROZEN);
|
.contains(DataclassFlags::FROZEN);
|
||||||
|
|
||||||
let class_is_frozen = class_params.flags(self.db())
|
let class_is_frozen = class_params
|
||||||
|
.flags(self.db())
|
||||||
.contains(DataclassFlags::FROZEN);
|
.contains(DataclassFlags::FROZEN);
|
||||||
|
|
||||||
match (base_is_frozen, class_is_frozen) {
|
match (base_is_frozen, class_is_frozen) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue