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

Configure Feed

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

Add warning for importing a dev dependency from `src`

+115 -5
+5 -1
compiler-core/src/analyse.rs
··· 43 43 use itertools::Itertools; 44 44 use name::{check_argument_names, check_name_case}; 45 45 use std::{ 46 - collections::HashMap, 46 + collections::{HashMap, HashSet}, 47 47 ops::Deref, 48 48 sync::{Arc, OnceLock}, 49 49 }; ··· 146 146 pub importable_modules: &'a im::HashMap<EcoString, ModuleInterface>, 147 147 pub warnings: &'a TypeWarningEmitter, 148 148 pub direct_dependencies: &'a HashMap<EcoString, A>, 149 + pub dev_dependencies: &'a HashSet<EcoString>, 149 150 pub target_support: TargetSupport, 150 151 pub package_config: &'a PackageConfig, 151 152 } ··· 167 168 importable_modules: self.importable_modules, 168 169 warnings: self.warnings, 169 170 direct_dependencies: self.direct_dependencies, 171 + dev_dependencies: self.dev_dependencies, 170 172 target_support: self.target_support, 171 173 package_config: self.package_config, 172 174 line_numbers, ··· 189 191 importable_modules: &'a im::HashMap<EcoString, ModuleInterface>, 190 192 warnings: &'a TypeWarningEmitter, 191 193 direct_dependencies: &'a HashMap<EcoString, A>, 194 + dev_dependencies: &'a HashSet<EcoString>, 192 195 target_support: TargetSupport, 193 196 package_config: &'a PackageConfig, 194 197 line_numbers: LineNumbers, ··· 224 227 importable_modules: self.importable_modules, 225 228 target_support: self.target_support, 226 229 current_origin: self.origin, 230 + dev_dependencies: self.dev_dependencies, 227 231 } 228 232 .build(); 229 233
+19 -3
compiler-core/src/analyse/imports.rs
··· 59 59 return; 60 60 }; 61 61 62 - if let Err(e) = self.check_for_invalid_imports(module_info, location, name.clone()) { 62 + if let Err(e) = self.check_for_invalid_imports(module_info, location) { 63 63 self.problems.error(e); 64 64 return; 65 65 } ··· 247 247 &mut self, 248 248 module_info: &ModuleInterface, 249 249 location: SrcSpan, 250 - imported_module: EcoString, 251 250 ) -> Result<(), Error> { 251 + // Files in `src` are not allowed to import dev dependencies. We can't 252 + // raise an error here, as that would be a breaking change, so we must 253 + // raise a warning instead. 254 + if self.origin.is_src() 255 + && self 256 + .environment 257 + .dev_dependencies 258 + .contains(&module_info.package) 259 + { 260 + self.problems.warning(Warning::SrcImportingDevDependency { 261 + importing_module: self.environment.current_module.clone(), 262 + imported_module: module_info.name.clone(), 263 + package: module_info.package.clone(), 264 + location, 265 + }); 266 + } 267 + 252 268 let kind = match (self.origin, module_info.origin) { 253 269 // `src` cannot import `test` or `dev` 254 270 (Origin::Src, Origin::Test) => InvalidImportKind::SrcImportingTest, ··· 261 277 Err(Error::InvalidImport { 262 278 location, 263 279 importing_module: self.environment.current_module.clone(), 264 - imported_module, 280 + imported_module: module_info.name.clone(), 265 281 kind, 266 282 }) 267 283 }
+4
compiler-core/src/ast/tests.rs
··· 50 50 importable_modules: &modules, 51 51 warnings: &TypeWarningEmitter::null(), 52 52 direct_dependencies: &std::collections::HashMap::new(), 53 + dev_dependencies: &std::collections::HashSet::new(), 53 54 target_support: TargetSupport::Enforced, 54 55 package_config: &config, 55 56 } ··· 76 77 // to have one place where we create all this required state for use in each 77 78 // place. 78 79 let _ = modules.insert(PRELUDE_MODULE_NAME.into(), type_::build_prelude(&ids)); 80 + let dev_dependencies = std::collections::HashSet::new(); 81 + 79 82 let mut environment = EnvironmentArguments { 80 83 ids, 81 84 current_package: "thepackage".into(), ··· 85 88 importable_modules: &modules, 86 89 target_support: TargetSupport::Enforced, 87 90 current_origin: Origin::Src, 91 + dev_dependencies: &dev_dependencies, 88 92 } 89 93 .build(); 90 94
+2
compiler-core/src/build/package_compiler.rs
··· 511 511 ) -> Outcome<Vec<Module>, Error> { 512 512 let mut modules = Vec::with_capacity(parsed_modules.len() + 1); 513 513 let direct_dependencies = package_config.dependencies_for(mode).expect("Package deps"); 514 + let dev_dependencies = package_config.dev_dependencies.keys().cloned().collect(); 514 515 515 516 // Insert the prelude 516 517 // DUPE: preludeinsertion ··· 542 543 importable_modules: module_types, 543 544 warnings: &TypeWarningEmitter::new(path.clone(), code.clone(), warnings.clone()), 544 545 direct_dependencies: &direct_dependencies, 546 + dev_dependencies: &dev_dependencies, 545 547 target_support, 546 548 package_config, 547 549 }
+2
compiler-core/src/erlang/tests.rs
··· 77 77 importable_modules: &modules, 78 78 warnings: &TypeWarningEmitter::null(), 79 79 direct_dependencies: &std::collections::HashMap::new(), 80 + dev_dependencies: &std::collections::HashSet::new(), 80 81 target_support: TargetSupport::NotEnforced, 81 82 package_config: &dep_config, 82 83 } ··· 100 101 importable_modules: &modules, 101 102 warnings: &TypeWarningEmitter::null(), 102 103 direct_dependencies: &direct_dependencies, 104 + dev_dependencies: &std::collections::HashSet::new(), 103 105 target_support: TargetSupport::NotEnforced, 104 106 package_config: &config, 105 107 }
+2
compiler-core/src/javascript/tests.rs
··· 155 155 importable_modules: &modules, 156 156 warnings: &TypeWarningEmitter::null(), 157 157 direct_dependencies: &std::collections::HashMap::new(), 158 + dev_dependencies: &std::collections::HashSet::new(), 158 159 target_support: TargetSupport::Enforced, 159 160 package_config: &dep_config, 160 161 } ··· 180 181 importable_modules: &modules, 181 182 warnings: &TypeWarningEmitter::null(), 182 183 direct_dependencies: &direct_dependencies, 184 + dev_dependencies: &std::collections::HashSet::new(), 183 185 target_support: TargetSupport::NotEnforced, 184 186 package_config: &config, 185 187 }
+2
compiler-core/src/package_interface/tests.rs
··· 88 88 importable_modules: &modules, 89 89 warnings: &TypeWarningEmitter::null(), 90 90 direct_dependencies: &std::collections::HashMap::new(), 91 + dev_dependencies: &std::collections::HashSet::new(), 91 92 target_support: TargetSupport::Enforced, 92 93 package_config: &config, 93 94 } ··· 115 116 importable_modules: &modules, 116 117 warnings: &TypeWarningEmitter::null(), 117 118 direct_dependencies: &direct_dependencies, 119 + dev_dependencies: &std::collections::HashSet::new(), 118 120 target_support: TargetSupport::Enforced, 119 121 package_config: &config, 120 122 }
+5
compiler-core/src/type_/environment.rs
··· 22 22 pub importable_modules: &'a im::HashMap<EcoString, ModuleInterface>, 23 23 pub target_support: TargetSupport, 24 24 pub current_origin: Origin, 25 + pub dev_dependencies: &'a HashSet<EcoString>, 25 26 } 26 27 27 28 impl<'a> EnvironmentArguments<'a> { ··· 90 91 pub echo_found: bool, 91 92 92 93 pub references: ReferenceTracker, 94 + 95 + pub dev_dependencies: &'a HashSet<EcoString>, 93 96 } 94 97 95 98 impl<'a> Environment<'a> { ··· 103 106 importable_modules, 104 107 target_support, 105 108 current_origin: origin, 109 + dev_dependencies, 106 110 }: EnvironmentArguments<'a>, 107 111 ) -> Self { 108 112 let prelude = importable_modules ··· 146 150 module_type_aliases: HashMap::new(), 147 151 echo_found: false, 148 152 references: ReferenceTracker::new(), 153 + dev_dependencies, 149 154 } 150 155 } 151 156 }
+13 -1
compiler-core/src/type_/error.rs
··· 1053 1053 location: SrcSpan, 1054 1054 outcome: ComparisonOutcome, 1055 1055 }, 1056 + 1057 + SrcImportingDevDependency { 1058 + importing_module: EcoString, 1059 + imported_module: EcoString, 1060 + package: EcoString, 1061 + location: SrcSpan, 1062 + }, 1056 1063 } 1057 1064 1058 1065 #[derive(Debug, Eq, Copy, PartialEq, Clone, serde::Serialize, serde::Deserialize)] ··· 1282 1289 | Warning::ModuleImportedTwice { 1283 1290 second: location, .. 1284 1291 } 1285 - | Warning::RedundantComparison { location, .. } => *location, 1292 + | Warning::RedundantComparison { location, .. } 1293 + | Warning::SrcImportingDevDependency { location, .. } => *location, 1286 1294 } 1287 1295 } 1288 1296 ··· 1291 1299 Self::Todo { .. } => true, 1292 1300 _ => false, 1293 1301 } 1302 + } 1303 + 1304 + pub(crate) fn imports_dev_dependency(&self) -> bool { 1305 + matches!(self, Self::SrcImportingDevDependency { .. }) 1294 1306 } 1295 1307 } 1296 1308
+5
compiler-core/src/type_/tests.rs
··· 352 352 // place. 353 353 let _ = modules.insert(PRELUDE_MODULE_NAME.into(), build_prelude(&ids)); 354 354 let mut problems = Problems::new(); 355 + let dev_dependencies = HashSet::new(); 355 356 let mut environment = EnvironmentArguments { 356 357 ids, 357 358 current_package: "thepackage".into(), ··· 361 362 importable_modules: &modules, 362 363 target_support: TargetSupport::Enforced, 363 364 current_origin: Origin::Src, 365 + dev_dependencies: &dev_dependencies, 364 366 } 365 367 .build(); 366 368 let res = ExprTyper::new( ··· 487 489 importable_modules: &modules, 488 490 warnings: &TypeWarningEmitter::null(), 489 491 direct_dependencies: &HashMap::new(), 492 + dev_dependencies: &HashSet::new(), 490 493 target_support, 491 494 package_config: &config, 492 495 } ··· 515 518 importable_modules: &modules, 516 519 warnings: &warnings, 517 520 direct_dependencies: &direct_dependencies, 521 + dev_dependencies: &HashSet::new(), 518 522 target_support: TargetSupport::Enforced, 519 523 package_config: &config, 520 524 } ··· 759 763 importable_modules: &modules, 760 764 warnings: &TypeWarningEmitter::null(), 761 765 direct_dependencies: &direct_dependencies, 766 + dev_dependencies: &HashSet::new(), 762 767 target_support: TargetSupport::Enforced, 763 768 package_config: &config, 764 769 }
+33
compiler-core/src/warning.rs
··· 1353 1353 extra_labels: vec![], 1354 1354 }), 1355 1355 }, 1356 + 1357 + type_::Warning::SrcImportingDevDependency { 1358 + location, 1359 + importing_module, 1360 + imported_module, 1361 + package, 1362 + } => { 1363 + let text = wrap(&format!( 1364 + "The application module `{importing_module}` is \ 1365 + importing the module `{imported_module}`, but `{package}`, the package it \ 1366 + belongs to, is a dev dependency. 1367 + 1368 + Dev dependencies are not included in production builds so application \ 1369 + modules should not import them. Perhaps change `{package}` to a regular dependency. 1370 + 1371 + In a future version of Gleam this may become a compile error." 1372 + )); 1373 + Diagnostic { 1374 + title: "App importing dev dependency".into(), 1375 + text, 1376 + hint: None, 1377 + level: diagnostic::Level::Warning, 1378 + location: Some(Location { 1379 + src: src.clone(), 1380 + path: path.to_path_buf(), 1381 + label: diagnostic::Label { 1382 + text: None, 1383 + span: *location, 1384 + }, 1385 + extra_labels: Vec::new(), 1386 + }), 1387 + } 1388 + } 1356 1389 }, 1357 1390 1358 1391 Warning::DeprecatedEnvironmentVariable { variable } => {
+6
test-package-compiler/cases/src_importing_dev_dependency/gleam.toml
··· 1 + name = "importy" 2 + version = "0.1.0" 3 + target = "erlang" 4 + 5 + [dev-dependencies] 6 + gleam_stdlib = ">= 0.62.0 and < 2.0.0"
+6
test-package-compiler/cases/src_importing_dev_dependency/src/one.gleam
··· 1 + // This should raise a warning as `gleam_stdlib` is a dev dependency. 2 + import gleam/io 3 + 4 + pub fn main() { 5 + io.println("Hello, world!") 6 + }
+11
test-package-compiler/src/generated_tests.rs
··· 311 311 312 312 #[rustfmt::skip] 313 313 #[test] 314 + fn src_importing_dev_dependency() { 315 + let output = crate::prepare("./cases/src_importing_dev_dependency"); 316 + insta::assert_snapshot!( 317 + "src_importing_dev_dependency", 318 + output, 319 + "./cases/src_importing_dev_dependency", 320 + ); 321 + } 322 + 323 + #[rustfmt::skip] 324 + #[test] 314 325 fn src_importing_test() { 315 326 let output = crate::prepare("./cases/src_importing_test"); 316 327 insta::assert_snapshot!(