Skip to content

Commit

Permalink
SONARPY-2219 Class type members to descriptors conversion implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
thomas-serre-sonarsource committed Oct 21, 2024
1 parent 05f36c7 commit 3670930
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public static ProjectLevelSymbolTable globalSymbols(List<File> files, File baseD
var astRoot = parseFile(pythonFile);
String packageName = pythonPackageName(file, baseDir.getAbsolutePath());
projectLevelSymbolTable.addModule(astRoot, packageName, pythonFile);
projectLevelSymbolTable.addModuleV2(packageName, pythonFile, astRoot);
}
return projectLevelSymbolTable;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,24 +221,24 @@ public TypeShedDescriptorsProvider typeShedDescriptorsProvider() {
return typeShedDescriptorsProvider;
}

public void addModule(String fullyQualifiedModuleName, Set<Descriptor> moduleDescriptors) {
public void addModuleV2(String fullyQualifiedModuleName, Set<Descriptor> moduleDescriptors) {
globalDescriptorsByModuleNameV2.put(fullyQualifiedModuleName, moduleDescriptors);
}

public void addModule(String fullyQualifiedModuleName, Map<SymbolV2, Set<PythonType>> typesBySymbol) {
public void addModuleV2(String fullyQualifiedModuleName, Map<SymbolV2, Set<PythonType>> typesBySymbol) {
var moduleDescriptors = typesBySymbol.entrySet()
.stream()
.map(entry -> pythonTypeToDescriptorConverter.convert(fullyQualifiedModuleName, entry.getKey(), entry.getValue()))
.collect(Collectors.toSet());
addModule(fullyQualifiedModuleName, moduleDescriptors);
addModuleV2(fullyQualifiedModuleName, moduleDescriptors);
}

public void addModule(String packageName, PythonFile pythonFile, FileInput astRoot) {
public void addModuleV2(String packageName, PythonFile pythonFile, FileInput astRoot) {
var fullyQualifiedModuleName = SymbolUtils.fullyQualifiedModuleName(packageName, pythonFile.fileName());
var symbolTable = new SymbolTableBuilderV2(astRoot).build();
var typeInferenceV2 = new TypeInferenceV2(new BasicTypeTable(), pythonFile, symbolTable);
var typesBySymbol = typeInferenceV2.inferTypes(astRoot);
addModule(fullyQualifiedModuleName, typesBySymbol);
addModuleV2(fullyQualifiedModuleName, typesBySymbol);
}

private class DjangoViewsVisitor extends BaseTreeVisitor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,39 +39,39 @@ public class PythonTypeToDescriptorConverter {

public Descriptor convert(String moduleFqn, SymbolV2 symbol, Set<PythonType> types) {
var candidates = types.stream()
.map(type -> convert(moduleFqn, symbol, type))
.map(type -> convert(moduleFqn, symbol.name(), type))
.collect(Collectors.toSet());

if (candidates.size() == 1) {
return candidates.iterator().next();
}
return new AmbiguousDescriptor(symbol.name(), symbolFqn(moduleFqn, symbol), candidates);
return new AmbiguousDescriptor(symbol.name(), symbolFqn(moduleFqn, symbol.name()), candidates);
}

private Descriptor convert(String moduleFqn, SymbolV2 symbol, PythonType type) {
private Descriptor convert(String moduleFqn, String symbolName, PythonType type) {
if (type instanceof FunctionType functionType) {
return convert(moduleFqn, symbol, functionType);
return convert(moduleFqn, symbolName, functionType);
}
if (type instanceof ClassType classType) {
return convert(moduleFqn, symbol, classType);
return convert(moduleFqn, symbolName, classType);
}
if (type instanceof UnionType unionType) {
return convert(moduleFqn, symbol, unionType);
return convert(moduleFqn, symbolName, unionType);
}
if (type instanceof UnknownType.UnresolvedImportType unresolvedImportType) {
return convert(moduleFqn, symbol, unresolvedImportType);
return convert(moduleFqn, symbolName, unresolvedImportType);
}
return new VariableDescriptor(symbol.name(), symbolFqn(moduleFqn, symbol), null);
return new VariableDescriptor(symbolName, symbolFqn(moduleFqn, symbolName), null);
}

private Descriptor convert(String moduleFqn, SymbolV2 symbol, FunctionType type) {
private Descriptor convert(String moduleFqn, String symbolName, FunctionType type) {

var parameters = type.parameters()
.stream()
.map(parameter -> convert(moduleFqn, parameter))
.toList();

return new FunctionDescriptor(symbol.name(), symbolFqn(moduleFqn, symbol),
return new FunctionDescriptor(symbolName, symbolFqn(moduleFqn, symbolName),
parameters,
type.isAsynchronous(),
type.isInstanceMethod(),
Expand All @@ -83,10 +83,11 @@ private Descriptor convert(String moduleFqn, SymbolV2 symbol, FunctionType type)
);
}

private Descriptor convert(String moduleFqn, SymbolV2 symbol, ClassType type) {
return new ClassDescriptor(symbol.name(), symbolFqn(moduleFqn, symbol),
private Descriptor convert(String moduleFqn, String symbolName, ClassType type) {
Set<Descriptor> memberDescriptors = type.members().stream().map(m -> convert(moduleFqn, m.name(), m.type())).collect(Collectors.toSet());
return new ClassDescriptor(symbolName, symbolFqn(moduleFqn, symbolName),
List.of(),
Set.of(),
memberDescriptors,
type.hasDecorators(),
type.definitionLocation().orElse(null),
false,
Expand All @@ -96,19 +97,19 @@ private Descriptor convert(String moduleFqn, SymbolV2 symbol, ClassType type) {
);
}

private Descriptor convert(String moduleFqn, SymbolV2 symbol, UnionType type) {
private Descriptor convert(String moduleFqn, String symbolName, UnionType type) {
var candidates = type.candidates().stream()
.map(candidateType -> convert(moduleFqn, symbol, candidateType))
.map(candidateType -> convert(moduleFqn, symbolName, candidateType))
.collect(Collectors.toSet());
return new AmbiguousDescriptor(symbol.name(),
symbolFqn(moduleFqn, symbol),
return new AmbiguousDescriptor(symbolName,
symbolFqn(moduleFqn, symbolName),
candidates
);
}

private Descriptor convert(String moduleFqn, SymbolV2 symbol, UnknownType.UnresolvedImportType type) {
return new VariableDescriptor(symbol.name(),
symbolFqn(moduleFqn, symbol),
private Descriptor convert(String moduleFqn, String symbolName, UnknownType.UnresolvedImportType type) {
return new VariableDescriptor(symbolName,
symbolFqn(moduleFqn, symbolName),
type.importPath()
);
}
Expand All @@ -132,8 +133,8 @@ public FunctionDescriptor.Parameter convert(String moduleFqn, ParameterV2 parame
parameter.location());
}

private static String symbolFqn(String moduleFqn, SymbolV2 symbol) {
return moduleFqn + "." + symbol.name();
private static String symbolFqn(String moduleFqn, String symbolName) {
return moduleFqn + "." + symbolName;
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ void inferFunctionParameterTypesMultiFile() {
ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable();
var modFile = pythonFile("mod.py");
projectLevelSymbolTable.addModule(tree, "", modFile);
projectLevelSymbolTable.addModule("", modFile, tree);
projectLevelSymbolTable.addModuleV2("", modFile, tree);
ProjectLevelTypeTable projectLevelTypeTable = new ProjectLevelTypeTable(projectLevelSymbolTable);
var modFileId = SymbolUtils.pathOf(modFile).toString();

Expand Down Expand Up @@ -2096,7 +2096,7 @@ void type_origin_of_project_function() {
);
ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable();
projectLevelSymbolTable.addModule(tree, "", pythonFile("mod.py"));
projectLevelSymbolTable.addModule("", pythonFile("mod.py"), tree);
projectLevelSymbolTable.addModuleV2("", pythonFile("mod.py"), tree);
ProjectLevelTypeTable projectLevelTypeTable = new ProjectLevelTypeTable(projectLevelSymbolTable);

var lines = """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,14 +275,14 @@ class B: pass
);
PythonFile pythonFile = PythonTestUtils.pythonFile("mod.py");
projectLevelSymbolTable.addModule(tree, "my_package", pythonFile);
projectLevelSymbolTable.addModule("my_package", pythonFile, tree);
projectLevelSymbolTable.addModuleV2("my_package", pythonFile, tree);
ProjectLevelTypeTable projectLevelTypeTable = new ProjectLevelTypeTable(projectLevelSymbolTable);
TypeChecker localTypeChecker = new TypeChecker(projectLevelTypeTable);

FileInput initTree = parseWithoutSymbols("");
PythonFile initFile = PythonTestUtils.pythonFile("__init__.py");
projectLevelSymbolTable.addModule(initTree, "my_package", initFile);
projectLevelSymbolTable.addModule("my_package", initFile, initTree);
projectLevelSymbolTable.addModuleV2("my_package", initFile, initTree);

var fileInput = parseAndInferTypes(projectLevelTypeTable, pythonFile, """
from my_package.mod import A
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.slf4j.Logger;
Expand All @@ -40,19 +38,9 @@
import org.sonar.plugins.python.api.SonarLintCache;
import org.sonar.plugins.python.api.caching.CacheContext;
import org.sonar.plugins.python.api.tree.FileInput;
import org.sonar.python.index.Descriptor;
import org.sonar.python.parser.PythonParser;
import org.sonar.python.semantic.ProjectLevelSymbolTable;
import org.sonar.python.semantic.SymbolUtils;
import org.sonar.python.semantic.v2.BasicTypeTable;
import org.sonar.python.semantic.v2.ProjectLevelTypeTable;
import org.sonar.python.semantic.v2.SymbolTableBuilderV2;
import org.sonar.python.semantic.v2.SymbolV2;
import org.sonar.python.semantic.v2.TypeInferenceV2;
import org.sonar.python.semantic.v2.converter.PythonTypeToDescriptorConverter;
import org.sonar.python.tree.PythonTreeMaker;
import org.sonar.python.types.TypeInference;
import org.sonar.python.types.v2.PythonType;

import static org.sonar.python.semantic.SymbolUtils.pythonPackageName;

Expand Down Expand Up @@ -108,7 +96,7 @@ void addFile(PythonInputFile inputFile) throws IOException {
projectLevelSymbolTable.addModule(astRoot, packageName, pythonFile);


projectLevelSymbolTable.addModule(packageName, pythonFile, astRoot);
projectLevelSymbolTable.addModuleV2(packageName, pythonFile, astRoot);
}

public abstract void buildOnce(SensorContext context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
Expand All @@ -35,6 +36,9 @@
import org.sonar.plugins.python.PythonInputFile;
import org.sonar.plugins.python.PythonInputFileImpl;
import org.sonar.plugins.python.TestUtils;
import org.sonar.python.index.ClassDescriptor;
import org.sonar.python.index.Descriptor;
import org.sonar.python.index.FunctionDescriptor;
import org.sonar.python.semantic.ProjectLevelSymbolTable;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -50,7 +54,28 @@ class SonarLintPythonIndexerProjectLevelSymbolTableBuildingTest {
void single_file_simple_test() throws IOException {
var projectLevelSymbolTable = buildProjectLevelSymbolTable("script.py");
assertThat(projectLevelSymbolTable.getDescriptorsFromModule("script")).hasSize(3);
assertThat(projectLevelSymbolTable.getDescriptorsFromModuleV2("script")).hasSize(3);
Set<Descriptor> moduleDescriptors = projectLevelSymbolTable.getDescriptorsFromModuleV2("script");
assertThat(moduleDescriptors).hasSize(3);

var aClassDescriptor = moduleDescriptors
.stream()
.filter(d -> d.name().equals("A"))
.findFirst()
.filter(ClassDescriptor.class::isInstance)
.map(ClassDescriptor.class::cast)
.orElse(null);
assertThat(aClassDescriptor).isNotNull();
assertThat(aClassDescriptor.members()).hasSize(1);

var doSomethingDescriptor = aClassDescriptor.members()
.stream()
.filter(d -> d.name().equals("do_something"))
.findFirst()
.filter(FunctionDescriptor.class::isInstance)
.map(FunctionDescriptor.class::cast)
.orElse(null);
assertThat(doSomethingDescriptor).isNotNull();
assertThat(doSomethingDescriptor.parameters()).hasSize(2);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
def foo(): ...

class A:
def do_something(self):
def do_something(self, a: int):
...


Expand Down

0 comments on commit 3670930

Please sign in to comment.