Repository navigation
Conversation
There was a problem hiding this comment.
Code Review
1. types.rs — Filter logic change
.filter(|def| def.id != type_id || def.file_id != file_id)Issue: potential logic inversion / unclear intent.
The comment says "Remove only this file's contribution, preserving real duplicates." To remove a def, the predicate must return false. The predicate returns false only when def.id == type_id && def.file_id == file_id. That does match the stated intent (remove only the matching id and file), so the logic is correct.
However, a subtle concern: if type_id is a named type ID shared across files, then a def with the same type_id but a different file_id is preserved. This is intended per the comment, but it means the function now silently keeps entries that previously would have been removed. Confirm that callers relying on "remove all defs with this id" semantics (e.g., full invalidation paths) are not affected. Consider renaming the parameter or adding a doc comment on the function to make the "per-file removal" contract explicit.
Minor style: the inline comment is placed between the iterator chain and .filter, which is fine, but the wording "real duplicates" is ambiguous — clarify that these are same-name declarations from other files.
2. mod.rs — Deduplication of workspace IDs
let mut seen = HashSet::new();
ids.retain(|id| seen.insert(*id));
idsCorrectness: HashSet::insert returns true on first insertion, so retain keeps the first occurrence and drops later duplicates — preserving registration order as the comment claims. Good.
Issues:
- Missing import:
HashSetmust be in scope. Verifyuse std::collections::HashSet;(or equivalent) exists in this module. The diff does not show it being added — this will fail to compile if absent. - Performance:
HashSetis allocated even whenidsis small (typically ≤ 3–4 entries). For such tiny inputs, a linear scan (ids.iter().position(...)) orVec::dedupafter sorting would avoid the allocation. Not a blocker, but worth considering given this runs during workspace setup. - Alternative:
itertools::unique()would express intent more directly if the crate already depends onitertools.
3. Test p10_multiple_main_roots_do_not_duplicate_type_definitions
Good coverage of the regression: duplicate MAIN roots, index rebuild after root change, and the negative case (genuinely distinct declarations still counted).
Concerns:
- Hard-coded count
3:assert_eq!(db.all_workspace_ids().len(), 3)assumes MAIN + library + REMOTE. Ifadd_main_workspaceoradd_library_workspacesemantics change (e.g., REMOTE auto-added differently), this breaks for unrelated reasons. Consider asserting the set of IDs rather than the count. - Magic file IDs (
1,2): consistent with surrounding tests, so acceptable, but a named constant would improve readability. - Trailing-newline test:
format!("{source}\n")is used to force a re-index. This is a fragile way to trigger invalidation — if the parser normalizes trailing whitespace, the test may not exercise the intended path. A comment explaining why the newline matters would help future maintainers. - Missing assertion on
workspace_lookup_orderafter the secondadd_main_workspace: the test checks dedup only once, before adding the third root. Consider re-asserting afterdb.add_main_workspace(p10_base().join("third"))to confirm dedup holds across repeated additions.
Summary
| Severity | Item |
|---|---|
| High | Verify HashSet import exists in mod.rs (compile error otherwise) |
| Medium | Confirm types.rs filter change doesn't break callers expecting full-id removal |
| Low | Avoid HashSet allocation for tiny ids; consider itertools::unique |
| Low | Test: replace hard-coded len() == 3 with set comparison; re-assert dedup after third root |
The core logic in both production changes appears correct and the test meaningfully guards the regression. The main actionable risk is the missing HashSet import.
|
I had GPT look at the feedback:
|
e7a0222 to
8b62fee
Compare
Several physical roots can share MAIN. Deduplicate workspace IDs while preserving lookup order, so one declaration does not produce duplicate-type diagnostics or navigation targets. Preserve other files with the same named type when removing an indexed contribution. Cover root changes, edits, genuine duplicate declarations and their removal. Signed-off-by: Henning P. Schmiedehausen <henning@schmiedehausen.org>
Use the existing itertools dependency to preserve first-occurrence order during collection. Clarify per-file removal and strengthen regressions for exact workspace IDs, repeated root additions, and class renames. Signed-off-by: Henning P. Schmiedehausen <henning@schmiedehausen.org>
8b62fee to
824bb04
Compare
This regression came up when working on the suggested change that replaces #1283. I use a "main project" in intellij and then add factorio mods as intellij modules. Each of those runs its own LSP server instance in addition to the "main project". Currently the main server sees all the submodules and gets confused because they all advertise as "MAIN". This PR changes that.
GPT had this to say:
Problem
This corrects a regression introduced by our merged #1266 (
ef3cf3c8). That change retains multiple main workspace roots so module resolution can use the caller's source root. However,compute_workspace_idsstill collects one ID per physical root, even when several roots shareWorkspaceId::MAIN.The global type aggregate consequently appends the same workspace's declarations repeatedly. With two workspace folders and just one declaration:
The analyzer reports
duplicate-typeand returns two definition locations for that single class. Adding more roots repeats it further. This was observed with a multi-folder IntelliJ workspace, but the problem is in the analyzer's workspace index rather than the editor.A focused regression passes on #1266's immediate parent (
1c8589bb), fails onef3cf3c8, and passes with this patch.Fix
The regression covers multiple MAIN roots, a library root, root changes, incremental edits, real duplicate declarations in different files, and removing one of those declarations without losing the other.