diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6c4df39b1..15246531d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -58,12 +58,13 @@ jobs: use-cross: true run-integration-tests: false # Cannot run aarch64 binaries on x86_64 supports-nix: true - - os: macos-12 # intel + # macos>=14 runs exclusively on aarch64 and will thus fail to execute properly for x64 + - os: macos-13 # intel target: x86_64-apple-darwin use-cross: false run-integration-tests: true supports-nix: true - - os: macos-14 # aarch64 + - os: macos-latest # aarch64 toolchain: stable target: aarch64-apple-darwin use-cross: false @@ -517,7 +518,7 @@ jobs: deno-version: v1.x - name: Install Bun - uses: oven-sh/setup-bun@v1 + uses: oven-sh/setup-bun@v2 - name: Install Erlang uses: erlef/setup-beam@v1 diff --git a/.github/workflows/release-containers.yaml b/.github/workflows/release-containers.yaml index 40506e43c..c463b21bd 100644 --- a/.github/workflows/release-containers.yaml +++ b/.github/workflows/release-containers.yaml @@ -96,7 +96,7 @@ jobs: mv glistix glistix-arm64 - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 33678b4be..672e5a677 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -32,10 +32,11 @@ jobs: - os: ubuntu-latest target: aarch64-unknown-linux-musl use-cross: true - - os: macos-latest + # macos>=14 runs exclusively on aarch64 and will thus fail to execute properly for x64 + - os: macos-13 target: x86_64-apple-darwin use-cross: false - - os: macos-11 + - os: macos-latest target: aarch64-apple-darwin use-cross: false - os: windows-latest @@ -108,3 +109,46 @@ jobs: ${{ env.ASSET }} ${{ env.ASSET }}.sha256 ${{ env.ASSET }}.sha512 + + build-release-wasm: + name: build-release-wasm + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: wasm32-unknown-unknown + profile: minimal + override: true + + - name: Install wasm-pack + run: curl -sSL https://rustwasm.github.io/wasm-pack/installer/init.sh | sh + + - name: Build wasm + run: wasm-pack build --release --target web compiler-wasm + + - name: Build wasm archive + run: | + VERSION="${GITHUB_REF#refs/tags/}" + ARCHIVE="glistix-$VERSION-browser.tar.gz" + + tar -C compiler-wasm/pkg/ -czvf $ARCHIVE . + + openssl dgst -r -sha256 -out "$ARCHIVE".sha256 "$ARCHIVE" + openssl dgst -r -sha512 -out "$ARCHIVE".sha512 "$ARCHIVE" + echo "ASSET=$ARCHIVE" >> $GITHUB_ENV + + - name: Upload release archive + uses: softprops/action-gh-release@v2 + with: + draft: true + prerelease: false + fail_on_unmatched_files: true + files: | + ${{ env.ASSET }} + ${{ env.ASSET }}.sha256 + ${{ env.ASSET }}.sha512 diff --git a/CHANGELOG.md b/CHANGELOG.md index 45c8f933b..db85a4dfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Gleam's Changelog -## v1.3.0 - Unreleased +## Unreleased ### Build tool @@ -11,19 +11,3 @@ ### Language Server ### Bug Fixes - - -## v1.2.1 - 2024-05-30 - -### Bug Fixes - -- Fixed a bug where the compiler could fail to detect modules that would clash - with Erlang modules. - ([Louis Pilfold](https://github.com/lpil)) - -- Fixed a bug where dependency version resolution could crash for certain - release candidate versions. - ([Marshall Bowers](https://github.com/maxdeviant)) - -- Fixed a bug where trailing comments would be moved out of a bit array. - ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) diff --git a/Cargo.lock b/Cargo.lock index 729d8e0c8..3e21aa2d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1188,9 +1188,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.38.0" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eab73f58e59ca6526037208f0e98851159ec1633cf17b6cd2e1f2c3fd5d53cc" +checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5" dependencies = [ "console", "lazy_static", @@ -2101,9 +2101,9 @@ dependencies = [ [[package]] name = "spdx" -version = "0.10.4" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ef1a0fa1e39ac22972c8db23ff89aea700ab96aa87114e1fb55937a631a0c9" +checksum = "47317bbaf63785b53861e1ae2d11b80d6b624211d42cb20efcd210ee6f8a14bc" dependencies = [ "smallvec", ] @@ -2294,9 +2294,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -2315,9 +2315,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -2579,9 +2579,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "vec1" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb60dcfffc189bfd4e2a81333c268619fee9db53da71bce2bcbd8e129c56936" +checksum = "eab68b56840f69efb0fefbe3ab6661499217ffdc58e2eef7c3f6f69835386322" [[package]] name = "version_check" diff --git a/Makefile b/Makefile index a1e513d66..b5c2ccc19 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ javascript-prelude-test-watch: ## Run the JavaScript prelude core tests when fil .PHONY: test-watch test-watch: ## Run compiler tests when files change - watchexec --changes-only -e rs,toml,gleam,html,capnp "cargo test --quiet" + watchexec -e rs,toml,gleam,html,capnp "cargo test --quiet" .PHONY: export-hex-tarball-test export-hex-tarball-test: ## Run `gleam export hex-tarball` and verify it is created diff --git a/changelog/v1.3.md b/changelog/v1.3.md new file mode 100644 index 000000000..e26d2feec --- /dev/null +++ b/changelog/v1.3.md @@ -0,0 +1,238 @@ +# Changelog + +## v1.3.0 - 2024-07-09 + +## v1.3.0-rc3 - 2024-07-08 + +- Fixed a bug where not all pure function calls in constant definitions would be + annotated as `@__PURE__` when compiling to JavaScript. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +## v1.3.0-rc2 - 2024-07-06 + +### Formatter + +- Fixed a bug when multiple subjects in a case would be split even if not + necessary. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +## v1.3.0-rc1 - 2024-06-30 + +### Build tool + +- `gleam remove` will now present an error if a package being removed does not + exist as a dependency in the project. + ([Changfeng Lou](https://github.com/hnlcf)) + +- `gleam add` now takes an optional package version specifier, + separated by a `@`, that resolves as follows: + + ```shell + gleam add lustre@1.2.3 # "1.2.3" + gleam add lustre@1.2 # ">= 1.2.0 and < 2.0.0" + gleam add lustre@1 # ">= 1.0.0 and < 2.0.0" + ``` + + ([Rahul D. Ghosal](https://github.com/rdghosal)) + +### Compiler + +- Added more an informative error message for when attempting to use the `..` + syntax to append to a list rather than prepend. + + ``` + error: Syntax error + ┌─ /src/parse/error.gleam:4:14 + │ + 4 │ [..rest, last] -> 1 + │ ^^^^^^ I wasn't expecting elements after this + + Lists are immutable and singly-linked, so to match on the end + of a list would require the whole list to be traversed. This + would be slow, so there is no built-in syntax for it. Pattern + match on the start of the list instead. + ``` + + ([Antonio Iaccarino](https://github.com/eingin)) + +- The compiler now emits a warning for redundant function captures in a + pipeline: + + ``` + warning: Redundant function capture + ┌─ /src/warning/wrn.gleam:5:17 + │ + 5 │ 1 |> wibble(_, 2) |> wibble(2) + │ ^ You can safely remove this + + This function capture is redundant since the value is already piped as the + first argument of this call. + + See: https://tour.gleam.run/functions/pipelines/ + ``` + + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +- The syntax `[a..b]` is now deprecated in favour of the `[a, ..b]` syntax. + This was to avoid it being mistaken for a range syntax, which Gleam does + not have. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +- Functions etc named `maybe` are now escaped in generated Erlang as it is now a + reserved word in Erlang/OTP 27. + ([Jake Barszcz](https://github.com/barszcz)) + +- Functions, types and constructors named `maybe` and `else` are now + escaped in generated Erlang to avoid clashing with Erlang's keywords. + ([Jake Barszcz](https://github.com/barszcz)) and + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +- Non byte aligned arrays that use literals for size are now marked as an + Unsupported feature for Javascript since they would already cause + a runtime error on Javascript. + + This means if you compile specifically for Javascript you will now recieve + this error: + + ``` + error: Unsupported feature for compilation target + ┌─ /src/test/gleam_test.gleam:6:5 + │ + 6 │ <<1:size(5)>> + │ ^^^^^^^^^ + + Non byte aligned array is not supported for JavaScript compilation. + ``` + + Else any functions which rely on this will not be compiled into Javascript. + ([Pi-Cla](https://github.com/Pi-Cla)) + +- Compilation fault tolerance is now at a statement level instead of a function + level. This means that the compiler will attempt to infer the rest of the + statements in a function when there is an error in a previous one. + ([Ameen Radwan](https://github.com/Acepie)) + +- The JavaScript prelude is no-longer rewritten each time a project is compiled + to JavaScript. + ([Ofek Doitch](https://github.com/ofekd)) + +- Compiler now supports arithmetic operations in guards. + ([Danielle Maywood](https://github.com/DanielleMaywood)) + +- Import cycles now show the location where the import occur. + ([Ameen Radwan](https://github.com/Acepie)) + +- Error messages resulting from unexpected tokens now include information on + the found token's type. This updated message explains how the lexer handled + the token, so as to guide the user towards providing correct syntax. + + Following is an example, where the error message indicates that the name of + the provided field conflicts with a keyword: + + ``` + 3 │ A(type: String) + │ ^^^^ I was not expecting this + + Found the keyword `type`, expected one of: + - `)` + - a constructor argument name + ``` + + ([Rahul D. Ghosal](https://github.com/rdghosal)) + +- When compiling to JavaScript constants will now be annotated as `@__PURE__`. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +### Formatter + +### Language Server + +- The language server will now suggest the "Remove redundant tuple" action even + if the case expression contains some catch all patterns: + + ``` + case #(a, b) { + #(1, 2) -> todo + _ -> todo + } + ``` + + Becomes: + + ``` + case a, b { + 1, 2 -> todo + _, _ -> todo + } + ``` + + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +- LSP can now suggest completions for values and types from importable modules + and adds the import to the top of the file. + ([Ameen Radwan](https://github.com/Acepie)) + +- Diagnostics with extra labels now show the diagnostic in all locations + including across multiple files. + ([Ameen Radwan](https://github.com/Acepie)) + +- LSP completions now use the "text_edit" language server API resulting in + better/more accurate insertions. + ([Ameen Radwan](https://github.com/Acepie)) + +- Completions are no longer provided inside comments. + ([Nicky Lim](https://github.com/nicklimmm)) + +- The language server will now show all the ignored fields when hovering over + `..` in a record pattern. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +### Bug Fixes + +- Fixed a bug where the compiler would output a confusing error message when + trying to use the spread syntax to append to a list. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +- Fixed a bug where the formatter would strip empty lines out of the body of an + anonymous function passed as an argument. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +- Fixed a bug where the compiler would crash when a type was defined with + the same name as an imported type. + ([Gears](https://github.com/gearsdatapacks)) + +- Fixed a bug where a horizontal scrollbar would appear on code blocks in built + documentation when they contained lines 79 or 80 characters long. + ([Richard Viney](https://github.com/richard-viney)) + +- Fixed a bug where importing a record constructor in an unqualified fashion and + aliasing it and then using it in a constant expression would generate invalid + JavaScript. + ([Louis Pilfold](https://github.com/lpil)) + +- Fixed a bug where the compiler would crash because types weren't registered if + they referenced a non-existent type. + ([Gears](https://github.com/gearsdatapacks)) + +- Fixed a bug where the compiler would generate invalid Erlang when pattern + matching on strings with an `as` pattern. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) + +- Fixed a bug where the compiler would warn that a module alias was unused when + it was only used in case patterns. + ([Michael Jones](https://github.com/michaeljones)) + +## v1.2.1 - 2024-05-30 + +### Bug Fixes + +- Fixed a bug where the compiler could fail to detect modules that would clash + with Erlang modules. + ([Louis Pilfold](https://github.com/lpil)) + +- Fixed a bug where dependency version resolution could crash for certain + release candidate versions. + ([Marshall Bowers](https://github.com/maxdeviant)) + +- Fixed a bug where trailing comments would be moved out of a bit array. + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) diff --git a/compiler-cli/src/dependencies.rs b/compiler-cli/src/dependencies.rs index 18421c1dd..17d5c9a99 100644 --- a/compiler-cli/src/dependencies.rs +++ b/compiler-cli/src/dependencies.rs @@ -119,6 +119,129 @@ pub fn update() -> Result<()> { Ok(()) } +fn parse_hex_requirement(package: &str) -> Result { + match package.find('@') { + Some(pos) => { + // Parse the major and minor from the provided semantic version. + let version = match package.get(pos + 1..) { + Some(version) => Ok(version), + None => Err(Error::InvalidVersionFormat { + input: package.to_string(), + error: "Failed to parse version from specifier".to_string(), + }), + }?; + let parts = version.split('.').collect::>(); + let major = match parts.first() { + Some(major) => Ok(major), + None => Err(Error::InvalidVersionFormat { + input: package.to_string(), + error: "Failed to parse semantic major version".to_string(), + }), + }?; + let minor = match parts.get(1) { + Some(minor) => minor, + None => "0", + }; + + // Using the major version specifier, calculate the maximum + // allowable version (i.e., the next major version). + let max_ver = match major.parse::() { + Ok(num) => Ok([&(num + 1).to_string(), "0", "0"].join(".")), + Err(_) => Err(Error::InvalidVersionFormat { + input: version.to_string(), + error: "Failed to parse semantic major version as integer".to_string(), + }), + }?; + + // Pad the provided version specifier with zeros map to a Hex version. + match parts.len() { + 1 | 2 => { + let min_ver = [major, minor, "0"].join("."); + Ok(Requirement::hex( + &[">=", &min_ver, "and", "<", &max_ver].join(" "), + )) + } + 3 => Ok(Requirement::hex(version)), + n_parts => Err(Error::InvalidVersionFormat { + input: version.to_string(), + error: format!( + "Expected up to 3 numbers in version specifier (MAJOR.MINOR.PATCH), found {n_parts}" + ), + }), + } + } + + // Default to the latest version available. + None => Ok(Requirement::hex(">= 0.0.0")), + } +} + +#[test] +fn parse_hex_requirement_invalid_semver() { + assert!(parse_hex_requirement("some_package@1.2.3.4").is_err()); +} + +#[test] +fn parse_hex_requirement_non_numeric_version() { + assert!(parse_hex_requirement("some_package@not_a_version").is_err()); +} + +#[test] +fn parse_hex_requirement_default() { + let provided = "some_package"; + let expected = ">= 0.0.0"; + let version = parse_hex_requirement(&provided).unwrap(); + match &version { + Requirement::Hex { version: v } => { + assert!(v.to_pubgrub().is_ok(), "failed pubgrub parse: {}", v); + } + _ => assert!(false, "failed hexpm version parse: {}", provided), + } + assert_eq!(version, Requirement::hex(expected)) +} + +#[test] +fn parse_hex_requirement_major_only() { + let provided = "some_package@1"; + let expected = ">= 1.0.0 and < 2.0.0"; + let version = parse_hex_requirement(&provided).unwrap(); + match &version { + Requirement::Hex { version: v } => { + assert!(v.to_pubgrub().is_ok(), "failed pubgrub parse: {}", v); + } + _ => assert!(false, "failed hexpm version parse: {}", provided), + } + assert_eq!(version, Requirement::hex(expected)) +} + +#[test] +fn parse_hex_requirement_major_and_minor() { + let provided = "some_package@1.2"; + let expected = ">= 1.2.0 and < 2.0.0"; + let version = parse_hex_requirement(&provided).unwrap(); + match &version { + Requirement::Hex { version: v } => { + assert!(v.to_pubgrub().is_ok(), "failed pubgrub parse: {}", v); + } + _ => assert!(false, "failed hexpm version parse: {}", provided), + } + assert_eq!(version, Requirement::hex(expected)) +} + +#[test] +fn parse_hex_requirement_major_minor_and_patch() { + let provided = "some_package@1.2.3"; + let expected = "1.2.3"; + let version = parse_hex_requirement(&provided).unwrap(); + match &version { + Requirement::Hex { version: v } => { + assert!(v.to_pubgrub().is_ok(), "failed pubgrub parse: {}", v); + } + _ => assert!(false, "failed hexpm version parse: {}", provided), + } + assert_eq!(version, Requirement::hex(expected)) +} + pub fn download( paths: &ProjectPaths, telemetry: Telem, @@ -149,7 +272,7 @@ pub fn download( // Insert the new packages to add, if it exists if let Some((packages, dev)) = new_package { for package in packages { - let version = Requirement::hex(">= 0.0.0"); + let version = parse_hex_requirement(&package)?; let _ = if dev { config.dev_dependencies.insert(package.into(), version) } else { diff --git a/compiler-cli/src/fs.rs b/compiler-cli/src/fs.rs index 25ce65bdd..ad39049e6 100644 --- a/compiler-cli/src/fs.rs +++ b/compiler-cli/src/fs.rs @@ -181,6 +181,10 @@ impl FileSystemWriter for ProjectIO { fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), Error> { write_bytes(path, content) } + + fn exists(&self, path: &Utf8Path) -> bool { + path.exists() + } } impl CommandExecutor for ProjectIO { diff --git a/compiler-cli/src/new.rs b/compiler-cli/src/new.rs index 380b3dece..dc683bb9d 100644 --- a/compiler-cli/src/new.rs +++ b/compiler-cli/src/new.rs @@ -96,7 +96,7 @@ impl FileToCreate { [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/{project_name}/) ```sh -glistix add {project_name} +glistix add {project_name}@1 ``` ```gleam import {project_name} @@ -184,8 +184,8 @@ target = "nix" # # description = "" # licences = ["Apache-2.0"] -# repository = {{ type = "github", user = "username", repo = "project" }} -# links = [{{ title = "Website", href = "https://gleam.run" }}] +# repository = {{ type = "github", user = "", repo = "" }} +# links = [{{ title = "Website", href = "" }}] # # For a full reference of all the available options, you can have a look at # https://gleam.run/writing-gleam/gleam-toml/. diff --git a/compiler-cli/src/remove.rs b/compiler-cli/src/remove.rs index e67a77fbe..dc6931b85 100644 --- a/compiler-cli/src/remove.rs +++ b/compiler-cli/src/remove.rs @@ -19,15 +19,27 @@ pub fn command(packages: Vec) -> Result<()> { })?; // Remove the specified dependencies + let mut packages_not_exist = vec![]; for package_to_remove in packages.iter() { #[allow(clippy::indexing_slicing)] - let _ = toml["dependencies"] + let maybe_removed_item = toml["dependencies"] .as_table_like_mut() .and_then(|deps| deps.remove(package_to_remove)); + #[allow(clippy::indexing_slicing)] - let _ = toml["dev-dependencies"] + let maybe_removed_dev_item = toml["dev-dependencies"] .as_table_like_mut() .and_then(|deps| deps.remove(package_to_remove)); + + if maybe_removed_item.or(maybe_removed_dev_item).is_none() { + packages_not_exist.push(package_to_remove.into()); + } + } + + if !packages_not_exist.is_empty() { + return Err(Error::RemovedPackagesNotExist { + packages: packages_not_exist, + }); } // Write the updated config diff --git a/compiler-core/src/analyse.rs b/compiler-core/src/analyse.rs index 585202752..d669c171f 100644 --- a/compiler-core/src/analyse.rs +++ b/compiler-core/src/analyse.rs @@ -156,7 +156,6 @@ impl<'a, A> ModuleAnalyzerConstructor<'a, A> { line_numbers, src_path, errors: vec![], - type_names: HashMap::with_capacity(module.definitions.len()), value_names: HashMap::with_capacity(module.definitions.len()), hydrators: HashMap::with_capacity(module.definitions.len()), module_name: module.name.clone(), @@ -177,7 +176,6 @@ struct ModuleAnalyzer<'a, A> { line_numbers: LineNumbers, src_path: Utf8PathBuf, errors: Vec, - type_names: HashMap, value_names: HashMap, hydrators: HashMap, module_name: EcoString, @@ -468,8 +466,9 @@ impl<'a, A> ModuleAnalyzer<'a, A> { self.ensure_annotations_present(&arguments, return_annotation.as_ref(), location); } + let has_body = !body.first().is_placeholder(); let definition = FunctionDefinition { - has_body: !body.first().is_placeholder(), + has_body, has_erlang_external: external_erlang.is_some(), has_javascript_external: external_javascript.is_some(), has_nix_external: external_nix.is_some(), @@ -533,6 +532,9 @@ impl<'a, A> ModuleAnalyzer<'a, A> { && publicity.is_importable() && environment.target_support.is_enforced() && !implementations.supports(target) + // We don't emit this error if there is a body + // since this would be caught at the statement level + && !has_body { self.errors.push(Error::UnsupportedPublicFunctionTarget { name: name.clone(), @@ -898,7 +900,13 @@ impl<'a, A> ModuleAnalyzer<'a, A> { constructor.arguments.iter().enumerate() { // Build a type from the annotation AST - let t = hydrator.type_from_ast(ast, environment)?; + let t = match hydrator.type_from_ast(ast, environment) { + Ok(t) => t, + Err(e) => { + self.errors.push(e); + continue; + } + }; fields.push(TypeValueConstructorField { type_: t.clone() }); @@ -1003,7 +1011,7 @@ impl<'a, A> ModuleAnalyzer<'a, A> { // former. I think we want to really keep the former both times. // The fact we can't straightforwardly do this indicated to me that we // could improve our approach here somewhat. - self.assert_unique_type_name(name, *location)?; + environment.assert_unique_type_name(name, *location)?; let mut hydrator = Hydrator::new(); let parameters = self.make_type_vars(parameters, *location, &mut hydrator, environment); @@ -1073,7 +1081,7 @@ impl<'a, A> ModuleAnalyzer<'a, A> { } = t; // A type alias must not have the same name as any other type in the module. - if let Err(error) = self.assert_unique_type_name(name, *location) { + if let Err(error) = environment.assert_unique_type_name(name, *location) { self.errors.push(error); // A type already exists with the name so we cannot continue and // register this new type with the same name. @@ -1146,21 +1154,6 @@ impl<'a, A> ModuleAnalyzer<'a, A> { } } - fn assert_unique_type_name( - &mut self, - name: &EcoString, - location: SrcSpan, - ) -> Result<(), Error> { - match self.type_names.insert(name.clone(), location) { - Some(previous_location) => Err(Error::DuplicateTypeName { - name: name.clone(), - previous_location, - location, - }), - None => Ok(()), - } - } - fn register_value_from_function( &mut self, f: &UntypedFunction, diff --git a/compiler-core/src/ast.rs b/compiler-core/src/ast.rs index 2ad7f472e..af0aa298a 100644 --- a/compiler-core/src/ast.rs +++ b/compiler-core/src/ast.rs @@ -17,7 +17,6 @@ use crate::type_::expression::Implementations; use crate::type_::{ self, Deprecation, ModuleValueConstructor, PatternConstructor, Type, ValueConstructor, }; -use std::cmp::Ordering; use std::sync::Arc; use ecow::EcoString; @@ -108,6 +107,7 @@ impl UntypedModule { #[test] fn module_dependencies_test() { let parsed = crate::parse::parse_module( + camino::Utf8PathBuf::from("test/path"), "import one @target(erlang) import two @@ -116,6 +116,7 @@ fn module_dependencies_test() { import three import four", + &crate::warning::WarningEmitter::null(), ) .expect("syntax error"); let module = parsed.module; @@ -154,6 +155,13 @@ impl Arg { pub fn get_variable_name(&self) -> Option<&EcoString> { self.names.get_variable_name() } + + pub fn is_capture_hole(&self) -> bool { + match &self.names { + ArgNames::Named { name } if name == CAPTURE_VARIABLE => true, + _ => false, + } + } } impl TypedArg { @@ -452,9 +460,9 @@ impl Publicity { /// /// ```gleam /// // Public function -/// pub fn bar() -> String { ... } +/// pub fn wobble() -> String { ... } /// // Private function -/// fn foo(x: Int) -> Int { ... } +/// fn wibble(x: Int) -> Int { ... } /// ``` pub struct Function { pub location: SrcSpan, @@ -1175,6 +1183,60 @@ pub enum ClauseGuard { right: Box, }, + AddInt { + location: SrcSpan, + left: Box, + right: Box, + }, + + AddFloat { + location: SrcSpan, + left: Box, + right: Box, + }, + + SubInt { + location: SrcSpan, + left: Box, + right: Box, + }, + + SubFloat { + location: SrcSpan, + left: Box, + right: Box, + }, + + MultInt { + location: SrcSpan, + left: Box, + right: Box, + }, + + MultFloat { + location: SrcSpan, + left: Box, + right: Box, + }, + + DivInt { + location: SrcSpan, + left: Box, + right: Box, + }, + + DivFloat { + location: SrcSpan, + left: Box, + right: Box, + }, + + RemainderInt { + location: SrcSpan, + left: Box, + right: Box, + }, + Or { location: SrcSpan, left: Box, @@ -1243,6 +1305,15 @@ impl ClauseGuard { | ClauseGuard::GtFloat { location, .. } | ClauseGuard::GtEqFloat { location, .. } | ClauseGuard::LtFloat { location, .. } + | ClauseGuard::AddInt { location, .. } + | ClauseGuard::AddFloat { location, .. } + | ClauseGuard::SubInt { location, .. } + | ClauseGuard::SubFloat { location, .. } + | ClauseGuard::MultInt { location, .. } + | ClauseGuard::MultFloat { location, .. } + | ClauseGuard::DivInt { location, .. } + | ClauseGuard::DivFloat { location, .. } + | ClauseGuard::RemainderInt { location, .. } | ClauseGuard::FieldAccess { location, .. } | ClauseGuard::LtEqFloat { location, .. } | ClauseGuard::ModuleSelect { location, .. } => *location, @@ -1271,6 +1342,15 @@ impl ClauseGuard { ClauseGuard::GtEqFloat { .. } => Some(BinOp::GtEqFloat), ClauseGuard::LtFloat { .. } => Some(BinOp::LtFloat), ClauseGuard::LtEqFloat { .. } => Some(BinOp::LtEqFloat), + ClauseGuard::AddInt { .. } => Some(BinOp::AddInt), + ClauseGuard::AddFloat { .. } => Some(BinOp::AddFloat), + ClauseGuard::SubInt { .. } => Some(BinOp::SubInt), + ClauseGuard::SubFloat { .. } => Some(BinOp::SubFloat), + ClauseGuard::MultInt { .. } => Some(BinOp::MultInt), + ClauseGuard::MultFloat { .. } => Some(BinOp::MultFloat), + ClauseGuard::DivInt { .. } => Some(BinOp::DivInt), + ClauseGuard::DivFloat { .. } => Some(BinOp::DivFloat), + ClauseGuard::RemainderInt { .. } => Some(BinOp::RemainderInt), ClauseGuard::Constant(_) | ClauseGuard::Var { .. } @@ -1291,6 +1371,17 @@ impl TypedClauseGuard { ClauseGuard::ModuleSelect { type_, .. } => type_.clone(), ClauseGuard::Constant(constant) => constant.type_(), + ClauseGuard::AddInt { .. } + | ClauseGuard::SubInt { .. } + | ClauseGuard::MultInt { .. } + | ClauseGuard::DivInt { .. } + | ClauseGuard::RemainderInt { .. } => type_::int(), + + ClauseGuard::AddFloat { .. } + | ClauseGuard::SubFloat { .. } + | ClauseGuard::MultFloat { .. } + | ClauseGuard::DivFloat { .. } => type_::float(), + ClauseGuard::Or { .. } | ClauseGuard::Not { .. } | ClauseGuard::And { .. } @@ -1308,7 +1399,7 @@ impl TypedClauseGuard { } } -#[derive(Debug, PartialEq, Eq, Default, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Default, Clone, Copy, serde::Serialize, serde::Deserialize)] pub struct SrcSpan { pub start: u32, pub end: u32, @@ -1322,16 +1413,6 @@ impl SrcSpan { pub fn contains(&self, byte_index: u32) -> bool { byte_index >= self.start && byte_index < self.end } - - pub fn cmp_byte_index(&self, byte_index: u32) -> Ordering { - if byte_index < self.start { - Ordering::Less - } else if self.end <= byte_index { - Ordering::Greater - } else { - Ordering::Equal - } - } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -1408,7 +1489,7 @@ pub enum Pattern { arguments: Vec>, module: Option, constructor: Inferred, - with_spread: bool, + spread: Option, type_: Type, }, @@ -1432,6 +1513,13 @@ pub enum Pattern { /// The variable on the right hand side of the `<>`. right_side_assignment: AssignName, }, + + /// A placeholder pattern used to allow module analysis to continue + /// even when there are type errors. Should never end up in generated code. + Invalid { + location: SrcSpan, + type_: Type, + }, } impl Default for Inferred<()> { @@ -1482,7 +1570,8 @@ impl Pattern { | Pattern::Tuple { location, .. } | Pattern::Constructor { location, .. } | Pattern::StringPrefix { location, .. } - | Pattern::BitArray { location, .. } => *location, + | Pattern::BitArray { location, .. } + | Pattern::Invalid { location, .. } => *location, } } @@ -1507,7 +1596,8 @@ impl TypedPattern { | Pattern::List { .. } | Pattern::Tuple { .. } | Pattern::BitArray { .. } - | Pattern::StringPrefix { .. } => None, + | Pattern::StringPrefix { .. } + | Pattern::Invalid { .. } => None, Pattern::Constructor { constructor, .. } => constructor.definition_location(), } @@ -1525,7 +1615,8 @@ impl TypedPattern { | Pattern::List { .. } | Pattern::Tuple { .. } | Pattern::BitArray { .. } - | Pattern::StringPrefix { .. } => None, + | Pattern::StringPrefix { .. } + | Pattern::Invalid { .. } => None, Pattern::Constructor { constructor, .. } => constructor.get_documentation(), } @@ -1542,7 +1633,8 @@ impl TypedPattern { Pattern::Variable { type_, .. } | Pattern::List { type_, .. } | Pattern::VarUsage { type_, .. } - | Pattern::Constructor { type_, .. } => type_.clone(), + | Pattern::Constructor { type_, .. } + | Pattern::Invalid { type_, .. } => type_.clone(), Pattern::Assign { pattern, .. } => pattern.type_(), @@ -1573,11 +1665,21 @@ impl TypedPattern { | Pattern::Assign { .. } | Pattern::Discard { .. } | Pattern::BitArray { .. } - | Pattern::StringPrefix { .. } => Some(Located::Pattern(self)), + | Pattern::StringPrefix { .. } + | Pattern::Invalid { .. } => Some(Located::Pattern(self)), + + Pattern::Constructor { + arguments, spread, .. + } => match spread { + Some(spread_location) if spread_location.contains(byte_index) => { + Some(Located::PatternSpread { + spread_location: *spread_location, + arguments, + }) + } - Pattern::Constructor { arguments, .. } => { - arguments.iter().find_map(|arg| arg.find_node(byte_index)) - } + Some(_) | None => arguments.iter().find_map(|arg| arg.find_node(byte_index)), + }, Pattern::List { elements, tail, .. } => elements .iter() .find_map(|p| p.find_node(byte_index)) diff --git a/compiler-core/src/ast/tests.rs b/compiler-core/src/ast/tests.rs index 935ad5db5..bc28c1efb 100644 --- a/compiler-core/src/ast/tests.rs +++ b/compiler-core/src/ast/tests.rs @@ -1,11 +1,14 @@ use std::sync::Arc; +use camino::Utf8PathBuf; + use crate::analyse::TargetSupport; use crate::build::Target; use crate::config::PackageConfig; use crate::line_numbers::LineNumbers; use crate::type_::expression::FunctionDefinition; use crate::type_::{Deprecation, PRELUDE_MODULE_NAME}; +use crate::warning::WarningEmitter; use crate::{ ast::{SrcSpan, TypedExpr}, build::Located, @@ -21,7 +24,9 @@ use super::{Publicity, Statement, TypedModule, TypedStatement}; fn compile_module(src: &str) -> TypedModule { use crate::type_::build_prelude; - let parsed = crate::parse::parse_module(src).expect("syntax error"); + let parsed = + crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) + .expect("syntax error"); let ast = parsed.module; let ids = UniqueIdGenerator::new(); let mut config = PackageConfig::default(); @@ -147,7 +152,6 @@ fn compile_expression(src: &str) -> TypedStatement { errors, ) .infer_statements(ast) - .expect("should successfully infer") .first() .clone() } diff --git a/compiler-core/src/ast/typed.rs b/compiler-core/src/ast/typed.rs index 7157de95a..8699d132c 100644 --- a/compiler-core/src/ast/typed.rs +++ b/compiler-core/src/ast/typed.rs @@ -147,6 +147,13 @@ pub enum TypedExpr { location: SrcSpan, value: Box, }, + + /// A placeholder expression used to allow module analysis to continue + /// even when there are type errors. Should never end up in generated code. + Invalid { + location: SrcSpan, + typ: Arc, + }, } impl TypedExpr { @@ -172,7 +179,8 @@ impl TypedExpr { | Self::Panic { .. } | Self::Float { .. } | Self::String { .. } - | Self::ModuleSelect { .. } => self.self_if_contains_location(byte_index), + | Self::ModuleSelect { .. } + | Self::Invalid { .. } => self.self_if_contains_location(byte_index), Self::Pipeline { assignments, @@ -315,7 +323,8 @@ impl TypedExpr { | Self::TupleIndex { location, .. } | Self::ModuleSelect { location, .. } | Self::RecordAccess { location, .. } - | Self::RecordUpdate { location, .. } => *location, + | Self::RecordUpdate { location, .. } + | Self::Invalid { location, .. } => *location, } } @@ -340,7 +349,8 @@ impl TypedExpr { | Self::TupleIndex { location, .. } | Self::ModuleSelect { location, .. } | Self::RecordAccess { location, .. } - | Self::RecordUpdate { location, .. } => *location, + | Self::RecordUpdate { location, .. } + | Self::Invalid { location, .. } => *location, Self::Block { statements, .. } => statements.last().location(), } } @@ -364,7 +374,8 @@ impl TypedExpr { | TypedExpr::Pipeline { .. } | TypedExpr::BitArray { .. } | TypedExpr::TupleIndex { .. } - | TypedExpr::RecordAccess { .. } => None, + | TypedExpr::RecordAccess { .. } + | Self::Invalid { .. } => None, // TODO: test // TODO: definition @@ -405,7 +416,8 @@ impl TypedExpr { | Self::TupleIndex { typ, .. } | Self::ModuleSelect { typ, .. } | Self::RecordAccess { typ, .. } - | Self::RecordUpdate { typ, .. } => typ.clone(), + | Self::RecordUpdate { typ, .. } + | Self::Invalid { typ, .. } => typ.clone(), Self::Pipeline { finally, .. } => finally.type_(), Self::Block { statements, .. } => statements.last().type_(), } @@ -457,7 +469,8 @@ impl TypedExpr { | TypedExpr::RecordUpdate { .. } | TypedExpr::RecordAccess { .. } | TypedExpr::NegateBool { .. } - | TypedExpr::NegateInt { .. } => None, + | TypedExpr::NegateInt { .. } + | TypedExpr::Invalid { .. } => None, } } @@ -517,10 +530,10 @@ impl TypedExpr { // pure value constructor and raise a warning for those as well. TypedExpr::Block { .. } | TypedExpr::Case { .. } => false, - // `panic` and `todo` are never considered pure value constructors, + // `panic`, `todo`, and placeholders are never considered pure value constructors, // we don't want to raise a warning for an unused value if it's one - // of those two. - TypedExpr::Todo { .. } | TypedExpr::Panic { .. } => false, + // of those. + TypedExpr::Todo { .. } | TypedExpr::Panic { .. } | TypedExpr::Invalid { .. } => false, } } diff --git a/compiler-core/src/ast/untyped.rs b/compiler-core/src/ast/untyped.rs index ca345299d..105161b10 100644 --- a/compiler-core/src/ast/untyped.rs +++ b/compiler-core/src/ast/untyped.rs @@ -179,7 +179,7 @@ impl UntypedExpr { match self { Self::BinOp { name, .. } => name.precedence(), Self::PipeLine { .. } => 5, - _ => std::u8::MAX, + _ => u8::MAX, } } diff --git a/compiler-core/src/ast/visit.rs b/compiler-core/src/ast/visit.rs index 4bf379e78..621b51fb1 100644 --- a/compiler-core/src/ast/visit.rs +++ b/compiler-core/src/ast/visit.rs @@ -277,6 +277,10 @@ pub trait Visit<'ast> { visit_typed_expr_negate_int(self, location, value) } + fn visit_typed_expr_invalid(&mut self, location: &'ast SrcSpan, typ: &'ast Arc) { + visit_typed_expr_invalid(self, location, typ); + } + fn visit_typed_statement(&mut self, stmt: &'ast TypedStatement) { visit_typed_statement(self, stmt); } @@ -465,6 +469,7 @@ where v.visit_typed_expr_negate_bool(location, value) } TypedExpr::NegateInt { location, value } => v.visit_typed_expr_negate_int(location, value), + TypedExpr::Invalid { location, typ } => v.visit_typed_expr_invalid(location, typ), } } @@ -826,3 +831,9 @@ where } => { /* TODO */ } } } + +pub fn visit_typed_expr_invalid<'a, V>(_v: &mut V, _location: &'a SrcSpan, _typ: &'a Arc) +where + V: Visit<'a> + ?Sized, +{ +} diff --git a/compiler-core/src/ast_folder.rs b/compiler-core/src/ast_folder.rs index 4cbd8243a..28cd0024f 100644 --- a/compiler-core/src/ast_folder.rs +++ b/compiler-core/src/ast_folder.rs @@ -1080,10 +1080,10 @@ pub trait PatternFolder { name, arguments, module, - with_spread, + spread, constructor: _, type_: (), - } => self.fold_pattern_constructor(location, name, arguments, module, with_spread), + } => self.fold_pattern_constructor(location, name, arguments, module, spread), Pattern::Tuple { location, elems } => self.fold_pattern_tuple(location, elems), @@ -1106,6 +1106,8 @@ pub trait PatternFolder { left_side_string, right_side_assignment, ), + + Pattern::Invalid { location, .. } => self.fold_pattern_invalid(location), } } @@ -1179,7 +1181,7 @@ pub trait PatternFolder { name: EcoString, arguments: Vec>, module: Option, - with_spread: bool, + spread: Option, ) -> UntypedPattern { Pattern::Constructor { location, @@ -1187,7 +1189,7 @@ pub trait PatternFolder { arguments, module, constructor: Inferred::Unknown, - with_spread, + spread, type_: (), } } @@ -1227,6 +1229,13 @@ pub trait PatternFolder { } } + fn fold_pattern_invalid(&mut self, location: SrcSpan) -> UntypedPattern { + Pattern::Invalid { + location, + type_: (), + } + } + /// You probably don't want to override this method. fn walk_pattern(&mut self, m: UntypedPattern) -> UntypedPattern { match m { @@ -1236,7 +1245,8 @@ pub trait PatternFolder { | Pattern::String { .. } | Pattern::Discard { .. } | Pattern::VarUsage { .. } - | Pattern::StringPrefix { .. } => m, + | Pattern::StringPrefix { .. } + | Pattern::Invalid { .. } => m, Pattern::Assign { name, @@ -1273,7 +1283,7 @@ pub trait PatternFolder { arguments, module, constructor, - with_spread, + spread, type_, } => { let arguments = arguments @@ -1289,7 +1299,7 @@ pub trait PatternFolder { arguments, module, constructor, - with_spread, + spread, type_, } } diff --git a/compiler-core/src/build.rs b/compiler-core/src/build.rs index a18d6768a..e5c3a7321 100644 --- a/compiler-core/src/build.rs +++ b/compiler-core/src/build.rs @@ -17,9 +17,10 @@ pub use self::project_compiler::{Built, Options, ProjectCompiler}; pub use self::telemetry::{NullTelemetry, Telemetry}; use crate::ast::{ - CustomType, DefinitionLocation, TypeAst, TypedArg, TypedDefinition, TypedExpr, TypedFunction, - TypedPattern, TypedStatement, + CallArg, CustomType, DefinitionLocation, Pattern, TypeAst, TypedArg, TypedDefinition, + TypedExpr, TypedFunction, TypedPattern, TypedStatement, }; +use crate::type_::Type; use crate::{ ast::{Definition, SrcSpan, TypedModule}, config::{self, PackageConfig}, @@ -34,6 +35,7 @@ use ecow::EcoString; use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::fmt::Debug; +use std::sync::Arc; use std::time::SystemTime; use std::{collections::HashMap, ffi::OsString, fs::DirEntry, iter::Peekable, process}; use strum::{Display, EnumIter, EnumString, EnumVariantNames, VariantNames}; @@ -296,13 +298,6 @@ impl Module { } } } - - pub(crate) fn dependencies_list(&self) -> Vec { - self.dependencies - .iter() - .map(|(name, _)| name.clone()) - .collect() - } } #[derive(Debug, Clone, PartialEq)] @@ -316,12 +311,16 @@ pub struct UnqualifiedImport<'a> { #[derive(Debug, Clone, PartialEq)] pub enum Located<'a> { Pattern(&'a TypedPattern), + PatternSpread { + spread_location: SrcSpan, + arguments: &'a Vec>>>, + }, Statement(&'a TypedStatement), Expression(&'a TypedExpr), ModuleStatement(&'a TypedDefinition), FunctionBody(&'a TypedFunction), Arg(&'a TypedArg), - Annotation(SrcSpan, std::sync::Arc), + Annotation(SrcSpan, std::sync::Arc), UnqualifiedImport(UnqualifiedImport<'a>), } @@ -330,7 +329,7 @@ impl<'a> Located<'a> { fn type_location( &self, importable_modules: &'a im::HashMap, - type_: std::sync::Arc, + type_: std::sync::Arc, ) -> Option> { type_constructor_from_modules(importable_modules, type_).map(|t| DefinitionLocation { module: Some(&t.module), @@ -343,6 +342,7 @@ impl<'a> Located<'a> { importable_modules: &'a im::HashMap, ) -> Option> { match self { + Self::PatternSpread { .. } => None, Self::Pattern(pattern) => pattern.definition_location(), Self::Statement(statement) => statement.definition_location(), Self::FunctionBody(statement) => None, @@ -382,11 +382,11 @@ impl<'a> Located<'a> { // Looks up the type constructor for the given type pub fn type_constructor_from_modules( importable_modules: &im::HashMap, - type_: std::sync::Arc, + type_: std::sync::Arc, ) -> Option<&type_::TypeConstructor> { let type_ = type_::collapse_links(type_); match type_.as_ref() { - type_::Type::Named { name, module, .. } => importable_modules + Type::Named { name, module, .. } => importable_modules .get(module) .and_then(|i| i.types.get(name)), _ => None, diff --git a/compiler-core/src/build/module_loader.rs b/compiler-core/src/build/module_loader.rs index 7a74fef18..43ee3bc69 100644 --- a/compiler-core/src/build/module_loader.rs +++ b/compiler-core/src/build/module_loader.rs @@ -16,7 +16,7 @@ use super::{ use crate::{ error::{FileIoAction, FileKind}, io::{CommandExecutor, FileSystemReader, FileSystemWriter}, - warning::WarningEmitter, + warning::{TypeWarningEmitter, WarningEmitter}, Error, Result, }; @@ -125,6 +125,7 @@ where name, self.package_name.clone(), mtime, + self.warnings.clone(), ) } @@ -147,16 +148,19 @@ pub(crate) fn read_source( name: EcoString, package_name: EcoString, mtime: SystemTime, + emitter: WarningEmitter, ) -> Result where IO: FileSystemReader + FileSystemWriter + CommandExecutor + Clone, { let code: EcoString = io.read(&path)?.into(); - let parsed = crate::parse::parse_module(&code).map_err(|error| Error::Parse { - path: path.clone(), - src: code.clone(), - error, + let parsed = crate::parse::parse_module(path.clone(), &code, &emitter).map_err(|error| { + Error::Parse { + path: path.clone(), + src: code.clone(), + error, + } })?; let mut ast = parsed.module; let extra = parsed.extra; diff --git a/compiler-core/src/build/package_compiler.rs b/compiler-core/src/build/package_compiler.rs index d5141e8ce..9537af00c 100644 --- a/compiler-core/src/build/package_compiler.rs +++ b/compiler-core/src/build/package_compiler.rs @@ -280,7 +280,7 @@ where let info = CacheMetadata { mtime: module.mtime, codegen_performed: self.perform_codegen, - dependencies: module.dependencies_list(), + dependencies: module.dependencies.clone(), fingerprint: SourceFingerprint::new(&module.code), line_numbers: module.ast.type_info.line_numbers.clone(), }; @@ -579,7 +579,7 @@ impl Input { pub fn dependencies(&self) -> Vec { match self { Input::New(m) => m.dependencies.iter().map(|(n, _)| n.clone()).collect(), - Input::Cached(m) => m.dependencies.clone(), + Input::Cached(m) => m.dependencies.iter().map(|(n, _)| n.clone()).collect(), } } @@ -604,7 +604,7 @@ impl Input { pub(crate) struct CachedModule { pub name: EcoString, pub origin: Origin, - pub dependencies: Vec, + pub dependencies: Vec<(EcoString, SrcSpan)>, pub source_path: Utf8PathBuf, pub line_numbers: LineNumbers, } @@ -613,7 +613,7 @@ pub(crate) struct CachedModule { pub(crate) struct CacheMetadata { pub mtime: SystemTime, pub codegen_performed: bool, - pub dependencies: Vec, + pub dependencies: Vec<(EcoString, SrcSpan)>, pub fingerprint: SourceFingerprint, pub line_numbers: LineNumbers, } diff --git a/compiler-core/src/build/package_loader.rs b/compiler-core/src/build/package_loader.rs index eea0a66cb..6bb6b1e05 100644 --- a/compiler-core/src/build/package_loader.rs +++ b/compiler-core/src/build/package_loader.rs @@ -12,12 +12,14 @@ use camino::{Utf8Path, Utf8PathBuf}; use ecow::EcoString; use itertools::Itertools; +use vec1::Vec1; use crate::{ + ast::SrcSpan, build::{module_loader::ModuleLoader, package_compiler::module_name, Module, Origin}, config::PackageConfig, dep_tree, - error::{FileIoAction, FileKind}, + error::{FileIoAction, FileKind, ImportCycleLocationDetails}, io::{CommandExecutor, FileSystemReader, FileSystemWriter}, metadata, type_, uid::UniqueIdGenerator, @@ -104,16 +106,22 @@ where let mut inputs = self.read_sources_and_caches()?; // Determine order in which modules are to be processed + let mut dep_location_map = HashMap::new(); let deps = inputs .values() - .map(|m| (m.name().clone(), m.dependencies())) + .map(|m| { + let name = m.name().clone(); + let _ = dep_location_map.insert(name.clone(), m); + (name, m.dependencies()) + }) // Making sure that the module order is deterministic, to prevent different // compilations of the same project compiling in different orders. This could impact // any bugged outcomes, though not any where the compiler is working correctly, so it's // mostly to aid debugging. .sorted_by(|(a, _), (b, _)| a.cmp(b)) .collect(); - let sequence = dep_tree::toposort_deps(deps).map_err(convert_deps_tree_error)?; + let sequence = dep_tree::toposort_deps(deps) + .map_err(|e| self.convert_deps_tree_error(e, dep_location_map))?; // Now that we have loaded sources and caches we check to see if any of // the caches need to be invalidated because their dependencies have @@ -262,8 +270,66 @@ where cached.name, self.package_name.clone(), mtime, + self.warnings.clone(), ) } + + fn convert_deps_tree_error( + &self, + e: dep_tree::Error, + dep_location_map: HashMap, + ) -> Error { + match e { + dep_tree::Error::Cycle(modules) => { + let modules = modules + .iter() + .enumerate() + .map(|(i, module)| { + // cycles are in order of reference so get next in list or loop back to first + let ind = if i == modules.len() - 1 { 0 } else { i + 1 }; + let next = modules.get(ind).expect("next module must exist"); + let input = dep_location_map.get(module).expect("dependency must exist"); + let location = match input { + Input::New(module) => { + let (_, location) = module + .dependencies + .iter() + .find(|d| &d.0 == next) + .expect("import must exist for there to be a cycle"); + ImportCycleLocationDetails { + location: *location, + path: module.path.clone(), + src: module.code.clone(), + } + } + Input::Cached(cached_module) => { + let (_, location) = cached_module + .dependencies + .iter() + .find(|d| &d.0 == next) + .expect("import must exist for there to be a cycle"); + let src = self + .io + .read(&cached_module.source_path) + .expect("failed to read source") + .into(); + ImportCycleLocationDetails { + location: *location, + path: cached_module.source_path.clone(), + src, + } + } + }; + (module.clone(), location) + }) + .collect_vec(); + Error::ImportCycle { + modules: Vec1::try_from(modules) + .expect("at least 1 module must exist in cycle"), + } + } + } + } } fn ensure_gleam_module_does_not_overwrite_standard_erlang_module(input: &Input) -> Result<()> { @@ -1546,13 +1612,6 @@ fn ensure_gleam_module_does_not_overwrite_standard_erlang_module(input: &Input) path: input.path.to_owned(), }) } - -fn convert_deps_tree_error(e: dep_tree::Error) -> Error { - match e { - dep_tree::Error::Cycle(modules) => Error::ImportCycle { modules }, - } -} - #[derive(Debug, Default)] pub struct StaleTracker(HashSet); @@ -1561,8 +1620,8 @@ impl StaleTracker { _ = self.0.insert(name); } - fn includes_any(&self, names: &[EcoString]) -> bool { - names.iter().any(|n| self.0.contains(n.as_str())) + fn includes_any(&self, names: &[(EcoString, SrcSpan)]) -> bool { + names.iter().any(|n| self.0.contains(n.0.as_str())) } pub fn empty(&mut self) { diff --git a/compiler-core/src/build/package_loader/tests.rs b/compiler-core/src/build/package_loader/tests.rs index db92a793c..69b668f95 100644 --- a/compiler-core/src/build/package_loader/tests.rs +++ b/compiler-core/src/build/package_loader/tests.rs @@ -28,7 +28,13 @@ fn write_src(fs: &InMemoryFileSystem, path: &str, seconds: u64, src: &str) { fs.set_modification_time(&path, SystemTime::UNIX_EPOCH + Duration::from_secs(seconds)); } -fn write_cache(fs: &InMemoryFileSystem, name: &str, seconds: u64, deps: Vec, src: &str) { +fn write_cache( + fs: &InMemoryFileSystem, + name: &str, + seconds: u64, + deps: Vec<(EcoString, SrcSpan)>, + src: &str, +) { let line_numbers = line_numbers::LineNumbers::new(src); let mtime = SystemTime::UNIX_EPOCH + Duration::from_secs(seconds); let cache_metadata = CacheMetadata { @@ -190,7 +196,13 @@ fn module_is_stale_if_deps_are_stale() { // Cache is fresh but dep is stale write_src(&fs, "/src/two.gleam", 1, "import one"); - write_cache(&fs, "two", 2, vec![EcoString::from("one")], "import one"); + write_cache( + &fs, + "two", + 2, + vec![(EcoString::from("one"), SrcSpan { start: 0, end: 0 })], + "import one", + ); // Cache is fresh write_src(&fs, "/src/three.gleam", 1, TEST_SOURCE_1); diff --git a/compiler-core/src/call_graph.rs b/compiler-core/src/call_graph.rs index db799bc91..0cd1bdbfa 100644 --- a/compiler-core/src/call_graph.rs +++ b/compiler-core/src/call_graph.rs @@ -292,7 +292,8 @@ impl<'a> CallGraphBuilder<'a> { | Pattern::StringPrefix { right_side_assignment: AssignName::Discard(_), .. - } => (), + } + | Pattern::Invalid { .. } => (), Pattern::StringPrefix { right_side_assignment: AssignName::Variable(name), @@ -390,6 +391,15 @@ impl<'a> CallGraphBuilder<'a> { | ClauseGuard::GtEqFloat { left, right, .. } | ClauseGuard::LtFloat { left, right, .. } | ClauseGuard::LtEqFloat { left, right, .. } + | ClauseGuard::AddInt { left, right, .. } + | ClauseGuard::AddFloat { left, right, .. } + | ClauseGuard::SubInt { left, right, .. } + | ClauseGuard::SubFloat { left, right, .. } + | ClauseGuard::MultInt { left, right, .. } + | ClauseGuard::MultFloat { left, right, .. } + | ClauseGuard::DivInt { left, right, .. } + | ClauseGuard::DivFloat { left, right, .. } + | ClauseGuard::RemainderInt { left, right, .. } | ClauseGuard::Or { left, right, .. } | ClauseGuard::And { left, right, .. } => { self.guard(left); diff --git a/compiler-core/src/codegen.rs b/compiler-core/src/codegen.rs index 041b9070b..acdf668fe 100644 --- a/compiler-core/src/codegen.rs +++ b/compiler-core/src/codegen.rs @@ -192,11 +192,22 @@ impl<'a> JavaScript<'a> { fn write_prelude(&self, writer: &impl FileSystemWriter) -> Result<()> { let rexport = format!("export * from \"{}\";\n", self.prelude_location); - writer.write(&self.output_directory.join("gleam.mjs"), &rexport)?; + let prelude_path = &self.output_directory.join("gleam.mjs"); + + // This check skips unnecessary `gleam.mjs` writes which confuse + // watchers and HMR build tools + if !writer.exists(prelude_path) { + writer.write(prelude_path, &rexport)?; + } if self.typescript == TypeScriptDeclarations::Emit { let rexport = rexport.replace(".mjs", ".d.mts"); - writer.write(&self.output_directory.join("gleam.d.mts"), &rexport)?; + let prelude_declaration_path = &self.output_directory.join("gleam.d.mts"); + + // Type decleration may trigger badly configured watchers + if !writer.exists(prelude_declaration_path) { + writer.write(prelude_declaration_path, &rexport)?; + } } Ok(()) diff --git a/compiler-core/src/diagnostic.rs b/compiler-core/src/diagnostic.rs index b4294371c..93ee44784 100644 --- a/compiler-core/src/diagnostic.rs +++ b/compiler-core/src/diagnostic.rs @@ -1,7 +1,9 @@ +use std::collections::HashMap; + use camino::Utf8PathBuf; pub use codespan_reporting::diagnostic::{LabelStyle, Severity}; -use codespan_reporting::{diagnostic::Label as CodespanLabel, files::SimpleFile}; +use codespan_reporting::{diagnostic::Label as CodespanLabel, files::SimpleFiles}; use ecow::EcoString; use termcolor::Buffer; @@ -19,18 +21,32 @@ pub struct Label { pub span: SrcSpan, } +impl Label { + fn to_codespan_label(&self, fileid: usize) -> CodespanLabel { + let label = CodespanLabel::new( + LabelStyle::Primary, + fileid, + (self.span.start as usize)..(self.span.end as usize), + ); + match &self.text { + None => label, + Some(text) => label.with_message(text.clone()), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExtraLabel { + pub src_info: Option<(EcoString, Utf8PathBuf)>, + pub label: Label, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Location { pub src: EcoString, pub path: Utf8PathBuf, pub label: Label, - pub extra_labels: Vec>; + + _ -> + <<""/utf8>> + end. diff --git a/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__patterns__string_prefix_as_pattern_with_multiple_subjects.snap b/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__patterns__string_prefix_as_pattern_with_multiple_subjects.snap new file mode 100644 index 000000000..c9ce59859 --- /dev/null +++ b/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__patterns__string_prefix_as_pattern_with_multiple_subjects.snap @@ -0,0 +1,18 @@ +--- +source: compiler-core/src/erlang/tests/patterns.rs +expression: "pub fn a(x) {\n case x, x {\n _, \"a\" as a <> _ -> a\n _, _ -> \"a\"\n }\n}" +--- +-module(my@mod). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). + +-export([a/1]). + +-spec a(binary()) -> binary(). +a(X) -> + case {X, X} of + {_, <>} when A =:= <<"a"/utf8>> -> + A; + + {_, _} -> + <<"a"/utf8>> + end. diff --git a/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__patterns__string_prefix_as_pattern_with_multiple_subjects_and_guard.snap b/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__patterns__string_prefix_as_pattern_with_multiple_subjects_and_guard.snap new file mode 100644 index 000000000..2e7f966ea --- /dev/null +++ b/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__patterns__string_prefix_as_pattern_with_multiple_subjects_and_guard.snap @@ -0,0 +1,18 @@ +--- +source: compiler-core/src/erlang/tests/patterns.rs +expression: "pub fn a(x) {\n case x, x {\n _, \"a\" as a <> rest if rest == \"a\" -> a\n _, _ -> \"a\"\n }\n}" +--- +-module(my@mod). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). + +-export([a/1]). + +-spec a(binary()) -> binary(). +a(X) -> + case {X, X} of + {_, <>} when (A =:= <<"a"/utf8>>) andalso (Rest =:= <<"a"/utf8>>) -> + A; + + {_, _} -> + <<"a"/utf8>> + end. diff --git a/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__reserved__escape_erlang_reserved_keywords_in_type_names.snap b/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__reserved__escape_erlang_reserved_keywords_in_type_names.snap index 8103de2c9..5166f032a 100644 --- a/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__reserved__escape_erlang_reserved_keywords_in_type_names.snap +++ b/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__reserved__escape_erlang_reserved_keywords_in_type_names.snap @@ -1,11 +1,11 @@ --- source: compiler-core/src/erlang/tests/reserved.rs -expression: "pub type After { TestAfter }\npub type And { TestAnd }\npub type Andalso { TestAndAlso }\npub type Band { TestBAnd }\npub type Begin { TestBegin }\npub type Bnot { TestBNot }\npub type Bor { TestBOr }\npub type Bsl { TestBsl }\npub type Bsr { TestBsr }\npub type Bxor { TestBXor }\npub type Case { TestCase }\npub type Catch { TestCatch }\npub type Cond { TestCond }\npub type Div { TestDiv }\npub type End { TestEnd }\npub type Fun { TestFun }\npub type If { TestIf }\npub type Let { TestLet }\npub type Not { TestNot }\npub type Of { TestOf }\npub type Or { TestOr }\npub type Orelse { TestOrElse }\npub type Query { TestQuery }\npub type Receive { TestReceive }\npub type Rem { TestRem }\npub type Try { TestTry }\npub type When { TestWhen }\npub type Xor { TestXor }" +expression: "pub type After { TestAfter }\npub type And { TestAnd }\npub type Andalso { TestAndAlso }\npub type Band { TestBAnd }\npub type Begin { TestBegin }\npub type Bnot { TestBNot }\npub type Bor { TestBOr }\npub type Bsl { TestBsl }\npub type Bsr { TestBsr }\npub type Bxor { TestBXor }\npub type Case { TestCase }\npub type Catch { TestCatch }\npub type Cond { TestCond }\npub type Div { TestDiv }\npub type End { TestEnd }\npub type Fun { TestFun }\npub type If { TestIf }\npub type Let { TestLet }\npub type Maybe { TestMaybe }\npub type Not { TestNot }\npub type Of { TestOf }\npub type Or { TestOr }\npub type Orelse { TestOrElse }\npub type Query { TestQuery }\npub type Receive { TestReceive }\npub type Rem { TestRem }\npub type Try { TestTry }\npub type When { TestWhen }\npub type Xor { TestXor }" --- -module(my@mod). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). --export_type(['after'/0, 'and'/0, 'andalso'/0, 'band'/0, 'begin'/0, 'bnot'/0, 'bor'/0, 'bsl'/0, 'bsr'/0, 'bxor'/0, 'case'/0, 'catch'/0, 'cond'/0, 'div'/0, 'end'/0, 'fun'/0, 'if'/0, 'let'/0, 'not'/0, 'of'/0, 'or'/0, 'orelse'/0, 'query'/0, 'receive'/0, 'rem'/0, 'try'/0, 'when'/0, 'xor'/0]). +-export_type(['after'/0, 'and'/0, 'andalso'/0, 'band'/0, 'begin'/0, 'bnot'/0, 'bor'/0, 'bsl'/0, 'bsr'/0, 'bxor'/0, 'case'/0, 'catch'/0, 'cond'/0, 'div'/0, 'end'/0, 'fun'/0, 'if'/0, 'let'/0, 'maybe'/0, 'not'/0, 'of'/0, 'or'/0, 'orelse'/0, 'query'/0, 'receive'/0, 'rem'/0, 'try'/0, 'when'/0, 'xor'/0]). -type 'after'() :: test_after. @@ -43,6 +43,8 @@ expression: "pub type After { TestAfter }\npub type And { TestAnd }\npub type An -type 'let'() :: test_let. +-type 'maybe'() :: test_maybe. + -type 'not'() :: test_not. -type 'of'() :: test_of. diff --git a/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__strings__string_prefix_assignment_with_guard.snap b/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__strings__string_prefix_assignment_with_guard.snap index cff3316d2..5be11ccce 100644 --- a/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__strings__string_prefix_assignment_with_guard.snap +++ b/compiler-core/src/erlang/tests/snapshots/glistix_core__erlang__tests__strings__string_prefix_assignment_with_guard.snap @@ -10,7 +10,7 @@ expression: "\npub fn go(x) {\n case x {\n \"Hello, \" as greeting <> name i -spec go(binary()) -> binary(). go(X) -> case X of - <> when Greeting =:= <<"Hello, "/utf8>> andalso (Name =:= <<"Dude"/utf8>>) -> + <> when (Greeting =:= <<"Hello, "/utf8>>) andalso (Name =:= <<"Dude"/utf8>>) -> <>; <> when Greeting@1 =:= <<"Hello, "/utf8>> -> diff --git a/compiler-core/src/error.rs b/compiler-core/src/error.rs index 23ec1db49..afeefc28e 100644 --- a/compiler-core/src/error.rs +++ b/compiler-core/src/error.rs @@ -1,6 +1,6 @@ #![allow(clippy::unwrap_used, clippy::expect_used)] use crate::build::{Outcome, Runtime, Target}; -use crate::diagnostic::{Diagnostic, Label, Location}; +use crate::diagnostic::{Diagnostic, ExtraLabel, Label, Location}; use crate::type_::error::RecordVariants; use crate::type_::error::{MissingAnnotation, UnknownTypeHint}; use crate::type_::{error::PatternMatchKind, FieldAccessUsage}; @@ -47,6 +47,13 @@ pub struct UnknownImportDetails { pub modules: Vec, } +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ImportCycleLocationDetails { + pub location: crate::ast::SrcSpan, + pub path: Utf8PathBuf, + pub src: EcoString, +} + #[derive(Debug, Eq, PartialEq, Error, Clone)] pub enum Error { #[error("failed to parse Gleam source code")] @@ -81,7 +88,9 @@ pub enum Error { DuplicateSourceFile { file: String }, #[error("cyclical module imports")] - ImportCycle { modules: Vec }, + ImportCycle { + modules: Vec1<(EcoString, ImportCycleLocationDetails)>, + }, #[error("cyclical package dependencies")] PackageCycle { packages: Vec }, @@ -167,6 +176,9 @@ pub enum Error { file_names.iter().map(|x| x.as_str()).join(", "))] OutputFilesAlreadyExist { file_names: Vec }, + #[error("Packages not exist: {}", packages.iter().join(", "))] + RemovedPackagesNotExist { packages: Vec }, + #[error("unable to find project root")] UnableToFindProjectRoot { path: String }, @@ -566,18 +578,18 @@ fn edit_distance(a: &str, b: &str, limit: usize) -> Option { _ => panic!("Index out of bounds"), }; - let insertion = current.get(j - 1).map_or(std::usize::MAX, |&x| x + 1); + let insertion = current.get(j - 1).map_or(usize::MAX, |&x| x + 1); if let Some(value) = current.get_mut(j) { *value = std::cmp::min( // deletion - prev.get(j).map_or(std::usize::MAX, |&x| x + 1), + prev.get(j).map_or(usize::MAX, |&x| x + 1), std::cmp::min( // insertion insertion, // substitution prev.get(j - 1) - .map_or(std::usize::MAX, |&x| x + substitution_cost), + .map_or(usize::MAX, |&x| x + substitution_cost), ), ); } @@ -608,7 +620,7 @@ fn edit_distance(a: &str, b: &str, limit: usize) -> Option { // `prev` because we already rotated the buffers. let distance = match prev.get(b.len()) { Some(&d) => d, - None => std::usize::MAX, + None => usize::MAX, }; (distance <= limit).then_some(distance) } @@ -854,6 +866,26 @@ If you want to overwrite these files, delete them and run the command again. location: None, }], + Error::RemovedPackagesNotExist { packages } => vec![ + Diagnostic { + title: "Package not found".into(), + text: format!( +"These packages are not dependencies of your package so they could not +be removed. + +{} +", + packages + .iter() + .map(|p| format!(" - {}", p.as_str())) + .join("\n") + ), + level: Level::Error, + hint: None, + location: None, + } + ], + Error::CannotPublishTodo { unfinished } => vec![Diagnostic { title: "Cannot publish unfinished code".into(), text: format!( @@ -1231,7 +1263,10 @@ modules cannot import them. Perhaps move the `{test_module}` module to the src d } }); let label = labels.next().expect("Unknown labels first label"); - let extra_labels = labels.collect(); + let extra_labels = labels.map(|label| ExtraLabel { + src_info: None, + label, + }).collect(); let text = if valid.is_empty() { "This constructor does not accept any labelled arguments.".into() } else if other_labels.is_empty() { @@ -1325,9 +1360,12 @@ Names in a Gleam module must be unique so one will need to be renamed." }, path: path.clone(), src: src.clone(), - extra_labels: vec![Label { - text: Some("First imported here".into()), - span: *previous_location, + extra_labels: vec![ExtraLabel { + src_info: None, + label: Label { + text: Some("First imported here".into()), + span: *previous_location, + }, }], }), } @@ -1360,9 +1398,12 @@ Names in a Gleam module must be unique so one will need to be renamed." }, path: path.clone(), src: src.clone(), - extra_labels: vec![Label { + extra_labels: vec![ExtraLabel { + src_info: None, + label: Label { text: Some("First defined here".into()), span: *first_location, + }, }], }), } @@ -1390,9 +1431,12 @@ Names in a Gleam module must be unique so one will need to be renamed." }, path: path.clone(), src: src.clone(), - extra_labels: vec![Label { + extra_labels: vec![ExtraLabel { + src_info: None, + label: Label { text: Some("First defined here".into()), span: *previous_location, + } }], }), } @@ -2966,12 +3010,13 @@ See: https://tour.gleam.run/advanced-features/use/"); }, path: path.clone(), src: src.clone(), - extra_labels: vec![ - Label { - text: Some(format!("Expected {expected}, got {given}")), - span: *pattern_location - } - ], + extra_labels: vec![ExtraLabel { + src_info: None, + label: Label { + text: Some(format!("Expected {expected}, got {given}")), + span: *pattern_location + } + }], }), } }, @@ -3010,10 +3055,19 @@ See: https://tour.gleam.run/advanced-features/use/"); } Error::ImportCycle { modules } => { + let first_location = &modules.first().1; + let rest_locations = modules.iter().skip(1).map(|(_, l)| ExtraLabel { + label: Label { + text: Some("Imported here".into()), + span: l.location + }, + src_info: Some((l.src.clone(), l.path.clone())), + }).collect_vec(); let mut text = "The import statements for these modules form a cycle: " .into(); - write_cycle(&mut text, modules); + let mod_names = modules.iter().map(|m| m.0.clone()).collect_vec(); + write_cycle(&mut text, &mod_names); text.push_str( "Gleam doesn't support dependency cycles like these, please break the cycle to continue.", @@ -3023,7 +3077,15 @@ cycle to continue.", text, hint: None, level: Level::Error, - location: None, + location: Some(Location { + label: Label { + text: Some("Imported here".into()), + span: first_location.location, + }, + path: first_location.path.clone(), + src: first_location.src.clone(), + extra_labels: rest_locations, + }), }] } diff --git a/compiler-core/src/exhaustiveness.rs b/compiler-core/src/exhaustiveness.rs index cb11d22e0..3c2028e5f 100644 --- a/compiler-core/src/exhaustiveness.rs +++ b/compiler-core/src/exhaustiveness.rs @@ -44,7 +44,7 @@ use crate::{ use ecow::EcoString; use id_arena::Arena; use itertools::Itertools; -use std::{cell::RefCell, collections::HashMap, sync::Arc, u64}; +use std::{cell::RefCell, collections::HashMap, sync::Arc}; pub use self::pattern::PatternArena; diff --git a/compiler-core/src/exhaustiveness/pattern.rs b/compiler-core/src/exhaustiveness/pattern.rs index 5819a40d4..298b967e9 100644 --- a/compiler-core/src/exhaustiveness/pattern.rs +++ b/compiler-core/src/exhaustiveness/pattern.rs @@ -104,6 +104,7 @@ impl PatternArena { pub fn register(&mut self, pattern: &TypedPattern) -> PatternId { match pattern { + TypedPattern::Invalid { .. } => self.insert(Pattern::Discard), TypedPattern::Discard { .. } => self.insert(Pattern::Discard), TypedPattern::Int { value, .. } => { diff --git a/compiler-core/src/fix.rs b/compiler-core/src/fix.rs index 8e1ddde65..38142a5ed 100644 --- a/compiler-core/src/fix.rs +++ b/compiler-core/src/fix.rs @@ -1,5 +1,6 @@ use crate::{ format::{Formatter, Intermediate}, + warning::WarningEmitter, Error, Result, }; use camino::Utf8Path; @@ -7,11 +8,12 @@ use ecow::EcoString; pub fn parse_fix_and_format(src: &EcoString, path: &Utf8Path) -> Result { // Parse - let parsed = crate::parse::parse_module(src).map_err(|error| Error::Parse { - path: path.to_path_buf(), - src: src.clone(), - error, - })?; + let parsed = crate::parse::parse_module(path.to_owned(), src, &WarningEmitter::null()) + .map_err(|error| Error::Parse { + path: path.to_path_buf(), + src: src.clone(), + error, + })?; let intermediate = Intermediate::from_extra(&parsed.extra, src); let module = parsed.module; diff --git a/compiler-core/src/format.rs b/compiler-core/src/format.rs index d76b666a4..e98b51478 100644 --- a/compiler-core/src/format.rs +++ b/compiler-core/src/format.rs @@ -12,6 +12,7 @@ use crate::{ parse::extra::{Comment, ModuleExtra}, pretty::{self, *}, type_::{self, Type}, + warning::WarningEmitter, Error, Result, }; use ecow::EcoString; @@ -25,11 +26,12 @@ use camino::Utf8Path; const INDENT: isize = 2; pub fn pretty(writer: &mut impl Utf8Writer, src: &EcoString, path: &Utf8Path) -> Result<()> { - let parsed = crate::parse::parse_module(src).map_err(|error| Error::Parse { - path: path.to_path_buf(), - src: src.clone(), - error, - })?; + let parsed = crate::parse::parse_module(path.to_owned(), src, &WarningEmitter::null()) + .map_err(|error| Error::Parse { + path: path.to_path_buf(), + src: src.clone(), + error, + })?; let intermediate = Intermediate::from_extra(&parsed.extra, src); Formatter::with_comments(&intermediate) .module(&parsed.module) @@ -1103,7 +1105,7 @@ impl<'comments> Formatter<'comments> { name: &'a str, args: &'a [CallArg], module: &'a Option, - with_spread: bool, + spread: Option, location: &SrcSpan, ) -> Document<'a> { fn is_breakable(expr: &UntypedPattern) -> bool { @@ -1121,11 +1123,11 @@ impl<'comments> Formatter<'comments> { None => name.to_doc(), }; - if args.is_empty() && with_spread { + if args.is_empty() && spread.is_some() { name.append("(..)") } else if args.is_empty() { name - } else if with_spread { + } else if spread.is_some() { let args = args.iter().map(|a| self.pattern_call_arg(a)).collect_vec(); name.append(self.wrap_args_with_spread(args, location.end)) } else { @@ -1238,15 +1240,16 @@ impl<'comments> Formatter<'comments> { if is_breakable_argument(to_expr(last_value), values.len()) && !self.any_comments(last_value.location().start) => { + let mut docs = initial_values + .iter() + .map(|value| to_doc(self, value)) + .collect_vec(); + let last_value_doc = to_doc(self, last_value) .group() .next_break_fits(NextBreakFitsMode::Enabled); - let docs = initial_values - .iter() - .map(|value| to_doc(self, value)) - .chain(std::iter::once(last_value_doc)) - .collect_vec(); + docs.append(&mut vec![last_value_doc]); doc.append(self.wrap_function_call_args(docs, location)) .next_break_fits(NextBreakFitsMode::Disabled) @@ -1269,7 +1272,7 @@ impl<'comments> Formatter<'comments> { ) -> Document<'a> { let subjects_doc = break_("case", "case ") .append(join( - subjects.iter().map(|s| self.expr(s)), + subjects.iter().map(|s| self.expr(s).group()), break_(",", ", "), )) .nest(INDENT) @@ -2023,10 +2026,10 @@ impl<'comments> Formatter<'comments> { name, arguments: args, module, - with_spread, + spread, location, .. - } => self.pattern_constructor(name, args, module, *with_spread, location), + } => self.pattern_constructor(name, args, module, *spread, location), Pattern::Tuple { elems, location, .. @@ -2064,6 +2067,8 @@ impl<'comments> Formatter<'comments> { None => docvec![left, " <> ", right], } } + + Pattern::Invalid { .. } => panic!("invalid patterns can not be in an untyped ast"), }; commented(doc, comments) } @@ -2189,6 +2194,33 @@ impl<'comments> Formatter<'comments> { ClauseGuard::LtEqFloat { left, right, .. } => { self.clause_guard_bin_op(&BinOp::LtEqFloat, left, right) } + ClauseGuard::AddInt { left, right, .. } => { + self.clause_guard_bin_op(&BinOp::AddInt, left, right) + } + ClauseGuard::AddFloat { left, right, .. } => { + self.clause_guard_bin_op(&BinOp::AddFloat, left, right) + } + ClauseGuard::SubInt { left, right, .. } => { + self.clause_guard_bin_op(&BinOp::SubInt, left, right) + } + ClauseGuard::SubFloat { left, right, .. } => { + self.clause_guard_bin_op(&BinOp::SubFloat, left, right) + } + ClauseGuard::MultInt { left, right, .. } => { + self.clause_guard_bin_op(&BinOp::MultInt, left, right) + } + ClauseGuard::MultFloat { left, right, .. } => { + self.clause_guard_bin_op(&BinOp::MultFloat, left, right) + } + ClauseGuard::DivInt { left, right, .. } => { + self.clause_guard_bin_op(&BinOp::DivInt, left, right) + } + ClauseGuard::DivFloat { left, right, .. } => { + self.clause_guard_bin_op(&BinOp::DivFloat, left, right) + } + ClauseGuard::RemainderInt { left, right, .. } => { + self.clause_guard_bin_op(&BinOp::RemainderInt, left, right) + } ClauseGuard::Var { name, .. } => name.to_doc(), diff --git a/compiler-core/src/format/tests.rs b/compiler-core/src/format/tests.rs index dafb2274c..44252a14c 100644 --- a/compiler-core/src/format/tests.rs +++ b/compiler-core/src/format/tests.rs @@ -2627,7 +2627,7 @@ fn pattern_discard() { assert_format!( r#"fn main() { - let _foo = 1 + let _wibble = 1 Nil } "# @@ -2851,10 +2851,10 @@ fn expr_case() { r#"fn main() { case bool { True -> { - "Foo" + "Wibble" |> io.println - "Bar" + "Wobble" |> io.println Nil @@ -4598,9 +4598,9 @@ fn empty_lines_work_with_trailing_space_and_eol_normalisation() { fn single_empty_line_between_comments() { // empty line isn't added if it's not already present assert_format!( - "pub fn foo() { - // foo - // bar + "pub fn wibble() { + // wibble + // wobble 123 } " @@ -4611,10 +4611,10 @@ fn single_empty_line_between_comments() { fn single_empty_line_between_comments1() { // single empty line between comments/statement preserved assert_format!( - "pub fn foo() { - // foo + "pub fn wibble() { + // wibble - // bar + // wobble 123 } @@ -4626,20 +4626,20 @@ fn single_empty_line_between_comments1() { fn single_empty_line_between_comments2() { // multiple consecutive empty lines condensed into one assert_format_rewrite!( - "pub fn foo() { - // foo + "pub fn wibble() { + // wibble - // bar + // wobble 123 } ", - "pub fn foo() { - // foo + "pub fn wibble() { + // wibble - // bar + // wobble 123 } @@ -4651,9 +4651,9 @@ fn single_empty_line_between_comments2() { fn single_empty_line_between_comments3() { // freestanding comments keep empty lines assert_format!( - "// foo + "// wibble -// bar +// wobble " ); } @@ -4662,14 +4662,14 @@ fn single_empty_line_between_comments3() { fn single_empty_line_between_comments4() { // freestanding comments condense consecutive empty lines assert_format_rewrite!( - "// foo + "// wibble -// bar +// wobble ", - "// foo + "// wibble -// bar +// wobble ", ); } @@ -4678,8 +4678,8 @@ fn single_empty_line_between_comments4() { #[test] fn no_newline_before_comments() { assert_format!( - "// foo -// bar + "// wibble +// wobble " ); } @@ -4785,9 +4785,9 @@ fn do_not_remove_required_braces_case_guard() { assert_format!( "fn main() { - let foo = True - case foo { - foo if True != { 1 == 2 } -> Nil + let wibble = True + case wibble { + wibble if True != { 1 == 2 } -> Nil _ -> Nil } } @@ -4796,10 +4796,10 @@ fn do_not_remove_required_braces_case_guard() { assert_format!( "fn main() { - let foo = True - let bar = False - case foo { - foo if True != { 1 == { bar == foo } } -> Nil + let wibble = True + let wobble = False + case wibble { + wibble if True != { 1 == { wobble == wibble } } -> Nil _ -> Nil } } @@ -4808,9 +4808,9 @@ fn do_not_remove_required_braces_case_guard() { assert_format!( "fn main() { - let foo = #(10, [0]) - case foo { - foo if True && { foo.0 == 10 || foo.0 == 1 } -> Nil + let wibble = #(10, [0]) + case wibble { + wibble if True && { wibble.0 == 10 || wibble.0 == 1 } -> Nil _ -> Nil } } @@ -4851,17 +4851,17 @@ fn remove_braces_case_guard() { fn remove_braces_case_guard_2() { assert_format_rewrite!( "fn main() { - let foo = #(10, [0]) - case foo { - foo if True && { foo.0 == 10 } -> Nil + let wibble = #(10, [0]) + case wibble { + wibble if True && { wibble.0 == 10 } -> Nil _ -> Nil } } ", "fn main() { - let foo = #(10, [0]) - case foo { - foo if True && foo.0 == 10 -> Nil + let wibble = #(10, [0]) + case wibble { + wibble if True && wibble.0 == 10 -> Nil _ -> Nil } } @@ -5550,7 +5550,7 @@ fn function_call_close_to_line_limit() { fn multiline_string_are_not_broken_with_string_concatenation_if_they_fit() { assert_format!( r#"pub fn main() { - "pub fn foo(" <> arg <> ") ->" <> type_ <> "{ + "pub fn wibble(" <> arg <> ") ->" <> type_ <> "{ body }" } @@ -5581,13 +5581,13 @@ fn nesting_goes_back_to_normal_after_multiline_string() { fn multiline_string_get_broken_on_newlines_as_function_arguments() { assert_format!( r#"pub fn main() { - foo( - bar, - "bar - asd - baz", - foo, - bar, + wibble( + wobble, + "wobble + wibble + wobble", + wibble, + wobble, ) } "# @@ -5598,11 +5598,11 @@ fn multiline_string_get_broken_on_newlines_as_function_arguments() { fn pipeline_used_as_function_arguments_gets_nested() { assert_format!( r#"pub fn main() { - foo( + wibble( a_variable_with_a_long_name |> another_variable_with_a_long_name |> yet_another_variable_with_a_long_name, - bar, + wobble, ) } "# @@ -5613,7 +5613,7 @@ fn pipeline_used_as_function_arguments_gets_nested() { fn pipeline_used_as_function_arguments_is_not_nested_if_it_is_the_only_argument() { assert_format!( r#"pub fn main() { - foo( + wibble( a_variable_with_a_long_name |> another_variable_with_a_long_name |> yet_another_variable_with_a_long_name, @@ -5628,7 +5628,7 @@ fn pipeline_inside_list_gets_nested() { assert_format!( r#"pub fn main() { [ - foo, + wibble, a_variable_with_a_long_name |> another_variable_with_a_long_name |> yet_another_variable_with_a_long_name, @@ -5657,7 +5657,7 @@ fn pipeline_inside_tuple_gets_nested() { assert_format!( r#"pub fn main() { #( - foo, + wibble, a_variable_with_a_long_name |> another_variable_with_a_long_name |> yet_another_variable_with_a_long_name, @@ -5841,7 +5841,7 @@ fn piped_blocks_are_not_needlessly_indented() { { "long enough to need to wrap. blah blah blah blah blah blah blah blah blah" } - |> foo, + |> wibble, 3, ) } @@ -6138,3 +6138,21 @@ fn trailing_comments_inside_non_empty_bit_arrays_are_not_moved() { "# ); } + +// https://github.com/gleam-lang/gleam/issues/3210 +#[test] +fn newlines_are_not_stripped_if_two_consecutive_anonymous_function_are_passed_as_arguments() { + assert_format!( + r#"pub fn main() { + fun( + fn() { + wibble + + wobble + }, + fn() { wibble }, + ) +} +"# + ); +} diff --git a/compiler-core/src/format/tests/binary_operators.rs b/compiler-core/src/format/tests/binary_operators.rs index 36b232368..293582a1e 100644 --- a/compiler-core/src/format/tests/binary_operators.rs +++ b/compiler-core/src/format/tests/binary_operators.rs @@ -25,12 +25,12 @@ pub fn long_comparison_chain() { && trying_other_comparisons < with_ints || trying_other_comparisons <= with_ints && trying_other_comparisons >= with_ints - || and_now_an_equality_check == with_a_function(foo, bar) + || and_now_an_equality_check == with_a_function(wibble, wobble) && trying_other_comparisons >. with_floats - || trying_other_comparisons <. with_floats(baz) + || trying_other_comparisons <. with_floats(wobble) && trying_other_comparisons <=. with_floats - || trying_other_comparisons(foo, bar) >=. with_floats - && foo <> bar + || trying_other_comparisons(wibble, wobble) >=. with_floats + && wibble <> wobble } "# ); @@ -42,7 +42,7 @@ pub fn long_chain_mixing_operators() { r#"pub fn main() { variable + variable - variable * variable / variable == variable * variable / variable - variable + variable - || foo * bar > 11 + || wibble * wobble > 11 } "# ); @@ -51,7 +51,7 @@ pub fn long_chain_mixing_operators() { r#"pub fn main() { variable +. variable -. variable *. variable /. variable == variable *. variable /. variable -. variable +. variable - || foo *. bar >=. 11 + || wibble *. wobble >=. 11 } "# ); @@ -120,7 +120,7 @@ fn labelled_field_with_binary_operators_are_not_broken_if_they_can_fit() { assert_format!( r#"pub fn main() { - Ok(foo( + Ok(wibble( name: names.name, text: text, code: code, @@ -134,7 +134,7 @@ fn labelled_field_with_binary_operators_are_not_broken_if_they_can_fit() { assert_format!( r#"pub fn main() { - Ok(foo( + Ok(wibble( name: names.name, text: text, code: code, @@ -155,8 +155,8 @@ fn math_binops_kept_on_a_single_line_in_pipes() { assert_format!( r#"pub fn main() { 1 + 2 * 3 / 4 - 5 - |> foo - |> bar + |> wibble + |> wobble } "# ); @@ -164,8 +164,8 @@ fn math_binops_kept_on_a_single_line_in_pipes() { assert_format!( r#"pub fn main() { 1 +. 2 *. 3 /. 4 -. 5 - |> foo - |> bar + |> wibble + |> wobble } "# ); @@ -175,11 +175,11 @@ fn math_binops_kept_on_a_single_line_in_pipes() { fn binop_used_as_function_arguments_gets_nested() { assert_format!( r#"pub fn main() { - foo( + wibble( a_variable_with_a_long_name <> another_variable_with_a_long_name <> yet_another_variable_with_a_long_name, - bar, + wobble, ) } "# @@ -190,7 +190,7 @@ fn binop_used_as_function_arguments_gets_nested() { fn binop_is_not_nested_if_the_only_argument() { assert_format!( r#"pub fn main() { - foo( + wibble( a_variable_with_a_long_name <> another_variable_with_a_long_name <> yet_another_variable_with_a_long_name, @@ -205,7 +205,7 @@ fn binop_inside_list_gets_nested() { assert_format!( r#"pub fn main() { [ - foo, + wibble, a_variable_with_a_long_name <> another_variable_with_a_long_name <> yet_another_variable_with_a_long_name, @@ -234,7 +234,7 @@ fn binop_inside_tuple_gets_nested() { assert_format!( r#"pub fn main() { #( - foo, + wibble, a_variable_with_a_long_name <> another_variable_with_a_long_name <> yet_another_variable_with_a_long_name, diff --git a/compiler-core/src/format/tests/blocks.rs b/compiler-core/src/format/tests/blocks.rs index 7cb2d4e42..f6c1fc952 100644 --- a/compiler-core/src/format/tests/blocks.rs +++ b/compiler-core/src/format/tests/blocks.rs @@ -74,8 +74,8 @@ fn last_comments_are_not_moved_out_of_blocks() { assert_format!( r#"fn main() { - case foo { - bar -> { + case wibble { + wobble -> { 1 // Hope I can stay inside this clause } diff --git a/compiler-core/src/format/tests/cases.rs b/compiler-core/src/format/tests/cases.rs index 7acfd8f07..aa98a5fbc 100644 --- a/compiler-core/src/format/tests/cases.rs +++ b/compiler-core/src/format/tests/cases.rs @@ -147,3 +147,19 @@ fn alternatives_are_not_split_if_not_necessary_2() { "# ); } + +#[test] +fn subjects_are_not_split_if_not_necessary() { + assert_format!( + r#"fn main() { + case + is_all_uppercase(remark), + string.ends_with(remark, "?"), + string.trim(remark) == "" + { + _, _, _ -> todo + } +} +"# + ); +} diff --git a/compiler-core/src/format/tests/function.rs b/compiler-core/src/format/tests/function.rs index b4fc2d203..16a765053 100644 --- a/compiler-core/src/format/tests/function.rs +++ b/compiler-core/src/format/tests/function.rs @@ -73,9 +73,9 @@ fn anonymous_function_with_multi_line_long_breakable_body_as_final_function_argu assert_format!( r#"pub fn main() { some_function(123, 456, fn(x) { - call_to_other_function(a, b, c, d, e, f, g, case foo { - Bar -> 1 - Baz -> 2 + call_to_other_function(a, b, c, d, e, f, g, case wibble { + Wibble -> 1 + Wobble -> 2 }) }) } @@ -166,7 +166,7 @@ fn nested_breakable_lists_in_function_calls() { assert_format!( r#"pub fn main() { html([attribute("lang", "en")], [ - head([attribute("foo", "bar")], [ + head([attribute("wibble", "wobble")], [ title([], [text("Hello this is some HTML")]), ]), body([], [h1([], [text("Hello, world!")])]), @@ -181,7 +181,7 @@ fn nested_breakable_tuples_in_function_calls() { assert_format!( r#"pub fn main() { html(#(attribute("lang", "en")), #( - head(#(attribute("foo", "bar")), #( + head(#(attribute("wibble", "wobble")), #( title(#(), #(text("Hello this is some HTML"))), body(#(), #(text("Hello this is some HTML"))), )), diff --git a/compiler-core/src/format/tests/tuple.rs b/compiler-core/src/format/tests/tuple.rs index 58720425b..0ff9a457d 100644 --- a/compiler-core/src/format/tests/tuple.rs +++ b/compiler-core/src/format/tests/tuple.rs @@ -56,7 +56,7 @@ fn tuple_with_last_splittable_arg() { #[test] fn constant_long_list_of_tuples() { assert_format!( - r#"const foo = [ + r#"const wibble = [ #(1, 2), #(3, 4), #(5, 6), #(7, 8), #(9, 10), #(11, 12), #(1, 2), #(3, 4), #(5, 6), #(7, 8), #(9, 10), #(11, 12), ] diff --git a/compiler-core/src/io.rs b/compiler-core/src/io.rs index c1afa4405..cbf87774d 100644 --- a/compiler-core/src/io.rs +++ b/compiler-core/src/io.rs @@ -236,6 +236,7 @@ pub trait FileSystemWriter { fn hardlink(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error>; fn symlink_dir(&self, from: &Utf8Path, to: &Utf8Path) -> Result<(), Error>; fn delete_file(&self, path: &Utf8Path) -> Result<(), Error>; + fn exists(&self, path: &Utf8Path) -> bool; } #[derive(Debug)] diff --git a/compiler-core/src/io/memory.rs b/compiler-core/src/io/memory.rs index 1c8ed16b1..3af9ea78f 100644 --- a/compiler-core/src/io/memory.rs +++ b/compiler-core/src/io/memory.rs @@ -127,6 +127,10 @@ impl FileSystemWriter for InMemoryFileSystem { .insert(path.to_path_buf(), file); Ok(()) } + + fn exists(&self, path: &Utf8Path) -> bool { + self.files.deref().borrow().contains_key(path) + } } impl FileSystemReader for InMemoryFileSystem { diff --git a/compiler-core/src/javascript.rs b/compiler-core/src/javascript.rs index c6199039e..8b1332dd5 100644 --- a/compiler-core/src/javascript.rs +++ b/compiler-core/src/javascript.rs @@ -17,6 +17,7 @@ use crate::{ }; use camino::Utf8Path; use ecow::EcoString; +use expression::Context; use itertools::Itertools; use self::import::{Imports, Member}; @@ -464,11 +465,15 @@ impl<'a> Generator<'a> { } else { "export const " }; + + let document = + expression::constant_expression(Context::Constant, &mut self.tracker, value)?; + Ok(docvec![ head, maybe_escape_identifier_doc(name), " = ", - expression::constant_expression(&mut self.tracker, value)?, + document, ";", ]) } diff --git a/compiler-core/src/javascript/expression.rs b/compiler-core/src/javascript/expression.rs index 1bd94bf5d..3210b6f7f 100644 --- a/compiler-core/src/javascript/expression.rs +++ b/compiler-core/src/javascript/expression.rs @@ -202,6 +202,10 @@ impl<'module> Generator<'module> { TypedExpr::NegateBool { value, .. } => self.negate_with("!", value), TypedExpr::NegateInt { value, .. } => self.negate_with("- ", value), + + TypedExpr::Invalid { .. } => { + panic!("invalid expressions should not reach code generation") + } }?; Ok(if expression.handles_own_return() { document @@ -228,6 +232,20 @@ impl<'module> Generator<'module> { // Sized ints [Opt::Size { value: size, .. }] => { + let size_int = match *size.clone() { + TypedExpr::Int { + location: _, + typ: _, + value, + } => value.parse().unwrap_or(0), + _ => 0, + }; + if size_int > 0 && size_int % 8 != 0 { + return Err(Error::Unsupported { + feature: "Non byte aligned array".into(), + location: segment.location, + }); + } self.tracker.sized_integer_segment_used = true; let size = self.not_in_tail_position(|gen| gen.wrap_expression(size))?; Ok(docvec!["sizedInt(", value, ", ", size, ")"]) @@ -372,7 +390,7 @@ impl<'module> Generator<'module> { ) -> Output<'a> { match &constructor.variant { ValueConstructorVariant::LocalConstant { literal } => { - constant_expression(self.tracker, literal) + constant_expression(Context::Function, self.tracker, literal) } ValueConstructorVariant::Record { arity, .. } => { Ok(self.record_constructor(constructor.type_.clone(), None, name, *arity)) @@ -1232,11 +1250,24 @@ pub(crate) fn guard_constant_expression<'a>( .map(|assignment| assignment.subject.clone().append(assignment.path.clone())) .unwrap_or_else(|| maybe_escape_identifier_doc(name))), - expression => constant_expression(tracker, expression), + expression => constant_expression(Context::Function, tracker, expression), } } +#[derive(Debug, Clone, Copy)] +/// The context where the constant expression is used, it might be inside a +/// function call, or in the definition of another constant. +/// +/// Based on the context we might want to annotate pure function calls as +/// "@__PURE__". +/// +pub enum Context { + Constant, + Function, +} + pub(crate) fn constant_expression<'a>( + context: Context, tracker: &mut UsageTracker, expression: &'a TypedConstant, ) -> Output<'a> { @@ -1244,13 +1275,24 @@ pub(crate) fn constant_expression<'a>( Constant::Int { value, .. } => Ok(int(value)), Constant::Float { value, .. } => Ok(float(value)), Constant::String { value, .. } => Ok(string(value)), - Constant::Tuple { elements, .. } => { - array(elements.iter().map(|e| constant_expression(tracker, e))) - } + Constant::Tuple { elements, .. } => array( + elements + .iter() + .map(|e| constant_expression(context, tracker, e)), + ), Constant::List { elements, .. } => { tracker.list_used = true; - list(elements.iter().map(|e| constant_expression(tracker, e))) + let list = list( + elements + .iter() + .map(|e| constant_expression(context, tracker, e)), + )?; + + match context { + Context::Constant => Ok(docvec!["/* @__PURE__ */ ", list]), + Context::Function => Ok(list), + } } Constant::Record { typ, name, .. } if typ.is_bool() && name == "True" => { @@ -1262,10 +1304,11 @@ pub(crate) fn constant_expression<'a>( Constant::Record { typ, .. } if typ.is_nil() => Ok("undefined".to_doc()), Constant::Record { - tag, - typ, args, module, + name, + tag, + typ, .. } => { if typ.is_result() { @@ -1277,12 +1320,25 @@ pub(crate) fn constant_expression<'a>( } let field_values: Vec<_> = args .iter() - .map(|arg| constant_expression(tracker, &arg.value)) + .map(|arg| constant_expression(context, tracker, &arg.value)) .try_collect()?; - Ok(construct_record(module.as_deref(), tag, field_values)) + + let constructor = construct_record(module.as_deref(), name, field_values); + match context { + Context::Constant => Ok(docvec!["/* @__PURE__ */ ", constructor]), + Context::Function => Ok(constructor), + } } - Constant::BitArray { segments, .. } => bit_array(tracker, segments, constant_expression), + Constant::BitArray { segments, .. } => { + let bit_array = bit_array(tracker, segments, |tracker, expr| { + constant_expression(context, tracker, expr) + })?; + match context { + Context::Constant => Ok(docvec!["/* @__PURE__ */ ", bit_array]), + Context::Function => Ok(bit_array), + } + } Constant::Var { name, module, .. } => Ok({ match module { @@ -1317,6 +1373,16 @@ fn bit_array<'a>( // Sized ints [Opt::Size { value: size, .. }] => { + let size_int = match *size.clone() { + Constant::Int { location: _, value } => value.parse().unwrap_or(0), + _ => 0, + }; + if size_int > 0 && size_int % 8 != 0 { + return Err(Error::Unsupported { + feature: "Non byte aligned array".into(), + location: segment.location, + }); + } tracker.sized_integer_segment_used = true; let size = constant_expr_fun(tracker, size)?; Ok(docvec!["sizedInt(", value, ", ", size, ")"]) @@ -1463,7 +1529,8 @@ impl TypedExpr { | TypedExpr::BitArray { .. } | TypedExpr::RecordUpdate { .. } | TypedExpr::NegateBool { .. } - | TypedExpr::NegateInt { .. } => false, + | TypedExpr::NegateInt { .. } + | TypedExpr::Invalid { .. } => false, } } } @@ -1527,7 +1594,8 @@ fn requires_semicolon(statement: &TypedStatement) -> bool { TypedExpr::Todo { .. } | TypedExpr::Case { .. } | TypedExpr::Panic { .. } - | TypedExpr::Pipeline { .. }, + | TypedExpr::Pipeline { .. } + | TypedExpr::Invalid { .. }, ) => false, Statement::Assignment(_) => false, diff --git a/compiler-core/src/javascript/pattern.rs b/compiler-core/src/javascript/pattern.rs index 26303a265..668928ebb 100644 --- a/compiler-core/src/javascript/pattern.rs +++ b/compiler-core/src/javascript/pattern.rs @@ -183,6 +183,15 @@ impl<'module_ctx, 'expression_gen, 'a> Generator<'module_ctx, 'expression_gen, ' | ClauseGuard::GtEqFloat { .. } | ClauseGuard::LtFloat { .. } | ClauseGuard::LtEqFloat { .. } + | ClauseGuard::AddInt { .. } + | ClauseGuard::AddFloat { .. } + | ClauseGuard::SubInt { .. } + | ClauseGuard::SubFloat { .. } + | ClauseGuard::MultInt { .. } + | ClauseGuard::MultFloat { .. } + | ClauseGuard::DivInt { .. } + | ClauseGuard::DivFloat { .. } + | ClauseGuard::RemainderInt { .. } | ClauseGuard::Or { .. } | ClauseGuard::And { .. } | ClauseGuard::ModuleSelect { .. } => Ok(docvec!("(", self.guard(guard)?, ")")), @@ -243,6 +252,37 @@ impl<'module_ctx, 'expression_gen, 'a> Generator<'module_ctx, 'expression_gen, ' docvec!(left, " <= ", right) } + ClauseGuard::AddFloat { left, right, .. } | ClauseGuard::AddInt { left, right, .. } => { + let left = self.wrapped_guard(left)?; + let right = self.wrapped_guard(right)?; + docvec!(left, " + ", right) + } + + ClauseGuard::SubFloat { left, right, .. } | ClauseGuard::SubInt { left, right, .. } => { + let left = self.wrapped_guard(left)?; + let right = self.wrapped_guard(right)?; + docvec!(left, " - ", right) + } + + ClauseGuard::MultFloat { left, right, .. } + | ClauseGuard::MultInt { left, right, .. } => { + let left = self.wrapped_guard(left)?; + let right = self.wrapped_guard(right)?; + docvec!(left, " * ", right) + } + + ClauseGuard::DivFloat { left, right, .. } | ClauseGuard::DivInt { left, right, .. } => { + let left = self.wrapped_guard(left)?; + let right = self.wrapped_guard(right)?; + docvec!(left, " / ", right) + } + + ClauseGuard::RemainderInt { left, right, .. } => { + let left = self.wrapped_guard(left)?; + let right = self.wrapped_guard(right)?; + docvec!(left, " % ", right) + } + ClauseGuard::Or { left, right, .. } => { let left = self.wrapped_guard(left)?; let right = self.wrapped_guard(right)?; @@ -408,13 +448,13 @@ impl<'module_ctx, 'expression_gen, 'a> Generator<'module_ctx, 'expression_gen, ' self.pop(); } if let Some((left, _)) = left_side_assignment { - // "foo" as prefix <> rest - // ^^^^^^^^^ In case the left prefix of the pattern matching is given an - // alias we bind it to a local variable so that it can be - // correctly referenced inside the case branch. - // let prefix = "foo"; - // ^^^^^^^^^^^^^^^^^^^ we're adding this assignment inside the if clause - // the case branch gets translated into. + // "wibble" as prefix <> rest + // ^^^^^^^^^ In case the left prefix of the pattern matching is given an + // alias we bind it to a local variable so that it can be + // correctly referenced inside the case branch. + // let prefix = "wibble"; + // ^^^^^^^^^^^^^^^^^^^^^ we're adding this assignment inside the if clause + // the case branch gets translated into. self.push_assignment(expression::string(left_side_string), left); } Ok(()) @@ -551,6 +591,7 @@ impl<'module_ctx, 'expression_gen, 'a> Generator<'module_ctx, 'expression_gen, ' feature: "Bit array matching".into(), location: *location, }), + Pattern::Invalid { .. } => panic!("invalid patterns should not reach code generation"), } } diff --git a/compiler-core/src/javascript/tests.rs b/compiler-core/src/javascript/tests.rs index a3baa9dd6..1474a3e95 100644 --- a/compiler-core/src/javascript/tests.rs +++ b/compiler-core/src/javascript/tests.rs @@ -4,9 +4,9 @@ use crate::{ config::PackageConfig, javascript::*, uid::UniqueIdGenerator, - warning::TypeWarningEmitter, + warning::{TypeWarningEmitter, WarningEmitter}, }; -use camino::Utf8Path; +use camino::{Utf8Path, Utf8PathBuf}; mod assignments; mod bit_arrays; @@ -14,6 +14,7 @@ mod blocks; mod bools; mod case; mod case_clause_guards; +mod consts; mod custom_types; mod externals; mod functions; @@ -98,7 +99,12 @@ pub fn compile(src: &str, deps: Vec<(&str, &str, &str)>) -> TypedModule { deps.iter().for_each(|(dep_package, dep_name, dep_src)| { let mut dep_config = PackageConfig::default(); dep_config.name = (*dep_package).into(); - let parsed = crate::parse::parse_module(dep_src).expect("dep syntax error"); + let parsed = crate::parse::parse_module( + Utf8PathBuf::from("test/path"), + dep_src, + &WarningEmitter::null(), + ) + .expect("dep syntax error"); let mut ast = parsed.module; ast.name = (*dep_name).into(); let line_numbers = LineNumbers::new(dep_src); @@ -119,7 +125,9 @@ pub fn compile(src: &str, deps: Vec<(&str, &str, &str)>) -> TypedModule { let _ = direct_dependencies.insert((*dep_package).into(), ()); }); - let parsed = crate::parse::parse_module(src).expect("syntax error"); + let parsed = + crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) + .expect("syntax error"); let mut ast = parsed.module; ast.name = "my/mod".into(); let line_numbers = LineNumbers::new(src); diff --git a/compiler-core/src/javascript/tests/assignments.rs b/compiler-core/src/javascript/tests/assignments.rs index 1ef6eb8c0..6864bea6d 100644 --- a/compiler-core/src/javascript/tests/assignments.rs +++ b/compiler-core/src/javascript/tests/assignments.rs @@ -37,19 +37,19 @@ fn variable_renaming() { assert_js!( r#" -fn go(x, foo) { +fn go(x, wibble) { let a = 1 - foo(a) + wibble(a) let a = 2 - foo(a) + wibble(a) let assert #(a, 3) = x let b = a - foo(b) + wibble(b) let c = { let a = a #(a, b) } - foo(a) + wibble(a) // make sure arguments are counted in initial state let x = c x @@ -97,10 +97,10 @@ fn rebound_argument() { #[test] fn rebound_function() { assert_js!( - r#"pub fn x() { + r#"pub fn x() { Nil } - + pub fn main() { let x = False x @@ -112,10 +112,10 @@ pub fn main() { #[test] fn rebound_function_and_arg() { assert_js!( - r#"pub fn x() { + r#"pub fn x() { Nil } - + pub fn main(x) { let x = False x @@ -153,7 +153,7 @@ fn module_const_var() { assert_js!( r#" pub const int = 42 -pub const int_alias = int +pub const int_alias = int pub fn use_int_alias() { int_alias } pub const compound: #(Int, Int) = #(int, int_alias) @@ -167,7 +167,7 @@ fn module_const_var1() { assert_ts_def!( r#" pub const int = 42 -pub const int_alias = int +pub const int_alias = int pub const compound: #(Int, Int) = #(int, int_alias) "# ); diff --git a/compiler-core/src/javascript/tests/bit_arrays.rs b/compiler-core/src/javascript/tests/bit_arrays.rs index 5232f82f7..a3cbd1a16 100644 --- a/compiler-core/src/javascript/tests/bit_arrays.rs +++ b/compiler-core/src/javascript/tests/bit_arrays.rs @@ -60,7 +60,7 @@ fn sized() { assert_js!( r#" fn go() { - <<256:4>> + <<256:64>> } "#, ); @@ -71,7 +71,7 @@ fn explicit_sized() { assert_js!( r#" fn go() { - <<256:size(4)>> + <<256:size(64)>> } "#, ); @@ -296,3 +296,56 @@ fn as_module_const() { "# ) } + +#[test] +fn negative_size() { + assert_js!( + r#" +fn go() { + <<1:size(-1)>> +} +"#, + ); +} + +// https://github.com/gleam-lang/gleam/issues/1591 +#[test] +fn not_byte_aligned() { + assert_js!( + r#" +fn thing() { + 4 +} + +fn go() { + <<256:4>> +} +"#, + ); +} + +#[test] +fn not_byte_aligned_explicit_sized() { + assert_js!( + r#" +fn go() { + <<256:size(4)>> +} +"#, + ); +} + +// This test would ideally also result in go() being deleted like the previous tests +// but we can not know for sure what the value of a variable is going to be +// so right now go() is not deleted. +#[test] +fn not_byte_aligned_variable() { + assert_js!( + r#" +fn go() { + let x = 4 + <<256:size(x)>> +} +"#, + ); +} diff --git a/compiler-core/src/javascript/tests/consts.rs b/compiler-core/src/javascript/tests/consts.rs new file mode 100644 index 000000000..d9af208a9 --- /dev/null +++ b/compiler-core/src/javascript/tests/consts.rs @@ -0,0 +1,122 @@ +use crate::assert_js; + +#[test] +fn custom_type_constructor_imported_and_aliased() { + assert_js!( + ("package", "other_module", "pub type T { A }"), + r#"import other_module.{A as B} + +pub const local = B +"#, + ); +} + +#[test] +fn imported_aliased_ok() { + assert_js!( + r#"import gleam.{Ok as Y} + +pub type X { + Ok +} + +pub const y = Y +"#, + ); +} + +#[test] +fn imported_ok() { + assert_js!( + r#"import gleam + +pub type X { + Ok +} + +pub const y = gleam.Ok +"#, + ); +} + +#[test] +fn constant_constructor_gets_pure_annotation() { + assert_js!( + r#" +pub type X { + X(Int, List(String)) +} + +pub const x = X(1, ["1"]) +const y = X(1, []) + "# + ); +} + +#[test] +fn constant_list_with_constructors_gets_pure_annotation() { + assert_js!( + r#" +pub type X { + X(Int, List(String)) +} + +pub const x = [X(1, ["1"])] +const y = [X(1, ["1"])] + "# + ); +} + +#[test] +fn constant_tuple_with_constructors_gets_pure_annotation() { + assert_js!( + r#" +pub type X { + X(Int, List(String)) +} + +pub const x = #(X(1, ["1"])) +const y = #(X(1, ["1"])) + "# + ); +} + +#[test] +fn literal_int_does_not_get_constant_annotation() { + assert_js!("pub const a = 1"); +} + +#[test] +fn literal_float_does_not_get_constant_annotation() { + assert_js!("pub const a = 1.1"); +} + +#[test] +fn literal_string_does_not_get_constant_annotation() { + assert_js!("pub const a = \"1\""); +} + +#[test] +fn literal_bool_does_not_get_constant_annotation() { + assert_js!( + " + pub const a = True + pub const b = False + " + ); +} + +#[test] +fn literal_list_does_not_get_constant_annotation() { + assert_js!("pub const a = [1, 2, 3]"); +} + +#[test] +fn literal_tuple_does_not_get_constant_annotation() { + assert_js!("pub const a = #(1, 2, 3)"); +} + +#[test] +fn literal_nil_does_not_get_constant_annotation() { + assert_js!("pub const a = Nil"); +} diff --git a/compiler-core/src/javascript/tests/functions.rs b/compiler-core/src/javascript/tests/functions.rs index a90976730..4ec9c697b 100644 --- a/compiler-core/src/javascript/tests/functions.rs +++ b/compiler-core/src/javascript/tests/functions.rs @@ -370,9 +370,9 @@ pub fn version(n) { #[test] fn pipe_shadow_import() { assert_js!( - (CURRENT_PACKAGE, "foo", "pub fn println(x: String) { }"), + (CURRENT_PACKAGE, "wibble", "pub fn println(x: String) { }"), r#" - import foo.{println} + import wibble.{println} pub fn main() { let println = "oh dear" diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__assignments__variable_renaming.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__assignments__variable_renaming.snap index db0bb59db..fb898ca40 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__assignments__variable_renaming.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__assignments__variable_renaming.snap @@ -1,14 +1,14 @@ --- source: compiler-core/src/javascript/tests/assignments.rs -expression: "\n\nfn go(x, foo) {\n let a = 1\n foo(a)\n let a = 2\n foo(a)\n let assert #(a, 3) = x\n let b = a\n foo(b)\n let c = {\n let a = a\n #(a, b)\n }\n foo(a)\n // make sure arguments are counted in initial state\n let x = c\n x\n}\n" +expression: "\n\nfn go(x, wibble) {\n let a = 1\n wibble(a)\n let a = 2\n wibble(a)\n let assert #(a, 3) = x\n let b = a\n wibble(b)\n let c = {\n let a = a\n #(a, b)\n }\n wibble(a)\n // make sure arguments are counted in initial state\n let x = c\n x\n}\n" --- import { makeError } from "../gleam.mjs"; -function go(x, foo) { +function go(x, wibble) { let a = 1; - foo(a); + wibble(a); let a$1 = 2; - foo(a$1); + wibble(a$1); if (x[1] !== 3) { throw makeError( "assignment_no_match", @@ -21,12 +21,12 @@ function go(x, foo) { } let a$2 = x[0]; let b = a$2; - foo(b); + wibble(b); let c = (() => { let a$3 = a$2; return [a$3, b]; })(); - foo(a$2); + wibble(a$2); let x$1 = c; return x$1; } diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__as_module_const.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__as_module_const.snap index 1a21622d2..a5aa88c4c 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__as_module_const.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__as_module_const.snap @@ -4,12 +4,16 @@ expression: "\n pub const data = <<\n 0x1,\n 2,\n --- import { toBitArray, sizedInt, stringBits, float64Bits } from "../gleam.mjs"; -export const data = toBitArray([ +export const data = /* @__PURE__ */ toBitArray([ 0x1, 2, sizedInt(2, 16), sizedInt(0x4, 32), stringBits("Gleam"), float64Bits(4.2), - toBitArray([toBitArray([1, 2, 3]).buffer, stringBits("Gleam"), 1024]).buffer, + /* @__PURE__ */ toBitArray([ + /* @__PURE__ */ toBitArray([1, 2, 3]).buffer, + stringBits("Gleam"), + 1024, + ]).buffer, ]); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__explicit_sized.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__explicit_sized.snap index 458c11f2b..28012fd84 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__explicit_sized.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__explicit_sized.snap @@ -5,5 +5,5 @@ expression: "\nfn go() {\n <<256:size(4)>>\n}\n" import { toBitArray, sizedInt } from "../gleam.mjs"; function go() { - return toBitArray([sizedInt(256, 4)]); + return toBitArray([sizedInt(256, 64)]); } diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__negative_size.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__negative_size.snap new file mode 100644 index 000000000..65205e37e --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__negative_size.snap @@ -0,0 +1,9 @@ +--- +source: compiler-core/src/javascript/tests/bit_arrays.rs +expression: "\nfn go() {\n <<1:size(-1)>>\n}\n" +--- +import { toBitArray, sizedInt } from "../gleam.mjs"; + +function go() { + return toBitArray([sizedInt(1, -1)]); +} diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__not_byte_aligned.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__not_byte_aligned.snap new file mode 100644 index 000000000..db89b02a3 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__not_byte_aligned.snap @@ -0,0 +1,9 @@ +--- +source: compiler-core/src/javascript/tests/bit_arrays.rs +expression: "\nfn thing() {\n 4\n}\n\nfn go() {\n <<256:4>>\n}\n" +--- +import { toBitArray } from "../gleam.mjs"; + +function thing() { + return 4; +} diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__not_byte_aligned_explicit_sized.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__not_byte_aligned_explicit_sized.snap new file mode 100644 index 000000000..84b6b230c --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__not_byte_aligned_explicit_sized.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/javascript/tests/bit_arrays.rs +expression: "\nfn go() {\n <<256:size(4)>>\n}\n" +--- +import { toBitArray } from "../gleam.mjs"; diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__not_byte_aligned_variable.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__not_byte_aligned_variable.snap new file mode 100644 index 000000000..f3e118f94 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__not_byte_aligned_variable.snap @@ -0,0 +1,10 @@ +--- +source: compiler-core/src/javascript/tests/bit_arrays.rs +expression: "\nfn go() {\n let x = 4\n <<256:size(x)>>\n}\n" +--- +import { toBitArray, sizedInt } from "../gleam.mjs"; + +function go() { + let x = 4; + return toBitArray([sizedInt(256, x)]); +} diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__sized.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__sized.snap index 3c94149d1..c8a826a65 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__sized.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__bit_arrays__sized.snap @@ -5,5 +5,5 @@ expression: "\nfn go() {\n <<256:4>>\n}\n" import { toBitArray, sizedInt } from "../gleam.mjs"; function go() { - return toBitArray([sizedInt(256, 4)]); + return toBitArray([sizedInt(256, 64)]); } diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__constant_constructor_gets_pure_annotation.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__constant_constructor_gets_pure_annotation.snap new file mode 100644 index 000000000..899a2dd85 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__constant_constructor_gets_pure_annotation.snap @@ -0,0 +1,17 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: "\npub type X {\n X(Int, List(String))\n}\n\npub const x = X(1, [\"1\"])\nconst y = X(1, [])\n " +--- +import { toList, CustomType as $CustomType } from "../gleam.mjs"; + +export class X extends $CustomType { + constructor(x0, x1) { + super(); + this[0] = x0; + this[1] = x1; + } +} + +export const x = /* @__PURE__ */ new X(1, /* @__PURE__ */ toList(["1"])); + +const y = /* @__PURE__ */ new X(1, /* @__PURE__ */ toList([])); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__constant_list_with_constructors_gets_pure_annotation.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__constant_list_with_constructors_gets_pure_annotation.snap new file mode 100644 index 000000000..4faa48d1e --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__constant_list_with_constructors_gets_pure_annotation.snap @@ -0,0 +1,21 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: "\npub type X {\n X(Int, List(String))\n}\n\npub const x = [X(1, [\"1\"])]\nconst y = [X(1, [\"1\"])]\n " +--- +import { toList, CustomType as $CustomType } from "../gleam.mjs"; + +export class X extends $CustomType { + constructor(x0, x1) { + super(); + this[0] = x0; + this[1] = x1; + } +} + +export const x = /* @__PURE__ */ toList([ + /* @__PURE__ */ new X(1, /* @__PURE__ */ toList(["1"])), +]); + +const y = /* @__PURE__ */ toList([ + /* @__PURE__ */ new X(1, /* @__PURE__ */ toList(["1"])), +]); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__constant_tuple_with_constructors_gets_pure_annotation.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__constant_tuple_with_constructors_gets_pure_annotation.snap new file mode 100644 index 000000000..830793305 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__constant_tuple_with_constructors_gets_pure_annotation.snap @@ -0,0 +1,17 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: "\npub type X {\n X(Int, List(String))\n}\n\npub const x = #(X(1, [\"1\"]))\nconst y = #(X(1, [\"1\"]))\n " +--- +import { toList, CustomType as $CustomType } from "../gleam.mjs"; + +export class X extends $CustomType { + constructor(x0, x1) { + super(); + this[0] = x0; + this[1] = x1; + } +} + +export const x = [/* @__PURE__ */ new X(1, /* @__PURE__ */ toList(["1"]))]; + +const y = [/* @__PURE__ */ new X(1, /* @__PURE__ */ toList(["1"]))]; diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__custom_type_constructor_imported_and_aliased.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__custom_type_constructor_imported_and_aliased.snap new file mode 100644 index 000000000..f1d987ba6 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__custom_type_constructor_imported_and_aliased.snap @@ -0,0 +1,8 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: "import other_module.{A as B}\n\npub const local = B\n" +--- +import * as $other_module from "../../package/other_module.mjs"; +import { A as B } from "../../package/other_module.mjs"; + +export const local = /* @__PURE__ */ new B(); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__imported_aliased_ok.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__imported_aliased_ok.snap new file mode 100644 index 000000000..ff0e4333b --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__imported_aliased_ok.snap @@ -0,0 +1,10 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: "import gleam.{Ok as Y}\n\npub type X {\n Ok\n}\n\npub const y = Y\n" +--- +import * as $gleam from "../gleam.mjs"; +import { Ok as Y, CustomType as $CustomType } from "../gleam.mjs"; + +export class Ok extends $CustomType {} + +export const y = /* @__PURE__ */ new Y(); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__imported_ok.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__imported_ok.snap new file mode 100644 index 000000000..7d3e72aa3 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__imported_ok.snap @@ -0,0 +1,10 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: "import gleam\n\npub type X {\n Ok\n}\n\npub const y = gleam.Ok\n" +--- +import * as $gleam from "../gleam.mjs"; +import { CustomType as $CustomType } from "../gleam.mjs"; + +export class Ok extends $CustomType {} + +export const y = /* @__PURE__ */ new $gleam.Ok(); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_bool_does_not_get_constant_annotation.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_bool_does_not_get_constant_annotation.snap new file mode 100644 index 000000000..593788ca9 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_bool_does_not_get_constant_annotation.snap @@ -0,0 +1,7 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: "\n pub const a = True\n pub const b = False\n " +--- +export const a = true; + +export const b = false; diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_float_does_not_get_constant_annotation.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_float_does_not_get_constant_annotation.snap new file mode 100644 index 000000000..06e11be3e --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_float_does_not_get_constant_annotation.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: pub const a = 1.1 +--- +export const a = 1.1; diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_int_does_not_get_constant_annotation.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_int_does_not_get_constant_annotation.snap new file mode 100644 index 000000000..00f44d98c --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_int_does_not_get_constant_annotation.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: pub const a = 1 +--- +export const a = 1; diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_list_does_not_get_constant_annotation.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_list_does_not_get_constant_annotation.snap new file mode 100644 index 000000000..efb3f8213 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_list_does_not_get_constant_annotation.snap @@ -0,0 +1,7 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: "pub const a = [1, 2, 3]" +--- +import { toList } from "../gleam.mjs"; + +export const a = /* @__PURE__ */ toList([1, 2, 3]); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_nil_does_not_get_constant_annotation.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_nil_does_not_get_constant_annotation.snap new file mode 100644 index 000000000..c18194192 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_nil_does_not_get_constant_annotation.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: pub const a = Nil +--- +export const a = undefined; diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_string_does_not_get_constant_annotation.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_string_does_not_get_constant_annotation.snap new file mode 100644 index 000000000..3fdc56ff8 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_string_does_not_get_constant_annotation.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: "pub const a = \"1\"" +--- +export const a = "1"; diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_tuple_does_not_get_constant_annotation.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_tuple_does_not_get_constant_annotation.snap new file mode 100644 index 000000000..12e997669 --- /dev/null +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__consts__literal_tuple_does_not_get_constant_annotation.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/javascript/tests/consts.rs +expression: "pub const a = #(1, 2, 3)" +--- +export const a = [1, 2, 3]; diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_ignoring_label.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_ignoring_label.snap index 2005a02be..c37839f9a 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_ignoring_label.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_ignoring_label.snap @@ -4,4 +4,4 @@ expression: "import other\npub const main = other.Two(1)\n" --- import * as $other from "../other.mjs"; -export const main = new $other.Two(1); +export const main = /* @__PURE__ */ new $other.Two(1); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_multiple_fields.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_multiple_fields.snap index fe3eff122..591b919e8 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_multiple_fields.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_multiple_fields.snap @@ -4,4 +4,4 @@ expression: "import other\npub const main = other.Two(b: 2, c: 3, a: 1)\n" --- import * as $other from "../other.mjs"; -export const main = new $other.Two(1, 2, 3); +export const main = /* @__PURE__ */ new $other.Two(1, 2, 3); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_no_label.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_no_label.snap index 2005a02be..c37839f9a 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_no_label.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_no_label.snap @@ -4,4 +4,4 @@ expression: "import other\npub const main = other.Two(1)\n" --- import * as $other from "../other.mjs"; -export const main = new $other.Two(1); +export const main = /* @__PURE__ */ new $other.Two(1); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_using_label.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_using_label.snap index 78069eff5..265c7ea3e 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_using_label.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_imported_using_label.snap @@ -4,4 +4,4 @@ expression: "import other\npub const main = other.Two(field: 1)\n" --- import * as $other from "../other.mjs"; -export const main = new $other.Two(1); +export const main = /* @__PURE__ */ new $other.Two(1); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_ignoring_label.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_ignoring_label.snap index 3a233b459..9a237af6f 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_ignoring_label.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_ignoring_label.snap @@ -5,4 +5,4 @@ expression: "import other.{Two}\npub const main = Two(1)\n" import * as $other from "../other.mjs"; import { Two } from "../other.mjs"; -export const main = new Two(1); +export const main = /* @__PURE__ */ new Two(1); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_multiple_fields.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_multiple_fields.snap index bc137e44e..f2125f0bf 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_multiple_fields.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_multiple_fields.snap @@ -5,4 +5,4 @@ expression: "import other.{Two}\npub const main = Two(b: 2, c: 3, a: 1)\n" import * as $other from "../other.mjs"; import { Two } from "../other.mjs"; -export const main = new Two(1, 2, 3); +export const main = /* @__PURE__ */ new Two(1, 2, 3); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_no_label.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_no_label.snap index 3a233b459..9a237af6f 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_no_label.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_no_label.snap @@ -5,4 +5,4 @@ expression: "import other.{Two}\npub const main = Two(1)\n" import * as $other from "../other.mjs"; import { Two } from "../other.mjs"; -export const main = new Two(1); +export const main = /* @__PURE__ */ new Two(1); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_using_label.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_using_label.snap index 062454011..4b0bd12a9 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_using_label.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_unqualified_imported_using_label.snap @@ -5,4 +5,4 @@ expression: "import other.{Two}\npub const main = Two(field: 1)\n" import * as $other from "../other.mjs"; import { Two } from "../other.mjs"; -export const main = new Two(1); +export const main = /* @__PURE__ */ new Two(1); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_with_fields.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_with_fields.snap index 42ffef397..ab4f6e73c 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_with_fields.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_with_fields.snap @@ -12,6 +12,6 @@ class Mine extends $CustomType { } } -const labels = new Mine(1, 2); +const labels = /* @__PURE__ */ new Mine(1, 2); -const no_labels = new Mine(3, 4); +const no_labels = /* @__PURE__ */ new Mine(3, 4); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_zero_arity_imported.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_zero_arity_imported.snap index 5d1a787fb..b5c52a155 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_zero_arity_imported.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_zero_arity_imported.snap @@ -4,4 +4,4 @@ expression: "import other\nconst x = other.Two\n" --- import * as $other from "../other.mjs"; -const x = new $other.Two(); +const x = /* @__PURE__ */ new $other.Two(); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_zero_arity_imported_unqualified.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_zero_arity_imported_unqualified.snap index 2c20f8353..f45a5405f 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_zero_arity_imported_unqualified.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__const_zero_arity_imported_unqualified.snap @@ -5,4 +5,4 @@ expression: "import other.{Two}\nconst a = Two\n" import * as $other from "../other.mjs"; import { Two } from "../other.mjs"; -const a = new Two(); +const a = /* @__PURE__ */ new Two(); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__custom_type_with_named_fields.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__custom_type_with_named_fields.snap index 8f4d83a1e..4d99df729 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__custom_type_with_named_fields.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__custom_type_with_named_fields.snap @@ -41,6 +41,6 @@ function update(cat) { return box.occupant.withFields({ cuteness: box.occupant.cuteness + 1 }); } -const felix = new Cat("Felix", 12); +const felix = /* @__PURE__ */ new Cat("Felix", 12); -const tom = new Cat("Tom", 1); +const tom = /* @__PURE__ */ new Cat("Tom", 1); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__unnamed_fields.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__unnamed_fields.snap index 5d764ecd2..a6bd0acdc 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__unnamed_fields.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__unnamed_fields.snap @@ -25,4 +25,4 @@ function destructure(x) { return raw; } -const local = new Ip("0.0.0.0"); +const local = /* @__PURE__ */ new Ip("0.0.0.0"); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__zero_arity_const.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__zero_arity_const.snap index 005fba84e..1cf436521 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__zero_arity_const.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__custom_types__zero_arity_const.snap @@ -8,6 +8,6 @@ class This extends $CustomType {} class ThatOneIsAMuchMuchMuchMuchMuchMuchMuchMuchMuchMuchMuchMuchLongerVariant extends $CustomType {} -const this$ = new This(); +const this$ = /* @__PURE__ */ new This(); -const that = new ThatOneIsAMuchMuchMuchMuchMuchMuchMuchMuchMuchMuchMuchMuchLongerVariant(); +const that = /* @__PURE__ */ new ThatOneIsAMuchMuchMuchMuchMuchMuchMuchMuchMuchMuchMuchMuchLongerVariant(); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__functions__pipe_shadow_import.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__functions__pipe_shadow_import.snap index 8920a9f49..9a5e4bf17 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__functions__pipe_shadow_import.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__functions__pipe_shadow_import.snap @@ -1,9 +1,9 @@ --- source: compiler-core/src/javascript/tests/functions.rs -expression: "\n import foo.{println}\n pub fn main() {\n let println =\n \"oh dear\"\n |> println\n println\n }" +expression: "\n import wibble.{println}\n pub fn main() {\n let println =\n \"oh dear\"\n |> println\n println\n }" --- -import * as $foo from "../foo.mjs"; -import { println } from "../foo.mjs"; +import * as $wibble from "../wibble.mjs"; +import { println } from "../wibble.mjs"; export function main() { let println$1 = (() => { diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__lists__list_constants.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__lists__list_constants.snap index 11b913111..9966712cb 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__lists__list_constants.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__lists__list_constants.snap @@ -4,6 +4,6 @@ expression: "\nconst a = []\nconst b = [1, 2, 3]\n" --- import { toList } from "../gleam.mjs"; -const a = toList([]); +const a = /* @__PURE__ */ toList([]); -const b = toList([1, 2, 3]); +const b = /* @__PURE__ */ toList([1, 2, 3]); diff --git a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__strings__string_prefix_utf16.snap b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__strings__string_prefix_utf16.snap index ba652a74d..d69a20957 100644 --- a/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__strings__string_prefix_utf16.snap +++ b/compiler-core/src/javascript/tests/snapshots/glistix_core__javascript__tests__strings__string_prefix_utf16.snap @@ -1,9 +1,9 @@ --- source: compiler-core/src/javascript/tests/strings.rs -expression: "\npub fn go(x) {\n case \"Θ foo bar\" {\n \"Θ\" <> rest -> rest\n _ -> \"\"\n }\n case \"🫥 is neutral dotted\" {\n \"🫥\" <> rest -> rest\n _ -> \"\"\n }\n case \"🇺🇸 is a cluster\" {\n \"🇺🇸\" <> rest -> rest\n _ -> \"\"\n }\n case \"\\\" is a an escaped quote\" {\n \"\\\"\" <> rest -> rest\n _ -> \"\"\n }\n case \"\\\\ is a an escaped backslash\" {\n \"\\\\\" <> rest -> rest\n _ -> \"\"\n }\n}\n" +expression: "\npub fn go(x) {\n case \"Θ wibble wobble\" {\n \"Θ\" <> rest -> rest\n _ -> \"\"\n }\n case \"🫥 is neutral dotted\" {\n \"🫥\" <> rest -> rest\n _ -> \"\"\n }\n case \"🇺🇸 is a cluster\" {\n \"🇺🇸\" <> rest -> rest\n _ -> \"\"\n }\n case \"\\\" is a an escaped quote\" {\n \"\\\"\" <> rest -> rest\n _ -> \"\"\n }\n case \"\\\\ is a an escaped backslash\" {\n \"\\\\\" <> rest -> rest\n _ -> \"\"\n }\n}\n" --- export function go(x) { - let $ = "Θ foo bar"; + let $ = "Θ wibble wobble"; if ($.startsWith("Θ")) { let rest = $.slice(1); rest diff --git a/compiler-core/src/javascript/tests/strings.rs b/compiler-core/src/javascript/tests/strings.rs index 7088fedfb..764d2cf8a 100644 --- a/compiler-core/src/javascript/tests/strings.rs +++ b/compiler-core/src/javascript/tests/strings.rs @@ -125,7 +125,7 @@ fn string_prefix_utf16() { assert_js!( r#" pub fn go(x) { - case "Θ foo bar" { + case "Θ wibble wobble" { "Θ" <> rest -> rest _ -> "" } diff --git a/compiler-core/src/language_server.rs b/compiler-core/src/language_server.rs index 168beccd3..21e4a6762 100644 --- a/compiler-core/src/language_server.rs +++ b/compiler-core/src/language_server.rs @@ -1,5 +1,6 @@ mod code_action; mod compiler; +mod completer; mod engine; mod feedback; mod files; diff --git a/compiler-core/src/language_server/code_action.rs b/compiler-core/src/language_server/code_action.rs index 0270063df..40d950892 100644 --- a/compiler-core/src/language_server/code_action.rs +++ b/compiler-core/src/language_server/code_action.rs @@ -1,7 +1,4 @@ -use std::sync::Arc; - -use ecow::EcoString; -use lsp_types::{CodeAction, CodeActionKind, CodeActionParams, TextEdit, Url}; +use std::{iter, sync::Arc}; use crate::{ ast::{self, visit::Visit as _, SrcSpan}, @@ -10,6 +7,8 @@ use crate::{ parse::extra::ModuleExtra, type_::Type, }; +use ecow::EcoString; +use lsp_types::{CodeAction, CodeActionKind, CodeActionParams, TextEdit, Url}; use super::{engine::overlaps, src_span_to_lsp_range}; @@ -134,6 +133,10 @@ impl<'ast> ast::visit::Visit<'ast> for RedundantTupleInCaseSubject<'_> { )) } + Some(ast::Pattern::Discard { location, .. }) => { + clause_edits.push(self.discard_tuple_items(*location, elems.len())) + } + // Do not edit for this subject at all and go to the next subject _ => continue 'subj, } @@ -263,4 +266,13 @@ impl<'a> RedundantTupleInCaseSubject<'a> { edits } + + fn discard_tuple_items(&self, discard_location: SrcSpan, tuple_items: usize) -> TextEdit { + // Replace the old discard with multiple discard, one for each of the + // tuple items. + TextEdit { + range: src_span_to_lsp_range(discard_location, &self.line_numbers), + new_text: itertools::intersperse(iter::repeat("_").take(tuple_items), ", ").collect(), + } + } } diff --git a/compiler-core/src/language_server/compiler.rs b/compiler-core/src/language_server/compiler.rs index 6685761c4..2517a85ff 100644 --- a/compiler-core/src/language_server/compiler.rs +++ b/compiler-core/src/language_server/compiler.rs @@ -183,7 +183,7 @@ where } } - pub fn get_module_inferface(&self, name: &str) -> Option<&ModuleInterface> { + pub fn get_module_interface(&self, name: &str) -> Option<&ModuleInterface> { self.project_compiler.get_importable_modules().get(name) } } diff --git a/compiler-core/src/language_server/completer.rs b/compiler-core/src/language_server/completer.rs new file mode 100644 index 000000000..b395771a8 --- /dev/null +++ b/compiler-core/src/language_server/completer.rs @@ -0,0 +1,703 @@ +use ecow::EcoString; +use lsp_types::{ + CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionTextEdit, + Documentation, MarkupContent, MarkupKind, Position, Range, TextDocumentPositionParams, + TextEdit, +}; +use strum::IntoEnumIterator; + +use crate::{ + ast::{Definition, Import, Publicity, TypedDefinition}, + build::Module, + io::{CommandExecutor, FileSystemReader, FileSystemWriter}, + line_numbers::LineNumbers, + type_::{ + pretty::Printer, ModuleInterface, PreludeType, TypeConstructor, ValueConstructorVariant, + }, + Result, +}; + +use super::{ + compiler::LspProjectCompiler, files::FileSystemProxy, DownloadDependencies, MakeLocker, +}; + +// The form in which a type completion is needed in context. +// Mainly used to determine if the "type" keyword should be appended to the completion +enum TypeCompletionForm { + // The type completion is for an unqualified import. + UnqualifiedImport, + Default, +} + +pub struct Completer<'a, IO> { + /// The direct buffer source code + src: &'a EcoString, + /// The line number information of the buffer source code + src_line_numbers: LineNumbers, + /// The current cursor position within the buffer source code + cursor_position: &'a Position, + /// A reference to the lsp compiler for getting module information + compiler: &'a LspProjectCompiler>, + /// A reference to the current module the completion is for + module: &'a Module, + /// The line number information of the latest compiled module. + /// This is not necessarily the same as src_line_numbers if the module + /// is in a non-compiling state + pub module_line_numbers: LineNumbers, +} + +impl<'a, IO> Completer<'a, IO> +where + // IO to be supplied from outside of gleam-core + IO: FileSystemReader + + FileSystemWriter + + CommandExecutor + + DownloadDependencies + + MakeLocker + + Clone, +{ + pub fn new( + src: &'a EcoString, + params: &'a TextDocumentPositionParams, + compiler: &'a LspProjectCompiler>, + module: &'a Module, + ) -> Self { + Completer { + src, + src_line_numbers: LineNumbers::new(src.as_str()), + cursor_position: ¶ms.position, + compiler, + module, + module_line_numbers: LineNumbers::new(&module.code), + } + } + + // Gets the current range around the cursor to place a completion + // and the phrase surrounding the cursor to use for completion. + // This method takes in a helper to determine what qualifies as + // a phrase depending on context. + fn get_phrase_surrounding_for_completion( + &'a self, + valid_phrase_char: &impl Fn(char) -> bool, + ) -> (Range, String) { + let cursor = self + .src_line_numbers + .byte_index(self.cursor_position.line, self.cursor_position.character); + + // Get part of phrase prior to cursor + let before = self + .src + .get(..cursor as usize) + .and_then(|line| line.rsplit_once(valid_phrase_char).map(|r| r.1)) + .unwrap_or(""); + // Get part of phrase following cursor + let after = self + .src + .get(cursor as usize..) + .and_then(|line| line.split_once(valid_phrase_char).map(|r| r.0)) + .unwrap_or(""); + + ( + Range { + start: Position { + line: self.cursor_position.line, + character: self.cursor_position.character - before.len() as u32, + }, + end: Position { + line: self.cursor_position.line, + character: self.cursor_position.character + after.len() as u32, + }, + }, + format!("{}{}", before, after), + ) + } + + // Gets the current range around the cursor to place a completion + // and any part of the phrase preceeding a dot if a module is being selected from. + // A continuous phrase in this case is a name or typename that may have a dot in it. + // This is used to match the exact location to fill in the completion. + fn get_phrase_surrounding_completion(&'a self) -> (Range, Option) { + let valid_phrase_char = |c: char| { + // Checks if a character is not a valid name/upname character or a dot. + !c.is_ascii_alphanumeric() && c != '.' && c != '_' + }; + let (range, word) = self.get_phrase_surrounding_for_completion(&valid_phrase_char); + (range, word.split_once('.').map(|c| String::from(c.0))) + } + + // Gets the current range around the cursor to place a completion. + // For unqualified imports we special case the word being completed to allow for whitespace but not dots. + // This is to allow `type MyType` to be treated as 1 "phrase" for the sake of completion. + fn get_phrase_surrounding_completion_for_import(&'a self) -> Range { + let valid_phrase_char = |c: char| { + // Checks if a character is not a valid name/upname character or whitespace. + // The newline character is not included as well. + !c.is_ascii_alphanumeric() && c != '_' && c != ' ' && c != '\t' + }; + let (range, _) = self.get_phrase_surrounding_for_completion(&valid_phrase_char); + range + } + + /// Checks if the line being editted is an import line and provides completions if it is. + /// If the line includes a dot then it provides unqualified import completions. + /// Otherwise it provides direct module import completions. + pub fn import_completions(&'a self) -> Option>>> { + let start_of_line = self + .src_line_numbers + .byte_index(self.cursor_position.line, 0); + let end_of_line = self + .src_line_numbers + .byte_index(self.cursor_position.line + 1, 0); + + // Drop all lines except the line the cursor is on + let src = self.src.get(start_of_line as usize..end_of_line as usize)?; + + // If this isn't an import line then we don't offer import completions + if !src.trim_start().starts_with("import") { + return None; + } + + // Check if we are completing an unqualified import + if let Some(dot_index) = src.find('.') { + // Find the module that is being imported from + let importing_module_name = src.get(6..dot_index)?.trim(); + let importing_module: &ModuleInterface = + self.compiler.get_module_interface(importing_module_name)?; + + Some(Ok(Some( + self.unqualified_completions_from_module(importing_module), + ))) + } else { + // Find where to start and end the import completion + let start = self.src_line_numbers.line_and_column_number(start_of_line); + let end = self + .src_line_numbers + .line_and_column_number(end_of_line - 1); + let start = Position::new(start.line - 1, start.column + 6); + let end = Position::new(end.line - 1, end.column - 1); + let completions = self.complete_modules_for_import(start, end); + + Some(Ok(Some(completions))) + } + } + + /// Gets the completes for unqualified imports from a module. + pub fn unqualified_completions_from_module( + &'a self, + module_being_imported_from: &'a ModuleInterface, + ) -> Vec { + let insert_range = self.get_phrase_surrounding_completion_for_import(); + let mut completions = vec![]; + + // Find values and type that have already previously been imported + let mut already_imported_types = std::collections::HashSet::new(); + let mut already_imported_values = std::collections::HashSet::new(); + + // Search the ast for import statements + for import in self.module.ast.definitions.iter().filter_map(get_import) { + // Find the import that matches the module being imported from + if import.module == module_being_imported_from.name { + // Add the values and types that have already been imported + for unqualified in &import.unqualified_types { + let _ = already_imported_types.insert(&unqualified.name); + } + + for unqualified in &import.unqualified_values { + let _ = already_imported_values.insert(&unqualified.name); + } + } + } + + // Get completable types + for (name, type_) in &module_being_imported_from.types { + // Skip types that should not be suggested + if !self.is_suggestable_import( + &type_.publicity, + module_being_imported_from.package.as_str(), + ) { + continue; + } + + // Skip type that are already imported + if already_imported_types.contains(name) { + continue; + } + + completions.push(type_completion( + None, + name, + type_, + insert_range, + TypeCompletionForm::UnqualifiedImport, + )); + } + + // Get completable values + for (name, value) in &module_being_imported_from.values { + // Skip values that should not be suggested + if !self.is_suggestable_import( + &value.publicity, + module_being_imported_from.package.as_str(), + ) { + continue; + } + + // Skip values that are already imported + if already_imported_values.contains(name) { + continue; + } + completions.push(value_completion( + None, + &module_being_imported_from.name, + name, + value, + insert_range, + )); + } + + completions + } + + // Get all the modules that can be imported that have not already been imported. + fn completable_modules_for_import(&'a self) -> Vec<(&EcoString, &ModuleInterface)> { + let mut direct_dep_packages: std::collections::HashSet<&EcoString> = + std::collections::HashSet::from_iter( + self.compiler.project_compiler.config.dependencies.keys(), + ); + if !self.module.origin.is_src() { + // In tests we can import direct dev dependencies + direct_dep_packages.extend( + self.compiler + .project_compiler + .config + .dev_dependencies + .keys(), + ) + } + + let already_imported: std::collections::HashSet = + std::collections::HashSet::from_iter( + self.module.dependencies.iter().map(|d| d.0.clone()), + ); + self.compiler + .project_compiler + .get_importable_modules() + .iter() + // + // It is possible to import modules from dependencies of dependencies + // but it's not recommended so we don't include them in completions + .filter(|(_, module)| { + let is_root_or_prelude = + module.package == self.root_package_name() || module.package.is_empty(); + is_root_or_prelude || direct_dep_packages.contains(&module.package) + }) + // + // src/ cannot import test/ + .filter(|(_, module)| module.origin.is_src() || !self.module.origin.is_src()) + // + // It is possible to import internal modules from other packages, + // but it's not recommended so we don't include them in completions + .filter(|(_, module)| module.package == self.root_package_name() || !module.is_internal) + // + // You cannot import a module twice + .filter(|(name, _)| !already_imported.contains(*name)) + // + // You cannot import yourself + .filter(|(name, _)| *name != &self.module.name) + .collect() + } + + // Get all the completions for modules that can be imported + fn complete_modules_for_import( + &'a self, + start: Position, + end: Position, + ) -> Vec { + self.completable_modules_for_import() + .iter() + .map(|(name, _)| CompletionItem { + label: name.to_string(), + kind: Some(CompletionItemKind::MODULE), + text_edit: Some(CompletionTextEdit::Edit(TextEdit { + range: Range { start, end }, + new_text: name.to_string(), + })), + ..Default::default() + }) + .collect() + } + + // NOTE: completion_types and completion_values are really similar + // but just different enough that an abstraction would + // be really hard to understand or use a lot of trait magic. + // For now I've left it as is but might be worth revisiting. + + /// Provides completions for when the context being editted is a type. + pub fn completion_types(&'a self) -> Vec { + let surrounding_completion = self.get_phrase_surrounding_completion(); + let mut completions = vec![]; + + let (insert_range, module_select) = surrounding_completion; + + // Prelude types + for type_ in PreludeType::iter() { + completions.push(CompletionItem { + label: type_.name().into(), + detail: Some("Type".into()), + kind: Some(CompletionItemKind::CLASS), + ..Default::default() + }); + } + + // Module types + // Do not complete direct module types if the user has already started typing a module select. + // e.x. when the user has typed mymodule.| we know local module types are no longer relevant + if module_select.is_none() { + for (name, type_) in &self.module.ast.type_info.types { + completions.push(type_completion( + None, + name, + type_, + insert_range, + TypeCompletionForm::Default, + )); + } + } + + // Imported modules + for import in self.module.ast.definitions.iter().filter_map(get_import) { + // The module may not be known of yet if it has not previously + // compiled yet in this editor session. + let Some(module) = self.compiler.get_module_interface(&import.module) else { + continue; + }; + + // Qualified types + for (name, type_) in &module.types { + if !self.is_suggestable_import(&type_.publicity, module.package.as_str()) { + continue; + } + + if let Some(module) = import.used_name() { + // If the user has already started a module select then don't show irrelevant modules. + // e.x. when the user has typed mymodule.| we should only show items from mymodule. + if let Some(input_mod_name) = &module_select { + if &module != input_mod_name { + continue; + } + } + completions.push(type_completion( + Some(&module), + name, + type_, + insert_range, + TypeCompletionForm::Default, + )); + } + } + + // Unqualified types + // Do not complete unqualified types if the user has already started typing a module select. + // e.x. when the user has typed mymodule.| we know unqualified module types are no longer relevant. + if module_select.is_none() { + for unqualified in &import.unqualified_types { + match module.get_public_type(&unqualified.name) { + Some(type_) => completions.push(type_completion( + None, + unqualified.used_name(), + type_, + insert_range, + TypeCompletionForm::Default, + )), + None => continue, + } + } + } + } + + // Importable modules + let import_location = self.first_import_line_in_module(); + for (module_full_name, module) in self.completable_modules_for_import() { + // Do not try to import the prelude. + if module_full_name == "gleam" { + continue; + } + + let qualifier = module_full_name + .split('/') + .last() + .unwrap_or(module_full_name); + + // If the user has already started a module select then don't show irrelevant modules. + // e.x. when the user has typed mymodule.| we should only show items from mymodule. + if let Some(input_mod_name) = &module_select { + if qualifier != input_mod_name { + continue; + } + } + + // Qualified types + for (name, type_) in &module.types { + if !self.is_suggestable_import(&type_.publicity, module.package.as_str()) { + continue; + } + + let mut completion = type_completion( + Some(qualifier), + name, + type_, + insert_range, + TypeCompletionForm::Default, + ); + add_import_to_completion(&mut completion, import_location, module_full_name); + completions.push(completion); + } + } + + completions + } + + /// Provides completions for when the context being editted is a value. + pub fn completion_values(&'a self) -> Vec { + let surrounding_completion = self.get_phrase_surrounding_completion(); + let mut completions = vec![]; + let mod_name = self.module.name.as_str(); + + let (insert_range, module_select) = surrounding_completion; + + // Module values + // Do not complete direct module values if the user has already started typing a module select. + // e.x. when the user has typed mymodule.| we know local module values are no longer relevant + if module_select.is_none() { + for (name, value) in &self.module.ast.type_info.values { + // Here we do not check for the internal attribute: we always want + // to show autocompletions for values defined in the same module, + // even if those are internal. + completions.push(value_completion(None, mod_name, name, value, insert_range)); + } + } + + // Imported modules + for import in self.module.ast.definitions.iter().filter_map(get_import) { + // The module may not be known of yet if it has not previously + // compiled yet in this editor session. + let Some(module) = self.compiler.get_module_interface(&import.module) else { + continue; + }; + + // Qualified values + for (name, value) in &module.values { + if !self.is_suggestable_import(&value.publicity, module.package.as_str()) { + continue; + } + + if let Some(module) = import.used_name() { + // If the user has already started a module select then don't show irrelevant modules. + // e.x. when the user has typed mymodule.| we should only show items from mymodule. + if let Some(input_mod_name) = &module_select { + if &module != input_mod_name { + continue; + } + } + completions.push(value_completion( + Some(&module), + mod_name, + name, + value, + insert_range, + )); + } + } + + // Unqualified values + // Do not complete unqualified values if the user has already started typing a module select. + // e.x. when the user has typed mymodule.| we know unqualified module values are no longer relevant. + if module_select.is_none() { + for unqualified in &import.unqualified_values { + match module.get_public_value(&unqualified.name) { + Some(value) => { + let name = unqualified.used_name(); + completions.push(value_completion( + None, + mod_name, + name, + value, + insert_range, + )) + } + None => continue, + } + } + } + } + + // Importable modules + let import_location = self.first_import_line_in_module(); + for (module_full_name, module) in self.completable_modules_for_import() { + // Do not try to import the prelude. + if module_full_name == "gleam" { + continue; + } + let qualifier = module_full_name + .split('/') + .last() + .unwrap_or(module_full_name); + + // If the user has already started a module select then don't show irrelevant modules. + // e.x. when the user has typed mymodule.| we should only show items from mymodule. + if let Some(input_mod_name) = &module_select { + if qualifier != input_mod_name { + continue; + } + } + + // Qualified values + for (name, value) in &module.values { + if !self.is_suggestable_import(&value.publicity, module.package.as_str()) { + continue; + } + + let mut completion = + value_completion(Some(qualifier), module_full_name, name, value, insert_range); + + add_import_to_completion(&mut completion, import_location, module_full_name); + completions.push(completion); + } + } + + completions + } + + fn root_package_name(&self) -> &str { + self.compiler.project_compiler.config.name.as_str() + } + + // checks based on the publicity if something should be suggested for import from root package + fn is_suggestable_import(&self, publicity: &Publicity, package: &str) -> bool { + match publicity { + // We skip private types as we never want those to appear in + // completions. + Publicity::Private => false, + // We only skip internal types if those are not defined in + // the root package. + Publicity::Internal if package != self.root_package_name() => false, + Publicity::Internal => true, + // We never skip public types. + Publicity::Public => true, + } + } + + // Gets the position of the line with the first import statement in the file. + fn first_import_line_in_module(&'a self) -> Position { + let import_location = self + .module + .ast + .definitions + .iter() + .find_map(get_import) + .map_or(0, |i| i.location.start); + let import_location = self.module_line_numbers.line_number(import_location); + Position::new(import_location - 1, 0) + } +} + +fn add_import_to_completion( + item: &mut CompletionItem, + import_location: Position, + module_full_name: &EcoString, +) { + item.additional_text_edits = Some(vec![TextEdit { + range: Range { + start: import_location, + end: import_location, + }, + new_text: ["import ", module_full_name, "\n"].concat(), + }]); +} + +fn type_completion( + module: Option<&str>, + name: &str, + type_: &TypeConstructor, + insert_range: Range, + include_type_in_completion: TypeCompletionForm, +) -> CompletionItem { + let label = match module { + Some(module) => format!("{module}.{name}"), + None => name.to_string(), + }; + + let kind = Some(if type_.typ.is_variable() { + CompletionItemKind::VARIABLE + } else { + CompletionItemKind::CLASS + }); + + CompletionItem { + label: label.clone(), + kind, + detail: Some("Type".into()), + text_edit: Some(CompletionTextEdit::Edit(TextEdit { + range: insert_range, + new_text: match include_type_in_completion { + TypeCompletionForm::UnqualifiedImport => format!("type {label}"), + TypeCompletionForm::Default => label.clone(), + }, + })), + ..Default::default() + } +} + +fn value_completion( + module_qualifier: Option<&str>, + module_name: &str, + name: &str, + value: &crate::type_::ValueConstructor, + insert_range: Range, +) -> CompletionItem { + let label = match module_qualifier { + Some(module) => format!("{module}.{name}"), + None => name.to_string(), + }; + + let type_ = Printer::new().pretty_print(&value.type_, 0); + + let kind = Some(match value.variant { + ValueConstructorVariant::LocalVariable { .. } => CompletionItemKind::VARIABLE, + ValueConstructorVariant::ModuleConstant { .. } => CompletionItemKind::CONSTANT, + ValueConstructorVariant::LocalConstant { .. } => CompletionItemKind::CONSTANT, + ValueConstructorVariant::ModuleFn { .. } => CompletionItemKind::FUNCTION, + ValueConstructorVariant::Record { arity: 0, .. } => CompletionItemKind::ENUM_MEMBER, + ValueConstructorVariant::Record { .. } => CompletionItemKind::CONSTRUCTOR, + }); + + let documentation = value.get_documentation().map(|d| { + Documentation::MarkupContent(MarkupContent { + kind: MarkupKind::Markdown, + value: d.into(), + }) + }); + + CompletionItem { + label: label.clone(), + kind, + detail: Some(type_), + label_details: Some(CompletionItemLabelDetails { + detail: None, + description: Some(module_name.into()), + }), + documentation, + text_edit: Some(CompletionTextEdit::Edit(TextEdit { + range: insert_range, + new_text: label.clone(), + })), + ..Default::default() + } +} + +fn get_import(statement: &TypedDefinition) -> Option<&Import> { + match statement { + Definition::Import(import) => Some(import), + _ => None, + } +} diff --git a/compiler-core/src/language_server/engine.rs b/compiler-core/src/language_server/engine.rs index 5c060f5d1..3ba86fcca 100644 --- a/compiler-core/src/language_server/engine.rs +++ b/compiler-core/src/language_server/engine.rs @@ -1,7 +1,7 @@ use crate::{ ast::{ - Arg, Definition, Import, ModuleConstant, Publicity, SrcSpan, TypedDefinition, TypedExpr, - TypedFunction, TypedModule, TypedPattern, + Arg, Definition, ModuleConstant, SrcSpan, TypedExpr, TypedFunction, TypedModule, + TypedPattern, }, build::{type_constructor_from_modules, Located, Module, UnqualifiedImport}, config::PackageConfig, @@ -11,10 +11,7 @@ use crate::{ }, line_numbers::LineNumbers, paths::ProjectPaths, - type_::{ - pretty::Printer, ModuleInterface, PreludeType, Type, TypeConstructor, - ValueConstructorVariant, - }, + type_::{pretty::Printer, ModuleInterface, Type, TypeConstructor, ValueConstructorVariant}, Error, Result, Warning, }; use camino::Utf8PathBuf; @@ -22,10 +19,10 @@ use ecow::EcoString; use lsp::CodeAction; use lsp_types::{self as lsp, Hover, HoverContents, MarkedString, Url}; use std::sync::Arc; -use strum::IntoEnumIterator; use super::{ code_action::{CodeActionBuilder, RedundantTupleInCaseSubject}, + completer::Completer, src_span_to_lsp_range, DownloadDependencies, MakeLocker, }; @@ -144,8 +141,6 @@ where self.compiler.take_warnings() } - // TODO: test different package module function calls - // // TODO: implement unqualified imported module functions // pub fn goto_definition( @@ -195,45 +190,53 @@ where None => return Ok(None), }; + let completer = Completer::new(&src, ¶ms, &this.compiler, module); + // Check current filercontents if the user is writing an import // and handle separately from the rest of the completion flow // Check if an import is being written - if let Some(value) = this.import_completions(src, ¶ms, module) { + if let Some(value) = completer.import_completions() { return value; } - let line_numbers = LineNumbers::new(&module.code); - let byte_index = - line_numbers.byte_index(params.position.line, params.position.character); + let byte_index = completer + .module_line_numbers + .byte_index(params.position.line, params.position.character); + + // If in comment context, do not provide completions + if module.extra.is_within_comment(byte_index) { + return Ok(None); + } let Some(found) = module.find_node(byte_index) else { return Ok(None); }; let completions = match found { + Located::PatternSpread { .. } => None, Located::Pattern(_pattern) => None, Located::Statement(_) | Located::Expression(_) => { - Some(this.completion_values(module)) + Some(completer.completion_values()) } Located::ModuleStatement(Definition::Function(_)) => { - Some(this.completion_types(module)) + Some(completer.completion_types()) } - Located::FunctionBody(_) => Some(this.completion_values(module)), + Located::FunctionBody(_) => Some(completer.completion_values()), Located::ModuleStatement(Definition::TypeAlias(_) | Definition::CustomType(_)) => { - Some(this.completion_types(module)) + Some(completer.completion_types()) } // If the import completions returned no results and we are in an import then // we should try to provide completions for unqualified values Located::ModuleStatement(Definition::Import(import)) => this .compiler - .get_module_inferface(import.module.as_str()) + .get_module_interface(import.module.as_str()) .map(|importing_module| { - this.unqualified_completions_from_module(importing_module, module, true) + completer.unqualified_completions_from_module(importing_module) }), Located::ModuleStatement(Definition::ModuleConstant(_)) => None, @@ -242,14 +245,17 @@ where Located::Arg(_) => None, - Located::Annotation(_, _) => Some(this.completion_types(module)), + Located::Annotation(_, _) => Some(completer.completion_types()), }; Ok(completions) }) } - pub fn action(&mut self, params: lsp::CodeActionParams) -> Response>> { + pub fn code_actions( + &mut self, + params: lsp::CodeActionParams, + ) -> Response>> { self.respond(|this| { let mut actions = vec![]; let Some(module) = this.module_for_uri(¶ms.text_document.uri) else { @@ -310,7 +316,7 @@ where location, }) => this .compiler - .get_module_inferface(module.as_str()) + .get_module_interface(module.as_str()) .and_then(|module| { if is_type { module.types.get(name).map(|t| { @@ -328,6 +334,48 @@ where } }), Located::Pattern(pattern) => Some(hover_for_pattern(pattern, lines)), + Located::PatternSpread { + spread_location, + arguments, + } => { + let range = Some(src_span_to_lsp_range(spread_location, &lines)); + + let mut positional = vec![]; + let mut labelled = vec![]; + for argument in arguments { + // We only want to display the arguments that were ignored using `..`. + // Any argument ignored that way is marked as implicit, so if it is + // not implicit we just ignore it. + if !argument.implicit { + continue; + } + + let type_ = Printer::new().pretty_print(argument.value.type_().as_ref(), 0); + match &argument.label { + Some(label) => labelled.push(format!("- `{}: {}`", label, type_)), + None => positional.push(format!("- `{}`", type_)), + } + } + + let positional = positional.join("\n"); + let labelled = labelled.join("\n"); + let content = match (positional.is_empty(), labelled.is_empty()) { + (true, false) => format!("Unused labelled fields:\n{labelled}"), + (false, true) => format!("Unused positional fields:\n{positional}"), + (_, _) => format!( + "Unused positional fields: +{positional} + +Unused labelled fields: +{labelled}" + ), + }; + + Some(Hover { + contents: HoverContents::Scalar(MarkedString::from_markdown(content)), + range, + }) + } Located::Expression(expression) => { let module = this.module_for_uri(¶ms.text_document.uri); @@ -399,370 +447,6 @@ where self.compiler.modules.get(&module_name) } - - /// checks based on the publicity if something should be suggested for import from root package - fn is_suggestable_import(&self, publicity: &Publicity, package: &str) -> bool { - match publicity { - // We skip private types as we never want those to appear in - // completions. - Publicity::Private => false, - // We only skip internal types if those are not defined in - // the root package. - Publicity::Internal if package != self.root_package_name() => false, - Publicity::Internal => true, - // We never skip public types. - Publicity::Public => true, - } - } - - fn completion_types<'b>(&'b self, module: &'b Module) -> Vec { - let mut completions = vec![]; - - // Prelude types - for type_ in PreludeType::iter() { - completions.push(lsp::CompletionItem { - label: type_.name().into(), - detail: Some("Type".into()), - kind: Some(lsp::CompletionItemKind::CLASS), - ..Default::default() - }); - } - - // Module types - for (name, type_) in &module.ast.type_info.types { - completions.push(type_completion(None, name, type_)); - } - - // Imported modules - for import in module.ast.definitions.iter().filter_map(get_import) { - // The module may not be known of yet if it has not previously - // compiled yet in this editor session. - // TODO: test getting completions from modules defined in other packages - let Some(module) = self.compiler.get_module_inferface(&import.module) else { - continue; - }; - - // Qualified types - for (name, type_) in &module.types { - if !self.is_suggestable_import(&type_.publicity, module.package.as_str()) { - continue; - } - - let module = import.used_name(); - if module.is_some() { - completions.push(type_completion(module.as_ref(), name, type_)); - } - } - - // Unqualified types - for unqualified in &import.unqualified_types { - match module.get_public_type(&unqualified.name) { - Some(type_) => { - completions.push(type_completion(None, unqualified.used_name(), type_)) - } - None => continue, - } - } - } - - completions - } - - fn completion_values<'b>(&'b self, module: &'b Module) -> Vec { - let mut completions = vec![]; - - // Module functions - for (name, value) in &module.ast.type_info.values { - // Here we do not check for the internal attribute: we always want - // to show autocompletions for values defined in the same module, - // even if those are internal. - completions.push(value_completion(None, name, value)); - } - - // Imported modules - for import in module.ast.definitions.iter().filter_map(get_import) { - // The module may not be known of yet if it has not previously - // compiled yet in this editor session. - // TODO: test getting completions from modules defined in other packages - let Some(module) = self.compiler.get_module_inferface(&import.module) else { - continue; - }; - - // Qualified values - for (name, value) in &module.values { - if !self.is_suggestable_import(&value.publicity, module.package.as_str()) { - continue; - } - - let module = import.used_name(); - if module.is_some() { - completions.push(value_completion(module.as_deref(), name, value)); - } - } - - // Unqualified values - for unqualified in &import.unqualified_values { - match module.get_public_value(&unqualified.name) { - Some(value) => { - completions.push(value_completion(None, unqualified.used_name(), value)) - } - None => continue, - } - } - } - - completions - } - - fn unqualified_completions_from_module<'b>( - &'b self, - importing_module: &'b ModuleInterface, - module: &'b Module, - // should type completions include the word "type" in the completion - include_type_in_completion: bool, - ) -> Vec { - let mut completions = vec![]; - - // Find values and type that have already previously been imported - let mut already_imported_types = std::collections::HashSet::new(); - let mut already_imported_values = std::collections::HashSet::new(); - - // Search the ast for import statements - for import in module.ast.definitions.iter().filter_map(get_import) { - // Find the import that matches the module being imported from - if import.module == importing_module.name { - // Add the values and types that have already been imported - for unqualified in &import.unqualified_types { - let _ = already_imported_types.insert(&unqualified.name); - } - - for unqualified in &import.unqualified_values { - let _ = already_imported_values.insert(&unqualified.name); - } - } - } - - // Get completable types - for (name, type_) in &importing_module.types { - // Skip types that should not be suggested - if !self.is_suggestable_import(&type_.publicity, importing_module.package.as_str()) { - continue; - } - - // Skip type that are already imported - if already_imported_types.contains(name) { - continue; - } - - let completion: lsp::CompletionItem = if !include_type_in_completion { - type_completion(None, name, type_) - } else { - let completion = type_completion(None, name, type_); - lsp::CompletionItem { - // Add type prior to unqualified import for types - insert_text: Some("type ".to_string() + &completion.label), - ..completion - } - }; - completions.push(completion); - } - - // Get completable values - for (name, value) in &importing_module.values { - // Skip values that should not be suggested - if !self.is_suggestable_import(&value.publicity, importing_module.package.as_str()) { - continue; - } - - // Skip values that are already imported - if already_imported_values.contains(name) { - continue; - } - completions.push(value_completion(None, name, value)); - } - - completions - } - - fn import_completions<'b>( - &'b self, - src: EcoString, - params: &lsp::TextDocumentPositionParams, - module: &'b Module, - ) -> Option>>> { - let line_num = LineNumbers::new(src.as_str()); - let start_of_line = line_num.byte_index(params.position.line, 0); - let end_of_line = line_num.byte_index(params.position.line + 1, 0); - - // Drop all lines except the line the cursor is on - let src = &src.get(start_of_line as usize..end_of_line as usize)?; - - // If this isn't an import line then we don't offer import completions - if !src.trim_start().starts_with("import") { - return None; - } - - // Check if we are completing an unqualified import - if let Some(dot_index) = src.find('.') { - // Find the module that is being imported from - let importing_module_name = src.get(6..dot_index)?.trim(); - let importing_module: &ModuleInterface = - self.compiler.get_module_inferface(importing_module_name)?; - - // Check if the cursor is proceeded by the word "type". - // We want to make sure suggestions don't include the word "type" - // if the cursor is proceeded by it. - let cursor = src.get(..params.position.character as usize)?; - Some(Ok(Some(self.unqualified_completions_from_module( - importing_module, - module, - !cursor.trim().ends_with("type"), - )))) - } else { - // Find where to start and end the import completion - let start = line_num.line_and_column_number(start_of_line); - let end = line_num.line_and_column_number(end_of_line - 1); - let start = lsp::Position::new(start.line - 1, start.column + 6); - let end = lsp::Position::new(end.line - 1, end.column - 1); - let completions = self.complete_modules_for_import(module, start, end); - - Some(Ok(Some(completions))) - } - } - - fn complete_modules_for_import<'b>( - &'b self, - current_module: &'b Module, - start: lsp::Position, - end: lsp::Position, - ) -> Vec { - let mut direct_dep_packages: std::collections::HashSet<&EcoString> = - std::collections::HashSet::from_iter( - self.compiler.project_compiler.config.dependencies.keys(), - ); - if !current_module.origin.is_src() { - // In tests we can import direct dev dependencies - direct_dep_packages.extend( - self.compiler - .project_compiler - .config - .dev_dependencies - .keys(), - ) - } - - let already_imported: std::collections::HashSet = - std::collections::HashSet::from_iter(current_module.dependencies_list()); - self.compiler - .project_compiler - .get_importable_modules() - .iter() - // - // It is possible to import modules from dependencies of dependencies - // but it's not recommended so we don't include them in completions - .filter(|(_, module)| { - let is_root_or_prelude = - module.package == self.root_package_name() || module.package.is_empty(); - is_root_or_prelude || direct_dep_packages.contains(&module.package) - }) - // - // src/ cannot import test/ - .filter(|(_, module)| module.origin.is_src() || !current_module.origin.is_src()) - // - // It is possible to import internal modules from other packages, - // but it's not recommended so we don't include them in completions - .filter(|(_, module)| module.package == self.root_package_name() || !module.is_internal) - // - // You cannot import a module twice - .filter(|(name, _)| !already_imported.contains(*name)) - // - // You cannot import yourself - .filter(|(name, _)| *name != ¤t_module.name) - // - // Everything else we suggest as a completion - .map(|(name, _)| lsp::CompletionItem { - label: name.to_string(), - kind: Some(lsp::CompletionItemKind::MODULE), - text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { - range: lsp::Range { start, end }, - new_text: name.to_string(), - })), - ..Default::default() - }) - .collect() - } - - fn root_package_name(&self) -> &str { - self.compiler.project_compiler.config.name.as_str() - } -} - -fn type_completion( - module: Option<&EcoString>, - name: &str, - type_: &TypeConstructor, -) -> lsp::CompletionItem { - let label = match module { - Some(module) => format!("{module}.{name}"), - None => name.to_string(), - }; - - let kind = Some(if type_.typ.is_variable() { - lsp::CompletionItemKind::VARIABLE - } else { - lsp::CompletionItemKind::CLASS - }); - - lsp::CompletionItem { - label, - kind, - detail: Some("Type".into()), - ..Default::default() - } -} - -fn value_completion( - module: Option<&str>, - name: &str, - value: &crate::type_::ValueConstructor, -) -> lsp::CompletionItem { - let label = match module { - Some(module) => format!("{module}.{name}"), - None => name.to_string(), - }; - - let type_ = Printer::new().pretty_print(&value.type_, 0); - - let kind = Some(match value.variant { - ValueConstructorVariant::LocalVariable { .. } => lsp::CompletionItemKind::VARIABLE, - ValueConstructorVariant::ModuleConstant { .. } => lsp::CompletionItemKind::CONSTANT, - ValueConstructorVariant::LocalConstant { .. } => lsp::CompletionItemKind::CONSTANT, - ValueConstructorVariant::ModuleFn { .. } => lsp::CompletionItemKind::FUNCTION, - ValueConstructorVariant::Record { arity: 0, .. } => lsp::CompletionItemKind::ENUM_MEMBER, - ValueConstructorVariant::Record { .. } => lsp::CompletionItemKind::CONSTRUCTOR, - }); - - let documentation = value.get_documentation().map(|d| { - lsp::Documentation::MarkupContent(lsp::MarkupContent { - kind: lsp::MarkupKind::Markdown, - value: d.to_string(), - }) - }); - - lsp::CompletionItem { - label, - kind, - detail: Some(type_), - documentation, - ..Default::default() - } -} - -fn get_import(statement: &TypedDefinition) -> Option<&Import> { - match statement { - Definition::Import(import) => Some(import), - _ => None, - } } fn hover_for_pattern(pattern: &TypedPattern, line_numbers: LineNumbers) -> Hover { diff --git a/compiler-core/src/language_server/files.rs b/compiler-core/src/language_server/files.rs index 4a596cc15..d28634780 100644 --- a/compiler-core/src/language_server/files.rs +++ b/compiler-core/src/language_server/files.rs @@ -93,6 +93,10 @@ where fn delete_file(&self, path: &Utf8Path) -> Result<()> { self.io.delete_file(path) } + + fn exists(&self, path: &Utf8Path) -> bool { + self.io.exists(path) + } } impl FileSystemReader for FileSystemProxy diff --git a/compiler-core/src/language_server/server.rs b/compiler-core/src/language_server/server.rs index e43a9b11c..c248a8b9f 100644 --- a/compiler-core/src/language_server/server.rs +++ b/compiler-core/src/language_server/server.rs @@ -17,6 +17,7 @@ use crate::{ }; use camino::{Utf8Path, Utf8PathBuf}; use debug_ignore::DebugIgnore; +use itertools::Itertools; use lsp_types::{ self as lsp, HoverProviderCapability, InitializeParams, Position, PublishDiagnosticsParams, Range, TextEdit, Url, @@ -316,7 +317,7 @@ where fn code_action(&mut self, params: lsp::CodeActionParams) -> (Json, Feedback) { let path = super::path(¶ms.text_document.uri); - self.respond_with_engine(path, |engine| engine.action(params)) + self.respond_with_engine(path, |engine| engine.code_actions(params)) } fn cache_file_in_memory(&mut self, path: Utf8PathBuf, text: String) -> Feedback { @@ -455,15 +456,42 @@ fn diagnostic_to_lsp(diagnostic: Diagnostic) -> Vec { .location .expect("Diagnostic given to LSP without location"); let line_numbers = LineNumbers::new(&location.src); + let path = path_to_uri(location.path); + let range = src_span_to_lsp_range(location.label.span, &line_numbers); + + let related_info = location + .extra_labels + .iter() + .map(|extra| { + let message = extra.label.text.clone().unwrap_or_default(); + let location = if let Some((src, path)) = &extra.src_info { + let line_numbers = LineNumbers::new(src); + lsp::Location { + uri: path_to_uri(path.clone()), + range: src_span_to_lsp_range(extra.label.span, &line_numbers), + } + } else { + lsp::Location { + uri: path.clone(), + range: src_span_to_lsp_range(extra.label.span, &line_numbers), + } + }; + lsp::DiagnosticRelatedInformation { location, message } + }) + .collect_vec(); let main = lsp::Diagnostic { - range: src_span_to_lsp_range(location.label.span, &line_numbers), + range, severity: Some(severity), code: None, code_description: None, source: None, message: text, - related_information: None, + related_information: if related_info.is_empty() { + None + } else { + Some(related_info) + }, tags: None, data: None, }; diff --git a/compiler-core/src/language_server/tests.rs b/compiler-core/src/language_server/tests.rs index daba93156..e90151543 100644 --- a/compiler-core/src/language_server/tests.rs +++ b/compiler-core/src/language_server/tests.rs @@ -198,6 +198,10 @@ impl FileSystemWriter for LanguageServerTestIO { fn write_bytes(&self, path: &Utf8Path, content: &[u8]) -> Result<(), crate::Error> { self.io.write_bytes(path, content) } + + fn exists(&self, path: &Utf8Path) -> bool { + self.io.exists(path) + } } impl DownloadDependencies for LanguageServerTestIO { diff --git a/compiler-core/src/language_server/tests/action.rs b/compiler-core/src/language_server/tests/action.rs index a43f27167..e4af877e2 100644 --- a/compiler-core/src/language_server/tests/action.rs +++ b/compiler-core/src/language_server/tests/action.rs @@ -1,5 +1,3 @@ -use std::assert_eq; - use crate::{language_server::engine, line_numbers::LineNumbers}; use lsp_types::{ CodeActionContext, CodeActionParams, PartialResultParams, Position, Range, @@ -49,10 +47,10 @@ fn engine_response(src: &str, line: u32) -> engine::Response String { @@ -96,16 +94,28 @@ fn apply_code_edit( let end = line_numbers.byte_index(edit.range.end.line, edit.range.end.character) - offset; let range = (start as usize)..(end as usize); - offset += end - start - edit.new_text.len() as u32; + offset += end - start; + offset -= edit.new_text.len() as u32; result.replace_range(range, &edit.new_text); } } result } +#[macro_export] +macro_rules! assert_code_action { + ($line:expr, $title:expr, $src:expr $(,)?) => { + let output = apply_first_code_action_with_title($src, $line, $title); + insta::assert_snapshot!(insta::internals::AutoName, output, $src); + }; +} + #[test] fn test_remove_unused_simple() { - let code = " + assert_code_action!( + 2, + REMOVE_UNUSED_IMPORTS, + " // test import // comment list as lispy @@ -115,45 +125,31 @@ import option pub fn main() { result.is_ok } -"; - let expected = " -// test -import result - -pub fn main() { - result.is_ok -} -"; - assert_eq!( - apply_first_code_action_with_title(code, 2, REMOVE_UNUSED_IMPORTS_TITLE), - expected.to_string() - ) +" + ); } #[test] fn test_remove_unused_start_of_file() { - let code = "import option + assert_code_action!( + 2, + REMOVE_UNUSED_IMPORTS, + "import option import result pub fn main() { result.is_ok } -"; - let expected = "import result - -pub fn main() { - result.is_ok -} -"; - assert_eq!( - apply_first_code_action_with_title(code, 2, REMOVE_UNUSED_IMPORTS_TITLE), - expected.to_string() - ) +" + ); } #[test] fn test_remove_unused_alias() { - let code = " + assert_code_action!( + 2, + REMOVE_UNUSED_IMPORTS, + " // test import result.{is_ok} as res import option @@ -161,66 +157,77 @@ import option pub fn main() { is_ok } -"; - let expected = " -// test -import result.{is_ok}%SPACE% - -pub fn main() { - is_ok -} -"; - assert_eq!( - apply_first_code_action_with_title(code, 2, REMOVE_UNUSED_IMPORTS_TITLE), - expected.replace("%SPACE%", " ") - ) +" + ); } #[test] fn test_remove_redundant_tuple_in_case_subject_simple() { - let code = " + assert_code_action!( + 2, + REMOVE_REDUNDANT_TUPLES, + " pub fn main() { case #(1) { #(a) -> 0 } case #(1, 2) { #(a, b) -> 0 } } -"; +" + ); +} - let expected = " +#[test] +fn test_remove_redundant_tuple_with_catch_all_pattern() { + assert_code_action!( + 4, + REMOVE_REDUNDANT_TUPLES, + " pub fn main() { - case 1 { a -> 0 } - case 1, 2 { a, b -> 0 } + case #(1, 2) { + #(1, 2) -> 0 + _ -> 1 + } } -"; - - assert_eq!( - apply_first_code_action_with_title(code, 7, REMOVE_REDUNDANT_TUPLES), - expected +" ); } #[test] -fn test_remove_redundant_tuple_in_case_subject_nested() { - let code = " +fn test_remove_multiple_redundant_tuple_with_catch_all_pattern() { + assert_code_action!( + 4, + REMOVE_REDUNDANT_TUPLES, + " pub fn main() { - case #(case #(0) { #(a) -> 0 }) { #(b) -> 0 } + case #(1, 2), #(3, 4) { + #(2, 2), #(2, 2) -> 0 + #(1, 2), _ -> 0 + _, #(1, 2) -> 0 + _, _ -> 1 + } +} +" + ); } -"; - let expected = " +#[test] +fn test_remove_redundant_tuple_in_case_subject_nested() { + assert_code_action!( + 2, + REMOVE_REDUNDANT_TUPLES, + " pub fn main() { - case case 0 { a -> 0 } { b -> 0 } + case #(case #(0) { #(a) -> 0 }) { #(b) -> 0 } } -"; - - assert_eq!( - apply_first_code_action_with_title(code, 7, REMOVE_REDUNDANT_TUPLES), - expected +" ); } #[test] fn test_remove_redundant_tuple_in_case_retain_extras() { - let code = " + assert_code_action!( + 7, + REMOVE_REDUNDANT_TUPLES, + " pub fn main() { case #( @@ -250,11 +257,8 @@ pub fn main() { ) -> 0 } } -"; - - let result = apply_first_code_action_with_title(code, 20, REMOVE_REDUNDANT_TUPLES); - - insta::assert_snapshot!(result); +" + ); } #[test] @@ -273,7 +277,10 @@ pub fn main() { #[test] fn test_remove_redundant_tuple_in_case_subject_only_safe_remove() { - let code = " + assert_code_action!( + 2, + REMOVE_REDUNDANT_TUPLES, + " pub fn main() { case #(0), #(1) { #(1), #(b) -> 0 @@ -281,21 +288,7 @@ pub fn main() { #(a), #(b) -> 2 } } -"; - - let expected = " -pub fn main() { - case #(0), 1 { - #(1), b -> 0 - a, 0 -> 1 // The first of this clause is not a tuple - #(a), b -> 2 - } -} -"; - - assert_eq!( - apply_first_code_action_with_title(code, 11, REMOVE_REDUNDANT_TUPLES), - expected +" ); } diff --git a/compiler-core/src/language_server/tests/completion.rs b/compiler-core/src/language_server/tests/completion.rs index 1716f7e1f..602ddbfef 100644 --- a/compiler-core/src/language_server/tests/completion.rs +++ b/compiler-core/src/language_server/tests/completion.rs @@ -1,8 +1,6 @@ +use insta::assert_debug_snapshot; use itertools::Itertools; -use lsp_types::{ - CompletionItem, CompletionItemKind, CompletionTextEdit, Documentation, MarkupContent, - MarkupKind, Position, Range, TextEdit, -}; +use lsp_types::{CompletionItem, Position}; use super::*; @@ -25,74 +23,6 @@ fn completion_at_default_position(tester: TestProject<'_>) -> Vec Vec { - vec![ - CompletionItem { - label: "BitArray".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "Bool".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "Float".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "Int".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "List".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "Nil".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "Result".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "String".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "UtfCodepoint".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - ] -} - #[test] fn completions_for_outside_a_function() { let code = " @@ -101,10 +31,10 @@ pub fn main() { 0 }"; - assert_eq!( - completion(TestProject::for_source(code), Position::new(0, 0)), - vec![] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code), + Position::new(0, 0) + )); } #[test] @@ -114,16 +44,9 @@ pub fn main() { 0 }"; - assert_eq!( - completion_at_default_position(TestProject::for_source(code)), - vec![CompletionItem { - label: "main".into(), - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Int".into()), - documentation: None, - ..Default::default() - }] - ); + assert_debug_snapshot!(completion_at_default_position(TestProject::for_source( + code + )),); } #[test] @@ -134,19 +57,9 @@ pub fn main() { 0 }"; - assert_eq!( - completion_at_default_position(TestProject::for_source(code)), - vec![CompletionItem { - label: "main".into(), - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Int".into()), - documentation: Some(Documentation::MarkupContent(MarkupContent { - kind: MarkupKind::Markdown, - value: " Hello\n".into(), - })), - ..Default::default() - }] - ); + assert_debug_snapshot!(completion_at_default_position(TestProject::for_source( + code + )),); } #[test] @@ -158,25 +71,9 @@ pub type Direction { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code)), - vec![ - CompletionItem { - label: "Left".into(), - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Direction".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "Right".into(), - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Direction".into()), - documentation: None, - ..Default::default() - } - ] - ); + assert_debug_snapshot!(completion_at_default_position(TestProject::for_source( + code + )),); } #[test] @@ -188,19 +85,9 @@ pub type Box { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code)), - vec![CompletionItem { - label: "Box".into(), - kind: Some(CompletionItemKind::CONSTRUCTOR), - detail: Some("fn(Int, Int, Float) -> Box".into()), - documentation: Some(Documentation::MarkupContent(MarkupContent { - kind: MarkupKind::Markdown, - value: " Hello\n".into(), - })), - ..Default::default() - }] - ); + assert_debug_snapshot!(completion_at_default_position(TestProject::for_source( + code + )),); } #[test] @@ -214,31 +101,9 @@ pub type Direction { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code)), - vec![ - CompletionItem { - label: "Left".into(), - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Direction".into()), - documentation: Some(Documentation::MarkupContent(MarkupContent { - kind: MarkupKind::Markdown, - value: " Hello\n".into(), - })), - ..Default::default() - }, - CompletionItem { - label: "Right".into(), - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Direction".into()), - documentation: Some(Documentation::MarkupContent(MarkupContent { - kind: MarkupKind::Markdown, - value: " Goodbye\n".into(), - })), - ..Default::default() - } - ] - ); + assert_debug_snapshot!(completion_at_default_position(TestProject::for_source( + code + )),); } #[test] @@ -249,16 +114,9 @@ pub type Box { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code)), - vec![CompletionItem { - label: "Box".into(), - kind: Some(CompletionItemKind::CONSTRUCTOR), - detail: Some("fn(Int, Int, Float) -> Box".into()), - documentation: None, - ..Default::default() - }] - ); + assert_debug_snapshot!(completion_at_default_position(TestProject::for_source( + code + )),); } #[test] @@ -272,16 +130,65 @@ pub fn wobble() { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![CompletionItem { - label: "dep.wobble".into(), - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Nil".into()), - documentation: None, - ..Default::default() - }] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); +} + +#[test] +fn importable_module_function() { + let code = " +"; + let dep = " +pub fn wobble() { + Nil +} +"; + + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); +} + +#[test] +fn importable_module_function_with_existing_imports() { + let code = " +//// Some module comments +// Some other whitespace + +import dep2 +"; + let dep = " +pub fn wobble() { + Nil +} +"; + let dep2 = " +pub fn wobble() { + Nil +} +"; + + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code) + .add_module("dep", dep) + .add_module("dep2", dep2) + ),); +} + +#[test] +fn importable_module_function_from_deep_module() { + let code = " +"; + let dep = " +pub fn wobble() { + Nil +} +"; + + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("a/b/dep", dep) + ),); } #[test] @@ -296,25 +203,9 @@ pub type Direction { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![ - CompletionItem { - label: "dep.Left".into(), - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Direction".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "dep.Right".into(), - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Direction".into()), - documentation: None, - ..Default::default() - } - ] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); } #[test] @@ -328,16 +219,9 @@ pub type Box { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![CompletionItem { - label: "dep.Box".into(), - kind: Some(CompletionItemKind::CONSTRUCTOR), - detail: Some("fn(Int) -> Box".into()), - documentation: None, - ..Default::default() - }] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); } #[test] @@ -351,25 +235,9 @@ pub fn wobble() { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![ - CompletionItem { - label: "dep.wobble".into(), - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Nil".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "wobble".into(), - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Nil".into()), - documentation: None, - ..Default::default() - }, - ] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); } #[test] @@ -384,32 +252,9 @@ pub type Direction { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![ - CompletionItem { - label: "Left".into(), - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Direction".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "dep.Left".into(), - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Direction".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "dep.Right".into(), - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Direction".into()), - documentation: None, - ..Default::default() - }, - ] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); } #[test] @@ -423,25 +268,9 @@ pub type Box { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![ - CompletionItem { - label: "Box".into(), - kind: Some(CompletionItemKind::CONSTRUCTOR), - detail: Some("fn(Int) -> Box".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "dep.Box".into(), - kind: Some(CompletionItemKind::CONSTRUCTOR), - detail: Some("fn(Int) -> Box".into()), - documentation: None, - ..Default::default() - }, - ] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); } #[test] @@ -453,16 +282,9 @@ fn private() { "; let dep = ""; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![CompletionItem { - label: "private".into(), - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Int".into()), - documentation: None, - ..Default::default() - },] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); } #[test] @@ -474,16 +296,9 @@ type Wibble { "; let dep = ""; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![CompletionItem { - label: "Wobble".into(), - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Wibble".into()), - documentation: None, - ..Default::default() - },] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); } #[test] @@ -495,16 +310,9 @@ pub opaque type Wibble { "; let dep = ""; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![CompletionItem { - label: "Wobble".into(), - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Wibble".into()), - documentation: None, - ..Default::default() - },] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); } #[test] @@ -516,10 +324,9 @@ fn private() { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); } #[test] @@ -531,10 +338,9 @@ type Wibble { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); } #[test] @@ -546,10 +352,9 @@ type Wibble { } "; - assert_eq!( - completion_at_default_position(TestProject::for_source(code).add_module("dep", dep)), - vec![] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source(code).add_module("dep", dep) + ),); } #[test] @@ -559,20 +364,10 @@ pub type Wibble { Wobble }"; - assert_eq!( - completion(TestProject::for_source(code), Position::new(2, 0)), - [ - prelude_type_completions(), - vec![CompletionItem { - label: "Wibble".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - },] - ] - .concat() - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code), + Position::new(2, 0) + ),); } #[test] @@ -584,20 +379,10 @@ pub type Wibble = Result( ) "; - assert_eq!( - completion(TestProject::for_source(code), Position::new(2, 0)), - [ - prelude_type_completions(), - vec![CompletionItem { - label: "Wibble".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - },] - ] - .concat() - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code), + Position::new(2, 0) + ),); } #[test] @@ -610,10 +395,10 @@ pub fn wibble( } "; - assert_eq!( - completion(TestProject::for_source(code), Position::new(2, 0)), - prelude_type_completions(), - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code), + Position::new(2, 0) + ),); } #[test] @@ -631,23 +416,213 @@ pub fn wibble( } "; - assert_eq!( - completion( - TestProject::for_source(code).add_module("dep", dep), - Position::new(3, 0) - ), - [ - prelude_type_completions(), - vec![CompletionItem { - label: "dep.Zoo".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - },] - ] - .concat() - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_module("dep", dep), + Position::new(3, 0) + ),); +} + +#[test] +fn imported_type_cursor_after_dot() { + let dep = " +pub type Zoo = List(String) +type Private = List(String) +"; + let code = "import dep + +pub fn wibble( + _: dep.Zoo, +) -> Nil { + Nil +} +"; + + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_module("dep", dep), + Position::new(3, 12) + ),); +} + +#[test] +fn imported_type_cursor_after_dot_other_matching_modules() { + let dep = " +pub type Zoo = List(String) +type Private = List(String) +"; + let dep2 = " +pub type Zoo = List(String) +type Private = List(String) +"; + let code = "import dep +import dep2 + +pub fn wibble( + _: dep.Zoo, +) -> Nil { + Nil +} +"; + + assert_debug_snapshot!(completion( + TestProject::for_source(code) + .add_module("dep", dep) + .add_module("dep2", dep2), + Position::new(4, 12) + ),); +} + +#[test] +fn imported_type_cursor_after_dot_other_modules() { + let dep = " +pub type Zoo = List(String) +type Private = List(String) +"; + let other = " +pub type Zoo = List(String) +type Private = List(String) +"; + let code = "import dep + +pub fn wibble( + _: dep.Zoo, +) -> Nil { + Nil +} +"; + + assert_debug_snapshot!(completion( + TestProject::for_source(code) + .add_module("dep", dep) + .add_module("other", other), + Position::new(3, 12) + ),); +} + +#[test] +fn imported_type_cursor_mid_phrase_other_modules() { + let dep = " +pub type Zoo = List(String) +type Private = List(String) +"; + let other = " +pub type Zoo = List(String) +type Private = List(String) +"; + let code = "import dep + +pub fn wibble( + _: dep.Zoo, +) -> Nil { + Nil +} +"; + + assert_debug_snapshot!(completion( + TestProject::for_source(code) + .add_module("dep", dep) + .add_module("other", other), + Position::new(3, 8) + ),); +} + +#[test] +fn importable_type() { + let dep = " +pub type Zoo = List(String) +type Private = List(String) +"; + let code = " + +pub fn wibble( + _: String, +) -> Nil { + Nil +} +"; + + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_module("dep", dep), + Position::new(3, 0) + ),); +} + +#[test] +fn importable_type_with_existing_imports_at_top() { + let dep = " +pub type Zoo = List(String) +type Private = List(String) +"; + let dep2 = " +pub type Zoo = List(String) +type Private = List(String) +"; + let code = "import dep2 + +pub fn wibble( + _: String, +) -> Nil { + Nil +} +"; + + assert_debug_snapshot!(completion( + TestProject::for_source(code) + .add_module("dep", dep) + .add_module("dep2", dep2), + Position::new(3, 0) + ),); +} + +#[test] +fn importable_type_with_existing_imports() { + let dep = " +pub type Zoo = List(String) +type Private = List(String) +"; + let dep2 = " +pub type Zoo = List(String) +type Private = List(String) +"; + let code = " +//// Some module comments +// Some other whitespace + +import dep2 + +pub fn wibble( + _: String, +) -> Nil { + Nil +} +"; + + assert_debug_snapshot!(completion( + TestProject::for_source(code) + .add_module("dep", dep) + .add_module("dep2", dep2), + Position::new(7, 0) + ),); +} + +#[test] +fn importable_type_from_deep_module() { + let dep = " +pub type Zoo = List(String) +type Private = List(String) +"; + let code = " + +pub fn wibble( + _: String, +) -> Nil { + Nil +} +"; + + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_module("a/b/dep", dep), + Position::new(3, 0) + ),); } #[test] @@ -665,32 +640,10 @@ pub fn wibble( } "; - assert_eq!( - completion( - TestProject::for_source(code).add_module("dep", dep), - Position::new(3, 0) - ), - [ - prelude_type_completions(), - vec![ - CompletionItem { - label: "Zoo".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "dep.Zoo".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - ] - ] - .concat() - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_module("dep", dep), + Position::new(3, 0) + ),); } #[test] @@ -705,20 +658,10 @@ pub fn wibble( } "; - assert_eq!( - completion(TestProject::for_source(code), Position::new(4, 0)), - [ - prelude_type_completions(), - vec![CompletionItem { - label: "Zoo".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }], - ] - .concat() - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code), + Position::new(4, 0) + ),); } #[test] @@ -727,49 +670,13 @@ fn internal_values_from_root_package_are_in_the_completions() { @external(erlang, "rand", "uniform") @internal pub fn random_float() -> Float @internal pub fn main() { 0 } -@internal pub type Foo { Bar } -@internal pub const foo = 1 +@internal pub type Wibble { Wobble } +@internal pub const wibble = 1 "#; - assert_eq!( - completion_at_default_position( - TestProject::for_source("import dep").add_module("dep", dep) - ), - vec![ - CompletionItem { - label: "dep.Bar".into(), - label_details: None, - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Foo".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "dep.foo".into(), - label_details: None, - kind: Some(CompletionItemKind::CONSTANT), - detail: Some("Int".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "dep.main".into(), - label_details: None, - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Int".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "dep.random_float".into(), - label_details: None, - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Float".into()), - documentation: None, - ..Default::default() - }, - ] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source("import dep").add_module("dep", dep) + ),); } #[test] @@ -786,31 +693,10 @@ pub fn wibble( @internal pub type Alias = Int @internal pub type AnotherType { Constructor } "#; - let mut expected_completions = prelude_type_completions(); - expected_completions.append(&mut vec![ - CompletionItem { - label: "dep.Alias".into(), - label_details: None, - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - ..Default::default() - }, - CompletionItem { - label: "dep.AnotherType".into(), - label_details: None, - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - ..Default::default() - }, - ]); - - assert_eq!( - completion( - TestProject::for_source(code).add_module("dep", dep), - Position::new(3, 0) - ), - expected_completions, - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_module("dep", dep), + Position::new(3, 0) + ),); } #[test] @@ -819,47 +705,13 @@ fn internal_values_from_the_same_module_are_in_the_completions() { @external(erlang, "rand", "uniform") @internal pub fn random_float() -> Float @internal pub fn main() { 0 } -@internal pub type Foo { Bar } -@internal pub const foo = 1 +@internal pub type Wibble { Wobble } +@internal pub const wibble = 1 "#; - assert_eq!( - completion_at_default_position(TestProject::for_source(code)), - vec![ - CompletionItem { - label: "Bar".into(), - label_details: None, - kind: Some(CompletionItemKind::ENUM_MEMBER), - detail: Some("Foo".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "foo".into(), - label_details: None, - kind: Some(CompletionItemKind::CONSTANT), - detail: Some("Int".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "main".into(), - label_details: None, - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Int".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "random_float".into(), - label_details: None, - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Float".into()), - documentation: None, - ..Default::default() - }, - ] - ); + assert_debug_snapshot!(completion_at_default_position(TestProject::for_source( + code + )),); } #[test] @@ -867,33 +719,14 @@ fn internal_types_from_the_same_module_are_in_the_completions() { let code = " @internal pub type Alias = Result(Int, String) @internal pub type AnotherType { - Bar + Wibble } "; - assert_eq!( - completion(TestProject::for_source(code), Position::new(3, 0)), - [ - vec![ - CompletionItem { - label: "Alias".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - CompletionItem { - label: "AnotherType".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - documentation: None, - ..Default::default() - }, - ], - prelude_type_completions(), - ] - .concat() - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code), + Position::new(3, 0) + ),); } #[test] @@ -911,13 +744,10 @@ pub fn wibble( @internal pub type AnotherType { Constructor } "#; - assert_eq!( - completion( - TestProject::for_source(code).add_dep_module("dep", dep), - Position::new(3, 0) - ), - prelude_type_completions(), - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_dep_module("dep", dep), + Position::new(3, 0) + ),); } #[test] @@ -926,16 +756,13 @@ fn internal_values_from_a_dependency_are_ignored() { @external(erlang, "rand", "uniform") @internal pub fn random_float() -> Float @internal pub fn main() { 0 } -@internal pub type Foo { Bar } -@internal pub const foo = 1 +@internal pub type Wibble { Wobble } +@internal pub const wibble = 1 "#; - assert_eq!( - completion_at_default_position( - TestProject::for_source("import dep").add_dep_module("dep", dep) - ), - vec![] - ); + assert_debug_snapshot!(completion_at_default_position( + TestProject::for_source("import dep").add_dep_module("dep", dep) + ),); } #[test] @@ -947,30 +774,10 @@ pub fn main() { }"; let dep = ""; - assert_eq!( - completion( - TestProject::for_source(code).add_module("dep", dep), - Position::new(0, 10) - ), - vec![CompletionItem { - label: "gleam".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 10 - } - }, - new_text: "gleam".into() - })), - ..Default::default() - }] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_module("dep", dep), + Position::new(0, 10) + ),); } #[test] @@ -988,13 +795,10 @@ pub fn main() { } "; - assert_eq!( - completion( - TestProject::for_source(code).add_test_module("my_tests", test), - Position::new(0, 10) - ), - vec![] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_test_module("my_tests", test), + Position::new(0, 10) + ),); } #[test] @@ -1027,47 +831,7 @@ pub fn test_helper() { let mut completions = response.result.unwrap().unwrap_or_default(); completions.sort_by(|a, b| a.label.cmp(&b.label)); - assert_eq!( - completions, - vec![ - CompletionItem { - label: "app".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 12 - } - }, - new_text: "app".into() - })), - ..Default::default() - }, - CompletionItem { - label: "test_helper".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 12 - } - }, - new_text: "test_helper".into() - })), - ..Default::default() - } - ] - ); + assert_debug_snapshot!(completions,); } #[test] @@ -1083,30 +847,10 @@ pub fn main() { pub fn main() { 1 } "; - assert_eq!( - completion( - TestProject::for_source(code).add_dep_module("dep", dep), - Position::new(0, 10) - ), - vec![CompletionItem { - label: "dep".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 12 - } - }, - new_text: "dep".into() - })), - ..Default::default() - }] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_dep_module("dep", dep), + Position::new(0, 10) + ),); } #[test] @@ -1118,30 +862,10 @@ pub fn main() { }"; let dep = ""; - assert_eq!( - completion( - TestProject::for_source(code).add_hex_module("example_module", dep), - Position::new(0, 10) - ), - vec![CompletionItem { - label: "example_module".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 12 - } - }, - new_text: "example_module".into() - })), - ..Default::default() - }] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_hex_module("example_module", dep), + Position::new(0, 10) + ),); } #[test] @@ -1153,32 +877,12 @@ pub fn main() { }"; let dep = ""; - assert_eq!( - completion( - TestProject::for_source(code) - .add_hex_module("example_module", dep) - .add_indirect_hex_module("indirect_module", ""), - Position::new(0, 10) - ), - vec![CompletionItem { - label: "example_module".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 12 - } - }, - new_text: "example_module".into() - })), - ..Default::default() - }] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code) + .add_hex_module("example_module", dep) + .add_indirect_hex_module("indirect_module", ""), + Position::new(0, 10) + ),); } #[test] @@ -1190,32 +894,12 @@ pub fn main() { }"; let dep = ""; - assert_eq!( - completion( - TestProject::for_source(code) - .add_hex_module("example_module", dep) - .add_dev_hex_module("indirect_module", ""), - Position::new(0, 10) - ), - vec![CompletionItem { - label: "example_module".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 12 - } - }, - new_text: "example_module".into() - })), - ..Default::default() - }] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code) + .add_hex_module("example_module", dep) + .add_dev_hex_module("indirect_module", ""), + Position::new(0, 10) + ),); } #[test] @@ -1244,65 +928,7 @@ pub fn main() { let mut completions = response.result.unwrap().unwrap_or_default(); completions.sort_by(|a, b| a.label.cmp(&b.label)); - assert_eq!( - completions, - vec![ - CompletionItem { - label: "app".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 12 - } - }, - new_text: "app".into() - })), - ..Default::default() - }, - CompletionItem { - label: "example_module".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 12 - } - }, - new_text: "example_module".into() - })), - ..Default::default() - }, - CompletionItem { - label: "indirect_module".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 12 - } - }, - new_text: "indirect_module".into() - })), - ..Default::default() - } - ] - ); + assert_debug_snapshot!(completions,); } #[test] @@ -1321,30 +947,10 @@ pub fn main() { pub fn main() { 1 } "; - assert_eq!( - completion( - TestProject::for_source(code).add_hex_module("example_module", dep), - Position::new(3, 10) - ), - vec![CompletionItem { - label: "example_module".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 3, - character: 7 - }, - end: Position { - line: 3, - character: 12 - } - }, - new_text: "example_module".into() - })), - ..Default::default() - }] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_hex_module("example_module", dep), + Position::new(3, 10) + ),); } #[test] @@ -1356,30 +962,10 @@ pub fn main() { }"; let dep = ""; - assert_eq!( - completion( - TestProject::for_source(code).add_dep_module("dep", dep), - Position::new(0, 0) - ), - vec![CompletionItem { - label: "dep".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 12 - } - }, - new_text: "dep".into() - })), - ..Default::default() - }] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_dep_module("dep", dep), + Position::new(0, 0) + ),); } #[test] @@ -1391,30 +977,10 @@ pub fn main() { }"; let dep = ""; - assert_eq!( - completion( - TestProject::for_source(code).add_dep_module("dep", dep), - Position::new(0, 2) - ), - vec![CompletionItem { - label: "dep".into(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 13 - } - }, - new_text: "dep".into() - })), - ..Default::default() - }] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_dep_module("dep", dep), + Position::new(0, 2) + ),); } #[test] @@ -1426,34 +992,14 @@ pub fn main() { }"; let internal_name = format!("{}/internal", LSP_TEST_ROOT_PACKAGE_NAME); - assert_eq!( - completion( - TestProject::for_source(code) - // Not included - .add_dep_module("dep/internal", "") - // Included - .add_module(&internal_name, ""), - Position::new(0, 0) - ), - vec![CompletionItem { - label: internal_name.clone(), - kind: Some(CompletionItemKind::MODULE), - text_edit: Some(CompletionTextEdit::Edit(TextEdit { - range: Range { - start: Position { - line: 0, - character: 7 - }, - end: Position { - line: 0, - character: 12 - } - }, - new_text: internal_name.clone(), - })), - ..Default::default() - },] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code) + // Not included + .add_dep_module("dep/internal", "") + // Included + .add_module(&internal_name, ""), + Position::new(0, 0) + ),); } #[test] @@ -1476,39 +1022,10 @@ pub fn myfun() { pub type Wibble = String "; - assert_eq!( - completion( - TestProject::for_source(code).add_module("dep", dep), - Position::new(1, 12) - ), - vec![ - CompletionItem { - label: "Wibble".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - insert_text: Some("type Wibble".into()), - ..Default::default() - }, - CompletionItem { - label: "myfun".into(), - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Int".into()), - ..Default::default() - }, - CompletionItem { - label: "wabble".into(), - kind: Some(CompletionItemKind::CONSTANT), - detail: Some("String".into()), - ..Default::default() - }, - CompletionItem { - label: "wibble".into(), - kind: Some(CompletionItemKind::CONSTANT), - detail: Some("String".into()), - ..Default::default() - }, - ] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_module("dep", dep), + Position::new(1, 12) + ),); } #[test] @@ -1531,30 +1048,13 @@ pub fn myfun() { pub type Wibble = String "; - assert_eq!( - completion( - TestProject::for_source(code).add_module("dep", dep), - // putting cursor at beginning of line because some formatters - // remove the empty whitespace in the test code. - // Does also work with (3, 2) when empty spaces are not removed. - Position::new(3, 0) - ), - vec![ - CompletionItem { - label: "Wibble".into(), - kind: Some(CompletionItemKind::CLASS), - detail: Some("Type".into()), - insert_text: Some("type Wibble".into()), - ..Default::default() - }, - CompletionItem { - label: "myfun".into(), - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Int".into()), - ..Default::default() - }, - ] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_module("dep", dep), + // putting cursor at beginning of line because some formatters + // remove the empty whitespace in the test code. + // Does also work with (3, 2) when empty spaces are not removed. + Position::new(3, 0) + ),); } #[test] @@ -1577,18 +1077,10 @@ pub fn myfun() { pub type Wibble = String "; - assert_eq!( - completion( - TestProject::for_source(code).add_module("dep", dep), - Position::new(1, 12) - ), - vec![CompletionItem { - label: "myfun".into(), - kind: Some(CompletionItemKind::FUNCTION), - detail: Some("fn() -> Int".into()), - ..Default::default() - },] - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code).add_module("dep", dep), + Position::new(1, 12) + ),); } #[test] @@ -1601,10 +1093,10 @@ pub fn wibble( } "; - assert_eq!( - completion(TestProject::for_source(code), Position::new(2, 11)), - prelude_type_completions(), - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code), + Position::new(2, 11) + ),); } #[test] @@ -1617,10 +1109,10 @@ pub fn wibble( } "; - assert_eq!( - completion(TestProject::for_source(code), Position::new(3, 7)), - prelude_type_completions(), - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code), + Position::new(3, 7) + ),); } #[test] @@ -1631,10 +1123,10 @@ pub fn main() { } "; - assert_eq!( - completion(TestProject::for_source(code), Position::new(2, 16)), - prelude_type_completions(), - ); + assert_debug_snapshot!(completion( + TestProject::for_source(code), + Position::new(2, 16) + ),); } #[test] @@ -1648,8 +1140,65 @@ pub fn main() { } "; + assert_debug_snapshot!(completion( + TestProject::for_source(code), + Position::new(2, 16) + ),); +} + +#[test] +fn ignore_completions_in_empty_comment() { + // Reproducing issue #2161 + let code = " +pub fn main() { + case 0 { + // + _ -> 1 + } +} +"; + + // End of the comment (right after the last `/`) + assert_eq!( + completion(TestProject::for_source(code), Position::new(3, 6)), + vec![], + ); +} + +#[test] +fn ignore_completions_in_middle_of_comment() { + // Reproducing issue #2161 + let code = " +pub fn main() { + case 0 { + // comment + _ -> 1 + } +} +"; + + // At `c` + assert_eq!( + completion(TestProject::for_source(code), Position::new(3, 7)), + vec![], + ); +} + +#[test] +fn ignore_completions_in_end_of_comment() { + // Reproducing issue #2161 + let code = " +pub fn main() { + case 0 { + // comment + _ -> 1 + } +} +"; + + // End of the comment (after `t`) assert_eq!( - completion(TestProject::for_source(code), Position::new(2, 16)), - prelude_type_completions(), + completion(TestProject::for_source(code), Position::new(3, 14)), + vec![], ); } diff --git a/compiler-core/src/language_server/tests/hover.rs b/compiler-core/src/language_server/tests/hover.rs index 0c6299217..3476e9a95 100644 --- a/compiler-core/src/language_server/tests/hover.rs +++ b/compiler-core/src/language_server/tests/hover.rs @@ -1,4 +1,4 @@ -use lsp_types::{Hover, HoverContents, HoverParams, MarkedString, Position, Range}; +use lsp_types::{Hover, HoverParams, Position, Range}; use super::*; @@ -14,35 +14,162 @@ fn hover(tester: TestProject<'_>, position: Position) -> Option { }) } +struct PositionFinder { + value: String, + offset: usize, + nth_occurrence: usize, +} + +impl PositionFinder { + fn with_char_offset(self, offset: usize) -> Self { + Self { + value: self.value, + offset, + nth_occurrence: self.nth_occurrence, + } + } + + fn under_char(self, char: char) -> Self { + Self { + offset: self.value.find(char).unwrap_or(0), + value: self.value, + nth_occurrence: self.nth_occurrence, + } + } + + fn under_last_char(self) -> Self { + let len = self.value.len(); + self.with_char_offset(len - 1) + } + + fn nth_occurrence(self, nth_occurrence: usize) -> Self { + Self { + value: self.value, + offset: self.offset, + nth_occurrence, + } + } + + fn for_value(value: &str) -> Self { + Self { + value: value.into(), + offset: 0, + nth_occurrence: 1, + } + } + + fn find_position(&self, src: &str) -> Position { + let PositionFinder { + value, + offset, + nth_occurrence, + } = self; + + let byte_index = src + .match_indices(value) + .nth(nth_occurrence - 1) + .expect("no match for position") + .0; + + byte_index_to_position(src, byte_index + offset) + } +} + +fn find_position_of(value: &str) -> PositionFinder { + PositionFinder::for_value(value) +} + +fn byte_index_to_position(src: &str, byte_index: usize) -> Position { + let mut line = 0; + let mut col = 0; + + for (i, char) in src.bytes().enumerate() { + if i == byte_index { + break; + } + + if char == b'\n' { + line += 1; + col = 0; + } else { + col += 1; + } + } + + Position::new(line, col) +} + +fn show_hover(code: &str, range: Range, position: Position) -> String { + let Range { start, end } = range; + + // When we display the over range the end character is always excluded! + let end = Position::new(end.line, end.character); + + let mut str: String = "".into(); + for (line_number, line) in code.lines().enumerate() { + let mut underline: String = "".into(); + let mut underline_empty = true; + + for (column_number, _) in line.chars().enumerate() { + let current_position = Position::new(line_number as u32, column_number as u32); + if current_position == position { + underline_empty = false; + underline.push('↑'); + } else if start.le(¤t_position) && current_position.lt(&end) { + underline_empty = false; + underline.push('▔'); + } else { + underline.push(' '); + } + } + + str.push_str(line); + if !underline_empty { + str.push('\n'); + str.push_str(&underline); + } + str.push('\n'); + } + + str +} + +#[macro_export] +macro_rules! assert_hover { + ($code:literal, $position:expr $(,)?) => { + let project = TestProject::for_source($code); + assert_hover!(project, $position); + }; + + ($project:expr, $position:expr $(,)?) => { + let src = $project.src; + let position = $position.find_position(src); + let result = hover($project, position).expect("no hover produced"); + let pretty_hover = show_hover(src, result.range.expect("hover with no range"), position); + let output = format!( + "{}\n\n----- Hover content -----\n{:#?}", + pretty_hover, result.contents + ); + insta::assert_snapshot!(insta::internals::AutoName, output, src); + }; +} + #[test] fn hover_function_definition() { - let code = " + assert_hover!( + " fn add_2(x) { x + 2 } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(1, 3)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam -fn(Int) -> Int -``` -" - .to_string() - )), - range: Some(Range { - start: Position::new(1, 0), - end: Position::new(1, 11) - }), - }) +", + find_position_of("add_2") ); } #[test] fn hover_local_function() { - let code = " + assert_hover!( + " fn my_fn() { Nil } @@ -50,36 +177,16 @@ fn my_fn() { fn main() { my_fn } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(6, 3)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam -fn() -> Nil -``` -" - .to_string() - )), - range: Some(Range { - start: Position { - line: 6, - character: 2, - }, - end: Position { - line: 6, - character: 7, - }, - },), - }) +", + find_position_of("my_fn").under_char('y').nth_occurrence(2) ); } // https://github.com/gleam-lang/gleam/issues/2654 #[test] fn hover_local_function_in_pipe() { - let code = " + assert_hover!( + " fn add1(num: Int) -> Int { num + 1 } @@ -92,95 +199,82 @@ pub fn main() { |> add1 |> add1 } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(6, 3)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam -fn(Int) -> Int -``` -" - .to_string() - )), - range: Some(Range { - start: Position { - line: 6, - character: 2, - }, - end: Position { - line: 6, - character: 6, - }, - },), - }) +", + find_position_of("add1") + .with_char_offset(1) + .nth_occurrence(2) ); - assert_eq!( - hover(TestProject::for_source(code), Position::new(9, 7)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam -fn(Int) -> Int -``` -" - .to_string() - )), - range: Some(Range { - start: Position { - line: 9, - character: 5, - }, - end: Position { - line: 9, - character: 9, - }, - },), - }) +} + +// https://github.com/gleam-lang/gleam/issues/2654 +#[test] +fn hover_local_function_in_pipe_1() { + assert_hover!( + " +fn add1(num: Int) -> Int { + num + 1 +} + +pub fn main() { + add1(1) + + 1 + |> add1 + |> add1 + |> add1 +} +", + find_position_of("add1") + .with_char_offset(2) + .nth_occurrence(3) ); - assert_eq!( - hover(TestProject::for_source(code), Position::new(10, 7)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam -fn(Int) -> Int -``` -" - .to_string() - )), - range: Some(Range { - start: Position { - line: 10, - character: 5, - }, - end: Position { - line: 10, - character: 9, - }, - },), - }) +} + +// https://github.com/gleam-lang/gleam/issues/2654 +#[test] +fn hover_local_function_in_pipe_2() { + assert_hover!( + " +fn add1(num: Int) -> Int { + num + 1 +} + +pub fn main() { + add1(1) + + 1 + |> add1 + |> add1 + |> add1 +} +", + find_position_of("add1") + .with_char_offset(2) + .nth_occurrence(4) ); - assert_eq!( - hover(TestProject::for_source(code), Position::new(11, 7)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam -fn(Int) -> Int -``` -" - .to_string() - )), - range: Some(Range { - start: Position { - line: 11, - character: 5, - }, - end: Position { - line: 11, - character: 9, - }, - },), - }) +} + +// https://github.com/gleam-lang/gleam/issues/2654 +#[test] +fn hover_local_function_in_pipe_3() { + assert_hover!( + " +fn add1(num: Int) -> Int { + num + 1 +} + +pub fn main() { + add1(1) + + 1 + |> add1 + |> add1 + |> add1 +} +", + find_position_of("add1") + .with_char_offset(2) + .nth_occurrence(5) ); } @@ -193,13 +287,10 @@ fn main() { } "; - // hovering over "my_fn" - let hover = hover( + assert_hover!( TestProject::for_source(code).add_module("example_module", "pub fn my_fn() { Nil }"), - Position::new(3, 19), - ) - .unwrap(); - insta::assert_debug_snapshot!(hover); + find_position_of("my_fn").under_char('_'), + ); } #[test] @@ -211,13 +302,10 @@ fn main() { } "; - // hovering over "my_fn" - let hover = hover( + assert_hover!( TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"), - Position::new(3, 19), - ) - .unwrap(); - insta::assert_debug_snapshot!(hover); + find_position_of("my_fn").under_char('_'), + ); } #[test] @@ -229,13 +317,10 @@ fn main() { } "; - // hovering over "my_fn" - let hover = hover( + assert_hover!( TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"), - Position::new(3, 5), - ) - .unwrap(); - insta::assert_debug_snapshot!(hover); + find_position_of("my_fn").under_char('f').nth_occurrence(2), + ); } #[test] @@ -247,13 +332,10 @@ fn main() { } "; - // hovering over "my_fn" - let hover = hover( + assert_hover!( TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"), - Position::new(3, 22), - ) - .unwrap(); - insta::assert_debug_snapshot!(hover); + find_position_of("my_fn").under_char('f'), + ); } #[test] @@ -265,13 +347,10 @@ fn main() { } "; - // hovering over "my_fn" - let hover = hover( + assert_hover!( TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"), - Position::new(3, 6), - ) - .unwrap(); - insta::assert_debug_snapshot!(hover); + find_position_of("my_fn").under_char('_').nth_occurrence(2), + ); } #[test] @@ -284,14 +363,11 @@ fn main() { } "; - // hovering over "my_fn" - let hover = hover( + assert_hover!( TestProject::for_source(code) .add_hex_module("my/nested/example_module", "pub fn my_fn() { Nil }"), - Position::new(3, 22), - ) - .unwrap(); - insta::assert_debug_snapshot!(hover); + find_position_of("my_fn").under_char('f'), + ); } #[test] @@ -303,19 +379,15 @@ fn main() { } "#; - // hovering over "my_fn" - let hover = hover( - TestProject::for_source(code).add_hex_module( - "example_module", - r#" + let hex_module = r#" @external(erlang, "my_mod_ffi", "renamed_fn") pub fn my_fn() -> Nil -"#, - ), - Position::new(3, 22), - ) - .unwrap(); - insta::assert_debug_snapshot!(hover); +"#; + + assert_hover!( + TestProject::for_source(code).add_hex_module("example_module", hex_module,), + find_position_of("my_fn").under_char('f'), + ); } #[test] @@ -327,13 +399,10 @@ fn main() { } "; - // hovering over "my_const" - let hover = hover( + assert_hover!( TestProject::for_source(code).add_hex_module("example_module", "pub const my_const = 42"), - Position::new(3, 19), - ) - .unwrap(); - insta::assert_debug_snapshot!(hover); + find_position_of("my_const").under_char('_'), + ); } #[test] @@ -345,13 +414,12 @@ fn main() { } "; - // hovering over "my_const" - let hover = hover( + assert_hover!( TestProject::for_source(code).add_hex_module("example_module", "pub const my_const = 42"), - Position::new(3, 5), - ) - .unwrap(); - insta::assert_debug_snapshot!(hover); + find_position_of("my_const") + .under_char('c') + .nth_occurrence(2), + ); } #[test] @@ -364,15 +432,12 @@ fn main() { } "; - // hovering over "my_const" - let hover = hover( + assert_hover!( TestProject::for_source(code) .add_hex_module("a/example_module", "pub const my_const = 42") .add_hex_module("b/example_module", "pub const my_const = 42"), - Position::new(4, 22), - ) - .unwrap(); - insta::assert_debug_snapshot!(hover); + find_position_of("my_const").under_char('c'), + ); } #[test] @@ -385,80 +450,41 @@ fn main() { } "; - // hovering over "my_const" - let hover = hover( + assert_hover!( TestProject::for_source(code) .add_hex_module("a/example_module", "pub const my_const = 42") .add_hex_module("b/example_module", "pub const my_const = 42"), - Position::new(4, 8), - ) - .unwrap(); - insta::assert_debug_snapshot!(hover); + find_position_of("my_const") + .under_char('o') + .nth_occurrence(3), + ); } #[test] fn hover_function_definition_with_docs() { - let code = " + assert_hover!( + " /// Exciting documentation /// Maybe even multiple lines fn append(x, y) { x <> y } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(3, 3)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam -fn(String, String) -> String -``` - Exciting documentation - Maybe even multiple lines -" - .to_string() - )), - range: Some(Range { - start: Position { - line: 3, - character: 0, - }, - end: Position { - line: 3, - character: 15, - }, - },), - }) +", + find_position_of("append") ); } #[test] fn hover_function_argument() { - let code = " + assert_hover!( + " /// Exciting documentation /// Maybe even multiple lines fn append(x, y) { x <> y } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(3, 10)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nString\n```".to_string() - )), - range: Some(Range { - start: Position { - line: 3, - character: 10, - }, - end: Position { - line: 3, - character: 11, - }, - },), - }) +", + find_position_of("append(x, y)").under_char('x') ); } @@ -480,73 +506,32 @@ fn append(x, y) { #[test] fn hover_expressions_in_function_body() { - let code = " + assert_hover!( + " fn append(x, y) { x <> y } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(2, 2)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam -String -``` -A locally defined variable." - .to_string() - )), - range: Some(Range { - start: Position { - line: 2, - character: 2 - }, - end: Position { - line: 2, - character: 3 - } - }), - }) +", + find_position_of("x").nth_occurrence(2) ); } #[test] fn hover_module_constant() { - let code = " + assert_hover!( + " /// Exciting documentation /// Maybe even multiple lines const one = 1 -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(3, 6)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam -Int -``` - Exciting documentation - Maybe even multiple lines -" - .to_string() - )), - range: Some(Range { - start: Position { - line: 3, - character: 6 - }, - end: Position { - line: 3, - character: 9 - }, - }), - }) +", + find_position_of("one") ); } #[test] fn hover_variable_in_use_expression() { - let code = " + assert_hover!( + " fn b(fun: fn(Int) -> String) { fun(42) } @@ -557,181 +542,137 @@ fn do_stuff() { use a <- b c } -"; - - // hover over `a` - assert_eq!( - hover(TestProject::for_source(code), Position::new(8, 6)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String("```gleam\nInt\n```".to_string())), - range: Some(Range::new(Position::new(8, 6), Position::new(8, 7))), - }) +", + find_position_of("use a").under_last_char() ); +} - // hover over `b` - assert_eq!( - hover(TestProject::for_source(code), Position::new(8, 11)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nfn(fn(Int) -> String) -> String\n```\n".to_string() - )), - range: Some(Range::new(Position::new(8, 11), Position::new(8, 12))), - }) +#[test] +fn hover_variable_in_use_expression_1() { + assert_hover!( + " +fn b(fun: fn(Int) -> String) { + fun(42) +} + +fn do_stuff() { + let c = \"done\" + + use a <- b + c +} +", + find_position_of("b").nth_occurrence(2) ); +} - // hover over `c` - assert_eq!( - hover(TestProject::for_source(code), Position::new(9, 2)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nString\n```\nA locally defined variable.".to_string() - )), - range: Some(Range::new(Position::new(9, 2), Position::new(9, 3))), - }) +#[test] +fn hover_variable_in_use_expression_2() { + assert_hover!( + " +fn b(fun: fn(Int) -> String) { + fun(42) +} + +fn do_stuff() { + let c = \"done\" + + use a <- b + c +} +", + find_position_of("c").nth_occurrence(2) ); } #[test] -fn hover_function_arg_annotation() { - let code = " +fn hover_function_arg_annotation_2() { + assert_hover!( + " /// Exciting documentation /// Maybe even multiple lines fn append(x: String, y: String) -> String { x <> y } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(3, 17)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nString\n```\n".to_string() - )), - range: Some(Range::new(Position::new(3, 13), Position::new(3, 19))), - }) +", + find_position_of("String").under_char('n') ); } #[test] fn hover_function_return_annotation() { - let code = " + assert_hover!( + " /// Exciting documentation /// Maybe even multiple lines fn append(x: String, y: String) -> String { x <> y } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(3, 39)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nString\n```\n".to_string() - )), - range: Some(Range::new(Position::new(3, 35), Position::new(3, 41))), - }) +", + find_position_of("String").under_char('n').nth_occurrence(3) ); } #[test] fn hover_function_return_annotation_with_tuple() { - let code = " + assert_hover!( + " /// Exciting documentation /// Maybe even multiple lines fn append(x: String, y: String) -> #(String, String) { #(x, y) } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(3, 39)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nString\n```\n".to_string() - )), - range: Some(Range::new(Position::new(3, 37), Position::new(3, 43))), - }) +", + find_position_of("String").under_char('r').nth_occurrence(3) ); } #[test] fn hover_module_constant_annotation() { - let code = " + assert_hover!( + " /// Exciting documentation /// Maybe even multiple lines const one: Int = 1 -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(3, 13)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nInt\n```\n".to_string() - )), - range: Some(Range::new(Position::new(3, 11), Position::new(3, 14))), - }) +", + find_position_of("Int").under_last_char() ); } #[test] fn hover_type_constructor_annotation() { - let code = " + assert_hover!( + " type Wibble { Wibble(arg: String) } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(2, 20)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nString\n```\n".to_string() - )), - range: Some(Range::new(Position::new(2, 16), Position::new(2, 22))), - }) +", + find_position_of("String").under_char('n') ); } #[test] fn hover_type_alias_annotation() { - let code = " -type Wibble = Int -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(1, 15)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nInt\n```\n".to_string() - )), - range: Some(Range::new(Position::new(1, 14), Position::new(1, 17))), - }) - ); + assert_hover!("type Wibble = Int", find_position_of("Int").under_char('n')); } #[test] fn hover_assignment_annotation() { - let code = " + assert_hover!( + " fn wibble() { let wobble: Int = 7 wobble } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(2, 18)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nInt\n```\n".to_string() - )), - range: Some(Range::new(Position::new(2, 16), Position::new(2, 19))), - }) +", + find_position_of("Int").under_last_char() ); } #[test] fn hover_function_arg_annotation_with_documentation() { - let code = " + assert_hover!( + " /// Exciting documentation /// Maybe even multiple lines type Wibble { @@ -741,17 +682,10 @@ type Wibble { fn identity(x: Wibble) -> Wibble { x } -"; - - assert_eq!( - hover(TestProject::for_source(code), Position::new(7, 20)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nWibble\n```\n Exciting documentation\n Maybe even multiple lines\n" - .to_string() - )), - range: Some(Range::new(Position::new(7, 15), Position::new(7, 21))), - }) +", + find_position_of("Wibble") + .under_last_char() + .nth_occurrence(3) ); } @@ -764,25 +698,16 @@ fn main() { } "; - assert_eq!( - hover( - TestProject::for_source(code).add_module( - "example_module", - " + assert_hover!( + TestProject::for_source(code).add_module( + "example_module", + " /// Exciting documentation /// Maybe even multiple lines pub const my_num = 1" - ), - Position::new(1, 26) ), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nInt\n```\n Exciting documentation\n Maybe even multiple lines\n" - .to_string() - )), - range: Some(Range::new(Position::new(1, 23), Position::new(1, 29))), - }) - ) + find_position_of("my_num").under_char('n') + ); } #[test] @@ -794,25 +719,16 @@ fn main() { } "; - assert_eq!( - hover( - TestProject::for_source(code).add_hex_module( - "example_module", - " + assert_hover!( + TestProject::for_source(code).add_hex_module( + "example_module", + " /// Exciting documentation /// Maybe even multiple lines pub const my_num = 1" - ), - Position::new(1, 26) ), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nInt\n```\n Exciting documentation\n Maybe even multiple lines\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_num)" - .to_string() - )), - range: Some(Range::new(Position::new(1, 23), Position::new(1, 29))), - }) - ) + find_position_of("my_num").under_char('n') + ); } #[test] @@ -824,46 +740,96 @@ fn main() -> MyType { } "; - assert_eq!( - hover( - TestProject::for_source(code).add_module( - "example_module", - " + assert_hover!( + TestProject::for_source(code).add_module( + "example_module", + " /// Exciting documentation /// Maybe even multiple lines pub type MyType { - MyType + MyType }" - ), - Position::new(1, 33) ), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nMyType\n```\n Exciting documentation\n Maybe even multiple lines\n" - .to_string() - )), - range: Some(Range::new(Position::new(1, 23), Position::new(1, 34))), - }) - ) + find_position_of("MyType").under_last_char() + ); } #[test] fn hover_works_even_for_invalid_code() { - let code = " + assert_hover!( + " fn invalid() { 1 + Nil } fn valid() { Nil } -"; +", + find_position_of("fn valid").under_char('v') + ); +} - assert_eq!( - hover(TestProject::for_source(code), Position::new(2, 3)), - Some(Hover { - contents: HoverContents::Scalar(MarkedString::String( - "```gleam\nfn() -> Nil\n```\n".to_string() - )), - range: Some(Range { - start: Position::new(2, 0), - end: Position::new(2, 10) - }), - }) +#[test] +fn hover_for_pattern_spread_ignoring_all_fields() { + assert_hover!( + " +pub type Model { + Model( + Int, + Float, + label1: Int, + label2: String, + ) +} + +pub fn main() { + case todo { + Model(..) -> todo + } +} +", + find_position_of("..") + ); +} + +#[test] +fn hover_for_pattern_spread_ignoring_some_fields() { + assert_hover!( + " +pub type Model { + Model( + Int, + Float, + label1: Int, + label2: String, + ) +} + +pub fn main() { + case todo { + Model(_, label1: _, ..) -> todo + } +} +", + find_position_of("..").under_last_char() + ); +} + +#[test] +fn hover_for_pattern_spread_ignoring_all_positional_fields() { + assert_hover!( + " +pub type Model { + Model( + Int, + Float, + label1: Int, + label2: String, + ) +} + +pub fn main() { + case todo { + Model(_, _, _, ..) -> todo + } +} +", + find_position_of("..") ); } diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_multiple_redundant_tuple_with_catch_all_pattern.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_multiple_redundant_tuple_with_catch_all_pattern.snap new file mode 100644 index 000000000..aee6264ce --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_multiple_redundant_tuple_with_catch_all_pattern.snap @@ -0,0 +1,12 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub fn main() {\n case #(1, 2), #(3, 4) {\n #(2, 2), #(2, 2) -> 0\n #(1, 2), _ -> 0\n _, #(1, 2) -> 0\n _, _ -> 1\n }\n}\n" +--- +pub fn main() { + case 1, 2, 3, 4 { + 2, 2, 2, 2 -> 0 + 1, 2, _, _ -> 0 + _, _, 1, 2 -> 0 + _, _, _, _ -> 1 + } +} diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_retain_extras.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_retain_extras.snap index 7a05e6667..2d949d7a3 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_retain_extras.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_retain_extras.snap @@ -1,6 +1,6 @@ --- source: compiler-core/src/language_server/tests/action.rs -expression: result +expression: "\npub fn main() {\n case\n #(\n // first comment\n 1,\n // second comment\n 2,\n 3 // third comment before comma\n\n ,\n\n // fourth comment after comma\n\n )\n {\n #(\n // first comment\n a,\n // second comment\n b,\n c // third comment before comma\n\n ,\n\n // fourth comment after comma\n\n ) -> 0\n }\n}\n" --- pub fn main() { case diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_subject_nested.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_subject_nested.snap new file mode 100644 index 000000000..887b6646e --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_subject_nested.snap @@ -0,0 +1,7 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub fn main() {\n case #(case #(0) { #(a) -> 0 }) { #(b) -> 0 }\n}\n" +--- +pub fn main() { + case case 0 { a -> 0 } { b -> 0 } +} diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_subject_only_safe_remove.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_subject_only_safe_remove.snap new file mode 100644 index 000000000..e9fa912d9 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_subject_only_safe_remove.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub fn main() {\n case #(0), #(1) {\n #(1), #(b) -> 0\n a, #(0) -> 1 // The first of this clause is not a tuple\n #(a), #(b) -> 2\n }\n}\n" +--- +pub fn main() { + case #(0), 1 { + #(1), b -> 0 + a, 0 -> 1 // The first of this clause is not a tuple + #(a), b -> 2 + } +} diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_subject_simple.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_subject_simple.snap new file mode 100644 index 000000000..410c89945 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_in_case_subject_simple.snap @@ -0,0 +1,8 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub fn main() {\n case #(1) { #(a) -> 0 }\n case #(1, 2) { #(a, b) -> 0 }\n}\n" +--- +pub fn main() { + case 1 { a -> 0 } + case 1, 2 { a, b -> 0 } +} diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_with_catch_all_pattern.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_with_catch_all_pattern.snap new file mode 100644 index 000000000..92c958db9 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_redundant_tuple_with_catch_all_pattern.snap @@ -0,0 +1,10 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\npub fn main() {\n case #(1, 2) {\n #(1, 2) -> 0\n _ -> 1\n }\n}\n" +--- +pub fn main() { + case 1, 2 { + 1, 2 -> 0 + _, _ -> 1 + } +} diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_unused_alias.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_unused_alias.snap new file mode 100644 index 000000000..1bb37a806 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_unused_alias.snap @@ -0,0 +1,10 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\n// test\nimport result.{is_ok} as res\nimport option\n\npub fn main() {\n is_ok\n}\n" +--- +// test +import result.{is_ok} + +pub fn main() { + is_ok +} diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_unused_simple.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_unused_simple.snap new file mode 100644 index 000000000..62834edeb --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_unused_simple.snap @@ -0,0 +1,10 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "\n// test\nimport // comment\n list as lispy\nimport result\nimport option\n\npub fn main() {\n result.is_ok\n}\n" +--- +// test +import result + +pub fn main() { + result.is_ok +} diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_unused_start_of_file.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_unused_start_of_file.snap new file mode 100644 index 000000000..df8cbd7f8 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__action__remove_unused_start_of_file.snap @@ -0,0 +1,9 @@ +--- +source: compiler-core/src/language_server/tests/action.rs +expression: "import option\nimport result\n\npub fn main() {\n result.is_ok\n}\n" +--- +import result + +pub fn main() { + result.is_ok +} diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_const_annotation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_const_annotation.snap new file mode 100644 index 000000000..7dac05bf2 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_const_annotation.snap @@ -0,0 +1,222 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code), Position::new(2, 16))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_function_arg_annotation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_function_arg_annotation.snap new file mode 100644 index 000000000..a2d50942f --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_function_arg_annotation.snap @@ -0,0 +1,222 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code), Position::new(2, 11))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_function_return_annotation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_function_return_annotation.snap new file mode 100644 index 000000000..8546d4269 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_function_return_annotation.snap @@ -0,0 +1,222 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code), Position::new(3, 7))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_var_annotation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_var_annotation.snap new file mode 100644 index 000000000..7dac05bf2 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_a_var_annotation.snap @@ -0,0 +1,222 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code), Position::new(2, 16))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import.snap new file mode 100644 index 000000000..358aaa6bf --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import.snap @@ -0,0 +1,44 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\", dep),\n Position::new(0, 10))" +--- +[ + CompletionItem { + label: "gleam", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 10, + }, + }, + new_text: "gleam", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_from_dependency.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_from_dependency.snap new file mode 100644 index 000000000..407004e94 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_from_dependency.snap @@ -0,0 +1,44 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_hex_module(\"example_module\",\n dep), Position::new(0, 10))" +--- +[ + CompletionItem { + label: "example_module", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 12, + }, + }, + new_text: "example_module", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_from_dependency_with_docs.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_from_dependency_with_docs.snap new file mode 100644 index 000000000..60e33169b --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_from_dependency_with_docs.snap @@ -0,0 +1,44 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_hex_module(\"example_module\",\n dep), Position::new(3, 10))" +--- +[ + CompletionItem { + label: "example_module", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 7, + }, + end: Position { + line: 3, + character: 12, + }, + }, + new_text: "example_module", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_no_test.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_no_test.snap new file mode 100644 index 000000000..cc08bcc29 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_no_test.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_test_module(\"my_tests\", test),\n Position::new(0, 10))" +--- +[] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_not_from_dev_dependency.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_not_from_dev_dependency.snap new file mode 100644 index 000000000..96b177599 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_not_from_dev_dependency.snap @@ -0,0 +1,44 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_hex_module(\"example_module\",\n dep).add_dev_hex_module(\"indirect_module\", \"\"),\n Position::new(0, 10))" +--- +[ + CompletionItem { + label: "example_module", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 12, + }, + }, + new_text: "example_module", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_not_from_dev_dependency_in_test.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_not_from_dev_dependency_in_test.snap new file mode 100644 index 000000000..804315102 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_not_from_dev_dependency_in_test.snap @@ -0,0 +1,120 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: completions +--- +[ + CompletionItem { + label: "app", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 12, + }, + }, + new_text: "app", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "example_module", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 12, + }, + }, + new_text: "example_module", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "indirect_module", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 12, + }, + }, + new_text: "indirect_module", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_not_from_indirect_dependency.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_not_from_indirect_dependency.snap new file mode 100644 index 000000000..ab34cbc54 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_not_from_indirect_dependency.snap @@ -0,0 +1,44 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_hex_module(\"example_module\",\n dep).add_indirect_hex_module(\"indirect_module\", \"\"),\n Position::new(0, 10))" +--- +[ + CompletionItem { + label: "example_module", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 12, + }, + }, + new_text: "example_module", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_preceeding_whitespace.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_preceeding_whitespace.snap new file mode 100644 index 000000000..795eb2c16 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_preceeding_whitespace.snap @@ -0,0 +1,44 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_dep_module(\"dep\", dep),\n Position::new(0, 2))" +--- +[ + CompletionItem { + label: "dep", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 13, + }, + }, + new_text: "dep", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_start.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_start.snap new file mode 100644 index 000000000..d20a012ca --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_start.snap @@ -0,0 +1,44 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_dep_module(\"dep\", dep),\n Position::new(0, 0))" +--- +[ + CompletionItem { + label: "dep", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 12, + }, + }, + new_text: "dep", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_while_in_test.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_while_in_test.snap new file mode 100644 index 000000000..3658014af --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_while_in_test.snap @@ -0,0 +1,82 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: completions +--- +[ + CompletionItem { + label: "app", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 12, + }, + }, + new_text: "app", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "test_helper", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 12, + }, + }, + new_text: "test_helper", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_with_docs.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_with_docs.snap new file mode 100644 index 000000000..a2ff953b6 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_import_with_docs.snap @@ -0,0 +1,44 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_dep_module(\"dep\", dep),\n Position::new(0, 10))" +--- +[ + CompletionItem { + label: "dep", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 12, + }, + }, + new_text: "dep", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_unqualified_import.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_unqualified_import.snap new file mode 100644 index 000000000..90be3b2f9 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_unqualified_import.snap @@ -0,0 +1,187 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\", dep),\n Position::new(1, 12))" +--- +[ + CompletionItem { + label: "Wibble", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 12, + }, + end: Position { + line: 1, + character: 12, + }, + }, + new_text: "type Wibble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "myfun", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "dep", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Int", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 12, + }, + end: Position { + line: 1, + character: 12, + }, + }, + new_text: "myfun", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "wabble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "dep", + ), + }, + ), + kind: Some( + Constant, + ), + detail: Some( + "String", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 12, + }, + end: Position { + line: 1, + character: 12, + }, + }, + new_text: "wabble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "wibble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "dep", + ), + }, + ), + kind: Some( + Constant, + ), + detail: Some( + "String", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 12, + }, + end: Position { + line: 1, + character: 12, + }, + }, + new_text: "wibble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_unqualified_import_already_imported.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_unqualified_import_already_imported.snap new file mode 100644 index 000000000..7b79aea3d --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_unqualified_import_already_imported.snap @@ -0,0 +1,53 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\", dep),\n Position::new(1, 12))" +--- +[ + CompletionItem { + label: "myfun", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "dep", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Int", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 12, + }, + end: Position { + line: 1, + character: 18, + }, + }, + new_text: "myfun", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_unqualified_import_on_new_line.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_unqualified_import_on_new_line.snap new file mode 100644 index 000000000..320e5c499 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_an_unqualified_import_on_new_line.snap @@ -0,0 +1,93 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\", dep),\n Position::new(3, 0))" +--- +[ + CompletionItem { + label: "Wibble", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "type Wibble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "myfun", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "dep", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Int", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "myfun", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_outside_a_function.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_outside_a_function.snap new file mode 100644 index 000000000..dea8d1886 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__completions_for_outside_a_function.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code), Position::new(0, 0))" +--- +[] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__for_custom_type_definition.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__for_custom_type_definition.snap new file mode 100644 index 000000000..100275372 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__for_custom_type_definition.snap @@ -0,0 +1,262 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code), Position::new(2, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Wibble", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 2, + character: 0, + }, + end: Position { + line: 2, + character: 0, + }, + }, + new_text: "Wibble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__for_function_arguments.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__for_function_arguments.snap new file mode 100644 index 000000000..49de1e084 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__for_function_arguments.snap @@ -0,0 +1,222 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code), Position::new(2, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__for_type_alias.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__for_type_alias.snap new file mode 100644 index 000000000..100275372 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__for_type_alias.snap @@ -0,0 +1,262 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code), Position::new(2, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Wibble", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 2, + character: 0, + }, + end: Position { + line: 2, + character: 0, + }, + }, + new_text: "Wibble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_module_function.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_module_function.snap new file mode 100644 index 000000000..12ad7f6cb --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_module_function.snap @@ -0,0 +1,69 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[ + CompletionItem { + label: "dep.wobble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "dep", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Nil", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.wobble", + }, + ), + ), + additional_text_edits: Some( + [ + TextEdit { + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + new_text: "import dep\n", + }, + ], + ), + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_module_function_from_deep_module.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_module_function_from_deep_module.snap new file mode 100644 index 000000000..ca805d984 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_module_function_from_deep_module.snap @@ -0,0 +1,69 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"a/b/dep\",\n dep))" +--- +[ + CompletionItem { + label: "dep.wobble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "a/b/dep", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Nil", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.wobble", + }, + ), + ), + additional_text_edits: Some( + [ + TextEdit { + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + new_text: "import a/b/dep\n", + }, + ], + ), + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_module_function_with_existing_imports.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_module_function_with_existing_imports.snap new file mode 100644 index 000000000..2a9481d80 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_module_function_with_existing_imports.snap @@ -0,0 +1,116 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep).add_module(\"dep2\", dep2))" +--- +[ + CompletionItem { + label: "dep.wobble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "dep", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Nil", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.wobble", + }, + ), + ), + additional_text_edits: Some( + [ + TextEdit { + range: Range { + start: Position { + line: 7, + character: 0, + }, + end: Position { + line: 7, + character: 0, + }, + }, + new_text: "import dep\n", + }, + ], + ), + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep2.wobble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Nil", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep2.wobble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type.snap new file mode 100644 index 000000000..858ccc9dd --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type.snap @@ -0,0 +1,278 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\", dep),\n Position::new(3, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "dep.Zoo", + }, + ), + ), + additional_text_edits: Some( + [ + TextEdit { + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + new_text: "import dep\n", + }, + ], + ), + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type_from_deep_module.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type_from_deep_module.snap new file mode 100644 index 000000000..6bd18a017 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type_from_deep_module.snap @@ -0,0 +1,278 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"a/b/dep\", dep),\n Position::new(3, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "dep.Zoo", + }, + ), + ), + additional_text_edits: Some( + [ + TextEdit { + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + new_text: "import a/b/dep\n", + }, + ], + ), + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type_with_existing_imports.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type_with_existing_imports.snap new file mode 100644 index 000000000..9bdb417b7 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type_with_existing_imports.snap @@ -0,0 +1,318 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\",\n dep).add_module(\"dep2\", dep2), Position::new(7, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 7, + character: 0, + }, + end: Position { + line: 7, + character: 0, + }, + }, + new_text: "dep.Zoo", + }, + ), + ), + additional_text_edits: Some( + [ + TextEdit { + range: Range { + start: Position { + line: 4, + character: 0, + }, + end: Position { + line: 4, + character: 0, + }, + }, + new_text: "import dep\n", + }, + ], + ), + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep2.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 7, + character: 0, + }, + end: Position { + line: 7, + character: 0, + }, + }, + new_text: "dep2.Zoo", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type_with_existing_imports_at_top.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type_with_existing_imports_at_top.snap new file mode 100644 index 000000000..c85fa1a37 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__importable_type_with_existing_imports_at_top.snap @@ -0,0 +1,318 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\",\n dep).add_module(\"dep2\", dep2), Position::new(3, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "dep.Zoo", + }, + ), + ), + additional_text_edits: Some( + [ + TextEdit { + range: Range { + start: Position { + line: 0, + character: 0, + }, + end: Position { + line: 0, + character: 0, + }, + }, + new_text: "import dep\n", + }, + ], + ), + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep2.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "dep2.Zoo", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_module_function.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_module_function.snap new file mode 100644 index 000000000..e23b29e2e --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_module_function.snap @@ -0,0 +1,53 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[ + CompletionItem { + label: "dep.wobble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Nil", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.wobble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_public_enum.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_public_enum.snap new file mode 100644 index 000000000..8d6710374 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_public_enum.snap @@ -0,0 +1,100 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[ + CompletionItem { + label: "dep.Left", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Direction", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.Left", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Right", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Direction", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.Right", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_public_record.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_public_record.snap new file mode 100644 index 000000000..8dbf898e9 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_public_record.snap @@ -0,0 +1,53 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[ + CompletionItem { + label: "dep.Box", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Constructor, + ), + detail: Some( + "fn(Int) -> Box", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.Box", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type.snap new file mode 100644 index 000000000..b6f3139e9 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type.snap @@ -0,0 +1,262 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\", dep),\n Position::new(3, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "dep.Zoo", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_after_dot.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_after_dot.snap new file mode 100644 index 000000000..b21d7441e --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_after_dot.snap @@ -0,0 +1,262 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\", dep),\n Position::new(3, 12))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 5, + }, + end: Position { + line: 3, + character: 12, + }, + }, + new_text: "dep.Zoo", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_after_dot_other_matching_modules.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_after_dot_other_matching_modules.snap new file mode 100644 index 000000000..9e3185b34 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_after_dot_other_matching_modules.snap @@ -0,0 +1,262 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\",\n dep).add_module(\"dep2\", dep2), Position::new(4, 12))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 4, + character: 5, + }, + end: Position { + line: 4, + character: 12, + }, + }, + new_text: "dep.Zoo", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_after_dot_other_modules.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_after_dot_other_modules.snap new file mode 100644 index 000000000..b74c476bb --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_after_dot_other_modules.snap @@ -0,0 +1,262 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\",\n dep).add_module(\"other\", other), Position::new(3, 12))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 5, + }, + end: Position { + line: 3, + character: 12, + }, + }, + new_text: "dep.Zoo", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_mid_phrase_other_modules.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_mid_phrase_other_modules.snap new file mode 100644 index 000000000..5ffe9cd0d --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_type_cursor_mid_phrase_other_modules.snap @@ -0,0 +1,262 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\",\n dep).add_module(\"other\", other), Position::new(3, 8))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 5, + }, + end: Position { + line: 3, + character: 12, + }, + }, + new_text: "dep.Zoo", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_unqualified_module_function.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_unqualified_module_function.snap new file mode 100644 index 000000000..bd5476063 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_unqualified_module_function.snap @@ -0,0 +1,100 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[ + CompletionItem { + label: "dep.wobble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Nil", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.wobble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "wobble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Nil", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "wobble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_unqualified_public_enum.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_unqualified_public_enum.snap new file mode 100644 index 000000000..7414677ca --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_unqualified_public_enum.snap @@ -0,0 +1,147 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[ + CompletionItem { + label: "Left", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Direction", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "Left", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Left", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Direction", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.Left", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Right", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Direction", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.Right", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_unqualified_public_record.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_unqualified_public_record.snap new file mode 100644 index 000000000..553b124af --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__imported_unqualified_public_record.snap @@ -0,0 +1,100 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[ + CompletionItem { + label: "Box", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Constructor, + ), + detail: Some( + "fn(Int) -> Box", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "Box", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Box", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Constructor, + ), + detail: Some( + "fn(Int) -> Box", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.Box", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__in_custom_type_definition.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__in_custom_type_definition.snap new file mode 100644 index 000000000..adc370eba --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__in_custom_type_definition.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_modules_from_same_package_are_included.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_modules_from_same_package_are_included.snap new file mode 100644 index 000000000..e3a7a3e2d --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_modules_from_same_package_are_included.snap @@ -0,0 +1,44 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_dep_module(\"dep/internal\",\n \"\").add_module(&internal_name, \"\"), Position::new(0, 0))" +--- +[ + CompletionItem { + label: "app/internal", + label_details: None, + kind: Some( + Module, + ), + detail: None, + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 0, + character: 7, + }, + end: Position { + line: 0, + character: 12, + }, + }, + new_text: "app/internal", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_types_from_a_dependency_are_ignored.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_types_from_a_dependency_are_ignored.snap new file mode 100644 index 000000000..85873e74b --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_types_from_a_dependency_are_ignored.snap @@ -0,0 +1,222 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_dep_module(\"dep\", dep),\n Position::new(3, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_types_from_root_package_are_in_the_completions.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_types_from_root_package_are_in_the_completions.snap new file mode 100644 index 000000000..6f3edd62b --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_types_from_root_package_are_in_the_completions.snap @@ -0,0 +1,302 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\", dep),\n Position::new(3, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Alias", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "dep.Alias", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.AnotherType", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "dep.AnotherType", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_types_from_the_same_module_are_in_the_completions.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_types_from_the_same_module_are_in_the_completions.snap new file mode 100644 index 000000000..08a271eec --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_types_from_the_same_module_are_in_the_completions.snap @@ -0,0 +1,302 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code), Position::new(3, 0))" +--- +[ + CompletionItem { + label: "Alias", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "Alias", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "AnotherType", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "AnotherType", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_values_from_a_dependency_are_ignored.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_values_from_a_dependency_are_ignored.snap new file mode 100644 index 000000000..2a4426a50 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_values_from_a_dependency_are_ignored.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(\"import dep\").add_dep_module(\"dep\",\n dep))" +--- +[] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_values_from_root_package_are_in_the_completions.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_values_from_root_package_are_in_the_completions.snap new file mode 100644 index 000000000..e07bf77c7 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_values_from_root_package_are_in_the_completions.snap @@ -0,0 +1,194 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(\"import dep\").add_module(\"dep\",\n dep))" +--- +[ + CompletionItem { + label: "dep.Wobble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Wibble", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.Wobble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.main", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Int", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.main", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.random_float", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Float", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.random_float", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.wibble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Constant, + ), + detail: Some( + "Int", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "dep.wibble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_values_from_the_same_module_are_in_the_completions.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_values_from_the_same_module_are_in_the_completions.snap new file mode 100644 index 000000000..829d4381e --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__internal_values_from_the_same_module_are_in_the_completions.snap @@ -0,0 +1,194 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code))" +--- +[ + CompletionItem { + label: "Wobble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Wibble", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "Wobble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "main", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Int", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "main", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "random_float", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Float", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "random_float", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "wibble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Constant, + ), + detail: Some( + "Int", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "wibble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_private_type.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_private_type.snap new file mode 100644 index 000000000..b170923e1 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_private_type.snap @@ -0,0 +1,262 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code), Position::new(4, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 4, + character: 0, + }, + end: Position { + line: 4, + character: 0, + }, + }, + new_text: "Zoo", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_enum.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_enum.snap new file mode 100644 index 000000000..d06b52528 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_enum.snap @@ -0,0 +1,100 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code))" +--- +[ + CompletionItem { + label: "Left", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Direction", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "Left", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Right", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Direction", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "Right", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_enum_with_documentation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_enum_with_documentation.snap new file mode 100644 index 000000000..b86f9349b --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_enum_with_documentation.snap @@ -0,0 +1,114 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code))" +--- +[ + CompletionItem { + label: "Left", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Direction", + ), + documentation: Some( + MarkupContent( + MarkupContent { + kind: Markdown, + value: " Hello\n", + }, + ), + ), + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "Left", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Right", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Direction", + ), + documentation: Some( + MarkupContent( + MarkupContent { + kind: Markdown, + value: " Goodbye\n", + }, + ), + ), + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "Right", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_function.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_function.snap new file mode 100644 index 000000000..0a5bc8e74 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_function.snap @@ -0,0 +1,53 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code))" +--- +[ + CompletionItem { + label: "main", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Int", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "main", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_function_with_documentation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_function_with_documentation.snap new file mode 100644 index 000000000..b776aefd9 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_function_with_documentation.snap @@ -0,0 +1,60 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code))" +--- +[ + CompletionItem { + label: "main", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Int", + ), + documentation: Some( + MarkupContent( + MarkupContent { + kind: Markdown, + value: " Hello\n", + }, + ), + ), + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "main", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_record.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_record.snap new file mode 100644 index 000000000..2214776bf --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_record.snap @@ -0,0 +1,60 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code))" +--- +[ + CompletionItem { + label: "Box", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Constructor, + ), + detail: Some( + "fn(Int, Int, Float) -> Box", + ), + documentation: Some( + MarkupContent( + MarkupContent { + kind: Markdown, + value: " Hello\n", + }, + ), + ), + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "Box", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_record_with_documentation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_record_with_documentation.snap new file mode 100644 index 000000000..0c3991025 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__local_public_record_with_documentation.snap @@ -0,0 +1,53 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code))" +--- +[ + CompletionItem { + label: "Box", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Constructor, + ), + detail: Some( + "fn(Int, Int, Float) -> Box", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "Box", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__opaque_type.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__opaque_type.snap new file mode 100644 index 000000000..c2d9de613 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__opaque_type.snap @@ -0,0 +1,53 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[ + CompletionItem { + label: "Wobble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Wibble", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "Wobble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_function.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_function.snap new file mode 100644 index 000000000..8e64c2ff5 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_function.snap @@ -0,0 +1,53 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[ + CompletionItem { + label: "private", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + Function, + ), + detail: Some( + "fn() -> Int", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "private", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_function_in_dep.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_function_in_dep.snap new file mode 100644 index 000000000..adc370eba --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_function_in_dep.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_type.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_type.snap new file mode 100644 index 000000000..c2d9de613 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_type.snap @@ -0,0 +1,53 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[ + CompletionItem { + label: "Wobble", + label_details: Some( + CompletionItemLabelDetails { + detail: None, + description: Some( + "app", + ), + }, + ), + kind: Some( + EnumMember, + ), + detail: Some( + "Wibble", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 1, + character: 0, + }, + end: Position { + line: 1, + character: 0, + }, + }, + new_text: "Wobble", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_type_in_dep.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_type_in_dep.snap new file mode 100644 index 000000000..adc370eba --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__private_type_in_dep.snap @@ -0,0 +1,5 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion_at_default_position(TestProject::for_source(code).add_module(\"dep\",\n dep))" +--- +[] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__unqualified_imported_type.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__unqualified_imported_type.snap new file mode 100644 index 000000000..a5ab8f4ff --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__completion__unqualified_imported_type.snap @@ -0,0 +1,302 @@ +--- +source: compiler-core/src/language_server/tests/completion.rs +expression: "completion(TestProject::for_source(code).add_module(\"dep\", dep),\n Position::new(3, 0))" +--- +[ + CompletionItem { + label: "BitArray", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Bool", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Float", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Int", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "List", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Nil", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Result", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "String", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "UtfCodepoint", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: None, + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "Zoo", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, + CompletionItem { + label: "dep.Zoo", + label_details: None, + kind: Some( + Class, + ), + detail: Some( + "Type", + ), + documentation: None, + deprecated: None, + preselect: None, + sort_text: None, + filter_text: None, + insert_text: None, + insert_text_format: None, + insert_text_mode: None, + text_edit: Some( + Edit( + TextEdit { + range: Range { + start: Position { + line: 3, + character: 0, + }, + end: Position { + line: 3, + character: 0, + }, + }, + new_text: "dep.Zoo", + }, + ), + ), + additional_text_edits: None, + command: None, + commit_characters: None, + data: None, + tags: None, + }, +] diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_assignment_annotation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_assignment_annotation.snap new file mode 100644 index 000000000..cc8a3c209 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_assignment_annotation.snap @@ -0,0 +1,17 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn wibble() {\n let wobble: Int = 7\n wobble\n}\n" +--- +fn wibble() { + let wobble: Int = 7 + ▔▔↑ + wobble +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nInt\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_expressions_in_function_body.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_expressions_in_function_body.snap new file mode 100644 index 000000000..85064815d --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_expressions_in_function_body.snap @@ -0,0 +1,16 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn append(x, y) {\n x <> y\n}\n" +--- +fn append(x, y) { + x <> y + ↑ +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nString\n```\nA locally defined variable.", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_function_with_another_value_same_name.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_function_with_another_value_same_name.snap index d30375491..510f6a077 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_function_with_another_value_same_name.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_function_with_another_value_same_name.snap @@ -1,23 +1,18 @@ --- source: compiler-core/src/language_server/tests/hover.rs -expression: hover +expression: "\nimport a/example_module.{my_const as discarded}\nimport b/example_module.{my_const} as _\nfn main() {\n my_const\n}\n" --- -Hover { - contents: Scalar( - String( - "```gleam\nInt\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/b/example_module.html#my_const)", - ), - ), - range: Some( - Range { - start: Position { - line: 4, - character: 4, - }, - end: Position { - line: 4, - character: 12, - }, - }, - ), +import a/example_module.{my_const as discarded} +import b/example_module.{my_const} as _ +fn main() { + my_const + ▔▔▔▔↑▔▔▔ } + + +----- Hover content ----- +Scalar( + String( + "```gleam\nInt\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/b/example_module.html#my_const)", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_constants.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_constants.snap index 1af98e131..7598b94ab 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_constants.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_constants.snap @@ -1,23 +1,17 @@ --- source: compiler-core/src/language_server/tests/hover.rs -expression: hover +expression: "\nimport example_module\nfn main() {\n example_module.my_const\n}\n" --- -Hover { - contents: Scalar( - String( - "```gleam\nInt\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_const)", - ), - ), - range: Some( - Range { - start: Position { - line: 3, - character: 16, - }, - end: Position { - line: 3, - character: 25, - }, - }, - ), +import example_module +fn main() { + example_module.my_const + ▔▔▔↑▔▔▔▔▔ } + + +----- Hover content ----- +Scalar( + String( + "```gleam\nInt\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_const)", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_ffi_renamed_function.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_ffi_renamed_function.snap index 886496b86..380ee6345 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_ffi_renamed_function.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_ffi_renamed_function.snap @@ -1,23 +1,17 @@ --- source: compiler-core/src/language_server/tests/hover.rs -expression: hover +expression: "\nimport example_module\nfn main() {\n example_module.my_fn\n}\n" --- -Hover { - contents: Scalar( - String( - "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_fn)", - ), - ), - range: Some( - Range { - start: Position { - line: 3, - character: 18, - }, - end: Position { - line: 3, - character: 24, - }, - }, - ), +import example_module +fn main() { + example_module.my_fn + ▔▔▔▔↑▔ } + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_fn)", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function.snap index bf1bd9249..5a54deec4 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function.snap @@ -1,23 +1,17 @@ --- source: compiler-core/src/language_server/tests/hover.rs -expression: hover +expression: "\nimport example_module\nfn main() {\n example_module.my_fn\n}\n" --- -Hover { - contents: Scalar( - String( - "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_fn)", - ), - ), - range: Some( - Range { - start: Position { - line: 3, - character: 16, - }, - end: Position { - line: 3, - character: 22, - }, - }, - ), +import example_module +fn main() { + example_module.my_fn + ▔▔▔↑▔▔ } + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_fn)", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function_nested_module.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function_nested_module.snap index 69ea58ffa..81b2f92c8 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function_nested_module.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function_nested_module.snap @@ -1,23 +1,17 @@ --- source: compiler-core/src/language_server/tests/hover.rs -expression: hover +expression: "\nimport my/nested/example_module\nfn main() {\n example_module.my_fn\n}\n" --- -Hover { - contents: Scalar( - String( - "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/my/nested/example_module.html#my_fn)", - ), - ), - range: Some( - Range { - start: Position { - line: 3, - character: 18, - }, - end: Position { - line: 3, - character: 24, - }, - }, - ), +import my/nested/example_module +fn main() { + example_module.my_fn + ▔▔▔▔↑▔ } + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/my/nested/example_module.html#my_fn)", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function_renamed_module.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function_renamed_module.snap index 886496b86..4b32c575e 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function_renamed_module.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_function_renamed_module.snap @@ -1,23 +1,17 @@ --- source: compiler-core/src/language_server/tests/hover.rs -expression: hover +expression: "\nimport example_module as renamed_module\nfn main() {\n renamed_module.my_fn\n}\n" --- -Hover { - contents: Scalar( - String( - "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_fn)", - ), - ), - range: Some( - Range { - start: Position { - line: 3, - character: 18, - }, - end: Position { - line: 3, - character: 24, - }, - }, - ), +import example_module as renamed_module +fn main() { + renamed_module.my_fn + ▔▔▔▔↑▔ } + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_fn)", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_unqualified_constants.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_unqualified_constants.snap index 1355418cd..74170210c 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_unqualified_constants.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_unqualified_constants.snap @@ -1,23 +1,17 @@ --- source: compiler-core/src/language_server/tests/hover.rs -expression: hover +expression: "\nimport example_module.{my_const}\nfn main() {\n my_const\n}\n" --- -Hover { - contents: Scalar( - String( - "```gleam\nInt\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_const)", - ), - ), - range: Some( - Range { - start: Position { - line: 3, - character: 2, - }, - end: Position { - line: 3, - character: 10, - }, - }, - ), +import example_module.{my_const} +fn main() { + my_const + ▔▔▔↑▔▔▔▔ } + + +----- Hover content ----- +Scalar( + String( + "```gleam\nInt\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_const)", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_unqualified_function.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_unqualified_function.snap index 482d2620c..0945fc282 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_unqualified_function.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_imported_unqualified_function.snap @@ -1,23 +1,17 @@ --- source: compiler-core/src/language_server/tests/hover.rs -expression: hover +expression: "\nimport example_module.{my_fn}\nfn main() {\n my_fn\n}\n" --- -Hover { - contents: Scalar( - String( - "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_fn)", - ), - ), - range: Some( - Range { - start: Position { - line: 3, - character: 2, - }, - end: Position { - line: 3, - character: 7, - }, - }, - ), +import example_module.{my_fn} +fn main() { + my_fn + ▔▔▔↑▔ } + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_fn)", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_unqualified_imported_function_renamed_module.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_unqualified_imported_function_renamed_module.snap index cf43a085b..6ab0b2363 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_unqualified_imported_function_renamed_module.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_unqualified_imported_function_renamed_module.snap @@ -1,23 +1,17 @@ --- source: compiler-core/src/language_server/tests/hover.rs -expression: hover +expression: "\nimport example_module.{my_fn} as renamed_module\nfn main() {\n my_fn\n}\n" --- -Hover { - contents: Scalar( - String( - "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_fn)", - ), - ), - range: Some( - Range { - start: Position { - line: 3, - character: 4, - }, - end: Position { - line: 3, - character: 9, - }, - }, - ), +import example_module.{my_fn} as renamed_module +fn main() { + my_fn + ▔▔↑▔▔ } + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn() -> Nil\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_fn)", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_value_with_two_modules_same_name.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_value_with_two_modules_same_name.snap index aea49eff3..fe432fc43 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_value_with_two_modules_same_name.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_external_value_with_two_modules_same_name.snap @@ -1,23 +1,18 @@ --- source: compiler-core/src/language_server/tests/hover.rs -expression: hover +expression: "\nimport a/example_module as _\nimport b/example_module\nfn main() {\n example_module.my_const\n}\n" --- -Hover { - contents: Scalar( - String( - "```gleam\nInt\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/b/example_module.html#my_const)", - ), - ), - range: Some( - Range { - start: Position { - line: 4, - character: 18, - }, - end: Position { - line: 4, - character: 27, - }, - }, - ), +import a/example_module as _ +import b/example_module +fn main() { + example_module.my_const + ▔▔▔▔↑▔▔▔▔ } + + +----- Hover content ----- +Scalar( + String( + "```gleam\nInt\n```\n\nView on [HexDocs](https://hexdocs.pm/hex/b/example_module.html#my_const)", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_for_pattern_spread_ignoring_all_fields.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_for_pattern_spread_ignoring_all_fields.snap new file mode 100644 index 000000000..c02a14f1a --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_for_pattern_spread_ignoring_all_fields.snap @@ -0,0 +1,27 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\npub type Model {\n Model(\n Int,\n Float,\n label1: Int,\n label2: String,\n )\n}\n\npub fn main() {\n case todo {\n Model(..) -> todo\n }\n}\n" +--- +pub type Model { + Model( + Int, + Float, + label1: Int, + label2: String, + ) +} + +pub fn main() { + case todo { + Model(..) -> todo + ↑▔ + } +} + + +----- Hover content ----- +Scalar( + String( + "Unused positional fields:\n- `Int`\n- `Float`\n\nUnused labelled fields:\n- `label1: Int`\n- `label2: String`", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_for_pattern_spread_ignoring_all_positional_fields.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_for_pattern_spread_ignoring_all_positional_fields.snap new file mode 100644 index 000000000..a6f2e23f8 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_for_pattern_spread_ignoring_all_positional_fields.snap @@ -0,0 +1,27 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\npub type Model {\n Model(\n Int,\n Float,\n label1: Int,\n label2: String,\n )\n}\n\npub fn main() {\n case todo {\n Model(_, _, _, ..) -> todo\n }\n}\n" +--- +pub type Model { + Model( + Int, + Float, + label1: Int, + label2: String, + ) +} + +pub fn main() { + case todo { + Model(_, _, _, ..) -> todo + ↑▔ + } +} + + +----- Hover content ----- +Scalar( + String( + "Unused labelled fields:\n- `label2: String`", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_for_pattern_spread_ignoring_some_fields.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_for_pattern_spread_ignoring_some_fields.snap new file mode 100644 index 000000000..7164f6c70 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_for_pattern_spread_ignoring_some_fields.snap @@ -0,0 +1,27 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\npub type Model {\n Model(\n Int,\n Float,\n label1: Int,\n label2: String,\n )\n}\n\npub fn main() {\n case todo {\n Model(_, label1: _, ..) -> todo\n }\n}\n" +--- +pub type Model { + Model( + Int, + Float, + label1: Int, + label2: String, + ) +} + +pub fn main() { + case todo { + Model(_, label1: _, ..) -> todo + ▔↑ + } +} + + +----- Hover content ----- +Scalar( + String( + "Unused positional fields:\n- `Float`\n\nUnused labelled fields:\n- `label2: String`", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_arg_annotation_2.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_arg_annotation_2.snap new file mode 100644 index 000000000..e91198cd8 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_arg_annotation_2.snap @@ -0,0 +1,18 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\nfn append(x: String, y: String) -> String {\n x <> y\n}\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +fn append(x: String, y: String) -> String { + ▔▔▔▔↑▔ + x <> y +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nString\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_arg_annotation_with_documentation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_arg_annotation_with_documentation.snap new file mode 100644 index 000000000..78782de6f --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_arg_annotation_with_documentation.snap @@ -0,0 +1,22 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\ntype Wibble {\n Wibble(arg: String)\n}\n\nfn identity(x: Wibble) -> Wibble {\n x\n}\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +type Wibble { + Wibble(arg: String) +} + +fn identity(x: Wibble) -> Wibble { + ▔▔▔▔▔↑ + x +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nWibble\n```\n Exciting documentation\n Maybe even multiple lines\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_argument.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_argument.snap new file mode 100644 index 000000000..83e1eb245 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_argument.snap @@ -0,0 +1,18 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\nfn append(x, y) {\n x <> y\n}\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +fn append(x, y) { + ↑ + x <> y +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nString\n```", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_definition.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_definition.snap new file mode 100644 index 000000000..e909c90bb --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_definition.snap @@ -0,0 +1,16 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn add_2(x) {\n x + 2\n}\n" +--- +fn add_2(x) { +▔▔▔↑▔▔▔▔▔▔▔ + x + 2 +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn(Int) -> Int\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_definition_with_docs.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_definition_with_docs.snap new file mode 100644 index 000000000..2cc8a9f13 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_definition_with_docs.snap @@ -0,0 +1,18 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\nfn append(x, y) {\n x <> y\n}\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +fn append(x, y) { +▔▔▔↑▔▔▔▔▔▔▔▔▔▔▔ + x <> y +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn(String, String) -> String\n```\n Exciting documentation\n Maybe even multiple lines\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_return_annotation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_return_annotation.snap new file mode 100644 index 000000000..ab797d7b8 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_return_annotation.snap @@ -0,0 +1,18 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\nfn append(x: String, y: String) -> String {\n x <> y\n}\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +fn append(x: String, y: String) -> String { + ▔▔▔▔↑▔ + x <> y +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nString\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_return_annotation_with_tuple.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_return_annotation_with_tuple.snap new file mode 100644 index 000000000..d0964ae39 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_function_return_annotation_with_tuple.snap @@ -0,0 +1,18 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\nfn append(x: String, y: String) -> #(String, String) {\n #(x, y)\n}\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +fn append(x: String, y: String) -> #(String, String) { + ▔▔↑▔▔▔ + #(x, y) +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nString\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_import_unqualified_type.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_import_unqualified_type.snap new file mode 100644 index 000000000..d82b572c9 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_import_unqualified_type.snap @@ -0,0 +1,17 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nimport example_module.{type MyType, MyType}\nfn main() -> MyType {\n MyType\n}\n" +--- +import example_module.{type MyType, MyType} + ▔▔▔▔▔▔▔▔▔▔↑ +fn main() -> MyType { + MyType +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nMyType\n```\n Exciting documentation\n Maybe even multiple lines\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_import_unqualified_value.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_import_unqualified_value.snap new file mode 100644 index 000000000..598b38c3f --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_import_unqualified_value.snap @@ -0,0 +1,17 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nimport example_module.{my_num}\nfn main() {\n my_num\n}\n" +--- +import example_module.{my_num} + ▔▔▔↑▔▔ +fn main() { + my_num +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nInt\n```\n Exciting documentation\n Maybe even multiple lines\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_import_unqualified_value_from_hex.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_import_unqualified_value_from_hex.snap new file mode 100644 index 000000000..ef6cdb404 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_import_unqualified_value_from_hex.snap @@ -0,0 +1,17 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nimport example_module.{my_num}\nfn main() {\n my_num\n}\n" +--- +import example_module.{my_num} + ▔▔▔↑▔▔ +fn main() { + my_num +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nInt\n```\n Exciting documentation\n Maybe even multiple lines\n\nView on [HexDocs](https://hexdocs.pm/hex/example_module.html#my_num)", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_imported_function.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_imported_function.snap index 9aa12cd1e..71a2bc797 100644 --- a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_imported_function.snap +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_imported_function.snap @@ -1,23 +1,17 @@ --- source: compiler-core/src/language_server/tests/hover.rs -expression: hover +expression: "\nimport example_module\nfn main() {\n example_module.my_fn\n}\n" --- -Hover { - contents: Scalar( - String( - "```gleam\nfn() -> Nil\n```\n", - ), - ), - range: Some( - Range { - start: Position { - line: 3, - character: 16, - }, - end: Position { - line: 3, - character: 22, - }, - }, - ), +import example_module +fn main() { + example_module.my_fn + ▔▔▔↑▔▔ } + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn() -> Nil\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function.snap new file mode 100644 index 000000000..25e4e2465 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function.snap @@ -0,0 +1,20 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn my_fn() {\n Nil\n}\n\nfn main() {\n my_fn\n}\n" +--- +fn my_fn() { + Nil +} + +fn main() { + my_fn + ▔↑▔▔▔ +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn() -> Nil\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe.snap new file mode 100644 index 000000000..182db3c36 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe.snap @@ -0,0 +1,25 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn add1(num: Int) -> Int {\n num + 1\n}\n\npub fn main() {\n add1(1)\n\n 1\n |> add1\n |> add1\n |> add1\n}\n" +--- +fn add1(num: Int) -> Int { + num + 1 +} + +pub fn main() { + add1(1) + ▔↑▔▔ + + 1 + |> add1 + |> add1 + |> add1 +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn(Int) -> Int\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe_1.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe_1.snap new file mode 100644 index 000000000..05ba4d74d --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe_1.snap @@ -0,0 +1,25 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn add1(num: Int) -> Int {\n num + 1\n}\n\npub fn main() {\n add1(1)\n\n 1\n |> add1\n |> add1\n |> add1\n}\n" +--- +fn add1(num: Int) -> Int { + num + 1 +} + +pub fn main() { + add1(1) + + 1 + |> add1 + ▔▔↑▔ + |> add1 + |> add1 +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn(Int) -> Int\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe_2.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe_2.snap new file mode 100644 index 000000000..3d2c282a9 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe_2.snap @@ -0,0 +1,25 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn add1(num: Int) -> Int {\n num + 1\n}\n\npub fn main() {\n add1(1)\n\n 1\n |> add1\n |> add1\n |> add1\n}\n" +--- +fn add1(num: Int) -> Int { + num + 1 +} + +pub fn main() { + add1(1) + + 1 + |> add1 + |> add1 + ▔▔↑▔ + |> add1 +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn(Int) -> Int\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe_3.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe_3.snap new file mode 100644 index 000000000..72b1f8b19 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_local_function_in_pipe_3.snap @@ -0,0 +1,25 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn add1(num: Int) -> Int {\n num + 1\n}\n\npub fn main() {\n add1(1)\n\n 1\n |> add1\n |> add1\n |> add1\n}\n" +--- +fn add1(num: Int) -> Int { + num + 1 +} + +pub fn main() { + add1(1) + + 1 + |> add1 + |> add1 + |> add1 + ▔▔↑▔ +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn(Int) -> Int\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_module_constant.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_module_constant.snap new file mode 100644 index 000000000..d3fb1a1d4 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_module_constant.snap @@ -0,0 +1,16 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\nconst one = 1\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +const one = 1 + ↑▔▔ + + +----- Hover content ----- +Scalar( + String( + "```gleam\nInt\n```\n Exciting documentation\n Maybe even multiple lines\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_module_constant_annotation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_module_constant_annotation.snap new file mode 100644 index 000000000..2815fec64 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_module_constant_annotation.snap @@ -0,0 +1,16 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\nconst one: Int = 1\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +const one: Int = 1 + ▔▔↑ + + +----- Hover content ----- +Scalar( + String( + "```gleam\nInt\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_type_alias_annotation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_type_alias_annotation.snap new file mode 100644 index 000000000..53f44f6f9 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_type_alias_annotation.snap @@ -0,0 +1,14 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\ntype Wibble = Int\n" +--- +type Wibble = Int + ▔↑▔ + + +----- Hover content ----- +Scalar( + String( + "```gleam\nInt\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_type_constructor_annotation.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_type_constructor_annotation.snap new file mode 100644 index 000000000..1bbf0facc --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_type_constructor_annotation.snap @@ -0,0 +1,16 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\ntype Wibble {\n Wibble(arg: String)\n}\n" +--- +type Wibble { + Wibble(arg: String) + ▔▔▔▔↑▔ +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nString\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_variable_in_use_expression.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_variable_in_use_expression.snap new file mode 100644 index 000000000..c2f3f1b79 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_variable_in_use_expression.snap @@ -0,0 +1,23 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn b(fun: fn(Int) -> String) {\n fun(42)\n}\n\nfn do_stuff() {\n let c = \"done\"\n\n use a <- b\n c\n}\n" +--- +fn b(fun: fn(Int) -> String) { + fun(42) +} + +fn do_stuff() { + let c = "done" + + use a <- b + ↑ + c +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nInt\n```", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_variable_in_use_expression_1.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_variable_in_use_expression_1.snap new file mode 100644 index 000000000..c8fb42970 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_variable_in_use_expression_1.snap @@ -0,0 +1,23 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn b(fun: fn(Int) -> String) {\n fun(42)\n}\n\nfn do_stuff() {\n let c = \"done\"\n\n use a <- b\n c\n}\n" +--- +fn b(fun: fn(Int) -> String) { + fun(42) +} + +fn do_stuff() { + let c = "done" + + use a <- b + ↑ + c +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn(fn(Int) -> String) -> String\n```\n", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_variable_in_use_expression_2.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_variable_in_use_expression_2.snap new file mode 100644 index 000000000..91cd2a9a2 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_variable_in_use_expression_2.snap @@ -0,0 +1,23 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn b(fun: fn(Int) -> String) {\n fun(42)\n}\n\nfn do_stuff() {\n let c = \"done\"\n\n use a <- b\n c\n}\n" +--- +fn b(fun: fn(Int) -> String) { + fun(42) +} + +fn do_stuff() { + let c = "done" + + use a <- b + c + ↑ +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nString\n```\nA locally defined variable.", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_works_even_for_invalid_code.snap b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_works_even_for_invalid_code.snap new file mode 100644 index 000000000..1c3d75a96 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/glistix_core__language_server__tests__hover__hover_works_even_for_invalid_code.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\nfn invalid() { 1 + Nil }\nfn valid() { Nil }\n" +--- +fn invalid() { 1 + Nil } +fn valid() { Nil } +▔▔▔↑▔▔▔▔▔▔ + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn() -> Nil\n```\n", + ), +) diff --git a/compiler-core/src/metadata/module_decoder.rs b/compiler-core/src/metadata/module_decoder.rs index 99d8d6842..ab76ff026 100755 --- a/compiler-core/src/metadata/module_decoder.rs +++ b/compiler-core/src/metadata/module_decoder.rs @@ -19,7 +19,7 @@ use crate::{ uid::UniqueIdGenerator, Result, }; -use std::{collections::HashMap, io::BufRead, sync::Arc, u64}; +use std::{collections::HashMap, io::BufRead, sync::Arc}; macro_rules! read_vec { ($reader:expr, $self:expr, $method:ident) => {{ diff --git a/compiler-core/src/metadata/module_encoder.rs b/compiler-core/src/metadata/module_encoder.rs index 268eff49c..2222077bc 100644 --- a/compiler-core/src/metadata/module_encoder.rs +++ b/compiler-core/src/metadata/module_encoder.rs @@ -211,7 +211,7 @@ impl<'a> ModuleEncoder<'a> { builder: type_value_constructor_parameter::Builder<'_>, parameter: &type_::TypeValueConstructorField, ) { - self.build_type(builder.init_type(), parameter.type_.as_ref()) + self.build_type(builder.init_type(), parameter.type_.as_ref()); } fn build_value_constructor( diff --git a/compiler-core/src/nix/expression.rs b/compiler-core/src/nix/expression.rs index 98854975f..bc1dc3757 100644 --- a/compiler-core/src/nix/expression.rs +++ b/compiler-core/src/nix/expression.rs @@ -165,6 +165,10 @@ impl<'module> Generator<'module> { } => self.case(subjects, clauses), TypedExpr::BitArray { segments, .. } => self.bit_array(segments), + + TypedExpr::Invalid { .. } => { + panic!("invalid expressions should not reach code generation") + } } } diff --git a/compiler-core/src/nix/pattern.rs b/compiler-core/src/nix/pattern.rs index a205ff486..efd0d95dd 100644 --- a/compiler-core/src/nix/pattern.rs +++ b/compiler-core/src/nix/pattern.rs @@ -269,6 +269,15 @@ impl<'module_ctx, 'expression_gen, 'a> Generator<'module_ctx, 'expression_gen, ' | ClauseGuard::GtEqFloat { .. } | ClauseGuard::LtFloat { .. } | ClauseGuard::LtEqFloat { .. } + | ClauseGuard::AddInt { .. } + | ClauseGuard::AddFloat { .. } + | ClauseGuard::SubInt { .. } + | ClauseGuard::SubFloat { .. } + | ClauseGuard::MultInt { .. } + | ClauseGuard::MultFloat { .. } + | ClauseGuard::DivInt { .. } + | ClauseGuard::DivFloat { .. } + | ClauseGuard::RemainderInt { .. } | ClauseGuard::Or { .. } | ClauseGuard::And { .. } | ClauseGuard::TupleIndex { .. } => Ok(docvec!("(", self.guard(guard)?, ")")), @@ -315,6 +324,46 @@ impl<'module_ctx, 'expression_gen, 'a> Generator<'module_ctx, 'expression_gen, ' docvec!(left, " <= ", right) } + ClauseGuard::AddFloat { left, right, .. } | ClauseGuard::AddInt { left, right, .. } => { + let left = self.wrapped_guard(left)?; + let right = self.wrapped_guard(right)?; + docvec!(left, " + ", right) + } + + ClauseGuard::SubFloat { left, right, .. } | ClauseGuard::SubInt { left, right, .. } => { + let left = self.wrapped_guard(left)?; + let right = self.wrapped_guard(right)?; + docvec!(left, " - ", right) + } + + ClauseGuard::MultFloat { left, right, .. } + | ClauseGuard::MultInt { left, right, .. } => { + let left = self.wrapped_guard(left)?; + let right = self.wrapped_guard(right)?; + docvec!(left, " * ", right) + } + + ClauseGuard::DivFloat { left, right, .. } => { + let left = self.wrapped_guard(left)?; + let right = self.wrapped_guard(right)?; + self.expression_generator.tracker.float_division_used = true; + syntax::fn_call("divideFloat".to_doc(), [left, right]) + } + + ClauseGuard::DivInt { left, right, .. } => { + let left = self.wrapped_guard(left)?; + let right = self.wrapped_guard(right)?; + self.expression_generator.tracker.int_division_used = true; + syntax::fn_call("divideInt".to_doc(), [left, right]) + } + + ClauseGuard::RemainderInt { left, right, .. } => { + let left = self.wrapped_guard(left)?; + let right = self.wrapped_guard(right)?; + self.expression_generator.tracker.int_remainder_used = true; + syntax::fn_call("remainderInt".to_doc(), [left, right]) + } + ClauseGuard::Or { left, right, .. } => { let left = self.wrapped_guard(left)?; let right = self.wrapped_guard(right)?; @@ -497,13 +546,13 @@ impl<'module_ctx, 'expression_gen, 'a> Generator<'module_ctx, 'expression_gen, ' self.pop_segment(); } if let Some((left, _)) = left_side_assignment { - // "foo" as prefix <> rest - // ^^^^^^^^^ In case the left prefix of the pattern matching is given an - // alias we bind it to a local variable so that it can be - // correctly referenced inside the case branch. - // let prefix = "foo"; - // ^^^^^^^^^^^^^^^^^^^ we're adding this assignment inside the if clause - // the case branch gets translated into. + // "wibble" as prefix <> rest + // ^^^^^^^^^ In case the left prefix of the pattern matching is given an + // alias we bind it to a local variable so that it can be + // correctly referenced inside the case branch. + // let prefix = "wibble"; + // ^^^^^^^^^^^^^^^^^^^^^ we're adding this assignment inside the if clause + // the case branch gets translated into. let left_side_string = expression::string(left_side_string, self.expression_generator.tracker); self.push_assignment(left_side_string, left); @@ -653,6 +702,7 @@ impl<'module_ctx, 'expression_gen, 'a> Generator<'module_ctx, 'expression_gen, ' feature: "Bit array matching".into(), location: *location, }), + Pattern::Invalid { .. } => panic!("invalid patterns should not reach code generation"), } } diff --git a/compiler-core/src/nix/syntax.rs b/compiler-core/src/nix/syntax.rs index ddcb01e97..252a43c35 100644 --- a/compiler-core/src/nix/syntax.rs +++ b/compiler-core/src/nix/syntax.rs @@ -16,6 +16,7 @@ use std::sync::OnceLock; /// 2. Those starting with `./` (relative paths). /// 3. Those starting with `~/` (user home paths). /// 4. Those surrounded by `<...>` (Nix store paths - don't support interpolation with ${...}). +/// /// Anything not in the four categories above is converted to a relative path. pub fn path(value: &str) -> Cow<'_, str> { // TODO: Consider introducing fallibility somewhere here. diff --git a/compiler-core/src/nix/tests.rs b/compiler-core/src/nix/tests.rs index a2b4baa22..9f7103694 100644 --- a/compiler-core/src/nix/tests.rs +++ b/compiler-core/src/nix/tests.rs @@ -6,9 +6,9 @@ use crate::{ build::{Origin, Target}, nix::*, uid::UniqueIdGenerator, - warning::TypeWarningEmitter, + warning::{TypeWarningEmitter, WarningEmitter}, }; -use camino::Utf8Path; +use camino::{Utf8Path, Utf8PathBuf}; mod assignments; mod basic; @@ -86,7 +86,12 @@ pub fn compile(src: &str, deps: Vec<(&str, &str, &str)>) -> TypedModule { deps.iter().for_each(|(dep_package, dep_name, dep_src)| { let mut dep_config = PackageConfig::default(); dep_config.name = (*dep_package).into(); - let parsed = crate::parse::parse_module(dep_src).expect("dep syntax error"); + let parsed = crate::parse::parse_module( + Utf8PathBuf::from("test/path"), + dep_src, + &WarningEmitter::null(), + ) + .expect("dep syntax error"); let mut ast = parsed.module; ast.name = (*dep_name).into(); let line_numbers = LineNumbers::new(dep_src); @@ -107,7 +112,9 @@ pub fn compile(src: &str, deps: Vec<(&str, &str, &str)>) -> TypedModule { let _ = direct_dependencies.insert((*dep_package).into(), ()); }); - let parsed = crate::parse::parse_module(src).expect("syntax error"); + let parsed = + crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) + .expect("syntax error"); let mut ast = parsed.module; ast.name = "my/mod".into(); let line_numbers = LineNumbers::new(src); diff --git a/compiler-core/src/nix/tests/assignments.rs b/compiler-core/src/nix/tests/assignments.rs index a6eb5cebc..2588158bf 100644 --- a/compiler-core/src/nix/tests/assignments.rs +++ b/compiler-core/src/nix/tests/assignments.rs @@ -37,19 +37,19 @@ fn variable_renaming() { assert_nix!( r#" -fn go(x, foo) { +fn go(x, wibble) { let a = 1 - foo(a) + wibble(a) let a = 2 - foo(a) + wibble(a) let assert #(a, 3) = x let b = a - foo(b) + wibble(b) let c = { let a = a #(a, b) } - foo(a) + wibble(a) // make sure arguments are counted in initial state let x = c x @@ -97,10 +97,10 @@ fn rebound_argument() { #[test] fn rebound_function() { assert_nix!( - r#"pub fn x() { + r#"pub fn x() { Nil } - + pub fn main() { let x = False x @@ -112,10 +112,10 @@ pub fn main() { #[test] fn rebound_function_and_arg() { assert_nix!( - r#"pub fn x() { + r#"pub fn x() { Nil } - + pub fn main(x) { let x = False x @@ -158,7 +158,7 @@ fn module_const_var() { assert_nix!( r#" pub const int = 42 -pub const int_alias = int +pub const int_alias = int pub fn use_int_alias() { int_alias } pub const compound: #(Int, Int) = #(int, int_alias) diff --git a/compiler-core/src/nix/tests/basic.rs b/compiler-core/src/nix/tests/basic.rs index 775db40c3..f47978ac4 100644 --- a/compiler-core/src/nix/tests/basic.rs +++ b/compiler-core/src/nix/tests/basic.rs @@ -17,11 +17,11 @@ pub fn sus(x: Int) -> Int { 5 + 5 } -fn go(x, foo, boolthing, bmong) { +fn go(x, wibble, boolthing, bmong) { let a = 1 - foo + wibble let a = 2 - let z = [fn(y: Int) { y }, foo(a, a, 50), fn(x: Int) { x }] + let z = [fn(y: Int) { y }, wibble(a, a, 50), fn(x: Int) { x }] let d = { "aaa" } @@ -33,19 +33,19 @@ fn go(x, foo, boolthing, bmong) { let remm = 5 % 5 let g = 0.5 let b = a - foo(a, a, 50) + wibble(a, a, 50) let c = { let a = a [a] } - foo + wibble let www = a == 5 let wwww = {a + a} == {10 - 5 * 5} let first_list = [1 < 4, 3 >= 5] let list_thing = [www, ..first_list] let pipes = a - |> foo(_, a, 50) + |> wibble(_, a, 50) |> fn(x) { x } let pipes2 = a |> fn(i: Int) { i } panic as "amongus" diff --git a/compiler-core/src/nix/tests/case_clause_guards.rs b/compiler-core/src/nix/tests/case_clause_guards.rs index fb4f614ee..846dda7a8 100644 --- a/compiler-core/src/nix/tests/case_clause_guards.rs +++ b/compiler-core/src/nix/tests/case_clause_guards.rs @@ -116,6 +116,89 @@ fn not_eq_scalar() { ); } +#[test] +fn addition() { + assert_nix!( + r#"pub fn main(x, y) { + case 1 { + _ if 1 + x == 2 -> 0 + _ if 1.0 +. y == 2.0 -> 0 + _ -> 1 + } +} +"#, + ); +} + +#[test] +fn subtraction() { + assert_nix!( + r#"pub fn main(x, y) { + case 1 { + _ if 1 - x == 2 -> 0 + _ if 1.0 -. y == 2.0 -> 0 + _ -> 1 + } +} +"#, + ); +} + +#[test] +fn division() { + assert_nix!( + r#"pub fn main(x, y) { + case 1 { + _ if 1 / x == 2 -> 0 + _ if 1.0 /. y == 2.0 -> 0 + _ -> 1 + } +} +"#, + ); +} + +#[test] +fn multiplication() { + assert_nix!( + r#"pub fn main(x, y) { + case 1 { + _ if 1 * x == 2 -> 0 + _ if 1.0 *. y == 2.0 -> 0 + _ -> 1 + } +} +"#, + ); +} + +#[test] +fn remainder() { + assert_nix!( + r#"pub fn main(x) { + case 1 { + _ if 1 % x == 2 -> 0 + _ -> 1 + } +} +"#, + ); +} + +#[test] +fn combined_arithmetic() { + assert_nix!( + r#"pub fn main(x, y) { + case 1 { + _ if 10 / 2 + 5 % 3 - 1 * x == 2 -> 0 + _ if 10.5 /. 2.5 +. 5.2 -. 1.3 *. y == 2.0 -> 0 + _ -> 1 + } +} +"#, + ); +} + #[test] fn tuple_index() { assert_nix!( diff --git a/compiler-core/src/nix/tests/functions.rs b/compiler-core/src/nix/tests/functions.rs index 0135ab5af..7a652bb97 100644 --- a/compiler-core/src/nix/tests/functions.rs +++ b/compiler-core/src/nix/tests/functions.rs @@ -339,9 +339,9 @@ pub fn version(n) { #[test] fn pipe_shadow_import() { assert_nix!( - (CURRENT_PACKAGE, "foo", "pub fn println(x: String) { }"), + (CURRENT_PACKAGE, "wibble", "pub fn println(x: String) { }"), r#" - import foo.{println} + import wibble.{println} pub fn main() { let println = "oh dear" diff --git a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__assignments__variable_renaming.snap b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__assignments__variable_renaming.snap index a7108c64f..7526402ba 100644 --- a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__assignments__variable_renaming.snap +++ b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__assignments__variable_renaming.snap @@ -1,17 +1,17 @@ --- source: compiler-core/src/nix/tests/assignments.rs -expression: "\n\nfn go(x, foo) {\n let a = 1\n foo(a)\n let a = 2\n foo(a)\n let assert #(a, 3) = x\n let b = a\n foo(b)\n let c = {\n let a = a\n #(a, b)\n }\n foo(a)\n // make sure arguments are counted in initial state\n let x = c\n x\n}\n" +expression: "\n\nfn go(x, wibble) {\n let a = 1\n wibble(a)\n let a = 2\n wibble(a)\n let assert #(a, 3) = x\n let b = a\n wibble(b)\n let c = {\n let a = a\n #(a, b)\n }\n wibble(a)\n // make sure arguments are counted in initial state\n let x = c\n x\n}\n" --- let inherit (builtins.import ./../gleam.nix) makeError seqAll; go = - x: foo: + x: wibble: let a = 1; - _' = foo a; + _' = wibble a; a'1 = 2; - _'1 = foo a'1; + _'1 = wibble a'1; _assert' = if (builtins.elemAt x 1) != 3 then builtins.throw @@ -25,9 +25,9 @@ let else null; a'2 = builtins.seq _assert' (builtins.elemAt x 0); b = a'2; - _'2 = foo b; + _'2 = wibble b; c = let a'3 = a'2; in [ a'3 b ]; - _'3 = foo a'2; + _'3 = wibble a'2; x'1 = c; in seqAll [ _' _'1 _assert' _'2 _'3 ] x'1; diff --git a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__basic__basic_test.snap b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__basic__basic_test.snap index 8a5064a0b..840c520d5 100644 --- a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__basic__basic_test.snap +++ b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__basic__basic_test.snap @@ -1,6 +1,6 @@ --- source: compiler-core/src/nix/tests/basic.rs -expression: "\npub type Amogus {\n AmogusA\n AmogusB(a: Int, b: Int)\n AmogusC(Int, Float)\n}\n\npub const sumongus: Int = 5\n\npub fn sus(x: Int) -> Int {\n 5 + 5\n}\n\nfn go(x, foo, boolthing, bmong) {\n let a = 1\n foo\n let a = 2\n let z = [fn(y: Int) { y }, foo(a, a, 50), fn(x: Int) { x }]\n let d = {\n \"aaa\"\n }\n let dd = {\n let amogus = 5.5e5\n }\n let intdiv = 5 / 5\n let floatdiv = 5.0 /. 5.0\n let remm = 5 % 5\n let g = 0.5\n let b = a\n foo(a, a, 50)\n let c = {\n let a = a\n [a]\n }\n foo\n let www = a == 5\n let wwww = {a + a} == {10 - 5 * 5}\n let first_list = [1 < 4, 3 >= 5]\n let list_thing = [www, ..first_list]\n let pipes =\n a\n |> foo(_, a, 50)\n |> fn(x) { x }\n let pipes2 = a |> fn(i: Int) { i }\n panic as \"amongus\"\n todo as \"amongus\"\n panic\n todo\n // let mongus: Amogus = bmong\n // let mongus = bmong.a\n // let mongus = Amogus(..bmong, a: 6)\n let y = -500 + 10 - {-a} - {-5}\n let yy = [ -a, -5, -y ]\n let f = -5.5 -. 5.2e-5\n let f = 5.5 -. 5.2e5\n // TODO: fix string escaping\n let ss = \"a\" <> \"b\" <> \"c d\"\n let ff = [ -5.2e-5, -5.5 ]\n let z = !boolthing\n let tupdatup = #(1, 2, \"a\", -5.5, #(1, 2, 3))\n let tata = tupdatup.1 + tupdatup.0 + 20\n let mabool = True\n let mnotbool = False\n let amagus_a = AmogusA\n let amagus_b = AmogusB(10, b: 15)\n let amagus_c = AmogusC(10, 5.5)\n let simplefunc = fn() { 5 }\n let gg = simplefunc() + 10\n let less_simple_func = fn(x) { fn() { x } }\n let gg = less_simple_func(5)() + 10\n let mnull = Nil\n let sus = \"hey! I'm still sus :(\"\n // make sure arguments are counted in initial state\n let x = tata\n x\n}\n\nfn mongus(a, b, c) {\n let x = fn() { 5 + 5 }\n let y = x()\n #(1, 2, 3, fn(x: Int) { x + 1 }, { 5 6 10 {10 + 5} })\n}\n" +expression: "\npub type Amogus {\n AmogusA\n AmogusB(a: Int, b: Int)\n AmogusC(Int, Float)\n}\n\npub const sumongus: Int = 5\n\npub fn sus(x: Int) -> Int {\n 5 + 5\n}\n\nfn go(x, wibble, boolthing, bmong) {\n let a = 1\n wibble\n let a = 2\n let z = [fn(y: Int) { y }, wibble(a, a, 50), fn(x: Int) { x }]\n let d = {\n \"aaa\"\n }\n let dd = {\n let amogus = 5.5e5\n }\n let intdiv = 5 / 5\n let floatdiv = 5.0 /. 5.0\n let remm = 5 % 5\n let g = 0.5\n let b = a\n wibble(a, a, 50)\n let c = {\n let a = a\n [a]\n }\n wibble\n let www = a == 5\n let wwww = {a + a} == {10 - 5 * 5}\n let first_list = [1 < 4, 3 >= 5]\n let list_thing = [www, ..first_list]\n let pipes =\n a\n |> wibble(_, a, 50)\n |> fn(x) { x }\n let pipes2 = a |> fn(i: Int) { i }\n panic as \"amongus\"\n todo as \"amongus\"\n panic\n todo\n // let mongus: Amogus = bmong\n // let mongus = bmong.a\n // let mongus = Amogus(..bmong, a: 6)\n let y = -500 + 10 - {-a} - {-5}\n let yy = [ -a, -5, -y ]\n let f = -5.5 -. 5.2e-5\n let f = 5.5 -. 5.2e5\n // TODO: fix string escaping\n let ss = \"a\" <> \"b\" <> \"c d\"\n let ff = [ -5.2e-5, -5.5 ]\n let z = !boolthing\n let tupdatup = #(1, 2, \"a\", -5.5, #(1, 2, 3))\n let tata = tupdatup.1 + tupdatup.0 + 20\n let mabool = True\n let mnotbool = False\n let amagus_a = AmogusA\n let amagus_b = AmogusB(10, b: 15)\n let amagus_c = AmogusC(10, 5.5)\n let simplefunc = fn() { 5 }\n let gg = simplefunc() + 10\n let less_simple_func = fn(x) { fn() { x } }\n let gg = less_simple_func(5)() + 10\n let mnull = Nil\n let sus = \"hey! I'm still sus :(\"\n // make sure arguments are counted in initial state\n let x = tata\n x\n}\n\nfn mongus(a, b, c) {\n let x = fn() { 5 + 5 }\n let y = x()\n #(1, 2, 3, fn(x: Int) { x + 1 }, { 5 6 10 {10 + 5} })\n}\n" --- let inherit @@ -23,12 +23,12 @@ let sus = x: 5 + 5; go = - x: foo: boolthing: bmong: + x: wibble: boolthing: bmong: let a = 1; - _' = foo; + _' = wibble; a'1 = 2; - z = toList [ (y: y) (foo a'1 a'1 50) (x: x) ]; + z = toList [ (y: y) (wibble a'1 a'1 50) (x: x) ]; d = "aaa"; dd = 5.5e5; intdiv = divideInt 5 5; @@ -36,9 +36,9 @@ let remm = remainderInt 5 5; g = 0.5; b = a'1; - _'1 = foo a'1 a'1 50; + _'1 = wibble a'1 a'1 50; c = let a'2 = a'1; in toList [ a'2 ]; - _'2 = foo; + _'2 = wibble; www = a'1 == 5; wwww = (a'1 + a'1) == (10 - (5 * 5)); first_list = toList [ (1 < 4) (3 >= 5) ]; @@ -46,7 +46,7 @@ let pipes = let _pipe = a'1; - _pipe'1 = (_capture: foo _capture a'1 50) _pipe; + _pipe'1 = (_capture: wibble _capture a'1 50) _pipe; in (x: x) _pipe'1; pipes2 = let _pipe = a'1; in (i: i) _pipe; diff --git a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__addition.snap b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__addition.snap new file mode 100644 index 000000000..38fa1833c --- /dev/null +++ b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__addition.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/nix/tests/case_clause_guards.rs +expression: "pub fn main(x, y) {\n case 1 {\n _ if 1 + x == 2 -> 0\n _ if 1.0 +. y == 2.0 -> 0\n _ -> 1\n }\n}\n" +--- +let + main = + x: y: + let + _pat' = 1; + in + if (1 + x) == 2 then 0 + else if (1.0 + y) == 2.0 then 0 + else 1; +in +{ inherit main; } diff --git a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__combined_arithmetic.snap b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__combined_arithmetic.snap new file mode 100644 index 000000000..75f4413e8 --- /dev/null +++ b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__combined_arithmetic.snap @@ -0,0 +1,17 @@ +--- +source: compiler-core/src/nix/tests/case_clause_guards.rs +expression: "pub fn main(x, y) {\n case 1 {\n _ if 10 / 2 + 5 % 3 - 1 * x == 2 -> 0\n _ if 10.5 /. 2.5 +. 5.2 -. 1.3 *. y == 2.0 -> 0\n _ -> 1\n }\n}\n" +--- +let + inherit (builtins.import ./../gleam.nix) remainderInt divideFloat divideInt; + + main = + x: y: + let + _pat' = 1; + in + if (((divideInt 10 2) + (remainderInt 5 3)) - (1 * x)) == 2 then 0 + else if (((divideFloat 10.5 2.5) + 5.2) - (1.3 * y)) == 2.0 then 0 + else 1; +in +{ inherit main; } diff --git a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__division.snap b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__division.snap new file mode 100644 index 000000000..d8f0d1f32 --- /dev/null +++ b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__division.snap @@ -0,0 +1,17 @@ +--- +source: compiler-core/src/nix/tests/case_clause_guards.rs +expression: "pub fn main(x, y) {\n case 1 {\n _ if 1 / x == 2 -> 0\n _ if 1.0 /. y == 2.0 -> 0\n _ -> 1\n }\n}\n" +--- +let + inherit (builtins.import ./../gleam.nix) divideFloat divideInt; + + main = + x: y: + let + _pat' = 1; + in + if (divideInt 1 x) == 2 then 0 + else if (divideFloat 1.0 y) == 2.0 then 0 + else 1; +in +{ inherit main; } diff --git a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__multiplication.snap b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__multiplication.snap new file mode 100644 index 000000000..e9e79a62e --- /dev/null +++ b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__multiplication.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/nix/tests/case_clause_guards.rs +expression: "pub fn main(x, y) {\n case 1 {\n _ if 1 * x == 2 -> 0\n _ if 1.0 *. y == 2.0 -> 0\n _ -> 1\n }\n}\n" +--- +let + main = + x: y: + let + _pat' = 1; + in + if (1 * x) == 2 then 0 + else if (1.0 * y) == 2.0 then 0 + else 1; +in +{ inherit main; } diff --git a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__remainder.snap b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__remainder.snap new file mode 100644 index 000000000..4f43712ef --- /dev/null +++ b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__remainder.snap @@ -0,0 +1,10 @@ +--- +source: compiler-core/src/nix/tests/case_clause_guards.rs +expression: "pub fn main(x) {\n case 1 {\n _ if 1 % x == 2 -> 0\n _ -> 1\n }\n}\n" +--- +let + inherit (builtins.import ./../gleam.nix) remainderInt; + + main = x: let _pat' = 1; in if (remainderInt 1 x) == 2 then 0 else 1; +in +{ inherit main; } diff --git a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__subtraction.snap b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__subtraction.snap new file mode 100644 index 000000000..7ec590ef3 --- /dev/null +++ b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__case_clause_guards__subtraction.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/nix/tests/case_clause_guards.rs +expression: "pub fn main(x, y) {\n case 1 {\n _ if 1 - x == 2 -> 0\n _ if 1.0 -. y == 2.0 -> 0\n _ -> 1\n }\n}\n" +--- +let + main = + x: y: + let + _pat' = 1; + in + if (1 - x) == 2 then 0 + else if (1.0 - y) == 2.0 then 0 + else 1; +in +{ inherit main; } diff --git a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__functions__pipe_shadow_import.snap b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__functions__pipe_shadow_import.snap index fd371c0d9..878ba1cf8 100644 --- a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__functions__pipe_shadow_import.snap +++ b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__functions__pipe_shadow_import.snap @@ -1,10 +1,10 @@ --- source: compiler-core/src/nix/tests/functions.rs -expression: "\n import foo.{println}\n pub fn main() {\n let println =\n \"oh dear\"\n |> println\n println\n }" +expression: "\n import wibble.{println}\n pub fn main() {\n let println =\n \"oh dear\"\n |> println\n println\n }" --- let - foo' = builtins.import ./../foo.nix; - inherit (builtins.import ./../foo.nix) println; + wibble' = builtins.import ./../wibble.nix; + inherit (builtins.import ./../wibble.nix) println; main = { }: let println'1 = let _pipe = "oh dear"; in println _pipe; in println'1; diff --git a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__strings__string_prefix_utf8.snap b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__strings__string_prefix_utf8.snap index 7575c65fa..caf88ae62 100644 --- a/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__strings__string_prefix_utf8.snap +++ b/compiler-core/src/nix/tests/snapshots/glistix_core__nix__tests__strings__string_prefix_utf8.snap @@ -1,6 +1,6 @@ --- source: compiler-core/src/nix/tests/strings.rs -expression: "\npub fn go(x) {\n case \"Θ foo bar\" {\n \"Θ\" <> rest -> rest\n _ -> \"\"\n }\n case \"🫥 is neutral dotted\" {\n \"🫥\" <> rest -> rest\n _ -> \"\"\n }\n case \"🇺🇸 is a cluster\" {\n \"🇺🇸\" <> rest -> rest\n _ -> \"\"\n }\n case \"\\\" is a an escaped quote\" {\n \"\\\"\" <> rest -> rest\n _ -> \"\"\n }\n case \"\\\\ is a an escaped backslash\" {\n \"\\\\\" <> rest -> rest\n _ -> \"\"\n }\n}\n" +expression: "\npub fn go(x) {\n case \"Θ wibble wobble\" {\n \"Θ\" <> rest -> rest\n _ -> \"\"\n }\n case \"🫥 is neutral dotted\" {\n \"🫥\" <> rest -> rest\n _ -> \"\"\n }\n case \"🇺🇸 is a cluster\" {\n \"🇺🇸\" <> rest -> rest\n _ -> \"\"\n }\n case \"\\\" is a an escaped quote\" {\n \"\\\"\" <> rest -> rest\n _ -> \"\"\n }\n case \"\\\\ is a an escaped backslash\" {\n \"\\\\\" <> rest -> rest\n _ -> \"\"\n }\n}\n" --- let inherit (builtins.import ./../gleam.nix) strHasPrefix seqAll; @@ -10,7 +10,7 @@ let let _' = let - _pat' = "Θ foo bar"; + _pat' = "Θ wibble wobble"; in if strHasPrefix "Θ" _pat' then let diff --git a/compiler-core/src/nix/tests/strings.rs b/compiler-core/src/nix/tests/strings.rs index d1fd121cd..8a46ecd08 100644 --- a/compiler-core/src/nix/tests/strings.rs +++ b/compiler-core/src/nix/tests/strings.rs @@ -125,7 +125,7 @@ fn string_prefix_utf8() { assert_nix!( r#" pub fn go(x) { - case "Θ foo bar" { + case "Θ wibble wobble" { "Θ" <> rest -> rest _ -> "" } diff --git a/compiler-core/src/package_interface.rs b/compiler-core/src/package_interface.rs index 4c69b6ffb..dcf5c5165 100644 --- a/compiler-core/src/package_interface.rs +++ b/compiler-core/src/package_interface.rs @@ -162,7 +162,7 @@ pub struct ImplementationsInterface { /// Consider the following function: /// /// ```gleam - /// @external(erlang, "foo", "bar") + /// @external(erlang, "wibble", "wobble") /// pub fn a_random_number() -> Int { /// 4 /// // This is a default implementation. @@ -205,7 +205,7 @@ pub struct ImplementationsInterface { /// Let's have a look at an example: /// /// ```gleam - /// @external(javascript, "foo", "bar") + /// @external(javascript, "wibble", "wobble") /// pub fn javascript_only() -> Int /// ``` /// @@ -319,8 +319,8 @@ pub enum TypeInterface { }, /// A type variable. /// ```gleam - /// pub fn foo(value: a) -> a {} - /// // ^ This is a type variable. + /// pub fn wibble(value: a) -> a {} + /// // ^ This is a type variable. /// ``` Variable { id: u64 }, /// A custom named type. @@ -625,7 +625,7 @@ fn from_type_helper(type_: &Type, id_map: &mut IdMap) -> TypeInterface { /// After type inference the ids associated with type variables can be quite /// high and are not the best to produce a human/machine readable output. /// -/// Imagine a function like this one: `pub fn foo(item: a, rest: b) -> c` +/// Imagine a function like this one: `pub fn wibble(item: a, rest: b) -> c` /// What we want here is for type variables to have increasing ids starting from /// 0: `a` with id `0`, `b` with id `1` and `c` with id `2`. /// diff --git a/compiler-core/src/package_interface/snapshots/glistix_core__package_interface__tests__type_definition.snap b/compiler-core/src/package_interface/snapshots/glistix_core__package_interface__tests__type_definition.snap index 94b37e8e3..dc114d484 100644 --- a/compiler-core/src/package_interface/snapshots/glistix_core__package_interface__tests__type_definition.snap +++ b/compiler-core/src/package_interface/snapshots/glistix_core__package_interface__tests__type_definition.snap @@ -1,6 +1,6 @@ --- source: compiler-core/src/package_interface/tests.rs -expression: "\n/// Wibble's documentation\npub type Wibble(a, b) {\n Wob\n Baz\n}\n" +expression: "\n/// Wibble's documentation\npub type Wibble(a, b) {\n Wibble\n Wobble\n}\n" --- { "name": "my_package", @@ -18,12 +18,12 @@ expression: "\n/// Wibble's documentation\npub type Wibble(a, b) {\n Wob\n Baz "constructors": [ { "documentation": null, - "name": "Wob", + "name": "Wibble", "parameters": [] }, { "documentation": null, - "name": "Baz", + "name": "Wobble", "parameters": [] } ] diff --git a/compiler-core/src/package_interface/tests.rs b/compiler-core/src/package_interface/tests.rs index edae72e0a..07376ab53 100644 --- a/compiler-core/src/package_interface/tests.rs +++ b/compiler-core/src/package_interface/tests.rs @@ -1,5 +1,6 @@ use std::time::SystemTime; +use camino::Utf8PathBuf; use ecow::EcoString; use globset::GlobBuilder; use hexpm::version::Identifier; @@ -12,7 +13,7 @@ use crate::{ line_numbers::LineNumbers, type_::PRELUDE_MODULE_NAME, uid::UniqueIdGenerator, - warning::TypeWarningEmitter, + warning::{TypeWarningEmitter, WarningEmitter}, }; use super::PackageInterface; @@ -69,7 +70,12 @@ pub fn compile_package( ); let mut direct_dependencies = std::collections::HashMap::from_iter(vec![]); if let Some((dep_package, dep_name, dep_src)) = dep { - let parsed = crate::parse::parse_module(dep_src).expect("dep syntax error"); + let parsed = crate::parse::parse_module( + Utf8PathBuf::from("test/path"), + dep_src, + &WarningEmitter::null(), + ) + .expect("dep syntax error"); let mut ast = parsed.module; ast.name = dep_name.into(); let line_numbers = LineNumbers::new(dep_src); @@ -91,7 +97,9 @@ pub fn compile_package( let _ = modules.insert(dep_name.into(), dep.type_info); let _ = direct_dependencies.insert(dep_package.into(), ()); } - let parsed = crate::parse::parse_module(src).expect("syntax error"); + let parsed = + crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) + .expect("syntax error"); let mut ast = parsed.module; let module_name = module_name @@ -195,8 +203,8 @@ pub fn internal_definitions_are_not_included() { " @internal pub const float = 1.1 @internal pub fn main() {} -@internal pub type Foo -@internal pub type Bar = Int +@internal pub type Wibble +@internal pub type Wobble = Int " ); } @@ -217,8 +225,8 @@ pub fn type_definition() { " /// Wibble's documentation pub type Wibble(a, b) { - Wob - Baz + Wibble + Wobble } " ) diff --git a/compiler-core/src/parse.rs b/compiler-core/src/parse.rs index 573f98fb2..6df9c5ca1 100644 --- a/compiler-core/src/parse.rs +++ b/compiler-core/src/parse.rs @@ -69,6 +69,9 @@ use crate::build::Target; use crate::parse::extra::ModuleExtra; use crate::type_::expression::Implementations; use crate::type_::Deprecation; +use crate::warning::{DeprecatedSyntaxWarning, WarningEmitter}; +use crate::Warning; +use camino::Utf8PathBuf; use ecow::EcoString; use error::{LexicalError, ParseError, ParseErrorType}; use lexer::{LexResult, Spanned}; @@ -130,11 +133,25 @@ impl Attributes { // // Public Interface // -pub fn parse_module(src: &str) -> Result { +pub fn parse_module( + path: Utf8PathBuf, + src: &str, + warnings: &WarningEmitter, +) -> Result { let lex = lexer::make_tokenizer(src); let mut parser = Parser::new(lex); let mut parsed = parser.parse_module()?; parsed.extra = parser.extra; + + let src = EcoString::from(src); + for warning in parser.warnings { + warnings.emit(Warning::DeprecatedSyntax { + path: path.clone(), + src: src.clone(), + warning, + }); + } + Ok(parsed) } @@ -177,6 +194,7 @@ pub fn parse_const_value(src: &str) -> Result, ParseError> { pub struct Parser> { tokens: T, lex_errors: Vec, + warnings: Vec, tok0: Option, tok1: Option, extra: ModuleExtra, @@ -190,6 +208,7 @@ where let mut parser = Parser { tokens: input, lex_errors: vec![], + warnings: vec![], tok0: None, tok1: None, extra: ModuleExtra::new(), @@ -225,11 +244,12 @@ where parse_result: Result, ) -> Result { let parse_result = self.ensure_no_errors(parse_result)?; - if let Some((start, _, end)) = self.next_tok() { + if let Some((start, token, end)) = self.next_tok() { // there are still more tokens let expected = vec!["An import, const, type, or function.".into()]; return parse_error( ParseErrorType::UnexpectedToken { + token, expected, hint: None, }, @@ -498,22 +518,24 @@ where // list Some((start, Token::LeftSquare, _)) => { self.advance(); - let elements = - Parser::series_of(self, &Parser::parse_expression, Some(&Token::Comma))?; + let (elements, elements_end_with_comma) = self.series_of_has_trailing_separator( + &Parser::parse_expression, + Some(&Token::Comma), + )?; // Parse an optional tail let mut tail = None; let mut elements_after_tail = None; let mut dot_dot_location = None; - if let Some(location) = self.maybe_one(&Token::DotDot) { - dot_dot_location = Some(location); - tail = self.parse_expression()?.map(Box::new); + if let Some((start, end)) = self.maybe_one(&Token::DotDot) { + dot_dot_location = Some((start, end)); + tail = self.parse_expression()?.map(Box::new); if self.maybe_one(&Token::Comma).is_some() { // See if there's a list of items after the tail, // like `[..wibble, wobble, wabble]` let elements = - Parser::series_of(self, &Parser::parse_expression, Some(&Token::Comma)); + self.series_of(&Parser::parse_expression, Some(&Token::Comma)); match elements { Err(_) => {} Ok(elements) => { @@ -521,6 +543,13 @@ where } }; }; + + if tail.is_some() && !elements_end_with_comma { + self.warnings + .push(DeprecatedSyntaxWarning::DeprecatedListPrepend { + location: SrcSpan { start, end }, + }); + } } let (_, end) = self.expect_one(&Token::RightSquare)?; @@ -535,7 +564,10 @@ where } _ => {} } - if tail.is_some() && elements.is_empty() { + if tail.is_some() + && elements.is_empty() + && elements_after_tail.as_ref().map_or(true, |e| e.is_empty()) + { return parse_error( ParseErrorType::ListSpreadWithoutElements, SrcSpan { start, end }, @@ -1154,18 +1186,60 @@ where // List Some((start, Token::LeftSquare, _)) => { self.advance(); - let elements = - Parser::series_of(self, &Parser::parse_pattern, Some(&Token::Comma))?; - let tail = if let Some((_, Token::DotDot, _)) = self.tok0 { + let (elements, elements_end_with_comma) = self.series_of_has_trailing_separator( + &Parser::parse_pattern, + Some(&Token::Comma), + )?; + + let mut elements_after_tail = None; + let mut dot_dot_location = None; + let tail = if let Some((start, Token::DotDot, end)) = self.tok0 { + dot_dot_location = Some((start, end)); + if !elements_end_with_comma { + self.warnings + .push(DeprecatedSyntaxWarning::DeprecatedListPattern { + location: SrcSpan { start, end }, + }); + } + self.advance(); let pat = self.parse_pattern()?; - let _ = self.maybe_one(&Token::Comma); + if self.maybe_one(&Token::Comma).is_some() { + // See if there's a list of items after the tail, + // like `[..wibble, wobble, wabble]` + let elements = + Parser::series_of(self, &Parser::parse_pattern, Some(&Token::Comma)); + match elements { + Err(_) => {} + Ok(elements) => { + elements_after_tail = Some(elements); + } + }; + }; Some(pat) } else { None }; + let (end, rsqb_e) = self.expect_one_following_series(&Token::RightSquare, "a pattern")?; + + // If there are elements after the tail, return an error + match elements_after_tail { + Some(elements) if !elements.is_empty() => { + let (start, end) = match (dot_dot_location, tail) { + (Some((start, _)), Some(Some(tail))) => (start, tail.location().end), + (Some((start, end)), Some(None)) => (start, end), + (_, _) => (start, end), + }; + return parse_error( + ParseErrorType::ListPatternSpreadFollowedByElements, + SrcSpan { start, end }, + ); + } + _ => {} + } + let tail = match tail { // There is a tail and it has a Pattern::Var or Pattern::Discard Some(Some(pat @ (Pattern::Variable { .. } | Pattern::Discard { .. }))) => { @@ -1435,7 +1509,7 @@ where module: Option<(u32, EcoString, u32)>, ) -> Result { let (mut start, name, end) = self.expect_upname()?; - let (args, with_spread, end) = self.parse_constructor_pattern_args(end)?; + let (args, spread, end) = self.parse_constructor_pattern_args(end)?; if let Some((s, _, _)) = module { start = s; } @@ -1444,7 +1518,7 @@ where arguments: args, module: module.map(|(_, n, _)| n), name, - with_spread, + spread, constructor: Inferred::Unknown, type_: (), }) @@ -1452,24 +1526,28 @@ where // examples: // ( args ) + #[allow(clippy::type_complexity)] fn parse_constructor_pattern_args( &mut self, upname_end: u32, - ) -> Result<(Vec>, bool, u32), ParseError> { + ) -> Result<(Vec>, Option, u32), ParseError> { if self.maybe_one(&Token::LeftParen).is_some() { let args = Parser::series_of( self, &Parser::parse_constructor_pattern_arg, Some(&Token::Comma), )?; - let with_spread = self.maybe_one(&Token::DotDot).is_some(); - if with_spread { + let spread = self + .maybe_one(&Token::DotDot) + .map(|(start, end)| SrcSpan { start, end }); + + if spread.is_some() { let _ = self.maybe_one(&Token::Comma); } let (_, end) = self.expect_one(&Token::RightParen)?; - Ok((args, with_spread, end)) + Ok((args, spread, end)) } else { - Ok((vec![], false, upname_end)) + Ok((vec![], None, upname_end)) } } @@ -2411,8 +2489,9 @@ where })), } } - Some((start, _, end)) => parse_error( + Some((start, token, end)) => parse_error( ParseErrorType::UnexpectedToken { + token, expected: vec!["UpName".into(), "Name".into()], hint: None, }, @@ -2746,7 +2825,7 @@ where Token::UpName { .. } => { parse_error(ParseErrorType::IncorrectName, SrcSpan { start, end }) } - _ if is_reserved_word(tok) => parse_error( + _ if tok.is_reserved_word() => parse_error( ParseErrorType::UnexpectedReservedWord, SrcSpan { start, end }, ), @@ -2825,12 +2904,27 @@ where parser: &impl Fn(&mut Self) -> Result, ParseError>, sep: Option<&Token>, ) -> Result, ParseError> { + let (res, _) = self.series_of_has_trailing_separator(parser, sep)?; + Ok(res) + } + + /// Parse a series by repeating a parser, and a separator. Returns true if + /// the series ends with the trailing separator. + fn series_of_has_trailing_separator( + &mut self, + parser: &impl Fn(&mut Self) -> Result, ParseError>, + sep: Option<&Token>, + ) -> Result<(Vec, bool), ParseError> { let mut results = vec![]; + let mut ends_with_sep = false; while let Some(result) = parser(self)? { results.push(result); if let Some(sep) = sep { if self.maybe_one(sep).is_none() { + ends_with_sep = false; break; + } else { + ends_with_sep = true; } // Helpful error if extra separator if let Some((start, end)) = self.maybe_one(sep) { @@ -2839,7 +2933,7 @@ where } } - Ok(results) + Ok((results, ends_with_sep)) } // If next token is a Name, consume it and return relevant info, otherwise, return none @@ -2884,13 +2978,13 @@ where } } - // Error on the next token or EOF + // Unexpected token error on the next token or EOF fn next_tok_unexpected(&mut self, expected: Vec) -> Result { match self.next_tok() { None => parse_error(ParseErrorType::UnexpectedEof, SrcSpan { start: 0, end: 0 }), - - Some((start, _, end)) => parse_error( + Some((start, token, end)) => parse_error( ParseErrorType::UnexpectedToken { + token, expected, hint: None, }, @@ -3360,6 +3454,60 @@ fn clause_guard_reduction( right, }, + Token::Plus => ClauseGuard::AddInt { + location, + left, + right, + }, + + Token::PlusDot => ClauseGuard::AddFloat { + location, + left, + right, + }, + + Token::Minus => ClauseGuard::SubInt { + location, + left, + right, + }, + + Token::MinusDot => ClauseGuard::SubFloat { + location, + left, + right, + }, + + Token::Star => ClauseGuard::MultInt { + location, + left, + right, + }, + + Token::StarDot => ClauseGuard::MultFloat { + location, + left, + right, + }, + + Token::Slash => ClauseGuard::DivInt { + location, + left, + right, + }, + + Token::SlashDot => ClauseGuard::DivFloat { + location, + left, + right, + }, + + Token::Percent => ClauseGuard::RemainderInt { + location, + left, + right, + }, + _ => panic!("Token could not be converted to Guard Op."), } } @@ -3422,90 +3570,6 @@ fn parse_error(error: ParseErrorType, location: SrcSpan) -> Result bool { - match tok { - Token::As - | Token::Assert - | Token::Case - | Token::Const - | Token::Fn - | Token::If - | Token::Import - | Token::Let - | Token::Opaque - | Token::Pub - | Token::Todo - | Token::Type - | Token::Use - | Token::Auto - | Token::Delegate - | Token::Derive - | Token::Echo - | Token::Else - | Token::Implement - | Token::Macro - | Token::Panic - | Token::Test => true, - - Token::Name { .. } - | Token::UpName { .. } - | Token::DiscardName { .. } - | Token::Int { .. } - | Token::Float { .. } - | Token::String { .. } - | Token::CommentDoc { .. } - | Token::LeftParen - | Token::RightParen - | Token::LeftSquare - | Token::RightSquare - | Token::LeftBrace - | Token::RightBrace - | Token::Plus - | Token::Minus - | Token::Star - | Token::Slash - | Token::Less - | Token::Greater - | Token::LessEqual - | Token::GreaterEqual - | Token::Percent - | Token::PlusDot - | Token::MinusDot - | Token::StarDot - | Token::SlashDot - | Token::LessDot - | Token::GreaterDot - | Token::LessEqualDot - | Token::GreaterEqualDot - | Token::LtGt - | Token::Colon - | Token::Comma - | Token::Hash - | Token::Bang - | Token::Equal - | Token::EqualEqual - | Token::NotEqual - | Token::Vbar - | Token::VbarVbar - | Token::AmperAmper - | Token::LtLt - | Token::GtGt - | Token::Pipe - | Token::Dot - | Token::RArrow - | Token::LArrow - | Token::DotDot - | Token::At - | Token::EndOfFile - | Token::CommentNormal - | Token::CommentModule - | Token::NewLine => false, - } -} - // Parsing a function call into the appropriate structure #[derive(Debug)] pub enum ParserArg { @@ -3524,6 +3588,8 @@ pub fn make_call( end: u32, ) -> Result { let mut num_holes = 0; + let mut hole_location = None; + let args = args .into_iter() .map(|a| match a { @@ -3534,10 +3600,12 @@ pub fn make_call( label, } => { num_holes += 1; + hole_location = Some(location); if name != "_" { return parse_error( ParseErrorType::UnexpectedToken { + token: Token::Name { name }, expected: vec!["An expression".into(), "An underscore".into()], hint: None, }, @@ -3572,7 +3640,7 @@ pub fn make_call( end_of_head_byte_index: call.location().end, is_capture: true, arguments: vec![Arg { - location: SrcSpan { start: 0, end: 0 }, + location: hole_location.expect("At least a capture hole"), annotation: None, names: ArgNames::Named { name: CAPTURE_VARIABLE.into(), diff --git a/compiler-core/src/parse/error.rs b/compiler-core/src/parse/error.rs index ae78f73f4..9b8a3428d 100644 --- a/compiler-core/src/parse/error.rs +++ b/compiler-core/src/parse/error.rs @@ -1,5 +1,6 @@ use crate::ast::SrcSpan; use crate::error::wrap; +use crate::parse::Token; use ecow::EcoString; use heck::{ToSnakeCase, ToUpperCamelCase}; @@ -154,13 +155,22 @@ utf16_codepoint, utf32_codepoint, signed, unsigned, big, little, native, size, u ], ), ParseErrorType::ListSpreadFollowedByElements => ( - "I wasn't expecting elements after this spread", + "I wasn't expecting elements after this", vec![ - "A spread can only be used to prepend elements to lists like this: `[first, ..rest]`.\n" - .into(), - "Hint: If you need to append elements to a list you can use `list.append`." - .into(), - "See: https://hexdocs.pm/gleam_stdlib/gleam/list.html#append".into(), + "Lists are immutable and singly-linked, so to append items to them".into(), + "all the elements of a list would need to be copied into a new list.".into(), + "This would be slow, so there is no built-in syntax for it.".into(), + "".into(), + "Hint: prepend items to the list and then reverse it once you are done.".into(), + ], + ), + ParseErrorType::ListPatternSpreadFollowedByElements => ( + "I wasn't expecting elements after this", + vec![ + "Lists are immutable and singly-linked, so to match on the end".into(), + "of a list would require the whole list to be traversed. This".into(), + "would be slow, so there is no built-in syntax for it. Pattern".into(), + "match on the start of the list instead.".into(), ], ), ParseErrorType::UnexpectedReservedWord => ( @@ -178,9 +188,24 @@ utf16_codepoint, utf32_codepoint, signed, unsigned, big, little, native, size, u "Argument labels are not allowed for anonymous functions", vec!["Please remove the argument label.".into()], ), - ParseErrorType::UnexpectedToken { expected, hint } => { - let messages = std::iter::once("Expected one of: ".to_string()) - .chain(expected.iter().map(|s| s.to_string())); + ParseErrorType::UnexpectedToken { + token, + expected, + hint, + } => { + let found = match token { + Token::Int { .. } => "an Int".to_string(), + Token::Float { .. } => "a Float".to_string(), + Token::String { .. } => "a String".to_string(), + Token::CommentDoc { .. } => "a comment".to_string(), + Token::DiscardName { .. } => "a discard name".to_string(), + Token::Name { .. } | Token::UpName { .. } => "a name".to_string(), + _ if token.is_reserved_word() => format!("the keyword {}", token), + _ => token.to_string(), + }; + + let messages = std::iter::once(format!("Found {found}, expected one of: ")) + .chain(expected.iter().map(|s| format!("- {}", s))); let messages = match hint { Some(hint_text) => messages @@ -220,12 +245,16 @@ utf16_codepoint, utf32_codepoint, signed, unsigned, big, little, native, size, u ), ParseErrorType::UnknownTarget => ("I don't know what this attribute is", vec![]), ParseErrorType::ExpectedFunctionBody => ("This function does not have a body", vec![]), - ParseErrorType::RedundantInternalAttribute => ("Redundant internal attribute", vec![ - format!("Only a public definition can be annotated as internal."), - "Hint: remove the `@internal` annotation.".into() - ]), + ParseErrorType::RedundantInternalAttribute => ( + "Redundant internal attribute", + vec![ + format!("Only a public definition can be annotated as internal."), + "Hint: remove the `@internal` annotation.".into(), + ], + ), ParseErrorType::InvalidModuleTypePattern => ( - "Invalid pattern", vec![ + "Invalid pattern", + vec![ "I'm expecting a pattern here".into(), "Hint: A pattern can be a constructor name, a literal value".into(), "or a variable to bind a value to, etc.".into(), @@ -277,6 +306,7 @@ pub enum ParseErrorType { UnexpectedEof, UnexpectedReservedWord, // reserved word used when a name was expected UnexpectedToken { + token: Token, expected: Vec, hint: Option, }, @@ -284,10 +314,11 @@ pub enum ParseErrorType { UnexpectedFunction, // a function was used called outside of another function // A variable was assigned or discarded on the left hand side of a <> pattern ConcatPatternVariableLeftHandSide, - ListSpreadWithoutTail, // let x = [1, ..] - ExpectedFunctionBody, // let x = fn() - RedundantInternalAttribute, // for a private definition marked as internal - InvalidModuleTypePattern, // for patterns that have a dot like: `name.thing` + ListSpreadWithoutTail, // let x = [1, ..] + ExpectedFunctionBody, // let x = fn() + RedundantInternalAttribute, // for a private definition marked as internal + InvalidModuleTypePattern, // for patterns that have a dot like: `name.thing` + ListPatternSpreadFollowedByElements, // When there is a pattern after a spread [..rest, pattern] } impl LexicalError { diff --git a/compiler-core/src/parse/extra.rs b/compiler-core/src/parse/extra.rs index 7bedf6303..986c9ddd6 100644 --- a/compiler-core/src/parse/extra.rs +++ b/compiler-core/src/parse/extra.rs @@ -1,3 +1,5 @@ +use std::cmp::Ordering; + use ecow::EcoString; use crate::ast::SrcSpan; @@ -16,18 +18,21 @@ impl ModuleExtra { Default::default() } + /// Detects if a byte index is in a comment context pub fn is_within_comment(&self, byte_index: u32) -> bool { - self.comments - .binary_search_by(|span| span.cmp_byte_index(byte_index)) - .is_ok() - || self - .doc_comments - .binary_search_by(|span| span.cmp_byte_index(byte_index)) - .is_ok() - || self - .module_comments - .binary_search_by(|span| span.cmp_byte_index(byte_index)) - .is_ok() + let cmp = |span: &SrcSpan| { + if byte_index < span.start { + Ordering::Less + } else if span.end < byte_index { + Ordering::Greater + } else { + Ordering::Equal + } + }; + + self.comments.binary_search_by(cmp).is_ok() + || self.doc_comments.binary_search_by(cmp).is_ok() + || self.module_comments.binary_search_by(cmp).is_ok() } } diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__arithmetic_in_guards.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__arithmetic_in_guards.snap new file mode 100644 index 000000000..2cadbc17f --- /dev/null +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__arithmetic_in_guards.snap @@ -0,0 +1,103 @@ +--- +source: compiler-core/src/parse/tests.rs +expression: "\ncase 2, 3 {\n x, y if x + y == 1 -> True\n}" +--- +[ + Expression( + Case { + location: SrcSpan { + start: 1, + end: 45, + }, + subjects: [ + Int { + location: SrcSpan { + start: 6, + end: 7, + }, + value: "2", + }, + Int { + location: SrcSpan { + start: 9, + end: 10, + }, + value: "3", + }, + ], + clauses: [ + Clause { + location: SrcSpan { + start: 17, + end: 43, + }, + pattern: [ + Variable { + location: SrcSpan { + start: 17, + end: 18, + }, + name: "x", + type_: (), + }, + Variable { + location: SrcSpan { + start: 20, + end: 21, + }, + name: "y", + type_: (), + }, + ], + alternative_patterns: [], + guard: Some( + Equals { + location: SrcSpan { + start: 25, + end: 35, + }, + left: AddInt { + location: SrcSpan { + start: 25, + end: 30, + }, + left: Var { + location: SrcSpan { + start: 25, + end: 26, + }, + type_: (), + name: "x", + }, + right: Var { + location: SrcSpan { + start: 29, + end: 30, + }, + type_: (), + name: "y", + }, + }, + right: Constant( + Int { + location: SrcSpan { + start: 34, + end: 35, + }, + value: "1", + }, + ), + }, + ), + then: Var { + location: SrcSpan { + start: 39, + end: 43, + }, + name: "True", + }, + }, + ], + }, + ), +] diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__assignment_pattern_invalid_bit_segment.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__assignment_pattern_invalid_bit_segment.snap index 31c0f5558..ca4d28b84 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__assignment_pattern_invalid_bit_segment.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__assignment_pattern_invalid_bit_segment.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 1004 expression: "\nfn main() {\n let <> = <<24, 3>>\n}\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 3 │ let <> = <<24, 3>> │ ^^^ I was not expecting this -Expected one of: -">>" -a bit array segment pattern +Found the keyword `pub`, expected one of: +- `>>` +- a bit array segment pattern diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__assignment_pattern_invalid_tuple.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__assignment_pattern_invalid_tuple.snap index 50c3871c4..fdc4bf1df 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__assignment_pattern_invalid_tuple.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__assignment_pattern_invalid_tuple.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 993 expression: "\nfn main() {\n let #(a, case, c) = #(1, 2, 3)\n}\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 3 │ let #(a, case, c) = #(1, 2, 3) │ ^^^^ I was not expecting this -Expected one of: -")" -a pattern +Found the keyword `case`, expected one of: +- `)` +- a pattern diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__bit_array_invalid_segment.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__bit_array_invalid_segment.snap index f9d5787cb..f45989cf6 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__bit_array_invalid_segment.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__bit_array_invalid_segment.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 945 expression: "\nfn main() {\n <<72, 101, 108, 108, 111, 44, 32, 74, 111, 101, const>>\n}\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 3 │ <<72, 101, 108, 108, 111, 44, 32, 74, 111, 101, const>> │ ^^^^^ I was not expecting this -Expected one of: -">>" -a bit array segment +Found the keyword `const`, expected one of: +- `>>` +- a bit array segment diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__capture_with_name.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__capture_with_name.snap index 3e002d4f6..5db0b9e77 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__capture_with_name.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__capture_with_name.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 765 expression: "\npub fn main() {\n add(_name, 1)\n}\n\nfn add(x, y) {\n x + y\n}\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 3 │ add(_name, 1) │ ^^^^^ I was not expecting this -Expected one of: -An expression -An underscore +Found a name, expected one of: +- An expression +- An underscore diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__case_invalid_case_pattern.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__case_invalid_case_pattern.snap index 9f03d06af..2ee0bbb56 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__case_invalid_case_pattern.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__case_invalid_case_pattern.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 969 expression: "\nfn main() {\n case 1 {\n -> -> 0\n }\n}\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 4 │ -> -> 0 │ ^^ I was not expecting this -Expected one of: -"}" -a case clause +Found `->`, expected one of: +- `}` +- a case clause diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__case_invalid_expression.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__case_invalid_expression.snap index f07af0ad3..c47827701 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__case_invalid_expression.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__case_invalid_expression.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 956 expression: "\nfn main() {\n case 1, type {\n _, _ -> 0\n }\n}\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 3 │ case 1, type { │ ^^^^ I was not expecting this -Expected one of: -"{" -an expression +Found the keyword `type`, expected one of: +- `{` +- an expression diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__case_list_pattern_after_spread.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__case_list_pattern_after_spread.snap new file mode 100644 index 000000000..d80be1415 --- /dev/null +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__case_list_pattern_after_spread.snap @@ -0,0 +1,15 @@ +--- +source: compiler-core/src/parse/tests.rs +assertion_line: 1007 +expression: "\nfn main() {\n case somelist {\n [..rest, last] -> 1\n _ -> 2\n }\n}\n" +--- +error: Syntax error + ┌─ /src/parse/error.gleam:4:10 + │ +4 │ [..rest, last] -> 1 + │ ^^^^^^ I wasn't expecting elements after this + +Lists are immutable and singly-linked, so to match on the end +of a list would require the whole list to be traversed. This +would be slow, so there is no built-in syntax for it. Pattern +match on the start of the list instead. diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_bit_array_segment.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_bit_array_segment.snap index 48c6ba7d1..886c04b5c 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_bit_array_segment.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_bit_array_segment.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 1116 expression: "\nconst a = <<1, 2, <->>\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 2 │ const a = <<1, 2, <->> │ ^^ I was not expecting this -Expected one of: -">>" -a bit array segment +Found `<-`, expected one of: +- `>>` +- a bit array segment diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_list.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_list.snap index 2959b24bf..9cc707fb1 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_list.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_list.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 1107 expression: "\nconst a = [1, 2, <-]\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 2 │ const a = [1, 2, <-] │ ^^ I was not expecting this -Expected one of: -"]" -a constant value +Found `<-`, expected one of: +- `]` +- a constant value diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_record_constructor.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_record_constructor.snap index 3e5bc4a93..d044fdf02 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_record_constructor.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_record_constructor.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 1125 expression: "\ntype A {\n A(String, Int)\n}\nconst a = A(\"a\", let)\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 5 │ const a = A("a", let) │ ^^^ I was not expecting this -Expected one of: -")" -a constant record argument +Found the keyword `let`, expected one of: +- `)` +- a constant record argument diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_tuple.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_tuple.snap index 206908694..1f8e75b1d 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_tuple.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__const_invalid_tuple.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 1098 expression: "\nconst a = #(1, 2, <-)\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 2 │ const a = #(1, 2, <-) │ ^^ I was not expecting this -Expected one of: -")" -a constant value +Found `<-`, expected one of: +- `)` +- a constant value diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__function_invalid_signature.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__function_invalid_signature.snap new file mode 100644 index 000000000..a2036c34c --- /dev/null +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__function_invalid_signature.snap @@ -0,0 +1,14 @@ +--- +source: compiler-core/src/parse/tests.rs +assertion_line: 1087 +expression: "\nfn f(a, \"b\") -> String {\n a <> b\n}\n" +--- +error: Syntax error + ┌─ /src/parse/error.gleam:2:9 + │ +2 │ fn f(a, "b") -> String { + │ ^^^ I was not expecting this + +Found a String, expected one of: +- `)` +- a function parameter diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__function_type_invalid_param_type.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__function_type_invalid_param_type.snap index 5f3b08bc0..5220d4891 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__function_type_invalid_param_type.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__function_type_invalid_param_type.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 1076 expression: "\nfn f(g: fn(Int, 1) -> Int) -> Int {\n g(0, 1)\n}\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 2 │ fn f(g: fn(Int, 1) -> Int) -> Int { │ ^ I was not expecting this -Expected one of: -")" -a type +Found an Int, expected one of: +- `)` +- a type diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__list_spread_as_first_item_followed_by_other_items.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__list_spread_as_first_item_followed_by_other_items.snap new file mode 100644 index 000000000..5d33ac31b --- /dev/null +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__list_spread_as_first_item_followed_by_other_items.snap @@ -0,0 +1,16 @@ +--- +source: compiler-core/src/parse/tests.rs +assertion_line: 796 +expression: "\npub fn main() -> Nil {\n let xs = [1, 2, 3]\n [..xs, 3 + 3, 4]\n}\n" +--- +error: Syntax error + ┌─ /src/parse/error.gleam:4:4 + │ +4 │ [..xs, 3 + 3, 4] + │ ^^^^ I wasn't expecting elements after this + +Lists are immutable and singly-linked, so to append items to them +all the elements of a list would need to be copied into a new list. +This would be slow, so there is no built-in syntax for it. + +Hint: prepend items to the list and then reverse it once you are done. diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__list_spread_followed_by_extra_items.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__list_spread_followed_by_extra_items.snap index e47f6632e..267cad6e0 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__list_spread_followed_by_extra_items.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__list_spread_followed_by_extra_items.snap @@ -1,14 +1,16 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 784 expression: "\npub fn main() -> Nil {\n let xs = [1, 2, 3]\n [1, 2, ..xs, 3 + 3, 4]\n}\n" --- error: Syntax error ┌─ /src/parse/error.gleam:4:10 │ 4 │ [1, 2, ..xs, 3 + 3, 4] - │ ^^^^ I wasn't expecting elements after this spread + │ ^^^^ I wasn't expecting elements after this -A spread can only be used to prepend elements to lists like this: `[first, ..rest]`. +Lists are immutable and singly-linked, so to append items to them +all the elements of a list would need to be copied into a new list. +This would be slow, so there is no built-in syntax for it. -Hint: If you need to append elements to a list you can use `list.append`. -See: https://hexdocs.pm/gleam_stdlib/gleam/list.html#append +Hint: prepend items to the list and then reverse it once you are done. diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_eq_after_binding_snapshot_1.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_eq_after_binding_snapshot_1.snap index 43af71c7d..5c68f0790 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_eq_after_binding_snapshot_1.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_eq_after_binding_snapshot_1.snap @@ -1,9 +1,9 @@ --- source: compiler-core/src/parse/tests.rs -expression: let foo +expression: let wibble --- error: Syntax error ┌─ /src/parse/error.gleam:1:5 │ -1 │ let foo - │ ^^^ I was expecting a '=' after this +1 │ let wibble + │ ^^^^^^ I was expecting a '=' after this diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_eq_after_binding_snapshot_2.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_eq_after_binding_snapshot_2.snap index b0a6b6a42..31955556b 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_eq_after_binding_snapshot_2.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_eq_after_binding_snapshot_2.snap @@ -1,9 +1,9 @@ --- source: compiler-core/src/parse/tests.rs -expression: "let foo\n foo = 4" +expression: "let wibble\n wibble = 4" --- error: Syntax error ┌─ /src/parse/error.gleam:1:5 │ -1 │ let foo - │ ^^^ I was expecting a '=' after this +1 │ let wibble + │ ^^^^^^ I was expecting a '=' after this diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_1.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_1.snap index 83347ab73..691d40242 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_1.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_1.snap @@ -1,12 +1,12 @@ --- source: compiler-core/src/parse/tests.rs -expression: foo = 4 +expression: wibble = 4 --- error: Syntax error - ┌─ /src/parse/error.gleam:1:5 + ┌─ /src/parse/error.gleam:1:8 │ -1 │ foo = 4 - │ ^ There must be a 'let' to bind variable to value +1 │ wibble = 4 + │ ^ There must be a 'let' to bind variable to value Hint: Use let for binding. See: https://tour.gleam.run/basics/assignments/ diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_2.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_2.snap index a62e4aece..c56184e14 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_2.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_2.snap @@ -1,12 +1,12 @@ --- source: compiler-core/src/parse/tests.rs -expression: "foo:Int = 4" +expression: "wibble:Int = 4" --- error: Syntax error - ┌─ /src/parse/error.gleam:1:4 + ┌─ /src/parse/error.gleam:1:7 │ -1 │ foo:Int = 4 - │ ^ There must be a 'let' to bind variable to value +1 │ wibble:Int = 4 + │ ^ There must be a 'let' to bind variable to value Hint: Use let for binding. See: https://tour.gleam.run/basics/assignments/ diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_3.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_3.snap index a2653bbc0..79ed8e40d 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_3.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__no_let_binding_snapshot_3.snap @@ -1,12 +1,12 @@ --- source: compiler-core/src/parse/tests.rs -expression: "let bar:Int = 32\n bar = 42" +expression: "let wobble:Int = 32\n wobble = 42" --- error: Syntax error - ┌─ /src/parse/error.gleam:2:13 + ┌─ /src/parse/error.gleam:2:16 │ -2 │ bar = 42 - │ ^ There must be a 'let' to bind variable to value +2 │ wobble = 42 + │ ^ There must be a 'let' to bind variable to value Hint: Use let for binding. See: https://tour.gleam.run/basics/assignments/ diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__tuple_invalid_expr.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__tuple_invalid_expr.snap index e4a55d6ab..b9d0ea02b 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__tuple_invalid_expr.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__tuple_invalid_expr.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 934 expression: "\nfn main() {\n #(1, 2, const)\n}\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 3 │ #(1, 2, const) │ ^^^^^ I was not expecting this -Expected one of: -")" -an expression +Found the keyword `const`, expected one of: +- `)` +- an expression diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_constructor.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_constructor.snap index 8d48a8b49..7d5a7ec8a 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_constructor.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_constructor.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 1029 expression: "\ntype A {\n A(String)\n type\n}\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 4 │ type │ ^^^^ I was not expecting this -Expected one of: -"}" -a record constructor +Found the keyword `type`, expected one of: +- `}` +- a record constructor diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_constructor_arg.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_constructor_arg.snap index 45a440fac..8587551d4 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_constructor_arg.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_constructor_arg.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 1052 expression: "\ntype A {\n A(type: String)\n}\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 3 │ A(type: String) │ ^^^^ I was not expecting this -Expected one of: -")" -a constructor argument name +Found the keyword `type`, expected one of: +- `)` +- a constructor argument name diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_record.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_record.snap new file mode 100644 index 000000000..d31936a14 --- /dev/null +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_record.snap @@ -0,0 +1,14 @@ +--- +source: compiler-core/src/parse/tests.rs +assertion_line: 1063 +expression: "\ntype A {\n One\n Two\n 3\n}\n" +--- +error: Syntax error + ┌─ /src/parse/error.gleam:5:5 + │ +5 │ 3 + │ ^ I was not expecting this + +Found an Int, expected one of: +- `}` +- a record constructor diff --git a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_type_name.snap b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_type_name.snap index 18fd44295..38fdd348c 100644 --- a/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_type_name.snap +++ b/compiler-core/src/parse/snapshots/glistix_core__parse__tests__type_invalid_type_name.snap @@ -1,5 +1,6 @@ --- source: compiler-core/src/parse/tests.rs +assertion_line: 1041 expression: "\ntype A(a, type) {\n A\n}\n" --- error: Syntax error @@ -8,6 +9,6 @@ error: Syntax error 2 │ type A(a, type) { │ ^^^^ I was not expecting this -Expected one of: -")" -a name +Found the keyword `type`, expected one of: +- `)` +- a name diff --git a/compiler-core/src/parse/tests.rs b/compiler-core/src/parse/tests.rs index f6799b501..1da68c4f8 100644 --- a/compiler-core/src/parse/tests.rs +++ b/compiler-core/src/parse/tests.rs @@ -4,6 +4,7 @@ use crate::parse::error::{ }; use crate::parse::lexer::make_tokenizer; use crate::parse::token::Token; +use crate::warning::WarningEmitter; use camino::Utf8PathBuf; use itertools::Itertools; @@ -29,7 +30,12 @@ macro_rules! assert_module_error { macro_rules! assert_parse_module { ($src:expr) => { - let result = crate::parse::parse_module($src).expect("should parse"); + let result = crate::parse::parse_module( + camino::Utf8PathBuf::from("test/path"), + $src, + &crate::warning::WarningEmitter::null(), + ) + .expect("should parse"); insta::assert_snapshot!(insta::internals::AutoName, &format!("{:#?}", result), $src); }; } @@ -42,7 +48,9 @@ macro_rules! assert_parse { } pub fn expect_module_error(src: &str) -> String { - let result = crate::parse::parse_module(src).expect_err("should not parse"); + let result = + crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) + .expect_err("should not parse"); let error = crate::error::Error::Parse { src: src.into(), path: Utf8PathBuf::from("/src/parse/error.gleam"), @@ -362,16 +370,16 @@ fn name2() { #[test] fn triple_equals() { assert_error!( - "let bar:Int = 32 - bar === 42", + "let wobble:Int = 32 + wobble === 42", ParseError { error: ParseErrorType::LexError { error: LexicalError { error: LexicalErrorType::InvalidTripleEqual, - location: SrcSpan { start: 29, end: 32 }, + location: SrcSpan { start: 35, end: 38 }, } }, - location: SrcSpan { start: 29, end: 32 }, + location: SrcSpan { start: 35, end: 38 }, } ); } @@ -379,11 +387,11 @@ fn triple_equals() { #[test] fn triple_equals_with_whitespace() { assert_error!( - "let bar:Int = 32 - bar == = 42", + "let wobble:Int = 32 + wobble == = 42", ParseError { error: ParseErrorType::NoLetBinding, - location: SrcSpan { start: 36, end: 37 }, + location: SrcSpan { start: 42, end: 43 }, } ); } @@ -429,9 +437,9 @@ fn anonymous_function_labeled_arguments() { #[test] fn no_let_binding() { assert_error!( - "foo = 32", + "wibble = 32", ParseError { - location: SrcSpan { start: 4, end: 5 }, + location: SrcSpan { start: 7, end: 8 }, error: ParseErrorType::NoLetBinding } ); @@ -440,9 +448,9 @@ fn no_let_binding() { #[test] fn no_let_binding1() { assert_error!( - "foo:Int = 32", + "wibble:Int = 32", ParseError { - location: SrcSpan { start: 3, end: 4 }, + location: SrcSpan { start: 6, end: 7 }, error: ParseErrorType::NoLetBinding } ); @@ -451,10 +459,10 @@ fn no_let_binding1() { #[test] fn no_let_binding2() { assert_error!( - "let bar:Int = 32 - bar = 42", + "let wobble:Int = 32 + wobble = 42", ParseError { - location: SrcSpan { start: 29, end: 30 }, + location: SrcSpan { start: 35, end: 36 }, error: ParseErrorType::NoLetBinding } ); @@ -474,9 +482,9 @@ fn no_let_binding3() { #[test] fn no_eq_after_binding() { assert_error!( - "let foo", + "let wibble", ParseError { - location: SrcSpan { start: 4, end: 7 }, + location: SrcSpan { start: 4, end: 10 }, error: ParseErrorType::ExpectedEqual } ); @@ -485,10 +493,10 @@ fn no_eq_after_binding() { #[test] fn no_eq_after_binding1() { assert_error!( - "let foo - foo = 4", + "let wibble + wibble = 4", ParseError { - location: SrcSpan { start: 4, end: 7 }, + location: SrcSpan { start: 4, end: 10 }, error: ParseErrorType::ExpectedEqual } ); @@ -496,31 +504,31 @@ fn no_eq_after_binding1() { #[test] fn no_let_binding_snapshot_1() { - assert_error!("foo = 4"); + assert_error!("wibble = 4"); } #[test] fn no_let_binding_snapshot_2() { - assert_error!("foo:Int = 4"); + assert_error!("wibble:Int = 4"); } #[test] fn no_let_binding_snapshot_3() { assert_error!( - "let bar:Int = 32 - bar = 42" + "let wobble:Int = 32 + wobble = 42" ); } #[test] fn no_eq_after_binding_snapshot_1() { - assert_error!("let foo"); + assert_error!("let wibble"); } #[test] fn no_eq_after_binding_snapshot_2() { assert_error!( - "let foo - foo = 4" + "let wibble + wibble = 4" ); } @@ -791,6 +799,18 @@ pub fn main() -> Nil { ); } +#[test] +fn list_spread_as_first_item_followed_by_other_items() { + assert_module_error!( + r#" +pub fn main() -> Nil { + let xs = [1, 2, 3] + [..xs, 3 + 3, 4] +} +"# + ); +} + // Tests for nested tuples and structs in tuples // https://github.com/gleam-lang/gleam/issues/1980 @@ -990,6 +1010,20 @@ fn main() { ); } +#[test] +fn case_list_pattern_after_spread() { + assert_module_error!( + " +fn main() { + case somelist { + [..rest, last] -> 1 + _ -> 2 + } +} +" + ); +} + #[test] fn type_invalid_constructor() { assert_module_error!( @@ -1024,6 +1058,19 @@ type A { ); } +#[test] +fn type_invalid_record() { + assert_module_error!( + " +type A { + One + Two + 3 +} +" + ); +} + #[test] fn function_type_invalid_param_type() { assert_module_error!( @@ -1035,6 +1082,17 @@ fn f(g: fn(Int, 1) -> Int) -> Int { ); } +#[test] +fn function_invalid_signature() { + assert_module_error!( + r#" +fn f(a, "b") -> String { + a <> b +} +"# + ); +} + #[test] fn const_invalid_tuple() { assert_module_error!( @@ -1087,3 +1145,14 @@ fn newline_tokens() { ] ); } + +// https://github.com/gleam-lang/gleam/issues/1756 +#[test] +fn arithmetic_in_guards() { + assert_parse!( + " +case 2, 3 { + x, y if x + y == 1 -> True +}" + ); +} diff --git a/compiler-core/src/parse/token.rs b/compiler-core/src/parse/token.rs index 5ec22d2a0..22f3afe4f 100644 --- a/compiler-core/src/parse/token.rs +++ b/compiler-core/src/parse/token.rs @@ -106,9 +106,94 @@ impl Token { | Self::GreaterEqualDot | Self::GreaterDot => Some(4), + Self::Plus | Self::PlusDot | Self::Minus | Self::MinusDot => Some(5), + + Self::Star | Self::StarDot | Self::Slash | Self::SlashDot | Self::Percent => Some(6), + _ => None, } } + + pub fn is_reserved_word(&self) -> bool { + match self { + Token::As + | Token::Assert + | Token::Case + | Token::Const + | Token::Fn + | Token::If + | Token::Import + | Token::Let + | Token::Opaque + | Token::Pub + | Token::Todo + | Token::Type + | Token::Use + | Token::Auto + | Token::Delegate + | Token::Derive + | Token::Echo + | Token::Else + | Token::Implement + | Token::Macro + | Token::Panic + | Token::Test => true, + + Token::Name { .. } + | Token::UpName { .. } + | Token::DiscardName { .. } + | Token::Int { .. } + | Token::Float { .. } + | Token::String { .. } + | Token::CommentDoc { .. } + | Token::LeftParen + | Token::RightParen + | Token::LeftSquare + | Token::RightSquare + | Token::LeftBrace + | Token::RightBrace + | Token::Plus + | Token::Minus + | Token::Star + | Token::Slash + | Token::Less + | Token::Greater + | Token::LessEqual + | Token::GreaterEqual + | Token::Percent + | Token::PlusDot + | Token::MinusDot + | Token::StarDot + | Token::SlashDot + | Token::LessDot + | Token::GreaterDot + | Token::LessEqualDot + | Token::GreaterEqualDot + | Token::LtGt + | Token::Colon + | Token::Comma + | Token::Hash + | Token::Bang + | Token::Equal + | Token::EqualEqual + | Token::NotEqual + | Token::Vbar + | Token::VbarVbar + | Token::AmperAmper + | Token::LtLt + | Token::GtGt + | Token::Pipe + | Token::Dot + | Token::RArrow + | Token::LArrow + | Token::DotDot + | Token::At + | Token::EndOfFile + | Token::CommentNormal + | Token::CommentModule + | Token::NewLine => false, + } + } } impl fmt::Display for Token { @@ -190,6 +275,6 @@ impl fmt::Display for Token { Token::Vbar => "|", Token::VbarVbar => "||", }; - write!(f, "\"{s}\"") + write!(f, "`{s}`") } } diff --git a/compiler-core/src/pretty.rs b/compiler-core/src/pretty.rs index 2770ca1dd..4f087963b 100644 --- a/compiler-core/src/pretty.rs +++ b/compiler-core/src/pretty.rs @@ -228,9 +228,9 @@ pub enum NestMode { /// to exactly the specified value. /// /// `doc.nest(2).set_nesting(0)` - /// "foo - /// bar <- no indentation is added! - /// baz" + /// "wibble + /// wobble <- no indentation is added! + /// wubble" Set, } diff --git a/compiler-core/src/pretty/tests.rs b/compiler-core/src/pretty/tests.rs index 14cb2e652..cfa18703f 100644 --- a/compiler-core/src/pretty/tests.rs +++ b/compiler-core/src/pretty/tests.rs @@ -293,25 +293,28 @@ fn empty_documents() { // strings assert!("".to_doc().is_empty()); - assert!(!"foo".to_doc().is_empty()); + assert!(!"wibble".to_doc().is_empty()); assert!(!" ".to_doc().is_empty()); assert!(!"\n".to_doc().is_empty()); // containers assert!("".to_doc().nest(2).is_empty()); - assert!(!"foo".to_doc().nest(2).is_empty()); + assert!(!"wibble".to_doc().nest(2).is_empty()); assert!("".to_doc().group().is_empty()); - assert!(!"foo".to_doc().group().is_empty()); + assert!(!"wibble".to_doc().group().is_empty()); assert!(break_("", "").is_empty()); - assert!(!break_("foo", "foo").is_empty()); - assert!(!break_("foo\nbar", "foo bar").is_empty()); + assert!(!break_("wibble", "wibble").is_empty()); + assert!(!break_("wibble\nwobble", "wibble wobble").is_empty()); assert!("".to_doc().append("".to_doc()).is_empty()); - assert!(!"foo".to_doc().append("".to_doc()).is_empty()); - assert!(!"".to_doc().append("foo".to_doc()).is_empty()); + assert!(!"wibble".to_doc().append("".to_doc()).is_empty()); + assert!(!"".to_doc().append("wibble".to_doc()).is_empty()); } #[test] fn set_nesting() { - let doc = Vec(vec!["foo".to_doc(), break_("", " "), "bar".to_doc()]).group(); - assert_eq!("foo\nbar", doc.set_nesting(0).nest(2).to_pretty_string(1)); + let doc = Vec(vec!["wibble".to_doc(), break_("", " "), "wobble".to_doc()]).group(); + assert_eq!( + "wibble\nwobble", + doc.set_nesting(0).nest(2).to_pretty_string(1) + ); } diff --git a/compiler-core/src/type_.rs b/compiler-core/src/type_.rs index acf66b94e..502e80570 100644 --- a/compiler-core/src/type_.rs +++ b/compiler-core/src/type_.rs @@ -7,6 +7,7 @@ pub(crate) mod pattern; pub(crate) mod pipe; pub(crate) mod prelude; pub mod pretty; +pub(crate) mod printer; #[cfg(test)] pub mod tests; @@ -755,7 +756,7 @@ pub struct PatternConstructor { pub name: EcoString, pub field_map: Option, pub documentation: Option, - pub module: Option, + pub module: EcoString, pub location: SrcSpan, pub constructor_index: u16, } @@ -763,7 +764,7 @@ pub struct PatternConstructor { impl PatternConstructor { pub fn definition_location(&self) -> Option> { Some(DefinitionLocation { - module: Some(self.module.as_deref()?), + module: Some(self.module.as_str()), span: self.location, }) } diff --git a/compiler-core/src/type_/environment.rs b/compiler-core/src/type_/environment.rs index 062f116aa..3bf408b0b 100644 --- a/compiler-core/src/type_/environment.rs +++ b/compiler-core/src/type_/environment.rs @@ -289,6 +289,22 @@ impl<'a> Environment<'a> { } } + pub fn assert_unique_type_name( + &mut self, + name: &EcoString, + location: SrcSpan, + ) -> Result<(), Error> { + match self.module_types.get(name) { + None => Ok(()), + Some(prelude_type) if is_prelude_module(&prelude_type.module) => Ok(()), + Some(previous) => Err(Error::DuplicateTypeName { + name: name.clone(), + location, + previous_location: previous.origin, + }), + } + } + /// Map a type to constructors in the current scope. /// pub fn insert_type_to_constructors( @@ -412,6 +428,7 @@ impl<'a> Environment<'a> { } })?; let _ = self.unused_modules.remove(module_name); + let _ = self.unused_module_aliases.remove(module_name); module.get_public_value(name).ok_or_else(|| { UnknownValueConstructorError::ModuleValue { name: name.clone(), diff --git a/compiler-core/src/type_/error.rs b/compiler-core/src/type_/error.rs index c99c5db83..579c19265 100644 --- a/compiler-core/src/type_/error.rs +++ b/compiler-core/src/type_/error.rs @@ -655,6 +655,18 @@ pub enum Warning { location: SrcSpan, panic_position: PanicPosition, }, + + /// When a function capture is used in a pipe to pipe into the first + /// argument of a function: + /// + /// ```gleam + /// wibble |> wobble(_, 1) + /// ^ Redundant and can be removed + /// ``` + /// + RedundantPipeFunctionCapture { + location: SrcSpan, + }, } #[derive(Debug, Eq, PartialEq, Clone, Copy)] diff --git a/compiler-core/src/type_/expression.rs b/compiler-core/src/type_/expression.rs index 4575bb6d9..8ad786701 100644 --- a/compiler-core/src/type_/expression.rs +++ b/compiler-core/src/type_/expression.rs @@ -27,8 +27,8 @@ pub struct Implementations { /// Imagine this scenario: /// /// ```gleam - /// @external(javascript, "foo", "bar") - /// @external(erlang, "foo", "bar") + /// @external(javascript, "wibble", "wobble") + /// @external(erlang, "wibble", "wobble") /// pub fn func() -> Int /// ``` /// @@ -136,7 +136,7 @@ impl Implementations { // // For example: // ```gleam - // @external(erlang, "foo", "bar") + // @external(erlang, "wibble", "wobble") // pub fn erlang_only_with_pure_gleam_default() -> Int { // 1 + 1 // } @@ -354,7 +354,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> { location, elements, tail, - .. } => self.infer_list(elements, tail, location), UntypedExpr::Call { @@ -536,7 +535,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { pub(crate) fn infer_statements( &mut self, untyped: Vec1, - ) -> Result, Error> { + ) -> Vec1 { let count = untyped.len(); let location = SrcSpan::new( untyped.first().location().start, @@ -545,12 +544,39 @@ impl<'a, 'b> ExprTyper<'a, 'b> { self.infer_iter_statements(location, count, untyped.into_iter()) } + // Helper to push a new error to the errors list with rigid names. + fn error_with_rigid_names(&mut self, error: Error) { + let rigid_names = self.hydrator.rigid_names(); + self.errors + .push(error.with_unify_error_rigid_names(&rigid_names)); + } + + // Helper to push a new error to the errors list and return an invalid expression. + fn error_expr_with_rigid_names(&mut self, location: SrcSpan, error: Error) -> TypedExpr { + self.error_with_rigid_names(error); + TypedExpr::Invalid { + location, + typ: self.new_unbound_var(), + } + } + + // Helper to push a new error to the errors list and return an invalid pattern. + fn error_pattern_with_rigid_names( + &mut self, + location: SrcSpan, + error: Error, + type_: Arc, + ) -> TypedPattern { + self.error_with_rigid_names(error); + Pattern::Invalid { location, type_ } + } + fn infer_iter_statements>( &mut self, location: SrcSpan, count: usize, mut untyped: StatementsIter, - ) -> Result, Error> { + ) -> Vec1 { let mut i = 0; let mut statements: Vec = Vec::with_capacity(count); @@ -559,13 +585,22 @@ impl<'a, 'b> ExprTyper<'a, 'b> { match statement { Statement::Use(use_) => { - let statement = self.infer_use(use_, location, untyped.collect())?; + let statement = match self.infer_use(use_, location, untyped.collect()) { + Ok(statement) => statement, + Err(error) => { + Statement::Expression(self.error_expr_with_rigid_names(location, error)) + } + }; statements.push(statement); break; // Inferring the use has consumed the rest of the exprs } Statement::Expression(expression) => { - let expression = self.infer(expression)?; + let location = expression.location(); + let expression = match self.infer(expression) { + Ok(expression) => expression, + Err(error) => self.error_expr_with_rigid_names(location, error), + }; // This isn't the final expression in the sequence, so call the // `expression_discarded` function to see if anything is being @@ -577,13 +612,13 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } Statement::Assignment(assignment) => { - let assignment = self.infer_assignment(assignment)?; + let assignment = self.infer_assignment(assignment); statements.push(Statement::Assignment(assignment)); } } } - Ok(Vec1::try_from_vec(statements).expect("empty sequence")) + Vec1::try_from_vec(statements).expect("empty sequence") } fn infer_use( @@ -593,7 +628,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { mut following_expressions: Vec, ) -> Result { let use_call_location = use_.call.location(); - let mut call = get_use_expression_call(*use_.call)?; + let mut call = get_use_expression_call(*use_.call); let assignments = UseAssignments::from_use_expression(use_.assignments); let mut statements = assignments.body_assignments; @@ -1198,10 +1233,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { .emit(Warning::InefficientEmptyListCheck { location, kind }); } - fn infer_assignment( - &mut self, - assignment: UntypedAssignment, - ) -> Result { + fn infer_assignment(&mut self, assignment: UntypedAssignment) -> TypedAssignment { let Assignment { pattern, value, @@ -1209,40 +1241,65 @@ impl<'a, 'b> ExprTyper<'a, 'b> { annotation, location, } = assignment; - let value = self.in_new_scope(|value_typer| value_typer.infer(*value))?; + let value_location = value.location(); + let value = match self.in_new_scope(|value_typer| value_typer.infer(*value)) { + Ok(value) => value, + Err(error) => self.error_expr_with_rigid_names(value_location, error), + }; + let value_typ = value.type_(); // Ensure the pattern matches the type of the value - let pattern = pattern::PatternTyper::new(self.environment, &self.hydrator) - .unify(pattern, value_typ.clone())?; + let pattern_location = pattern.location(); + let pattern = match pattern::PatternTyper::new(self.environment, &self.hydrator) + .unify(pattern, value_typ.clone()) + { + Ok(pattern) => pattern, + Err(error) => { + self.error_pattern_with_rigid_names(pattern_location, error, value_typ.clone()) + } + }; // Check that any type annotation is accurate. if let Some(annotation) = &annotation { - let ann_typ = self + match self .type_from_ast(annotation) - .map(|t| self.instantiate(t, &mut hashmap![]))?; - unify(ann_typ, value_typ.clone()) - .map_err(|e| convert_unify_error(e, value.type_defining_location()))?; + .map(|t| self.instantiate(t, &mut hashmap![])) + { + Ok(ann_typ) => { + if let Err(error) = unify(ann_typ, value_typ.clone()) + .map_err(|e| convert_unify_error(e, value.type_defining_location())) + { + self.error_with_rigid_names(error); + } + } + Err(error) => { + self.error_with_rigid_names(error); + } + } } // Do not perform exhaustiveness checking if user explicitly used `let assert ... = ...`. let exhaustiveness_check = self.check_let_exhaustiveness(location, value.type_(), &pattern); - match kind { - AssignmentKind::Let => exhaustiveness_check?, - AssignmentKind::Assert { location } if exhaustiveness_check.is_ok() => self + match (kind, exhaustiveness_check) { + (AssignmentKind::Let, Ok(_)) => {} + (AssignmentKind::Let, Err(e)) => { + self.error_with_rigid_names(e); + } + (AssignmentKind::Assert { location }, Ok(_)) => self .environment .warnings .emit(Warning::RedundantAssertAssignment { location }), - AssignmentKind::Assert { .. } => {} + (AssignmentKind::Assert { .. }, _) => {} } - Ok(Assignment { + Assignment { location, annotation, kind, pattern, value: Box::new(value), - }) + } } fn infer_case( @@ -1708,6 +1765,172 @@ impl<'a, 'b> ExprTyper<'a, 'b> { }) } + ClauseGuard::AddInt { + location, + left, + right, + .. + } => { + let left = self.infer_clause_guard(*left)?; + unify(int(), left.type_()).map_err(|e| convert_unify_error(e, left.location()))?; + let right = self.infer_clause_guard(*right)?; + unify(int(), right.type_()) + .map_err(|e| convert_unify_error(e, right.location()))?; + Ok(ClauseGuard::AddInt { + location, + left: Box::new(left), + right: Box::new(right), + }) + } + + ClauseGuard::AddFloat { + location, + left, + right, + .. + } => { + let left = self.infer_clause_guard(*left)?; + unify(float(), left.type_()) + .map_err(|e| convert_unify_error(e, left.location()))?; + let right = self.infer_clause_guard(*right)?; + unify(float(), right.type_()) + .map_err(|e| convert_unify_error(e, right.location()))?; + Ok(ClauseGuard::AddFloat { + location, + left: Box::new(left), + right: Box::new(right), + }) + } + + ClauseGuard::SubInt { + location, + left, + right, + .. + } => { + let left = self.infer_clause_guard(*left)?; + unify(int(), left.type_()).map_err(|e| convert_unify_error(e, left.location()))?; + let right = self.infer_clause_guard(*right)?; + unify(int(), right.type_()) + .map_err(|e| convert_unify_error(e, right.location()))?; + Ok(ClauseGuard::SubInt { + location, + left: Box::new(left), + right: Box::new(right), + }) + } + + ClauseGuard::SubFloat { + location, + left, + right, + .. + } => { + let left = self.infer_clause_guard(*left)?; + unify(float(), left.type_()) + .map_err(|e| convert_unify_error(e, left.location()))?; + let right = self.infer_clause_guard(*right)?; + unify(float(), right.type_()) + .map_err(|e| convert_unify_error(e, right.location()))?; + Ok(ClauseGuard::SubFloat { + location, + left: Box::new(left), + right: Box::new(right), + }) + } + + ClauseGuard::MultInt { + location, + left, + right, + .. + } => { + let left = self.infer_clause_guard(*left)?; + unify(int(), left.type_()).map_err(|e| convert_unify_error(e, left.location()))?; + let right = self.infer_clause_guard(*right)?; + unify(int(), right.type_()) + .map_err(|e| convert_unify_error(e, right.location()))?; + Ok(ClauseGuard::MultInt { + location, + left: Box::new(left), + right: Box::new(right), + }) + } + + ClauseGuard::MultFloat { + location, + left, + right, + .. + } => { + let left = self.infer_clause_guard(*left)?; + unify(float(), left.type_()) + .map_err(|e| convert_unify_error(e, left.location()))?; + let right = self.infer_clause_guard(*right)?; + unify(float(), right.type_()) + .map_err(|e| convert_unify_error(e, right.location()))?; + Ok(ClauseGuard::MultFloat { + location, + left: Box::new(left), + right: Box::new(right), + }) + } + + ClauseGuard::DivInt { + location, + left, + right, + .. + } => { + let left = self.infer_clause_guard(*left)?; + unify(int(), left.type_()).map_err(|e| convert_unify_error(e, left.location()))?; + let right = self.infer_clause_guard(*right)?; + unify(int(), right.type_()) + .map_err(|e| convert_unify_error(e, right.location()))?; + Ok(ClauseGuard::DivInt { + location, + left: Box::new(left), + right: Box::new(right), + }) + } + + ClauseGuard::DivFloat { + location, + left, + right, + .. + } => { + let left = self.infer_clause_guard(*left)?; + unify(float(), left.type_()) + .map_err(|e| convert_unify_error(e, left.location()))?; + let right = self.infer_clause_guard(*right)?; + unify(float(), right.type_()) + .map_err(|e| convert_unify_error(e, right.location()))?; + Ok(ClauseGuard::DivFloat { + location, + left: Box::new(left), + right: Box::new(right), + }) + } + + ClauseGuard::RemainderInt { + location, + left, + right, + .. + } => { + let left = self.infer_clause_guard(*left)?; + unify(int(), left.type_()).map_err(|e| convert_unify_error(e, left.location()))?; + let right = self.infer_clause_guard(*right)?; + unify(int(), right.type_()) + .map_err(|e| convert_unify_error(e, right.location()))?; + Ok(ClauseGuard::RemainderInt { + location, + left: Box::new(left), + right: Box::new(right), + }) + } + ClauseGuard::Constant(constant) => { Ok(ClauseGuard::Constant(self.infer_const(&None, constant))) } @@ -2771,12 +2994,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } let body = body_typer.infer_statements(body); - let body_rigid_names = body_typer.hydrator.rigid_names(); - let body = body.map_err(|e| e.with_unify_error_rigid_names(&body_rigid_names))?; // Check that any return type is accurate. if let Some(return_type) = return_type { unify(return_type, body.last().type_()).map_err(|e| { + let body_rigid_names = body_typer.hydrator.rigid_names(); e.return_annotation_mismatch() .into_error(body.last().type_defining_location()) .with_unify_error_rigid_names(&body_rigid_names) @@ -2793,7 +3015,7 @@ impl<'a, 'b> ExprTyper<'a, 'b> { location: SrcSpan, ) -> Result { self.in_new_scope(|typer| { - let statements = typer.infer_statements(statements)?; + let statements = typer.infer_statements(statements); Ok(TypedExpr::Block { statements, location, @@ -3001,7 +3223,7 @@ struct UseCall { arguments: Vec>, } -fn get_use_expression_call(call: UntypedExpr) -> Result { +fn get_use_expression_call(call: UntypedExpr) -> UseCall { // Ensure that the use's call is of the right structure. i.e. it is a // call to a function. match call { @@ -3009,15 +3231,15 @@ fn get_use_expression_call(call: UntypedExpr) -> Result { fun: function, arguments, .. - } => Ok(UseCall { + } => UseCall { arguments, function, - }), + }, - other => Ok(UseCall { + other => UseCall { function: Box::new(other), arguments: vec![], - }), + }, } } @@ -3085,7 +3307,8 @@ impl UseAssignments { | Pattern::Constructor { .. } | Pattern::Tuple { .. } | Pattern::BitArray { .. } - | Pattern::StringPrefix { .. }) => { + | Pattern::StringPrefix { .. } + | Pattern::Invalid { .. }) => { let name: EcoString = format!("{USE_ASSIGNMENT_VARIABLE}{index}").into(); assignments.function_arguments.push(Arg { location, diff --git a/compiler-core/src/type_/pattern.rs b/compiler-core/src/type_/pattern.rs index 7694d20aa..72ea5ef41 100644 --- a/compiler-core/src/type_/pattern.rs +++ b/compiler-core/src/type_/pattern.rs @@ -212,6 +212,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { name, location, }), + Pattern::Invalid { location, .. } => Ok(Pattern::Invalid { type_, location }), Pattern::Variable { name, location, .. } => { self.insert_variable(&name, type_.clone(), location) @@ -419,7 +420,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { module, name, arguments: mut pattern_args, - with_spread, + spread, .. } => { // Register the value as seen for detection of unused values @@ -433,25 +434,16 @@ impl<'a, 'b> PatternTyper<'a, 'b> { match cons.field_map() { // The fun has a field map so labelled arguments may be present and need to be reordered. Some(field_map) => { - if with_spread { + if let Some(spread_location) = spread { // Using the spread operator when you have already provided variables for all of the // record's fields throws an error if pattern_args.len() == field_map.arity as usize { return Err(Error::UnnecessarySpreadOperator { - location: SrcSpan { - start: location.end - 3, - end: location.end - 1, - }, + location: spread_location, arity: field_map.arity as usize, }); } - // The location of the spread operator itself - let spread_location = SrcSpan { - start: location.end - 3, - end: location.end - 1, - }; - // Insert discard variables to match the unspecified fields // In order to support both positional and labelled arguments we have to insert // them after all positional variables and before the labelled ones. This means @@ -463,6 +455,52 @@ impl<'a, 'b> PatternTyper<'a, 'b> { .position(|a| a.label.is_some()) .unwrap_or(pattern_args.len()); + // In Gleam we can pass in positional unlabelled args to a constructor + // even if the field was defined as labelled + // + // pub type Wibble { + // Wibble(Int, two: Int, three: Int, four: Int) + // } + // Wibble(1, 2, 3, 4) + // + // When using `..` to ignore some fields the compiler needs to add a + // placeholder implicit discard pattern for each one of the ignored + // arguments. To give those discards the proper missing label we need to + // know how many of the labelled fields were provided as unlabelled. + // + // That's why we want to keep track of the number of unlabelled argument + // that have been supplied to the pattern and all the labels that have + // been explicitly supplied. + // + // Wibble(a, b, four: c, ..) + // ┬─── ┬────── + // │ ╰ We supplied 1 labelled arg + // ╰ We supplied 2 unlabelled args + // + let supplied_unlabelled_args = index_of_first_labelled_arg; + let supplied_labelled_args = pattern_args + .iter() + .filter_map(|l| l.label.clone()) + .collect::>(); + let constructor_unlabelled_args = + field_map.arity - field_map.fields.len() as u32; + let labelled_arguments_supplied_as_unlabelled = + supplied_unlabelled_args + .saturating_sub(constructor_unlabelled_args as usize); + + let mut missing_labels = field_map + .fields + .iter() + // We take the labels in order of definition in the constructor... + .sorted_by_key(|(_, pos)| *pos) + .map(|(label, _)| label.clone()) + // ...and then remove the ones that were supplied as unlabelled + // positional arguments... + .skip(labelled_arguments_supplied_as_unlabelled) + // ... lastly we still need to remove all those labels that + // were explicitly supplied in the pattern. + .filter(|label| !supplied_labelled_args.contains(label)); + while pattern_args.len() < field_map.arity as usize { let new_call_arg = CallArg { value: Pattern::Discard { @@ -471,8 +509,8 @@ impl<'a, 'b> PatternTyper<'a, 'b> { type_: (), }, location: spread_location, - label: None, - implicit: false, + label: missing_labels.next(), + implicit: true, }; pattern_args.insert(index_of_first_labelled_arg, new_call_arg); @@ -486,13 +524,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { // The fun has no field map and so we error if arguments have been labelled assert_no_labelled_arguments(&pattern_args)?; - if with_spread { - // The location of the spread operator itself - let spread_location = SrcSpan { - start: location.end - 3, - end: location.end - 1, - }; - + if let Some(spread_location) = spread { if let ValueConstructorVariant::Record { arity, .. } = &cons.variant { while pattern_args.len() < usize::from(*arity) { pattern_args.push(CallArg { @@ -524,7 +556,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { documentation: documentation.clone(), name: name.clone(), field_map: cons.field_map().cloned(), - module: Some(module.clone()), + module: module.clone(), location: *location, constructor_index: *constructor_index, }, @@ -581,7 +613,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { name, arguments: pattern_args, constructor: Inferred::Known(constructor), - with_spread, + spread, type_: retrn.clone(), }) } else { @@ -604,7 +636,7 @@ impl<'a, 'b> PatternTyper<'a, 'b> { name, arguments: vec![], constructor: Inferred::Known(constructor), - with_spread, + spread, type_: instantiated_constructor_type, }) } else { diff --git a/compiler-core/src/type_/pipe.rs b/compiler-core/src/type_/pipe.rs index 8273402f9..39e12ab1c 100644 --- a/compiler-core/src/type_/pipe.rs +++ b/compiler-core/src/type_/pipe.rs @@ -1,7 +1,9 @@ use self::expression::CallKind; use super::*; -use crate::ast::{Assignment, AssignmentKind, TypedAssignment, UntypedExpr, PIPE_VARIABLE}; +use crate::ast::{ + Assignment, AssignmentKind, Statement, TypedAssignment, UntypedExpr, PIPE_VARIABLE, +}; use vec1::Vec1; #[derive(Debug)] @@ -33,12 +35,7 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> { expressions: Vec1, ) -> Result { let size = expressions.len(); - let end = &expressions[..] - .last() - // The vec is non-empty, this indexing can never fail - .expect("Empty pipeline in typer") - .location() - .end; + let end = expressions.last().location().end; let mut expressions = expressions.into_iter(); let first = expr_typer.infer(expressions.next().expect("Empty pipeline in typer"))?; let mut typer = Self { @@ -48,7 +45,7 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> { argument_location: first.location(), location: SrcSpan { start: first.location().start, - end: *end, + end, }, assignments: Vec::with_capacity(size), }; @@ -90,6 +87,8 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> { .warn_for_unreachable_code(call.location(), PanicPosition::PreviousExpression); } + self.warn_if_call_first_argument_is_hole(&call); + let call = match call { // left |> right(..args) UntypedExpr::Call { @@ -120,6 +119,7 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> { self.push_assignment(call); } } + Ok(finally.expect("Empty pipeline in typer")) } @@ -316,4 +316,26 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> { _ => false, } } + + fn warn_if_call_first_argument_is_hole(&mut self, call: &UntypedExpr) { + if let UntypedExpr::Fn { + is_capture: true, + body, + .. + } = &call + { + if let Statement::Expression(UntypedExpr::Call { arguments, .. }) = body.first() { + match arguments.as_slice() { + [first] | [first, ..] if first.is_capture_hole() => { + self.expr_typer.environment.warnings.emit( + Warning::RedundantPipeFunctionCapture { + location: first.location, + }, + ) + } + _ => (), + } + } + } + } } diff --git a/compiler-core/src/type_/printer.rs b/compiler-core/src/type_/printer.rs new file mode 100644 index 000000000..b593b4e09 --- /dev/null +++ b/compiler-core/src/type_/printer.rs @@ -0,0 +1,571 @@ +#![allow(dead_code)] + +use ecow::EcoString; +use im::{HashMap, HashSet}; +use std::sync::Arc; + +use crate::type_::{Type, TypeVar}; + +/// This class keeps track of what names are used for modules in the current +/// scope, so they can be printed in errors, etc. +/// +#[derive(Debug)] +pub struct TypeNames { + uid: u64, + current_module: EcoString, + + /// Types that exist in the current module, either defined or imported in an + /// unqualified fashion. + /// + /// key: (Defining module name, type name) + /// value: Alias name + /// + /// # Example 1 + /// + /// ```gleam + /// type Wibble = wobble.Woo + /// ``` + /// would result in + /// - key: `("wibble", "Woo")` + /// - value: `"Wibble"` + /// + /// # Example 2 + /// + /// ```gleam + /// import some/module.{type Wibble} + /// ``` + /// would result in + /// - key: `("some/module", "Wibble")` + /// - value: `"Wibble"` + /// + /// # Example 3 + /// + /// ```gleam + /// import some/module.{type Wibble as Wobble} + /// ``` + /// would result in + /// - key: `("some/module", "Wibble")` + /// - value: `"Wobble"` + /// + local_types: HashMap<(EcoString, EcoString), EcoString>, + + /// Mapping of imported modules to their locally used named + /// + /// key: The name of the module + /// value: The name the module is aliased to + /// + /// # Example 1 + /// + /// ```gleam + /// import mod1 as my_mod + /// ``` + /// would result in: + /// - key: "mod1" + /// - value: "my_mod" + /// + /// # Example 2 + /// + /// ```gleam + /// import mod1 + /// ``` + /// would result in: + /// - key: "mod1" + /// - value: "mod1" + /// + imported_modules: HashMap, + + /// Generic type parameters that have been annotated in the current + /// function. + /// + /// key: The id of generic type that was annotated + /// value: The name that is used for the generic type in the annotation. + /// + /// # Example 1 + /// + /// ```gleam + /// fn equal(x: something, y: something) -> Bool { + /// arg1 == arg2 + /// } + /// ``` + /// + /// key: + /// value: `"something"` + /// + type_variables: HashMap, + type_variable_names: HashSet, +} + +impl TypeNames { + pub fn new(current_module: EcoString) -> Self { + Self { + uid: Default::default(), + current_module, + local_types: Default::default(), + imported_modules: Default::default(), + type_variables: Default::default(), + type_variable_names: Default::default(), + } + } + + /// Record a named type in this module. + pub fn named_type_in_scope( + &mut self, + module_name: EcoString, + type_name: EcoString, + local_alias: EcoString, + ) { + _ = self + .local_types + .insert((module_name, type_name), local_alias); + } + + /// Record a type variable in this module. + pub fn type_variable_in_scope(&mut self, id: u64, local_alias: EcoString) { + _ = self.type_variables.insert(id, local_alias.clone()); + _ = self.type_variable_names.insert(local_alias); + } + + /// Record an imported module in this module. + pub fn imported_module(&mut self, module_name: EcoString, module_alias: EcoString) { + _ = self.imported_modules.insert(module_name, module_alias) + } + + /// Get the name and optional module qualifier for a named type. + pub fn named_type<'a>( + &'a self, + module: &'a EcoString, + name: &'a EcoString, + ) -> NamedTypeNames<'a> { + let key = (module.clone(), name.clone()); + + // There is a local name for this type, use that. + if let Some(name) = self.local_types.get(&key) { + return NamedTypeNames::Unqualified(name.as_str()); + } + + // This type is from a module that has been imported + if let Some(module) = self.imported_modules.get(module) { + return NamedTypeNames::Qualified(module, name.as_str()); + }; + + return NamedTypeNames::Unimported(name.as_str()); + } + + /// A suitable name of a type variable. + pub fn type_variable(&mut self, id: u64) -> EcoString { + if let Some(name) = self.type_variables.get(&id) { + return name.clone(); + } + + loop { + let name = self.next_letter(); + if !self.type_variable_names.contains(&name) { + _ = self.type_variable_names.insert(name.clone()); + _ = self.type_variables.insert(id, name.clone()); + return name; + } + } + } + + fn next_letter(&mut self) -> EcoString { + let alphabet_length = 26; + let char_offset = 97; + let mut chars = vec![]; + let mut n; + let mut rest = self.uid; + + loop { + n = rest % alphabet_length; + rest /= alphabet_length; + chars.push((n as u8 + char_offset) as char); + + if rest == 0 { + break; + } + rest -= 1 + } + + self.uid += 1; + chars.into_iter().rev().collect() + } +} + +#[derive(Debug)] +pub enum NamedTypeNames<'a> { + /// This type is from a module that has not been imported in this module. + Unimported(&'a str), + /// This type has been imported in an unqualifid fashion in this module. + Unqualified(&'a str), + /// This type is from a module that has been imported. + Qualified(&'a str, &'a str), +} + +/// A type printer that does not wrap and indent, but does take into account the +/// names that types and modules have been aliased with in the current module. +#[derive(Debug)] +pub struct Printer<'a> { + names: &'a mut TypeNames, +} + +impl<'a> Printer<'a> { + pub fn new(names: &'a mut TypeNames) -> Self { + Printer { names } + } + + pub fn print_type(&mut self, type_: &Type) -> EcoString { + let mut buffer = EcoString::new(); + self.print(type_, &mut buffer); + buffer + } + + fn print(&mut self, type_: &Type, buffer: &mut EcoString) { + match type_ { + Type::Named { + name, args, module, .. + } => { + let (module, name) = match self.names.named_type(module, name) { + NamedTypeNames::Qualified(m, n) => (Some(m), n), + NamedTypeNames::Unqualified(n) => (None, n), + // TODO: indicate that the module is not import and as such + // needs to be, as well as how. + NamedTypeNames::Unimported(n) => { + (Some(module.split('/').last().unwrap_or(module)), n) + } + }; + + if let Some(module) = module { + buffer.push_str(module); + buffer.push('.'); + } + buffer.push_str(name); + + if !args.is_empty() { + buffer.push('('); + self.print_arguments(args, buffer); + buffer.push(')'); + } + } + + Type::Fn { args, retrn } => { + buffer.push_str("fn("); + self.print_arguments(args, buffer); + buffer.push_str(") -> "); + self.print(retrn, buffer); + } + + Type::Var { type_: typ, .. } => match *typ.borrow() { + TypeVar::Link { type_: ref typ, .. } => self.print(typ, buffer), + TypeVar::Unbound { id, .. } | TypeVar::Generic { id, .. } => { + buffer.push_str(&self.names.type_variable(id)) + } + }, + + Type::Tuple { elems, .. } => { + buffer.push_str("#("); + self.print_arguments(elems, buffer); + buffer.push(')'); + } + } + } + + fn print_arguments(&mut self, args: &[Arc], typ_str: &mut EcoString) { + for (i, arg) in args.iter().enumerate() { + self.print(arg, typ_str); + if i < args.len() - 1 { + typ_str.push_str(", "); + } + } + } +} + +#[test] +fn test_local_type() { + let mut names = TypeNames::new("module".into()); + names.named_type_in_scope("mod".into(), "Tiger".into(), "Cat".into()); + let mut printer = Printer::new(&mut names); + + let typ = Type::Named { + name: "Tiger".into(), + args: vec![], + module: "mod".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }; + + assert_eq!(printer.print_type(&typ), "Cat"); +} + +#[test] +fn test_generic_type_annotation() { + let mut names = TypeNames::new("module".into()); + names.type_variable_in_scope(0, "one".into()); + let mut printer = Printer::new(&mut names); + + let typ = Type::Var { + type_: Arc::new(std::cell::RefCell::new(TypeVar::Generic { id: 0 })), + }; + + assert_eq!(printer.print_type(&typ), "one"); +} + +#[test] +fn test_generic_type_var() { + let mut names = TypeNames::new("module".into()); + let mut printer = Printer::new(&mut names); + + let typ = Type::Var { + type_: Arc::new(std::cell::RefCell::new(TypeVar::Unbound { id: 0 })), + }; + + let typ2 = Type::Var { + type_: Arc::new(std::cell::RefCell::new(TypeVar::Unbound { id: 1 })), + }; + + assert_eq!(printer.print_type(&typ), "a"); + assert_eq!(printer.print_type(&typ2), "b"); +} + +#[test] +fn test_tuple_type() { + let mut names = TypeNames::new("module".into()); + let mut printer = Printer::new(&mut names); + + let typ = Type::Tuple { + elems: vec![ + Arc::new(Type::Named { + name: "Int".into(), + args: vec![], + module: "gleam".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }), + Arc::new(Type::Named { + name: "String".into(), + args: vec![], + module: "gleam".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }), + ], + }; + + assert_eq!(printer.print_type(&typ), "#(gleam.Int, gleam.String)"); +} + +#[test] +fn test_fn_type() { + let mut names = TypeNames::new("module".into()); + names.named_type_in_scope("gleam".into(), "Int".into(), "Int".into()); + names.named_type_in_scope("gleam".into(), "Bool".into(), "Bool".into()); + let mut printer = Printer::new(&mut names); + + let typ = Type::Fn { + args: vec![ + Arc::new(Type::Named { + name: "Int".into(), + args: vec![], + module: "gleam".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }), + Arc::new(Type::Named { + name: "String".into(), + args: vec![], + module: "gleam".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }), + ], + retrn: Arc::new(Type::Named { + name: "Bool".into(), + args: vec![], + module: "gleam".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }), + }; + + assert_eq!(printer.print_type(&typ), "fn(Int, gleam.String) -> Bool"); +} + +#[test] +fn test_module_alias() { + let mut names = TypeNames::new("module".into()); + names.imported_module("mod1".into(), "animals".into()); + let mut printer = Printer::new(&mut names); + + let typ = Type::Named { + name: "Cat".into(), + args: vec![], + module: "mod1".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }; + + assert_eq!(printer.print_type(&typ), "animals.Cat"); +} + +#[test] +fn test_type_alias_and_generics() { + let mut names = TypeNames::new("module".into()); + + names.named_type_in_scope("mod".into(), "Tiger".into(), "Cat".into()); + + names.type_variable_in_scope(0, "one".into()); + + let mut printer = Printer::new(&mut names); + + let typ = Type::Named { + name: "Tiger".into(), + args: vec![Arc::new(Type::Var { + type_: Arc::new(std::cell::RefCell::new(TypeVar::Generic { id: 0 })), + })], + module: "mod".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }; + + assert_eq!(printer.print_type(&typ), "Cat(one)"); +} + +#[test] +fn test_unqualified_import_and_generic() { + let mut names = TypeNames::new("module".into()); + + names.named_type_in_scope("mod".into(), "Cat".into(), "C".into()); + + names.type_variable_in_scope(0, "one".into()); + + let mut printer = Printer::new(&mut names); + + let typ = Type::Named { + name: "Cat".into(), + args: vec![Arc::new(Type::Var { + type_: Arc::new(std::cell::RefCell::new(TypeVar::Generic { id: 0 })), + })], + module: "mod".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }; + + assert_eq!(printer.print_type(&typ), "C(one)"); +} + +#[test] +fn nested_module() { + let mut names = TypeNames::new("module".into()); + let mut printer = Printer::new(&mut names); + let typ = Type::Named { + name: "Cat".into(), + args: vec![], + module: "one/two/three".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }; + + assert_eq!(printer.print_type(&typ), "three.Cat"); +} + +#[test] +fn test_unqualified_import_and_module_alias() { + let mut names = TypeNames::new("module".into()); + + names.imported_module("mod1".into(), "animals".into()); + + let _ = names + .local_types + .insert(("mod1".into(), "Cat".into()), "C".into()); + + let mut printer = Printer::new(&mut names); + + let typ = Type::Named { + name: "Cat".into(), + args: vec![], + module: "mod1".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }; + + assert_eq!(printer.print_type(&typ), "C"); +} + +#[test] +fn test_module_imports() { + let mut names = TypeNames::new("module".into()); + names.imported_module("mod".into(), "animals".into()); + let _ = names + .local_types + .insert(("mod2".into(), "Cat".into()), "Cat".into()); + + let mut printer = Printer::new(&mut names); + + let typ = Type::Named { + name: "Cat".into(), + args: vec![], + module: "mod".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }; + + let typ1 = Type::Named { + name: "Cat".into(), + args: vec![], + module: "mod2".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }; + + assert_eq!(printer.print_type(&typ), "animals.Cat"); + assert_eq!(printer.print_type(&typ1), "Cat"); +} + +#[test] +fn test_multiple_generic_annotations() { + let mut names = TypeNames::new("module".into()); + + names.type_variable_in_scope(0, "one".into()); + names.type_variable_in_scope(1, "two".into()); + + let mut printer = Printer::new(&mut names); + + let typ = Type::Named { + name: "Tiger".into(), + args: vec![ + Arc::new(Type::Var { + type_: Arc::new(std::cell::RefCell::new(TypeVar::Generic { id: 0 })), + }), + Arc::new(Type::Var { + type_: Arc::new(std::cell::RefCell::new(TypeVar::Generic { id: 1 })), + }), + ], + module: "tigermodule".into(), + publicity: crate::ast::Publicity::Public, + package: "".into(), + }; + + let typ1 = Type::Var { + type_: Arc::new(std::cell::RefCell::new(TypeVar::Generic { id: 2 })), + }; + + assert_eq!(printer.print_type(&typ), "tigermodule.Tiger(one, two)"); + assert_eq!(printer.print_type(&typ1), "a"); +} + +#[test] +fn test_variable_name_already_in_scope() { + let mut names = TypeNames::new("module".into()); + + names.type_variable_in_scope(1, "a".into()); + names.type_variable_in_scope(2, "b".into()); + + let mut printer = Printer::new(&mut names); + + let type_ = |id| Type::Var { + type_: Arc::new(std::cell::RefCell::new(TypeVar::Generic { id })), + }; + + assert_eq!(printer.print_type(&type_(0)), "c"); + assert_eq!(printer.print_type(&type_(1)), "a"); + assert_eq!(printer.print_type(&type_(2)), "b"); + assert_eq!(printer.print_type(&type_(3)), "d"); +} diff --git a/compiler-core/src/type_/tests.rs b/compiler-core/src/type_/tests.rs index 46ad643b8..b5ac7d5c5 100644 --- a/compiler-core/src/type_/tests.rs +++ b/compiler-core/src/type_/tests.rs @@ -164,25 +164,19 @@ macro_rules! assert_with_module_error { }; } -fn get_warnings(src: &str, deps: Vec>) -> Vec { +fn get_warnings(src: &str, deps: Vec>) -> Vec { let warnings = VectorWarningEmitterIO::default(); _ = compile_module("test_module", src, Some(Arc::new(warnings.clone())), deps).unwrap(); - warnings - .take() - .into_iter() - .map(|warning| match warning { - crate::Warning::Type { warning, .. } => warning, - crate::Warning::InvalidSource { .. } => panic!("Invalid module file name"), - }) - .collect_vec() + warnings.take().into_iter().collect_vec() } fn get_printed_warnings(src: &str, deps: Vec>) -> String { - let warnings = get_warnings(src, deps); + print_warnings(get_warnings(src, deps)) +} + +fn print_warnings(warnings: Vec) -> String { let mut nocolor = termcolor::Buffer::no_color(); for warning in warnings { - let path = Utf8PathBuf::from("/src/warning/wrn.gleam"); - let warning = warning.into_warning(path, src.into()); warning.pretty(&mut nocolor); } String::from_utf8(nocolor.into_inner()).expect("Error printing produced invalid utf8") @@ -190,7 +184,7 @@ fn get_printed_warnings(src: &str, deps: Vec>) -> String { #[macro_export] macro_rules! assert_warnings_with_imports { - ($(($name:literal, $module_src:literal)),+; $src:literal, $($warning:expr),+) => { + ($(($name:literal, $module_src:literal)),+; $src:literal,) => { let warnings = $crate::type_::tests::get_warnings( $src, vec![ @@ -198,7 +192,8 @@ macro_rules! assert_warnings_with_imports { ], ); assert!(!warnings.is_empty()); - assert_eq!(vec![$($warning),*], warnings); + let output = $crate::type_::tests::print_warnings(warnings); + insta::assert_snapshot!(insta::internals::AutoName, output, $src); }; } @@ -227,21 +222,6 @@ macro_rules! assert_warning { assert!(!output.is_empty()); insta::assert_snapshot!(insta::internals::AutoName, output, $src); }; - - ($src:expr, $warning:expr $(,)?) => { - let warnings = $crate::type_::tests::get_warnings($src, vec![]); - assert!(!warnings.is_empty()); - assert_eq!($warning, warnings[0]); - }; - - ($(($name:expr, $module_src:literal)),+, $src:expr, $warning:expr $(,)?) => { - let warnings = $crate::type_::tests::get_warnings( - $src, - vec![$(("thepackage", $name, $module_src)),*], - ); - assert!(!warnings.is_empty()); - assert_eq!($warning, warnings[0]); - }; } #[macro_export] @@ -290,14 +270,9 @@ fn compile_statement_sequence( errors, ) .infer_statements(ast); - match (res, Vec1::try_from_vec(errors.to_vec())) { - (Ok(res), Err(_)) => Ok(res), - (Ok(_), Ok(errors)) => Err(errors), - (Err(err), Ok(mut errors)) => { - errors.push(err); - Err(errors) - } - (Err(err), Err(_)) => Err(Vec1::new(err)), + match Vec1::try_from_vec(errors.to_vec()) { + Err(_) => Ok(res), + Ok(errors) => Err(errors), } } @@ -379,12 +354,9 @@ pub fn compile_module_with_opts( ) -> Result> { let ids = UniqueIdGenerator::new(); let mut modules = im::HashMap::new(); - let warnings = TypeWarningEmitter::new( - Utf8PathBuf::new(), - "".into(), - WarningEmitter::new( - warnings.unwrap_or_else(|| Arc::new(VectorWarningEmitterIO::default())), - ), + + let emitter = WarningEmitter::new( + warnings.unwrap_or_else(|| Arc::new(VectorWarningEmitterIO::default())), ); // DUPE: preludeinsertion @@ -395,7 +367,9 @@ pub fn compile_module_with_opts( let mut direct_dependencies = HashMap::from_iter(vec![]); for (package, name, module_src) in dep { - let parsed = crate::parse::parse_module(module_src).expect("syntax error"); + let parsed = + crate::parse::parse_module(Utf8PathBuf::from("test/path"), module_src, &emitter) + .expect("syntax error"); let mut ast = parsed.module; ast.name = name.into(); let line_numbers = LineNumbers::new(module_src); @@ -420,11 +394,14 @@ pub fn compile_module_with_opts( } } - let parsed = crate::parse::parse_module(src).expect("syntax error"); + let parsed = crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &emitter) + .expect("syntax error"); let mut ast = parsed.module; ast.name = module_name.into(); let mut config = PackageConfig::default(); config.name = "thepackage".into(); + + let warnings = TypeWarningEmitter::new("/src/warning/wrn.gleam".into(), src.into(), emitter); let inference_result = crate::analyse::ModuleAnalyzerConstructor::<()> { target, ids: &ids, @@ -471,7 +448,9 @@ pub fn module_error_with_target( } pub fn syntax_error(src: &str) -> String { - let error = crate::parse::parse_module(src).expect_err("should trigger an error when parsing"); + let error = + crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) + .expect_err("should trigger an error when parsing"); let error = Error::Parse { src: src.into(), path: Utf8PathBuf::from("/src/one/two.gleam"), diff --git a/compiler-core/src/type_/tests/custom_types.rs b/compiler-core/src/type_/tests/custom_types.rs index 3a229709d..1878c766f 100644 --- a/compiler-core/src/type_/tests/custom_types.rs +++ b/compiler-core/src/type_/tests/custom_types.rs @@ -1,4 +1,4 @@ -use crate::{assert_module_error, assert_module_infer, assert_warning}; +use crate::{assert_module_error, assert_module_infer, assert_warning, assert_with_module_error}; // https://github.com/gleam-lang/gleam/issues/2215 #[test] @@ -59,3 +59,12 @@ type Three(a, a) { "# ); } + +#[test] +fn conflict_with_import() { + // We cannot declare a type with the same name as an imported type + assert_with_module_error!( + ("wibble", "pub type A { B }"), + "import wibble.{type A} type A { C }", + ); +} diff --git a/compiler-core/src/type_/tests/errors.rs b/compiler-core/src/type_/tests/errors.rs index 59fc11b2b..eb8249dbe 100644 --- a/compiler-core/src/type_/tests/errors.rs +++ b/compiler-core/src/type_/tests/errors.rs @@ -16,17 +16,17 @@ fn main() { #[test] fn bit_arrays2() { - assert_error!("let <> = <<1>> x"); + assert_error!("let <> = <<1>>"); } #[test] fn bit_arrays3() { - assert_error!("let <> = <<1>> x"); + assert_error!("let <> = <<1>>"); } #[test] fn bit_arrays4() { - assert_error!("let <> = <<1>> x"); + assert_error!("let <> = <<1>>"); } #[test] @@ -369,12 +369,12 @@ fn recursive_var() { #[test] fn true_fn() { - assert_error!("let True(x) = 1 x"); + assert_error!("let True(x) = 1"); } #[test] fn ok_2_args() { - assert_error!("let Ok(1, x) = 1 x"); + assert_error!("let Ok(1, x) = 1"); } #[test] @@ -394,7 +394,7 @@ fn tuple_int_float() { #[test] fn tuple_int() { - assert_error!("let #(a, b) = 1 a"); + assert_error!("let #(a, b) = 1"); } #[test] @@ -666,7 +666,7 @@ pub fn x() { id(1, 1.0) }" fn module_could_not_unify4() { assert_module_error!( " -fn bar() -> Int { +fn wobble() -> Int { 5 } @@ -675,7 +675,7 @@ fn run(one: fn() -> String) { } fn demo() { - run(bar) + run(wobble) }" ); } @@ -684,7 +684,7 @@ fn demo() { fn module_could_not_unify5() { assert_module_error!( " -fn bar(x: Int) -> Int { +fn wobble(x: Int) -> Int { x * 5 } @@ -693,7 +693,7 @@ fn run(one: fn(String) -> Int) { } fn demo() { - run(bar) + run(wobble) }" ); } @@ -705,7 +705,7 @@ fn module_could_not_unify6() { #[test] fn module_could_not_unify7() { - assert_module_error!("fn main() { let assert 5: Int = \"\" 5 }"); + assert_module_error!("fn main() { let assert 5 = \"\" }"); } #[test] @@ -715,7 +715,7 @@ fn module_could_not_unify8() { #[test] fn module_could_not_unify9() { - assert_module_error!("fn main() { let [1, 2, ..x]: List(String) = [1,2,3] x }"); + assert_module_error!("fn main() { let assert [1, 2, ..x]: List(String) = [1,2,3] x }"); } #[test] @@ -889,7 +889,6 @@ type Triple { fn main() { let triple = Triple(1,2,3) let Triple(a, b, c, ..) = triple - a }" ); } @@ -1046,16 +1045,16 @@ fn duplicate() { 2 }" #[test] fn duplicate_const_const() { assert_module_error!( - "const foo = 1 -const foo = 2" + "const wibble = 1 +const wibble = 2" ); } #[test] fn duplicate_fn_fn() { assert_module_error!( - "fn foo() { 1 } -fn foo() { 2 }" + "fn wibble() { 1 } +fn wibble() { 2 }" ); } @@ -1064,9 +1063,9 @@ fn duplicate_extfn_extfn() { assert_module_error!( r#" @external(erlang, "module1", "function1") -fn foo() -> Float +fn wibble() -> Float @external(erlang, "module2", "function2") -fn foo() -> Float +fn wibble() -> Float "# ); } @@ -1076,19 +1075,19 @@ fn duplicate_extfn_fn() { assert_module_error!( " @external(erlang, \"module1\", \"function1\") -fn foo() -> Float +fn wibble() -> Float -fn foo() { 2 }" +fn wibble() { 2 }" ); } #[test] fn duplicate_fn_extfn() { assert_module_error!( - "fn foo() { 1 } + "fn wibble() { 1 } @external(erlang, \"module2\", \"function2\") -fn foo() -> Float +fn wibble() -> Float " ); } @@ -1096,10 +1095,10 @@ fn foo() -> Float #[test] fn duplicate_const_extfn() { assert_module_error!( - "const foo = 1 + "const wibble = 1 @external(erlang, \"module2\", \"function2\") -fn foo() -> Float +fn wibble() -> Float " ); } @@ -1109,25 +1108,25 @@ fn duplicate_extfn_const() { assert_module_error!( " @external(erlang, \"module1\", \"function1\") -fn foo() -> Float +fn wibble() -> Float -const foo = 2" +const wibble = 2" ); } #[test] fn duplicate_const_fn() { assert_module_error!( - "const foo = 1 -fn foo() { 2 }" + "const wibble = 1 +fn wibble() { 2 }" ); } #[test] fn duplicate_fn_const() { assert_module_error!( - "fn foo() { 1 } -const foo = 2" + "fn wibble() { 1 } +const wibble = 2" ); } @@ -1265,9 +1264,9 @@ fn wrong_type_var() { // A unification error should show the type var as named by user // See https://github.com/gleam-lang/gleam/issues/1256 assert_module_error!( - r#"fn foo(x: String) { x } + r#"fn wibble(x: String) { x } fn multi_result(x: some_name) { - foo(x) + wibble(x) }"# ); } @@ -1276,9 +1275,9 @@ fn multi_result(x: some_name) { fn wrong_type_arg() { assert_module_error!( r#" -fn foo(x: List(Int)) { x } +fn wibble(x: List(Int)) { x } fn main(y: List(something)) { - foo(y) + wibble(y) }"# ); } @@ -1523,10 +1522,10 @@ fn negate_string() { #[test] fn ambiguous_type_error() { assert_with_module_error!( - ("foo", "pub type Thing { Thing }"), - "import foo pub type Thing { Thing } + ("wibble", "pub type Thing { Thing }"), + "import wibble pub type Thing { Thing } pub fn main() { - [Thing] == [foo.Thing] + [Thing] == [wibble.Thing] }", ); } @@ -1534,13 +1533,13 @@ fn ambiguous_type_error() { #[test] fn ambiguous_import_error_no_unqualified() { assert_with_module_error!( - ("foo/sub", "pub fn bar() { 1 }"), - ("foo2/sub", "pub fn bar() { 1 }"), + ("wibble/sub", "pub fn wobble() { 1 }"), + ("wibble2/sub", "pub fn wobble() { 1 }"), " - import foo/sub - import foo2/sub + import wibble/sub + import wibble2/sub pub fn main() { - sub.bar() + sub.wobble() } ", ); @@ -1549,13 +1548,13 @@ fn ambiguous_import_error_no_unqualified() { #[test] fn ambiguous_import_error_with_unqualified() { assert_with_module_error!( - ("foo/sub", "pub fn bar() { 1 }"), - ("foo2/sub", "pub fn bar() { 1 }"), + ("wibble/sub", "pub fn wobble() { 1 }"), + ("wibble2/sub", "pub fn wobble() { 1 }"), " - import foo/sub - import foo2/sub.{bar} + import wibble/sub + import wibble2/sub.{wobble} pub fn main() { - sub.bar() + sub.wobble() } ", ); @@ -1565,16 +1564,16 @@ fn ambiguous_import_error_with_unqualified() { fn same_imports_multiple_times() { assert_with_module_error!( ( - "gleam/foo", + "gleam/wibble", " - pub fn bar() { 1 } + pub fn wobble() { 1 } pub fn zoo() { 1 } " ), " - import gleam/foo.{bar} - import gleam/foo.{zoo} - pub fn go() { bar() + zoo() } + import gleam/wibble.{wobble} + import gleam/wibble.{zoo} + pub fn go() { wobble() + zoo() } " ); } @@ -1805,12 +1804,12 @@ pub fn main(_x: two.Thing) { fn value_imported_as_type() { assert_with_module_error!( ( - "gleam/foo", - "pub type Bar { - Baz + "gleam/wibble", + "pub type Wibble { + Wobble }" ), - "import gleam/foo.{type Baz}" + "import gleam/wibble.{type Wobble}" ); } @@ -1818,12 +1817,12 @@ fn value_imported_as_type() { fn type_imported_as_value() { assert_with_module_error!( ( - "gleam/foo", - "pub type Bar { - Baz + "gleam/wibble", + "pub type Wibble { + Wobble }" ), - "import gleam/foo.{Bar}" + "import gleam/wibble.{Wibble}" ); } @@ -1881,7 +1880,7 @@ fn list() { #[test] fn mismatched_list_tail() { - assert_error!("[\"foo\", ..[1, 2]]"); + assert_error!("[\"wibble\", ..[1, 2]]"); } #[test] diff --git a/compiler-core/src/type_/tests/exhaustiveness.rs b/compiler-core/src/type_/tests/exhaustiveness.rs index 5dcf25199..ab4445688 100644 --- a/compiler-core/src/type_/tests/exhaustiveness.rs +++ b/compiler-core/src/type_/tests/exhaustiveness.rs @@ -929,7 +929,7 @@ pub type Returned(a) { Returned(List(a)) } -fn foo(user: Returned(#())) -> Int { +fn wibble(user: Returned(#())) -> Int { let Returned([#()]) = user 1 } @@ -966,3 +966,23 @@ pub fn main(x: something) { "# ); } + +#[test] +fn reference_absent_type() { + // This test is here because this code previously caused the compiler + // to crash, and we want to make sure that it doesn't break again + assert_module_error!( + " +type Wibble { + One(Int) + Two(Absent) +} + +pub fn main(wibble) { + case wibble { + One(x) -> x + } +} +" + ); +} diff --git a/compiler-core/src/type_/tests/functions.rs b/compiler-core/src/type_/tests/functions.rs index 5d5013f89..773f7b169 100644 --- a/compiler-core/src/type_/tests/functions.rs +++ b/compiler-core/src/type_/tests/functions.rs @@ -247,3 +247,71 @@ pub fn two() -> Nil { "# ); } + +#[test] +fn multiple_bad_statement_assignment_fault_tolerance() { + assert_module_error!( + r#" +pub fn main() { + let a = 1 + 2.0 + let b = 3 + 4.0 + let c = a + b +} +"# + ); +} + +#[test] +fn multiple_bad_statement_assignment_with_annotation_fault_tolerance() { + assert_module_error!( + r#" +pub fn main() { + let a: Int = "not an int" + let b: String = 1 + let c = a + 2 +} +"# + ); +} + +#[test] +fn multiple_bad_statement_assignment_with_annotation_fault_tolerance2() { + assert_module_error!( + r#" +pub fn main() { + // Since the value is invalid the type is the annotation + let a: Int = Junk + let b: String = 1 + let c = a + 2 +} +"# + ); +} + +#[test] +fn multiple_bad_statement_assignment_with_pattern_fault_tolerance2() { + assert_module_error!( + r#" +pub fn main() { + // Since the pattern is invalid no variable is created + let Junk(a) = 7 + // Pattern is valid but does not type check + let Ok(b) = 1 + let c = a + b +} +"# + ); +} + +#[test] +fn multiple_bad_statement_expression_fault_tolerance() { + assert_module_error!( + r#" +pub fn main() { + 1 + 2.0 + 3 + 4.0 + let c = 1 + 2 +} +"# + ); +} diff --git a/compiler-core/src/type_/tests/imports.rs b/compiler-core/src/type_/tests/imports.rs index 6138ca78d..5ee96b5d7 100644 --- a/compiler-core/src/type_/tests/imports.rs +++ b/compiler-core/src/type_/tests/imports.rs @@ -281,10 +281,10 @@ fn deprecated_type_import_conflict_two_modules() { #[test] fn imported_constructor_instead_of_type() { assert_with_module_error!( - ("module", "pub type Foo { Foo }"), - "import module.{Foo} + ("module", "pub type Wibble { Wibble }"), + "import module.{Wibble} -pub fn main(x: Foo) { +pub fn main(x: Wibble) { todo }", ); diff --git a/compiler-core/src/type_/tests/pretty.rs b/compiler-core/src/type_/tests/pretty.rs index 409456390..981bdb71d 100644 --- a/compiler-core/src/type_/tests/pretty.rs +++ b/compiler-core/src/type_/tests/pretty.rs @@ -15,7 +15,7 @@ fn print(type_: Arc) -> String { fn custom_bool() -> Arc { Arc::new(Type::Named { publicity: Publicity::Public, - package: "foo".into(), + package: "wibble".into(), module: "one/two".into(), name: "Bool".into(), args: vec![], diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__custom_types__conflict_with_import.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__custom_types__conflict_with_import.snap new file mode 100644 index 000000000..b9e88ba90 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__custom_types__conflict_with_import.snap @@ -0,0 +1,14 @@ +--- +source: compiler-core/src/type_/tests/custom_types.rs +expression: "import wibble.{type A} type A { C }" +--- +error: Duplicate type definition + ┌─ /src/one/two.gleam:1:16 + │ +1 │ import wibble.{type A} type A { C } + │ ^^^^^^ ^^^^^^ Redefined here + │ │ + │ First defined here + +The type `A` has been defined multiple times. +Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_import_error_no_unqualified.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_import_error_no_unqualified.snap index 47182cf05..9e9fbf3eb 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_import_error_no_unqualified.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_import_error_no_unqualified.snap @@ -1,14 +1,14 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "\n import foo/sub\n import foo2/sub\n pub fn main() {\n sub.bar()\n }\n " +expression: "\n import wibble/sub\n import wibble2/sub\n pub fn main() {\n sub.wobble()\n }\n " --- error: Duplicate import ┌─ /src/one/two.gleam:2:9 │ -2 │ import foo/sub - │ ^^^^^^^^^^^^^^ First imported here -3 │ import foo2/sub - │ ^^^^^^^^^^^^^^^ Reimported here +2 │ import wibble/sub + │ ^^^^^^^^^^^^^^^^^ First imported here +3 │ import wibble2/sub + │ ^^^^^^^^^^^^^^^^^^ Reimported here `sub` has been imported multiple times. Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_import_error_with_unqualified.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_import_error_with_unqualified.snap index b78329ad4..dbfacfc3f 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_import_error_with_unqualified.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_import_error_with_unqualified.snap @@ -1,14 +1,14 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "\n import foo/sub\n import foo2/sub.{bar}\n pub fn main() {\n sub.bar()\n }\n " +expression: "\n import wibble/sub\n import wibble2/sub.{wobble}\n pub fn main() {\n sub.wobble()\n }\n " --- error: Duplicate import ┌─ /src/one/two.gleam:2:9 │ -2 │ import foo/sub - │ ^^^^^^^^^^^^^^ First imported here -3 │ import foo2/sub.{bar} - │ ^^^^^^^^^^^^^^^^^^^^^ Reimported here +2 │ import wibble/sub + │ ^^^^^^^^^^^^^^^^^ First imported here +3 │ import wibble2/sub.{wobble} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Reimported here `sub` has been imported multiple times. Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_type_error.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_type_error.snap index 798f89956..743fd2865 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_type_error.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ambiguous_type_error.snap @@ -1,12 +1,12 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "import foo pub type Thing { Thing }\n pub fn main() {\n [Thing] == [foo.Thing]\n }" +expression: "import wibble pub type Thing { Thing }\n pub fn main() {\n [Thing] == [wibble.Thing]\n }" --- error: Type mismatch ┌─ /src/one/two.gleam:3:24 │ -3 │ [Thing] == [foo.Thing] - │ ^^^^^^^^^^^ +3 │ [Thing] == [wibble.Thing] + │ ^^^^^^^^^^^^^^ Expected type: @@ -14,4 +14,4 @@ Expected type: Found type: - List(foo.Thing) + List(wibble.Thing) diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays2.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays2.snap index b4e5e0823..d7692c8e0 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays2.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays2.snap @@ -1,11 +1,11 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "let <> = <<1>> x" +expression: "let <> = <<1>>" --- error: Invalid bit array segment ┌─ /src/one/two.gleam:1:7 │ -1 │ let <> = <<1>> x +1 │ let <> = <<1>> │ ^^^^^^ This cannot be a variable Hint: in patterns utf8, utf16, and utf32 must be an exact string. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays3.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays3.snap index fe757b37d..b3a4fe492 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays3.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays3.snap @@ -1,11 +1,11 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "let <> = <<1>> x" +expression: "let <> = <<1>>" --- error: Invalid bit array segment ┌─ /src/one/two.gleam:1:7 │ -1 │ let <> = <<1>> x +1 │ let <> = <<1>> │ ^^^^^^^ This cannot be a variable Hint: in patterns utf8, utf16, and utf32 must be an exact string. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays4.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays4.snap index 80f9b0e65..096fbf053 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays4.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__bit_arrays4.snap @@ -1,11 +1,11 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "let <> = <<1>> x" +expression: "let <> = <<1>>" --- error: Invalid bit array segment ┌─ /src/one/two.gleam:1:7 │ -1 │ let <> = <<1>> x +1 │ let <> = <<1>> │ ^^^^^^^ This cannot be a variable Hint: in patterns utf8, utf16, and utf32 must be an exact string. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_const.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_const.snap index 8c8f37e76..e068a47d9 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_const.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_const.snap @@ -1,14 +1,14 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "const foo = 1\nconst foo = 2" +expression: "const wibble = 1\nconst wibble = 2" --- error: Duplicate definition ┌─ /src/one/two.gleam:1:7 │ -1 │ const foo = 1 - │ ^^^ First defined here -2 │ const foo = 2 - │ ^^^ Redefined here +1 │ const wibble = 1 + │ ^^^^^^ First defined here +2 │ const wibble = 2 + │ ^^^^^^ Redefined here -`foo` has been defined multiple times. +`wibble` has been defined multiple times. Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_extfn.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_extfn.snap index ec95a6055..f2f34446c 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_extfn.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_extfn.snap @@ -1,15 +1,15 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "const foo = 1\n\n@external(erlang, \"module2\", \"function2\")\nfn foo() -> Float\n" +expression: "const wibble = 1\n\n@external(erlang, \"module2\", \"function2\")\nfn wibble() -> Float\n" --- error: Duplicate definition ┌─ /src/one/two.gleam:1:7 │ -1 │ const foo = 1 - │ ^^^ First defined here +1 │ const wibble = 1 + │ ^^^^^^ First defined here · -4 │ fn foo() -> Float - │ ^^^^^^^^ Redefined here +4 │ fn wibble() -> Float + │ ^^^^^^^^^^^ Redefined here -`foo` has been defined multiple times. +`wibble` has been defined multiple times. Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_fn.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_fn.snap index ae5fa2608..39df63148 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_fn.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_const_fn.snap @@ -1,14 +1,14 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "const foo = 1\nfn foo() { 2 }" +expression: "const wibble = 1\nfn wibble() { 2 }" --- error: Duplicate definition ┌─ /src/one/two.gleam:1:7 │ -1 │ const foo = 1 - │ ^^^ First defined here -2 │ fn foo() { 2 } - │ ^^^^^^^^ Redefined here +1 │ const wibble = 1 + │ ^^^^^^ First defined here +2 │ fn wibble() { 2 } + │ ^^^^^^^^^^^ Redefined here -`foo` has been defined multiple times. +`wibble` has been defined multiple times. Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_const.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_const.snap index a7871bdf5..8169ea3e9 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_const.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_const.snap @@ -1,15 +1,15 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "\n@external(erlang, \"module1\", \"function1\")\nfn foo() -> Float\n\nconst foo = 2" +expression: "\n@external(erlang, \"module1\", \"function1\")\nfn wibble() -> Float\n\nconst wibble = 2" --- error: Duplicate definition ┌─ /src/one/two.gleam:3:1 │ -3 │ fn foo() -> Float - │ ^^^^^^^^ First defined here +3 │ fn wibble() -> Float + │ ^^^^^^^^^^^ First defined here 4 │ -5 │ const foo = 2 - │ ^^^ Redefined here +5 │ const wibble = 2 + │ ^^^^^^ Redefined here -`foo` has been defined multiple times. +`wibble` has been defined multiple times. Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_extfn.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_extfn.snap index 1d79fa9a4..61e28c86d 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_extfn.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_extfn.snap @@ -1,15 +1,15 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "\n@external(erlang, \"module1\", \"function1\")\nfn foo() -> Float\n@external(erlang, \"module2\", \"function2\")\nfn foo() -> Float\n" +expression: "\n@external(erlang, \"module1\", \"function1\")\nfn wibble() -> Float\n@external(erlang, \"module2\", \"function2\")\nfn wibble() -> Float\n" --- error: Duplicate definition ┌─ /src/one/two.gleam:3:1 │ -3 │ fn foo() -> Float - │ ^^^^^^^^ First defined here +3 │ fn wibble() -> Float + │ ^^^^^^^^^^^ First defined here 4 │ @external(erlang, "module2", "function2") -5 │ fn foo() -> Float - │ ^^^^^^^^ Redefined here +5 │ fn wibble() -> Float + │ ^^^^^^^^^^^ Redefined here -`foo` has been defined multiple times. +`wibble` has been defined multiple times. Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_fn.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_fn.snap index 5c80dbcb0..1936a18c8 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_fn.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_extfn_fn.snap @@ -1,15 +1,15 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "\n@external(erlang, \"module1\", \"function1\")\nfn foo() -> Float\n\nfn foo() { 2 }" +expression: "\n@external(erlang, \"module1\", \"function1\")\nfn wibble() -> Float\n\nfn wibble() { 2 }" --- error: Duplicate definition ┌─ /src/one/two.gleam:3:1 │ -3 │ fn foo() -> Float - │ ^^^^^^^^ First defined here +3 │ fn wibble() -> Float + │ ^^^^^^^^^^^ First defined here 4 │ -5 │ fn foo() { 2 } - │ ^^^^^^^^ Redefined here +5 │ fn wibble() { 2 } + │ ^^^^^^^^^^^ Redefined here -`foo` has been defined multiple times. +`wibble` has been defined multiple times. Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_const.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_const.snap index fc87e1df6..8949f8109 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_const.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_const.snap @@ -1,14 +1,14 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "fn foo() { 1 }\nconst foo = 2" +expression: "fn wibble() { 1 }\nconst wibble = 2" --- error: Duplicate definition ┌─ /src/one/two.gleam:1:1 │ -1 │ fn foo() { 1 } - │ ^^^^^^^^ First defined here -2 │ const foo = 2 - │ ^^^ Redefined here +1 │ fn wibble() { 1 } + │ ^^^^^^^^^^^ First defined here +2 │ const wibble = 2 + │ ^^^^^^ Redefined here -`foo` has been defined multiple times. +`wibble` has been defined multiple times. Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_extfn.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_extfn.snap index c699ed65e..f91b466f1 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_extfn.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_extfn.snap @@ -1,15 +1,15 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "fn foo() { 1 }\n\n@external(erlang, \"module2\", \"function2\")\nfn foo() -> Float\n" +expression: "fn wibble() { 1 }\n\n@external(erlang, \"module2\", \"function2\")\nfn wibble() -> Float\n" --- error: Duplicate definition ┌─ /src/one/two.gleam:1:1 │ -1 │ fn foo() { 1 } - │ ^^^^^^^^ First defined here +1 │ fn wibble() { 1 } + │ ^^^^^^^^^^^ First defined here · -4 │ fn foo() -> Float - │ ^^^^^^^^ Redefined here +4 │ fn wibble() -> Float + │ ^^^^^^^^^^^ Redefined here -`foo` has been defined multiple times. +`wibble` has been defined multiple times. Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_fn.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_fn.snap index c61fa71e9..6fc3f801b 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_fn.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__duplicate_fn_fn.snap @@ -1,14 +1,14 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "fn foo() { 1 }\nfn foo() { 2 }" +expression: "fn wibble() { 1 }\nfn wibble() { 2 }" --- error: Duplicate definition ┌─ /src/one/two.gleam:1:1 │ -1 │ fn foo() { 1 } - │ ^^^^^^^^ First defined here -2 │ fn foo() { 2 } - │ ^^^^^^^^ Redefined here +1 │ fn wibble() { 1 } + │ ^^^^^^^^^^^ First defined here +2 │ fn wibble() { 2 } + │ ^^^^^^^^^^^ Redefined here -`foo` has been defined multiple times. +`wibble` has been defined multiple times. Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__mismatched_list_tail.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__mismatched_list_tail.snap index 9b9e6067e..d4b0a12b2 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__mismatched_list_tail.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__mismatched_list_tail.snap @@ -1,12 +1,12 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "[\"foo\", ..[1, 2]]" +expression: "[\"wibble\", ..[1, 2]]" --- error: Type mismatch - ┌─ /src/one/two.gleam:1:11 + ┌─ /src/one/two.gleam:1:14 │ -1 │ ["foo", ..[1, 2]] - │ ^^^^^^ +1 │ ["wibble", ..[1, 2]] + │ ^^^^^^ All elements in a list must have the same type, but the elements of this list don't match the type of the elements being prepended to it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify4.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify4.snap index 50c06e328..73c73e410 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify4.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify4.snap @@ -1,12 +1,12 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "\nfn bar() -> Int {\n 5\n}\n\nfn run(one: fn() -> String) {\n one()\n}\n\nfn demo() {\n run(bar)\n}" +expression: "\nfn wobble() -> Int {\n 5\n}\n\nfn run(one: fn() -> String) {\n one()\n}\n\nfn demo() {\n run(wobble)\n}" --- error: Type mismatch ┌─ /src/one/two.gleam:11:9 │ -11 │ run(bar) - │ ^^^ +11 │ run(wobble) + │ ^^^^^^ Expected type: diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify5.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify5.snap index 5a84baf68..2ecfdc79f 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify5.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify5.snap @@ -1,12 +1,12 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "\nfn bar(x: Int) -> Int {\n x * 5\n}\n\nfn run(one: fn(String) -> Int) {\n one(\"one.\")\n}\n\nfn demo() {\n run(bar)\n}" +expression: "\nfn wobble(x: Int) -> Int {\n x * 5\n}\n\nfn run(one: fn(String) -> Int) {\n one(\"one.\")\n}\n\nfn demo() {\n run(wobble)\n}" --- error: Type mismatch ┌─ /src/one/two.gleam:11:9 │ -11 │ run(bar) - │ ^^^ +11 │ run(wobble) + │ ^^^^^^ Expected type: diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify7.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify7.snap index 648176970..a4900deba 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify7.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify7.snap @@ -1,11 +1,11 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "fn main() { let assert 5: Int = \"\" 5 }" +expression: "fn main() { let assert 5 = \"\" }" --- error: Type mismatch ┌─ /src/one/two.gleam:1:24 │ -1 │ fn main() { let assert 5: Int = "" 5 } +1 │ fn main() { let assert 5 = "" } │ ^ Expected type: diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify9.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify9.snap index b9f9e13d8..5f455acc9 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify9.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__module_could_not_unify9.snap @@ -1,12 +1,12 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "fn main() { let [1, 2, ..x]: List(String) = [1,2,3] x }" +expression: "fn main() { let assert [1, 2, ..x]: List(String) = [1,2,3] x }" --- error: Type mismatch - ┌─ /src/one/two.gleam:1:45 + ┌─ /src/one/two.gleam:1:52 │ -1 │ fn main() { let [1, 2, ..x]: List(String) = [1,2,3] x } - │ ^^^^^^^ +1 │ fn main() { let assert [1, 2, ..x]: List(String) = [1,2,3] x } + │ ^^^^^^^ Expected type: diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ok_2_args.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ok_2_args.snap index ad029fb01..6f1383e44 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ok_2_args.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__ok_2_args.snap @@ -1,9 +1,9 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "let Ok(1, x) = 1 x" +expression: "let Ok(1, x) = 1" --- error: Incorrect arity ┌─ /src/one/two.gleam:1:5 │ -1 │ let Ok(1, x) = 1 x +1 │ let Ok(1, x) = 1 │ ^^^^^^^^ Expected 1 argument, got 2 diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__same_imports_multiple_times.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__same_imports_multiple_times.snap index 3662e7451..e8eb6cc14 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__same_imports_multiple_times.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__same_imports_multiple_times.snap @@ -1,22 +1,22 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "\n import gleam/foo.{bar}\n import gleam/foo.{zoo}\n pub fn go() { bar() + zoo() }\n " +expression: "\n import gleam/wibble.{wobble}\n import gleam/wibble.{zoo}\n pub fn go() { wobble() + zoo() }\n " --- error: Duplicate import ┌─ /src/one/two.gleam:2:9 │ -2 │ import gleam/foo.{bar} - │ ^^^^^^^^^^^^^^^^^^^^^^ First imported here -3 │ import gleam/foo.{zoo} - │ ^^^^^^^^^^^^^^^^^^^^^^ Reimported here +2 │ import gleam/wibble.{wobble} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ First imported here +3 │ import gleam/wibble.{zoo} + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ Reimported here -`foo` has been imported multiple times. +`wibble` has been imported multiple times. Names in a Gleam module must be unique so one will need to be renamed. error: Unknown variable - ┌─ /src/one/two.gleam:4:31 + ┌─ /src/one/two.gleam:4:34 │ -4 │ pub fn go() { bar() + zoo() } - │ ^^^ +4 │ pub fn go() { wobble() + zoo() } + │ ^^^ The name `zoo` is not in scope here. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__true_fn.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__true_fn.snap index 4674b6dee..3ae662fb6 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__true_fn.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__true_fn.snap @@ -1,9 +1,9 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: let True(x) = 1 x +expression: let True(x) = 1 --- error: Incorrect arity ┌─ /src/one/two.gleam:1:5 │ -1 │ let True(x) = 1 x +1 │ let True(x) = 1 │ ^^^^^^^ Expected no arguments, got 1 diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__tuple_int.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__tuple_int.snap index a523566d1..425f28b42 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__tuple_int.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__tuple_int.snap @@ -1,11 +1,11 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "let #(a, b) = 1 a" +expression: "let #(a, b) = 1" --- error: Type mismatch ┌─ /src/one/two.gleam:1:5 │ -1 │ let #(a, b) = 1 a +1 │ let #(a, b) = 1 │ ^^^^^^^ Expected type: diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__type_imported_as_value.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__type_imported_as_value.snap index e9364240b..d8f2eb378 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__type_imported_as_value.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__type_imported_as_value.snap @@ -1,11 +1,11 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "import gleam/foo.{Bar}" +expression: "import gleam/wibble.{Wibble}" --- error: Unknown module field - ┌─ /src/one/two.gleam:1:19 + ┌─ /src/one/two.gleam:1:22 │ -1 │ import gleam/foo.{Bar} - │ ^^^ Did you mean `type Bar`? +1 │ import gleam/wibble.{Wibble} + │ ^^^^^^ Did you mean `type Wibble`? -`Bar` is only a type, it cannot be imported as a value. +`Wibble` is only a type, it cannot be imported as a value. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__type_variables_in_body.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__type_variables_in_body.snap index 30cb8bfbe..7a074eb99 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__type_variables_in_body.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__type_variables_in_body.snap @@ -15,3 +15,17 @@ Expected type: Found type: Box(b) + +error: Type mismatch + ┌─ /src/one/two.gleam:7:19 + │ +7 │ let _: Box(b) = box1 + │ ^^^^ + +Expected type: + + Box(b) + +Found type: + + Box(a) diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__value_imported_as_type.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__value_imported_as_type.snap index 00e16c02e..05959e25a 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__value_imported_as_type.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__value_imported_as_type.snap @@ -1,11 +1,11 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "import gleam/foo.{type Baz}" +expression: "import gleam/wibble.{type Wobble}" --- error: Unknown module type - ┌─ /src/one/two.gleam:1:19 + ┌─ /src/one/two.gleam:1:22 │ -1 │ import gleam/foo.{type Baz} - │ ^^^^^^^^ Did you mean `Baz`? +1 │ import gleam/wibble.{type Wobble} + │ ^^^^^^^^^^^ Did you mean `Wobble`? -`Baz` is only a value, it cannot be imported as a type. +`Wobble` is only a value, it cannot be imported as a type. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__wrong_type_arg.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__wrong_type_arg.snap index 717d7e319..cf7eabecc 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__wrong_type_arg.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__wrong_type_arg.snap @@ -1,12 +1,12 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "\nfn foo(x: List(Int)) { x }\nfn main(y: List(something)) {\n foo(y)\n}" +expression: "\nfn wibble(x: List(Int)) { x }\nfn main(y: List(something)) {\n wibble(y)\n}" --- error: Type mismatch - ┌─ /src/one/two.gleam:4:7 + ┌─ /src/one/two.gleam:4:10 │ -4 │ foo(y) - │ ^ +4 │ wibble(y) + │ ^ Expected type: diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__wrong_type_var.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__wrong_type_var.snap index baba42335..b0629d6ce 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__wrong_type_var.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__errors__wrong_type_var.snap @@ -1,12 +1,12 @@ --- source: compiler-core/src/type_/tests/errors.rs -expression: "fn foo(x: String) { x }\nfn multi_result(x: some_name) {\n foo(x)\n}" +expression: "fn wibble(x: String) { x }\nfn multi_result(x: some_name) {\n wibble(x)\n}" --- error: Type mismatch - ┌─ /src/one/two.gleam:3:7 + ┌─ /src/one/two.gleam:3:10 │ -3 │ foo(x) - │ ^ +3 │ wibble(x) + │ ^ Expected type: diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__exhaustiveness__reference_absent_type.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__exhaustiveness__reference_absent_type.snap new file mode 100644 index 000000000..0585a92c0 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__exhaustiveness__reference_absent_type.snap @@ -0,0 +1,38 @@ +--- +source: compiler-core/src/type_/tests/exhaustiveness.rs +expression: "\ntype Wibble {\n One(Int)\n Two(Absent)\n}\n\npub fn main(wibble) {\n case wibble {\n One(x) -> x\n }\n}\n" +--- +error: Unknown type + ┌─ /src/one/two.gleam:4:9 + │ +4 │ Two(Absent) + │ ^^^^^^ + +The type `Absent` is not defined or imported in this module. + +error: Private type used in public interface + ┌─ /src/one/two.gleam:7:1 + │ +7 │ pub fn main(wibble) { + │ ^^^^^^^^^^^^^^^^^^^ + +The following type is private, but is being used by this public export. + + Wibble + +Private types can only be used within the module that defines them. + +error: Inexhaustive patterns + ┌─ /src/one/two.gleam:8:5 + │ + 8 │ ╭ case wibble { + 9 │ │ One(x) -> x +10 │ │ } + │ ╰─────^ + +This case expression does not have a pattern for all possible values. +If it is run on one of the values without a pattern then it will crash. + +The missing patterns are: + + Two diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_fault_tolerance.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_fault_tolerance.snap new file mode 100644 index 000000000..edfeb4f22 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_fault_tolerance.snap @@ -0,0 +1,36 @@ +--- +source: compiler-core/src/type_/tests/functions.rs +expression: "\npub fn main() {\n let a = 1 + 2.0\n let b = 3 + 4.0\n let c = a + b\n}\n" +--- +error: Type mismatch + ┌─ /src/one/two.gleam:3:15 + │ +3 │ let a = 1 + 2.0 + │ ^^^ + +The + operator expects arguments of this type: + + Int + +But this argument has this type: + + Float + +Hint: the +. operator can be used with Floats + + +error: Type mismatch + ┌─ /src/one/two.gleam:4:15 + │ +4 │ let b = 3 + 4.0 + │ ^^^ + +The + operator expects arguments of this type: + + Int + +But this argument has this type: + + Float + +Hint: the +. operator can be used with Floats diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_with_annotation_fault_tolerance.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_with_annotation_fault_tolerance.snap new file mode 100644 index 000000000..1f8058fbd --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_with_annotation_fault_tolerance.snap @@ -0,0 +1,48 @@ +--- +source: compiler-core/src/type_/tests/functions.rs +expression: "\npub fn main() {\n let a: Int = \"not an int\"\n let b: String = 1\n let c = a + 2\n}\n" +--- +error: Type mismatch + ┌─ /src/one/two.gleam:3:16 + │ +3 │ let a: Int = "not an int" + │ ^^^^^^^^^^^^ + +Expected type: + + Int + +Found type: + + String + +error: Type mismatch + ┌─ /src/one/two.gleam:4:19 + │ +4 │ let b: String = 1 + │ ^ + +Expected type: + + String + +Found type: + + Int + +error: Type mismatch + ┌─ /src/one/two.gleam:5:11 + │ +5 │ let c = a + 2 + │ ^ + +The + operator expects arguments of this type: + + Int + +But this argument has this type: + + String + +Hint: Strings can be joined using the `append` or `concat` functions from the +`gleam/string` module. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_with_annotation_fault_tolerance2.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_with_annotation_fault_tolerance2.snap new file mode 100644 index 000000000..210842bec --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_with_annotation_fault_tolerance2.snap @@ -0,0 +1,25 @@ +--- +source: compiler-core/src/type_/tests/functions.rs +expression: "\npub fn main() {\n // Since the value is invalid the type is the annotation\n let a: Int = Junk\n let b: String = 1\n let c = a + 2\n}\n" +--- +error: Unknown variable + ┌─ /src/one/two.gleam:4:16 + │ +4 │ let a: Int = Junk + │ ^^^^ + +The name `Junk` is not in scope here. + +error: Type mismatch + ┌─ /src/one/two.gleam:5:19 + │ +5 │ let b: String = 1 + │ ^ + +Expected type: + + String + +Found type: + + Int diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_with_pattern_fault_tolerance2.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_with_pattern_fault_tolerance2.snap new file mode 100644 index 000000000..e5a72ac26 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_assignment_with_pattern_fault_tolerance2.snap @@ -0,0 +1,33 @@ +--- +source: compiler-core/src/type_/tests/functions.rs +expression: "\npub fn main() {\n // Since the pattern is invalid no variable is created\n let Junk(a) = 7\n // Pattern is valid but does not type check\n let Ok(b) = 1\n let c = a + b\n}\n" +--- +error: Unknown variable + ┌─ /src/one/two.gleam:4:7 + │ +4 │ let Junk(a) = 7 + │ ^^^^^^^ + +The name `Junk` is not in scope here. + +error: Type mismatch + ┌─ /src/one/two.gleam:6:7 + │ +6 │ let Ok(b) = 1 + │ ^^^^^ + +Expected type: + + Int + +Found type: + + Result(a, b) + +error: Unknown variable + ┌─ /src/one/two.gleam:7:11 + │ +7 │ let c = a + b + │ ^ Did you mean `b`? + +The name `a` is not in scope here. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_expression_fault_tolerance.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_expression_fault_tolerance.snap new file mode 100644 index 000000000..883e3618b --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__functions__multiple_bad_statement_expression_fault_tolerance.snap @@ -0,0 +1,36 @@ +--- +source: compiler-core/src/type_/tests/functions.rs +expression: "\npub fn main() {\n 1 + 2.0\n 3 + 4.0\n let c = 1 + 2\n}\n" +--- +error: Type mismatch + ┌─ /src/one/two.gleam:3:7 + │ +3 │ 1 + 2.0 + │ ^^^ + +The + operator expects arguments of this type: + + Int + +But this argument has this type: + + Float + +Hint: the +. operator can be used with Floats + + +error: Type mismatch + ┌─ /src/one/two.gleam:4:7 + │ +4 │ 3 + 4.0 + │ ^^^ + +The + operator expects arguments of this type: + + Int + +But this argument has this type: + + Float + +Hint: the +. operator can be used with Floats diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__imports__imported_constructor_instead_of_type.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__imports__imported_constructor_instead_of_type.snap index 11eb5a6c5..1dfcd647c 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__imports__imported_constructor_instead_of_type.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__imports__imported_constructor_instead_of_type.snap @@ -1,13 +1,13 @@ --- source: compiler-core/src/type_/tests/imports.rs -expression: "import module.{Foo}\n\npub fn main(x: Foo) {\n todo\n}" +expression: "import module.{Wibble}\n\npub fn main(x: Wibble) {\n todo\n}" --- error: Unknown type ┌─ /src/one/two.gleam:3:16 │ -3 │ pub fn main(x: Foo) { - │ ^^^ +3 │ pub fn main(x: Wibble) { + │ ^^^^^^ -The type `Foo` is not defined or imported in this module. -There is a value in scope with the name `Foo`, but no type in scope with +The type `Wibble` is not defined or imported in this module. +There is a value in scope with the name `Wibble`, but no type in scope with that name. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__type_alias__conflict_with_import.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__type_alias__conflict_with_import.snap new file mode 100644 index 000000000..b28f25026 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__type_alias__conflict_with_import.snap @@ -0,0 +1,14 @@ +--- +source: compiler-core/src/type_/tests/type_alias.rs +expression: "import wibble.{type Wobble} type Wobble = Int" +--- +error: Duplicate type definition + ┌─ /src/one/two.gleam:1:16 + │ +1 │ import wibble.{type Wobble} type Wobble = Int + │ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ Redefined here + │ │ + │ First defined here + +The type `Wobble` has been defined multiple times. +Names in a Gleam module must be unique so one will need to be renamed. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__use___multiple_bad_statement_use_fault_tolerance.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__use___multiple_bad_statement_use_fault_tolerance.snap new file mode 100644 index 000000000..3a108b41e --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__use___multiple_bad_statement_use_fault_tolerance.snap @@ -0,0 +1,36 @@ +--- +source: compiler-core/src/type_/tests/use_.rs +expression: "\nlet x = fn(f) { f() + 1 }\nuse <- x()\n\n1 + 2.0\n3.0 + 4\n5\n" +--- +error: Type mismatch + ┌─ /src/one/two.gleam:5:5 + │ +5 │ 1 + 2.0 + │ ^^^ + +The + operator expects arguments of this type: + + Int + +But this argument has this type: + + Float + +Hint: the +. operator can be used with Floats + + +error: Type mismatch + ┌─ /src/one/two.gleam:6:1 + │ +6 │ 3.0 + 4 + │ ^^^ + +The + operator expects arguments of this type: + + Int + +But this argument has this type: + + Float + +Hint: the +. operator can be used with Floats diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__deprecated_list_append_syntax.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__deprecated_list_append_syntax.snap new file mode 100644 index 000000000..103f98570 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__deprecated_list_append_syntax.snap @@ -0,0 +1,13 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub fn main() {\n let letters = [\"b\", \"c\"]\n [\"a\"..letters]\n }\n " +--- +warning: Deprecated prepend syntax + ┌─ test/path:4:11 + │ +4 │ ["a"..letters] + │ ^^ This spread should be preceded by a comma + +This syntax for prepending to a list is deprecated. +When prepending an item to a list it should be preceded by a comma, like +this: `[item, ..list]`. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__deprecated_list_pattern_syntax.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__deprecated_list_pattern_syntax.snap new file mode 100644 index 000000000..31bdb9a23 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__deprecated_list_pattern_syntax.snap @@ -0,0 +1,13 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub fn main() {\n let letters = [\"b\", \"c\"]\n case letters {\n [\"a\"..rest] -> rest\n _ -> []\n }\n }\n " +--- +warning: Deprecated list pattern matching syntax + ┌─ test/path:5:13 + │ +5 │ ["a"..rest] -> rest + │ ^^ This spread should be preceded by a comma + +This syntax for pattern matching on a list is deprecated. +When matching on the rest of a list it should always be preceded by a +comma, like this: `[item, ..list]`. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__deprecated_list_prepend_syntax.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__deprecated_list_prepend_syntax.snap new file mode 100644 index 000000000..65ce3d5ad --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__deprecated_list_prepend_syntax.snap @@ -0,0 +1,13 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub fn main() {\n let letters = [\"b\", \"c\"]\n [\"a\"..letters]\n }\n " +--- +warning: Deprecated prepend syntax + ┌─ /src/warning/wrn.gleam:4:11 + │ +4 │ ["a"..letters] + │ ^^ This spread should be preceded by a comma + +This syntax for prepending to a list is deprecated. +When prepending an item to a list it should be preceded by a comma, like +this: `[item, ..list]`. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__empty_func_warning_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__empty_func_warning_test.snap index 4d1d7e4b2..1b87860b9 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__empty_func_warning_test.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__empty_func_warning_test.snap @@ -1,12 +1,12 @@ --- source: compiler-core/src/type_/tests/warnings.rs -expression: "pub fn main() { foo() }\n pub fn foo() { }" +expression: "pub fn main() { wibble() }\npub fn wibble() { }\n" --- warning: Unimplemented function - ┌─ /src/warning/wrn.gleam:2:9 + ┌─ /src/warning/wrn.gleam:2:1 │ -2 │ pub fn foo() { } - │ ^^^^^^^^^^^^ This code is incomplete +2 │ pub fn wibble() { } + │ ^^^^^^^^^^^^^^^ This code is incomplete This code will crash if it is run. Be sure to finish it before running your program. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__record_update_warnings_test2.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__record_update_warnings_test2.snap new file mode 100644 index 000000000..3741e37cd --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__record_update_warnings_test2.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub type Person {\n Person(name: String, age: Int)\n }\n pub fn update_person() {\n let past = Person(\"Quinn\", 27)\n let present = Person(..past)\n present\n }" +--- +warning: Fieldless record update + ┌─ /src/warning/wrn.gleam:7:27 + │ +7 │ let present = Person(..past) + │ ^^^^^^^^^^^^^^ This record update doesn't change any fields + +Hint: Add some fields to change or replace it with the record itself. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__record_update_warnings_test3.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__record_update_warnings_test3.snap new file mode 100644 index 000000000..91dbb973b --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__record_update_warnings_test3.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub type Person {\n Person(name: String, age: Int)\n }\n pub fn update_person() {\n let past = Person(\"Quinn\", 27)\n let present = Person(..past, name: \"Quinn\", age: 28)\n present\n }" +--- +warning: Redundant record update + ┌─ /src/warning/wrn.gleam:7:27 + │ +7 │ let present = Person(..past, name: "Quinn", age: 28) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This record update specifies all fields + +Hint: It is better style to use the record creation syntax. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_1.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_1.snap new file mode 100644 index 000000000..f06cc8296 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_1.snap @@ -0,0 +1,14 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub fn wibble(_, _) { 1 }\n\n pub fn main() {\n 1 |> wibble(_, 2) |> wibble(2)\n }\n" +--- +warning: Redundant function capture + ┌─ /src/warning/wrn.gleam:5:17 + │ +5 │ 1 |> wibble(_, 2) |> wibble(2) + │ ^ You can safely remove this + +This function capture is redundant since the value is already piped as the +first argument of this call. + +See: https://tour.gleam.run/functions/pipelines/ diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_2.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_2.snap new file mode 100644 index 000000000..e7b70b684 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_2.snap @@ -0,0 +1,14 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub fn wobble(_) { 1 }\n\n pub fn main() {\n 1 |> wobble(_) |> wobble\n }\n" +--- +warning: Redundant function capture + ┌─ /src/warning/wrn.gleam:5:17 + │ +5 │ 1 |> wobble(_) |> wobble + │ ^ You can safely remove this + +This function capture is redundant since the value is already piped as the +first argument of this call. + +See: https://tour.gleam.run/functions/pipelines/ diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_3.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_3.snap new file mode 100644 index 000000000..0c466e9b9 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_3.snap @@ -0,0 +1,14 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub fn wobble(_) { 1 }\n\n pub fn main() {\n 1 |> wobble |> wobble(_)\n }\n" +--- +warning: Redundant function capture + ┌─ /src/warning/wrn.gleam:5:27 + │ +5 │ 1 |> wobble |> wobble(_) + │ ^ You can safely remove this + +This function capture is redundant since the value is already piped as the +first argument of this call. + +See: https://tour.gleam.run/functions/pipelines/ diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_4.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_4.snap new file mode 100644 index 000000000..6a365a8ec --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__redundant_function_capture_in_pipe_4.snap @@ -0,0 +1,14 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub fn wibble(_, _) { 1 }\n\n pub fn main() {\n 1 |> wibble(2) |> wibble(_, 2)\n }\n" +--- +warning: Redundant function capture + ┌─ /src/warning/wrn.gleam:5:30 + │ +5 │ 1 |> wibble(2) |> wibble(_, 2) + │ ^ You can safely remove this + +This function capture is redundant since the value is already piped as the +first argument of this call. + +See: https://tour.gleam.run/functions/pipelines/ diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__result_discard_warning_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__result_discard_warning_test.snap new file mode 100644 index 000000000..756c822de --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__result_discard_warning_test.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\npub fn wibble() { Ok(5) }\npub fn main() {\n wibble()\n 5\n}" +--- +warning: Unused result value + ┌─ /src/warning/wrn.gleam:4:3 + │ +4 │ wibble() + │ ^^^^^^^^ The Result value created here is unused + +Hint: If you are sure you don't need it you can assign it to `_`. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__result_in_case_discarded.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__result_in_case_discarded.snap new file mode 100644 index 000000000..0dc3751d5 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__result_in_case_discarded.snap @@ -0,0 +1,13 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\npub fn main(x) {\n case x {\n _ -> Error(Nil)\n }\n Nil\n}" +--- +warning: Unused result value + ┌─ /src/warning/wrn.gleam:3:3 + │ +3 │ ╭ case x { +4 │ │ _ -> Error(Nil) +5 │ │ } + │ ╰───^ The Result value created here is unused + +Hint: If you are sure you don't need it you can assign it to `_`. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__todo_warning_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__todo_warning_test.snap new file mode 100644 index 000000000..1dcadec12 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__todo_warning_test.snap @@ -0,0 +1,14 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1 == todo }" +--- +warning: Todo found + ┌─ /src/warning/wrn.gleam:1:22 + │ +1 │ pub fn main() { 1 == todo } + │ ^^^^ This code is incomplete + +This code will crash if it is run. Be sure to finish it before +running your program. + +Hint: I think its type is `Int`. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_alias_for_duplicate_module_no_warning_for_alias_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_alias_for_duplicate_module_no_warning_for_alias_test.snap new file mode 100644 index 000000000..6bad4ec9d --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_alias_for_duplicate_module_no_warning_for_alias_test.snap @@ -0,0 +1,19 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n import a/wibble\n import b/wibble as wobble\n const one = wibble.one\n " +--- +warning: Unused private constant + ┌─ /src/warning/wrn.gleam:4:19 + │ +4 │ const one = wibble.one + │ ^^^ This private constant is never used + +Hint: You can safely remove it. + +warning: Unused imported module + ┌─ /src/warning/wrn.gleam:3:13 + │ +3 │ import b/wibble as wobble + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ This imported module is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_alias_warning_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_alias_warning_test.snap new file mode 100644 index 000000000..bfa27d422 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_alias_warning_test.snap @@ -0,0 +1,21 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n import gleam/wibble.{one} as wobble\n const one = one\n " +--- +warning: Unused private constant + ┌─ /src/warning/wrn.gleam:3:19 + │ +3 │ const one = one + │ ^^^ This private constant is never used + +Hint: You can safely remove it. + +warning: Unused imported module alias + ┌─ /src/warning/wrn.gleam:2:39 + │ +2 │ import gleam/wibble.{one} as wobble + │ ^^^^^^^^^ This alias is never used + +Hint: You can safely remove it. + + import gleam/wibble as _ diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_bit_array.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_bit_array.snap new file mode 100644 index 000000000..0df8e7457 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_bit_array.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub fn main() {\n <<3>>\n\t\t\t\t2\n }" +--- +warning: Unused literal + ┌─ /src/warning/wrn.gleam:3:9 + │ +3 │ <<3>> + │ ^^^^^ This value is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_destructure.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_destructure.snap new file mode 100644 index 000000000..6781edb13 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_destructure.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn a(b) { case b { #(c, _) -> 5 } }" +--- +warning: Unused variable + ┌─ /src/warning/wrn.gleam:1:26 + │ +1 │ pub fn a(b) { case b { #(c, _) -> 5 } } + │ ^ This variable is never used + +Hint: You can ignore it with an underscore: `_c`. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_float.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_float.snap new file mode 100644 index 000000000..8f4b65a2b --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_float.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1.0 2 }" +--- +warning: Unused literal + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1.0 2 } + │ ^^^ This value is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_imported_module_warnings_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_imported_module_warnings_test.snap new file mode 100644 index 000000000..406c58b81 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_imported_module_warnings_test.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: import gleam/wibble +--- +warning: Unused imported module + ┌─ /src/warning/wrn.gleam:1:1 + │ +1 │ import gleam/wibble + │ ^^^^^^^^^^^^^^^^^^^ This imported module is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_imported_module_with_alias_and_unqualified_name_warnings_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_imported_module_with_alias_and_unqualified_name_warnings_test.snap new file mode 100644 index 000000000..752ad8204 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_imported_module_with_alias_and_unqualified_name_warnings_test.snap @@ -0,0 +1,21 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "import gleam/one.{two} as three" +--- +warning: Unused imported value + ┌─ /src/warning/wrn.gleam:1:19 + │ +1 │ import gleam/one.{two} as three + │ ^^^ This imported value is never used + +Hint: You can safely remove it. + +warning: Unused imported module alias + ┌─ /src/warning/wrn.gleam:1:24 + │ +1 │ import gleam/one.{two} as three + │ ^^^^^^^^ This alias is never used + +Hint: You can safely remove it. + + import gleam/one as _ diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_imported_module_with_alias_warnings_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_imported_module_with_alias_warnings_test.snap new file mode 100644 index 000000000..2fc682c20 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_imported_module_with_alias_warnings_test.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: import gleam/wibble as wobble +--- +warning: Unused imported module + ┌─ /src/warning/wrn.gleam:1:1 + │ +1 │ import gleam/wibble as wobble + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This imported module is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_int.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_int.snap new file mode 100644 index 000000000..23be04d93 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_int.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn main() { 1 2 }" +--- +warning: Unused literal + ┌─ /src/warning/wrn.gleam:1:17 + │ +1 │ pub fn main() { 1 2 } + │ ^ This value is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_list.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_list.snap new file mode 100644 index 000000000..1d4b15ebb --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_list.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub fn main() {\n [1, 2, 3]\n\t\t\t\t2\n }" +--- +warning: Unused literal + ┌─ /src/warning/wrn.gleam:3:9 + │ +3 │ [1, 2, 3] + │ ^^^^^^^^^ This value is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_module_wuth_alias_warning_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_module_wuth_alias_warning_test.snap new file mode 100644 index 000000000..2fc682c20 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_module_wuth_alias_warning_test.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: import gleam/wibble as wobble +--- +warning: Unused imported module + ┌─ /src/warning/wrn.gleam:1:1 + │ +1 │ import gleam/wibble as wobble + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This imported module is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_const_warnings_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_const_warnings_test.snap new file mode 100644 index 000000000..cb9c77803 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_const_warnings_test.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: const a = 1 +--- +warning: Unused private constant + ┌─ /src/warning/wrn.gleam:1:7 + │ +1 │ const a = 1 + │ ^ This private constant is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_fn_warnings_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_fn_warnings_test.snap new file mode 100644 index 000000000..dbd7dbdb5 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_fn_warnings_test.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "fn a() { 1 }" +--- +warning: Unused private function + ┌─ /src/warning/wrn.gleam:1:1 + │ +1 │ fn a() { 1 } + │ ^^^^^^ This private function is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_type_warnings_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_type_warnings_test.snap new file mode 100644 index 000000000..89847ed5f --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_type_warnings_test.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: type X +--- +warning: Unused private type + ┌─ /src/warning/wrn.gleam:1:1 + │ +1 │ type X + │ ^^^^^^ This private type is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_type_warnings_test3.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_type_warnings_test3.snap new file mode 100644 index 000000000..0dfd6b303 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_type_warnings_test3.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: type X = Int +--- +warning: Unused private type + ┌─ /src/warning/wrn.gleam:1:1 + │ +1 │ type X = Int + │ ^^^^^^^^^^^^ This private type is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_type_warnings_test6.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_type_warnings_test6.snap new file mode 100644 index 000000000..3002d1481 --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_private_type_warnings_test6.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "type X { X }" +--- +warning: Unused private constructor + ┌─ /src/warning/wrn.gleam:1:10 + │ +1 │ type X { X } + │ ^ This private constructor is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_string.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_string.snap new file mode 100644 index 000000000..1982704fb --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_string.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub fn main() {\n \"1\"\n 2\n }" +--- +warning: Unused literal + ┌─ /src/warning/wrn.gleam:3:9 + │ +3 │ "1" + │ ^^^ This value is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_tuple.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_tuple.snap new file mode 100644 index 000000000..6f797966a --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_tuple.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "\n pub fn main() {\n #(1.0, \"Hello world\")\n\t\t\t\t2\n }" +--- +warning: Unused literal + ┌─ /src/warning/wrn.gleam:3:9 + │ +3 │ #(1.0, "Hello world") + │ ^^^^^^^^^^^^^^^^^^^^^ This value is never used + +Hint: You can safely remove it. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_variable_shadowing_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_variable_shadowing_test.snap new file mode 100644 index 000000000..bb460fcec --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_variable_shadowing_test.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn a() { let b = 1 let b = 2 b }" +--- +warning: Unused variable + ┌─ /src/warning/wrn.gleam:1:18 + │ +1 │ pub fn a() { let b = 1 let b = 2 b } + │ ^ This variable is never used + +Hint: You can ignore it with an underscore: `_b`. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_variable_warnings_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_variable_warnings_test.snap new file mode 100644 index 000000000..c905c7c5f --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_variable_warnings_test.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn a(b) { 1 }" +--- +warning: Unused variable + ┌─ /src/warning/wrn.gleam:1:10 + │ +1 │ pub fn a(b) { 1 } + │ ^ This variable is never used + +Hint: You can ignore it with an underscore: `_b`. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_variable_warnings_test2.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_variable_warnings_test2.snap new file mode 100644 index 000000000..9e3b895ad --- /dev/null +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__unused_variable_warnings_test2.snap @@ -0,0 +1,11 @@ +--- +source: compiler-core/src/type_/tests/warnings.rs +expression: "pub fn a() { let b = 1 5 }" +--- +warning: Unused variable + ┌─ /src/warning/wrn.gleam:1:18 + │ +1 │ pub fn a() { let b = 1 5 } + │ ^ This variable is never used + +Hint: You can ignore it with an underscore: `_b`. diff --git a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__warning_variable_never_used_test.snap b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__warning_variable_never_used_test.snap index a1d3468c0..2741e1308 100644 --- a/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__warning_variable_never_used_test.snap +++ b/compiler-core/src/type_/tests/snapshots/glistix_core__type___tests__warnings__warning_variable_never_used_test.snap @@ -1,11 +1,11 @@ --- source: compiler-core/src/type_/tests/warnings.rs -expression: "\npub fn foo() { Ok(5) }\npub fn main() { let five = foo() }" +expression: "\npub fn wibble() { Ok(5) }\npub fn main() { let five = wibble() }" --- warning: Unused variable ┌─ /src/warning/wrn.gleam:3:21 │ -3 │ pub fn main() { let five = foo() } +3 │ pub fn main() { let five = wibble() } │ ^^^^ This variable is never used Hint: You can ignore it with an underscore: `_five`. diff --git a/compiler-core/src/type_/tests/target_implementations.rs b/compiler-core/src/type_/tests/target_implementations.rs index 1a3703407..dc6c851bc 100644 --- a/compiler-core/src/type_/tests/target_implementations.rs +++ b/compiler-core/src/type_/tests/target_implementations.rs @@ -76,7 +76,7 @@ pub fn pure_gleam_2() { pure_gleam_1() * 2 } pub fn erlang_only_function() { assert_targets!( r#" -@external(erlang, "foo", "bar") +@external(erlang, "wibble", "wobble") pub fn erlang_only_1() -> Int pub fn erlang_only_2() { erlang_only_1() * 2 } @@ -114,8 +114,8 @@ pub fn erlang_only_2() { erlang_only_1() * 2 } pub fn externals_only_function() { assert_targets!( r#" -@external(erlang, "foo", "bar") -@external(javascript, "foo", "bar") +@external(erlang, "wibble", "wobble") +@external(javascript, "wibble", "wobble") pub fn all_externals_1() -> Int pub fn all_externals_2() { all_externals_1() * 2 } @@ -153,10 +153,10 @@ pub fn all_externals_2() { all_externals_1() * 2 } pub fn externals_with_pure_gleam_body() { assert_targets!( r#" -@external(javascript, "foo", "bar") +@external(javascript, "wibble", "wobble") pub fn javascript_external_and_pure_body() -> Int { 1 + 1 } -@external(erlang, "foo", "bar") +@external(erlang, "wibble", "wobble") pub fn erlang_external_and_pure_body() -> Int { 1 + 1 } pub fn pure_gleam() { @@ -208,10 +208,10 @@ pub fn pure_gleam() { pub fn erlang_external_with_javascript_body() { assert_targets!( r#" -@external(javascript, "foo", "bar") +@external(javascript, "wibble", "wobble") fn javascript_only() -> Int -@external(erlang, "foo", "bar") +@external(erlang, "wibble", "wobble") pub fn erlang_external_and_javascript_body() -> Int { javascript_only() } pub fn all_externals() -> Int { erlang_external_and_javascript_body() } @@ -261,10 +261,10 @@ pub fn all_externals() -> Int { erlang_external_and_javascript_body() } pub fn javascript_external_with_erlang_body() { assert_targets!( r#" -@external(erlang, "foo", "bar") +@external(erlang, "wibble", "wobble") pub fn erlang_only() -> Int -@external(javascript, "foo", "bar") +@external(javascript, "wibble", "wobble") pub fn javascript_external_and_erlang_body() -> Int { erlang_only() } pub fn all_externals() -> Int { javascript_external_and_erlang_body() } @@ -314,10 +314,10 @@ pub fn all_externals() -> Int { javascript_external_and_erlang_body() } pub fn function_with_no_valid_implementations() { assert_module_error!( r#" -@external(javascript, "foo", "bar") +@external(javascript, "wibble", "wobble") fn javascript_only() -> Int -@external(erlang, "foo", "bar") +@external(erlang, "wibble", "wobble") fn erlang_only() -> Int pub fn main() { @@ -331,11 +331,11 @@ pub fn main() { #[test] pub fn invalid_both_and_one_called_from_erlang() { let src = r#" -@external(erlang, "foo", "bar") -@external(javascript, "foo", "bar") +@external(erlang, "wibble", "wobble") +@external(javascript, "wibble", "wobble") fn both_external() -> Int -@external(javascript, "foo", "bar") +@external(javascript, "wibble", "wobble") fn javascript_only() -> Int pub fn no_valid_erlang_impl() { @@ -357,11 +357,11 @@ pub fn no_valid_erlang_impl() { #[test] pub fn invalid_both_and_one_called_from_javascript() { let src = r#" -@external(erlang, "foo", "bar") -@external(javascript, "foo", "bar") +@external(erlang, "wibble", "wobble") +@external(javascript, "wibble", "wobble") fn both_external() -> Int -@external(erlang, "foo", "bar") +@external(erlang, "wibble", "wobble") fn erlang_only() -> Int pub fn no_valid_javascript_impl() { @@ -383,11 +383,11 @@ pub fn no_valid_javascript_impl() { #[test] pub fn invalid_both_and_one_called_from_erlang_flipped() { let src = r#" -@external(erlang, "foo", "bar") -@external(javascript, "foo", "bar") +@external(erlang, "wibble", "wobble") +@external(javascript, "wibble", "wobble") fn both_external() -> Int -@external(javascript, "foo", "bar") +@external(javascript, "wibble", "wobble") fn javascript_only() -> Int pub fn no_valid_erlang_impl() { @@ -409,11 +409,11 @@ pub fn no_valid_erlang_impl() { #[test] pub fn invalid_both_and_one_called_from_javascript_flipped() { let src = r#" -@external(erlang, "foo", "bar") -@external(javascript, "foo", "bar") +@external(erlang, "wibble", "wobble") +@external(javascript, "wibble", "wobble") fn both_external() -> Int -@external(erlang, "foo", "bar") +@external(erlang, "wibble", "wobble") fn erlang_only() -> Int pub fn no_valid_javascript_impl() { @@ -435,7 +435,7 @@ pub fn no_valid_javascript_impl() { #[test] pub fn invalid_erlang_with_external() { let src = r#" -@external(javascript, "foo", "bar") +@external(javascript, "wibble", "wobble") fn javascript_only() -> Int @external(javascript, "one", "two") @@ -457,7 +457,7 @@ pub fn no_valid_erlang_impl() { #[test] pub fn invalid_javascript_with_external() { let src = r#" -@external(erlang, "foo", "bar") +@external(erlang, "wibble", "wobble") fn erlang_only() -> Int @external(erlang, "one", "two") diff --git a/compiler-core/src/type_/tests/type_alias.rs b/compiler-core/src/type_/tests/type_alias.rs index aa0e2ef0e..6ea2c3123 100644 --- a/compiler-core/src/type_/tests/type_alias.rs +++ b/compiler-core/src/type_/tests/type_alias.rs @@ -1,4 +1,6 @@ -use crate::{assert_infer_with_module, assert_module_error, assert_module_infer}; +use crate::{ + assert_infer_with_module, assert_module_error, assert_module_infer, assert_with_module_error, +}; #[test] fn alias_dep() { @@ -137,3 +139,12 @@ fn example(a: X) { "# ); } + +#[test] +fn conflict_with_import() { + // We cannot declare a type with the same name as an imported type + assert_with_module_error!( + ("wibble", "pub type Wobble = String"), + "import wibble.{type Wobble} type Wobble = Int", + ); +} diff --git a/compiler-core/src/type_/tests/use_.rs b/compiler-core/src/type_/tests/use_.rs index 927911340..5493ec4fa 100644 --- a/compiler-core/src/type_/tests/use_.rs +++ b/compiler-core/src/type_/tests/use_.rs @@ -479,3 +479,17 @@ fn apply(arg, fun) { "# ); } + +#[test] +fn multiple_bad_statement_use_fault_tolerance() { + assert_error!( + r#" +let x = fn(f) { f() + 1 } +use <- x() + +1 + 2.0 +3.0 + 4 +5 +"# + ); +} diff --git a/compiler-core/src/type_/tests/warnings.rs b/compiler-core/src/type_/tests/warnings.rs index d53109aba..39ccc04c9 100644 --- a/compiler-core/src/type_/tests/warnings.rs +++ b/compiler-core/src/type_/tests/warnings.rs @@ -1,5 +1,4 @@ use super::*; -use crate::ast::TodoKind; use crate::{assert_no_warnings, assert_warning, assert_warnings_with_imports}; #[test] @@ -14,16 +13,7 @@ fn unknown_label() { #[test] fn todo_warning_test() { - assert_warning!( - "fn main() { 1 == todo }", - Warning::Todo { - kind: TodoKind::Keyword, - location: SrcSpan { start: 17, end: 21 }, - typ: Arc::new(Type::Var { - type_: Arc::new(RefCell::new(TypeVar::Link { type_: int() })), - }), - }, - ); + assert_warning!("pub fn main() { 1 == todo }"); } // https://github.com/gleam-lang/gleam/issues/1669 @@ -48,8 +38,9 @@ fn todo_with_known_type() { #[test] fn empty_func_warning_test() { assert_warning!( - "pub fn main() { foo() } - pub fn foo() { }" + "pub fn main() { wibble() } +pub fn wibble() { } +" ); } @@ -57,8 +48,8 @@ fn empty_func_warning_test() { fn warning_variable_never_used_test() { assert_warning!( " -pub fn foo() { Ok(5) } -pub fn main() { let five = foo() }" +pub fn wibble() { Ok(5) } +pub fn main() { let five = wibble() }" ); } @@ -80,14 +71,11 @@ fn result_discard_warning_test() { // Implicitly discarded Results emit warnings assert_warning!( " -fn foo() { Ok(5) } -fn main() { - foo() - 5 -}", - Warning::ImplicitlyDiscardedResult { - location: SrcSpan { start: 34, end: 39 } - } +pub fn wibble() { Ok(5) } +pub fn main() { + wibble() + 5 +}" ); } @@ -96,42 +84,29 @@ fn result_discard_warning_test2() { // Explicitly discarded Results do not emit warnings assert_no_warnings!( " -pub fn foo() { Ok(5) } -pub fn main() { let _ = foo() 5 }", +pub fn wibble() { Ok(5) } +pub fn main() { let _ = wibble() 5 }", ); } #[test] fn unused_int() { - assert_warning!( - "fn main() { 1 2 }", - Warning::UnusedLiteral { - location: SrcSpan { start: 12, end: 13 } - } - ); + assert_warning!("pub fn main() { 1 2 }"); } #[test] fn unused_float() { - assert_warning!( - "fn main() { 1.0 2 }", - Warning::UnusedLiteral { - location: SrcSpan { start: 12, end: 15 } - } - ); + assert_warning!("pub fn main() { 1.0 2 }"); } #[test] fn unused_string() { assert_warning!( " - fn main() { + pub fn main() { \"1\" - 2 - }", - Warning::UnusedLiteral { - location: SrcSpan { start: 25, end: 28 } - } + 2 + }" ); } @@ -139,13 +114,10 @@ fn unused_string() { fn unused_bit_array() { assert_warning!( " - fn main() { + pub fn main() { <<3>> 2 - }", - Warning::UnusedLiteral { - location: SrcSpan { start: 25, end: 30 } - } + }" ); } @@ -153,13 +125,10 @@ fn unused_bit_array() { fn unused_tuple() { assert_warning!( " - fn main() { + pub fn main() { #(1.0, \"Hello world\") 2 - }", - Warning::UnusedLiteral { - location: SrcSpan { start: 25, end: 46 } - } + }" ); } @@ -167,13 +136,10 @@ fn unused_tuple() { fn unused_list() { assert_warning!( " - fn main() { + pub fn main() { [1, 2, 3] 2 - }", - Warning::UnusedLiteral { - location: SrcSpan { start: 25, end: 34 } - } + }" ); } @@ -205,13 +171,7 @@ fn record_update_warnings_test2() { let past = Person(\"Quinn\", 27) let present = Person(..past) present - }", - Warning::NoFieldsRecordUpdate { - location: SrcSpan { - start: 182, - end: 196 - } - } + }" ); } @@ -227,27 +187,14 @@ fn record_update_warnings_test3() { let past = Person(\"Quinn\", 27) let present = Person(..past, name: \"Quinn\", age: 28) present - }", - Warning::AllFieldsRecordUpdate { - location: SrcSpan { - start: 182, - end: 220 - } - } + }" ); } #[test] fn unused_private_type_warnings_test() { // External type - assert_warning!( - "type X", - Warning::UnusedType { - name: "X".into(), - location: SrcSpan { start: 0, end: 6 }, - imported: false - } - ); + assert_warning!("type X"); } #[test] @@ -258,14 +205,7 @@ fn unused_private_type_warnings_test2() { #[test] fn unused_private_type_warnings_test3() { // Type alias - assert_warning!( - "type X = Int", - Warning::UnusedType { - name: "X".into(), - location: SrcSpan { start: 0, end: 12 }, - imported: false - } - ); + assert_warning!("type X = Int"); } #[test] @@ -281,14 +221,7 @@ fn unused_private_type_warnings_test5() { #[test] fn unused_private_type_warnings_test6() { // Custom type - assert_warning!( - "type X { X }", - Warning::UnusedConstructor { - name: "X".into(), - location: SrcSpan { start: 9, end: 10 }, - imported: false - } - ); + assert_warning!("type X { X }"); } #[test] @@ -313,13 +246,7 @@ pub fn a() { #[test] fn unused_private_fn_warnings_test() { - assert_warning!( - "fn a() { 1 }", - Warning::UnusedPrivateFunction { - name: "a".into(), - location: SrcSpan { start: 0, end: 6 }, - } - ); + assert_warning!("fn a() { 1 }"); } #[test] @@ -334,13 +261,7 @@ fn used_private_fn_warnings_test2() { #[test] fn unused_private_const_warnings_test() { - assert_warning!( - "const a = 1", - Warning::UnusedPrivateModuleConstant { - name: "a".into(), - location: SrcSpan { start: 6, end: 7 }, - } - ); + assert_warning!("const a = 1"); } #[test] @@ -356,13 +277,7 @@ fn used_private_const_warnings_test2() { #[test] fn unused_variable_warnings_test() { // function argument - assert_warning!( - "pub fn a(b) { 1 }", - Warning::UnusedVariable { - name: "b".into(), - location: SrcSpan { start: 9, end: 10 }, - } - ); + assert_warning!("pub fn a(b) { 1 }"); } #[test] @@ -373,13 +288,7 @@ fn used_variable_warnings_test() { #[test] fn unused_variable_warnings_test2() { // Simple let - assert_warning!( - "pub fn a() { let b = 1 5 }", - Warning::UnusedVariable { - name: "b".into(), - location: SrcSpan { start: 17, end: 18 }, - } - ); + assert_warning!("pub fn a() { let b = 1 5 }"); } #[test] @@ -389,13 +298,7 @@ fn used_variable_warnings_test2() { #[test] fn unused_variable_shadowing_test() { - assert_warning!( - "pub fn a() { let b = 1 let b = 2 b }", - Warning::UnusedVariable { - name: "b".into(), - location: SrcSpan { start: 17, end: 18 }, - } - ); + assert_warning!("pub fn a() { let b = 1 let b = 2 b }"); } #[test] @@ -406,13 +309,7 @@ fn used_variable_shadowing_test() { #[test] fn unused_destructure() { // Destructure - assert_warning!( - "pub fn a(b) { case b { #(c, _) -> 5 } }", - Warning::UnusedVariable { - name: "c".into(), - location: SrcSpan { start: 25, end: 26 }, - } - ); + assert_warning!("pub fn a(b) { case b { #(c, _) -> 5 } }"); } #[test] @@ -423,49 +320,25 @@ fn used_destructure() { #[test] fn unused_imported_module_warnings_test() { assert_warning!( - ("gleam/foo", "pub fn bar() { 1 }"), - "import gleam/foo", - Warning::UnusedImportedModule { - name: "foo".into(), - location: SrcSpan { start: 0, end: 16 }, - } + ("gleam/wibble", "pub fn wobble() { 1 }"), + "import gleam/wibble" ); } #[test] fn unused_imported_module_with_alias_warnings_test() { assert_warning!( - ("gleam/foo", "pub fn bar() { 1 }"), - "import gleam/foo as bar", - Warning::UnusedImportedModule { - name: "bar".into(), - location: SrcSpan { start: 0, end: 23 }, - } + ("gleam/wibble", "pub fn wobble() { 1 }"), + "import gleam/wibble as wobble" ); } // https://github.com/gleam-lang/gleam/issues/2326 #[test] fn unused_imported_module_with_alias_and_unqualified_name_warnings_test() { - let warnings = get_warnings( - "import gleam/one.{two} as three", - vec![("thepackage", "gleam/one", "pub fn two() { 1 }")], - ); - assert!(!warnings.is_empty()); - assert_eq!( - Warning::UnusedImportedValue { - name: "two".into(), - location: SrcSpan { start: 18, end: 21 }, - }, - warnings[0] - ); - assert_eq!( - Warning::UnusedImportedModuleAlias { - alias: "three".into(), - location: SrcSpan { start: 23, end: 31 }, - module_name: "gleam/one".into() - }, - warnings[1] + assert_warning!( + ("thepackage", "gleam/one", "pub fn two() { 1 }"), + "import gleam/one.{two} as three" ); } @@ -473,39 +346,52 @@ fn unused_imported_module_with_alias_and_unqualified_name_warnings_test() { fn unused_imported_module_with_alias_and_unqualified_name_no_warnings_test() { assert_warning!( ("package", "gleam/one", "pub fn two() { 1 }"), - "import gleam/one.{two} as three\npub fn baz() { two() }" + "import gleam/one.{two} as three\npub fn wibble() { two() }" ); } #[test] fn unused_imported_module_no_warning_on_used_function_test() { assert_no_warnings!( - ("thepackage", "gleam/foo", "pub fn bar() { 1 }"), - "import gleam/foo pub fn baz() { foo.bar() }", + ("thepackage", "gleam/wibble", "pub fn wobble() { 1 }"), + "import gleam/wibble pub fn wibble() { wibble.wobble() }", ); } #[test] fn unused_imported_module_no_warning_on_used_type_test() { assert_no_warnings!( - ("thepackage", "gleam/foo", "pub type Foo = Int"), - "import gleam/foo pub fn baz(a: foo.Foo) { a }", + ("thepackage", "gleam/wibble", "pub type Wibble = Int"), + "import gleam/wibble pub fn wibble(a: wibble.Wibble) { a }", ); } #[test] fn unused_imported_module_no_warning_on_used_unqualified_function_test() { assert_no_warnings!( - ("thepackage", "gleam/foo", "pub fn bar() { 1 }"), - "import gleam/foo.{bar} pub fn baz() { bar() }", + ("thepackage", "gleam/wibble", "pub fn wobble() { 1 }"), + "import gleam/wibble.{wobble} pub fn wibble() { wobble() }", ); } #[test] fn unused_imported_module_no_warning_on_used_unqualified_type_test() { assert_no_warnings!( - ("thepackage", "gleam/foo", "pub type Foo = Int"), - "import gleam/foo.{type Foo} pub fn baz(a: Foo) { a }", + ("thepackage", "gleam/wibble", "pub type Wibble = Int"), + "import gleam/wibble.{type Wibble} pub fn wibble(a: Wibble) { a }", + ); +} + +// https://github.com/gleam-lang/gleam/issues/3313 +#[test] +fn imported_module_with_alias_no_warning_when_only_used_in_case_test() { + assert_no_warnings!( + ( + "thepackage", + "gleam/wibble", + "pub type Wibble { Wibble(Int) }" + ), + "import gleam/wibble as f\npub fn wibble(a) { case a { f.Wibble(int) -> { int } } }", ); } @@ -1088,66 +974,48 @@ fn const_bytes_option() { #[test] fn unused_module_wuth_alias_warning_test() { assert_warning!( - ("gleam/foo", "pub const one = 1"), - "import gleam/foo as bar", - Warning::UnusedImportedModule { - name: "bar".into(), - location: SrcSpan { start: 0, end: 23 }, - } + ("gleam/wibble", "pub const one = 1"), + "import gleam/wibble as wobble" ); } #[test] fn unused_alias_warning_test() { assert_warnings_with_imports!( - ("gleam/foo", "pub const one = 1"); + ("gleam/wibble", "pub const one = 1"); r#" - import gleam/foo.{one} as bar + import gleam/wibble.{one} as wobble const one = one "#, - Warning::UnusedPrivateModuleConstant { - name: "one".into(), - location: SrcSpan { start: 61, end: 64 }, - }, - Warning::UnusedImportedModuleAlias { - alias:"bar".into(), - location: SrcSpan { start: 36, end: 42 }, - module_name: "gleam/foo".into(), - } ); } #[test] fn used_type_with_import_alias_no_warning_test() { assert_no_warnings!( - ("gleam", "gleam/foo", "pub const one = 1"), - "import gleam/foo as _bar" + ("gleam", "gleam/wibble", "pub const one = 1"), + "import gleam/wibble as _wobble" ); } #[test] fn discarded_module_no_warnings_test() { - assert_no_warnings!(("gleam", "foo", "pub const one = 1"), "import foo as _bar"); + assert_no_warnings!( + ("gleam", "wibble", "pub const one = 1"), + "import wibble as _wobble" + ); } #[test] fn unused_alias_for_duplicate_module_no_warning_for_alias_test() { assert_warnings_with_imports!( - ("a/foo", "pub const one = 1"), - ("b/foo", "pub const two = 2"); + ("a/wibble", "pub const one = 1"), + ("b/wibble", "pub const two = 2"); r#" - import a/foo - import b/foo as bar - const one = foo.one + import a/wibble + import b/wibble as wobble + const one = wibble.one "#, - Warning::UnusedPrivateModuleConstant { - name: "one".into(), - location: SrcSpan { start: 76, end: 79 }, - }, - Warning::UnusedImportedModule { - name: "bar".into(), - location: SrcSpan { start: 38, end: 57 }, - } ); } @@ -1160,10 +1028,7 @@ pub fn main(x) { _ -> Error(Nil) } Nil -}", - Warning::ImplicitlyDiscardedResult { - location: SrcSpan { start: 20, end: 52 } - } +}" ); } @@ -2012,3 +1877,95 @@ fn warnings_for_matches_on_literal_values_that_are_not_like_an_if_2() { "# ); } + +#[test] +fn redundant_function_capture_in_pipe_1() { + assert_warning!( + " + pub fn wibble(_, _) { 1 } + + pub fn main() { + 1 |> wibble(_, 2) |> wibble(2) + } +" + ); +} + +#[test] +fn redundant_function_capture_in_pipe_2() { + assert_warning!( + " + pub fn wobble(_) { 1 } + + pub fn main() { + 1 |> wobble(_) |> wobble + } +" + ); +} + +#[test] +fn redundant_function_capture_in_pipe_3() { + assert_warning!( + " + pub fn wobble(_) { 1 } + + pub fn main() { + 1 |> wobble |> wobble(_) + } +" + ); +} + +#[test] +fn redundant_function_capture_in_pipe_4() { + assert_warning!( + " + pub fn wibble(_, _) { 1 } + + pub fn main() { + 1 |> wibble(2) |> wibble(_, 2) + } +" + ); +} + +#[test] +fn redundant_function_capture_in_pipe_5() { + assert_no_warnings!( + " + pub fn wibble(_, _) { 1 } + + pub fn main() { + 1 |> wibble(2, _) + } +" + ); +} + +#[test] +fn deprecated_list_append_syntax() { + assert_warning!( + r#" + pub fn main() { + let letters = ["b", "c"] + ["a"..letters] + } + "# + ); +} + +#[test] +fn deprecated_list_pattern_syntax() { + assert_warning!( + r#" + pub fn main() { + let letters = ["b", "c"] + case letters { + ["a"..rest] -> rest + _ -> [] + } + } + "# + ); +} diff --git a/compiler-core/src/warning.rs b/compiler-core/src/warning.rs index 5cfe999c1..15565a0ba 100644 --- a/compiler-core/src/warning.rs +++ b/compiler-core/src/warning.rs @@ -1,5 +1,5 @@ use crate::{ - ast::TodoKind, + ast::{SrcSpan, TodoKind}, diagnostic::{self, Diagnostic, Location}, error::wrap, type_::{ @@ -148,9 +148,34 @@ pub enum Warning { src: EcoString, warning: type_::Warning, }, + InvalidSource { path: Utf8PathBuf, }, + + DeprecatedSyntax { + path: Utf8PathBuf, + src: EcoString, + warning: DeprecatedSyntaxWarning, + }, +} + +#[derive(Debug, Clone, Eq, PartialEq, Copy)] +pub enum DeprecatedSyntaxWarning { + /// If someone uses the deprecated syntax to append to a list: + /// `["a"..rest]`, notice how there's no comma! + DeprecatedListPrepend { location: SrcSpan }, + + /// If someone uses the deprecated syntax to pattern match on a list: + /// ```gleam + /// case list { + /// [first..rest] -> todo + /// // ^^ notice there's no comma! + /// _ -> + /// } + /// ``` + /// + DeprecatedListPattern { location: SrcSpan }, } impl Warning { @@ -168,6 +193,56 @@ only lowercase alphanumeric characters or underscores." "Rename `{path}` to be valid, or remove this file from the project source." )), }, + + Warning::DeprecatedSyntax { + path, + src, + warning: DeprecatedSyntaxWarning::DeprecatedListPrepend { location }, + } => Diagnostic { + title: "Deprecated prepend syntax".into(), + text: wrap( + "This syntax for prepending to a list is deprecated. +When prepending an item to a list it should be preceded by a comma, \ +like this: `[item, ..list]`.", + ), + + hint: None, + level: diagnostic::Level::Warning, + location: Some(Location { + label: diagnostic::Label { + text: Some("This spread should be preceded by a comma".into()), + span: *location, + }, + path: path.clone(), + src: src.clone(), + extra_labels: vec![], + }), + }, + + Warning::DeprecatedSyntax { + path, + src, + warning: DeprecatedSyntaxWarning::DeprecatedListPattern { location }, + } => Diagnostic { + title: "Deprecated list pattern matching syntax".into(), + text: wrap( + "This syntax for pattern matching on a list is deprecated. +When matching on the rest of a list it should always be preceded by a comma, \ +like this: `[item, ..list]`.", + ), + hint: None, + level: diagnostic::Level::Warning, + location: Some(Location { + label: diagnostic::Label { + text: Some("This spread should be preceded by a comma".into()), + span: *location, + }, + path: path.clone(), + src: src.clone(), + extra_labels: vec![], + }), + }, + Self::Type { path, warning, src } => match warning { type_::Warning::Todo { kind, @@ -830,6 +905,27 @@ Your code will crash before reaching this point.", }), } } + + type_::Warning::RedundantPipeFunctionCapture { location } => Diagnostic { + title: "Redundant function capture".into(), + text: wrap( + "This function capture is redundant since the value is already piped as \ +the first argument of this call. + +See: https://tour.gleam.run/functions/pipelines/", + ), + hint: None, + level: diagnostic::Level::Warning, + location: Some(Location { + label: diagnostic::Label { + text: Some("You can safely remove this".into()), + span: *location, + }, + path: path.clone(), + src: src.clone(), + extra_labels: vec![], + }), + }, }, } } diff --git a/compiler-core/templates/docs-css/index.css b/compiler-core/templates/docs-css/index.css index 27f45cded..361c275ac 100644 --- a/compiler-core/templates/docs-css/index.css +++ b/compiler-core/templates/docs-css/index.css @@ -124,7 +124,7 @@ --accent: var(--pink); /* Sizes */ - --content-width: 750px; + --content-width: 772px; --header-height: 60px; --hash-offset: calc(var(--header-height) * 1.67); --sidebar-width: 240px; diff --git a/compiler-wasm/src/wasm_filesystem.rs b/compiler-wasm/src/wasm_filesystem.rs index 0a2d1f818..b035e0ce6 100644 --- a/compiler-wasm/src/wasm_filesystem.rs +++ b/compiler-wasm/src/wasm_filesystem.rs @@ -70,6 +70,10 @@ impl FileSystemWriter for WasmFileSystem { tracing::trace!("write_bytes {:?}", path); self.imfs.write_bytes(path, content) } + + fn exists(&self, path: &Utf8Path) -> bool { + self.imfs.exists(path) + } } impl FileSystemReader for WasmFileSystem { diff --git a/docs/annoyances.md b/docs/annoyances.md index be352ad5b..d8ce36287 100644 --- a/docs/annoyances.md +++ b/docs/annoyances.md @@ -6,8 +6,6 @@ Gleam code today, so that we can devise solutions to them in future. There are other annoyances that have known solutions that are yet to be implemented. These are tracked in the Gleam issue tracker instead. -## Cannot infer what targets a package supports - ## Dynamic decoding boilerplate ## JSON (de|en)coding boilerplate @@ -18,55 +16,8 @@ implemented. These are tracked in the Gleam issue tracker instead. For example, regex compilation. -## No good story for creating CLI programs - -The Erlang virtual machine is not typically installed on a user's computer, and -bytecode compiled for it is not easy to distribute. - -JavaScript runtimes do better, but it is still not as good an experience as -languages that compile to a single binary, and forcing Gleam programmers to use -promises and a single thread is not ideal. - ## Runtime debugging is basic Rust's `dbg!` and Elixir's `dbg` were mentioned as good additions. -## Last of if/else makes boolean logic less pretty and more alien - -With case: - -```gleam -case str == "+" { - True -> Keep(Plus) - False -> - case all(str, is_ascii_letter) { - True -> Keep(Ident(str)) - False -> - case all(str, is_digit) { - True -> { - let assert Ok(int) = int.parse(str) - Keep(Number(int) - } - False -> Next - } -} -``` - -With use: - -```gleam -use <- bool.guard( - when: str == "+", - return: Keep(Plus), -) -use <- bool.guard( - when: all(str, is_ascii_letter), - return: Keep(Ident(str), -) -use <- bool.guard( - when: !all(str, is_digit), - return: Next, -) -let assert Ok(int) = int.parse(str) -Keep(Number(int)) -``` +We have a reserved `echo` keyword for this. diff --git a/nix/glistix.nix b/nix/glistix.nix index fd49f03cd..1a0b2a1d5 100644 --- a/nix/glistix.nix +++ b/nix/glistix.nix @@ -39,7 +39,7 @@ rustPlatform.buildRustPackage { buildInputs = [ openssl ] ++ lib.optionals stdenv.isDarwin [ Security SystemConfiguration ]; - cargoHash = "sha256-COZ3EwLZCM/mC5Okulb4FX+7c9CpYwGIhFVZa2U3BsI="; + cargoHash = "sha256-I5oYfY4zcuhDe5WsbWzso+CFafqymOYEmxD4DmV/NOo="; meta = with lib; { description = "A fork of the Gleam compiler with a Nix backend"; diff --git a/test-package-compiler/cases/import_cycle_multi/gleam.toml b/test-package-compiler/cases/import_cycle_multi/gleam.toml new file mode 100644 index 000000000..89e51684c --- /dev/null +++ b/test-package-compiler/cases/import_cycle_multi/gleam.toml @@ -0,0 +1,3 @@ +name = "importy" +version = "0.1.0" +target = "erlang" diff --git a/test-package-compiler/cases/import_cycle_multi/src/one.gleam b/test-package-compiler/cases/import_cycle_multi/src/one.gleam new file mode 100644 index 000000000..d6ec06a97 --- /dev/null +++ b/test-package-compiler/cases/import_cycle_multi/src/one.gleam @@ -0,0 +1 @@ +import two diff --git a/test-package-compiler/cases/import_cycle_multi/src/two.gleam b/test-package-compiler/cases/import_cycle_multi/src/two.gleam new file mode 100644 index 000000000..4481f61e4 --- /dev/null +++ b/test-package-compiler/cases/import_cycle_multi/src/two.gleam @@ -0,0 +1 @@ +import one diff --git a/test-package-compiler/src/generated_tests.rs b/test-package-compiler/src/generated_tests.rs index 1f4e6e951..aefea2a1f 100644 --- a/test-package-compiler/src/generated_tests.rs +++ b/test-package-compiler/src/generated_tests.rs @@ -145,6 +145,18 @@ fn import_cycle() { ); } +#[rustfmt::skip] +#[test] +fn import_cycle_multi() { + let output = + crate::prepare("./cases/import_cycle_multi"); + insta::assert_snapshot!( + "import_cycle_multi", + output, + "./cases/import_cycle_multi" + ); +} + #[rustfmt::skip] #[test] fn import_shadowed_name_warning() { diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__alias_unqualified_import.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__alias_unqualified_import.snap index 02edb73c7..875434e91 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__alias_unqualified_import.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__alias_unqualified_import.snap @@ -26,7 +26,7 @@ id(X) -> <.cache binary> //// /out/lib/the_package/_gleam_artefacts/two.cache_meta -<80 byte binary> +<88 byte binary> //// /out/lib/the_package/_gleam_artefacts/two.erl -module(two). diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_bug_752.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_bug_752.snap index 40fadf110..ceb2c58b6 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_bug_752.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_bug_752.snap @@ -23,7 +23,7 @@ expression: "./cases/erlang_bug_752" <.cache binary> //// /out/lib/the_package/_gleam_artefacts/two.cache_meta -<76 byte binary> +<84 byte binary> //// /out/lib/the_package/_gleam_artefacts/two.erl -module(two). diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_escape_names.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_escape_names.snap index 9d6490619..16c218ce9 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_escape_names.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_escape_names.snap @@ -23,7 +23,7 @@ expression: "./cases/erlang_escape_names" <.cache binary> //// /out/lib/the_package/_gleam_artefacts/two.cache_meta -<128 byte binary> +<136 byte binary> //// /out/lib/the_package/_gleam_artefacts/two.erl -module(two). diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_import.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_import.snap index 5eaf595a0..b23c8694b 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_import.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_import.snap @@ -6,7 +6,7 @@ expression: "./cases/erlang_import" <.cache binary> //// /out/lib/the_package/_gleam_artefacts/one.cache_meta -<80 byte binary> +<88 byte binary> //// /out/lib/the_package/_gleam_artefacts/one.erl -module(one). diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_import_shadowing_prelude.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_import_shadowing_prelude.snap index a2e014eba..0a512a6ed 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_import_shadowing_prelude.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_import_shadowing_prelude.snap @@ -23,7 +23,7 @@ expression: "./cases/erlang_import_shadowing_prelude" <.cache binary> //// /out/lib/the_package/_gleam_artefacts/two.cache_meta -<80 byte binary> +<88 byte binary> //// /out/lib/the_package/_gleam_artefacts/two.erl -module(two). diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_nested_qualified_constant.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_nested_qualified_constant.snap index eeb0646b9..5f3afcf43 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_nested_qualified_constant.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__erlang_nested_qualified_constant.snap @@ -23,7 +23,7 @@ expression: "./cases/erlang_nested_qualified_constant" <.cache binary> //// /out/lib/the_package/_gleam_artefacts/two.cache_meta -<92 byte binary> +<100 byte binary> //// /out/lib/the_package/_gleam_artefacts/two.erl -module(two). diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__import_cycle.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__import_cycle.snap index c51092c0b..be3980256 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__import_cycle.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__import_cycle.snap @@ -1,9 +1,12 @@ --- source: test-package-compiler/src/generated_tests.rs -assertion_line: 141 expression: "./cases/import_cycle" --- error: Import cycle + ┌─ src/one.gleam:1:1 + │ +1 │ import one + │ ^ Imported here The import statements for these modules form a cycle: @@ -12,4 +15,3 @@ The import statements for these modules form a cycle: └─────┘ Gleam doesn't support dependency cycles like these, please break the cycle to continue. - diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__import_cycle_multi.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__import_cycle_multi.snap new file mode 100644 index 000000000..19aa07e64 --- /dev/null +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__import_cycle_multi.snap @@ -0,0 +1,24 @@ +--- +source: test-package-compiler/src/generated_tests.rs +expression: "./cases/import_cycle_multi" +--- +error: Import cycle + ┌─ src/two.gleam:1:1 + │ +1 │ import one + │ ^ Imported here + │ + ┌─ src/one.gleam:1:1 + │ +1 │ import two + │ ^ Imported here + +The import statements for these modules form a cycle: + + ┌─────┐ + │ two + │ ↓ + │ one + └─────┘ +Gleam doesn't support dependency cycles like these, please break the +cycle to continue. diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__import_shadowed_name_warning.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__import_shadowed_name_warning.snap index 1ee775856..aee8a906d 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__import_shadowed_name_warning.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__import_shadowed_name_warning.snap @@ -23,7 +23,7 @@ expression: "./cases/import_shadowed_name_warning" <.cache binary> //// /out/lib/the_package/_gleam_artefacts/two.cache_meta -<112 byte binary> +<120 byte binary> //// /out/lib/the_package/_gleam_artefacts/two.erl -module(two). diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_constants.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_constants.snap index 36f47279f..9bdb84324 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_constants.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_constants.snap @@ -27,7 +27,7 @@ expression: "./cases/imported_constants" <.cache binary> //// /out/lib/the_package/_gleam_artefacts/two.cache_meta -<316 byte binary> +<324 byte binary> //// /out/lib/the_package/_gleam_artefacts/two.erl -module(two). diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_external_fns.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_external_fns.snap index e6279d48f..3e0f7a2a7 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_external_fns.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_external_fns.snap @@ -23,7 +23,7 @@ thing() -> <.cache binary> //// /out/lib/the_package/_gleam_artefacts/two.cache_meta -<303 byte binary> +<319 byte binary> //// /out/lib/the_package/_gleam_artefacts/two.erl -module(two). diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_record_constructors.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_record_constructors.snap index 7b637b109..70c118f08 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_record_constructors.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__imported_record_constructors.snap @@ -27,7 +27,7 @@ expression: "./cases/imported_record_constructors" <.cache binary> //// /out/lib/the_package/_gleam_artefacts/two.cache_meta -<475 byte binary> +<491 byte binary> //// /out/lib/the_package/_gleam_artefacts/two.erl -module(two). diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__javascript_import.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__javascript_import.snap index ba6f49310..e79a95a77 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__javascript_import.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__javascript_import.snap @@ -12,7 +12,7 @@ expression: "./cases/javascript_import" <.cache binary> //// /out/lib/the_package/_gleam_artefacts/two.cache_meta -<72 byte binary> +<80 byte binary> //// /out/lib/the_package/gleam.d.mts export * from "../prelude.d.mts"; @@ -47,4 +47,4 @@ export const x: $two.A$; /// import * as $two from "./one/two.mjs"; -export const x = new $two.A(); +export const x = /* @__PURE__ */ new $two.A(); diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__opaque_type_accessor.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__opaque_type_accessor.snap index 6d1332d2a..00efac7b3 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__opaque_type_accessor.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__opaque_type_accessor.snap @@ -14,3 +14,14 @@ The value being accessed has this type: It does not have any fields. +error: Unknown record field + ┌─ src/two.gleam:8:19 + │ +8 │ let score = user.score + │ ^ This field does not exist + +The value being accessed has this type: + + User + +It does not have any fields. diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__opaque_type_destructure.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__opaque_type_destructure.snap index a0d9a3883..a52beba6c 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__opaque_type_destructure.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__opaque_type_destructure.snap @@ -9,3 +9,11 @@ error: Unknown module field │ ^ The module `one` does not have a `User` value. + +error: Unknown variable + ┌─ src/two.gleam:8:5 + │ +8 │ #(name, score) + │ ^ + +The name `name` is not in scope here. diff --git a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__variable_or_module.snap b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__variable_or_module.snap index 27eb5c11a..9df63e3f7 100644 --- a/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__variable_or_module.snap +++ b/test-package-compiler/src/snapshots/test_package_compiler__generated_tests__variable_or_module.snap @@ -6,7 +6,7 @@ expression: "./cases/variable_or_module" <.cache binary> //// /out/lib/the_package/_gleam_artefacts/main.cache_meta -<110 byte binary> +<118 byte binary> //// /out/lib/the_package/_gleam_artefacts/main.erl -module(main). diff --git a/test/language/test/language_test.gleam b/test/language/test/language_test.gleam index ef27ceb80..b188cf639 100644 --- a/test/language/test/language_test.gleam +++ b/test/language/test/language_test.gleam @@ -657,6 +657,55 @@ fn clause_guard_tests() -> List(Test) { _ -> 1 }) }), + "1 + 1 == 2" + |> example(fn() { + assert_equal(0, case Nil { + _ if 1 + 1 == 2 -> 0 + _ -> 1 + }) + }), + "47 % 5 == 2" + |> example(fn() { + assert_equal(0, case Nil { + _ if 47 % 5 == 2 -> 0 + _ -> 1 + }) + }), + "3 * 5 == 15" + |> example(fn() { + assert_equal(0, case Nil { + _ if 3 * 5 == 15 -> 0 + _ -> 1 + }) + }), + "3 * 5 + 1 == 16" + |> example(fn() { + assert_equal(0, case Nil { + _ if 3 * 5 + 1 == 16 -> 0 + _ -> 1 + }) + }), + "1 + 3 * 5 == 16" + |> example(fn() { + assert_equal(0, case Nil { + _ if 1 + 3 * 5 == 16 -> 0 + _ -> 1 + }) + }), + "1 - 15 / 5 == -2" + |> example(fn() { + assert_equal(0, case Nil { + _ if 1 - 15 / 5 == -2 -> 0 + _ -> 1 + }) + }), + "15 / 5 - 1 == 2" + |> example(fn() { + assert_equal(0, case Nil { + _ if 15 / 5 - 1 == 2 -> 0 + _ -> 1 + }) + }), "#(True, False).0" |> example(fn() { assert_equal(0, case Nil { @@ -1400,7 +1449,7 @@ fn string_pattern_matching_tests() { }), "match Θ test" |> example(fn() { - assert_equal(" foo bar", case "Θ foo bar" { + assert_equal(" wibble wobble", case "Θ wibble wobble" { "Θ" <> rest -> rest _ -> panic }) @@ -1570,7 +1619,7 @@ fn tuple_access_tests() { assert_equal( { let tup = #( - Person("Quinn", 27, "Canada"), + Person("Quinn", 27, "Canada"), Person("Nikita", 99, "Internet"), ) tup.0.name