Skip to content

Commit ea52d33

Browse files
SONARPY-2029 Implement API to get a module type's member type from project level types table (#1880)
1 parent 723f0eb commit ea52d33

File tree

3 files changed

+113
-0
lines changed

3 files changed

+113
-0
lines changed

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.sonar.python.semantic.ProjectLevelSymbolTable;
2525
import org.sonar.python.types.v2.ModuleType;
2626
import org.sonar.python.types.v2.PythonType;
27+
import org.sonar.python.types.v2.TriBool;
2728

2829
public class ProjectLevelTypeTable {
2930

@@ -61,4 +62,30 @@ public ModuleType getModule(List<String> moduleNameParts) {
6162
}
6263
return parent;
6364
}
65+
66+
public PythonType getType(String typeFqn) {
67+
return getType(typeFqn.split("\\."));
68+
}
69+
70+
public PythonType getType(String... typeFqnParts) {
71+
return getType(List.of(typeFqnParts));
72+
}
73+
74+
public PythonType getType(List<String> typeFqnParts) {
75+
var parent = (PythonType) rootModule;
76+
for (int i = 0; i < typeFqnParts.size(); i++) {
77+
var part = typeFqnParts.get(i);
78+
if (parent.hasMember(part) == TriBool.TRUE) {
79+
parent = parent.resolveMember(part).orElse(PythonType.UNKNOWN);
80+
} else if (parent instanceof ModuleType module) {
81+
var moduleFqn = IntStream.rangeClosed(0, i)
82+
.mapToObj(typeFqnParts::get)
83+
.toList();
84+
parent = symbolsModuleTypeProvider.createModuleType(moduleFqn, module);
85+
} else {
86+
return PythonType.UNKNOWN;
87+
}
88+
}
89+
return parent;
90+
}
6491
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ public Optional<PythonType> resolveMember(String memberName) {
5151
return Optional.ofNullable(members.get(memberName));
5252
}
5353

54+
@Override
55+
public TriBool hasMember(String memberName) {
56+
if (resolveMember(memberName).isPresent()) {
57+
return TriBool.TRUE;
58+
}
59+
return TriBool.UNKNOWN;
60+
}
61+
5462
@Override
5563
public String toString() {
5664
return "ModuleType{" +
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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 org.assertj.core.api.Assertions;
23+
import org.junit.jupiter.api.Test;
24+
import org.sonar.python.semantic.ProjectLevelSymbolTable;
25+
import org.sonar.python.types.v2.ClassType;
26+
import org.sonar.python.types.v2.ModuleType;
27+
import org.sonar.python.types.v2.PythonType;
28+
import org.sonar.python.types.v2.TriBool;
29+
30+
class ProjectLevelTypeTableTest {
31+
32+
@Test
33+
void getBuiltinTypeTest() {
34+
var symbolTable = ProjectLevelSymbolTable.empty();
35+
var table = new ProjectLevelTypeTable(symbolTable, new TypeShed(symbolTable));
36+
37+
var listClassType = table.getType("list");
38+
Assertions.assertThat(listClassType).isNotNull().isInstanceOf(ClassType.class);
39+
40+
var builtinsModuleType = table.getType();
41+
Assertions.assertThat(builtinsModuleType).isNotNull().isInstanceOf(ModuleType.class);
42+
Assertions.assertThat(builtinsModuleType.resolveMember("list")).isPresent().containsSame(listClassType);
43+
44+
Assertions.assertThat(table.getType("list.something")).isSameAs(PythonType.UNKNOWN);
45+
}
46+
47+
@Test
48+
void getTypeshedTypeTest() {
49+
var symbolTable = ProjectLevelSymbolTable.empty();
50+
var table = new ProjectLevelTypeTable(symbolTable, new TypeShed(symbolTable));
51+
52+
var generatorClassType = table.getType("typing.Generator");
53+
Assertions.assertThat(generatorClassType).isNotNull().isInstanceOf(ClassType.class);
54+
55+
var typingModuleType = table.getType("typing");
56+
Assertions.assertThat(typingModuleType).isNotNull().isInstanceOf(ModuleType.class);
57+
Assertions.assertThat(typingModuleType.resolveMember("Generator")).isPresent().containsSame(generatorClassType);
58+
}
59+
60+
@Test
61+
void updateTypeTableDuringGetTypeTest() {
62+
var symbolTable = ProjectLevelSymbolTable.empty();
63+
var table = new ProjectLevelTypeTable(symbolTable, new TypeShed(symbolTable));
64+
65+
var rootModuleType = table.getType();
66+
Assertions.assertThat(rootModuleType.hasMember("typing")).isNotEqualTo(TriBool.TRUE);
67+
68+
var generatorClassType = table.getType("typing.Generator");
69+
Assertions.assertThat(generatorClassType).isNotNull().isInstanceOf(ClassType.class);
70+
71+
var typingModuleType = table.getType("typing");
72+
Assertions.assertThat(typingModuleType).isNotNull().isInstanceOf(ModuleType.class);
73+
Assertions.assertThat(typingModuleType.resolveMember("Generator")).isPresent().containsSame(generatorClassType);
74+
75+
Assertions.assertThat(rootModuleType.hasMember("typing")).isEqualTo(TriBool.TRUE);
76+
Assertions.assertThat(rootModuleType.resolveMember("typing")).containsSame(typingModuleType);
77+
}
78+
}

0 commit comments

Comments
 (0)