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..7e3f51f157 100644 --- a/tests/overrides.rs +++ b/tests/overrides.rs @@ -268,3 +268,93 @@ fn submodule_override_not_evaluated() { .stdout("b\n") .success(); } + +#[test] +fn overrides_of_modules_sharing_a_source_file() { + Test::new() + .write( + "shared.just", + " + x := 'foo' + show: + @echo x={{x}} + ", + ) + .justfile( + " + mod a 'shared.just' + + mod b 'shared.just' + ", + ) + .args(["--set", "a::x", "bar", "a::show", "b::show"]) + .stdout( + " + x=bar + x=foo + ", + ) + .success(); + + Test::new() + .write( + "shared.just", + " + x := 'foo' + show: + @echo x={{x}} + ", + ) + .justfile( + " + mod a 'shared.just' + + mod b 'shared.just' + ", + ) + .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 := 'foo'") + .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", "bar", "a::show", "b::show"]) + .stdout( + " + x=bar + x=foo + ", + ) + .success(); +}