Skip to content

Commit

Permalink
Reprioritize the analysis search path
Browse files Browse the repository at this point in the history
The new search path priority is (highest to lowest):
  - `sonar.sources`
  - `DCCReference`
  - `Debugger_DebugSourcePath`
  - `DCC_UnitSearchPath`
  - Standard library

This change enables behaviors like:
- shadowing standard library units with a unit on `DCC_UnitSearchPath`
- shadowing `DCC_UnitSearchPath` units with a `DCCReference` unit

These cases are somewhat unusual, but they are possible in Delphi.
  • Loading branch information
Cirras committed Jan 13, 2025
1 parent 7c70fda commit 975c98e
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 6 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<Path> referencedFiles;
try (Stream<Path> referencedFilesStream = Files.list(referencedFilesPath)) {
referencedFiles = referencedFilesStream.collect(Collectors.toUnmodifiableList());
}

List<Path> sourceFiles;
try (Stream<Path> 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<Path> referencedFiles;
try (Stream<Path> 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(
Expand Down

0 comments on commit 975c98e

Please sign in to comment.