Repository: gleam-lang/gleam (rust)

Issue:
lsp crash
<!--
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`
-->


To reproduce 
Create a new project with `gleam new name` 
in vscode
Open src/name.gleam
Paste in my code
> popup apears:
> The Gleam Language Server server crashed 5 times in the last 3 minutes. The server will not be restarted. See the output for more information. 

<details>  <summary>Crash logs from output</summary>

 INFO language_server_starting
Hello human!

This command is intended to be run by language server clients such
as a text editor rather than being run directly in the console.

Many editors will automatically start the language server for you
when you open a Gleam project. If yours does not you may need to
look up how to configure your editor to use a language server.

You can exit this program by pressing ctrl+c.

 INFO creating_new_language_server_engine path="/home/harcma/Ram/g1eam_lsp"
 INFO compile{package=gleam_stdlib}: analysing_modules count=0
 INFO compile{package=gleeunit}: analysing_modules count=0
 INFO compile{package=g1eam_lsp}: analysing_modules count=2
error: Fatal compiler bug!

This is a bug in the Gleam compiler, sorry!

Please report this crash to https://github.com/gleam-lang/gleam/issues/new
and include this error message with your report.

Panic: compiler-core/src/exhaustiveness.rs:746
	Custom type variants must exist: Type { name: "Token", hint: AlternativeTypes(["String", "UtfCodepoint", "Result", "Float", "Int", "BitArray", "CalcError", "Token", "Bool", "Nil", "List"]) }
Gleam version: 1.4.0
Operating system: linux

If you can also share your code and say what file you were editing or any
steps to reproduce the crash that would be a great help.

You may also want to try again with the `GLEAM_LOG=trace` environment
variable set.

[Error - 3:30:41 PM] The Gleam Language Server server crashed 5 times in the last 3 minutes. The server will not be restarted. See the output for more information.

</details>
<details><summary>My code</summary>
import gleam/int.{to_string}
import gleam/io
import gleam/list
import gleam/string

const al = [
  "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
  "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f",
  "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
  "w", "x", "y", "z",
]

const num = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."]

const opr = ["^", "*", "/", "+", "-", "="]

pub type CalcError {
  ParseError
  CalcError
}

pub type Token {
  Opr(v: String)
  Num(v: String)
  Num(v: String)
  Alp(v: String)
  Par(v: String)
  Unk(v: String)
}

pub fn calc() -> Result(Int, CalcError) {
  io.println("Hello pivops1_gleam!")
  Error(ParseError)
}

fn which(c) {
  case list.contains(al, c), list.contains(num, c), list.contains(opr, c) {
    True, False, False -> Alp(c)
    False, True, False -> Num(c)
    False, False, True -> Opr(c)
    _, _, _ -> Unk(c)
  }
}

pub fn route(s: String, l) {
  let str = string.pop_grapheme(s)
  case str {
    Ok(str) -> {
      let cc = str.0
      let ss = str.1
      case cc, which(cc) {
        "(", _ -> route(ss, list.append([Par("(")], l))
        ")", _ -> route(ss, list.append([Par(")")], l))
        _, Alp(_) -> alphab(s, l)
        _, Num(_) -> number(s, l)
        _, Opr(_) -> operat(s, l)
        _, _ -> l
      }
    }
    _ -> l
  }
}

pub fn operat(s, l) {
  case string.pop_grapheme(s) {
    Error(Nil) -> l
    Ok(c) -> {
      case opr |> list.contains(c.0) {
        True -> {
          let string = c.1
          let grapheme = c.0
          let out = case grapheme, l {
            //"-", [Opr(o), ..lis]->{ io.debug("reached") list.append([Opr("-"), Opr(o)], lis)}
            _, [Opr(tail), ..lis] -> list.append([Opr(tail <> grapheme)], lis)
            _, [_, ..] -> list.append([Opr(grapheme)], l)
            _, [] -> [Opr(grapheme)]
          }
          alphab(string, out)
        }
        False -> route(s, l)
      }
    }
  }
}

pub fn alphab(s: String, l: List(Token)) {
  case string.pop_grapheme(s) {
    Error(Nil) -> l
    Ok(c) -> {
      case al |> list.contains(c.0) {
        //|| num |> contains(c.0) 
        True -> {
          let string = c.1
          let grapheme = c.0
          let out = case l {
            [Alp(tail), ..lis] -> list.append([Alp(tail <> grapheme)], lis)
            [_, ..] -> list.append([Alp(grapheme)], l)
            [] -> [Alp(grapheme)]
          }
          alphab(string, out)
        }
        False -> route(s, l)
      }
    }
  }
}

pub fn number(s: String, l: List(Token)) {
  case string.pop_grapheme(s) {
    Error(Nil) -> l
    Ok(c) -> {
      case al |> list.contains(c.0) || num |> list.contains(c.0) {
        True -> {
          let string = c.1
          let grapheme = c.0
          let out = case l {
            //[Opr("-"), Opr(")"), ..lis] -> list.append([Num(grapheme)], lis)
            [Num(tail), ..lis] -> list.append([Num(tail <> grapheme)], lis)
            [Opr("-"), Opr(o), ..lis] ->
              list.append([Num("-" <> grapheme), Opr(o)], lis)
            [_, ..] -> list.append([Num(grapheme)], l)
            [] -> [Num(grapheme)]
          }
          number(string, out)
        }
        False -> route(s, l)
      }
    }
  }
}

pub fn o() {
  case calc() {
    Ok(num) -> io.print(to_string(num))
    Error(ParseError) -> io.print("parse error")
    Error(CalcError) -> io.print("Calc error")
  }
}

pub fn main() {
  let a = list.reverse(route("sin(10*PI)", []))
  io.debug(a)
  io.debug(rpa(a, #([], [])))
}

fn rpa(li, out: List(Int), opr: List(Token)) {
  case li {
    [o, ..l] ->
      case o {
        Num(n) -> rpa(l, out, opr)
        Alp(_) -> rpa(l, out, opr)
        Opr(_) -> rpa(l, out, opr)
        Par(_) -> rpa(l, out, opr)
        Unk(_) -> rpa(l, out, opr)
      }
    [] -> li
  }
}


</details>


Pull request description:
Fix crash on duplicate construcor definition
Fixes #3489 
This is similar to #3209, where the analyser would return from the `register_values_from_custom_type` function if it found any duplicate names, and so would never get round to actually registering the values. I have now just made it report the error and continue, so it can get to the end of the function.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c45776c75..c490da7cf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,10 @@
 
 ### Bug Fixes
 
+- Fixed a bug which caused the language server and compiler to crash when
+  two constructors of the same name were created.
+  ([Surya Rose](https://github.com/GearsDatapacks))
+
 ## v1.4.1 - 2024-08-04
 
 ### Bug Fixes
diff --git a/compiler-core/src/analyse.rs b/compiler-core/src/analyse.rs
index bac174009..6054a17b5 100644
--- a/compiler-core/src/analyse.rs
+++ b/compiler-core/src/analyse.rs
@@ -867,11 +867,14 @@ impl<'a, A> ModuleAnalyzer<'a, A> {
         let mut constructors_data = vec![];
 
         for (index, constructor) in constructors.iter().enumerate() {
-            assert_unique_name(
+            if let Err(error) = assert_unique_name(
                 &mut self.value_names,
                 &constructor.name,
                 constructor.location,
-            )?;
+            ) {
+                self.problems.error(error);
+                continue;
+            }
 
             let mut field_map = FieldMap::new(constructor.arguments.len() as u32);
             let mut args_types = Vec::with_capacity(constructor.arguments.len());


Test patch:
diff --git a/compiler-core/src/type_/tests/errors.rs b/compiler-core/src/type_/tests/errors.rs
index 0ea51a9ad..c0a4d7366 100644
--- a/compiler-core/src/type_/tests/errors.rs
+++ b/compiler-core/src/type_/tests/errors.rs
@@ -2172,3 +2172,23 @@ pub fn main() {
 "
     );
 }
+
+#[test]
+fn no_crash_on_duplicate_definition() {
+    // This previous caused the compiler to crash
+    assert_module_error!(
+        "
+type Wibble {
+  Wobble
+  Wobble
+}
+
+pub fn main() {
+  let wibble = Wobble
+  case wibble {
+    Wobble -> Nil
+  }
+}
+"
+    );
+}
diff --git a/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__no_crash_on_duplicate_definition.snap b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__no_crash_on_duplicate_definition.snap
new file mode 100644
index 000000000..4d71f10e5
--- /dev/null
+++ b/compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__no_crash_on_duplicate_definition.snap
@@ -0,0 +1,14 @@
+---
+source: compiler-core/src/type_/tests/errors.rs
+expression: "\ntype Wibble {\n  Wobble\n  Wobble\n}\n\npub fn main() {\n  let wibble = Wobble\n  case wibble {\n    Wobble -> Nil\n  }\n}\n"
+---
+error: Duplicate definition
+  ┌─ /src/one/two.gleam:3:3
+  │
+3 │   Wobble
+  │   ^^^^^^ First defined here
+4 │   Wobble
+  │   ^^^^^^ Redefined here
+
+`Wobble` has been defined multiple times.
+Names in a Gleam module must be unique so one will need to be renamed.