Repository: gleam-lang/gleam (rust)

Issue:
LSP: Goto Definition not working properly for unqualified imports
<!--
Please include the following:
- Steps and code to reproduce the problem
- Versions for Gleam, OS, the runtime and any other software used
- Logs from running Gleam with the `GLEAM_LOG` environment variable set to `trace`
-->

The LSP action "Goto Definition" (`gd` in nvim) is not working for unqualified imported functions.
With the following example of (part of) a file (from the [Building your first Gleam web app with Wisp and Lustre](https://gleaming.dev/articles/building-your-first-gleam-web-app/) tutorial),

```gleam
import app/routes/item_routes.{items_middleware}
import app/web.{type Context}
import wisp.{type Request, type Response}

pub fn handle_request(req: Request, ctx: Context) -> Response {
  use req <- web.middleware(req, ctx)
  use ctx <- items_middleware(req, ctx)
// ...
```

the following behaviors are observed:
* Placing the cursor on `items_middleware` and hitting `gd` moves the cursor to the `req` symbol on the same line.
* Placing the cursor on `web.middleware` (specifically on the `.middleware` part) and hitting `gd` correctly jumps to that function's definition in `app/web`.
* Placing the cursor on any of the imported types (`Context` or `Request`) and hitting `gd` correctly jumps to their definition.

Gleam v1.3.2 running on WSL (Ubuntu 22.04.4 LTS)
Neovim v0.11.0-dev

Pull request:
Fix go to definition for unqualified functions
Fixes #3457
Very simple fix, functions just weren't returning their module in `definition_location`.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c490da7cf..b0cb7e7f3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,10 @@
   two constructors of the same name were created.
   ([Surya Rose](https://github.com/GearsDatapacks))
 
+- Fixed a bug where jumping to the definition of an unqualified function would
+  produce the correct location, but remain in the same file.
+  ([Surya Rose](https://github.com/gearsdatapacks))
+
 ## v1.4.1 - 2024-08-04
 
 ### Bug Fixes
diff --git a/compiler-core/src/type_.rs b/compiler-core/src/type_.rs
index 368f0c792..64fb01a8b 100644
--- a/compiler-core/src/type_.rs
+++ b/compiler-core/src/type_.rs
@@ -963,6 +963,9 @@ impl ValueConstructor {
             }
             | ValueConstructorVariant::ModuleConstant {
                 location, module, ..
+            }
+            | ValueConstructorVariant::ModuleFn {
+                location, module, ..
             } => DefinitionLocation {
                 module: Some(module.as_str()),
                 span: *location,
@@ -973,8 +976,7 @@ impl ValueConstructor {
                 span: literal.location(),
             },
 
-            ValueConstructorVariant::ModuleFn { location, .. }
-            | ValueConstructorVariant::LocalVariable { location } => DefinitionLocation {
+            ValueConstructorVariant::LocalVariable { location } => DefinitionLocation {
                 module: None,
                 span: *location,
             },


Test patch:
diff --git a/compiler-core/src/language_server/tests/definition.rs b/compiler-core/src/language_server/tests/definition.rs
index 26c569d28..e35c42581 100644
--- a/compiler-core/src/language_server/tests/definition.rs
+++ b/compiler-core/src/language_server/tests/definition.rs
@@ -906,6 +906,41 @@ fn main() {
     )
 }
 
+#[test]
+fn goto_definition_unqualified_function() {
+    let code = "
+import wibble.{wobble}
+fn main() {
+  wobble()
+}
+";
+
+    assert_eq!(
+        definition(
+            TestProject::for_source(code).add_module("wibble", "pub fn wobble() {}"),
+            Position::new(3, 5)
+        ),
+        Some(Location {
+            uri: Url::from_file_path(Utf8PathBuf::from(if cfg!(target_family = "windows") {
+                r"\\?\C:\src\wibble.gleam"
+            } else {
+                "/src/wibble.gleam"
+            }))
+            .unwrap(),
+            range: Range {
+                start: Position {
+                    line: 0,
+                    character: 0
+                },
+                end: Position {
+                    line: 0,
+                    character: 15
+                }
+            }
+        })
+    )
+}
+
 #[test]
 fn goto_definition_import_unqualified_type() {
     let code = "