From ddeedfa147c343c55c6ccf90f8f6457923f6bd1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 16 Nov 2023 01:33:52 +0100 Subject: [PATCH] Disallow symbol imports from another module This commit fixes a compiler bug that allowed to import symbols that were not defined in the module, but only imported from another module. Changelog: fixed --- compiler/src/type_check/imports.rs | 78 +++++++++++++++++++++++++++++- types/src/lib.rs | 22 +++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/compiler/src/type_check/imports.rs b/compiler/src/type_check/imports.rs index 9babe7fb8..3933c4051 100644 --- a/compiler/src/type_check/imports.rs +++ b/compiler/src/type_check/imports.rs @@ -101,7 +101,7 @@ impl<'a> DefineImportedTypes<'a> { let name = &node.name.name; let import_as = &node.import_as.name; - if let Some(symbol) = source.symbol(self.db(), name) { + if let Some(symbol) = source.import_symbol(self.db(), name) { if self.module.symbol_exists(self.db(), import_as) { self.state.diagnostics.duplicate_symbol( import_as, @@ -714,4 +714,80 @@ mod tests { assert_eq!(error.file(), &PathBuf::from("test.inko")); assert_eq!(error.location(), &cols(3, 3)); } + + #[test] + fn test_import_symbol_from_another_module() { + let symbol = "fizz".to_string(); + let mut state = State::new(Config::new()); + let mut modules = vec![ + hir_module( + &mut state, + ModuleName::new("foo"), + vec![hir::TopLevelExpression::Import(Box::new(hir::Import { + source: vec![hir::Identifier { + name: "fizz".to_string(), + location: cols(1, 1), + }], + symbols: vec![hir::ImportSymbol { + name: hir::Identifier { + name: symbol.clone(), + location: cols(4, 4), + }, + import_as: hir::Identifier { + name: symbol.clone(), + location: cols(1, 1), + }, + location: cols(1, 1), + }], + location: cols(1, 1), + }))], + ), + hir_module( + &mut state, + ModuleName::new("bar"), + vec![hir::TopLevelExpression::Import(Box::new(hir::Import { + source: vec![hir::Identifier { + name: "foo".to_string(), + location: cols(1, 1), + }], + symbols: vec![hir::ImportSymbol { + name: hir::Identifier { + name: symbol.clone(), + location: cols(4, 4), + }, + import_as: hir::Identifier { + name: symbol.clone(), + location: cols(1, 1), + }, + location: cols(1, 1), + }], + location: cols(1, 1), + }))], + ), + ]; + + let fizz_mod = Module::alloc( + &mut state.db, + ModuleName::new("fizz"), + "fizz.inko".into(), + ); + + let fizz = Method::alloc( + &mut state.db, + fizz_mod, + symbol.clone(), + Visibility::Public, + MethodKind::Instance, + ); + + fizz_mod.new_symbol(&mut state.db, symbol, Symbol::Method(fizz)); + + assert!(!DefineImportedTypes::run_all(&mut state, &mut modules)); + + let error = state.diagnostics.iter().next().unwrap(); + + assert_eq!(error.id(), DiagnosticId::InvalidSymbol); + assert_eq!(error.file(), &PathBuf::from("test.inko")); + assert_eq!(error.location(), &cols(4, 4)); + } } diff --git a/types/src/lib.rs b/types/src/lib.rs index a9c6b486e..3d1025106 100644 --- a/types/src/lib.rs +++ b/types/src/lib.rs @@ -2618,6 +2618,28 @@ impl ModuleId { self.get(db).symbols.contains_key(name) } + pub fn import_symbol(self, db: &Database, name: &str) -> Option { + let Some(symbol) = self.symbol(db, name) else { + return None; + }; + + let module_id = match symbol { + Symbol::Class(id) => id.module(db), + Symbol::Trait(id) => id.module(db), + Symbol::Constant(id) => id.module(db), + Symbol::Method(id) => id.module(db), + Symbol::Module(id) => id, + // Type parameters can't be imported. + Symbol::TypeParameter(_) => return None, + }; + + if self == module_id { + Some(symbol) + } else { + None + } + } + pub fn new_symbol(self, db: &mut Database, name: String, symbol: Symbol) { self.get_mut(db).symbols.insert(name, symbol); }