|
62 | 62 | import org.sonar.plugins.python.api.types.v2.ObjectType; |
63 | 63 | import org.sonar.plugins.python.api.types.v2.ParameterV2; |
64 | 64 | import org.sonar.plugins.python.api.types.v2.PythonType; |
| 65 | +import org.sonar.plugins.python.api.types.v2.SelfType; |
65 | 66 | import org.sonar.plugins.python.api.types.v2.TypeOrigin; |
66 | 67 | import org.sonar.plugins.python.api.types.v2.TypeSource; |
67 | 68 | import org.sonar.plugins.python.api.types.v2.TypeWrapper; |
@@ -3938,4 +3939,134 @@ private static Statement lastStatement(StatementList statementList) { |
3938 | 3939 | List<Statement> statements = statementList.statements(); |
3939 | 3940 | return statements.get(statements.size() - 1); |
3940 | 3941 | } |
| 3942 | + |
| 3943 | + @Test |
| 3944 | + void selfParameterTypeInMethod() { |
| 3945 | + FileInput root = inferTypes(""" |
| 3946 | + class MyClass: |
| 3947 | + def foo(self, x): |
| 3948 | + self |
| 3949 | + def bar(self): |
| 3950 | + ... |
| 3951 | + """); |
| 3952 | + |
| 3953 | + var classDef = (ClassDef) root.statements().statements().get(0); |
| 3954 | + var fooMethodDef = (FunctionDef) classDef.body().statements().get(0); |
| 3955 | + var selfExpressionStatement = (ExpressionStatement) fooMethodDef.body().statements().get(0); |
| 3956 | + var selfExpression = selfExpressionStatement.expressions().get(0); |
| 3957 | + var selfType = selfExpression.typeV2(); |
| 3958 | + |
| 3959 | + // The self parameter should have type ObjectType[SelfType[MyClass]] |
| 3960 | + assertThat(selfType).isInstanceOf(ObjectType.class); |
| 3961 | + var objectType = (ObjectType) selfType; |
| 3962 | + assertThat(objectType.unwrappedType()).isInstanceOf(SelfType.class); |
| 3963 | + |
| 3964 | + var selfTypeInner = (SelfType) objectType.unwrappedType(); |
| 3965 | + var myClassType = classDef.name().typeV2(); |
| 3966 | + assertThat(selfTypeInner.innerType()).isEqualTo(myClassType); |
| 3967 | + } |
| 3968 | + |
| 3969 | + @Test |
| 3970 | + void selfParameterTypeInMethodWithNonStandardName() { |
| 3971 | + FileInput root = inferTypes(""" |
| 3972 | + class MyClass: |
| 3973 | + def foo(this): |
| 3974 | + this |
| 3975 | + """); |
| 3976 | + |
| 3977 | + var classDef = (ClassDef) root.statements().statements().get(0); |
| 3978 | + var fooMethodDef = (FunctionDef) classDef.body().statements().get(0); |
| 3979 | + var thisExpressionStatement = (ExpressionStatement) fooMethodDef.body().statements().get(0); |
| 3980 | + var thisExpression = thisExpressionStatement.expressions().get(0); |
| 3981 | + |
| 3982 | + assertThat(thisExpression.typeV2()).isInstanceOf(ObjectType.class); |
| 3983 | + var objectType = (ObjectType) thisExpression.typeV2(); |
| 3984 | + assertThat(objectType.unwrappedType()).isInstanceOf(SelfType.class); |
| 3985 | + } |
| 3986 | + |
| 3987 | + @Test |
| 3988 | + void selfParameterTypeOverwritesExplicitAnnotation() { |
| 3989 | + FileInput root = inferTypes(""" |
| 3990 | + class MyClass: |
| 3991 | + def foo(self: int): |
| 3992 | + self |
| 3993 | + """); |
| 3994 | + |
| 3995 | + var classDef = (ClassDef) root.statements().statements().get(0); |
| 3996 | + var fooMethodDef = (FunctionDef) classDef.body().statements().get(0); |
| 3997 | + var selfExpressionStatement = (ExpressionStatement) fooMethodDef.body().statements().get(0); |
| 3998 | + var selfExpression = selfExpressionStatement.expressions().get(0); |
| 3999 | + |
| 4000 | + // SelfType overwrites the explicit type annotation |
| 4001 | + assertThat(selfExpression.typeV2()).isInstanceOf(ObjectType.class); |
| 4002 | + var objectType = (ObjectType) selfExpression.typeV2(); |
| 4003 | + assertThat(objectType.unwrappedType()).isInstanceOf(SelfType.class); |
| 4004 | + } |
| 4005 | + |
| 4006 | + @Test |
| 4007 | + void classMethodFirstParameterType() { |
| 4008 | + FileInput root = inferTypes(""" |
| 4009 | + class MyClass: |
| 4010 | + @classmethod |
| 4011 | + def foo(cls): |
| 4012 | + cls |
| 4013 | + """); |
| 4014 | + |
| 4015 | + var classDef = (ClassDef) root.statements().statements().get(0); |
| 4016 | + var fooMethodDef = (FunctionDef) classDef.body().statements().get(0); |
| 4017 | + var clsExpressionStatement = (ExpressionStatement) fooMethodDef.body().statements().get(0); |
| 4018 | + var clsExpression = clsExpressionStatement.expressions().get(0); |
| 4019 | + |
| 4020 | + assertThat(clsExpression.typeV2()).isNotInstanceOf(SelfType.class); |
| 4021 | + assertThat(clsExpression.typeV2()) |
| 4022 | + .extracting(PythonType::unwrappedType) |
| 4023 | + .isNotInstanceOf(SelfType.class); |
| 4024 | + } |
| 4025 | + |
| 4026 | + @Test |
| 4027 | + void staticMethodHasNoImplicitSelfParameter() { |
| 4028 | + FileInput root = inferTypes(""" |
| 4029 | + class MyClass: |
| 4030 | + @staticmethod |
| 4031 | + def foo(x): |
| 4032 | + x |
| 4033 | + """); |
| 4034 | + |
| 4035 | + var classDef = (ClassDef) root.statements().statements().get(0); |
| 4036 | + var fooMethodDef = (FunctionDef) classDef.body().statements().get(0); |
| 4037 | + var xExpressionStatement = (ExpressionStatement) fooMethodDef.body().statements().get(0); |
| 4038 | + var xExpression = xExpressionStatement.expressions().get(0); |
| 4039 | + |
| 4040 | + assertThat(xExpression.typeV2()).isNotInstanceOf(SelfType.class); |
| 4041 | + assertThat(xExpression.typeV2()) |
| 4042 | + .extracting(PythonType::unwrappedType) |
| 4043 | + .isNotInstanceOf(SelfType.class); |
| 4044 | + } |
| 4045 | + |
| 4046 | + @Test |
| 4047 | + void setSelfParameterTypeWithNoParameters() { |
| 4048 | + FileInput root = inferTypes(""" |
| 4049 | + class MyClass: |
| 4050 | + def foo(): |
| 4051 | + pass |
| 4052 | + """); |
| 4053 | + |
| 4054 | + var classDef = (ClassDef) root.statements().statements().get(0); |
| 4055 | + var fooMethodDef = (FunctionDef) classDef.body().statements().get(0); |
| 4056 | + assertThat(fooMethodDef.name().typeV2()).isInstanceOf(FunctionType.class); |
| 4057 | + } |
| 4058 | + |
| 4059 | + @Test |
| 4060 | + void setSelfParameterTypeWithOnlyTupleParameters() { |
| 4061 | + // Python 2 syntax: tuple parameters are allowed |
| 4062 | + FileInput root = inferTypes(""" |
| 4063 | + class MyClass: |
| 4064 | + def foo((a, b)): |
| 4065 | + pass |
| 4066 | + """); |
| 4067 | + |
| 4068 | + var classDef = (ClassDef) root.statements().statements().get(0); |
| 4069 | + var fooMethodDef = (FunctionDef) classDef.body().statements().get(0); |
| 4070 | + assertThat(fooMethodDef.name().typeV2()).isInstanceOf(FunctionType.class); |
| 4071 | + } |
3941 | 4072 | } |
0 commit comments