Skip to content

Commit 0d4f7dd

Browse files
authored
[ty] Treat __new__ as a static method (#20212)
## Summary Pull this out of #18473 as an isolated change to make sure it has no adverse effects. The wrong behavior is observable on `main` for something like ```py class C: def __new__(cls) -> "C": cls.x = 1 C.x # previously: Attribute `x` can only be accessed on instances # now: Type `<class 'C'>` has no attribute `x` ``` where we currently treat `x` as an *instance* attribute (because we consider `__new__` to be a normal function and `cls` to be the "self" attribute). With this PR, we do not consider `x` to be an attribute, neither on the class nor on instances of `C`. If this turns out to be an important feature, we should add it intentionally, instead of accidentally. ## Test Plan Ecosystem checks.
1 parent cb1ba0d commit 0d4f7dd

File tree

3 files changed

+10
-10
lines changed

3 files changed

+10
-10
lines changed

crates/ty_python_semantic/src/types/call/bind.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,7 @@ impl<'db> Bindings<'db> {
290290
}
291291
_ => {}
292292
}
293-
} else if function.has_known_decorator(db, FunctionDecorators::STATICMETHOD)
294-
{
293+
} else if function.is_staticmethod(db) {
295294
overload.set_return_type(Type::FunctionLiteral(function));
296295
} else if let [Some(first), _] = overload.parameter_types() {
297296
if first.is_none(db) {

crates/ty_python_semantic/src/types/class.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ use std::sync::{LazyLock, Mutex};
33
use super::TypeVarVariance;
44
use super::{
55
BoundTypeVarInstance, IntersectionBuilder, MemberLookupPolicy, Mro, MroError, MroIterator,
6-
SpecialFormType, SubclassOfType, Truthiness, Type, TypeQualifiers,
7-
class_base::ClassBase,
8-
function::{FunctionDecorators, FunctionType},
9-
infer_expression_type, infer_unpack_types,
6+
SpecialFormType, SubclassOfType, Truthiness, Type, TypeQualifiers, class_base::ClassBase,
7+
function::FunctionType, infer_expression_type, infer_unpack_types,
108
};
119
use crate::FxOrderMap;
1210
use crate::module_resolver::KnownModule;
@@ -1257,10 +1255,7 @@ pub(super) enum MethodDecorator {
12571255

12581256
impl MethodDecorator {
12591257
fn try_from_fn_type(db: &dyn Db, fn_type: FunctionType) -> Result<Self, ()> {
1260-
match (
1261-
fn_type.is_classmethod(db),
1262-
fn_type.has_known_decorator(db, FunctionDecorators::STATICMETHOD),
1263-
) {
1258+
match (fn_type.is_classmethod(db), fn_type.is_staticmethod(db)) {
12641259
(true, true) => Err(()), // A method can't be static and class method at the same time.
12651260
(true, false) => Ok(Self::ClassMethod),
12661261
(false, true) => Ok(Self::StaticMethod),

crates/ty_python_semantic/src/types/function.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,12 @@ impl<'db> FunctionType<'db> {
731731
)
732732
}
733733

734+
/// Returns true if this method is decorated with `@staticmethod`, or if it is implicitly a
735+
/// static method.
736+
pub(crate) fn is_staticmethod(self, db: &'db dyn Db) -> bool {
737+
self.has_known_decorator(db, FunctionDecorators::STATICMETHOD) || self.name(db) == "__new__"
738+
}
739+
734740
/// If the implementation of this function is deprecated, returns the `@warnings.deprecated`.
735741
///
736742
/// Checking if an overload is deprecated requires deeper call analysis.

0 commit comments

Comments
 (0)