From 0af285f3a37ce83d7a0836f4fe2a99fa438088df Mon Sep 17 00:00:00 2001 From: "Henning P. Schmiedehausen" Date: Wed, 14 Oct 2026 16:22:33 -0700 Subject: [PATCH 1/2] fix(workspace): aggregate each logical workspace only once 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. Cover root changes, edits, genuine duplicate declarations and their removal. File-specific removal is already supplied by upstream commit 9fdfca14. Signed-off-by: Henning P. Schmiedehausen --- .../src/semantic_db/mod.rs | 4 ++ .../src/semantic_db/phase_tests/p10_tests.rs | 37 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/crates/emmylua_code_analysis/src/semantic_db/mod.rs b/crates/emmylua_code_analysis/src/semantic_db/mod.rs index a7c0f61b6..204ce0a8f 100644 --- a/crates/emmylua_code_analysis/src/semantic_db/mod.rs +++ b/crates/emmylua_code_analysis/src/semantic_db/mod.rs @@ -263,6 +263,10 @@ fn compute_workspace_ids(roots: &[WorkspaceRoot]) -> Vec { if !ids.contains(&WorkspaceId::REMOTE) { ids.push(WorkspaceId::REMOTE); } + // Several roots can belong to the same logical workspace (notably MAIN). + // Aggregate each workspace index once, preserving registration order. + let mut seen = HashSet::new(); + ids.retain(|id| seen.insert(*id)); ids } diff --git a/crates/emmylua_code_analysis/src/semantic_db/phase_tests/p10_tests.rs b/crates/emmylua_code_analysis/src/semantic_db/phase_tests/p10_tests.rs index 276e2d862..5b7c81e09 100644 --- a/crates/emmylua_code_analysis/src/semantic_db/phase_tests/p10_tests.rs +++ b/crates/emmylua_code_analysis/src/semantic_db/phase_tests/p10_tests.rs @@ -119,6 +119,43 @@ fn p10_workspace_id_caches_follow_root_changes() { assert_eq!(db.workspace_id_of(fid), Some(WorkspaceId::MAIN)); } +#[test] +fn p10_multiple_main_roots_do_not_duplicate_type_definitions() { + let mut db = setup(); + let other_root = p10_base().join("other"); + db.add_main_workspace(other_root.clone()); + db.add_library_workspace(&WorkspaceFolder::new(p10_library_root(), true)); + assert_eq!(db.all_workspace_ids().len(), 3); + assert_eq!( + db.workspace_lookup_order() + .iter() + .filter(|id| **id == WorkspaceId::MAIN) + .count(), + 1 + ); + + let source = "---@class Metrics\nlocal Metrics = {}"; + let first = set_test_file(&mut db, 1, &p10_main_file("metrics.lua"), source); + assert_eq!(db.analysis().type_def_locations(first, "Metrics").len(), 1); + + // Rebuilding indexes after a root change must preserve the same count. + db.add_main_workspace(p10_base().join("third")); + assert_eq!(db.analysis().type_def_locations(first, "Metrics").len(), 1); + set_test_file( + &mut db, + 1, + &p10_main_file("metrics.lua"), + &format!("{source}\n"), + ); + assert_eq!(db.analysis().type_def_locations(first, "Metrics").len(), 1); + + // Different declarations with the same name still count as duplicates. + set_test_file(&mut db, 2, &other_root.join("metrics.lua"), source); + assert_eq!(db.analysis().type_def_locations(first, "Metrics").len(), 2); + set_test_file(&mut db, 2, &other_root.join("metrics.lua"), "return {}"); + assert_eq!(db.analysis().type_def_locations(first, "Metrics").len(), 1); +} + #[test] fn p10_type_buckets_share_arc_and_invalidate_on_write() { let mut db = setup(); From ff472e53ba4aaa43efa79ce8efea57ece369232e Mon Sep 17 00:00:00 2001 From: "Henning P. Schmiedehausen" Date: Wed, 14 Oct 2026 17:32:20 -0700 Subject: [PATCH 2/2] refactor(workspace): refine stable workspace deduplication Use the existing itertools dependency to preserve first-occurrence order during collection. Strengthen regressions for exact workspace IDs, repeated root additions, class renames and genuine duplicates. Signed-off-by: Henning P. Schmiedehausen --- .../src/semantic_db/mod.rs | 9 ++--- .../src/semantic_db/phase_tests/p10_tests.rs | 40 ++++++++++++++----- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/crates/emmylua_code_analysis/src/semantic_db/mod.rs b/crates/emmylua_code_analysis/src/semantic_db/mod.rs index 204ce0a8f..0812ff3b7 100644 --- a/crates/emmylua_code_analysis/src/semantic_db/mod.rs +++ b/crates/emmylua_code_analysis/src/semantic_db/mod.rs @@ -17,6 +17,7 @@ pub(crate) mod types; pub(crate) mod update; use hashbrown::{HashMap, HashSet}; +use itertools::Itertools; use std::fmt; use std::path::PathBuf; use std::sync::Arc; @@ -258,15 +259,13 @@ fn compute_workspace_ids(roots: &[WorkspaceRoot]) -> Vec { let mut ids: Vec = if roots.is_empty() { vec![WorkspaceId::MAIN] } else { - roots.iter().map(|root| root.id).collect() + // Several roots can belong to the same logical workspace (notably MAIN). + // Aggregate each workspace index once, preserving registration order. + roots.iter().map(|root| root.id).unique().collect() }; if !ids.contains(&WorkspaceId::REMOTE) { ids.push(WorkspaceId::REMOTE); } - // Several roots can belong to the same logical workspace (notably MAIN). - // Aggregate each workspace index once, preserving registration order. - let mut seen = HashSet::new(); - ids.retain(|id| seen.insert(*id)); ids } diff --git a/crates/emmylua_code_analysis/src/semantic_db/phase_tests/p10_tests.rs b/crates/emmylua_code_analysis/src/semantic_db/phase_tests/p10_tests.rs index 5b7c81e09..b89f6e066 100644 --- a/crates/emmylua_code_analysis/src/semantic_db/phase_tests/p10_tests.rs +++ b/crates/emmylua_code_analysis/src/semantic_db/phase_tests/p10_tests.rs @@ -125,14 +125,15 @@ fn p10_multiple_main_roots_do_not_duplicate_type_definitions() { let other_root = p10_base().join("other"); db.add_main_workspace(other_root.clone()); db.add_library_workspace(&WorkspaceFolder::new(p10_library_root(), true)); - assert_eq!(db.all_workspace_ids().len(), 3); - assert_eq!( - db.workspace_lookup_order() - .iter() - .filter(|id| **id == WorkspaceId::MAIN) - .count(), - 1 - ); + let library_id = db + .all_workspace_ids() + .iter() + .copied() + .find(|id| id.is_library()) + .unwrap(); + let expected_ids = [WorkspaceId::MAIN, library_id, WorkspaceId::REMOTE]; + assert_eq!(db.all_workspace_ids(), expected_ids); + assert_eq!(db.workspace_lookup_order(), expected_ids); let source = "---@class Metrics\nlocal Metrics = {}"; let first = set_test_file(&mut db, 1, &p10_main_file("metrics.lua"), source); @@ -140,12 +141,33 @@ fn p10_multiple_main_roots_do_not_duplicate_type_definitions() { // Rebuilding indexes after a root change must preserve the same count. db.add_main_workspace(p10_base().join("third")); + assert_eq!(db.all_workspace_ids(), expected_ids); + assert_eq!(db.workspace_lookup_order(), expected_ids); assert_eq!(db.analysis().type_def_locations(first, "Metrics").len(), 1); + + // A real declaration edit must remove the old name and index the new one. set_test_file( &mut db, 1, &p10_main_file("metrics.lua"), - &format!("{source}\n"), + &source.replace("Metrics", "RenamedMetrics"), + ); + assert!( + db.analysis() + .type_def_locations(first, "Metrics") + .is_empty() + ); + assert_eq!( + db.analysis() + .type_def_locations(first, "RenamedMetrics") + .len(), + 1 + ); + set_test_file(&mut db, 1, &p10_main_file("metrics.lua"), source); + assert!( + db.analysis() + .type_def_locations(first, "RenamedMetrics") + .is_empty() ); assert_eq!(db.analysis().type_def_locations(first, "Metrics").len(), 1);