Repository: gleam-lang/gleam (rust)

Issue:
`case` guard using qualified constructor from other module does not compile
With `module.gleam` containing:
```gleam
pub type Wibble {
  Wibble
  Wobble
}
```

and 
```gleam
import module

fn wibble(i: Int, w: module.Wibble) {
  case i {
    0 if w == module.Wobble -> 0
    _ -> 42
  }
}
```

compilation fails with 
```
  │
5 │     0 if w == module.Wobble -> 0
  │                      ^^^^^^ I'm expecting a lowercase name here
```

If doing an unqualified import with
```gleam
import module.{Wobble}
```

using `w == Wobble` works as expected.

Gleam version 1.6.1

Pull request description:
Allow qualified records in clause guards
Fixes #3883
This PR adds a function which parses qualified records in the same way as unqualified records, by treating them as constant values.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 008388df9..d9c334b06 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -129,6 +129,9 @@
   file of same name without warning. It now produces an error.
   ([PgBiel](https://github.com/PgBiel))
 
+- Fixed a bug where qualified records were not allowed in clause guards.
+  ([Surya Rose](https://github.com/GearsDatapacks))
+
 ## v1.6.1 - 2024-11-19
 
 ### Bug fixed
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 3296375cf..6e0ed7a74 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,73 +1,134 @@
+
 # Contributor Covenant Code of Conduct
 
 ## Our Pledge
 
-In the interest of fostering an open and welcoming environment, we as
-contributors and maintainers pledge to making participation in our project and
-our community a harassment-free experience for everyone, regardless of age, body
-size, disability, ethnicity, sex characteristics, gender identity and expression,
-level of experience, education, socio-economic status, nationality, personal
-appearance, race, religion, or sexual identity and orientation.
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual
+identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
 
 ## Our Standards
 
-Examples of behavior that contributes to creating a positive environment
-include:
+Examples of behavior that contributes to a positive environment for our
+community include:
 
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+  and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the overall
+  community
 
-Examples of unacceptable behavior by participants include:
+Examples of unacceptable behavior include:
 
-* The use of sexualized language or imagery and unwelcome sexual attention or
-  advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
+* The use of sexualized language or imagery, and sexual attention or advances of
+  any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
 * Public or private harassment
-* Publishing others' private information, such as a physical or electronic
-  address, without explicit permission
+* Publishing others' private information, such as a physical or email address,
+  without their explicit permission
 * Other conduct which could reasonably be considered inappropriate in a
   professional setting
 
-## Our Responsibilities
+## Enforcement Responsibilities
 
-Project maintainers are responsible for clarifying the standards of acceptable
-behavior and are expected to take appropriate and fair corrective action in
-response to any instances of unacceptable behavior.
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
 
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct, or to ban temporarily or
-permanently any contributor for other behaviors that they deem inappropriate,
-threatening, offensive, or harmful.
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
 
 ## Scope
 
-This Code of Conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community. Examples of
-representing a project or community include using an official project e-mail
-address, posting via an official social media account, or acting as an appointed
-representative at an online or offline event. Representation of a project may be
-further defined and clarified by project maintainers.
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official email address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
 
 ## Enforcement
 
 Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at louis at lpil.uk. All
-complaints will be reviewed and investigated and will result in a response that
-is deemed necessary and appropriate to the circumstances. The project team is
-obligated to maintain confidentiality with regard to the reporter of an incident.
-Further details of specific enforcement policies may be posted separately.
+reported to the community leaders responsible for enforcement at
+hello@gleam.run.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of
+actions.
 
-Project maintainers who do not follow or enforce the Code of Conduct in good
-faith may face temporary or permanent repercussions as determined by other
-members of the project's leadership.
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or permanent
+ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the
+community.
 
 ## Attribution
 
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
-available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
+[https://www.contributor-covenant.org/translations][translations].
 
 [homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[Mozilla CoC]: https://github.com/mozilla/diversity
+[FAQ]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
+
diff --git a/compiler-core/src/parse.rs b/compiler-core/src/parse.rs
index 122818870..9ea7cc1dc 100644
--- a/compiler-core/src/parse.rs
+++ b/compiler-core/src/parse.rs
@@ -1556,14 +1556,21 @@ where
 
             Some((start, Token::Name { name }, end)) => {
                 self.advance();
-                let mut unit = ClauseGuard::Var {
-                    location: SrcSpan { start, end },
-                    type_: (),
-                    name,
-                };
 
                 self.parse_function_call_in_clause_guard(start)?;
 
+                let mut unit = if let Some(record) =
+                    self.parse_record_in_clause_guard(&name, SrcSpan { start, end })?
+                {
+                    record
+                } else {
+                    ClauseGuard::Var {
+                        location: SrcSpan { start, end },
+                        type_: (),
+                        name,
+                    }
+                };
+
                 loop {
                     let dot_s = match self.maybe_one(&Token::Dot) {
                         Some((dot_s, _)) => dot_s,
@@ -1643,6 +1650,37 @@ where
         }
     }
 
+    fn parse_record_in_clause_guard(
+        &mut self,
+        module: &EcoString,
+        module_location: SrcSpan,
+    ) -> Result<Option<UntypedClauseGuard>, ParseError> {
+        let (name, end) = match (self.tok0.take(), self.peek_tok1()) {
+            (Some((_, Token::Dot, _)), Some(Token::UpName { .. })) => {
+                self.advance(); // dot
+                let Some((_, Token::UpName { name }, end)) = self.next_tok() else {
+                    return Ok(None);
+                };
+                (name, end)
+            }
+            (tok0, _) => {
+                self.tok0 = tok0;
+                return Ok(None);
+            }
+        };
+
+        if let Some(record) = self.parse_const_record_finish(
+            module_location.start,
+            Some((module.clone(), module_location)),
+            name,
+            end,
+        )? {
+            Ok(Some(ClauseGuard::Constant(record)))
+        } else {
+            Ok(None)
+        }
+    }
+
     // examples:
     //   UpName( args )
     fn expect_constructor_pattern(


Test patch:
diff --git a/compiler-core/src/type_/tests/guards.rs b/compiler-core/src/type_/tests/guards.rs
index 67b893560..fe0508654 100644
--- a/compiler-core/src/type_/tests/guards.rs
+++ b/compiler-core/src/type_/tests/guards.rs
@@ -1,4 +1,4 @@
-use crate::{assert_module_error, assert_module_infer};
+use crate::{assert_infer_with_module, assert_module_error, assert_module_infer};
 
 #[test]
 fn nested_record_access() {
@@ -45,3 +45,42 @@ pub fn a(a: String) {
 "#
     );
 }
+
+#[test]
+fn qualified_record() {
+    assert_infer_with_module!(
+        ("wibble", "pub type Wibble { Wibble Wobble }"),
+        "
+import wibble
+
+pub fn main(wibble: wibble.Wibble) {
+  case wibble {
+    w if w == wibble.Wobble -> True
+    _ -> False
+  }
+}
+",
+        vec![("main", "fn(Wibble) -> Bool")]
+    );
+}
+
+#[test]
+fn qualified_record_with_arguments() {
+    assert_infer_with_module!(
+        (
+            "wibble",
+            "pub type Wibble { Wibble(Int) Wobble(Int, Float) }"
+        ),
+        "
+import wibble
+
+pub fn main(wibble: wibble.Wibble) {
+  case wibble {
+    w if w == wibble.Wobble(1, 3.8) -> True
+    _ -> False
+  }
+}
+",
+        vec![("main", "fn(Wibble) -> Bool")]
+    );
+}