From e3d3e9a2817eac2c8499b2773fe8d136c2a79c51 Mon Sep 17 00:00:00 2001 From: Rudy Ges Date: Tue, 9 Jun 2026 23:19:35 +0200 Subject: [PATCH 1/2] [server] fix false module shadowing across packages check_module_shadowing built the candidate shadow path from the module's short name only (dir/.hx), ignoring its package. So a changed directory containing a same-short-named file in a *different* package would falsely shadow a packaged module, marking it dirty on every build and cascading to its whole dependency cone. Only treat a changed directory as a shadow source for a module when the directory is that module's package directory (its path ends with the module's package). Root-package modules keep the previous behavior; legitimate shadowing (incl. _std overrides) is unaffected. --- src/compiler/server/serverCache.ml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/compiler/server/serverCache.ml b/src/compiler/server/serverCache.ml index fe010e25068..946dc2966e6 100644 --- a/src/compiler/server/serverCache.ml +++ b/src/compiler/server/serverCache.ml @@ -148,14 +148,21 @@ let check_module sctx com m_path m_extra p = with Not_found -> true in + let pack_path = match fst m_path with [] -> "" | pack -> String.concat "/" pack ^ "/" in + let dir_is_package_dir c_path = + let n = String.length pack_path and l = String.length c_path in + n = 0 || (l >= n && String.sub c_path (l - n) n = pack_path) + in let check_module_shadowing paths m_path m_extra = List.iter (fun dir -> - let file = (dir.c_path ^ (snd m_path)) ^ ".hx" in - if Sys.file_exists file then begin - let time = file_time file in - if time > m_extra.m_time then begin - ServerMessage.module_path_changed com "" (m_path,m_extra,time,file); - raise (Dirty (Shadowed file)) + if dir_is_package_dir dir.c_path then begin + let file = (dir.c_path ^ (snd m_path)) ^ ".hx" in + if Sys.file_exists file then begin + let time = file_time file in + if time > m_extra.m_time then begin + ServerMessage.module_path_changed com "" (m_path,m_extra,time,file); + raise (Dirty (Shadowed file)) + end end end ) paths From f7fab65f9b2503866ae2f0f8dbd9c584d0f37bb1 Mon Sep 17 00:00:00 2001 From: Rudy Ges Date: Wed, 10 Jun 2026 07:57:51 +0200 Subject: [PATCH 2/2] [server] shadowing: allow sub-package shadows (parent-package browsing) The previous fix restricted the shadow scan to the module's *exact* package directory, which is unsound under Haxe's parent-package browsing: from a.b.X an unqualified R resolves a.b.R > a.R > R, so a newly created deeper a.b.R shadows the cached a.R for references made from a.b. Requiring the changed dir to equal the module's package dir misses that and leaves dependents bound to the stale shallower module. Match instead when the module's package is a *prefix* of the changed dir's package (the package dir itself or any sub-package of it). Tested textually (prefix at path start or as a "/"-delimited sub-path) rather than by classpath-root stripping, so a path-normalization mismatch can't become a missed shadow; the only residual is a rare harmless over-invalidation when a same-named package is nested under another. --- src/compiler/server/serverCache.ml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/compiler/server/serverCache.ml b/src/compiler/server/serverCache.ml index 946dc2966e6..a90f26d51bf 100644 --- a/src/compiler/server/serverCache.ml +++ b/src/compiler/server/serverCache.ml @@ -149,13 +149,19 @@ let check_module sctx com m_path m_extra p = true in let pack_path = match fst m_path with [] -> "" | pack -> String.concat "/" pack ^ "/" in - let dir_is_package_dir c_path = - let n = String.length pack_path and l = String.length c_path in - n = 0 || (l >= n && String.sub c_path (l - n) n = pack_path) + let contains hay needle = + let nl = String.length needle and hl = String.length hay in + let rec loop i = i + nl <= hl && (String.sub hay i nl = needle || loop (i + 1)) in + nl = 0 || loop 0 + in + let dir_hosts_shadow c_path = + pack_path = "" + || String.starts_with ~prefix:pack_path c_path + || contains c_path ("/" ^ pack_path) in let check_module_shadowing paths m_path m_extra = List.iter (fun dir -> - if dir_is_package_dir dir.c_path then begin + if dir_hosts_shadow dir.c_path then begin let file = (dir.c_path ^ (snd m_path)) ^ ".hx" in if Sys.file_exists file then begin let time = file_time file in