Repository: biomejs/biome (language: rust)

Issue:
🐛 Panic `Called 'unwrap' on an 'Absent' syntax`
### Environment information

```block
CLI:
  Version:                      0.0.0 (self compiled 4848994e4507d80e56ea07c40ff41a7b303918cd)
  Color support:                true

Platform:
  CPU Architecture:             x86_64
  OS:                           linux

Environment:
  BIOME_LOG_PATH:               unset
  BIOME_LOG_PREFIX_NAME:        unset
  BIOME_CONFIG_PATH:            unset
  NO_COLOR:                     unset
  TERM:                         "xterm-256color"
  JS_RUNTIME_VERSION:           unset
  JS_RUNTIME_NAME:              unset
  NODE_PACKAGE_MANAGER:         unset

Biome Configuration:
  Status:                       unset

Workspace:
  Open Documents:               0
```


### What happened?

File content(at the bottom should be attached raw, not formatted file - github removes some non-printable characters, so copying from here may not work):
```
for@s[]of
```

command
```
timeout -v 150 biome lint TEST___FILE.js
```

cause this
```
Biome encountered an unexpected error

This is a bug in Biome, not an error in your code, and we would appreciate it if you could report it to https://github.com/biomejs/biome/issues/ along with the following information to help us fixing the issue:

Source Location: crates/biome_js_parser/src/syntax/assignment.rs:94:59
Thread Name: biome::worker_0
Message: Called `unwrap` on an `Absent` syntax


##### Automatic Fuzzer note, output status "None", output signal "Some(6)"
```

[compressed.zip](https://github.com/user-attachments/files/17878769/compressed.zip)


### Expected result

No crash

### Code of Conduct

- [X] I agree to follow Biome's Code of Conduct


Discussion / hints:

Fix patch:
diff --git a/crates/biome_js_parser/src/syntax/assignment.rs b/crates/biome_js_parser/src/syntax/assignment.rs
index bbc13fc52749..d406ef09295e 100644
--- a/crates/biome_js_parser/src/syntax/assignment.rs
+++ b/crates/biome_js_parser/src/syntax/assignment.rs
@@ -83,17 +83,17 @@ pub(crate) fn expression_to_assignment_pattern(
     p: &mut JsParser,
     target: CompletedMarker,
     checkpoint: JsParserCheckpoint,
-) -> CompletedMarker {
+) -> ParsedSyntax {
     match target.kind(p) {
         JS_OBJECT_EXPRESSION => {
             p.rewind(checkpoint);
-            ObjectAssignmentPattern.parse_object_pattern(p).unwrap()
+            ObjectAssignmentPattern.parse_object_pattern(p)
         }
         JS_ARRAY_EXPRESSION => {
             p.rewind(checkpoint);
-            ArrayAssignmentPattern.parse_array_pattern(p).unwrap()
+            ArrayAssignmentPattern.parse_array_pattern(p)
         }
-        _ => expression_to_assignment(p, target, checkpoint),
+        _ => ParsedSyntax::Present(expression_to_assignment(p, target, checkpoint)),
     }
 }
 
@@ -119,7 +119,7 @@ pub(crate) fn parse_assignment_pattern(p: &mut JsParser) -> ParsedSyntax {
     let assignment_expression = parse_conditional_expr(p, ExpressionContext::default());
 
     assignment_expression
-        .map(|expression| expression_to_assignment_pattern(p, expression, checkpoint))
+        .and_then(|expression| expression_to_assignment_pattern(p, expression, checkpoint))
 }
 
 /// Re-parses an expression as an assignment.
diff --git a/crates/biome_js_parser/src/syntax/expr.rs b/crates/biome_js_parser/src/syntax/expr.rs
index 29a1f7a7350c..713a2ee160bc 100644
--- a/crates/biome_js_parser/src/syntax/expr.rs
+++ b/crates/biome_js_parser/src/syntax/expr.rs
@@ -352,7 +352,10 @@ fn parse_assign_expr_recursive(
             target.change_kind(p, JS_BOGUS_ASSIGNMENT);
             target
         } else {
-            expression_to_assignment_pattern(p, target, checkpoint)
+            match expression_to_assignment_pattern(p, target, checkpoint) {
+                Present(target) => target,
+                Absent => return Absent,
+            }
         };
 
         let m = target.precede(p);
diff --git a/crates/biome_js_parser/src/syntax/stmt.rs b/crates/biome_js_parser/src/syntax/stmt.rs
index df9231590a96..871bbf689514 100644
--- a/crates/biome_js_parser/src/syntax/stmt.rs
+++ b/crates/biome_js_parser/src/syntax/stmt.rs
@@ -1646,10 +1646,9 @@ fn parse_for_head(p: &mut JsParser, has_l_paren: bool, is_for_await: bool) -> Js
 
         if p.at(T![in]) || p.at(T![of]) {
             // for (assignment_pattern in ...
-            if let Present(assignment_expr) = init_expr {
-                let mut assignment =
-                    expression_to_assignment_pattern(p, assignment_expr, checkpoint);
-
+            if let Present(mut assignment) = init_expr.and_then(|assignment_expr| {
+                expression_to_assignment_pattern(p, assignment_expr, checkpoint)
+            }) {
                 if TypeScript.is_supported(p)
                     && p.at(T![in])
                     && matches!(


Test patch:
diff --git a/crates/biome_js_parser/tests/js_test_suite/error/issue4617.js b/crates/biome_js_parser/tests/js_test_suite/error/issue4617.js
new file mode 100644
index 000000000000..10b98dd1e259
--- /dev/null
+++ b/crates/biome_js_parser/tests/js_test_suite/error/issue4617.js
@@ -0,0 +1,1 @@
+for@s[]of
diff --git a/crates/biome_js_parser/tests/js_test_suite/error/issue4617.js.snap b/crates/biome_js_parser/tests/js_test_suite/error/issue4617.js.snap
new file mode 100644
index 000000000000..214e755ead68
--- /dev/null
+++ b/crates/biome_js_parser/tests/js_test_suite/error/issue4617.js.snap
@@ -0,0 +1,108 @@
+---
+source: crates/biome_js_parser/tests/spec_test.rs
+expression: snapshot
+---
+## Input
+
+```js
+for@s[]of
+
+```
+
+
+## AST
+
+```
+JsModule {
+    bom_token: missing (optional),
+    interpreter_token: missing (optional),
+    directives: JsDirectiveList [],
+    items: JsModuleItemList [
+        JsBogusStatement {
+            items: [
+                FOR_KW@0..3 "for" [] [],
+                JsBogusExpression {
+                    items: [
+                        JsDecorator {
+                            at_token: AT@3..4 "@" [] [],
+                            expression: JsIdentifierExpression {
+                                name: JsReferenceIdentifier {
+                                    value_token: IDENT@4..5 "s" [] [],
+                                },
+                            },
+                        },
+                    ],
+                },
+                JsArrayExpression {
+                    l_brack_token: L_BRACK@5..6 "[" [] [],
+                    elements: JsArrayElementList [],
+                    r_brack_token: R_BRACK@6..7 "]" [] [],
+                },
+                JsExpressionStatement {
+                    expression: JsIdentifierExpression {
+                        name: JsReferenceIdentifier {
+                            value_token: IDENT@7..9 "of" [] [],
+                        },
+                    },
+                    semicolon_token: missing (optional),
+                },
+            ],
+        },
+    ],
+    eof_token: EOF@9..10 "" [Newline("\n")] [],
+}
+```
+
+## CST
+
+```
+0: JS_MODULE@0..10
+  0: (empty)
+  1: (empty)
+  2: JS_DIRECTIVE_LIST@0..0
+  3: JS_MODULE_ITEM_LIST@0..9
+    0: JS_BOGUS_STATEMENT@0..9
+      0: FOR_KW@0..3 "for" [] []
+      1: JS_BOGUS_EXPRESSION@3..5
+        0: JS_DECORATOR@3..5
+          0: AT@3..4 "@" [] []
+          1: JS_IDENTIFIER_EXPRESSION@4..5
+            0: JS_REFERENCE_IDENTIFIER@4..5
+              0: IDENT@4..5 "s" [] []
+      2: JS_ARRAY_EXPRESSION@5..7
+        0: L_BRACK@5..6 "[" [] []
+        1: JS_ARRAY_ELEMENT_LIST@6..6
+        2: R_BRACK@6..7 "]" [] []
+      3: JS_EXPRESSION_STATEMENT@7..9
+        0: JS_IDENTIFIER_EXPRESSION@7..9
+          0: JS_REFERENCE_IDENTIFIER@7..9
+            0: IDENT@7..9 "of" [] []
+        1: (empty)
+  4: EOF@9..10 "" [Newline("\n")] []
+
+```
+
+## Diagnostics
+
+```
+issue4617.js:1:4 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+  × expected `(` but instead found `@`
+  
+  > 1 │ for@s[]of
+      │    ^
+    2 │ 
+  
+  i Remove @
+  
+issue4617.js:1:8 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+  × expected `)` but instead found `of`
+  
+  > 1 │ for@s[]of
+      │        ^^
+    2 │ 
+  
+  i Remove of
+  
+```