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