Repository: gleam-lang/gleam (rust)

Issue:
"Import" code action sometimes fails and deletes a range of code
<!--
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`
-->
### Description
Occasionally an "Import" action is suggested for the type annotation on function arguments that, when applied, _sometimes_ causes:
1. A range of code to be deleted near the cursor
2. No import to be added

### Reproduction Steps
I've experienced a bit of non-determinism with the issue that makes it difficult to give exact steps to reproduce. I am able to get the issue to occur somewhat frequently by repeatedly doing variations of the following:
1. Begin writing a top-level method with a type annotation that uses a missing import e.g. 
    ```gleam
    fn foo(set: set.Set(String)) {
      todo
    }
    ```
2. Return the cursor to the module name for the import and apply the suggested "Import action"
3. Notice that some code is deleted
### Environment:
- **Editor** VSCode
- **Gleam VSCode Extension:** 2.10.0
- **OS:** Ubuntu on WSL 2
- **Gleam:** 1.6.3
- **Gleam Runtime:** Erlang OTP 25

I am not sure where to look for logs that would be useful here, I only see `INFO compile=...` in the `Gleam Language Server` output in VSCode.

### Screenshots / Recording
| Suggested action| Applied action|
|--------|--------|
| ![image](https://github.com/user-attachments/assets/193dccbd-67e9-4103-9af0-461043580edd) | ![image](https://github.com/user-attachments/assets/dd7e128e-9c79-4c1f-b7b1-307f01272d89) | 

Recording: https://github.com/user-attachments/assets/885af89e-9dd5-402f-830d-c399e51590ba



Pull request:
Make function parameters fault tolerant
This PR seems to fix #4092.
This PR makes function parameters fault tolerant, meaning that if an error occurs (e.g. an invalid type is referenced), type inference can continue. As far as I can tell, this is what was causing the linked issue.
As detailed in the above issue, it's not easy to reproduce, especially using automated tests. I'm not sure if there's a good way to test it. For now I've added a test in `type_::tests` to test this fault tolerance.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8422906c8..82d223fa2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -185,6 +185,10 @@
 - The compiler will not generate needless code for unused pattern variables.
   ([Giacomo Cavalieri](https://github.com/giacomocavalieri))
 
+- Function parameters are now fault tolerant, meaning that type-checking can
+  continue to occur if a function references invalid type in its signature.
+  ([Surya Rose](https://github.com/GearsDatapacks))
+
 ### Build tool
 
 - The build tool now supports placing modules in a directory called `dev`,
@@ -405,7 +409,7 @@
   ([Surya Rose](https://github.com/GearsDatapacks))
 
 - Fixed a bug where LSP ranges would be incorrect when a file contained characters
-  that were represented wit more than one byte in UTF-8.
+  that were represented with more than one byte in UTF-8.
   ([Surya Rose](https://github.com/GearsDatapacks))
 
 - Only print the user-friendly LSP message when stdin is a terminal to avoid
diff --git a/changelog/v1.1.md b/changelog/v1.1.md
index 0fda41045..4eed46d84 100644
--- a/changelog/v1.1.md
+++ b/changelog/v1.1.md
@@ -336,7 +336,7 @@
   when path dependencies are used.
 - Fixed a bug where `gleam new` would generate a gitignore with `build` rather
   than `/build`.
-- Fixed where the types of generic constants could be incorrecly inferred.
+- Fixed where the types of generic constants could be incorrectly inferred.
 - `Utf8Codepoint` has been renamed to `UtfCodepoint` in `prelude.d.mts`.
 - Fixed a bug where `gleam deps list` would look in filesystem root instead of
   the current directory.
@@ -439,7 +439,7 @@
 - Fixed a bug where the build directory could be left in an invalid state if
   there is Elixir code to compile and running on Windows without permission to
   create symlinks.
-- Fixed a bug where numbers with preceeding zeros could generate incorrect
+- Fixed a bug where numbers with preceding zeros could generate incorrect
   JavaScript.
 - The Erlang code generated by the `/.` operator no longer generates a warning
   for the upcoming negative zero float change in Erlang OTP 27.
@@ -591,7 +591,7 @@
 - The main process started with `gleam run` no longer traps exits on Erlang.
 - The formatting of code in rendered HTML documentation has been improved.
 - The formatter no longer moves trailing comments out of custom type definitions.
-- Fixed a bug where some hexidecimal numbers would generate incorrect Erlang.
+- Fixed a bug where some hexadecimal numbers would generate incorrect Erlang.
 - Fixed a bug where markdown tables would not render correctly in HTML
   documentation.
 - The float 0.0 is now rendered in Erlang as `+0.0` to silence warnings in
@@ -1141,7 +1141,7 @@
 - Fixed a bug where the formatter would remove braces from blocks used as the
   subject of a case expression.
 - Fixed a bug alternative patterns with a clause containing a pipe with a pipe
-  after the case expresson could render incorrect Erlang.
+  after the case expression could render incorrect Erlang.
 - Fixed a bug where formatter would strip curly braces around case guards even
   when they are required to specify boolean precedence.
 - Fixed a bug where `gleam new` would in some situations not validate the
diff --git a/changelog/v1.5.md b/changelog/v1.5.md
index b993ebf87..953f24657 100644
--- a/changelog/v1.5.md
+++ b/changelog/v1.5.md
@@ -36,7 +36,7 @@
 - OTP application trees are now shut down gracefully when `main` exits.
   ([Louis Pilfold](https://github.com/lpil))
 
-- The `gleam fix` command can now update a project's `gleam` version contraint
+- The `gleam fix` command can now update a project's `gleam` version constraint
   to make sure it respects the inferred minimum required version.
   ([Giacomo Cavalieri](https://github.com/giacomocavalieri))
 
diff --git a/changelog/v1.6.md b/changelog/v1.6.md
index b92b3b6a6..d555fab2f 100644
--- a/changelog/v1.6.md
+++ b/changelog/v1.6.md
@@ -41,7 +41,7 @@
   `javascript` to specify what target to use, with `erlang` being the default.
   ([Mohammed Khouni](https://github.com/Tar-Tarus))
 
-- The Erlang/Elixir compiler process is now re-used for all packages, shaving
+- The Erlang/Elixir compiler process is now reused for all packages, shaving
   off 0.3-0.5s per compiled package.
   ([yoshi](https://github.com/joshi-monster))
 
diff --git a/compiler-core/src/analyse.rs b/compiler-core/src/analyse.rs
index f9bccfe4c..518ff712e 100644
--- a/compiler-core/src/analyse.rs
+++ b/compiler-core/src/analyse.rs
@@ -248,9 +248,7 @@ impl<'a, A> ModuleAnalyzer<'a, A> {
         }
 
         for f in &statements.functions {
-            if let Err(error) = self.register_value_from_function(f, &mut env) {
-                return self.all_errors(error);
-            }
+            self.register_value_from_function(f, &mut env);
         }
 
         // Infer the types of each statement in the module
@@ -1403,7 +1401,7 @@ impl<'a, A> ModuleAnalyzer<'a, A> {
         &mut self,
         f: &UntypedFunction,
         environment: &mut Environment<'_>,
-    ) -> Result<(), Error> {
+    ) {
         let Function {
             name,
             arguments: args,
@@ -1438,7 +1436,9 @@ impl<'a, A> ModuleAnalyzer<'a, A> {
         {
             check_argument_names(names, &mut self.problems);
 
-            builder.add(names.get_label(), *location)?;
+            if let Err(error) = builder.add(names.get_label(), *location) {
+                self.problems.error(error);
+            }
         }
         let field_map = builder.finish();
         let mut hydrator = Hydrator::new();
@@ -1450,11 +1450,30 @@ impl<'a, A> ModuleAnalyzer<'a, A> {
         let arg_types = args
             .iter()
             .map(|arg| {
-                hydrator.type_from_option_ast(&arg.annotation, environment, &mut self.problems)
+                match hydrator.type_from_option_ast(
+                    &arg.annotation,
+                    environment,
+                    &mut self.problems,
+                ) {
+                    Ok(type_) => type_,
+                    Err(error) => {
+                        self.problems.error(error);
+                        environment.new_unbound_var()
+                    }
+                }
             })
-            .try_collect()?;
+            .collect();
+
         let return_type =
-            hydrator.type_from_option_ast(return_annotation, environment, &mut self.problems)?;
+            match hydrator.type_from_option_ast(return_annotation, environment, &mut self.problems)
+            {
+                Ok(type_) => type_,
+                Err(error) => {
+                    self.problems.error(error);
+                    environment.new_unbound_var()
+                }
+            };
+
         let type_ = fn_(arg_types, return_type);
         let _ = self.hydrators.insert(name.clone(), hydrator);
 
@@ -1481,7 +1500,6 @@ impl<'a, A> ModuleAnalyzer<'a, A> {
             *publicity,
             deprecation.clone(),
         );
-        Ok(())
     }
 
     fn check_for_type_leaks(&mut self, value: &ValueConstructor) {
diff --git a/docs/runtime-errors.md b/docs/runtime-errors.md
index 2979d1ab2..31fef2bad 100644
--- a/docs/runtime-errors.md
+++ b/docs/runtime-errors.md
@@ -130,7 +130,7 @@ The expression maps have this structure:
 | end    | Int          | Number           | The byte-index of the end of this expression in the source code   |
 
 If the expression is a literal, such as `True` or `15`, it will have the `literal`
-kind. This signals that its value is not runtime dependant, and may not need to
+kind. This signals that its value is not runtime dependent, and may not need to
 be printed.
 
 If the expression is on the right hand side of a short-circuiting operator, like


Test patch:
diff --git a/compiler-core/src/type_/snapshots/gleam_core__type___tests__function_parameter_errors_do_not_stop_inference.snap b/compiler-core/src/type_/snapshots/gleam_core__type___tests__function_parameter_errors_do_not_stop_inference.snap
new file mode 100644
index 000000000..345013155
--- /dev/null
+++ b/compiler-core/src/type_/snapshots/gleam_core__type___tests__function_parameter_errors_do_not_stop_inference.snap
@@ -0,0 +1,33 @@
+---
+source: compiler-core/src/type_/tests.rs
+expression: "\npub fn wibble(x: NonExistent) {\n  1 + False\n}\n"
+---
+----- SOURCE CODE
+
+pub fn wibble(x: NonExistent) {
+  1 + False
+}
+
+
+----- ERROR
+error: Unknown type
+  ┌─ /src/one/two.gleam:2:18
+  │
+2 │ pub fn wibble(x: NonExistent) {
+  │                  ^^^^^^^^^^^
+
+The type `NonExistent` is not defined or imported in this module.
+
+error: Type mismatch
+  ┌─ /src/one/two.gleam:3:7
+  │
+3 │   1 + False
+  │       ^^^^^
+
+The + operator expects arguments of this type:
+
+    Int
+
+But this argument has this type:
+
+    Bool
diff --git a/compiler-core/src/type_/tests.rs b/compiler-core/src/type_/tests.rs
index ed3a9a700..649c69c8f 100644
--- a/compiler-core/src/type_/tests.rs
+++ b/compiler-core/src/type_/tests.rs
@@ -3107,3 +3107,14 @@ pub type Bad {
 "
     );
 }
+
+#[test]
+fn function_parameter_errors_do_not_stop_inference() {
+    assert_module_error!(
+        "
+pub fn wibble(x: NonExistent) {
+  1 + False
+}
+"
+    );
+}