@@ -4105,6 +4105,213 @@ def foo((a, b)):
41054105 }
41064106
41074107 @ Test
4108+ void inferParameterTypeHintedWithTypingSelf () {
4109+ FileInput root = inferTypes ("""
4110+ from typing import Self
4111+ class A:
4112+ def foo(self, other: Self) -> Self:
4113+ ...
4114+ """ );
4115+
4116+ var classDef = (ClassDef ) root .statements ().statements ().get (1 );
4117+ var classType = (ClassType ) classDef .name ().typeV2 ();
4118+ var functionDef = (FunctionDef ) classDef .body ().statements ().get (0 );
4119+ var functionType = (FunctionType ) functionDef .name ().typeV2 ();
4120+
4121+ var otherParamType = functionType .parameters ().get (1 ).declaredType ().type ();
4122+ assertThat (otherParamType ).isInstanceOf (ObjectType .class );
4123+ var otherParamUnwrapped = otherParamType .unwrappedType ();
4124+ assertThat (otherParamUnwrapped ).isInstanceOf (SelfType .class );
4125+ assertThat (((SelfType ) otherParamUnwrapped ).innerType ()).isEqualTo (classType );
4126+
4127+ var returnType = functionType .returnType ();
4128+ assertThat (returnType ).isInstanceOf (ObjectType .class );
4129+ var returnTypeUnwrapped = returnType .unwrappedType ();
4130+ assertThat (returnTypeUnwrapped ).isInstanceOf (SelfType .class );
4131+ assertThat (((SelfType ) returnTypeUnwrapped ).innerType ()).isEqualTo (classType );
4132+ }
4133+
4134+ @ Test
4135+ void parameterTypeHintedWithNonSelfUnresolvedImport () {
4136+ FileInput root = inferTypes ("""
4137+ from unknown_module import SomeType
4138+ class A:
4139+ def foo(self, x: SomeType) -> None:
4140+ ...
4141+ """ );
4142+
4143+ var classDef = (ClassDef ) root .statements ().statements ().get (1 );
4144+ var functionDef = (FunctionDef ) classDef .body ().statements ().get (0 );
4145+ var functionType = (FunctionType ) functionDef .name ().typeV2 ();
4146+
4147+ var xParamType = functionType .parameters ().get (1 ).declaredType ().type ();
4148+ assertThat (xParamType ).isInstanceOf (ObjectType .class );
4149+ assertThat (xParamType .unwrappedType ()).isInstanceOf (UnknownType .UnresolvedImportType .class );
4150+ assertThat (xParamType .unwrappedType ()).isNotInstanceOf (SelfType .class );
4151+ }
4152+
4153+ @ Test
4154+ void parameterTypeHintedWithNonSelfSpecialForm () {
4155+ FileInput root = inferTypes ("""
4156+ from typing import Final
4157+ class A:
4158+ def foo(self, x: Final) -> None:
4159+ ...
4160+ """ );
4161+
4162+ var classDef = (ClassDef ) root .statements ().statements ().get (1 );
4163+ var functionDef = (FunctionDef ) classDef .body ().statements ().get (0 );
4164+ var functionType = (FunctionType ) functionDef .name ().typeV2 ();
4165+
4166+ var xParamType = functionType .parameters ().get (1 ).declaredType ().type ();
4167+ assertThat (xParamType ).isInstanceOf (ObjectType .class );
4168+ assertThat (xParamType .unwrappedType ()).isNotInstanceOf (SelfType .class );
4169+ }
4170+
4171+ @ Test
4172+ void selfReturnTypeWithoutEnclosingClass () {
4173+ FileInput root = inferTypes ("""
4174+ from typing import Self
4175+ def foo() -> Self:
4176+ ...
4177+ """ );
4178+
4179+ var functionDef = (FunctionDef ) root .statements ().statements ().get (1 );
4180+ var functionType = (FunctionType ) functionDef .name ().typeV2 ();
4181+
4182+ var returnType = functionType .returnType ();
4183+ assertThat (returnType ).isNotInstanceOf (SelfType .class );
4184+ assertThat (returnType .unwrappedType ()).isNotInstanceOf (SelfType .class );
4185+ }
4186+
4187+ @ Test
4188+ void selfReturnTypeInNestedFunctionWithoutEnclosingClass () {
4189+ FileInput root = inferTypes ("""
4190+ from typing import Self
4191+ def foo():
4192+ def bar() -> Self:
4193+ pass
4194+ """ );
4195+
4196+ var outerFunctionDef = (FunctionDef ) root .statements ().statements ().get (1 );
4197+ var innerFunctionDef = (FunctionDef ) outerFunctionDef .body ().statements ().get (0 );
4198+ var innerFunctionType = (FunctionType ) innerFunctionDef .name ().typeV2 ();
4199+
4200+ var returnType = innerFunctionType .returnType ();
4201+ assertThat (returnType ).isNotInstanceOf (SelfType .class );
4202+ assertThat (returnType .unwrappedType ()).isNotInstanceOf (SelfType .class );
4203+ }
4204+
4205+ @ Test
4206+ void selfReturnTypeInNestedFunctionInsideMethod () {
4207+ FileInput root = inferTypes ("""
4208+ from typing import Self
4209+ class A:
4210+ def foo(self):
4211+ def bar() -> Self:
4212+ pass
4213+ """ );
4214+
4215+ var classDef = (ClassDef ) root .statements ().statements ().get (1 );
4216+ var methodDef = (FunctionDef ) classDef .body ().statements ().get (0 );
4217+ var innerFunctionDef = (FunctionDef ) methodDef .body ().statements ().get (0 );
4218+ var innerFunctionType = (FunctionType ) innerFunctionDef .name ().typeV2 ();
4219+
4220+ // bar's return type should NOT be resolved to SelfType(A) because bar is not directly owned by class A
4221+ var returnType = innerFunctionType .returnType ();
4222+ assertThat (returnType ).isNotInstanceOf (SelfType .class );
4223+ assertThat (returnType .unwrappedType ()).isNotInstanceOf (SelfType .class );
4224+ }
4225+
4226+ @ Test
4227+ void stringLiteralTypeInference () {
4228+ Expression root = lastExpression ("""
4229+ def bar(param: "MyParameterType") -> "MyReturnType":
4230+ return param
4231+ bar
4232+ """ );
4233+
4234+ assertThat (root .typeV2 ()).isInstanceOf (FunctionType .class );
4235+ FunctionType functionType = (FunctionType ) root .typeV2 ();
4236+ assertThat (functionType .parameters ().get (0 ).declaredType ().type ().unwrappedType ()).isEqualTo (PythonType .UNKNOWN );
4237+ assertThat (functionType .returnType ().unwrappedType ()).isEqualTo (PythonType .UNKNOWN );
4238+ }
4239+
4240+ @ Test
4241+ void selfInUnionReturnType () {
4242+ FileInput root = inferTypes ("""
4243+ from typing import Self
4244+ class A:
4245+ def foo(self) -> Self | None:
4246+ ...
4247+ """ );
4248+
4249+ var classDef = (ClassDef ) root .statements ().statements ().get (1 );
4250+ var classType = (ClassType ) classDef .name ().typeV2 ();
4251+ var functionDef = (FunctionDef ) classDef .body ().statements ().get (0 );
4252+ var functionType = (FunctionType ) functionDef .name ().typeV2 ();
4253+
4254+ var returnType = functionType .returnType ();
4255+ assertThat (returnType ).isInstanceOf (ObjectType .class );
4256+ var unwrappedReturnType = returnType .unwrappedType ();
4257+ assertThat (unwrappedReturnType ).isInstanceOf (UnionType .class );
4258+
4259+ var unionType = (UnionType ) unwrappedReturnType ;
4260+ var candidates = unionType .candidates ();
4261+ assertThat (candidates ).hasSize (2 );
4262+
4263+ var selfCandidate = candidates .stream ()
4264+ .filter (c -> c .unwrappedType () instanceof SelfType )
4265+ .findFirst ();
4266+ assertThat (selfCandidate ).isPresent ();
4267+ var selfType = (SelfType ) selfCandidate .get ().unwrappedType ();
4268+ assertThat (selfType .innerType ()).isEqualTo (classType );
4269+
4270+ var noneCandidate = candidates .stream ()
4271+ .filter (c -> !(c .unwrappedType () instanceof SelfType ))
4272+ .findFirst ();
4273+ assertThat (noneCandidate ).isPresent ();
4274+ }
4275+
4276+ @ Test
4277+ void selfInUnionReturnTypeWithoutEnclosingClass () {
4278+ FileInput root = inferTypes ("""
4279+ from typing import Self
4280+ def foo() -> Self | None:
4281+ ...
4282+ """ );
4283+
4284+ var functionDef = (FunctionDef ) root .statements ().statements ().get (1 );
4285+ var functionType = (FunctionType ) functionDef .name ().typeV2 ();
4286+
4287+ var returnType = functionType .returnType ();
4288+ assertThat (returnType ).isInstanceOf (ObjectType .class );
4289+ var unwrappedReturnType = returnType .unwrappedType ();
4290+ assertThat (unwrappedReturnType ).isInstanceOf (UnionType .class );
4291+
4292+ var unionType = (UnionType ) unwrappedReturnType ;
4293+ assertThat (unionType .candidates ()).noneMatch (c -> c .unwrappedType () instanceof SelfType );
4294+ }
4295+
4296+ @ Test
4297+ void selfOrNoneTypeAlias () {
4298+ FileInput root = inferTypes ("""
4299+ from typing import Self
4300+ SelfOrNone = Self | None
4301+ class A:
4302+ def foo(self) -> SelfOrNone:
4303+ ...
4304+ """ );
4305+
4306+ var classDef = (ClassDef ) root .statements ().statements ().get (2 );
4307+ var functionDef = (FunctionDef ) classDef .body ().statements ().get (0 );
4308+ var functionType = (FunctionType ) functionDef .name ().typeV2 ();
4309+
4310+ var returnType = functionType .returnType ();
4311+ assertThat (returnType ).isSameAs (PythonType .UNKNOWN ); // SONARPY-3559 should change this to correctly resolve to a union type of Self and None
4312+ }
4313+
4314+ @ Test
41084315 void anyResolvesToUnknown () {
41094316 Expression anyExpression = lastExpression ("""
41104317 from typing import Any
0 commit comments