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
11 kB 321 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, 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, name.clone()) { 63 self.problems.error(e); 64 return; 65 } 66 67 if let Err(e) = self.register_module(import, module_info) { 68 self.problems.error(e); 69 return; 70 } 71 72 // Insert unqualified imports into scope 73 let module_name = &module_info.name; 74 for type_ in &import.unqualified_types { 75 self.register_unqualified_type(type_, module_name.clone(), module_info); 76 } 77 for value in &import.unqualified_values { 78 self.register_unqualified_value(value, module_name.clone(), module_info); 79 } 80 } 81 82 fn register_unqualified_type( 83 &mut self, 84 import: &UnqualifiedImport, 85 module_name: EcoString, 86 module: &ModuleInterface, 87 ) { 88 let imported_name = import.as_name.as_ref().unwrap_or(&import.name); 89 90 // Register the unqualified import if it is a type constructor 91 let Some(type_info) = module.get_public_type(&import.name) else { 92 // TODO: refine to a type specific error 93 self.problems.error(Error::UnknownModuleType { 94 location: import.location, 95 name: import.name.clone(), 96 module_name: module.name.clone(), 97 type_constructors: module.public_type_names(), 98 value_with_same_name: module.get_public_value(&import.name).is_some(), 99 }); 100 return; 101 }; 102 103 let type_info = type_info.clone().with_location(import.location); 104 105 self.environment.names.type_in_scope( 106 imported_name.clone(), 107 type_info.type_.as_ref(), 108 &type_info.parameters, 109 ); 110 111 self.environment.references.register_type( 112 imported_name.clone(), 113 EntityKind::ImportedType { 114 module: module_name, 115 }, 116 import.location, 117 Publicity::Private, 118 ); 119 120 self.environment.references.register_type_reference( 121 type_info.module.clone(), 122 import.name.clone(), 123 imported_name, 124 import.imported_name_location, 125 ReferenceKind::Import, 126 ); 127 128 if let Err(e) = self 129 .environment 130 .insert_type_constructor(imported_name.clone(), type_info) 131 { 132 self.problems.error(e); 133 } 134 } 135 136 fn register_unqualified_value( 137 &mut self, 138 import: &UnqualifiedImport, 139 module_name: EcoString, 140 module: &ModuleInterface, 141 ) { 142 let import_name = &import.name; 143 let location = import.location; 144 let used_name = import.as_name.as_ref().unwrap_or(&import.name); 145 146 // Register the unqualified import if it is a value 147 let variant = match module.get_public_value(import_name) { 148 Some(value) => { 149 let implementations = value.variant.implementations(); 150 // Check the target support of the imported value 151 if self.environment.target_support.is_enforced() 152 && !implementations.supports(self.environment.target) 153 { 154 self.problems.error(Error::UnsupportedExpressionTarget { 155 target: self.environment.target, 156 location, 157 }) 158 } 159 160 self.environment.insert_variable( 161 used_name.clone(), 162 value.variant.clone(), 163 value.type_.clone(), 164 value.publicity, 165 value.deprecation.clone(), 166 ); 167 &value.variant 168 } 169 None => { 170 self.problems.error(Error::UnknownModuleValue { 171 location, 172 name: import_name.clone(), 173 module_name: module.name.clone(), 174 value_constructors: module.public_value_names(), 175 type_with_same_name: module.get_public_type(import_name).is_some(), 176 context: crate::type_::error::ModuleValueUsageContext::UnqualifiedImport, 177 }); 178 return; 179 } 180 }; 181 182 match variant { 183 ValueConstructorVariant::Record { name, module, .. } => { 184 self.environment.names.named_constructor_in_scope( 185 module.clone(), 186 name.clone(), 187 used_name.clone(), 188 ); 189 self.environment.references.register_value( 190 used_name.clone(), 191 EntityKind::ImportedConstructor { 192 module: module_name, 193 }, 194 location, 195 Publicity::Private, 196 ); 197 198 self.environment.references.register_value_reference( 199 module.clone(), 200 import_name.clone(), 201 used_name, 202 import.imported_name_location, 203 ReferenceKind::Import, 204 ); 205 } 206 ValueConstructorVariant::ModuleConstant { module, .. } 207 | ValueConstructorVariant::ModuleFn { module, .. } => { 208 self.environment.references.register_value( 209 used_name.clone(), 210 EntityKind::ImportedValue { 211 module: module_name, 212 }, 213 location, 214 Publicity::Private, 215 ); 216 self.environment.references.register_value_reference( 217 module.clone(), 218 import_name.clone(), 219 used_name, 220 import.imported_name_location, 221 ReferenceKind::Import, 222 ); 223 } 224 _ => {} 225 }; 226 227 // Check if value already was imported 228 if let Some(previous) = self.environment.unqualified_imported_names.get(used_name) { 229 self.problems.error(Error::DuplicateImport { 230 location, 231 previous_location: *previous, 232 name: import_name.clone(), 233 }); 234 return; 235 } 236 237 // Register the name as imported so it can't be imported a 238 // second time in future 239 let _ = self 240 .environment 241 .unqualified_imported_names 242 .insert(used_name.clone(), location); 243 } 244 245 /// Check for invalid imports, such as `src` importing `test` or `dev`. 246 fn check_for_invalid_imports( 247 &mut self, 248 module_info: &ModuleInterface, 249 location: SrcSpan, 250 imported_module: EcoString, 251 ) -> Result<(), Error> { 252 let kind = match (self.origin, module_info.origin) { 253 // `src` cannot import `test` or `dev` 254 (Origin::Src, Origin::Test) => InvalidImportKind::SrcImportingTest, 255 (Origin::Src, Origin::Dev) => InvalidImportKind::SrcImportingDev, 256 // `dev` cannot import `test` 257 (Origin::Dev, Origin::Test) => InvalidImportKind::DevImportingTest, 258 _ => return Ok(()), 259 }; 260 261 Err(Error::InvalidImport { 262 location, 263 importing_module: self.environment.current_module.clone(), 264 imported_module, 265 kind, 266 }) 267 } 268 269 fn register_module( 270 &mut self, 271 import: &UntypedImport, 272 import_info: &'context ModuleInterface, 273 ) -> Result<(), Error> { 274 if let Some(used_name) = import.used_name() { 275 self.check_not_a_duplicate_import(&used_name, import.location)?; 276 277 if let Some(alias_location) = import.alias_location() { 278 self.environment.references.register_aliased_module( 279 used_name.clone(), 280 import.module.clone(), 281 alias_location, 282 import.location, 283 ); 284 } else { 285 self.environment.references.register_module( 286 used_name.clone(), 287 import.module.clone(), 288 import.location, 289 ); 290 } 291 292 // Insert imported module into scope 293 let _ = self 294 .environment 295 .imported_modules 296 .insert(used_name.clone(), (import.location, import_info)); 297 298 self.environment 299 .names 300 .imported_module(import.module.clone(), used_name); 301 }; 302 303 Ok(()) 304 } 305 306 fn check_not_a_duplicate_import( 307 &self, 308 used_name: &EcoString, 309 location: SrcSpan, 310 ) -> Result<(), Error> { 311 // Check if a module was already imported with this name 312 if let Some((previous_location, _)) = self.environment.imported_modules.get(used_name) { 313 return Err(Error::DuplicateImport { 314 location, 315 previous_location: *previous_location, 316 name: used_name.clone(), 317 }); 318 } 319 Ok(()) 320 } 321}