Repository: gleam-lang/gleam (Rust)

Issue:
Confusing incorrect arity hint when labelled arguments are provided without labels
```gleam
fn wibble(a a: Int, b b: Float, c c: String) {
  todo
}

pub fn wobble() {
  wibble(1, 2.0)
}
```

produces

```txt
error: Incorrect arity
  ┌─ /home/arkan/Projects/oss/gleam/probe/src/probe.gleam:6:3
  │
6 │   wibble(1, 2)
  │   ^^^^^^^^^^^^ Expected 3 arguments, got 2

This call accepts these additional labelled arguments:

  - a
  - b
  - c
```

I think the hint (_"this call accepts these additional labelled arguments: ..."_) is confusing here, because I already provide values for `a` and `b`, but using positional arguments instead. While it is technically correct that I could provide any of the arguments as a labelled argument, changing the error from an _incorrect arity_ to a _type mismatch_ in the process, I don't think it's very useful to tell me that I could change the meaning of the arguments I already pass.

I chose different types to make it more obvious, but in my opinion all arguments that I already provide should not be included in the hint, even if the types were compatible. This is probably even more subjective. ~ :purple_heart: 


Discussion:
What should the hint say in this case? Just suggest the `c` label?
I personally would want it to only suggest `c` since I don't ever mix positioned and labelled arguments in a way that would change their order, (e.g. only passing the second argument of a ternary function as a labelled argument), but I'm not sure if that's the best/indented experience for everyone.





We could remove the word "additional"?
That might be not clear as well, since arguments that are passed as labelled arguments are removed from the list right now.

Would it maybe be possible to split the list like so?

```

This call is missing these arguments:

  - c c: String

Additionally, it accepts the following labelled arguments:

  - a: Int
  - b: Float
```

It could list positional missing arguments as well in the first list, without a label. I added type annotations in the example because I think that would also be cool, but that's probably another issue :)
Can you tell which ones have been supplied when it's incomplete like that?
Do you not think that we can assume that positional arguments that type-check are valid? If they don't typecheck, you get a type mismatch error for the first one as well right now (there seem to be some inconsistencies here between positional and labelled arguments too), and if they have a label, they would still be printed anyways in the second list.

Maybe this is the assumption I make because _I_ work this way, but is not generalisable to everyone?
If only positional arguments are supplied it doesn't sound too bad, but it gets less clear if there's a mix. I may be worrying about nothing!
Not sure if I should open a new issue, but when you pass additional labels (ie. when you decide to remove some field from the constructor), the hint isn't that helpful because it doesn't say which label(s) should be removed, just `Expected X arguments, got X+1`
Could you make an issue for that please



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eeaca17a235..927aee2983f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -105,6 +105,36 @@
   ([Carl Bordum Hansen](https://github.com/carlbordum)) and
   ([Giacomo Cavalieri](https://github.com/giacomocavalieri))
 
+- The error message one gets when calling a function with the wrong number of
+  arguments has been improved and now only suggests the relevant missing labels.
+  For example, this piece of code:
+
+  ```gleam
+  pub type Pokemon {
+    Pokemon(id: Int, name: String, moves: List(String))
+  }
+
+  pub fn best_pokemon() {
+    Pokemon(198, name: "murkrow")
+  }
+  ```
+
+  Would result in the following error, suggesting the missing labels:
+
+  ```txt
+  error: Incorrect arity
+    ┌─ /src/main.gleam:6:3
+    │
+  6 │   Pokemon(198, name: "murkrow")
+    │   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Expected 3 arguments, got 2
+
+  This call accepts these additional labelled arguments:
+
+    - moves
+  ```
+
+  ([Giacomo Cavalieri](https://github.com/giacomocavalieri))
+
 ### Build tool
 
 - `gleam update`, `gleam deps update`, and `gleam deps download` will now print
@@ -125,7 +155,7 @@
 
 - Docs generator now strips trailing slashes from Gitea/Forgejo hosts so
   sidebar "Repository" and "View Source" links never include `//`, and
-  single-line “View Source” anchors emit `#Lx` instead of `#Lx-x`.
+  single-line "View Source" anchors emit `#Lx` instead of `#Lx-x`.
   ([Aayush Tripathi](https://github.com/aayush-tripathi))
 
 ### Language server
diff --git a/compiler-core/src/type_/fields.rs b/compiler-core/src/type_/fields.rs
index 7d622ec1f00..f1dba302ed5 100644
--- a/compiler-core/src/type_/fields.rs
+++ b/compiler-core/src/type_/fields.rs
@@ -55,7 +55,7 @@ impl FieldMap {
 
         if self.arity as usize != args.len() {
             return Err(Error::IncorrectArity {
-                labels: self.incorrect_arity_labels(args),
+                labels: self.missing_labels(args),
                 location,
                 context,
                 expected: self.arity as usize,
@@ -145,17 +145,6 @@ impl FieldMap {
         }
     }
 
-    pub fn incorrect_arity_labels<A>(&self, args: &[CallArg<A>]) -> Vec<EcoString> {
-        let given: HashSet<_> = args.iter().filter_map(|arg| arg.label.as_ref()).collect();
-
-        self.fields
-            .keys()
-            .filter(|f| !given.contains(f))
-            .cloned()
-            .sorted()
-            .collect()
-    }
-
     /// This returns an array of the labels that are unused given an argument
     /// list.
     /// The unused labels are in the order they are expected to be passed in
@@ -169,7 +158,7 @@ impl FieldMap {
     /// wibble(1, label3: 2) // -> unused labels: [label2]
     /// ```
     ///
-    pub fn missing_labels<A: std::fmt::Debug>(&self, args: &[CallArg<A>]) -> Vec<EcoString> {
+    pub fn missing_labels<A>(&self, args: &[CallArg<A>]) -> Vec<EcoString> {
         // We need to know how many positional arguments are in the function
         // arguments. That's given by the position of the first labelled
         // argument; if the first label argument is third, then we know the


Test patch:
diff --git a/compiler-core/src/type_/tests/errors.rs b/compiler-core/src/type_/tests/errors.rs
index 89c4e10fe06..ac92f5fcfa4 100644
--- a/compiler-core/src/type_/tests/errors.rs
+++ b/compiler-core/src/type_/tests/errors.rs
@@ -3208,3 +3208,19 @@ pub fn main() {
 "
     );
 }
+
+// https://github.com/gleam-lang/gleam/issues/3884
+#[test]
+fn show_only_missing_labels() {
+    assert_module_error!(
+        "
+fn wibble(a a: Int, b b: Float, c c: String) {
+    todo
+}
+
+pub fn wobble() {
+    wibble(1, 2.0)
+}
+"
+    );
+}
diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__guard_record_wrong_arity.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__guard_record_wrong_arity.snap
index bf2f39f6a50..ce2fb9b03e2 100644
--- a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__guard_record_wrong_arity.snap
+++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__guard_record_wrong_arity.snap
@@ -17,5 +17,4 @@ error: Incorrect arity
 
 This call accepts these additional labelled arguments:
 
-  - a
   - b
diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__show_only_missing_labels.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__show_only_missing_labels.snap
new file mode 100644
index 00000000000..d577de7b554
--- /dev/null
+++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__show_only_missing_labels.snap
@@ -0,0 +1,25 @@
+---
+source: compiler-core/src/type_/tests/errors.rs
+expression: "\nfn wibble(a a: Int, b b: Float, c c: String) {\n    todo\n}\n\npub fn wobble() {\n    wibble(1, 2.0)\n}\n"
+---
+----- SOURCE CODE
+
+fn wibble(a a: Int, b b: Float, c c: String) {
+    todo
+}
+
+pub fn wobble() {
+    wibble(1, 2.0)
+}
+
+
+----- ERROR
+error: Incorrect arity
+  ┌─ /src/one/two.gleam:7:5
+  │
+7 │     wibble(1, 2.0)
+  │     ^^^^^^^^^^^^^^ Expected 3 arguments, got 2
+
+This call accepts these additional labelled arguments:
+
+  - c
diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__functions__function_call_incorrect_arity_with_label_shorthand_fault_tolerance2.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__functions__function_call_incorrect_arity_with_label_shorthand_fault_tolerance2.snap
index 0d5cb18e3f1..f1f0be920c8 100644
--- a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__functions__function_call_incorrect_arity_with_label_shorthand_fault_tolerance2.snap
+++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__functions__function_call_incorrect_arity_with_label_shorthand_fault_tolerance2.snap
@@ -24,7 +24,6 @@ error: Incorrect arity
 This call accepts these additional labelled arguments:
 
   - wabble
-  - wibble
 
 error: Type mismatch
   ┌─ /src/one/two.gleam:8:10
diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__functions__function_call_incorrect_arity_with_labels_fault_tolerance2.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__functions__function_call_incorrect_arity_with_labels_fault_tolerance2.snap
index 6385a89c823..92b1095ac77 100644
--- a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__functions__function_call_incorrect_arity_with_labels_fault_tolerance2.snap
+++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__functions__function_call_incorrect_arity_with_labels_fault_tolerance2.snap
@@ -23,7 +23,6 @@ error: Incorrect arity
 This call accepts these additional labelled arguments:
 
   - wabble
-  - wibble
 
 error: Type mismatch
   ┌─ /src/one/two.gleam:7:10