mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 19:58:18 +00:00
Load and can imports inside defs
After parsing a module, we now recursively traverse the tree to find all imports inside Defs, not just the top-level ones. Previously, imported modules were available in the entire file, but that's no longer the case. Therefore, Scope now keeps track of imported modules and Env::qualified_lookup checks whether a module is available in the provided scope. Note: Unused import warnings are still global and need to be updated.
This commit is contained in:
parent
710d62f754
commit
c617963b22
10 changed files with 434 additions and 84 deletions
|
@ -2148,6 +2148,7 @@ fn report_unused_imported_modules(
|
|||
module_id: ModuleId,
|
||||
constrained_module: &ConstrainedModule,
|
||||
) {
|
||||
// [modules-revamp] TODO: take expr-level into account
|
||||
let mut unused_imported_modules = constrained_module.imported_modules.clone();
|
||||
let mut unused_imports = constrained_module.module.exposed_imports.clone();
|
||||
|
||||
|
@ -5228,35 +5229,23 @@ fn parse<'a>(
|
|||
|
||||
let mut imported: Vec<(QualifiedModuleName, Region)> = vec![];
|
||||
|
||||
for (index, either_index) in parsed_defs.tags.iter().enumerate() {
|
||||
if let Err(value_index) = either_index.split() {
|
||||
let value_def = &parsed_defs.value_defs[value_index.index()];
|
||||
let region = parsed_defs.regions[index];
|
||||
for (def, region) in ast::RecursiveValueDefIter::new(&parsed_defs) {
|
||||
match def {
|
||||
ValueDef::ModuleImport(import) => {
|
||||
let qualified_module_name = QualifiedModuleName {
|
||||
opt_package: import.name.value.package,
|
||||
module: import.name.value.name.into(),
|
||||
};
|
||||
|
||||
match value_def {
|
||||
ValueDef::Annotation(..) => {}
|
||||
|
||||
ValueDef::ModuleImport(import) => {
|
||||
let qualified_module_name = QualifiedModuleName {
|
||||
opt_package: import.name.value.package,
|
||||
module: import.name.value.name.into(),
|
||||
};
|
||||
|
||||
imported.push((qualified_module_name, region));
|
||||
}
|
||||
|
||||
ValueDef::IngestedFileImport(_ingested_file) => {
|
||||
todo!("[modules-revamp] do we need to do anything here?")
|
||||
}
|
||||
|
||||
_ => {
|
||||
|
||||
// [modules-revamp] TODO: traverse exprs
|
||||
}
|
||||
imported.push((qualified_module_name, *region));
|
||||
}
|
||||
|
||||
ValueDef::IngestedFileImport(_ingested_file) => {
|
||||
todo!("[modules-revamp] do we need to do anything here?")
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut exposed: Vec<Symbol> = Vec::with_capacity(num_exposes);
|
||||
|
||||
// Make sure the module_ids has ModuleIds for all our deps,
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
interface ExposedUsedOutsideScope exposes [good, bad] imports []
|
||||
|
||||
good =
|
||||
import Dep2 exposing [two]
|
||||
two
|
||||
|
||||
bad =
|
||||
two
|
12
crates/compiler/load_internal/tests/fixtures/build/interface_with_deps/ImportInsideDef.roc
vendored
Normal file
12
crates/compiler/load_internal/tests/fixtures/build/interface_with_deps/ImportInsideDef.roc
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
interface ImportInsideDef exposes [dep1Str, dep2TwoDobuled] imports []
|
||||
|
||||
dep1Str =
|
||||
import Dep1
|
||||
Dep1.str
|
||||
|
||||
dep2TwoDobuled =
|
||||
2
|
||||
* (
|
||||
import Dep2 exposing [two]
|
||||
two
|
||||
)
|
|
@ -0,0 +1,8 @@
|
|||
interface ImportUsedOutsideScope exposes [good, bad] imports []
|
||||
|
||||
good =
|
||||
import Dep2
|
||||
Dep2.two
|
||||
|
||||
bad =
|
||||
Dep2.two
|
|
@ -438,6 +438,42 @@ fn import_alias() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_inside_def() {
|
||||
let subs_by_module = Default::default();
|
||||
let loaded_module = load_fixture("interface_with_deps", "ImportInsideDef", subs_by_module);
|
||||
|
||||
expect_types(
|
||||
loaded_module,
|
||||
hashmap! {
|
||||
"dep1Str" => "Str",
|
||||
"dep2TwoDobuled" => "Frac *",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "LookupNotInScope")]
|
||||
fn exposed_used_outside_scope() {
|
||||
let subs_by_module = Default::default();
|
||||
load_fixture(
|
||||
"interface_with_deps",
|
||||
"ExposedUsedOutsideScope",
|
||||
subs_by_module,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "ModuleNotImported")]
|
||||
fn import_used_outside_scope() {
|
||||
let subs_by_module = Default::default();
|
||||
load_fixture(
|
||||
"interface_with_deps",
|
||||
"ImportUsedOutsideScope",
|
||||
subs_by_module,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_and_typecheck() {
|
||||
let subs_by_module = Default::default();
|
||||
|
@ -878,6 +914,15 @@ fn opaque_wrapped_unwrapped_outside_defining_module() {
|
|||
^^^
|
||||
|
||||
Note: Opaque types can only be wrapped and unwrapped in the module they are defined in!
|
||||
|
||||
── UNUSED IMPORT ─── tmp/opaque_wrapped_unwrapped_outside_defining_module/Main ─
|
||||
|
||||
Nothing from Age is used in this module.
|
||||
|
||||
3│ import Age exposing [Age]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Since Age isn't used, you don't need to import it.
|
||||
"
|
||||
),
|
||||
"\n{}",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue