Skip to content

Commit 399db7e

Browse files
SONARPY-2332 Fix FP on S5845 when comparing enum objects with types
1 parent 2e55165 commit 399db7e

File tree

3 files changed

+56
-7
lines changed

3 files changed

+56
-7
lines changed

python-checks/src/test/resources/checks/tests/assertOnDissimilarTypes.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,8 @@ class ComparingTypeAndEnum(unittest.TestCase):
198198
def test_type_of_enum_and_enum_class(self):
199199
EnumType = Enum("EnumType", names=["one", "two"])
200200
enum_member = EnumType(1)
201-
# FP SONARPY-2332: EnumType is a proper "type" object since the Enum constructor with argument creates a new Enum type
202-
self.assertIs(type(enum_member), EnumType) # Noncompliant
201+
# EnumType is a proper "type" object since the Enum constructor with argument creates a new Enum type
202+
self.assertIs(type(enum_member), EnumType)
203203

204204
class DerivedEnumWithMembers(Enum):
205205
ONE = 1
@@ -208,7 +208,7 @@ class DerivedEnumWithMembers(Enum):
208208
class ComparingTypeAndDerivedEnumWithMembers(unittest.TestCase):
209209
def test_type_of_derived_enum_and_derived_enum_class(self):
210210
derived_enum_member = DerivedEnumWithMembers(1)
211-
self.assertIs(type(enum_member), EnumType)
211+
self.assertIs(type(derived_enum_member), EnumType)
212212

213213
class DerivedEnumWithoutMembers(Enum):
214214
...
@@ -218,12 +218,12 @@ class ComparingTypeAndDerivedEnumWithoutMembers(unittest.TestCase):
218218
def test_type_of_derived_enum_and_derived_enum_class(self):
219219
DerivedEnumType = DerivedEnumWithoutMembers("DerivedEnumType", names=["one", "two"])
220220
derived_enum_member = DerivedEnumType(1)
221-
# FP SONARPY-2332: DerivedEnumType is a proper "type" object because `DerivedEnumWithoutMembers(str, list)` will create a new enum,
221+
# DerivedEnumType is a proper "type" object because `DerivedEnumWithoutMembers(str, list)` will create a new enum,
222222
# just like `Enum(str, list)` would
223-
self.assertIs(type(derived_enum_member), DerivedEnumType) # Noncompliant
223+
self.assertIs(type(derived_enum_member), DerivedEnumType)
224224

225225
DerivedEnumTypeFromDict = DerivedEnumWithoutMembers("DerivedEnumType", OrderedDict([("one", 1), ("two", 2)]))
226226
derived_enum_member_from_dict = DerivedEnumTypeFromDict(1)
227-
# FP SONARPY-2332: DerivedEnumTypeFromDict is a proper "type" object because `DerivedEnumWithoutMembers(str, list)` will create a new enum,
227+
# DerivedEnumTypeFromDict is a proper "type" object because `DerivedEnumWithoutMembers(str, list)` will create a new enum,
228228
# just like `Enum(str, list)`
229-
self.assertIs(type(derived_enum_member_from_dict), DerivedEnumTypeFromDict) # Noncompliant
229+
self.assertIs(type(derived_enum_member_from_dict), DerivedEnumTypeFromDict)

python-frontend/src/main/java/org/sonar/python/types/RuntimeType.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131

3232
public class RuntimeType implements InferredType {
3333

34+
// Name of the type returned by the type() function
35+
private static final String TYPE_CLASS_NAME = "type";
36+
3437
private ClassSymbol typeClass;
3538
private String builtinFullyQualifiedName;
3639
private Set<String> typeClassSuperClassesFQN = null;
@@ -52,9 +55,27 @@ public boolean isIdentityComparableWith(InferredType other) {
5255
if (other instanceof UnionType) {
5356
return other.isIdentityComparableWith(this);
5457
}
58+
if (isComparingTypeWithMetaclass(other)) {
59+
return true;
60+
}
5561
return this.equals(other);
5662
}
5763

64+
private boolean isComparingTypeWithMetaclass(InferredType other) {
65+
if (other instanceof RuntimeType otherRuntimeType) {
66+
boolean hasOtherMetaClass = hasMetaclassInHierarchy(otherRuntimeType);
67+
boolean hasThisMetaClass = hasMetaclassInHierarchy(this);
68+
return (TYPE_CLASS_NAME.equals(getTypeClass().name()) && hasOtherMetaClass)
69+
|| (hasThisMetaClass && TYPE_CLASS_NAME.equals(otherRuntimeType.getTypeClass().name()));
70+
}
71+
return false;
72+
}
73+
74+
private static boolean hasMetaclassInHierarchy(RuntimeType runtimeType) {
75+
return runtimeType.getTypeClass().hasMetaClass()
76+
|| runtimeType.getTypeClass().superClasses().stream().filter(ClassSymbol.class::isInstance).anyMatch(c -> ((ClassSymbol) c).hasMetaClass());
77+
}
78+
5879
@Override
5980
public boolean canHaveMember(String memberName) {
6081
if (MOCK_FQNS.stream().anyMatch(this::mustBeOrExtend)){

python-frontend/src/test/java/org/sonar/python/types/RuntimeTypeTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,34 @@ void isIdentityComparableWith() {
6464
assertThat(aType.isIdentityComparableWith(new DeclaredType(b))).isTrue();
6565
}
6666

67+
@Test
68+
void isIdentityComparableWithMetaclass() {
69+
RuntimeType typeType = new RuntimeType(new ClassSymbolImpl("type", "type"));
70+
71+
ClassSymbolImpl metaclassSymbol = new ClassSymbolImpl("Meta", "Meta");
72+
metaclassSymbol.setHasMetaClass();
73+
RuntimeType metaClassType = new RuntimeType(metaclassSymbol);
74+
75+
ClassSymbolImpl classSymbolWithSuperMetaClass = new ClassSymbolImpl("SuperMeta", "SuperMeta");
76+
classSymbolWithSuperMetaClass.addSuperClass(metaclassSymbol);
77+
RuntimeType superMetaClassType = new RuntimeType(classSymbolWithSuperMetaClass);
78+
79+
UnknownClassType unknownClassType = new UnknownClassType(metaclassSymbol);
80+
81+
assertThat(typeType.isIdentityComparableWith(typeType)).isTrue();
82+
assertThat(typeType.isIdentityComparableWith(metaClassType)).isTrue();
83+
assertThat(typeType.isIdentityComparableWith(superMetaClassType)).isTrue();
84+
assertThat(typeType.isIdentityComparableWith(unknownClassType)).isFalse();
85+
86+
assertThat(metaClassType.isIdentityComparableWith(typeType)).isTrue();
87+
assertThat(metaClassType.isIdentityComparableWith(metaClassType)).isTrue();
88+
assertThat(metaClassType.isIdentityComparableWith(superMetaClassType)).isFalse();
89+
90+
assertThat(superMetaClassType.isIdentityComparableWith(typeType)).isTrue();
91+
assertThat(superMetaClassType.isIdentityComparableWith(metaClassType)).isFalse();
92+
assertThat(superMetaClassType.isIdentityComparableWith(superMetaClassType)).isTrue();
93+
}
94+
6795
@Test
6896
void member() {
6997
ClassSymbolImpl x = new ClassSymbolImpl("x", "x");

0 commit comments

Comments
 (0)