Repository: swc-project/swc (rust)

Issue:
Parser error: "class" as object key in template string
### Describe the bug

parser throw error when there is an object with key as "class" in template string:

```js
console.log(`${toStr({class: fn})}`)
```

output:

```
  x Unexpected token ```. Expected template token
   ,-[input.js:1:1]
 1 | console.log(`${toStr({class: fn})}`)
   :                                   ^
   `----


Caused by:
    0: failed to process js file
    1: failed to process input file
    2: Syntax Error
```

### Playground link

https://play.swc.rs/?version=1.2.246&code=H4sIAAAAAAAAA0vOzyvOz0nVy8lP10hQqS7JDy4p0qhOzkksLrZSSMur1axN0AQA2T4G8SQAAAA%3D&config=H4sIAAAAAAAAA0WMSwrDMAxE76K1F8WLLnKHHkK4SnDxD0mBGuO71w4u2Q0zb16DjzjYGhRkIZ5JalL8wgbkIopjXxTMwEa1YxDqBhT5IJ2I2Ie1Yw45Cy3AQPTJ73XKXI6FSeSeMB3hT%2Fbhivl9zqKB1kKX8wn9dqyfl9cClU%2FqPxrFaXe4AAAA

### Version

**1.2.246**

Pull request description:
fix(es/lexer): drop `ClassExpr` context in object prop
<!-- Note: CI script will automatically rebase your PR so please do not rebase unless required -->

<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

**BREAKING CHANGE:**

<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**
- fix #5722 

Patch:
diff --git a/crates/swc_ecma_parser/src/lexer/state.rs b/crates/swc_ecma_parser/src/lexer/state.rs
index 0f67c78e76..a736eac82e 100644
--- a/crates/swc_ecma_parser/src/lexer/state.rs
+++ b/crates/swc_ecma_parser/src/lexer/state.rs
@@ -479,9 +479,18 @@ impl State {
                     false
                 }
 
-                tok!(':') if context.current() == Some(TokenContext::FnExpr) => {
-                    // function keyword is object prop: { function: expr }
-                    context.pop(); // Remove FnExpr
+                tok!(':')
+                    if matches!(
+                        context.current(),
+                        Some(TokenContext::FnExpr | TokenContext::ClassExpr)
+                    ) =>
+                {
+                    // `function`/`class` keyword is object prop
+                    //
+                    // ```JavaScript
+                    // { function: expr, class: expr }
+                    // ```
+                    context.pop(); // Remove FnExpr or ClassExpr
                     true
                 }
 
diff --git a/crates/swc_ecma_parser/src/parser/stmt.rs b/crates/swc_ecma_parser/src/parser/stmt.rs
index ce5d33037a..196b30f67a 100644
--- a/crates/swc_ecma_parser/src/parser/stmt.rs
+++ b/crates/swc_ecma_parser/src/parser/stmt.rs
@@ -2488,4 +2488,11 @@ const foo;"#;
 
         test_parser(src, Default::default(), |p| p.parse_script());
     }
+
+    #[test]
+    fn issue_5722_class_keyword_in_tpl() {
+        let src = "console.log(`${toStr({class: fn})}`)";
+
+        test_parser(src, Default::default(), |p| p.parse_script());
+    }
 }


Test patch:
diff --git a/crates/swc_ecma_parser/src/lexer/tests.rs b/crates/swc_ecma_parser/src/lexer/tests.rs
index 2266e37c4e..5fa0eba920 100644
--- a/crates/swc_ecma_parser/src/lexer/tests.rs
+++ b/crates/swc_ecma_parser/src/lexer/tests.rs
@@ -1080,6 +1080,35 @@ fn issue_191() {
     );
 }
 
+#[test]
+fn issue_5722() {
+    assert_eq!(
+        lex_tokens(Default::default(), "`${{class: 0}}`"),
+        vec![
+            tok!('`'),
+            Template {
+                raw: "".into(),
+                cooked: Ok("".into()),
+            },
+            tok!("${"),
+            tok!('{'),
+            Word(Word::Keyword(Keyword::Class)),
+            tok!(':'),
+            Num {
+                value: 0.into(),
+                raw: "0".into()
+            },
+            tok!('}'),
+            tok!('}'),
+            Template {
+                raw: "".into(),
+                cooked: Ok("".into()),
+            },
+            tok!('`'),
+        ]
+    )
+}
+
 #[test]
 fn jsx_05() {
     assert_eq!(