Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / src / analyse / imports.rs
12 kB 351 lines
1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2023 The Gleam contributors 3 4use ecow::EcoString; 5 6use crate::{ 7 ast::{Publicity, SrcSpan, UnqualifiedImport, UntypedImport}, 8 build::Origin, 9 reference::{EntityKind, ReferenceKind}, 10 type_::{ 11 Environment, Error, ModuleInterface, Problems, ValueConstructorVariant, Warning, 12 error::InvalidImportKind, 13 }, 14}; 15 16use super::Imported; 17 18#[derive(Debug)] 19pub struct Importer<'context, 'problems> { 20 origin: Origin, 21 environment: Environment<'context>, 22 problems: &'problems mut Problems, 23} 24 25impl<'context, 'problems> Importer<'context, 'problems> { 26 pub fn new( 27 origin: Origin, 28 environment: Environment<'context>, 29 problems: &'problems mut Problems, 30 ) -> Self { 31 Self { 32 origin, 33 environment, 34 problems, 35 } 36 } 37 38 pub fn run<'code>( 39 origin: Origin, 40 env: Environment<'context>, 41 imports: &'code [UntypedImport], 42 problems: &'problems mut Problems, 43 ) -> Environment<'context> { 44 let mut importer = Self::new(origin, env, problems); 45 for import in imports { 46 importer.register_import(import) 47 } 48 importer.environment 49 } 50 51 fn register_import(&mut self, import: &UntypedImport) { 52 let location = import.location; 53 let name = import.module.clone(); 54 55 // Find imported module 56 let Some(module_info) = self.environment.importable_modules.get(&name) else { 57 self.problems.error(Error::UnknownModule { 58 location, 59 name: name.clone(), 60 suggestions: self.environment.suggest_modules(&name, Imported::Module), 61 }); 62 return; 63 }; 64 65 if let Err(e) = self.check_for_invalid_imports(module_info, location) { 66 self.problems.error(e); 67 } 68 69 if let Err(e) = self.register_module(import, module_info) { 70 self.problems.error(e); 71 return; 72 } 73 74 // Insert unqualified imports into scope 75 let module_name = &module_info.name; 76 for type_ in &import.unqualified_types { 77 self.register_unqualified_type(type_, module_name.clone(), module_info); 78 } 79 for value in &import.unqualified_values { 80 self.register_unqualified_value(value, module_name.clone(), module_info); 81 } 82 } 83 84 fn register_unqualified_type( 85 &mut self, 86 import: &UnqualifiedImport, 87 module_name: EcoString, 88 module: &ModuleInterface, 89 ) { 90 let imported_name = import.as_name.as_ref().unwrap_or(&import.name); 91 92 // Register the unqualified import if it is a type constructor 93 let Some(type_info) = module.get_importable_type(&import.name) else { 94 // TODO: refine to a type specific error 95 self.problems.error(Error::UnknownModuleType { 96 location: import.location, 97 name: import.name.clone(), 98 module_name: module.name.clone(), 99 type_constructors: module.public_type_names(), 100 value_with_same_name: module.get_importable_value(&import.name).is_some(), 101 }); 102 return; 103 }; 104 105 let type_info = type_info.clone().with_location(import.location); 106 107 self.environment.names.type_in_scope( 108 imported_name.clone(), 109 type_info.type_.as_ref(), 110 &type_info.parameters, 111 ); 112 113 self.environment.references.register_type( 114 imported_name.clone(), 115 EntityKind::ImportedType { 116 module: module_name, 117 }, 118 import.location, 119 Publicity::Private, 120 ); 121 122 self.environment.references.register_type_reference( 123 type_info.module.clone(), 124 import.name.clone(), 125 imported_name, 126 import.imported_name_location, 127 ReferenceKind::Import, 128 ); 129 130 if let Err(e) = self 131 .environment 132 .insert_type_constructor(imported_name.clone(), type_info) 133 { 134 self.problems.error(e); 135 } 136 } 137 138 fn register_unqualified_value( 139 &mut self, 140 import: &UnqualifiedImport, 141 module_name: EcoString, 142 module: &ModuleInterface, 143 ) { 144 let import_name = &import.name; 145 let location = import.location; 146 let used_name = import.as_name.as_ref().unwrap_or(&import.name); 147 148 // Register the unqualified import if it is a value 149 let variant = match module.get_importable_value(import_name) { 150 Some(value) => { 151 let implementations = value.variant.implementations(); 152 // Check the target support of the imported value 153 if self.environment.target_support.is_enforced() 154 && !implementations.supports(self.environment.target) 155 { 156 self.problems.error(Error::UnsupportedExpressionTarget { 157 target: self.environment.target, 158 location, 159 }) 160 } 161 162 self.environment.insert_variable( 163 used_name.clone(), 164 value.variant.clone(), 165 value.type_.clone(), 166 value.publicity, 167 value.deprecation.clone(), 168 ); 169 &value.variant 170 } 171 None => { 172 self.problems.error(Error::UnknownModuleValue { 173 location, 174 name: import_name.clone(), 175 module_name: module.name.clone(), 176 value_constructors: module.public_value_names(), 177 type_with_same_name: module.get_importable_type(import_name).is_some(), 178 context: crate::type_::error::ModuleValueUsageContext::UnqualifiedImport, 179 }); 180 return; 181 } 182 }; 183 184 match variant { 185 ValueConstructorVariant::Record { name, module, .. } => { 186 self.environment.names.named_constructor_in_scope( 187 module.clone(), 188 name.clone(), 189 used_name.clone(), 190 ); 191 self.environment.references.register_value( 192 used_name.clone(), 193 EntityKind::ImportedConstructor { 194 module: module_name, 195 }, 196 location, 197 Publicity::Private, 198 ); 199 200 self.environment.references.register_value_reference( 201 module.clone(), 202 import_name.clone(), 203 used_name, 204 import.imported_name_location, 205 ReferenceKind::Import, 206 ); 207 } 208 ValueConstructorVariant::ModuleConstant { module, .. } 209 | ValueConstructorVariant::ModuleFn { module, .. } => { 210 self.environment.references.register_value( 211 used_name.clone(), 212 EntityKind::ImportedValue { 213 module: module_name, 214 }, 215 location, 216 Publicity::Private, 217 ); 218 self.environment.references.register_value_reference( 219 module.clone(), 220 import_name.clone(), 221 used_name, 222 import.imported_name_location, 223 ReferenceKind::Import, 224 ); 225 } 226 ValueConstructorVariant::LocalVariable { .. } => {} 227 }; 228 229 // Check if value already was imported 230 if let Some(previous) = self.environment.unqualified_imported_names.get(used_name) { 231 self.problems.error(Error::DuplicateImport { 232 location, 233 previous_location: *previous, 234 name: import_name.clone(), 235 }); 236 return; 237 } 238 239 // Register the name as imported so it can't be imported a 240 // second time in future 241 let _ = self 242 .environment 243 .unqualified_imported_names 244 .insert(used_name.clone(), location); 245 } 246 247 /// Check for invalid imports, such as `src` importing `test` or `dev`. 248 fn check_for_invalid_imports( 249 &mut self, 250 module_info: &ModuleInterface, 251 location: SrcSpan, 252 ) -> Result<(), Error> { 253 if self.origin.is_src() 254 && self 255 .environment 256 .dev_dependencies 257 .contains(&module_info.package) 258 { 259 return Err(Error::SrcImportingDevDependency { 260 importing_module: self.environment.current_module.clone(), 261 imported_module: module_info.name.clone(), 262 package: module_info.package.clone(), 263 location, 264 }); 265 } 266 267 let kind = match (self.origin, module_info.origin) { 268 // `src` cannot import `test` or `dev` 269 (Origin::Src, Origin::Test) => InvalidImportKind::SrcImportingTest, 270 (Origin::Src, Origin::Dev) => InvalidImportKind::SrcImportingDev, 271 // `dev` cannot import `test` 272 (Origin::Dev, Origin::Test) => InvalidImportKind::DevImportingTest, 273 _ => return Ok(()), 274 }; 275 276 Err(Error::InvalidImport { 277 location, 278 importing_module: self.environment.current_module.clone(), 279 imported_module: module_info.name.clone(), 280 kind, 281 }) 282 } 283 284 fn register_module( 285 &mut self, 286 import: &UntypedImport, 287 import_info: &'context ModuleInterface, 288 ) -> Result<(), Error> { 289 let Some(used_name) = import.used_name() else { 290 return Ok(()); 291 }; 292 293 self.check_not_a_duplicate_import(&used_name, import.location)?; 294 295 if let Some(alias_location) = import.alias_location() { 296 self.environment.references.register_aliased_module( 297 used_name.clone(), 298 import.module.clone(), 299 alias_location, 300 import.location, 301 ); 302 } else { 303 self.environment.references.register_module( 304 used_name.clone(), 305 import.module.clone(), 306 import.location, 307 ); 308 } 309 310 // Insert imported module into scope 311 let _ = self 312 .environment 313 .imported_modules 314 .insert(used_name.clone(), (import.location, import_info)); 315 316 // Register this module as being imported 317 // 318 // Emit a warning if the module had already been imported. 319 // This isn't an error so long as the modules have different local aliases. In Gleam v2 320 // this will likely become an error. 321 if let Some(previous) = self.environment.names.imported_module( 322 import.module.clone(), 323 used_name, 324 import.location, 325 ) { 326 self.problems.warning(Warning::ModuleImportedTwice { 327 name: import.module.clone(), 328 first: previous, 329 second: import.location, 330 }); 331 } 332 333 Ok(()) 334 } 335 336 fn check_not_a_duplicate_import( 337 &self, 338 used_name: &EcoString, 339 location: SrcSpan, 340 ) -> Result<(), Error> { 341 // Check if a module was already imported with this name 342 if let Some((previous_location, _)) = self.environment.imported_modules.get(used_name) { 343 return Err(Error::DuplicateImport { 344 location, 345 previous_location: *previous_location, 346 name: used_name.clone(), 347 }); 348 } 349 Ok(()) 350 } 351}