From 9edbfb00715d82b2d30804472f7a72deee5f227f Mon Sep 17 00:00:00 2001 From: Ghislain Piot Date: Fri, 29 Nov 2024 17:41:19 +0100 Subject: [PATCH 1/4] SONARPY-2346 Remove usage of old ProjectLevelSymbolTable#from using V1 symbols --- .../semantic/ProjectLevelSymbolTable.java | 24 +- .../python/api/PythonVisitorContextTest.java | 14 +- .../semantic/ProjectLevelSymbolTableTest.java | 216 ++++++++---------- .../semantic/v2/TypeInferenceV2Test.java | 10 +- 4 files changed, 117 insertions(+), 147 deletions(-) diff --git a/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java b/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java index e4af63e0c8..ebce8f48dd 100644 --- a/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java +++ b/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java @@ -61,25 +61,23 @@ public class ProjectLevelSymbolTable { private TypeShedDescriptorsProvider typeShedDescriptorsProvider = null; public static ProjectLevelSymbolTable empty() { - return new ProjectLevelSymbolTable(Collections.emptyMap()); + return new ProjectLevelSymbolTable(); } - public static ProjectLevelSymbolTable from(Map> globalSymbolsByModuleName) { - return new ProjectLevelSymbolTable(globalSymbolsByModuleName); - } + public static ProjectLevelSymbolTable from(Map> globalDescriptorsByModuleName) { + var projectLevelSymbolTable = new ProjectLevelSymbolTable(); - public ProjectLevelSymbolTable() { - this.globalDescriptorsByModuleName = new HashMap<>(); + for (var entry : globalDescriptorsByModuleName.entrySet()) { + var descriptors = entry.getValue(); + projectLevelSymbolTable.globalDescriptorsByModuleName.put(entry.getKey(), descriptors); + projectLevelSymbolTable.addModuleToGlobalSymbolsByFQN(descriptors); + } + + return projectLevelSymbolTable; } - private ProjectLevelSymbolTable(Map> globalSymbolsByModuleName) { + public ProjectLevelSymbolTable() { this.globalDescriptorsByModuleName = new HashMap<>(); - globalSymbolsByModuleName.entrySet().forEach(entry -> { - String moduleName = entry.getKey(); - Set symbols = entry.getValue(); - Set globalDescriptors = symbols.stream().map(DescriptorUtils::descriptor).collect(Collectors.toSet()); - globalDescriptorsByModuleName.put(moduleName, globalDescriptors); - }); } public void removeModule(String packageName, String fileName) { diff --git a/python-frontend/src/test/java/org/sonar/plugins/python/api/PythonVisitorContextTest.java b/python-frontend/src/test/java/org/sonar/plugins/python/api/PythonVisitorContextTest.java index 8ac1501585..b4c1872ad5 100644 --- a/python-frontend/src/test/java/org/sonar/plugins/python/api/PythonVisitorContextTest.java +++ b/python-frontend/src/test/java/org/sonar/plugins/python/api/PythonVisitorContextTest.java @@ -18,10 +18,7 @@ import com.sonar.sslr.api.RecognitionException; import java.io.File; -import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import org.junit.jupiter.api.Test; @@ -33,9 +30,10 @@ import org.sonar.plugins.python.api.tree.Tree; import org.sonar.python.PythonTestUtils; import org.sonar.python.caching.CacheContextImpl; +import org.sonar.python.index.Descriptor; +import org.sonar.python.index.VariableDescriptor; import org.sonar.python.parser.PythonParser; import org.sonar.python.semantic.ProjectLevelSymbolTable; -import org.sonar.python.semantic.SymbolImpl; import org.sonar.python.tree.FileInputImpl; import org.sonar.python.tree.PythonTreeMaker; @@ -82,9 +80,11 @@ void globalSymbols() { String code = "from mod import a, b"; FileInput fileInput = new PythonTreeMaker().fileInput(PythonParser.create().parse(code)); PythonFile pythonFile = pythonFile("my_module.py"); - List modSymbols = Arrays.asList(new SymbolImpl("a", null), new SymbolImpl("b", null)); - Map> globalSymbols = Collections.singletonMap("mod", new HashSet<>(modSymbols)); - new PythonVisitorContext(fileInput, pythonFile, null, "my_package", ProjectLevelSymbolTable.from(globalSymbols), null); + + Set descriptors = Set.of(new VariableDescriptor("a", "mod.a", null), new VariableDescriptor("b", "mod.b", null)); + Map> globalDescriptors = Collections.singletonMap("mod", descriptors); + + new PythonVisitorContext(fileInput, pythonFile, null, "my_package", ProjectLevelSymbolTable.from(globalDescriptors), CacheContextImpl.dummyCache()); assertThat(fileInput.globalVariables()).extracting(Symbol::name).containsExactlyInAnyOrder("a", "b"); } diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java b/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java index 6eccf6cb8f..591646c013 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java @@ -17,7 +17,6 @@ package org.sonar.python.semantic; import com.google.common.base.Functions; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -46,6 +45,7 @@ import org.sonar.python.index.ClassDescriptor; import org.sonar.python.index.Descriptor; import org.sonar.python.index.DescriptorUtils; +import org.sonar.python.index.FunctionDescriptor; import org.sonar.python.index.VariableDescriptor; import org.sonar.python.tree.TreeUtils; import org.sonar.python.types.DeclaredType; @@ -68,32 +68,27 @@ private Map getSymbolByName(FileInput fileInput) { @Test void wildcard_import() { - SymbolImpl exportedA = new SymbolImpl("a", "mod.a"); - SymbolImpl exportedB = new SymbolImpl("b", "mod.b"); - SymbolImpl exportedC = new ClassSymbolImpl("C", "mod.C"); - List modSymbols = Arrays.asList(exportedA, exportedB, exportedC); - Map> globalSymbols = Collections.singletonMap("mod", new HashSet<>(modSymbols)); + Map> globalDescriptors = Collections.singletonMap("mod", + Set.of( + new VariableDescriptor("a", "mod.a", null), + new VariableDescriptor("b", "mod.b", null), + new ClassDescriptor("C", "mod.C", Set.of(), Set.of(), false, null, false, false, null, false))); + FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "from mod import *", "print(a)" ); assertThat(tree.globalVariables()).extracting(Symbol::name).containsExactlyInAnyOrder("a", "b", "C"); Symbol a = getSymbolByName(tree).get("a"); - assertThat(exportedA.usages()).isEmpty(); - assertThat(a).isNotEqualTo(exportedA); assertThat(a.fullyQualifiedName()).isEqualTo("mod.a"); assertThat(a.usages()).extracting(Usage::kind).containsExactlyInAnyOrder(Usage.Kind.OTHER, Usage.Kind.IMPORT); Symbol b = getSymbolByName(tree).get("b"); - assertThat(exportedB.usages()).isEmpty(); - assertThat(b).isNotEqualTo(exportedB); assertThat(b.fullyQualifiedName()).isEqualTo("mod.b"); assertThat(b.usages()).extracting(Usage::kind).containsExactly(Usage.Kind.IMPORT); Symbol c = getSymbolByName(tree).get("C"); - assertThat(exportedC.usages()).isEmpty(); - assertThat(c).isNotEqualTo(exportedC); assertThat(c.fullyQualifiedName()).isEqualTo("mod.C"); assertThat(c.usages()).extracting(Usage::kind).containsExactly(Usage.Kind.IMPORT); } @@ -113,10 +108,12 @@ void unresolved_wildcard_import() { void function_symbol() { FunctionDef functionDef = (FunctionDef) parse("def fn(p1, p2): pass").statements().statements().get(0); FunctionSymbolImpl fnSymbol = new FunctionSymbolImpl(functionDef, "mod.fn", pythonFile("mod.py")); - List modSymbols = Collections.singletonList(fnSymbol); - Map> globalSymbols = Collections.singletonMap("mod", new HashSet<>(modSymbols)); + + var fnDescriptor = new FunctionDescriptor.FunctionDescriptorBuilder().withName("fn").withFullyQualifiedName("mod.fn").build(); + Map> globalDescriptors = Collections.singletonMap("mod", Set.of(fnDescriptor)); + FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "from mod import fn", "fn(1, 2)" ); @@ -128,7 +125,7 @@ void function_symbol() { assertThat(importedFnSymbol.usages()).extracting(Usage::kind).containsExactlyInAnyOrder(Usage.Kind.IMPORT, Usage.Kind.OTHER); tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "import mod", "mod.fn(1, 2)" ); @@ -140,7 +137,7 @@ void function_symbol() { assertThat(importedFnSymbol.usages()).extracting(Usage::kind).containsExactlyInAnyOrder(Usage.Kind.OTHER); tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "import mod as mod1", "mod1.fn(1, 2)" ); @@ -153,36 +150,27 @@ void function_symbol() { @Test void submodule_import() { - FunctionDef functionDef = (FunctionDef) parse("def fn(p1, p2): pass").statements().statements().get(0); - FunctionDef subModFunctionDef = (FunctionDef) parse("def fn2(p1, p2): pass").statements().statements().get(0); - FunctionSymbolImpl fnSymbol = new FunctionSymbolImpl(functionDef, "mod.fn", pythonFile("mod.py")); - FunctionSymbolImpl subModfnSymbol = new FunctionSymbolImpl(subModFunctionDef, "mod.submod.fn2", pythonFile("submod.py")); - List modSymbols = Collections.singletonList(fnSymbol); - List subModSymbols = Collections.singletonList(subModfnSymbol); - Map> globalSymbols = Map.of( - "mod", new HashSet<>(modSymbols), - "mod.submod", new HashSet<>(subModSymbols) - ); + var fnDescriptor = new FunctionDescriptor.FunctionDescriptorBuilder().withName("fn").withFullyQualifiedName("mod.fn").build(); + var subModFnDescriptor = new FunctionDescriptor.FunctionDescriptorBuilder().withName("fn2").withFullyQualifiedName("mod.submod.fn2").build(); + Map> globalDescriptors = Map.of( + "mod", Set.of(fnDescriptor), + "mod.submod", Set.of(subModFnDescriptor)); FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "import mod.submod", - "mod.submod.fn2(1, 2)" - ); + "mod.submod.fn2(1, 2)"); CallExpression callExpression = PythonTestUtils.getFirstChild(tree, t -> t.is(Tree.Kind.CALL_EXPR)); Symbol importedFnSymbol = callExpression.calleeSymbol(); assertThat(importedFnSymbol.is(Symbol.Kind.FUNCTION)).isTrue(); assertThat(importedFnSymbol.fullyQualifiedName()).isEqualTo("mod.submod.fn2"); - assertThat(importedFnSymbol).isNotEqualTo(subModfnSymbol); } @Test void import_already_existing_symbol() { - FunctionDef functionDef = (FunctionDef) parse("def fn(p1, p2): pass").statements().statements().get(0); - FunctionSymbolImpl fnSymbol = new FunctionSymbolImpl(functionDef, "mod.fn", pythonFile("mod.py")); - List modSymbols = Collections.singletonList(fnSymbol); - Map> globalSymbols = Collections.singletonMap("mod", new HashSet<>(modSymbols)); + var fnDescriptor = new FunctionDescriptor.FunctionDescriptorBuilder().withName("fn").withFullyQualifiedName("mod.fn").build(); + Map> globalDescriptors = Collections.singletonMap("mod", Set.of(fnDescriptor)); FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "fn = 42", "from mod import fn" ); @@ -190,10 +178,10 @@ void import_already_existing_symbol() { Symbol importedFnSymbol = tree.globalVariables().iterator().next(); assertThat(importedFnSymbol.kind()).isEqualTo(Symbol.Kind.OTHER); assertThat(importedFnSymbol.name()).isEqualTo("fn"); - assertThat(importedFnSymbol.fullyQualifiedName()).isEqualTo(null); + assertThat(importedFnSymbol.fullyQualifiedName()).isNull(); tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "mod = 42", "import mod" ); @@ -201,72 +189,63 @@ void import_already_existing_symbol() { Symbol modSymbol = tree.globalVariables().iterator().next(); assertThat(modSymbol.kind()).isEqualTo(Symbol.Kind.OTHER); assertThat(modSymbol.name()).isEqualTo("mod"); - assertThat(modSymbol.fullyQualifiedName()).isEqualTo(null); + assertThat(modSymbol.fullyQualifiedName()).isNull(); } @Test void other_imported_symbol() { - SymbolImpl xSymbol = new SymbolImpl("x", "mod.x"); - List modSymbols = Collections.singletonList(xSymbol); - Map> globalSymbols = Collections.singletonMap("mod", new HashSet<>(modSymbols)); + VariableDescriptor xDescriptor = new VariableDescriptor("x", "mod.x", null); + Set modDescriptors = Set.of(xDescriptor); + Map> globalDescriptors = Collections.singletonMap("mod", modDescriptors); FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), - "from mod import x" - ); + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), + "from mod import x"); Symbol importedXSymbol = tree.globalVariables().iterator().next(); assertThat(importedXSymbol.name()).isEqualTo("x"); assertThat(importedXSymbol.kind()).isEqualTo(Symbol.Kind.OTHER); assertThat(importedXSymbol.fullyQualifiedName()).isEqualTo("mod.x"); assertThat(importedXSymbol.usages()).hasSize(1); - assertThat(xSymbol).isNotEqualTo(importedXSymbol); - assertThat(xSymbol.usages()).isEmpty(); } @Test void aliased_imported_symbols() { - SymbolImpl xSymbol = new SymbolImpl("x", "mod.x"); - List modSymbols = Collections.singletonList(xSymbol); - Map> globalSymbols = Collections.singletonMap("mod", new HashSet<>(modSymbols)); + VariableDescriptor xDescriptor = new VariableDescriptor("x", "mod.x", null); + Set modDescriptors = Set.of(xDescriptor); + Map> globalDescriptors = Collections.singletonMap("mod", modDescriptors); FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), - "from mod import x as y" - ); + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), + "from mod import x as y"); Symbol importedYSymbol = tree.globalVariables().iterator().next(); assertThat(importedYSymbol.name()).isEqualTo("y"); tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), - "import mod as mod1" - ); + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), + "import mod as mod1"); Symbol importedModSymbol = tree.globalVariables().iterator().next(); assertThat(importedModSymbol.name()).isEqualTo("mod1"); } @Test void type_hierarchy() { - ClassSymbolImpl classASymbol = new ClassSymbolImpl("A", "mod1.A"); - classASymbol.addSuperClass(new SymbolImpl("B", "mod2.B")); - Set mod1Symbols = Collections.singleton(classASymbol); - ClassSymbolImpl classBSymbol = new ClassSymbolImpl("B", "mod2.B"); - Set mod2Symbols = Collections.singleton(classBSymbol); - Map> globalSymbols = new HashMap<>(); - globalSymbols.put("mod1", mod1Symbols); - globalSymbols.put("mod2", mod2Symbols); + var classADescriptor = new ClassDescriptor("A", "mod1.A", Collections.singleton("mod2.B"), Set.of(), false, null, false, false, null, false); + var classBDescriptor = new ClassDescriptor("B", "mod2.B", List.of(), Set.of(), false, null, false, false, null, false); + Map> globalDescriptors = new HashMap<>(); + globalDescriptors.put("mod1", Set.of(classADescriptor)); + globalDescriptors.put("mod2", Set.of(classBDescriptor)); FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), - "from mod1 import A" - ); + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), + "from mod1 import A"); Symbol importedASymbol = tree.globalVariables().iterator().next(); assertThat(importedASymbol.name()).isEqualTo("A"); assertThat(importedASymbol.fullyQualifiedName()).isEqualTo("mod1.A"); assertThat(importedASymbol.kind()).isEqualTo(Symbol.Kind.CLASS); ClassSymbol classA = (ClassSymbol) importedASymbol; - assertThat(classA.hasUnresolvedTypeHierarchy()).isEqualTo(false); + assertThat(classA.hasUnresolvedTypeHierarchy()).isFalse(); assertThat(classA.superClasses()).hasSize(1); assertThat(classA.superClasses().get(0).kind()).isEqualTo(Symbol.Kind.CLASS); tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "import mod1", "mod1.A" ); @@ -277,37 +256,34 @@ void type_hierarchy() { assertThat(importedASymbol.fullyQualifiedName()).isEqualTo("mod1.A"); assertThat(importedASymbol.kind()).isEqualTo(Symbol.Kind.CLASS); classA = (ClassSymbol) importedASymbol; - assertThat(classA.hasUnresolvedTypeHierarchy()).isEqualTo(false); + assertThat(classA.hasUnresolvedTypeHierarchy()).isFalse(); assertThat(classA.superClasses()).hasSize(1); assertThat(classA.superClasses().get(0).kind()).isEqualTo(Symbol.Kind.CLASS); } @Test void not_class_symbol_in_super_class() { - ClassSymbolImpl classASymbol = new ClassSymbolImpl("A", "mod1.A"); - classASymbol.addSuperClass(new SymbolImpl("foo", "mod1.foo")); + var classADescriptor = new ClassDescriptor("A", "mod1.A", Collections.singleton("mod1.foo"), Set.of(), false, null, false, false, null, false); + Map> globalDescriptors = Collections.singletonMap("mod1", Set.of(classADescriptor)); FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(Collections.singletonMap("mod1", Collections.singleton(classASymbol)))), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "from mod1 import A" ); Symbol importedASymbol = tree.globalVariables().iterator().next(); assertThat(importedASymbol.kind()).isEqualTo(Symbol.Kind.CLASS); ClassSymbol classA = (ClassSymbol) importedASymbol; - assertThat(classA.hasUnresolvedTypeHierarchy()).isEqualTo(true); + assertThat(classA.hasUnresolvedTypeHierarchy()).isTrue(); assertThat(classA.superClasses()).hasSize(1); assertThat(classA.superClasses().get(0).kind()).isEqualTo(Symbol.Kind.OTHER); } @Test void metaclass_in_imported_symbol() { - Set globalsMod = parse( - new SymbolTableBuilder("", pythonFile("mod1")), - "from abc import ABCMeta", - "class A(metaclass=ABCMeta): ..." - ).globalVariables(); + var aClassDescriptor = new ClassDescriptor("A", "mod1.A", Collections.singleton("abc.ABCMeta"), Set.of(), false, null, false, true, "abc.ABCMeta", false); + Map> globalDescriptors = Collections.singletonMap("mod1", Set.of(aClassDescriptor)); FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(Collections.singletonMap("mod1", globalsMod))), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "from mod1 import A" ); @@ -320,12 +296,12 @@ void metaclass_in_imported_symbol() { @Test void builtin_symbol_in_super_class() { - ClassSymbolImpl classASymbol = new ClassSymbolImpl("A", "mod1.A"); - classASymbol.addSuperClass(new SymbolImpl("BaseException", "BaseException")); + Map> globalDescriptors = Collections.singletonMap("mod1", + Collections.singleton( + new ClassDescriptor("A", "mod1.A", Collections.singleton("BaseException"), Set.of(), false, null, false, false, null, false))); FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(Collections.singletonMap("mod1", Collections.singleton(classASymbol)))), - "from mod1 import A" - ); + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), + "from mod1 import A"); Symbol importedASymbol = tree.globalVariables().iterator().next(); assertThat(importedASymbol.kind()).isEqualTo(Symbol.Kind.CLASS); @@ -337,36 +313,29 @@ void builtin_symbol_in_super_class() { @Test void multi_level_type_hierarchy() { - ClassSymbolImpl classASymbol = new ClassSymbolImpl("A", "mod1.A"); - classASymbol.addSuperClass(new SymbolImpl("B", "mod2.B")); - Set mod1Symbols = Collections.singleton(classASymbol); - - ClassSymbolImpl classBSymbol = new ClassSymbolImpl("B", "mod2.B"); - classBSymbol.addSuperClass(new SymbolImpl("C", "mod3.C")); - Set mod2Symbols = Collections.singleton(classBSymbol); + ClassDescriptor classADescriptor = new ClassDescriptor("A", "mod1.A", Collections.singleton("mod2.B"), Set.of(), false, null, false, false, null, false); + ClassDescriptor classBDescriptor = new ClassDescriptor("B", "mod2.B", Collections.singleton("mod3.C"), Set.of(), false, null, false, false, null, false); + ClassDescriptor classCDescriptor = new ClassDescriptor("C", "mod3.C", Set.of(), Set.of(), false, null, false, false, null, false); - ClassSymbolImpl classCSymbol = new ClassSymbolImpl("C", "mod3.C"); - Set mod3Symbols = Collections.singleton(classCSymbol); + Map> globalDescriptors = new HashMap<>(); + globalDescriptors.put("mod1", Set.of(classADescriptor)); + globalDescriptors.put("mod2", Set.of(classBDescriptor)); + globalDescriptors.put("mod3", Set.of(classCDescriptor)); - Map> globalSymbols = new HashMap<>(); - globalSymbols.put("mod1", mod1Symbols); - globalSymbols.put("mod2", mod2Symbols); - globalSymbols.put("mod3", mod3Symbols); FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), - "from mod1 import A" - ); + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), + "from mod1 import A"); Symbol importedASymbol = tree.globalVariables().iterator().next(); assertThat(importedASymbol.name()).isEqualTo("A"); assertThat(importedASymbol.fullyQualifiedName()).isEqualTo("mod1.A"); assertThat(importedASymbol.kind()).isEqualTo(Symbol.Kind.CLASS); ClassSymbol classA = (ClassSymbol) importedASymbol; - assertThat(classA.hasUnresolvedTypeHierarchy()).isEqualTo(false); + assertThat(classA.hasUnresolvedTypeHierarchy()).isFalse(); assertThat(classA.superClasses()).hasSize(1); assertThat(classA.superClasses().get(0).kind()).isEqualTo(Symbol.Kind.CLASS); tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "import mod1", "mod1.A" ); @@ -377,7 +346,7 @@ void multi_level_type_hierarchy() { assertThat(importedASymbol.fullyQualifiedName()).isEqualTo("mod1.A"); assertThat(importedASymbol.kind()).isEqualTo(Symbol.Kind.CLASS); classA = (ClassSymbol) importedASymbol; - assertThat(classA.hasUnresolvedTypeHierarchy()).isEqualTo(false); + assertThat(classA.hasUnresolvedTypeHierarchy()).isFalse(); assertThat(classA.superClasses()).hasSize(1); assertThat(classA.superClasses().get(0).kind()).isEqualTo(Symbol.Kind.CLASS); @@ -388,17 +357,22 @@ void multi_level_type_hierarchy() { @Test void ambiguous_imported_symbol() { - Set modSymbols = parse( - new SymbolTableBuilder("", pythonFile("mod")), - "@overload", - "def foo(a, b): ...", - "@overload", - "def foo(a, b, c): ..." - ).globalVariables(); - - Map> globalSymbols = Collections.singletonMap("mod", modSymbols); + var ambiguousDescriptor = AmbiguousDescriptor.create( + new FunctionDescriptor.FunctionDescriptorBuilder().withName("foo").withFullyQualifiedName("mod.foo").withParameters( + List.of( + new FunctionDescriptor.Parameter("a", null, false, false, false, false, false, null), + new FunctionDescriptor.Parameter("b", null, false, false, false, false, false, null))) + .build(), + new FunctionDescriptor.FunctionDescriptorBuilder().withName("foo").withFullyQualifiedName("mod.foo").withParameters( + List.of( + new FunctionDescriptor.Parameter("a", null, false, false, false, false, false, null), + new FunctionDescriptor.Parameter("b", null, false, false, false, false, false, null), + new FunctionDescriptor.Parameter("c", null, false, false, false, false, false, null))) + .build()); + Map> globalDescriptors = Collections.singletonMap("mod", Set.of(ambiguousDescriptor)); + FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "from mod import foo" ); Symbol importedFooSymbol = tree.globalVariables().iterator().next(); @@ -410,14 +384,12 @@ void ambiguous_imported_symbol() { @Test void imported_class_hasSuperClassWithoutSymbol() { - Set modSymbols = parse( - new SymbolTableBuilder("", pythonFile("mod")), - "def foo(): ...", - "class A(foo()): ..." - ).globalVariables(); - Map> globalSymbols = Collections.singletonMap("mod", modSymbols); + Map> globalDescriptors = Collections.singletonMap("mod", + Set.of(new FunctionDescriptor.FunctionDescriptorBuilder().withName("foo").withFullyQualifiedName("mod.foo").build(), + new ClassDescriptor.ClassDescriptorBuilder().withName("A").withFullyQualifiedName("mod.A").withSuperClasses(Set.of("foo")).build())); + FileInput tree = parse( - new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalSymbols)), + new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "from mod import A" ); Symbol importedFooSymbol = tree.globalVariables().iterator().next(); diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java index 1a0a8dc6c3..b04d30cfb1 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java @@ -17,8 +17,8 @@ package org.sonar.python.semantic.v2; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -55,6 +55,7 @@ import org.sonar.plugins.python.api.tree.StringLiteral; import org.sonar.plugins.python.api.tree.Tree; import org.sonar.python.PythonTestUtils; +import org.sonar.python.index.ClassDescriptor; import org.sonar.python.semantic.ClassSymbolImpl; import org.sonar.python.semantic.ProjectLevelSymbolTable; import org.sonar.python.semantic.SymbolUtils; @@ -400,11 +401,10 @@ void testRelativeImport() { @Test void testProjectLevelSymbolTableImports() { - var classSymbol = new ClassSymbolImpl("C", "something.known.C"); - ProjectLevelTypeTable projectLevelTypeTable = new ProjectLevelTypeTable(ProjectLevelSymbolTable.from( - Map.of("something", new HashSet<>(), "something.known", Set.of(classSymbol))) - ); + Map.of("something.known", Collections.singleton(new ClassDescriptor.ClassDescriptorBuilder().withName("C").withFullyQualifiedName("something.known.C").build()), "something", + Set.of()))); + var root = inferTypes(""" import something.known """, projectLevelTypeTable); From 1ae000b7216940d00d3d76225301318195f14254 Mon Sep 17 00:00:00 2001 From: Ghislain Piot Date: Fri, 29 Nov 2024 17:49:31 +0100 Subject: [PATCH 2/4] Cleanup DescriptorUtils --- .../sonar/python/index/DescriptorUtils.java | 75 ------------------- .../python/index/AmbiguousDescriptorTest.java | 8 +- .../semantic/ProjectLevelSymbolTableTest.java | 21 ------ 3 files changed, 2 insertions(+), 102 deletions(-) diff --git a/python-frontend/src/main/java/org/sonar/python/index/DescriptorUtils.java b/python-frontend/src/main/java/org/sonar/python/index/DescriptorUtils.java index 4b304fe71c..41159660f0 100644 --- a/python-frontend/src/main/java/org/sonar/python/index/DescriptorUtils.java +++ b/python-frontend/src/main/java/org/sonar/python/index/DescriptorUtils.java @@ -19,15 +19,10 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nullable; -import org.sonar.plugins.python.api.symbols.AmbiguousSymbol; -import org.sonar.plugins.python.api.symbols.ClassSymbol; -import org.sonar.plugins.python.api.symbols.FunctionSymbol; import org.sonar.plugins.python.api.symbols.Symbol; import org.sonar.plugins.python.api.types.InferredType; import org.sonar.python.semantic.AmbiguousSymbolImpl; @@ -45,76 +40,6 @@ public class DescriptorUtils { private DescriptorUtils() {} - public static Descriptor descriptor(Symbol symbol) { - switch (symbol.kind()) { - case FUNCTION: - return functionDescriptor(((FunctionSymbol) symbol)); - case CLASS: - return classDescriptor((ClassSymbol) symbol); - case AMBIGUOUS: - return ambiguousDescriptor((AmbiguousSymbol) symbol); - default: - return new VariableDescriptor(symbol.name(), symbol.fullyQualifiedName(), symbol.annotatedTypeName()); - } - } - - private static ClassDescriptor classDescriptor(ClassSymbol classSymbol) { - ClassDescriptor.ClassDescriptorBuilder classDescriptor = new ClassDescriptor.ClassDescriptorBuilder() - .withName(classSymbol.name()) - .withFullyQualifiedName(classSymbol.fullyQualifiedName()) - .withMembers(classSymbol.declaredMembers().stream().map(DescriptorUtils::descriptor).collect(Collectors.toSet())) - .withSuperClasses(classSymbol.superClasses().stream().map(Symbol::fullyQualifiedName).filter(Objects::nonNull).toList()) - .withDefinitionLocation(classSymbol.definitionLocation()) - .withHasMetaClass(((ClassSymbolImpl) classSymbol).hasMetaClass()) - .withHasSuperClassWithoutDescriptor(((ClassSymbolImpl) classSymbol).hasSuperClassWithoutSymbol() || - // Setting hasSuperClassWithoutDescriptor if a parent has a null FQN as it would be impossible to retrieve it without one, even if the parent exists. - classSymbol.superClasses().stream().anyMatch(s -> s.fullyQualifiedName() == null)) - .withMetaclassFQN(((ClassSymbolImpl) classSymbol).metaclassFQN()) - .withHasDecorators(classSymbol.hasDecorators()) - .withSupportsGenerics(((ClassSymbolImpl) classSymbol).supportsGenerics()); - - return classDescriptor.build(); - } - - private static FunctionDescriptor functionDescriptor(FunctionSymbol functionSymbol) { - return new FunctionDescriptor.FunctionDescriptorBuilder() - .withName(functionSymbol.name()) - .withFullyQualifiedName(functionSymbol.fullyQualifiedName()) - .withParameters(parameters(functionSymbol.parameters())) - .withHasDecorators(functionSymbol.hasDecorators()) - .withDecorators(functionSymbol.decorators()) - .withIsAsynchronous(functionSymbol.isAsynchronous()) - .withIsInstanceMethod(functionSymbol.isInstanceMethod()) - .withAnnotatedReturnTypeName(functionSymbol.annotatedReturnTypeName()) - .withDefinitionLocation(functionSymbol.definitionLocation()) - .build(); - } - - private static AmbiguousDescriptor ambiguousDescriptor(AmbiguousSymbol ambiguousSymbol) { - return ambiguousDescriptor(ambiguousSymbol, null); - } - - public static AmbiguousDescriptor ambiguousDescriptor(AmbiguousSymbol ambiguousSymbol, @Nullable String overriddenFQN) { - String fullyQualifiedName = overriddenFQN != null ? overriddenFQN : ambiguousSymbol.fullyQualifiedName(); - Set alternatives = ambiguousSymbol.alternatives().stream() - .map(DescriptorUtils::descriptor) - .collect(Collectors.toSet()); - return new AmbiguousDescriptor(ambiguousSymbol.name(), fullyQualifiedName, alternatives); - } - - private static List parameters(List parameters) { - return parameters.stream().map(parameter -> new FunctionDescriptor.Parameter( - parameter.name(), - ((FunctionSymbolImpl.ParameterImpl) parameter).annotatedTypeName(), - parameter.hasDefaultValue(), - parameter.isKeywordOnly(), - parameter.isPositionalOnly(), - parameter.isPositionalVariadic(), - parameter.isKeywordVariadic(), - parameter.location() - )).toList(); - } - // TODO SONARPY-958: Cleanup the symbol construction from descriptors by extracting this logic in a builder class public static Symbol symbolFromDescriptor(Descriptor descriptor, ProjectLevelSymbolTable projectLevelSymbolTable, @Nullable String localSymbolName, Map createdSymbolsByDescriptor, Map createdSymbolsByFqn) { diff --git a/python-frontend/src/test/java/org/sonar/python/index/AmbiguousDescriptorTest.java b/python-frontend/src/test/java/org/sonar/python/index/AmbiguousDescriptorTest.java index 983bb34521..28fe085609 100644 --- a/python-frontend/src/test/java/org/sonar/python/index/AmbiguousDescriptorTest.java +++ b/python-frontend/src/test/java/org/sonar/python/index/AmbiguousDescriptorTest.java @@ -20,7 +20,6 @@ import java.util.Set; import org.junit.jupiter.api.Test; import org.sonar.plugins.python.api.tree.Name; -import org.sonar.python.semantic.SymbolImpl; import org.sonar.python.semantic.v2.SymbolV2; import org.sonar.python.semantic.v2.converter.PythonTypeToDescriptorConverter; import org.sonar.python.types.v2.PythonType; @@ -30,7 +29,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.sonar.python.index.ClassDescriptorTest.lastClassDescriptor; import static org.sonar.python.index.DescriptorToProtobufTestUtils.assertDescriptorToProtobuf; -import static org.sonar.python.index.DescriptorUtils.descriptor; import static org.sonar.python.index.FunctionDescriptorTest.lastFunctionDescriptor; import static org.sonar.python.types.v2.TypesTestUtils.lastName; @@ -110,10 +108,8 @@ void test_different_names_illegal_argument() { @Test void ambiguous_descriptor_creation_different_name_same_fqn() { - SymbolImpl foo = new SymbolImpl("foo", "mod.bar"); - SymbolImpl bar = new SymbolImpl("bar", "mod.bar"); - Descriptor fooDesc = descriptor(foo); - Descriptor barDesc = descriptor(bar); + Descriptor fooDesc = new VariableDescriptor("foo", "mod.bar", null, false); + Descriptor barDesc = new VariableDescriptor("bar", "mod.bar", null, false); assertThatThrownBy(() -> AmbiguousDescriptor.create(fooDesc, barDesc)).isInstanceOf(IllegalArgumentException.class); } diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java b/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java index 591646c013..94984c1d9e 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java @@ -44,7 +44,6 @@ import org.sonar.python.index.AmbiguousDescriptor; import org.sonar.python.index.ClassDescriptor; import org.sonar.python.index.Descriptor; -import org.sonar.python.index.DescriptorUtils; import org.sonar.python.index.FunctionDescriptor; import org.sonar.python.index.VariableDescriptor; import org.sonar.python.tree.TreeUtils; @@ -1080,26 +1079,6 @@ void function_decorator_symbol() { assertThat(functionDef.localVariables()).isEmpty(); } - @Test - void descriptorsForModule() { - FileInput tree = PythonTestUtils.parseWithoutSymbols( - "class A: ...", - "class B(A): ...", - "def foo(): ...", - "x :int = 42", - "def bar(): ...", - "bar = 24" - ); - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); - projectLevelSymbolTable.addModule(tree, "", PythonTestUtils.pythonFile("mod.py")); - Set symbols = projectLevelSymbolTable.getSymbolsFromModule("mod"); - Set retrievedDescriptors = projectLevelSymbolTable.descriptorsForModule("mod"); - Set recomputedDescriptors = new HashSet<>(); - assertThat(symbols).isNotNull(); - symbols.forEach(s -> recomputedDescriptors.add(DescriptorUtils.descriptor(s))); - assertThat(recomputedDescriptors).usingRecursiveFieldByFieldElementComparator().containsExactlyInAnyOrderElementsOf(retrievedDescriptors); - } - @Test void superclasses_without_descriptor() { var code = """ From cdc0ae33927dc06e1017e97ed5fb532dc0cd6bb8 Mon Sep 17 00:00:00 2001 From: Ghislain Piot Date: Mon, 2 Dec 2024 16:02:27 +0100 Subject: [PATCH 3/4] Address review comments --- .../python/semantic/ProjectLevelSymbolTable.java | 1 - .../semantic/ProjectLevelSymbolTableTest.java | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java b/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java index ebce8f48dd..93c87498cf 100644 --- a/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java +++ b/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java @@ -70,7 +70,6 @@ public static ProjectLevelSymbolTable from(Map> globalDe for (var entry : globalDescriptorsByModuleName.entrySet()) { var descriptors = entry.getValue(); projectLevelSymbolTable.globalDescriptorsByModuleName.put(entry.getKey(), descriptors); - projectLevelSymbolTable.addModuleToGlobalSymbolsByFQN(descriptors); } return projectLevelSymbolTable; diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java b/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java index 94984c1d9e..1ed207bdfe 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java @@ -71,7 +71,7 @@ void wildcard_import() { Set.of( new VariableDescriptor("a", "mod.a", null), new VariableDescriptor("b", "mod.b", null), - new ClassDescriptor("C", "mod.C", Set.of(), Set.of(), false, null, false, false, null, false))); + new ClassDescriptor.ClassDescriptorBuilder().withName("C").withFullyQualifiedName("mod.C").build())); FileInput tree = parse( new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), @@ -226,8 +226,8 @@ void aliased_imported_symbols() { @Test void type_hierarchy() { - var classADescriptor = new ClassDescriptor("A", "mod1.A", Collections.singleton("mod2.B"), Set.of(), false, null, false, false, null, false); - var classBDescriptor = new ClassDescriptor("B", "mod2.B", List.of(), Set.of(), false, null, false, false, null, false); + var classADescriptor = new ClassDescriptor.ClassDescriptorBuilder().withName("A").withFullyQualifiedName("mod1.A").withSuperClasses(Set.of("mod2.B")).build(); + var classBDescriptor = new ClassDescriptor.ClassDescriptorBuilder().withName("B").withFullyQualifiedName("mod2.B").build(); Map> globalDescriptors = new HashMap<>(); globalDescriptors.put("mod1", Set.of(classADescriptor)); globalDescriptors.put("mod2", Set.of(classBDescriptor)); @@ -297,7 +297,7 @@ void metaclass_in_imported_symbol() { void builtin_symbol_in_super_class() { Map> globalDescriptors = Collections.singletonMap("mod1", Collections.singleton( - new ClassDescriptor("A", "mod1.A", Collections.singleton("BaseException"), Set.of(), false, null, false, false, null, false))); + new ClassDescriptor.ClassDescriptorBuilder().withName("A").withFullyQualifiedName("mod1.A").withSuperClasses(Set.of("BaseException")).build())); FileInput tree = parse( new SymbolTableBuilder("my_package", pythonFile("my_module.py"), from(globalDescriptors)), "from mod1 import A"); @@ -312,9 +312,9 @@ void builtin_symbol_in_super_class() { @Test void multi_level_type_hierarchy() { - ClassDescriptor classADescriptor = new ClassDescriptor("A", "mod1.A", Collections.singleton("mod2.B"), Set.of(), false, null, false, false, null, false); - ClassDescriptor classBDescriptor = new ClassDescriptor("B", "mod2.B", Collections.singleton("mod3.C"), Set.of(), false, null, false, false, null, false); - ClassDescriptor classCDescriptor = new ClassDescriptor("C", "mod3.C", Set.of(), Set.of(), false, null, false, false, null, false); + var classADescriptor = new ClassDescriptor.ClassDescriptorBuilder().withName("A").withFullyQualifiedName("mod1.A").withSuperClasses(Set.of("mod2.B")).build(); + var classBDescriptor = new ClassDescriptor.ClassDescriptorBuilder().withName("B").withFullyQualifiedName("mod2.B").withSuperClasses(Set.of("mod3.C")).build(); + var classCDescriptor = new ClassDescriptor.ClassDescriptorBuilder().withName("C").withFullyQualifiedName("mod3.C").build(); Map> globalDescriptors = new HashMap<>(); globalDescriptors.put("mod1", Set.of(classADescriptor)); From ad679062d90f074358481a4aa6ae16bcb88d5fe5 Mon Sep 17 00:00:00 2001 From: Ghislain Piot Date: Mon, 2 Dec 2024 16:14:13 +0100 Subject: [PATCH 4/4] Remove public constructor --- .../sonar/python/TestPythonVisitorRunner.java | 2 +- .../semantic/ProjectLevelSymbolTable.java | 4 +- .../semantic/ProjectLevelSymbolTableTest.java | 46 +++++++++---------- .../v2/ProjectLevelTypeTableTest.java | 18 ++++---- .../semantic/v2/TypeInferenceV2Test.java | 10 ++-- .../python/types/v2/TypeCheckerTest.java | 2 +- .../plugins/python/indexer/PythonIndexer.java | 2 +- 7 files changed, 42 insertions(+), 42 deletions(-) diff --git a/python-frontend/src/main/java/org/sonar/python/TestPythonVisitorRunner.java b/python-frontend/src/main/java/org/sonar/python/TestPythonVisitorRunner.java index 2b3f867625..69dd80aa02 100644 --- a/python-frontend/src/main/java/org/sonar/python/TestPythonVisitorRunner.java +++ b/python-frontend/src/main/java/org/sonar/python/TestPythonVisitorRunner.java @@ -81,7 +81,7 @@ public static PythonVisitorContext createNotebookContext(File file, Map files, File baseDir) { - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); for (File file : files) { var pythonFile = new TestPythonFile(file); if (pythonFile.isIPython()) { diff --git a/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java b/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java index 93c87498cf..127c951ad1 100644 --- a/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java +++ b/python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java @@ -65,7 +65,7 @@ public static ProjectLevelSymbolTable empty() { } public static ProjectLevelSymbolTable from(Map> globalDescriptorsByModuleName) { - var projectLevelSymbolTable = new ProjectLevelSymbolTable(); + var projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); for (var entry : globalDescriptorsByModuleName.entrySet()) { var descriptors = entry.getValue(); @@ -75,7 +75,7 @@ public static ProjectLevelSymbolTable from(Map> globalDe return projectLevelSymbolTable; } - public ProjectLevelSymbolTable() { + private ProjectLevelSymbolTable() { this.globalDescriptorsByModuleName = new HashMap<>(); } diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java b/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java index 1ed207bdfe..a388672625 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java @@ -400,7 +400,7 @@ void imported_class_hasSuperClassWithoutSymbol() { } private static Set globalSymbols(FileInput fileInput, String packageName) { - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = empty(); projectLevelSymbolTable.addModule(fileInput, packageName, pythonFile("mod.py")); return projectLevelSymbolTable.getSymbolsFromModule(packageName.isEmpty() ? "mod" : packageName + ".mod"); } @@ -410,7 +410,7 @@ void test_remove_module() { FileInput tree = parseWithoutSymbols( "class A: pass" ); - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = empty(); projectLevelSymbolTable.addModule(tree, "", pythonFile("mod.py")); assertThat(projectLevelSymbolTable.getSymbolsFromModule("mod")).extracting(Symbol::name).containsExactlyInAnyOrder("A"); projectLevelSymbolTable.removeModule("", "mod.py"); @@ -419,7 +419,7 @@ void test_remove_module() { @Test void test_insert_entry() { - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = empty(); VariableDescriptor variableDescriptor = new VariableDescriptor("x", "mod.x", null); projectLevelSymbolTable.insertEntry("mod", Set.of(variableDescriptor)); assertThat(projectLevelSymbolTable.descriptorsForModule("mod")).containsExactly(variableDescriptor); @@ -431,7 +431,7 @@ void test_add_module_after_creation() { FileInput tree = parseWithoutSymbols( "class A: pass" ); - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = empty(); projectLevelSymbolTable.addModule(tree, "", pythonFile("mod.py")); assertThat(projectLevelSymbolTable.getSymbolsFromModule("mod")).extracting(Symbol::name).containsExactlyInAnyOrder("A"); assertThat(projectLevelSymbolTable.getSymbolsFromModule("mod2")).isNull(); @@ -452,7 +452,7 @@ void test_imported_modules() { FileInput tree = parseWithoutSymbols( "import A" ); - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = empty(); projectLevelSymbolTable.addModule(tree, "", pythonFile("mod.py")); assertThat(projectLevelSymbolTable.importsByModule()).containsExactly(Map.entry("mod", Set.of("A"))); assertThat(projectLevelSymbolTable.getSymbolsFromModule("mod2")).isNull(); @@ -651,7 +651,7 @@ void class_symbol_inheritance_from_nested_class() { @Test void child_class_method_call_is_not_a_member_of_parent_class() { - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = empty(); FileInput importedFileInput = parseWithoutSymbols( "class A:", " def meth(self): ", @@ -797,7 +797,7 @@ void loop_in_class_inheritance() { "class B(A): ..." }; - ProjectLevelSymbolTable projectSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(foo), "", pythonFile("foo.py")); projectSymbolTable.addModule(parseWithoutSymbols(bar), "", pythonFile("bar.py")); @@ -838,7 +838,7 @@ void symbols_from_module_should_be_the_same() { "class A: ...", "class B(A): ..." ); - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = empty(); projectLevelSymbolTable.addModule(tree, "", pythonFile("mod.py")); Set mod = projectLevelSymbolTable.getSymbolsFromModule("mod"); assertThat(mod).extracting(Symbol::name).containsExactlyInAnyOrder("A", "B"); @@ -890,7 +890,7 @@ void no_stackoverflow_for_ambiguous_descriptor() { String[] bar = { "from foo import *\n", }; - ProjectLevelSymbolTable projectSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(foo), "", pythonFile("foo.py")); projectSymbolTable.addModule(parseWithoutSymbols(bar), "", pythonFile("bar.py")); @@ -914,7 +914,7 @@ void no_stackoverflow_for_ambiguous_descriptor() { @Test void ambiguous_descriptor_alternatives_dont_rely_on_FQN_for_conversion() { - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = empty(); Set descriptors = new HashSet<>(); VariableDescriptor variableDescriptor = new VariableDescriptor("Ambiguous", "foo.Ambiguous", null); ClassDescriptor classDescriptor = new ClassDescriptor("Ambiguous", "foo.Ambiguous", List.of(), Set.of(), false, null, false, false, null, false); @@ -946,7 +946,7 @@ void loop_in_inheritance_with_method_paraneters_of_same_type() { " def my_B_other_method(param: B): ..." }; - ProjectLevelSymbolTable projectSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(foo), "", pythonFile("foo.py")); projectSymbolTable.addModule(parseWithoutSymbols(bar), "", pythonFile("bar.py")); @@ -979,7 +979,7 @@ void django_views() { "def bar(): ..." }; - ProjectLevelSymbolTable projectSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(urls), "", pythonFile("urls.py")); assertThat(projectSymbolTable.isDjangoView("views.foo")).isTrue(); @@ -1011,7 +1011,7 @@ def qix(): ... urlpatterns.append(path('bar', MyOtherClass.MyNestedClass.qix, name='bar')) """; - ProjectLevelSymbolTable projectSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(content), "my_package", pythonFile("urls.py")); assertThat(projectSymbolTable.isDjangoView("my_package.urls.foo")).isTrue(); assertThat(projectSymbolTable.isDjangoView("my_package.urls.MyClass.bar")).isTrue(); @@ -1028,7 +1028,7 @@ def ambiguous(): ... def ambiguous(): ... urlpatterns = [path('bar', ambiguous, name='bar')] """; - ProjectLevelSymbolTable projectSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(content), "my_package", pythonFile("urls.py")); assertThat(projectSymbolTable.isDjangoView("my_package.urls.ambiguous")).isFalse(); } @@ -1040,7 +1040,7 @@ void django_views_conf_import() { import views urlpatterns = [conf.path('foo', views.foo, name='foo'), conf.path('baz')] """; - ProjectLevelSymbolTable projectSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(content), "my_package", pythonFile("urls.py")); assertThat(projectSymbolTable.isDjangoView("views.foo")).isTrue(); } @@ -1057,7 +1057,7 @@ def view_method(self): def get_urlpatterns(self): return [path("something", self.view_method, name="something")] """; - ProjectLevelSymbolTable projectSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(content), "my_package", pythonFile("mod.py")); // SONARPY-2322: should be true assertThat(projectSymbolTable.isDjangoView("my_package.mod.ClassWithViews.view_method")).isFalse(); @@ -1086,7 +1086,7 @@ class MetaField: ... class Field(MetaField()): ... """; - var projectSymbolTable = new ProjectLevelSymbolTable(); + var projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(code), "", pythonFile("mod.py")); var descriptors = projectSymbolTable.getDescriptorsFromModule("mod"); @@ -1111,7 +1111,7 @@ void superclasses_without_descriptor_unresolved_import() { class Field(MetaField): ... """; - var projectSymbolTable = new ProjectLevelSymbolTable(); + var projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(code), "", pythonFile("mod.py")); var symbol = (ClassSymbol) projectSymbolTable.getSymbol("mod.Field"); assertThat(symbol.hasUnresolvedTypeHierarchy()).isTrue(); @@ -1124,7 +1124,7 @@ void class_wth_imported_metaclass() { class WithMetaclass(metaclass=ABCMeta): ... """; - var projectSymbolTable = new ProjectLevelSymbolTable(); + var projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(code), "", pythonFile("mod.py")); var symbol = (ClassSymbolImpl) projectSymbolTable.getSymbol("mod.WithMetaclass"); assertThat(symbol.metaclassFQN()).isEqualTo("abc.ABCMeta"); @@ -1137,7 +1137,7 @@ class LocalMetaClass: ... class WithMetaclass(metaclass=LocalMetaClass): ... """; - var projectSymbolTable = new ProjectLevelSymbolTable(); + var projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(code), "", pythonFile("mod.py")); var symbol = (ClassSymbolImpl) projectSymbolTable.getSymbol("mod.WithMetaclass"); assertThat(symbol.metaclassFQN()).isEqualTo("mod.LocalMetaClass"); @@ -1150,7 +1150,7 @@ void class_wth_unresolved_import_metaclass() { class WithMetaclass(metaclass=UnresolvedMetaClass): ... """; - var projectSymbolTable = new ProjectLevelSymbolTable(); + var projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(code), "", pythonFile("mod.py")); var symbol = (ClassSymbolImpl) projectSymbolTable.getSymbol("mod.WithMetaclass"); assertThat(symbol.metaclassFQN()).isEqualTo("unknown.UnresolvedMetaClass"); @@ -1163,7 +1163,7 @@ def foo(): ... class WithMetaclass(metaclass=foo()): ... """; - var projectSymbolTable = new ProjectLevelSymbolTable(); + var projectSymbolTable = empty(); projectSymbolTable.addModule(parseWithoutSymbols(code), "", pythonFile("mod.py")); var symbol = (ClassSymbolImpl) projectSymbolTable.getSymbol("mod.WithMetaclass"); assertThat(symbol.hasMetaClass()).isTrue(); @@ -1172,7 +1172,7 @@ class WithMetaclass(metaclass=foo()): ... @Test void projectPackages() { - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = empty(); projectLevelSymbolTable.addProjectPackage("first.package"); projectLevelSymbolTable.addProjectPackage("second.package"); projectLevelSymbolTable.addProjectPackage("third"); diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/v2/ProjectLevelTypeTableTest.java b/python-frontend/src/test/java/org/sonar/python/semantic/v2/ProjectLevelTypeTableTest.java index 5380febae0..5686bf1f01 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/v2/ProjectLevelTypeTableTest.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/v2/ProjectLevelTypeTableTest.java @@ -139,7 +139,7 @@ void nameConflictBetweenSubmoduleAndInitNameFromStubs2() { @Test void importingSubmodulesTest() { - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); FileInput libTree = parseWithoutSymbols( """ @@ -180,7 +180,7 @@ class A: ... @Test void importingRedefinedSubmodulesTest() { - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); FileInput libTree = parseWithoutSymbols( """ @@ -224,7 +224,7 @@ class A: ... @Test void importingRedefinedSubmodules2Test() { - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); FileInput libTree = parseWithoutSymbols( """ @@ -264,7 +264,7 @@ void resolveStubsWithImportedModuleVariableDescriptor() { @Test void importFunctionWithDecorators() { - var projectLevelSymbolTable = new ProjectLevelSymbolTable(); + var projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); var libTree = parseWithoutSymbols( """ def lib_decorator(): ... @@ -290,7 +290,7 @@ def foo(): ... @Test void importFunctionWithImportedDecorators() { - var projectLevelSymbolTable = new ProjectLevelSymbolTable(); + var projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); var libTree = parseWithoutSymbols( """ def lib_decorator(): ... @@ -321,7 +321,7 @@ def foo(): ... @Test void importedFunctionDecoratorNamesTest() { - var projectLevelSymbolTable = new ProjectLevelSymbolTable(); + var projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); var libTree = parseWithoutSymbols( """ from abc import abstractmethod @@ -353,7 +353,7 @@ def foo(self): ... @Test void relativeImports() { - var projectLevelSymbolTable = new ProjectLevelSymbolTable(); + var projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); FileInput initTree = parseWithoutSymbols(""); PythonFile initFile = pythonFile("__init__.py"); projectLevelSymbolTable.addModule(initTree, "my_package", initFile); @@ -384,7 +384,7 @@ def foo(): ... @Test void importFunctionWithUnresolvedImportParameterTypes() { - var projectLevelSymbolTable = new ProjectLevelSymbolTable(); + var projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); var libTree = parseWithoutSymbols( """ def imported_function(params: list[str]): ... @@ -406,7 +406,7 @@ def imported_function(params: list[str]): ... @Test void classTypeAndAlias() { - var projectLevelSymbolTable = new ProjectLevelSymbolTable(); + var projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); var libTree = parseWithoutSymbols( """ class MyClass(): diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java index b04d30cfb1..4d6deaabb0 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java @@ -699,7 +699,7 @@ class A: b : int """ ); - var projectLevelSymbolTable = new ProjectLevelSymbolTable(); + var projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); projectLevelSymbolTable.addModule(tree, "", pythonFile("mod.py")); var symbol = (ClassSymbol) projectLevelSymbolTable.getSymbol("mod.A"); @@ -1268,7 +1268,7 @@ void inferFunctionParameterTypesMultiFile() { "class A: ...", "def foo2(p1: dict, p2: A): ..." ); - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); var modFile = pythonFile("mod.py"); projectLevelSymbolTable.addModule(tree, "", modFile); ProjectLevelTypeTable projectLevelTypeTable = new ProjectLevelTypeTable(projectLevelSymbolTable); @@ -2722,7 +2722,7 @@ void type_origin_of_project_function() { FileInput tree = parseWithoutSymbols( "def foo() -> int: ..." ); - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); projectLevelSymbolTable.addModule(tree, "", pythonFile("mod.py")); ProjectLevelTypeTable projectLevelTypeTable = new ProjectLevelTypeTable(projectLevelSymbolTable); @@ -3378,7 +3378,7 @@ void wildCardImportsMultiFile() { def foo(): pass def bar(): pass """); - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); var modFile = pythonFile("mod.py"); projectLevelSymbolTable.addModule(tree, "", modFile); ProjectLevelTypeTable projectLevelTypeTable = new ProjectLevelTypeTable(projectLevelSymbolTable); @@ -3549,7 +3549,7 @@ private static Statement lastStatement(StatementList statementList) { } private static class TestProject { - private final ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + private final ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); public TestProject addModule(String moduleName, String code) { FileInput tree = parseWithoutSymbols(code); diff --git a/python-frontend/src/test/java/org/sonar/python/types/v2/TypeCheckerTest.java b/python-frontend/src/test/java/org/sonar/python/types/v2/TypeCheckerTest.java index 53964ef7b6..1bb2d92092 100644 --- a/python-frontend/src/test/java/org/sonar/python/types/v2/TypeCheckerTest.java +++ b/python-frontend/src/test/java/org/sonar/python/types/v2/TypeCheckerTest.java @@ -272,7 +272,7 @@ void isTypeWithNameStubNamesTest() { @Test void isTypeWithNameProjectNamesTest() { - ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); FileInput tree = parseWithoutSymbols( """ diff --git a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/indexer/PythonIndexer.java b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/indexer/PythonIndexer.java index 33c70f4251..a626a2cc08 100644 --- a/sonar-python-plugin/src/main/java/org/sonar/plugins/python/indexer/PythonIndexer.java +++ b/sonar-python-plugin/src/main/java/org/sonar/plugins/python/indexer/PythonIndexer.java @@ -49,7 +49,7 @@ public abstract class PythonIndexer { private final Map packageNames = new HashMap<>(); private final PythonParser parser = PythonParser.create(); - private final ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable(); + private final ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty(); public ProjectLevelSymbolTable projectLevelSymbolTable() { return projectLevelSymbolTable;