Skip to content

Commit 3c3bab0

Browse files
committed
Clean stale symbol references (#1785)
1 parent 2de10de commit 3c3bab0

3 files changed

Lines changed: 96 additions & 0 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
category: fixed
3+
issues:
4+
- 1785
5+
affected:
6+
- src/CodeIndex/Database/DbWriter.cs
7+
- tests/CodeIndex.Tests/DatabaseTests.cs
8+
---
9+
10+
## English
11+
12+
- **Stale file purges remove cross-file references to symbols defined only by the purged files (#1785)** — cleanup now drops phantom symbol edges when a deleted file was the only remaining definition for the referenced name.
13+
14+
## 日本語
15+
16+
- **stale file purge が purge 対象ファイルにしか定義が残っていないシンボルへの cross-file reference を削除するようになりました (#1785)** — 削除済みファイルが参照名の唯一の定義だった場合、phantom symbol edge を cleanup で取り除きます。

src/CodeIndex/Database/DbWriter.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,8 @@ private void DeleteFilesByIdBatched(IEnumerable<long> fileIds, int batchSize = D
578578

579579
private void DeleteFileIdBatch(IReadOnlyList<long> fileIds)
580580
{
581+
DeleteCrossFileReferencesToSymbolsDefinedOnlyByFiles(fileIds);
582+
581583
using var deleteCmd = _conn.CreateCommand();
582584
var parameters = new List<string>(fileIds.Count);
583585
for (var i = 0; i < fileIds.Count; i++)
@@ -591,6 +593,38 @@ private void DeleteFileIdBatch(IReadOnlyList<long> fileIds)
591593
deleteCmd.ExecuteNonQuery();
592594
}
593595

596+
private void DeleteCrossFileReferencesToSymbolsDefinedOnlyByFiles(IReadOnlyList<long> fileIds)
597+
{
598+
using var deleteCmd = _conn.CreateCommand();
599+
var parameters = new List<string>(fileIds.Count);
600+
for (var i = 0; i < fileIds.Count; i++)
601+
{
602+
var parameterName = $"@id{i}";
603+
parameters.Add(parameterName);
604+
deleteCmd.Parameters.Add(parameterName, SqliteType.Integer).Value = fileIds[i];
605+
}
606+
607+
var idList = string.Join(", ", parameters);
608+
deleteCmd.CommandText = $@"
609+
DELETE FROM symbol_references
610+
WHERE file_id NOT IN ({idList})
611+
AND symbol_name IS NOT NULL
612+
AND symbol_name <> ''
613+
AND EXISTS (
614+
SELECT 1
615+
FROM symbols deleted_symbols
616+
WHERE deleted_symbols.file_id IN ({idList})
617+
AND deleted_symbols.name = symbol_references.symbol_name
618+
)
619+
AND NOT EXISTS (
620+
SELECT 1
621+
FROM symbols retained_symbols
622+
WHERE retained_symbols.file_id NOT IN ({idList})
623+
AND retained_symbols.name = symbol_references.symbol_name
624+
)";
625+
deleteCmd.ExecuteNonQuery();
626+
}
627+
594628
private static string GetRelativeDirectory(string relativePath)
595629
{
596630
var normalized = relativePath.Replace('\\', '/');

tests/CodeIndex.Tests/DatabaseTests.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,52 @@ INSERT INTO symbol_references (
138138
Assert.Equal(0L, reader.GetInt64(1));
139139
}
140140

141+
[Fact]
142+
public void PurgeStaleFiles_RemovesCrossFileReferencesToSymbolsDefinedOnlyByDeletedFiles()
143+
{
144+
var projectRoot = TestProjectHelper.CreateTempProject("purge-stale-symbol-ref");
145+
try
146+
{
147+
Directory.CreateDirectory(Path.Combine(projectRoot, "src"));
148+
File.WriteAllText(Path.Combine(projectRoot, "src", "target.py"), "# retained rename target");
149+
150+
var callerFileId = UpsertTestFile("src/caller.cs", checksum: "caller");
151+
var staleTargetFileId = UpsertTestFile("src/target.cs", checksum: "target");
152+
_ = UpsertTestFile("src/target.py", checksum: "target");
153+
_writer.InsertSymbols([
154+
new SymbolRecord
155+
{
156+
FileId = staleTargetFileId,
157+
Kind = "function",
158+
Name = "DeletedTarget",
159+
Line = 1,
160+
},
161+
]);
162+
_writer.InsertReferences([
163+
new ReferenceRecord
164+
{
165+
FileId = callerFileId,
166+
SymbolName = "DeletedTarget",
167+
ReferenceKind = "call",
168+
Line = 1,
169+
Column = 1,
170+
Context = "DeletedTarget();",
171+
},
172+
]);
173+
174+
var purged = _writer.PurgeStaleFilesSharingDirectoryAndStem(projectRoot, "src/target.py");
175+
176+
Assert.Equal(1, purged);
177+
using var cmd = _db.Connection.CreateCommand();
178+
cmd.CommandText = "SELECT COUNT(*) FROM symbol_references WHERE symbol_name = 'DeletedTarget'";
179+
Assert.Equal(0L, (long)cmd.ExecuteScalar()!);
180+
}
181+
finally
182+
{
183+
TestProjectHelper.DeleteDirectory(projectRoot);
184+
}
185+
}
186+
141187
[Fact]
142188
public void OptimizeFts_ResetsIncrementalWriteCounterAndStampsTime()
143189
{

0 commit comments

Comments
 (0)