From 10c4096e003e5f4406d7fe121735fc1745cc66af Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 6 Jul 2026 01:01:14 -0700 Subject: [PATCH 1/2] Don't deduplicate modules by path --- src/analyzer.rs | 23 ++++++++++---- src/compilation.rs | 7 +++-- src/compiler.rs | 32 +++++++++++++++----- src/modulepath.rs | 2 +- src/testing.rs | 5 ++-- tests/overrides.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 126 insertions(+), 18 deletions(-) diff --git a/src/analyzer.rs b/src/analyzer.rs index 80e3fbcf5c..c0ee1b913c 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -14,11 +14,12 @@ pub(crate) struct Analyzer<'run, 'src> { impl<'run, 'src> Analyzer<'run, 'src> { pub(crate) fn analyze( - asts: &'run HashMap>, + asts: &'run HashMap<(Modulepath, PathBuf), Ast<'src>>, config: &Config, doc: Option, groups: &[StringLiteral<'src>], loaded: &[PathBuf], + module_path: &Modulepath, name: Option>, overrides: &mut HashMap, paths: &HashMap, @@ -26,17 +27,28 @@ impl<'run, 'src> Analyzer<'run, 'src> { root: &Path, ) -> CompileResult<'src, Justfile<'src>> { Self::default().justfile( - asts, config, doc, groups, loaded, name, overrides, paths, private, root, + asts, + config, + doc, + groups, + loaded, + module_path, + name, + overrides, + paths, + private, + root, ) } fn justfile( mut self, - asts: &'run HashMap>, + asts: &'run HashMap<(Modulepath, PathBuf), Ast<'src>>, config: &Config, doc: Option, groups: &[StringLiteral<'src>], loaded: &[PathBuf], + module_path: &Modulepath, name: Option>, overrides: &mut HashMap, paths: &HashMap, @@ -51,7 +63,7 @@ impl<'run, 'src> Analyzer<'run, 'src> { let mut unstable_features = BTreeSet::new(); let mut stack = Vec::new(); - let ast = asts.get(root).unwrap(); + let ast = asts.get(&(module_path.clone(), root.to_owned())).unwrap(); stack.push(ast); while let Some(ast) = stack.pop() { @@ -80,7 +92,7 @@ impl<'run, 'src> Analyzer<'run, 'src> { if let Some(absolute) = absolute && imports.insert(absolute) { - stack.push(asts.get(absolute).unwrap()); + stack.push(asts.get(&(module_path.clone(), absolute.clone())).unwrap()); } } Item::Module { @@ -99,6 +111,7 @@ impl<'run, 'src> Analyzer<'run, 'src> { doc.clone(), &attributes.groups(), loaded, + &module_path.join(name.lexeme()), Some(*name), overrides, paths, diff --git a/src/compilation.rs b/src/compilation.rs index 341b77fae1..d30999c058 100644 --- a/src/compilation.rs +++ b/src/compilation.rs @@ -2,7 +2,7 @@ use super::*; #[derive(Debug)] pub(crate) struct Compilation<'src> { - pub(crate) asts: HashMap>, + pub(crate) asts: HashMap<(Modulepath, PathBuf), Ast<'src>>, pub(crate) justfile: Justfile<'src>, pub(crate) overrides: HashMap, pub(crate) root: PathBuf, @@ -10,6 +10,9 @@ pub(crate) struct Compilation<'src> { impl<'src> Compilation<'src> { pub(crate) fn root_ast(&self) -> &Ast<'src> { - self.asts.get(&self.root).unwrap() + self + .asts + .get(&(Modulepath::default(), self.root.clone())) + .unwrap() } } diff --git a/src/compiler.rs b/src/compiler.rs index 0576d306aa..d21186d55f 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -8,7 +8,7 @@ impl Compiler { loader: &'src Loader, root: &Path, ) -> RunResult<'src, Compilation<'src>> { - let mut asts = HashMap::::new(); + let mut asts = HashMap::<(Modulepath, PathBuf), Ast>::new(); let mut loaded = Vec::new(); let mut numerator = Numerator::new(); let mut paths = HashMap::::new(); @@ -16,15 +16,29 @@ impl Compiler { stack.push(Source::root(root)); while let Some(current) = stack.pop() { - if paths.contains_key(¤t.path) { + let key = ( + current + .namepath + .as_ref() + .map(Modulepath::from) + .unwrap_or_default(), + current.path.clone(), + ); + + if asts.contains_key(&key) { continue; } let (relative, src) = loader.load(config, root, ¤t.path)?; - loaded.push(relative.into()); - let mut ast = Parser::parse_source(&mut numerator, relative, ¤t, src)?; - paths.insert(current.path.clone(), relative.into()); + if paths + .insert(current.path.clone(), relative.into()) + .is_none() + { + loaded.push(relative.into()); + } + + let mut ast = Parser::parse_source(&mut numerator, relative, ¤t, src)?; for item in &mut ast.items { if !item.is_enabled() { @@ -93,7 +107,7 @@ impl Compiler { } } - asts.insert(current.path, ast.clone()); + asts.insert(key, ast.clone()); } let mut overrides = HashMap::new(); @@ -104,6 +118,7 @@ impl Compiler { None, &[], &loaded, + &Modulepath::default(), None, &mut overrides, &paths, @@ -247,8 +262,8 @@ impl Compiler { let tokens = Lexer::test_lex(src)?; let ast = Parser::parse_tokens(&mut Numerator::new(), &tokens)?; let root = PathBuf::from("justfile"); - let mut asts: HashMap = HashMap::new(); - asts.insert(root.clone(), ast); + let mut asts: HashMap<(Modulepath, PathBuf), Ast> = HashMap::new(); + asts.insert((Modulepath::default(), root.clone()), ast); let mut paths: HashMap = HashMap::new(); paths.insert(root.clone(), root.clone()); Analyzer::analyze( @@ -257,6 +272,7 @@ impl Compiler { None, &[], &[], + &Modulepath::default(), None, &mut HashMap::new(), &paths, diff --git a/src/modulepath.rs b/src/modulepath.rs index 5c21ca444d..0247866766 100644 --- a/src/modulepath.rs +++ b/src/modulepath.rs @@ -1,6 +1,6 @@ use super::*; -#[derive(Debug, Default, Eq, Ord, PartialEq, PartialOrd, Clone)] +#[derive(Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Clone)] pub(crate) struct Modulepath { pub(crate) components: Vec, pub(crate) spaced: bool, diff --git a/src/testing.rs b/src/testing.rs index 957f1ad87f..3ed1677bf9 100644 --- a/src/testing.rs +++ b/src/testing.rs @@ -62,8 +62,8 @@ pub(crate) fn analysis_error( .expect("Parsing failed in analysis test..."); let root = PathBuf::from("justfile"); - let mut asts: HashMap = HashMap::new(); - asts.insert(root.clone(), ast); + let mut asts: HashMap<(Modulepath, PathBuf), Ast> = HashMap::new(); + asts.insert((Modulepath::default(), root.clone()), ast); let mut paths: HashMap = HashMap::new(); paths.insert("justfile".into(), "justfile".into()); @@ -74,6 +74,7 @@ pub(crate) fn analysis_error( None, &[], &[], + &Modulepath::default(), None, &mut HashMap::new(), &paths, diff --git a/tests/overrides.rs b/tests/overrides.rs index 9e495e528b..33cd874376 100644 --- a/tests/overrides.rs +++ b/tests/overrides.rs @@ -268,3 +268,78 @@ fn submodule_override_not_evaluated() { .stdout("b\n") .success(); } + +#[test] +fn overrides_of_modules_sharing_a_source_file() { + Test::new() + .write( + "shared.just", + " + x := 'def' + show: + @echo x={{x}} + ", + ) + .justfile( + " + mod a 'shared.just' + + mod b 'shared.just' + ", + ) + .args(["--set", "a::x", "over", "a::show", "b::show"]) + .stdout("x=over\nx=def\n") + .success(); + + Test::new() + .write( + "shared.just", + " + x := 'def' + show: + @echo x={{x}} + ", + ) + .justfile( + " + mod a 'shared.just' + + mod b 'shared.just' + ", + ) + .args(["--set", "b::x", "over", "a::show", "b::show"]) + .stdout("x=def\nx=over\n") + .success(); +} + +#[test] +fn overrides_of_import_shared_across_modules() { + Test::new() + .write("common.just", "x := 'def'") + .write( + "a.just", + " + import 'common.just' + show: + @echo x={{x}} + ", + ) + .write( + "b.just", + " + import 'common.just' + show: + @echo x={{x}} + ", + ) + .justfile( + " + mod a 'a.just' + + mod b 'b.just' + ", + ) + .args(["--set", "a::x", "over", "a::show", "b::show"]) + .stdout("x=over\nx=def\n") + .success(); +} From f3b3be415edebeaba0b7aeb688ccb0ec1e8ff5f9 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Mon, 6 Jul 2026 15:17:42 -0700 Subject: [PATCH 2/2] Adapt --- tests/overrides.rs | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/tests/overrides.rs b/tests/overrides.rs index 33cd874376..7e3f51f157 100644 --- a/tests/overrides.rs +++ b/tests/overrides.rs @@ -275,7 +275,7 @@ fn overrides_of_modules_sharing_a_source_file() { .write( "shared.just", " - x := 'def' + x := 'foo' show: @echo x={{x}} ", @@ -287,15 +287,20 @@ fn overrides_of_modules_sharing_a_source_file() { mod b 'shared.just' ", ) - .args(["--set", "a::x", "over", "a::show", "b::show"]) - .stdout("x=over\nx=def\n") + .args(["--set", "a::x", "bar", "a::show", "b::show"]) + .stdout( + " + x=bar + x=foo + ", + ) .success(); Test::new() .write( "shared.just", " - x := 'def' + x := 'foo' show: @echo x={{x}} ", @@ -307,15 +312,20 @@ fn overrides_of_modules_sharing_a_source_file() { mod b 'shared.just' ", ) - .args(["--set", "b::x", "over", "a::show", "b::show"]) - .stdout("x=def\nx=over\n") + .args(["--set", "b::x", "bar", "a::show", "b::show"]) + .stdout( + " + x=foo + x=bar + ", + ) .success(); } #[test] fn overrides_of_import_shared_across_modules() { Test::new() - .write("common.just", "x := 'def'") + .write("common.just", "x := 'foo'") .write( "a.just", " @@ -339,7 +349,12 @@ fn overrides_of_import_shared_across_modules() { mod b 'b.just' ", ) - .args(["--set", "a::x", "over", "a::show", "b::show"]) - .stdout("x=over\nx=def\n") + .args(["--set", "a::x", "bar", "a::show", "b::show"]) + .stdout( + " + x=bar + x=foo + ", + ) .success(); }