diff --git a/python-frontend/src/main/java/org/sonar/python/semantic/v2/converter/PythonTypeToDescriptorConverter.java b/python-frontend/src/main/java/org/sonar/python/semantic/v2/converter/PythonTypeToDescriptorConverter.java index b71de5678d..90d49a1c5e 100644 --- a/python-frontend/src/main/java/org/sonar/python/semantic/v2/converter/PythonTypeToDescriptorConverter.java +++ b/python-frontend/src/main/java/org/sonar/python/semantic/v2/converter/PythonTypeToDescriptorConverter.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import javax.annotation.CheckForNull; import org.sonar.python.index.AmbiguousDescriptor; import org.sonar.python.index.ClassDescriptor; import org.sonar.python.index.Descriptor; @@ -32,6 +33,7 @@ import org.sonar.python.types.v2.FunctionType; import org.sonar.python.types.v2.ParameterV2; import org.sonar.python.types.v2.PythonType; +import org.sonar.python.types.v2.TypeWrapper; import org.sonar.python.types.v2.UnionType; import org.sonar.python.types.v2.UnknownType; @@ -85,8 +87,9 @@ private Descriptor convert(String moduleFqn, String symbolName, FunctionType typ private Descriptor convert(String moduleFqn, String symbolName, ClassType type) { Set memberDescriptors = type.members().stream().map(m -> convert(moduleFqn, m.name(), m.type())).collect(Collectors.toSet()); + List superClasses = type.superClasses().stream().map(TypeWrapper::type).map(t -> typeFqn(moduleFqn, t)).collect(Collectors.toList()); return new ClassDescriptor(symbolName, symbolFqn(moduleFqn, symbolName), - List.of(), + superClasses, memberDescriptors, type.hasDecorators(), type.definitionLocation().orElse(null), @@ -115,13 +118,8 @@ private Descriptor convert(String moduleFqn, String symbolName, UnknownType.Unre } public FunctionDescriptor.Parameter convert(String moduleFqn, ParameterV2 parameter) { - String annotatedType = null; var type = parameter.declaredType().type().unwrappedType(); - if (type instanceof UnknownType.UnresolvedImportType importType) { - annotatedType = importType.importPath(); - } else if (type instanceof ClassType classType) { - annotatedType = moduleFqn + "." + classType.name(); - } + var annotatedType = typeFqn(moduleFqn, type); return new FunctionDescriptor.Parameter(parameter.name(), annotatedType, @@ -133,9 +131,18 @@ public FunctionDescriptor.Parameter convert(String moduleFqn, ParameterV2 parame parameter.location()); } + @CheckForNull + private static String typeFqn(String moduleFqn, PythonType type) { + if (type instanceof UnknownType.UnresolvedImportType importType) { + return importType.importPath(); + } else if (type instanceof ClassType classType) { + return moduleFqn + "." + classType.name(); + } + return null; + } + private static String symbolFqn(String moduleFqn, String symbolName) { return moduleFqn + "." + symbolName; } - } diff --git a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/indexer/SonarLintPythonIndexerProjectLevelSymbolTableBuildingTest.java b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/indexer/SonarLintPythonIndexerProjectLevelSymbolTableBuildingTest.java index 1bc2a04144..c9a0f64cc3 100644 --- a/sonar-python-plugin/src/test/java/org/sonar/plugins/python/indexer/SonarLintPythonIndexerProjectLevelSymbolTableBuildingTest.java +++ b/sonar-python-plugin/src/test/java/org/sonar/plugins/python/indexer/SonarLintPythonIndexerProjectLevelSymbolTableBuildingTest.java @@ -53,9 +53,9 @@ class SonarLintPythonIndexerProjectLevelSymbolTableBuildingTest { @Test void single_file_simple_test() throws IOException { var projectLevelSymbolTable = buildProjectLevelSymbolTable("script.py"); - assertThat(projectLevelSymbolTable.getDescriptorsFromModule("script")).hasSize(3); + assertThat(projectLevelSymbolTable.getDescriptorsFromModule("script")).hasSize(4); Set moduleDescriptors = projectLevelSymbolTable.getDescriptorsFromModuleV2("script"); - assertThat(moduleDescriptors).hasSize(3); + assertThat(moduleDescriptors).hasSize(4); var aClassDescriptor = moduleDescriptors .stream() @@ -66,6 +66,7 @@ void single_file_simple_test() throws IOException { .orElse(null); assertThat(aClassDescriptor).isNotNull(); assertThat(aClassDescriptor.members()).hasSize(1); + assertThat(aClassDescriptor.superClasses()).containsOnly("script.Parent", "int"); var doSomethingDescriptor = aClassDescriptor.members() .stream() diff --git a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/indexer/v2/script.py b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/indexer/v2/script.py index 3e04aa4e8d..19292e3712 100644 --- a/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/indexer/v2/script.py +++ b/sonar-python-plugin/src/test/resources/org/sonar/plugins/python/indexer/v2/script.py @@ -1,10 +1,12 @@ def foo(): ... -class A: +class Parent: + ... + +class A(Parent, int): def do_something(self, a: int): ... - if something: class B: def method_one(self): ...