Skip to content

Commit 9e2dfed

Browse files
ghislainpiotguillaume-dequenne
authored andcommitted
SONARPY-2165 Implement AmbiguousDescriptorToPythonTypeConverter
1 parent c465221 commit 9e2dfed

File tree

4 files changed

+135
-3
lines changed

4 files changed

+135
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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.converter;
21+
22+
import java.util.stream.Collectors;
23+
import org.sonar.python.index.AmbiguousDescriptor;
24+
import org.sonar.python.index.Descriptor;
25+
import org.sonar.python.types.v2.PythonType;
26+
import org.sonar.python.types.v2.UnionType;
27+
28+
public class AmbiguousDescriptorToPythonTypeConverter implements DescriptorToPythonTypeConverter {
29+
30+
public PythonType convert(ConversionContext ctx, AmbiguousDescriptor from) {
31+
var candidates = from.alternatives().stream().map(ctx::convert).collect(Collectors.toSet());
32+
return new UnionType(candidates);
33+
}
34+
35+
@Override
36+
public PythonType convert(ConversionContext ctx, Descriptor from) {
37+
if (from instanceof AmbiguousDescriptor ambiguousDescriptor) {
38+
return convert(ctx, ambiguousDescriptor);
39+
}
40+
throw new IllegalArgumentException("Unsupported Descriptor");
41+
}
42+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ public AnyDescriptorToPythonTypeConverter(LazyTypesContext lazyTypesContext) {
3636
converters = new EnumMap<>(Map.of(
3737
Descriptor.Kind.CLASS, new ClassDescriptorToPythonTypeConverter(),
3838
Descriptor.Kind.FUNCTION, new FunctionDescriptorToPythonTypeConverter(),
39-
Descriptor.Kind.VARIABLE, new VariableDescriptorToPythonTypeConverter()
39+
Descriptor.Kind.VARIABLE, new VariableDescriptorToPythonTypeConverter(),
40+
Descriptor.Kind.AMBIGUOUS, new AmbiguousDescriptorToPythonTypeConverter()
4041
));
4142
}
4243

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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.converter;
21+
22+
import org.assertj.core.api.Assertions;
23+
import org.junit.jupiter.api.Test;
24+
import org.mockito.Mockito;
25+
import org.sonar.python.index.ClassDescriptor;
26+
27+
class AmbiguousDescriptorToPythonTypeConverterTest {
28+
@Test
29+
void unsupportedClassTest() {
30+
var ctx = Mockito.mock(ConversionContext.class);
31+
var descriptor = Mockito.mock(ClassDescriptor.class);
32+
var converter = new AmbiguousDescriptorToPythonTypeConverter();
33+
Assertions.assertThatThrownBy(() -> converter.convert(ctx, descriptor))
34+
.isInstanceOf(IllegalArgumentException.class)
35+
.hasMessage("Unsupported Descriptor");
36+
}
37+
38+
}

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

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.sonar.python.semantic.v2.converter;
2121

2222
import java.util.List;
23+
import java.util.Set;
2324
import org.assertj.core.api.Assertions;
2425
import org.junit.jupiter.api.Test;
2526
import org.mockito.Mockito;
@@ -37,8 +38,10 @@
3738
import org.sonar.python.types.v2.LazyTypeWrapper;
3839
import org.sonar.python.types.v2.Member;
3940
import org.sonar.python.types.v2.ObjectType;
41+
import org.sonar.python.types.v2.ParameterV2;
4042
import org.sonar.python.types.v2.PythonType;
4143
import org.sonar.python.types.v2.TypeWrapper;
44+
import org.sonar.python.types.v2.UnionType;
4245

4346
class DescriptorToPythonTypeConverterTest {
4447

@@ -56,11 +59,59 @@ void unknownDescriptorToPythonTypeConverterTest() {
5659
void ambiguousDescriptorConversionTest() {
5760
var lazyTypesContext = Mockito.mock(LazyTypesContext.class);
5861
var converter = new AnyDescriptorToPythonTypeConverter(lazyTypesContext);
62+
63+
var descriptorAlternative1 = Mockito.mock(FunctionDescriptor.class);
64+
65+
var returnTypeName = "Returned";
66+
var resolvedReturnType = new ClassTypeBuilder().withName(returnTypeName).build();
67+
Mockito.when(lazyTypesContext.resolveLazyType(Mockito.argThat(lt -> returnTypeName.equals(lt.fullyQualifiedName()))))
68+
.thenReturn(resolvedReturnType);
69+
70+
Mockito.when(descriptorAlternative1.kind()).thenReturn(Descriptor.Kind.FUNCTION);
71+
Mockito.when(descriptorAlternative1.name()).thenReturn("Sample");
72+
Mockito.when(descriptorAlternative1.annotatedReturnTypeName()).thenReturn(returnTypeName);
73+
Mockito.when(descriptorAlternative1.parameters()).thenReturn(List.of(
74+
new FunctionDescriptor.Parameter(
75+
"p1",
76+
"Returned",
77+
false,
78+
false,
79+
true,
80+
false,
81+
false,
82+
new LocationInFile("m1", 1, 10, 1, 15))
83+
));
84+
85+
var descriptorAlternative2 = Mockito.mock(FunctionDescriptor.class);
86+
Mockito.when(descriptorAlternative2.kind()).thenReturn(Descriptor.Kind.FUNCTION);
87+
Mockito.when(descriptorAlternative2.name()).thenReturn("Sample");
88+
Mockito.when(descriptorAlternative2.annotatedReturnTypeName()).thenReturn(returnTypeName);
89+
Mockito.when(descriptorAlternative2.parameters()).thenReturn(List.of(
90+
new FunctionDescriptor.Parameter(
91+
"p2",
92+
"Returned",
93+
false,
94+
false,
95+
true,
96+
false,
97+
false,
98+
new LocationInFile("m1", 2, 10, 2, 15))
99+
));
100+
59101
var descriptor = Mockito.mock(AmbiguousDescriptor.class);
60102
Mockito.when(descriptor.kind()).thenReturn(Descriptor.Kind.AMBIGUOUS);
103+
Mockito.when(descriptor.alternatives()).thenReturn(Set.of(descriptorAlternative1, descriptorAlternative2));
104+
105+
var type = (UnionType) converter.convert(descriptor);
106+
Assertions.assertThat(type.candidates())
107+
.hasSize(2);
108+
109+
Assertions.assertThat(type.candidates())
110+
.extracting(FunctionType.class::cast)
111+
.flatExtracting(FunctionType::parameters)
112+
.extracting(ParameterV2::name)
113+
.containsOnly("p1", "p2");
61114

62-
var type = converter.convert(descriptor);
63-
Assertions.assertThat(type).isEqualTo(PythonType.UNKNOWN);
64115
}
65116

66117
@Test

0 commit comments

Comments
 (0)