Handle explicit builtin imports with empty exposing lists

Also includes related style suggestions by Ayaz on #6658
This commit is contained in:
Agus Zubiaga 2024-04-22 18:29:48 -03:00
parent 66bf955a6e
commit d952d5576a
No known key found for this signature in database
3 changed files with 92 additions and 59 deletions

View file

@ -2975,29 +2975,23 @@ fn to_pending_value_def<'a>(
return PendingValue::ImportNameConflict; return PendingValue::ImportNameConflict;
} }
let mut exposed_symbols; let exposed_names = module_import
.exposed
.map(|kw| kw.item.items)
.unwrap_or_default();
let is_automatically_imported = if exposed_names.is_empty() && !env.home.is_builtin() && module_id.is_automatically_imported() {
!env.home.is_builtin() && module_id.is_automatically_imported(); env.problems.push(Problem::ExplicitBuiltinImport(module_id, region));
match module_import.exposed {
None => {
exposed_symbols = Vec::new();
if is_automatically_imported {
env.problems
.push(Problem::ExplicitBuiltinImport(module_id, region));
} }
}
Some(exposed_kw) => {
let exposed_ids = env let exposed_ids = env
.dep_idents .dep_idents
.get(&module_id) .get(&module_id)
.expect("Module id should have been added in load"); .expect("Module id should have been added in load");
exposed_symbols = Vec::with_capacity(exposed_kw.item.len()); let mut exposed_symbols = Vec::with_capacity(exposed_names.len());
for loc_name in exposed_kw.item.items { for loc_name in exposed_names {
let exposed_name = loc_name.value.item(); let exposed_name = loc_name.value.item();
let name = exposed_name.as_str(); let name = exposed_name.as_str();
let ident = Ident::from(name); let ident = Ident::from(name);
@ -3008,11 +3002,7 @@ fn to_pending_value_def<'a>(
exposed_symbols.push((symbol, loc_name.region)); exposed_symbols.push((symbol, loc_name.region));
if let Err((_shadowed_symbol, existing_symbol_region)) = scope.import_symbol(ident, symbol, loc_name.region) { if let Err((_shadowed_symbol, existing_symbol_region)) = scope.import_symbol(ident, symbol, loc_name.region) {
if is_automatically_imported if symbol.is_automatically_imported() {
&& Symbol::builtin_types_in_scope(module_id)
.iter()
.any(|(_, (s, _))| *s == symbol)
{
env.problem(Problem::ExplicitBuiltinTypeImport( env.problem(Problem::ExplicitBuiltinTypeImport(
symbol, symbol,
loc_name.region, loc_name.region,
@ -3043,8 +3033,7 @@ fn to_pending_value_def<'a>(
})) }))
} }
} }
}
}
} }
PendingValue::ModuleImport(IntroducedImport { PendingValue::ModuleImport(IntroducedImport {

View file

@ -1146,6 +1146,41 @@ fn explicit_builtin_import() {
); );
} }
#[test]
fn explicit_builtin_import_empty_exposing() {
let modules = vec![(
"Main.roc",
indoc!(
r#"
interface Main exposes [main] imports []
import Bool exposing []
main = Bool.true
"#
),
)];
let err = multiple_modules("empty_exposing_builtin_import", modules).unwrap_err();
assert_eq!(
err,
indoc!(
r"
EXPLICIT BUILTIN IMPORT in tmp/empty_exposing_builtin_import/Main.roc
The builtin Bool was imported here:
3 import Bool exposing []
^^^^^^^^^^^^^^^^^^^^^^^
Builtins are imported automatically, so you can remove this import.
Tip: Learn more about builtins in the tutorial:
<https://www.roc-lang.org/tutorial#builtin-modules>
"
)
);
}
#[test] #[test]
fn explicit_builtin_type_import() { fn explicit_builtin_type_import() {
let modules = vec![( let modules = vec![(

View file

@ -115,6 +115,15 @@ impl Symbol {
) )
} }
pub fn is_automatically_imported(self) -> bool {
let module_id = self.module_id();
module_id.is_automatically_imported()
&& Self::builtin_types_in_scope(module_id)
.iter()
.any(|(_, (s, _))| *s == self)
}
pub fn module_string<'a>(&self, interns: &'a Interns) -> &'a ModuleName { pub fn module_string<'a>(&self, interns: &'a Interns) -> &'a ModuleName {
interns interns
.module_ids .module_ids