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

Configure Feed

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

1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2024 The Gleam contributors 3 4use std::time::SystemTime; 5 6use camino::Utf8PathBuf; 7use ecow::EcoString; 8use globset::GlobBuilder; 9use hexpm::version::Identifier; 10 11use crate::{ 12 analyse::TargetSupport, 13 build::{Module, Origin, Package, Target}, 14 config::{Docs, ErlangConfig, GleamVersion, JavaScriptConfig, PackageConfig}, 15 line_numbers::LineNumbers, 16 type_::PRELUDE_MODULE_NAME, 17 uid::UniqueIdGenerator, 18 warning::{TypeWarningEmitter, WarningEmitter}, 19}; 20 21use super::PackageInterface; 22 23#[macro_export] 24macro_rules! assert_package_interface_with_name { 25 ($module_name:expr, $src:expr) => { 26 let output = 27 $crate::package_interface::tests::compile_package(Some($module_name), $src, None); 28 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 29 }; 30} 31 32#[macro_export] 33macro_rules! assert_package_interface { 34 (($dep_package:expr, $dep_name:expr, $dep_src:expr), $src:expr $(,)?) => {{ 35 let output = $crate::package_interface::tests::compile_package( 36 None, 37 $src, 38 Some(($dep_package, $dep_name, $dep_src)), 39 ); 40 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 41 }}; 42 43 (($dep_package:expr, $dep_name:expr, $dep_src:expr), $src:expr $(,)?) => {{ 44 let output = $crate::package_interface::tests::compile_package( 45 None, 46 $src, 47 Some(($dep_package, $dep_name, $dep_src)), 48 ); 49 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 50 }}; 51 52 ($src:expr) => {{ 53 let output = $crate::package_interface::tests::compile_package(None, $src, None); 54 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 55 }}; 56} 57 58pub fn compile_package( 59 module_name: Option<&str>, 60 src: &str, 61 dep: Option<(&str, &str, &str)>, 62) -> String { 63 let mut modules = im::HashMap::new(); 64 let ids = UniqueIdGenerator::new(); 65 // DUPE: preludeinsertion 66 // TODO: Currently we do this here and also in the tests. It would be better 67 // to have one place where we create all this required state for use in each 68 // place. 69 let _ = modules.insert( 70 PRELUDE_MODULE_NAME.into(), 71 crate::type_::build_prelude(&ids), 72 ); 73 let mut direct_dependencies = std::collections::HashMap::from_iter(vec![]); 74 if let Some((dep_package, dep_name, dep_src)) = dep { 75 let parsed = crate::parse::parse_module( 76 Utf8PathBuf::from("test/path"), 77 dep_src, 78 &WarningEmitter::null(), 79 ) 80 .expect("dep syntax error"); 81 let mut ast = parsed.module; 82 ast.name = dep_name.into(); 83 let line_numbers = LineNumbers::new(dep_src); 84 let mut config = PackageConfig::default(); 85 config.name = dep_package.into(); 86 87 let dep = crate::analyse::ModuleAnalyzerConstructor::<()> { 88 target: Target::Erlang, 89 ids: &ids, 90 origin: Origin::Src, 91 importable_modules: &modules, 92 warnings: &TypeWarningEmitter::null(), 93 direct_dependencies: &std::collections::HashMap::new(), 94 dev_dependencies: &std::collections::HashSet::new(), 95 target_support: TargetSupport::Enforced, 96 package_config: &config, 97 } 98 .infer_module(ast, line_numbers, "".into()) 99 .expect("should successfully infer"); 100 let _ = modules.insert(dep_name.into(), dep.type_info); 101 let _ = direct_dependencies.insert(dep_package.into(), ()); 102 } 103 let parsed = 104 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) 105 .expect("syntax error"); 106 107 let mut ast = parsed.module; 108 let module_name = module_name 109 .map(EcoString::from) 110 .unwrap_or("my/module".into()); 111 112 ast.name = module_name.clone(); 113 let mut config = PackageConfig::default(); 114 config.name = "my_package".into(); 115 let ast = crate::analyse::ModuleAnalyzerConstructor { 116 target: Target::Erlang, 117 ids: &ids, 118 origin: Origin::Src, 119 importable_modules: &modules, 120 warnings: &TypeWarningEmitter::null(), 121 direct_dependencies: &direct_dependencies, 122 dev_dependencies: &std::collections::HashSet::new(), 123 target_support: TargetSupport::Enforced, 124 package_config: &config, 125 } 126 .infer_module(ast, LineNumbers::new(src), "".into()) 127 .expect("should successfully infer"); 128 129 // TODO: all the bits above are basically copy pasted from the javascript 130 // and erlang test helpers. A refactor might be due here. 131 let mut module = Module { 132 name: module_name, 133 code: src.into(), 134 mtime: SystemTime::UNIX_EPOCH, 135 input_path: "wibble".into(), 136 origin: Origin::Src, 137 ast, 138 extra: parsed.extra, 139 dependencies: vec![], 140 }; 141 module.attach_doc_and_module_comments(); 142 let package: Package = package_from_module(module); 143 serde_json::to_string_pretty(&PackageInterface::from_package( 144 &package, 145 &im::HashMap::new(), 146 )) 147 .expect("to json") 148} 149 150fn package_from_module(module: Module) -> Package { 151 Package { 152 config: PackageConfig { 153 name: "my_package".into(), 154 version: hexpm::version::Version { 155 major: 11, 156 minor: 10, 157 patch: 9, 158 pre: vec![ 159 Identifier::Numeric(1), 160 Identifier::AlphaNumeric("wibble".into()), 161 ], 162 build: Some("build".into()), 163 }, 164 gleam_version: Some(GleamVersion::new("1.0.0".to_string()).unwrap()), 165 licences: vec![], 166 description: "description".into(), 167 documentation: Docs { pages: vec![] }, 168 dependencies: std::collections::HashMap::new(), 169 dev_dependencies: std::collections::HashMap::new(), 170 repository: None, 171 links: vec![], 172 erlang: ErlangConfig::default(), 173 javascript: JavaScriptConfig::default(), 174 target: Target::Erlang, 175 internal_modules: Some(vec![ 176 GlobBuilder::new("internals/*") 177 .build() 178 .expect("internals glob"), 179 ]), 180 }, 181 cached_module_names: Vec::new(), 182 modules: vec![module], 183 } 184} 185 186#[test] 187pub fn package_documentation_is_included() { 188 assert_package_interface!( 189 " 190//// Some package 191//// documentation! 192 193pub fn main() { 1 } 194" 195 ); 196} 197 198#[test] 199pub fn private_definitions_are_not_included() { 200 assert_package_interface!( 201 " 202const float = 1.1 203fn main() {} 204type Wibble 205type Wob = Int 206" 207 ); 208} 209 210#[test] 211pub fn internal_definitions_are_not_included() { 212 assert_package_interface!( 213 " 214@internal pub const float = 1.1 215@internal pub fn main() {} 216@internal pub type Wibble 217@internal pub type Wobble = Int 218" 219 ); 220} 221 222#[test] 223pub fn opaque_constructors_are_not_exposed() { 224 assert_package_interface!("pub opaque type Wibble { Wob }") 225} 226 227#[test] 228pub fn type_aliases() { 229 assert_package_interface!("pub type Wibble(a) = List(a)") 230} 231 232#[test] 233pub fn type_definition() { 234 assert_package_interface!( 235 " 236/// Wibble's documentation 237pub type Wibble(a, b) { 238 Wibble 239 Wobble 240} 241" 242 ) 243} 244 245#[test] 246pub fn prelude_types() { 247 assert_package_interface!( 248 r#" 249pub const float = 1.1 250pub const string = "" 251pub const int = 1 252pub const bool = True 253"# 254 ); 255} 256 257#[test] 258pub fn generic_function() { 259 assert_package_interface!( 260 r#" 261pub type Wob(a) { Wob } 262@deprecated("deprecation message") 263pub fn main() { Wob } 264"# 265 ); 266} 267 268#[test] 269pub fn imported_type() { 270 assert_package_interface!( 271 ("other_package", "other_module", "pub type Element(a)"), 272 r#" 273import other_module.{type Element} 274pub fn main() -> Element(Int) {} 275"# 276 ); 277} 278 279#[test] 280pub fn imported_aliased_type_keeps_original_name() { 281 assert_package_interface!( 282 ("other_package", "other_module", "pub type Element(a)"), 283 r#" 284import other_module.{type Element as Alias} as module_alias 285pub fn main() -> Alias(module_alias.Element(a)) {} 286"# 287 ); 288} 289 290#[test] 291pub fn multiple_type_variables() { 292 assert_package_interface!( 293 r#" 294pub type Box(a, b) 295pub fn some_type_variables(a: a, b: b, c: Box(c, d)) -> Box(a, d) {} 296"# 297 ); 298} 299 300#[test] 301pub fn type_constructors() { 302 assert_package_interface!( 303 r#" 304pub type Box(a, b) { 305 Box(b, Int) 306 OtherBox(message: String, a: a) 307} 308"# 309 ); 310} 311 312#[test] 313pub fn internal_modules_are_not_exported() { 314 assert_package_interface_with_name!("internals/internal_module", "pub fn main() { 1 }"); 315} 316 317#[test] 318pub fn labelled_function_parameters() { 319 assert_package_interface!( 320 r#" 321pub fn fold(list: List(a), from acc: b, with f: fn(a, b) -> b) -> b { 322 todo 323} 324"# 325 ); 326} 327 328#[test] 329pub fn constructors_with_documentation() { 330 assert_package_interface!( 331 r#" 332pub type Wibble { 333 /// This is the Wibble variant. It contains some example data. 334 Wibble(Int) 335 /// This is the Wobble variant. It is a recursive type. 336 Wobble(Wibble) 337} 338"# 339 ); 340} 341 342// https://github.com/gleam-lang/gleam/issues/5957 343#[test] 344pub fn type_alias_with_two_parameters() { 345 assert_package_interface!("pub type Wibble(a, b) = Result(a, b)"); 346} 347 348// https://github.com/gleam-lang/gleam/issues/5957 349#[test] 350pub fn type_alias_with_two_parameters_in_reverse() { 351 assert_package_interface!("pub type Wibble(a, b) = Result(b, a)"); 352}