Repository: gleam-lang/gleam (rust)

Issue:
LSP: go to definiton does not work for parameters in anonymous functions
**Steps to reproduce**
1. `$ gleam new bug_demo`
2. Add this to `src/bug_demo.gleam`:
```gleam
pub fn main() {
  let ad_hoc_wibble = fn(hello: Hello) { todo }

  Nil
}

type Hello {
  Hello
}

fn wibble(hello: Hello) {
  todo
}
```
3. In line 2: `let ad_hoc_wibble = fn(hello: Hello) { todo }` hover over `Hello` and "Go to definition"
4. Nothing happens

**Expected behavior**
The cursor should move to the definition of `Hello`. It works if you do the same thing but on the function `wibble` at the bottom.

I also noticed that documentation comments are not shown (at least in Zed) when hovering over `Hello` in `ad_hoc_wibble`.

Pull request:
Fix bug with goto definition from anonymous function annotations
Fixes #3758
This PR moves the locating of annotations from function definitions to function arguments, allowing annotations to be located in the parameters of anonymous functions as well.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45e897763..7f3211a83 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -301,6 +301,10 @@
   the tail of a list.
   ([Giacomo Cavalieri](https://github.com/giacomocavalieri))
 
+- Fixed a bug where attempting to jump to the definition of a type from the
+  annotation of a parameter of an anonymous function would do nothing.
+  ([Surya Rose](https://github.com/GearsDatapacks))
+
 ## v1.5.1 - 2024-09-26
 
 ### Bug Fixes
diff --git a/compiler-core/src/ast.rs b/compiler-core/src/ast.rs
index 33cf3d48e..4e27237ce 100644
--- a/compiler-core/src/ast.rs
+++ b/compiler-core/src/ast.rs
@@ -170,6 +170,11 @@ impl<A> Arg<A> {
 impl TypedArg {
     pub fn find_node(&self, byte_index: u32) -> Option<Located<'_>> {
         if self.location.contains(byte_index) {
+            if let Some(annotation) = &self.annotation {
+                return annotation
+                    .find_node(byte_index, self.type_.clone())
+                    .or(Some(Located::Arg(self)));
+            }
             Some(Located::Arg(self))
         } else {
             None
@@ -829,15 +834,9 @@ impl TypedDefinition {
                 if let Some(found_arg) = function
                     .arguments
                     .iter()
-                    .find(|arg| arg.location.contains(byte_index))
+                    .find_map(|arg| arg.find_node(byte_index))
                 {
-                    // Check if location is within the arg annotation.
-                    if let Some(a) = &found_arg.annotation {
-                        return a
-                            .find_node(byte_index, found_arg.type_.clone())
-                            .or(Some(Located::Arg(found_arg)));
-                    }
-                    return Some(Located::Arg(found_arg));
+                    return Some(found_arg);
                 };
 
                 if let Some(found_statement) = function


Test patch:
diff --git a/compiler-core/src/language_server/tests/definition.rs b/compiler-core/src/language_server/tests/definition.rs
index 42dfba05d..5d73a86c5 100644
--- a/compiler-core/src/language_server/tests/definition.rs
+++ b/compiler-core/src/language_server/tests/definition.rs
@@ -609,3 +609,20 @@ fn main() {
             .under_char('l')
     );
 }
+
+// https://github.com/gleam-lang/gleam/issues/3758
+#[test]
+fn goto_definition_from_anonymous_function() {
+    let code = "
+pub type Wibble
+
+pub fn main() {
+  fn(w: Wibble) { todo }
+}
+";
+
+    assert_goto!(
+        TestProject::for_source(code),
+        find_position_of("w: Wibble").under_char('i')
+    );
+}
diff --git a/compiler-core/src/language_server/tests/hover.rs b/compiler-core/src/language_server/tests/hover.rs
index bd003c2d1..998968461 100644
--- a/compiler-core/src/language_server/tests/hover.rs
+++ b/compiler-core/src/language_server/tests/hover.rs
@@ -1256,3 +1256,21 @@ pub fn main() {
 
     assert_hover!(TestProject::for_source(code), find_position_of("x"));
 }
+
+// https://github.com/gleam-lang/gleam/issues/3758
+#[test]
+fn hover_for_anonymous_function_annotation() {
+    let code = "
+/// An example type.
+pub type Wibble
+
+pub fn main() {
+  fn(w: Wibble) { todo }
+}
+";
+
+    assert_hover!(
+        TestProject::for_source(code),
+        find_position_of("w: Wibble").under_char('b')
+    );
+}
diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__definition__goto_definition_from_anonymous_function.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__definition__goto_definition_from_anonymous_function.snap
new file mode 100644
index 000000000..f80423a2e
--- /dev/null
+++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__definition__goto_definition_from_anonymous_function.snap
@@ -0,0 +1,21 @@
+---
+source: compiler-core/src/language_server/tests/definition.rs
+expression: output
+---
+----- Jumping from `src/app.gleam`
+
+pub type Wibble
+
+pub fn main() {
+  fn(w: Wibble) { todo }
+         ↑              
+}
+
+----- Jumped to `src/app.gleam`
+
+pub type Wibble
+↑▔▔▔▔▔▔▔▔▔▔▔▔▔▔
+
+pub fn main() {
+  fn(w: Wibble) { todo }
+}
diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_for_anonymous_function_annotation.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_for_anonymous_function_annotation.snap
new file mode 100644
index 000000000..b97f25cb5
--- /dev/null
+++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_for_anonymous_function_annotation.snap
@@ -0,0 +1,19 @@
+---
+source: compiler-core/src/language_server/tests/hover.rs
+expression: "\n/// An example type.\npub type Wibble\n\npub fn main() {\n  fn(w: Wibble) { todo }\n}\n"
+---
+/// An example type.
+pub type Wibble
+
+pub fn main() {
+  fn(w: Wibble) { todo }
+        ▔▔↑▔▔▔          
+}
+
+
+----- Hover content -----
+Scalar(
+    String(
+        "```gleam\napp.Wibble\n```\n An example type.\n",
+    ),
+)