Skip to content

Commit

Permalink
Disallow symbol imports from another module
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bartlomieju committed Nov 16, 2023
1 parent 8d3c6ef commit ddeedfa
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 1 deletion.
78 changes: 77 additions & 1 deletion compiler/src/type_check/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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));
}
}
22 changes: 22 additions & 0 deletions types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2618,6 +2618,28 @@ impl ModuleId {
self.get(db).symbols.contains_key(name)
}

pub fn import_symbol(self, db: &Database, name: &str) -> Option<Symbol> {
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);
}
Expand Down

0 comments on commit ddeedfa

Please sign in to comment.