Skip to content

Commit 35e72cc

Browse files
SONARPY-1896 Fix performance of new type inference hashCode computations (#1821)
1 parent de55043 commit 35e72cc

File tree

13 files changed

+260
-190
lines changed

13 files changed

+260
-190
lines changed

python-frontend/src/main/java/org/sonar/python/semantic/v2/SymbolV2.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,18 @@
2727
import org.sonar.python.tree.NameImpl;
2828

2929
@Beta
30-
public record SymbolV2(String name, @Nullable String fullyQualifiedName, List<UsageV2> usages) {
30+
public class SymbolV2 {
31+
32+
private final String name;
33+
private final List<UsageV2> usages;
34+
35+
public SymbolV2(String name, List<UsageV2> usages) {
36+
this.name = name;
37+
this.usages = usages;
38+
}
3139

3240
public SymbolV2(String name) {
33-
this(name, null, new ArrayList<>());
41+
this(name, new ArrayList<>());
3442
}
3543

3644
void addUsage(Name name, UsageV2.Kind kind) {
@@ -45,4 +53,13 @@ void addUsage(Name name, UsageV2.Kind kind) {
4553
public boolean hasSingleBindingUsage() {
4654
return usages.stream().filter(UsageV2::isBindingUsage).toList().size() == 1;
4755
}
56+
57+
public String name() {
58+
return name;
59+
}
60+
61+
public List<UsageV2> usages() {
62+
return usages;
63+
}
64+
4865
}

python-frontend/src/main/java/org/sonar/python/semantic/v2/TypeShed.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.HashSet;
2828
import java.util.List;
2929
import java.util.Map;
30+
import java.util.Optional;
3031
import java.util.Set;
3132
import java.util.stream.Collectors;
3233
import java.util.stream.Stream;
@@ -130,7 +131,11 @@ public Map<String, Symbol> symbolsForModule(String moduleName) {
130131
return Collections.emptyMap();
131132
}
132133
if (!typeShedSymbols.containsKey(moduleName)) {
133-
Map<String, Symbol> symbols = searchTypeShedForModule(moduleName);
134+
var symbols = Optional.of(org.sonar.python.types.TypeShed.getLoadedTypeShedSymbols())
135+
.filter(m -> m.containsKey(moduleName))
136+
.map(m -> m.get(moduleName))
137+
.orElseGet(() -> searchTypeShedForModule(moduleName));
138+
134139
typeShedSymbols.put(moduleName, symbols);
135140
return symbols;
136141
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ public static Map<String, Symbol> builtinSymbols() {
115115
return builtins;
116116
}
117117

118+
public static Map<String, Map<String, Symbol>> getLoadedTypeShedSymbols() {
119+
return typeShedSymbols;
120+
}
121+
118122
public static ClassSymbol typeShedClass(String fullyQualifiedName) {
119123
Symbol symbol = builtinSymbols().get(fullyQualifiedName);
120124
if (symbol == null) {

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

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,29 @@
3535
* ClassType
3636
*/
3737
@Beta
38-
public record ClassType(
39-
String name,
40-
Set<Member> members,
41-
List<PythonType> attributes,
42-
List<PythonType> superClasses,
43-
List<PythonType> metaClasses,
44-
@Nullable LocationInFile locationInFile) implements PythonType {
38+
public final class ClassType implements PythonType {
39+
40+
private final String name;
41+
private final Set<Member> members;
42+
private final List<PythonType> attributes;
43+
private final List<PythonType> superClasses;
44+
private final List<PythonType> metaClasses;
45+
private final LocationInFile locationInFile;
46+
47+
public ClassType(
48+
String name,
49+
Set<Member> members,
50+
List<PythonType> attributes,
51+
List<PythonType> superClasses,
52+
List<PythonType> metaClasses,
53+
@Nullable LocationInFile locationInFile) {
54+
this.name = name;
55+
this.members = members;
56+
this.attributes = attributes;
57+
this.superClasses = superClasses;
58+
this.metaClasses = metaClasses;
59+
this.locationInFile = locationInFile;
60+
}
4561

4662
public ClassType(String name) {
4763
this(name, new HashSet<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), null);
@@ -153,11 +169,11 @@ public TriBool hasMember(String memberName) {
153169

154170
public boolean hasMetaClass() {
155171
return !this.metaClasses.isEmpty() ||
156-
this.superClasses()
157-
.stream()
158-
.filter(ClassType.class::isInstance)
159-
.map(ClassType.class::cast)
160-
.anyMatch(ClassType::hasMetaClass);
172+
this.superClasses()
173+
.stream()
174+
.filter(ClassType.class::isInstance)
175+
.map(ClassType.class::cast)
176+
.anyMatch(ClassType::hasMetaClass);
161177
}
162178

163179
public TriBool instancesHaveMember(String memberName) {
@@ -177,27 +193,28 @@ public Optional<LocationInFile> definitionLocation() {
177193
}
178194

179195
@Override
180-
public boolean equals(Object o) {
181-
if (this == o) return true;
182-
if (o == null || getClass() != o.getClass()) return false;
183-
ClassType classType = (ClassType) o;
184-
boolean haveSameAttributes = Objects.equals(name, classType.name) && Objects.equals(members, classType.members) && Objects.equals(attributes, classType.attributes);
185-
List<String> parentNames = superClasses.stream().map(PythonType::key).toList();
186-
List<String> metaClassNames = metaClasses.stream().map(PythonType::key).toList();
187-
List<String> otherParentNames = classType.superClasses.stream().map(PythonType::key).toList();
188-
List<String> otherMetaClassNames = classType.metaClasses.stream().map(PythonType::key).toList();
189-
return haveSameAttributes && Objects.equals(parentNames, otherParentNames) && Objects.equals(metaClassNames, otherMetaClassNames);
196+
public String toString() {
197+
return "ClassType[%s]".formatted(name);
190198
}
191199

192200
@Override
193-
public int hashCode() {
194-
List<String> parentNames = superClasses.stream().map(PythonType::key).toList();
195-
List<String> metaClassNames = metaClasses.stream().map(PythonType::key).toList();
196-
return Objects.hash(name, members, attributes, parentNames, metaClassNames);
201+
public String name() {
202+
return name;
197203
}
198204

199-
@Override
200-
public String toString() {
201-
return "ClassType[%s]".formatted(name);
205+
public Set<Member> members() {
206+
return members;
207+
}
208+
209+
public List<PythonType> attributes() {
210+
return attributes;
211+
}
212+
213+
public List<PythonType> superClasses() {
214+
return superClasses;
215+
}
216+
217+
public List<PythonType> metaClasses() {
218+
return metaClasses;
202219
}
203220
}

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

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
package org.sonar.python.types.v2;
2121

2222
import java.util.List;
23-
import java.util.Objects;
2423
import java.util.Optional;
24+
import javax.annotation.CheckForNull;
2525
import javax.annotation.Nullable;
2626
import org.sonar.api.Beta;
2727
import org.sonar.plugins.python.api.LocationInFile;
@@ -30,32 +30,43 @@
3030
* FunctionType
3131
*/
3232
@Beta
33-
public record FunctionType(
34-
String name,
35-
List<PythonType> attributes,
36-
List<ParameterV2> parameters,
37-
PythonType returnType,
38-
boolean isAsynchronous,
39-
boolean hasDecorators,
40-
boolean isInstanceMethod,
41-
boolean hasVariadicParameter,
42-
@Nullable PythonType owner,
43-
@Nullable LocationInFile locationInFile
44-
) implements PythonType {
33+
public final class FunctionType implements PythonType {
34+
private final String name;
35+
private final List<PythonType> attributes;
36+
private final List<ParameterV2> parameters;
37+
private final PythonType returnType;
38+
private final boolean isAsynchronous;
39+
private final boolean hasDecorators;
40+
private final boolean isInstanceMethod;
41+
private final boolean hasVariadicParameter;
42+
private final PythonType owner;
43+
private final LocationInFile locationInFile;
4544

46-
@Override
47-
public boolean equals(Object o) {
48-
if (this == o) return true;
49-
if (o == null || getClass() != o.getClass()) return false;
50-
FunctionType that = (FunctionType) o;
51-
return hasDecorators == that.hasDecorators
52-
&& isAsynchronous == that.isAsynchronous
53-
&& isInstanceMethod == that.isInstanceMethod
54-
&& hasVariadicParameter == that.hasVariadicParameter
55-
&& Objects.equals(name, that.name)
56-
&& Objects.equals(returnType, that.returnType)
57-
&& Objects.equals(attributes, that.attributes)
58-
&& Objects.equals(parameters, that.parameters);
45+
/**
46+
*
47+
*/
48+
public FunctionType(
49+
String name,
50+
List<PythonType> attributes,
51+
List<ParameterV2> parameters,
52+
PythonType returnType,
53+
boolean isAsynchronous,
54+
boolean hasDecorators,
55+
boolean isInstanceMethod,
56+
boolean hasVariadicParameter,
57+
@Nullable PythonType owner,
58+
@Nullable LocationInFile locationInFile
59+
) {
60+
this.name = name;
61+
this.attributes = attributes;
62+
this.parameters = parameters;
63+
this.returnType = returnType;
64+
this.isAsynchronous = isAsynchronous;
65+
this.hasDecorators = hasDecorators;
66+
this.isInstanceMethod = isInstanceMethod;
67+
this.hasVariadicParameter = hasVariadicParameter;
68+
this.owner = owner;
69+
this.locationInFile = locationInFile;
5970
}
6071

6172
@Override
@@ -69,12 +80,45 @@ public Optional<LocationInFile> definitionLocation() {
6980
}
7081

7182
@Override
72-
public int hashCode() {
73-
return Objects.hash(name, attributes, parameters, returnType, isAsynchronous, hasDecorators, isInstanceMethod, hasVariadicParameter);
83+
public String toString() {
84+
return "FunctionType[%s]".formatted(name);
7485
}
7586

7687
@Override
77-
public String toString() {
78-
return "FunctionType[%s]".formatted(name);
88+
public String name() {
89+
return name;
90+
}
91+
92+
public List<PythonType> attributes() {
93+
return attributes;
94+
}
95+
96+
public List<ParameterV2> parameters() {
97+
return parameters;
98+
}
99+
100+
public PythonType returnType() {
101+
return returnType;
102+
}
103+
104+
public boolean isAsynchronous() {
105+
return isAsynchronous;
106+
}
107+
108+
public boolean hasDecorators() {
109+
return hasDecorators;
110+
}
111+
112+
public boolean isInstanceMethod() {
113+
return isInstanceMethod;
114+
}
115+
116+
public boolean hasVariadicParameter() {
117+
return hasVariadicParameter;
118+
}
119+
120+
@CheckForNull
121+
public PythonType owner() {
122+
return owner;
79123
}
80124
}

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

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,22 @@
2323
import java.util.Map;
2424
import java.util.Objects;
2525
import java.util.Optional;
26+
import javax.annotation.CheckForNull;
2627
import javax.annotation.Nullable;
2728
import org.sonar.api.Beta;
2829

2930
@Beta
30-
public record ModuleType(@Nullable String name, @Nullable ModuleType parent, Map<String, PythonType> members) implements PythonType {
31+
public final class ModuleType implements PythonType {
32+
private final String name;
33+
private final ModuleType parent;
34+
private final Map<String, PythonType> members;
35+
36+
public ModuleType(@Nullable String name, @Nullable ModuleType parent, Map<String, PythonType> members) {
37+
this.name = name;
38+
this.parent = parent;
39+
this.members = members;
40+
}
41+
3142
public ModuleType(@Nullable String name) {
3243
this(name, null);
3344
}
@@ -41,25 +52,27 @@ public Optional<PythonType> resolveMember(String memberName) {
4152
return Optional.ofNullable(members.get(memberName));
4253
}
4354

44-
@Override
45-
public boolean equals(Object o) {
46-
// TODO: Find a way how we want to compare modules
47-
if (this == o) return true;
48-
if (o == null || getClass() != o.getClass()) return false;
49-
ModuleType that = (ModuleType) o;
50-
return Objects.equals(name, that.name) && Objects.equals(members, that.members);
51-
}
52-
53-
@Override
54-
public int hashCode() {
55-
return Objects.hash(name, members);
56-
}
57-
5855
@Override
5956
public String toString() {
6057
return "ModuleType{" +
6158
"name='" + name + '\'' +
6259
", members=" + members +
6360
'}';
6461
}
62+
63+
@Override
64+
@CheckForNull
65+
public String name() {
66+
return name;
67+
}
68+
69+
@CheckForNull
70+
public ModuleType parent() {
71+
return parent;
72+
}
73+
74+
public Map<String, PythonType> members() {
75+
return members;
76+
}
77+
6578
}

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,23 @@ public TriBool hasMember(String memberName) {
7272
public Optional<LocationInFile> definitionLocation() {
7373
return type.definitionLocation();
7474
}
75+
76+
@Override
77+
public boolean equals(Object o) {
78+
if (this == o) return true;
79+
if (o == null || getClass() != o.getClass()) return false;
80+
ObjectType that = (ObjectType) o;
81+
List<String> membersNames = members.stream().map(Member::name).toList();
82+
List<String> otherMembersNames = that.members.stream().map(Member::name).toList();
83+
List<String> attributesNames = attributes.stream().map(PythonType::key).toList();
84+
List<String> otherAttributesNames = that.attributes.stream().map(PythonType::key).toList();
85+
return Objects.equals(type, that.type) && Objects.equals(membersNames, otherMembersNames) && Objects.equals(attributesNames, otherAttributesNames);
86+
}
87+
88+
@Override
89+
public int hashCode() {
90+
List<String> membersNames = members.stream().map(Member::name).toList();
91+
List<String> attributesNames = attributes.stream().map(PythonType::key).toList();
92+
return Objects.hash(type, attributesNames, membersNames);
93+
}
7594
}

0 commit comments

Comments
 (0)