Skip to content

Commit da74ffc

Browse files
maksim-grebeniuk-sonarsourcesonartech
authored andcommitted
SONARPY-2846 Share the same ProjectLevelTypeTable instance across files analysis (#360)
GitOrigin-RevId: 673c28b6ac4ed5db19430d57872a5a02fec1b594
1 parent ed45cb1 commit da74ffc

File tree

5 files changed

+34
-11
lines changed

5 files changed

+34
-11
lines changed

python-commons/src/main/java/org/sonar/plugins/python/PythonScanner.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ private PythonVisitorContext createVisitorContext(PythonInputFile inputFile, Pyt
155155
.workingDirectory(getWorkingDirectory(context))
156156
.packageName(indexer.packageName(inputFile))
157157
.projectLevelSymbolTable(indexer.projectLevelSymbolTable())
158+
.typeTable(indexer.projectLevelTypeTable())
158159
.cacheContext(indexer.cacheContext())
159160
.sonarProduct(context.runtime().getProduct())
160161
.build();

python-commons/src/main/java/org/sonar/plugins/python/indexer/PythonIndexer.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
import org.sonar.python.project.config.SignatureBasedAwsLambdaHandlersCollector;
4242
import org.sonar.python.parser.PythonParser;
4343
import org.sonar.python.semantic.ProjectLevelSymbolTable;
44+
import org.sonar.python.semantic.v2.ProjectLevelTypeTable;
45+
import org.sonar.python.semantic.v2.TypeTable;
4446
import org.sonar.python.tree.PythonTreeMaker;
4547

4648
import static org.sonar.python.semantic.SymbolUtils.pythonPackageName;
@@ -53,7 +55,10 @@ public abstract class PythonIndexer {
5355

5456
private final Map<URI, String> packageNames = new ConcurrentHashMap<>();
5557
private final Supplier<PythonParser> parserSupplier = PythonParser::create;
58+
5659
private final ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty();
60+
private final ProjectLevelTypeTable projectLevelTypeTable = new ProjectLevelTypeTable(projectLevelSymbolTable);
61+
5762
private final SignatureBasedAwsLambdaHandlersCollector signatureBasedAwsLambdaHandlersCollector = new SignatureBasedAwsLambdaHandlersCollector();
5863
private final ProjectConfigurationBuilder projectConfigurationBuilder;
5964

@@ -69,6 +74,10 @@ public ProjectConfiguration projectConfig() {
6974
return projectConfigurationBuilder.build();
7075
}
7176

77+
public TypeTable projectLevelTypeTable() {
78+
return projectLevelTypeTable;
79+
}
80+
7281
public String packageName(PythonInputFile inputFile) {
7382
if (!packageNames.containsKey(inputFile.wrappedFile().uri())) {
7483
String name = pythonPackageName(inputFile.wrappedFile().file(), projectBaseDirAbsolutePath);

python-frontend/src/main/java/org/sonar/plugins/python/api/PythonVisitorContext.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.sonar.python.semantic.v2.SymbolTableBuilderV2;
3838
import org.sonar.python.semantic.v2.TypeInferenceV2;
3939
import org.sonar.python.semantic.v2.callgraph.CallGraph;
40+
import org.sonar.python.semantic.v2.TypeTable;
4041
import org.sonar.python.types.v2.TypeChecker;
4142

4243
public class PythonVisitorContext extends PythonInputFileContext {
@@ -71,7 +72,6 @@ private PythonVisitorContext(FileInput rootTree,
7172
}
7273

7374

74-
7575
public PythonVisitorContext(PythonFile pythonFile, RecognitionException parsingException, SonarProduct sonarProduct) {
7676
super(pythonFile, null, CacheContextImpl.dummyCache(), sonarProduct, ProjectLevelSymbolTable.empty());
7777
this.rootTree = null;
@@ -122,13 +122,13 @@ public static class Builder {
122122
private final FileInput rootTree;
123123

124124
private Optional<ProjectLevelSymbolTable> projectLevelSymbolTable = Optional.empty();
125+
private Optional<TypeTable> typeTable = Optional.empty();
125126
private Optional<CacheContext> cacheContext = Optional.empty();
126127
private Optional<SonarProduct> sonarProduct = Optional.empty();
127128
private Optional<File> workingDirectory = Optional.empty();
128129
private Optional<ProjectConfiguration> projectConfiguration = Optional.empty();
129130
private Optional<CallGraph> callGraph = Optional.empty();
130131
private Optional<String> packageName = Optional.empty();
131-
private Optional<ProjectLevelTypeTable> projectLevelTypeTable = Optional.empty();
132132
private Optional<ModuleType> moduleType = Optional.empty();
133133

134134
public Builder(FileInput rootTree, PythonFile pythonFile) {
@@ -150,6 +150,11 @@ public Builder projectLevelSymbolTable(ProjectLevelSymbolTable projectLevelSymbo
150150
this.projectLevelSymbolTable = Optional.of(projectLevelSymbolTable);
151151
return this;
152152
}
153+
154+
public Builder typeTable(TypeTable typeTable) {
155+
this.typeTable = Optional.ofNullable(typeTable);
156+
return this;
157+
}
153158

154159
public Builder cacheContext(CacheContext cacheContext) {
155160
this.cacheContext = Optional.of(cacheContext);
@@ -166,11 +171,6 @@ public Builder projectConfiguration(ProjectConfiguration projectConfiguration) {
166171
return this;
167172
}
168173

169-
public Builder projectLevelTypeTable(ProjectLevelTypeTable projectLevelTypeTable) {
170-
this.projectLevelTypeTable = Optional.of(projectLevelTypeTable);
171-
return this;
172-
}
173-
174174
public Builder moduleType(ModuleType moduleType) {
175175
this.moduleType = Optional.of(moduleType);
176176
return this;
@@ -185,11 +185,11 @@ public PythonVisitorContext build() {
185185
var symbolTable = projectLevelSymbolTable.orElseGet(ProjectLevelSymbolTable::empty);
186186
var pkgName = packageName.orElse("");
187187
buildSymbols(rootTree, pythonFile, pkgName, symbolTable);
188-
var typeTable = this.projectLevelTypeTable.orElseGet(() -> new ProjectLevelTypeTable(symbolTable));
188+
var finalTypeTable = this.typeTable.orElseGet(() -> new ProjectLevelTypeTable(symbolTable));
189189
var mt = moduleType.orElseGet(() -> {
190190
var symbolTableBuilderV2 = new SymbolTableBuilderV2(rootTree);
191191
var symbolTableV2 = symbolTableBuilderV2.build();
192-
return new TypeInferenceV2(typeTable, pythonFile, symbolTableV2, pkgName).inferModuleType(rootTree);
192+
return new TypeInferenceV2(finalTypeTable, pythonFile, symbolTableV2, pkgName).inferModuleType(rootTree);
193193
});
194194

195195
return new PythonVisitorContext(
@@ -201,7 +201,7 @@ public PythonVisitorContext build() {
201201
sonarProduct.orElse(SonarProduct.SONARQUBE),
202202
projectConfiguration.orElse(new ProjectConfiguration()),
203203
mt,
204-
new TypeChecker(typeTable),
204+
new TypeChecker(finalTypeTable),
205205
callGraph.orElse(CallGraph.EMPTY)
206206
);
207207
}

python-frontend/src/main/java/org/sonar/python/TestPythonVisitorRunner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public static PythonVisitorContext createContext(File file, @Nullable File worki
103103
.projectLevelSymbolTable(projectLevelSymbolTable)
104104
.cacheContext(cacheContext)
105105
.projectConfiguration(projectConfiguration)
106-
.projectLevelTypeTable(typeTable)
106+
.typeTable(typeTable)
107107
.moduleType(moduleType)
108108
.build();
109109
}

python-frontend/src/test/java/org/sonar/plugins/python/api/PythonVisitorContextTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.sonar.python.index.VariableDescriptor;
3434
import org.sonar.python.parser.PythonParser;
3535
import org.sonar.python.semantic.ProjectLevelSymbolTable;
36+
import org.sonar.python.semantic.v2.ProjectLevelTypeTable;
3637
import org.sonar.python.tree.FileInputImpl;
3738
import org.sonar.python.tree.PythonTreeMaker;
3839

@@ -101,13 +102,15 @@ void globalSymbols() {
101102
void sonar_product() {
102103
CacheContextImpl cacheContext = CacheContextImpl.dummyCache();
103104
ProjectLevelSymbolTable projectLevelSymbolTable = ProjectLevelSymbolTable.empty();
105+
var projectLevelTypeTable = new ProjectLevelTypeTable(projectLevelSymbolTable);
104106
String myPackage = "my_package";
105107
PythonFile pythonFile = pythonFile("my_module.py");
106108
FileInput fileInput = mock(FileInputImpl.class);
107109

108110
PythonVisitorContext pythonVisitorContext = new PythonVisitorContext.Builder(fileInput, pythonFile)
109111
.packageName(myPackage)
110112
.projectLevelSymbolTable(projectLevelSymbolTable)
113+
.typeTable(projectLevelTypeTable)
111114
.cacheContext(cacheContext)
112115
.sonarProduct(SonarProduct.SONARLINT)
113116
.build();
@@ -116,6 +119,7 @@ void sonar_product() {
116119
pythonVisitorContext = new PythonVisitorContext.Builder(fileInput, pythonFile)
117120
.packageName(myPackage)
118121
.projectLevelSymbolTable(projectLevelSymbolTable)
122+
.typeTable(projectLevelTypeTable)
119123
.cacheContext(cacheContext)
120124
.sonarProduct(SonarProduct.SONARQUBE)
121125
.build();
@@ -124,6 +128,7 @@ void sonar_product() {
124128
pythonVisitorContext = new PythonVisitorContext.Builder(fileInput, pythonFile)
125129
.packageName(myPackage)
126130
.projectLevelSymbolTable(projectLevelSymbolTable)
131+
.typeTable(projectLevelTypeTable)
127132
.cacheContext(cacheContext)
128133
.build();
129134
assertThat(pythonVisitorContext.sonarProduct()).isEqualTo(SonarProduct.SONARQUBE);
@@ -134,5 +139,13 @@ void sonar_product() {
134139

135140
pythonVisitorContext = new PythonVisitorContext(pythonFile, parsingException, SonarProduct.SONARLINT);
136141
assertThat(pythonVisitorContext.sonarProduct()).isEqualTo(SonarProduct.SONARLINT);
142+
143+
pythonVisitorContext = new PythonVisitorContext.Builder(fileInput, pythonFile)
144+
.packageName(myPackage)
145+
.projectLevelSymbolTable(projectLevelSymbolTable)
146+
.cacheContext(cacheContext)
147+
.sonarProduct(SonarProduct.SONARLINT)
148+
.build();
149+
assertThat(pythonVisitorContext.sonarProduct()).isEqualTo(SonarProduct.SONARLINT);
137150
}
138151
}

0 commit comments

Comments
 (0)