Skip to content

Commit f7641dc

Browse files
ghislainpiotguillaume-dequenne
authored andcommitted
SONARPY-2133 Modify ObjectType to use TypeWrapper
1 parent e2d6475 commit f7641dc

File tree

7 files changed

+268
-9
lines changed

7 files changed

+268
-9
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.semantic.v2;
21+
22+
import java.util.List;
23+
import org.sonar.plugins.python.api.LocationInFile;
24+
import org.sonar.python.types.v2.Member;
25+
import org.sonar.python.types.v2.ObjectType;
26+
import org.sonar.python.types.v2.PythonType;
27+
import org.sonar.python.types.v2.TypeSource;
28+
import org.sonar.python.types.v2.TypeWrapper;
29+
30+
public class ObjectTypeBuilder implements TypeBuilder<ObjectType> {
31+
32+
private TypeWrapper typeWrapper;
33+
private List<PythonType> attributes;
34+
private List<Member> members;
35+
private TypeSource typeSource;
36+
37+
@Override
38+
public ObjectType build() {
39+
return new ObjectType(typeWrapper, attributes, members, typeSource);
40+
}
41+
42+
@Override
43+
public TypeBuilder<ObjectType> withDefinitionLocation(LocationInFile definitionLocation) {
44+
throw new IllegalStateException("Object type does not have definition location");
45+
}
46+
47+
public ObjectTypeBuilder withTypeWrapper(TypeWrapper typeWrapper) {
48+
this.typeWrapper = typeWrapper;
49+
return this;
50+
}
51+
52+
public ObjectTypeBuilder withAttributes(List<PythonType> attributes) {
53+
this.attributes = attributes;
54+
return this;
55+
}
56+
57+
public ObjectTypeBuilder withMembers(List<Member> members) {
58+
this.members = members;
59+
return this;
60+
}
61+
62+
public ObjectTypeBuilder withTypeSource(TypeSource typeSource) {
63+
this.typeSource = typeSource;
64+
return this;
65+
}
66+
67+
public static ObjectTypeBuilder fromObjectType(ObjectType objectType) {
68+
return new ObjectTypeBuilder()
69+
.withTypeWrapper(objectType.typeWrapper())
70+
.withAttributes(objectType.attributes())
71+
.withMembers(objectType.members())
72+
.withTypeSource(objectType.typeSource());
73+
}
74+
}

python-frontend/src/main/java/org/sonar/python/types/v2/LazyTypeWrapper.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
*/
2020
package org.sonar.python.types.v2;
2121

22+
import java.util.Objects;
23+
2224
public class LazyTypeWrapper implements TypeWrapper {
2325
private PythonType type;
2426

@@ -39,4 +41,24 @@ public void resolveLazyType(PythonType pythonType) {
3941
}
4042
this.type = pythonType;
4143
}
44+
45+
@Override
46+
public boolean equals(Object o) {
47+
if (this == o) return true;
48+
if (o == null || getClass() != o.getClass()) return false;
49+
LazyTypeWrapper that = (LazyTypeWrapper) o;
50+
return Objects.equals(type, that.type);
51+
}
52+
53+
@Override
54+
public int hashCode() {
55+
return Objects.hashCode(type);
56+
}
57+
58+
@Override
59+
public String toString() {
60+
return "LazyTypeWrapper{" +
61+
"type=" + type +
62+
'}';
63+
}
4264
}

python-frontend/src/main/java/org/sonar/python/types/v2/ObjectType.java

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,31 @@
2727
import org.sonar.plugins.python.api.LocationInFile;
2828

2929
@Beta
30-
public record ObjectType(PythonType type, List<PythonType> attributes, List<Member> members, TypeSource typeSource) implements PythonType {
30+
public final class ObjectType implements PythonType {
31+
private final TypeWrapper typeWrapper;
32+
private final List<PythonType> attributes;
33+
private final List<Member> members;
34+
private final TypeSource typeSource;
35+
36+
public ObjectType(TypeWrapper typeWrapper, List<PythonType> attributes, List<Member> members, TypeSource typeSource) {
37+
this.typeWrapper = typeWrapper;
38+
this.attributes = attributes;
39+
this.members = members;
40+
this.typeSource = typeSource;
41+
}
42+
43+
public ObjectType(PythonType type, List<PythonType> attributes, List<Member> members, TypeSource typeSource) {
44+
this(new SimpleTypeWrapper(type), attributes, members, typeSource);
45+
}
3146

3247
public ObjectType(PythonType type) {
3348
this(type, TypeSource.EXACT);
3449
}
3550

51+
public ObjectType(TypeWrapper typeWrapper) {
52+
this(typeWrapper, new ArrayList<>(), new ArrayList<>(), TypeSource.EXACT);
53+
}
54+
3655
public ObjectType(PythonType type, TypeSource typeSource) {
3756
this(type, new ArrayList<>(), new ArrayList<>(), typeSource);
3857
}
@@ -43,17 +62,17 @@ public ObjectType(PythonType type, List<PythonType> attributes, List<Member> mem
4362

4463
@Override
4564
public Optional<String> displayName() {
46-
return type.instanceDisplayName();
65+
return typeWrapper.type().instanceDisplayName();
4766
}
4867

4968
@Override
5069
public boolean isCompatibleWith(PythonType another) {
51-
return this.type.isCompatibleWith(another);
70+
return this.typeWrapper.type().isCompatibleWith(another);
5271
}
5372

5473
@Override
5574
public PythonType unwrappedType() {
56-
return this.type;
75+
return this.typeWrapper.type();
5776
}
5877

5978
@Override
@@ -62,23 +81,23 @@ public Optional<PythonType> resolveMember(String memberName) {
6281
.filter(member -> Objects.equals(member.name(), memberName))
6382
.map(Member::type)
6483
.findFirst()
65-
.or(() -> type.resolveMember(memberName));
84+
.or(() -> typeWrapper.type().resolveMember(memberName));
6685
}
6786

6887
@Override
6988
public TriBool hasMember(String memberName) {
7089
if (resolveMember(memberName).isPresent()) {
7190
return TriBool.TRUE;
7291
}
73-
if (type instanceof ClassType classType) {
92+
if (typeWrapper.type() instanceof ClassType classType) {
7493
return classType.instancesHaveMember(memberName);
7594
}
7695
return TriBool.UNKNOWN;
7796
}
7897

7998
@Override
8099
public Optional<LocationInFile> definitionLocation() {
81-
return type.definitionLocation();
100+
return typeWrapper.type().definitionLocation();
82101
}
83102

84103
@Override
@@ -90,13 +109,44 @@ public boolean equals(Object o) {
90109
List<String> otherMembersNames = that.members.stream().map(Member::name).toList();
91110
List<String> attributesNames = attributes.stream().map(PythonType::key).toList();
92111
List<String> otherAttributesNames = that.attributes.stream().map(PythonType::key).toList();
93-
return Objects.equals(type, that.type) && Objects.equals(membersNames, otherMembersNames) && Objects.equals(attributesNames, otherAttributesNames);
112+
return Objects.equals(typeWrapper, that.typeWrapper) && Objects.equals(membersNames, otherMembersNames) && Objects.equals(attributesNames, otherAttributesNames);
94113
}
95114

96115
@Override
97116
public int hashCode() {
98117
List<String> membersNames = members.stream().map(Member::name).toList();
99118
List<String> attributesNames = attributes.stream().map(PythonType::key).toList();
100-
return Objects.hash(type, attributesNames, membersNames);
119+
return Objects.hash(typeWrapper, attributesNames, membersNames);
101120
}
121+
122+
public PythonType type() {
123+
return typeWrapper.type();
124+
}
125+
126+
public TypeWrapper typeWrapper() {
127+
return typeWrapper;
128+
}
129+
130+
public List<PythonType> attributes() {
131+
return attributes;
132+
}
133+
134+
public List<Member> members() {
135+
return members;
136+
}
137+
138+
@Override
139+
public TypeSource typeSource() {
140+
return typeSource;
141+
}
142+
143+
@Override
144+
public String toString() {
145+
return "ObjectType[" +
146+
"type=" + typeWrapper + ", " +
147+
"attributes=" + attributes + ", " +
148+
"members=" + members + ", " +
149+
"typeSource=" + typeSource + ']';
150+
}
151+
102152
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.types.v2;
21+
22+
import java.util.Objects;
23+
24+
public class SimpleTypeWrapper implements TypeWrapper {
25+
@Override
26+
public boolean equals(Object o) {
27+
if (this == o) return true;
28+
if (o == null || getClass() != o.getClass()) return false;
29+
SimpleTypeWrapper that = (SimpleTypeWrapper) o;
30+
return Objects.equals(type, that.type);
31+
}
32+
33+
@Override
34+
public int hashCode() {
35+
return Objects.hashCode(type);
36+
}
37+
38+
private final PythonType type;
39+
40+
public SimpleTypeWrapper(PythonType type) {
41+
this.type = type;
42+
}
43+
44+
@Override
45+
public PythonType type() {
46+
return type;
47+
}
48+
49+
@Override
50+
public String toString() {
51+
return "SimpleTypeWrapper{" +
52+
"type=" + type +
53+
'}';
54+
}
55+
}

python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeCheckerBuilderTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,12 @@ void isInstanceOfTest() {
126126
);
127127
}
128128

129+
@Test
130+
void objectTypeThrowsOnDefinitionLocation() {
131+
var objectTypeBuilder = new ObjectTypeBuilder();
132+
Assertions.assertThatThrownBy(() -> objectTypeBuilder.withDefinitionLocation(null))
133+
.isInstanceOf(IllegalStateException.class)
134+
.hasMessage("Object type does not have definition location");
135+
}
136+
129137
}

python-frontend/src/test/java/org/sonar/python/types/v2/LazyTypeWrapperTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,18 @@ void resolvingNonLazyTypeThrowsException() {
4949
.isInstanceOf(IllegalStateException.class)
5050
.hasMessage("Trying to resolve an already resolved lazy type.");
5151
}
52+
53+
@Test
54+
void testEquals() {
55+
var lazyTypeWrapper = new LazyTypeWrapper(new LazyType("random", Mockito.mock(LazyTypesContext.class)));
56+
assertThat(lazyTypeWrapper.equals(lazyTypeWrapper)).isTrue();
57+
assertThat(lazyTypeWrapper.equals(null)).isFalse();
58+
assertThat(lazyTypeWrapper.equals("str")).isFalse();
59+
}
60+
61+
@Test
62+
void testToString() {
63+
var lazyTypeWrapper = new LazyTypeWrapper(new LazyType("random", Mockito.mock(LazyTypesContext.class)));
64+
assertThat(lazyTypeWrapper.toString()).startsWith("LazyTypeWrapper{type=");
65+
}
5266
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* SonarQube Python Plugin
3+
* Copyright (C) 2011-2024 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonar.python.types.v2;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
26+
class SimpleTypeWrapperTest {
27+
28+
@Test
29+
void testEquals() {
30+
var simpleTypeWrapper = new SimpleTypeWrapper(PythonType.UNKNOWN);
31+
assertThat(simpleTypeWrapper.equals(simpleTypeWrapper)).isTrue();
32+
assertThat(simpleTypeWrapper.equals(null)).isFalse();
33+
assertThat(simpleTypeWrapper.equals("str")).isFalse();
34+
}
35+
36+
}

0 commit comments

Comments
 (0)