Update unused warnings for inline imports

Now that imports can be limited to smaller scopes than the entire module,
unused import warnings need to work like unused def warnings.

This commit moves unused import warnings discovery and reporting from load
to canonicalization where we can track their usage per scope.

This also fixes a longstanding bug where unused exposed names from an import
were not reported if they were only used in a qualified manner.
This commit is contained in:
Agus Zubiaga 2024-01-11 18:09:01 -03:00
parent 08e6b79dca
commit 7b3317dbb6
No known key found for this signature in database
18 changed files with 334 additions and 122 deletions

View file

@ -217,10 +217,21 @@ fn load_fixture(
let home = loaded_module.module_id;
assert_eq!(
loaded_module.can_problems.remove(&home).unwrap_or_default(),
Vec::new()
);
let (filepath, src) = loaded_module.sources.get(&home).unwrap();
let can_problems = loaded_module.can_problems.remove(&home).unwrap_or_default();
if !can_problems.is_empty() {
panic!(
"{}",
format_can_problems(
can_problems,
home,
&loaded_module.interns,
filepath.clone(),
src,
)
);
}
assert!(loaded_module
.type_problems
.remove(&home)
@ -453,7 +464,7 @@ fn import_inside_def() {
}
#[test]
#[should_panic(expected = "LookupNotInScope")]
#[should_panic(expected = "UNRECOGNIZED NAME")]
fn exposed_used_outside_scope() {
let subs_by_module = Default::default();
load_fixture(
@ -464,7 +475,7 @@ fn exposed_used_outside_scope() {
}
#[test]
#[should_panic(expected = "ModuleNotImported")]
#[should_panic(expected = "MODULE NOT IMPORTED")]
fn import_used_outside_scope() {
let subs_by_module = Default::default();
load_fixture(
@ -915,9 +926,9 @@ 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
UNUSED IMPORT in tmp/opaque_wrapped_unwrapped_outside_defining_module/Main
Nothing from Age is used in this module.
Age is imported but not used.
3 import Age exposing [Age]
^^^^^^^^^^^^^^^^^^^^^^^^^
@ -930,6 +941,114 @@ fn opaque_wrapped_unwrapped_outside_defining_module() {
);
}
#[test]
fn unused_imports() {
let modules = vec![
(
"Dep1",
indoc!(
r#"
interface Dep1 exposes [one] imports []
one = 1
"#
),
),
(
"Dep2",
indoc!(
r#"
interface Dep2 exposes [two] imports []
two = 2
"#
),
),
(
"Dep3",
indoc!(
r#"
interface Dep3 exposes [Three, three] imports []
Three : [Three]
three = Three
"#
),
),
(
"Main",
indoc!(
r#"
interface Main exposes [usedModule, unusedModule, unusedExposed, usingThreeValue] imports []
import Dep1
import Dep3 exposing [Three]
usedModule =
import Dep2
Dep2.two
unusedModule =
import Dep2
2
unusedExposed =
import Dep2 exposing [two]
2
usingThreeValue =
Dep3.three
"#
),
),
];
let err = multiple_modules("unused_imports", modules).unwrap_err();
assert_eq!(
err,
indoc!(
r"
UNUSED IMPORT in tmp/unused_imports/Main
Dep2 is imported but not used.
11 import Dep2
^^^^^^^^^^^
Since Dep2 isn't used, you don't need to import it.
UNUSED IMPORT in tmp/unused_imports/Main
Dep2 is imported but not used.
15 import Dep2 exposing [two]
^^^^^^^^^^^^^^^^^^^^^^^^^^
Since Dep2 isn't used, you don't need to import it.
UNUSED IMPORT in tmp/unused_imports/Main
Dep1 is imported but not used.
3 import Dep1
^^^^^^^^^^^
Since Dep1 isn't used, you don't need to import it.
UNUSED IMPORT in tmp/unused_imports/Main
`Dep3.Three` is not used in this module.
4 import Dep3 exposing [Three]
^^^^^
Since `Dep3.Three` isn't used, you don't need to import it.
"
),
"\n{}",
err
)
}
#[test]
fn issue_2863_module_type_does_not_exist() {
let modules = vec![