Skip to content

Commit 5621e77

Browse files
SONARPY-2126 Implement any Descriptor to a PythonType converter infrastructure (#1968)
1 parent b540f4e commit 5621e77

File tree

6 files changed

+301
-0
lines changed

6 files changed

+301
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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.Map;
23+
import org.sonar.python.index.Descriptor;
24+
import org.sonar.python.semantic.v2.LazyTypesContext;
25+
import org.sonar.python.types.v2.PythonType;
26+
27+
public class AnyDescriptorToPythonTypeConverter {
28+
29+
private static final DescriptorToPythonTypeConverter UNKNOWN_DESCRIPTOR_CONVERTER = new UnknownDescriptorToPythonTypeConverter();
30+
private final Map<Descriptor.Kind, DescriptorToPythonTypeConverter> converters;
31+
private final LazyTypesContext lazyTypesContext;
32+
33+
public AnyDescriptorToPythonTypeConverter(LazyTypesContext lazyTypesContext) {
34+
this.lazyTypesContext = lazyTypesContext;
35+
converters = Map.of();
36+
}
37+
38+
public PythonType convert(Descriptor from) {
39+
var ctx = new ConversionContext(lazyTypesContext, this::convert);
40+
return convert(ctx, from);
41+
}
42+
43+
private PythonType convert(ConversionContext ctx, Descriptor from) {
44+
return converterFor(from).convert(ctx, from);
45+
}
46+
47+
private DescriptorToPythonTypeConverter converterFor(Descriptor descriptor) {
48+
return converters.getOrDefault(descriptor.kind(), UNKNOWN_DESCRIPTOR_CONVERTER);
49+
}
50+
51+
52+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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.ArrayDeque;
23+
import java.util.Deque;
24+
import org.sonar.python.index.Descriptor;
25+
import org.sonar.python.semantic.v2.LazyTypesContext;
26+
import org.sonar.python.types.v2.PythonType;
27+
28+
public class ConversionContext {
29+
private final LazyTypesContext lazyTypesContext;
30+
private final DescriptorToPythonTypeConverter converter;
31+
private final Deque<PythonType> parents;
32+
33+
public ConversionContext(LazyTypesContext lazyTypesContext, DescriptorToPythonTypeConverter converter) {
34+
this.lazyTypesContext = lazyTypesContext;
35+
this.converter = converter;
36+
this.parents = new ArrayDeque<>();
37+
}
38+
39+
public LazyTypesContext lazyTypesContext() {
40+
return lazyTypesContext;
41+
}
42+
43+
public PythonType convert(Descriptor from) {
44+
return converter.convert(this, from);
45+
}
46+
47+
public void pushParent(PythonType pythonType) {
48+
parents.push(pythonType);
49+
}
50+
51+
public PythonType currentParent() {
52+
return parents.peek();
53+
}
54+
55+
public PythonType pollParent() {
56+
return parents.poll();
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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.sonar.python.index.Descriptor;
23+
import org.sonar.python.types.v2.PythonType;
24+
25+
public interface DescriptorToPythonTypeConverter {
26+
27+
PythonType convert(ConversionContext ctx, Descriptor from);
28+
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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.sonar.python.index.Descriptor;
23+
import org.sonar.python.types.v2.PythonType;
24+
25+
public class UnknownDescriptorToPythonTypeConverter implements DescriptorToPythonTypeConverter {
26+
@Override
27+
public PythonType convert(ConversionContext ctx, Descriptor from) {
28+
return PythonType.UNKNOWN;
29+
}
30+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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.junit.jupiter.api.Assertions;
23+
import org.junit.jupiter.api.Test;
24+
import org.mockito.Mockito;
25+
import org.sonar.python.index.Descriptor;
26+
import org.sonar.python.semantic.v2.LazyTypesContext;
27+
import org.sonar.python.types.v2.PythonType;
28+
29+
class ConversionContextTest {
30+
31+
@Test
32+
void lazyTypeContextTest() {
33+
var expectedLazyTypeContext = Mockito.mock(LazyTypesContext.class);
34+
var rootConverter = Mockito.mock(DescriptorToPythonTypeConverter.class);
35+
var ctx = new ConversionContext(expectedLazyTypeContext, rootConverter);
36+
var lazyTypesContext = ctx.lazyTypesContext();
37+
Assertions.assertSame(expectedLazyTypeContext, lazyTypesContext);
38+
}
39+
40+
@Test
41+
void parentsTest() {
42+
var expectedLazyTypeContext = Mockito.mock(LazyTypesContext.class);
43+
var rootConverter = Mockito.mock(DescriptorToPythonTypeConverter.class);
44+
var ctx = new ConversionContext(expectedLazyTypeContext, rootConverter);
45+
var firstParent = Mockito.mock(PythonType.class);
46+
var secondParent = Mockito.mock(PythonType.class);
47+
48+
Assertions.assertNull(ctx.currentParent());
49+
ctx.pushParent(firstParent);
50+
Assertions.assertSame(firstParent, ctx.currentParent());
51+
ctx.pushParent(secondParent);
52+
Assertions.assertSame(secondParent, ctx.currentParent());
53+
Assertions.assertSame(secondParent, ctx.pollParent());
54+
Assertions.assertSame(firstParent, ctx.currentParent());
55+
Assertions.assertSame(firstParent, ctx.pollParent());
56+
Assertions.assertNull(ctx.currentParent());
57+
}
58+
59+
@Test
60+
void convertTest() {
61+
var descriptor = Mockito.mock(Descriptor.class);
62+
var expectedType = Mockito.mock(PythonType.class);
63+
64+
var lazyTypeContext = Mockito.mock(LazyTypesContext.class);
65+
var rootConverter = Mockito.mock(DescriptorToPythonTypeConverter.class);
66+
var ctx = new ConversionContext(lazyTypeContext, rootConverter);
67+
68+
Mockito.when(rootConverter.convert(ctx, descriptor))
69+
.thenReturn(expectedType);
70+
71+
var type = ctx.convert(descriptor);
72+
Assertions.assertSame(expectedType, type);
73+
}
74+
75+
76+
}
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.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.AmbiguousDescriptor;
26+
import org.sonar.python.index.Descriptor;
27+
import org.sonar.python.semantic.v2.LazyTypesContext;
28+
import org.sonar.python.types.v2.PythonType;
29+
30+
class DescriptorToPythonTypeConverterTest {
31+
32+
@Test
33+
void unknownDescriptorToPythonTypeConverterTest() {
34+
var ctx = Mockito.mock(ConversionContext.class);
35+
var descriptor = Mockito.mock(Descriptor.class);
36+
Mockito.when(descriptor.kind()).thenReturn(null);
37+
var converter = new UnknownDescriptorToPythonTypeConverter();
38+
var type = converter.convert(ctx, descriptor);
39+
Assertions.assertThat(type).isEqualTo(PythonType.UNKNOWN);
40+
}
41+
42+
@Test
43+
void ambiguousDescriptorConversionTest() {
44+
var lazyTypesContext = Mockito.mock(LazyTypesContext.class);
45+
var converter = new AnyDescriptorToPythonTypeConverter(lazyTypesContext);
46+
var descriptor = Mockito.mock(AmbiguousDescriptor.class);
47+
Mockito.when(descriptor.kind()).thenReturn(Descriptor.Kind.AMBIGUOUS);
48+
49+
var type = converter.convert(descriptor);
50+
Assertions.assertThat(type).isEqualTo(PythonType.UNKNOWN);
51+
}
52+
53+
54+
55+
}

0 commit comments

Comments
 (0)