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 370 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 let alias_location = SrcSpan { 123 start: import.name_location().end, 124 end: import.location.end, 125 }; 126 127 self.environment.references.register_type_reference( 128 type_info.module.clone(), 129 import.name.clone(), 130 imported_name, 131 import.name_location(), 132 ReferenceKind::Import(alias_location), 133 ); 134 135 if let Err(e) = self 136 .environment 137 .insert_type_constructor(imported_name.clone(), type_info) 138 { 139 self.problems.error(e); 140 } 141 } 142 143 fn register_unqualified_value( 144 &mut self, 145 import: &UnqualifiedImport, 146 module_name: EcoString, 147 module: &ModuleInterface, 148 ) { 149 let import_name = &import.name; 150 let location = import.location; 151 let used_name = import.as_name.as_ref().unwrap_or(&import.name); 152 153 // Register the unqualified import if it is a value 154 let variant = match module.get_importable_value(import_name) { 155 Some(value) => { 156 let implementations = value.variant.implementations(); 157 // Check the target support of the imported value 158 if self.environment.target_support.is_enforced() 159 && !implementations.supports(self.environment.target) 160 { 161 self.problems.error(Error::UnsupportedExpressionTarget { 162 target: self.environment.target, 163 location, 164 }); 165 } 166 167 self.environment.insert_variable( 168 used_name.clone(), 169 value.variant.clone(), 170 value.type_.clone(), 171 value.publicity, 172 value.deprecation.clone(), 173 ); 174 &value.variant 175 } 176 None => { 177 self.problems.error(Error::UnknownModuleValue { 178 location, 179 name: import_name.clone(), 180 module_name: module.name.clone(), 181 value_constructors: module.public_value_names(), 182 type_with_same_name: module.get_importable_type(import_name).is_some(), 183 context: crate::type_::error::ModuleValueUsageContext::UnqualifiedImport, 184 }); 185 return; 186 } 187 }; 188 189 match variant { 190 ValueConstructorVariant::Record { name, module, .. } => { 191 self.environment.names.named_constructor_in_scope( 192 module.clone(), 193 name.clone(), 194 used_name.clone(), 195 ); 196 self.environment.references.register_value( 197 used_name.clone(), 198 EntityKind::ImportedConstructor { 199 module: module_name, 200 }, 201 location, 202 Publicity::Private, 203 ); 204 205 let alias_location = SrcSpan { 206 start: import.name_location().end, 207 end: import.location.end, 208 }; 209 210 self.environment.references.register_value_reference( 211 module.clone(), 212 import_name.clone(), 213 used_name, 214 import.name_location(), 215 ReferenceKind::Import(alias_location), 216 ); 217 } 218 ValueConstructorVariant::ModuleConstant { module, .. } 219 | ValueConstructorVariant::ModuleFn { module, .. } => { 220 self.environment.references.register_value( 221 used_name.clone(), 222 EntityKind::ImportedValue { 223 module: module_name, 224 }, 225 location, 226 Publicity::Private, 227 ); 228 229 let alias_location = SrcSpan { 230 start: import.name_location().end, 231 end: import.location.end, 232 }; 233 234 self.environment.references.register_value_reference( 235 module.clone(), 236 import_name.clone(), 237 used_name, 238 import.name_location(), 239 ReferenceKind::Import(alias_location), 240 ); 241 } 242 ValueConstructorVariant::LocalVariable { .. } => {} 243 } 244 245 // Check if value already was imported 246 if let Some(previous) = self.environment.unqualified_imported_names.get(used_name) { 247 self.problems.error(Error::DuplicateImport { 248 location, 249 previous_location: *previous, 250 name: import_name.clone(), 251 }); 252 return; 253 } 254 255 // Register the name as imported so it can't be imported a 256 // second time in future 257 let _ = self 258 .environment 259 .unqualified_imported_names 260 .insert(used_name.clone(), location); 261 } 262 263 /// Check for invalid imports, such as `src` importing `test` or `dev`. 264 fn check_for_invalid_imports( 265 &mut self, 266 module_info: &ModuleInterface, 267 location: SrcSpan, 268 ) -> Result<(), Error> { 269 if self.origin.is_src() 270 && self 271 .environment 272 .dev_dependencies 273 .contains(&module_info.package) 274 { 275 return Err(Error::SrcImportingDevDependency { 276 importing_module: self.environment.current_module.clone(), 277 imported_module: module_info.name.clone(), 278 package: module_info.package.clone(), 279 location, 280 }); 281 } 282 283 let kind = match (self.origin, module_info.origin) { 284 // `src` cannot import `test` or `dev` 285 (Origin::Src, Origin::Test) => InvalidImportKind::SrcImportingTest, 286 (Origin::Src, Origin::Dev) => InvalidImportKind::SrcImportingDev, 287 // `dev` cannot import `test` 288 (Origin::Dev, Origin::Test) => InvalidImportKind::DevImportingTest, 289 _ => return Ok(()), 290 }; 291 292 Err(Error::InvalidImport { 293 location, 294 importing_module: self.environment.current_module.clone(), 295 imported_module: module_info.name.clone(), 296 kind, 297 }) 298 } 299 300 fn register_module( 301 &mut self, 302 import: &UntypedImport, 303 import_info: &'context ModuleInterface, 304 ) -> Result<(), Error> { 305 let Some(used_name) = import.used_name() else { 306 return Ok(()); 307 }; 308 309 self.check_not_a_duplicate_import(&used_name, import.location)?; 310 311 if let Some(alias_location) = import.alias_location() { 312 self.environment.references.register_aliased_module( 313 used_name.clone(), 314 import.module.clone(), 315 alias_location, 316 import.location, 317 import.module_location, 318 ); 319 } else { 320 self.environment.references.register_module( 321 used_name.clone(), 322 import.module.clone(), 323 import.location, 324 import.module_location, 325 None, 326 ); 327 } 328 329 // Insert imported module into scope 330 let _ = self 331 .environment 332 .imported_modules 333 .insert(used_name.clone(), (import.location, import_info)); 334 335 // Register this module as being imported 336 // 337 // Emit a warning if the module had already been imported. 338 // This isn't an error so long as the modules have different local aliases. In Gleam v2 339 // this will likely become an error. 340 if let Some(previous) = self.environment.names.imported_module( 341 import.module.clone(), 342 used_name, 343 import.location, 344 ) { 345 self.problems.warning(Warning::ModuleImportedTwice { 346 name: import.module.clone(), 347 first: previous, 348 second: import.location, 349 }); 350 } 351 352 Ok(()) 353 } 354 355 fn check_not_a_duplicate_import( 356 &self, 357 used_name: &EcoString, 358 location: SrcSpan, 359 ) -> Result<(), Error> { 360 // Check if a module was already imported with this name 361 if let Some((previous_location, _)) = self.environment.imported_modules.get(used_name) { 362 return Err(Error::DuplicateImport { 363 location, 364 previous_location: *previous_location, 365 name: used_name.clone(), 366 }); 367 } 368 Ok(()) 369 } 370}