Skip to content

Commit 7bfb947

Browse files
SONARPY-2156 Implement ClassSymbolToDescriptorConverter
1 parent 214b742 commit 7bfb947

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed
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.typeshed;
21+
22+
import java.util.function.Function;
23+
import java.util.stream.Collectors;
24+
import java.util.stream.Stream;
25+
import org.sonar.python.index.ClassDescriptor;
26+
import org.sonar.python.index.Descriptor;
27+
import org.sonar.python.types.protobuf.SymbolsProtos;
28+
29+
public class ClassSymbolToDescriptorConverter {
30+
31+
private final VarSymbolToDescriptorConverter varConverter;
32+
private final FunctionSymbolToDescriptorConverter functionConverter;
33+
private final OverloadedFunctionSymbolToDescriptorConverter overloadedFunctionConverter;
34+
35+
public ClassSymbolToDescriptorConverter(VarSymbolToDescriptorConverter varConverter,
36+
FunctionSymbolToDescriptorConverter functionConverter,
37+
OverloadedFunctionSymbolToDescriptorConverter overloadedFunctionConverter) {
38+
this.varConverter = varConverter;
39+
this.functionConverter = functionConverter;
40+
this.overloadedFunctionConverter = overloadedFunctionConverter;
41+
}
42+
43+
public ClassDescriptor convert(SymbolsProtos.ClassSymbol classSymbol) {
44+
var fullyQualifiedName = TypeShedUtils.normalizedFqn(classSymbol.getFullyQualifiedName());
45+
var superClasses = classSymbol.getSuperClassesList().stream().map(TypeShedUtils::normalizedFqn).toList();
46+
var metaclassName = TypeShedUtils.normalizedFqn(classSymbol.getMetaclassName());
47+
48+
var variableDescriptors = classSymbol.getAttributesList()
49+
.stream()
50+
.map(varConverter::convert);
51+
52+
var functionDescriptors = classSymbol.getMethodsList()
53+
.stream()
54+
.map(s -> functionConverter.convert(s, true));
55+
56+
var overloadedFunctionDescriptors = classSymbol.getOverloadedMethodsList()
57+
.stream()
58+
.map(s -> overloadedFunctionConverter.convert(s, true));
59+
60+
var members = Stream.of(variableDescriptors, functionDescriptors, overloadedFunctionDescriptors)
61+
.flatMap(Function.identity())
62+
.map(Descriptor.class::cast)
63+
.collect(Collectors.toSet());
64+
65+
return new ClassDescriptor.ClassDescriptorBuilder()
66+
.withName(classSymbol.getName())
67+
.withFullyQualifiedName(fullyQualifiedName)
68+
.withSuperClasses(superClasses)
69+
.withMetaclassFQN(metaclassName)
70+
.withHasMetaClass(classSymbol.getHasMetaclass())
71+
.withHasDecorators(classSymbol.getHasDecorators())
72+
.withMembers(members)
73+
.build();
74+
}
75+
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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.typeshed;
21+
22+
import java.util.function.Function;
23+
import java.util.stream.Collectors;
24+
import org.assertj.core.api.Assertions;
25+
import org.junit.jupiter.api.Test;
26+
import org.sonar.python.index.AmbiguousDescriptor;
27+
import org.sonar.python.index.Descriptor;
28+
import org.sonar.python.index.FunctionDescriptor;
29+
import org.sonar.python.index.VariableDescriptor;
30+
import org.sonar.python.types.protobuf.SymbolsProtos;
31+
32+
class ClassSymbolToDescriptorConverterTest {
33+
34+
@Test
35+
void test() {
36+
var functionConverter = new FunctionSymbolToDescriptorConverter();
37+
var variableConverter = new VarSymbolToDescriptorConverter();
38+
var overloadedFunctionConverter = new OverloadedFunctionSymbolToDescriptorConverter(functionConverter);
39+
var converter = new ClassSymbolToDescriptorConverter(variableConverter, functionConverter, overloadedFunctionConverter);
40+
41+
var symbol = SymbolsProtos.ClassSymbol.newBuilder()
42+
.setName("MyClass")
43+
.setFullyQualifiedName("module.MyClass")
44+
.addSuperClasses("module.AnotherClass")
45+
.setMetaclassName("module.MetaClass")
46+
.setHasMetaclass(true)
47+
.setHasDecorators(true)
48+
.addAttributes(SymbolsProtos.VarSymbol.newBuilder()
49+
.setName("v1")
50+
.setFullyQualifiedName("module.MyClass.v1")
51+
.build())
52+
.addMethods(SymbolsProtos.FunctionSymbol.newBuilder()
53+
.setName("foo")
54+
.setFullyQualifiedName("module.MyClass.foo")
55+
.build())
56+
.addOverloadedMethods(SymbolsProtos.OverloadedFunctionSymbol.newBuilder()
57+
.setName("overloaded_foo")
58+
.setFullname("module.MyClass.overloaded_foo")
59+
.addDefinitions(SymbolsProtos.FunctionSymbol.newBuilder()
60+
.setName("overloaded_foo")
61+
.setFullyQualifiedName("module.MyClass.overloaded_foo")
62+
.build())
63+
.addDefinitions(SymbolsProtos.FunctionSymbol.newBuilder()
64+
.setName("overloaded_foo")
65+
.setFullyQualifiedName("module.MyClass.overloaded_foo")
66+
.build())
67+
.build())
68+
.build();
69+
70+
var descriptor = converter.convert(symbol);
71+
72+
Assertions.assertThat(descriptor.name()).isEqualTo("MyClass");
73+
Assertions.assertThat(descriptor.fullyQualifiedName()).isEqualTo("module.MyClass");
74+
Assertions.assertThat(descriptor.superClasses()).hasSize(1).containsOnly("module.AnotherClass");
75+
Assertions.assertThat(descriptor.metaclassFQN()).isEqualTo("module.MetaClass");
76+
Assertions.assertThat(descriptor.hasMetaClass()).isTrue();
77+
Assertions.assertThat(descriptor.hasDecorators()).isTrue();
78+
Assertions.assertThat(descriptor.members()).hasSize(3);
79+
80+
var membersByName = descriptor.members()
81+
.stream()
82+
.collect(Collectors.toMap(Descriptor::name, Function.identity()));
83+
84+
Assertions.assertThat(membersByName).hasSize(3);
85+
Assertions.assertThat(membersByName.get("v1")).isInstanceOf(VariableDescriptor.class);
86+
Assertions.assertThat(membersByName.get("foo")).isInstanceOf(FunctionDescriptor.class);
87+
Assertions.assertThat(membersByName.get("overloaded_foo")).isInstanceOf(AmbiguousDescriptor.class);
88+
89+
var foo = (FunctionDescriptor) membersByName.get("foo");
90+
var overloadedFoo = (AmbiguousDescriptor) membersByName.get("overloaded_foo");
91+
var overloadedFooCandidates = overloadedFoo.alternatives().stream().toList();
92+
93+
Assertions.assertThat(foo.isInstanceMethod()).isTrue();
94+
Assertions.assertThat(overloadedFooCandidates)
95+
.extracting(FunctionDescriptor.class::cast)
96+
.extracting(FunctionDescriptor::isInstanceMethod)
97+
.containsOnly(true, true);
98+
}
99+
100+
@Test
101+
void builtinsTest() {
102+
var functionConverter = new FunctionSymbolToDescriptorConverter();
103+
var variableConverter = new VarSymbolToDescriptorConverter();
104+
var overloadedFunctionConverter = new OverloadedFunctionSymbolToDescriptorConverter(functionConverter);
105+
var converter = new ClassSymbolToDescriptorConverter(variableConverter, functionConverter, overloadedFunctionConverter);
106+
107+
var symbol = SymbolsProtos.ClassSymbol.newBuilder()
108+
.setName("int")
109+
.setFullyQualifiedName("builtins.int")
110+
.addSuperClasses("builtins.int_superclass")
111+
.setMetaclassName("builtins.meta_class")
112+
.build();
113+
114+
var descriptor = converter.convert(symbol);
115+
116+
Assertions.assertThat(descriptor.name()).isEqualTo("int");
117+
Assertions.assertThat(descriptor.fullyQualifiedName()).isEqualTo("int");
118+
Assertions.assertThat(descriptor.superClasses()).containsOnly("int_superclass");
119+
Assertions.assertThat(descriptor.metaclassFQN()).isEqualTo("meta_class");
120+
}
121+
122+
}

0 commit comments

Comments
 (0)