diff --git a/CHANGELOG.md b/CHANGELOG.md index f88db3d95..447d6ce20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Prefer `Debugger_DebugSourcePath` over `DCC_UnitSearchPath` in the analysis search path. +- Reprioritize the analysis search path in the following order (highest to lowest): + - `sonar.sources` + - `DCCReference` + - `Debugger_DebugSourcePath` + - `DCC_UnitSearchPath` + - Standard library + ## [1.12.2] - 2025-01-06 diff --git a/delphi-frontend/src/main/java/au/com/integradev/delphi/symbol/SymbolTableBuilder.java b/delphi-frontend/src/main/java/au/com/integradev/delphi/symbol/SymbolTableBuilder.java index 702469d08..cc9e1028c 100644 --- a/delphi-frontend/src/main/java/au/com/integradev/delphi/symbol/SymbolTableBuilder.java +++ b/delphi-frontend/src/main/java/au/com/integradev/delphi/symbol/SymbolTableBuilder.java @@ -185,7 +185,7 @@ private static boolean isPasFile(Path path) { } private void createUnitData(Path unitPath, boolean isSourceFile) { - if (unitPaths.add(unitPath) || isSourceFile) { + if (unitPaths.add(unitPath)) { String unitName = FilenameUtils.getBaseName(unitPath.toString()); UnitData unitData = new UnitData(unitPath, isSourceFile); @@ -459,10 +459,11 @@ public SymbolTable build() { throw new SymbolTableConstructionException("typeFactory was not supplied."); } - processStandardLibrarySearchPaths(); - searchPath.getRootDirectories().forEach(this::processSearchPath); - referencedFiles.forEach(file -> this.createUnitData(file, false)); sourceFiles.forEach(file -> this.createUnitData(file, true)); + referencedFiles.forEach(file -> this.createUnitData(file, false)); + searchPath.getRootDirectories().forEach(this::processSearchPath); + + processStandardLibrarySearchPaths(); ProgressReport progressReport = new ProgressReport( diff --git a/delphi-frontend/src/test/java/au/com/integradev/delphi/symbol/SymbolTableBuilderTest.java b/delphi-frontend/src/test/java/au/com/integradev/delphi/symbol/SymbolTableBuilderTest.java index 1bc0809f9..8a5aecdab 100644 --- a/delphi-frontend/src/test/java/au/com/integradev/delphi/symbol/SymbolTableBuilderTest.java +++ b/delphi-frontend/src/test/java/au/com/integradev/delphi/symbol/SymbolTableBuilderTest.java @@ -38,6 +38,8 @@ import java.nio.file.spi.FileSystemProvider; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; @@ -74,7 +76,7 @@ void testIOExceptionWhileProcessingSearchPath() throws IOException { builder.searchPath(searchPath); - assertThatThrownBy(builder::build).isInstanceOf(SymbolTableConstructionException.class); + assertThatThrownBy(builder::build).isInsta nceOf(SymbolTableConstructionException.class); } @Test @@ -157,6 +159,95 @@ void testStandardLibrarySearchPathShouldExcludeToolsUnits( assertThat(symbolTable.getUnitByPath(excludedPath.toString())).isNull(); } + @Test + void testSonarSourcesArePrioritizedOverReferencedFiles( + @TempDir Path standardLibraryPath, + @TempDir Path referencedFilesPath, + @TempDir Path sourceFilesPath) + throws IOException { + createStandardLibrary(standardLibraryPath); + createStandardLibrary(referencedFilesPath); + createStandardLibrary(sourceFilesPath); + + List referencedFiles; + try (Stream referencedFilesStream = Files.list(referencedFilesPath)) { + referencedFiles = referencedFilesStream.collect(Collectors.toUnmodifiableList()); + } + + List sourceFiles; + try (Stream sourceFilesStream = Files.list(sourceFilesPath)) { + sourceFiles = sourceFilesStream.collect(Collectors.toUnmodifiableList()); + } + + SymbolTable symbolTable = + SymbolTable.builder() + .preprocessorFactory(new DelphiPreprocessorFactory(Platform.WINDOWS)) + .typeFactory(TypeFactoryUtils.defaultFactory()) + .standardLibraryPath(standardLibraryPath) + .referencedFiles(referencedFiles) + .sourceFiles(sourceFiles) + .build(); + + assertThat(symbolTable.getUnitByPath(standardLibraryPath.resolve("SysInit.pas").toString())) + .isNull(); + assertThat(symbolTable.getUnitByPath(referencedFilesPath.resolve("SysInit.pas").toString())) + .isNull(); + assertThat(symbolTable.getUnitByPath(sourceFilesPath.resolve("SysInit.pas").toString())) + .isNotNull(); + } + + @Test + void testReferencedFilesArePrioritizedOverSearchPath( + @TempDir Path standardLibraryPath, + @TempDir Path searchPathRoot, + @TempDir Path referencedFilesPath) + throws IOException { + createStandardLibrary(standardLibraryPath); + createStandardLibrary(searchPathRoot); + createStandardLibrary(referencedFilesPath); + + List referencedFiles; + try (Stream referencedFilesStream = Files.list(referencedFilesPath)) { + referencedFiles = referencedFilesStream.collect(Collectors.toUnmodifiableList()); + } + + SymbolTable symbolTable = + SymbolTable.builder() + .preprocessorFactory(new DelphiPreprocessorFactory(Platform.WINDOWS)) + .typeFactory(TypeFactoryUtils.defaultFactory()) + .standardLibraryPath(standardLibraryPath) + .searchPath(SearchPath.create(List.of(searchPathRoot))) + .referencedFiles(referencedFiles) + .build(); + + assertThat(symbolTable.getUnitByPath(standardLibraryPath.resolve("SysInit.pas").toString())) + .isNull(); + assertThat(symbolTable.getUnitByPath(searchPathRoot.resolve("SysInit.pas").toString())) + .isNull(); + assertThat(symbolTable.getUnitByPath(referencedFilesPath.resolve("SysInit.pas").toString())) + .isNotNull(); + } + + @Test + void testSearchPathIsPrioritizedOverStandardLibrary( + @TempDir Path standardLibraryPath, @TempDir Path searchPathRoot) throws IOException { + createStandardLibrary(standardLibraryPath); + createStandardLibrary(searchPathRoot); + + SymbolTable symbolTable = + SymbolTable.builder() + .preprocessorFactory(new DelphiPreprocessorFactory(Platform.WINDOWS)) + .typeFactory(TypeFactoryUtils.defaultFactory()) + .standardLibraryPath(standardLibraryPath) + .searchPath(SearchPath.create(List.of(searchPathRoot))) + .build(); + + assertThat(symbolTable.getUnitByPath(standardLibraryPath.resolve("SysInit.pas").toString())) + .isNull(); + assertThat(symbolTable.getUnitByPath(searchPathRoot.resolve("SysInit.pas").toString())) + .isNotNull(); + } + @ParameterizedTest @ValueSource(strings = {"#$@", "unit ErrorUnit;"}) void testRecognitionErrorInImportShouldNotThrow(