Repository: swc-project/swc (rust)

Issue:
Parser error for object literal with `function` property inside JSX
**Describe the bug**

An object literal with a property named `function` inside JSX causes a parser error.

```
error: Expression expected
 --> x.js:1:43
  |
1 | const x = <div onclick={{ function: 123 }}></div>;
  |                                           ^

error: Unexpected token `/`. Expected jsx identifier
 --> x.js:1:45
  |
1 | const x = <div onclick={{ function: 123 }}></div>;
  |                                             ^
```

**Input code**

```jsx
const x = <div onclick={(e) => ({ function: 123 })}></div>;
```
or
```jsx
const x = <div onclick={{ function: 123 }}></div>;
```


**Config**

```json
{
  "jsc": {
    "parser": {
      "syntax": "ecmascript",
      "jsx": true
    }
  }
}
```

**Expected behavior**
Should be parser & transpiled successfully.

**Version**
The version of @swc/core: 1.2.77

**Additional context**
https://github.com/parcel-bundler/parcel/issues/6719


Pull request:
fix(es/parser): Fix parsing of `function` in property names
swc_ecma_parser:
 - Use correct token context when `function` is used as the name of a property. (Closes #2075)

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index fbb8e42063..52b4e64bcf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2381,7 +2381,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_parser"
-version = "0.66.4"
+version = "0.66.5"
 dependencies = [
  "either",
  "enum_kind",
diff --git a/ecmascript/parser/Cargo.toml b/ecmascript/parser/Cargo.toml
index e4bc54175e..6f8485015e 100644
--- a/ecmascript/parser/Cargo.toml
+++ b/ecmascript/parser/Cargo.toml
@@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "examples/**/*.rs"]
 license = "Apache-2.0/MIT"
 name = "swc_ecma_parser"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.66.4"
+version = "0.66.5"
 
 [package.metadata.docs.rs]
 all-features = true
diff --git a/ecmascript/parser/src/parser/object.rs b/ecmascript/parser/src/parser/object.rs
index 58379942f8..e23884f6ec 100644
--- a/ecmascript/parser/src/parser/object.rs
+++ b/ecmascript/parser/src/parser/object.rs
@@ -1,7 +1,7 @@
 //! Parser for object literal.
 
 use super::*;
-use crate::parser::class_and_fn::is_not_this;
+use crate::{lexer::TokenContext, parser::class_and_fn::is_not_this, token::Keyword};
 use swc_atoms::js_word;
 use swc_common::Spanned;
 
@@ -39,6 +39,8 @@ impl<'a, I: Tokens> Parser<I> {
 
     /// spec: 'PropertyName'
     pub(super) fn parse_prop_name(&mut self) -> PResult<PropName> {
+        trace_cur!(self, parse_prop_name);
+
         let ctx = self.ctx();
         self.with_ctx(Context {
             in_property_name: true,
@@ -74,7 +76,19 @@ impl<'a, I: Tokens> Parser<I> {
                     _ => unreachable!(),
                 },
                 Word(..) => match bump!(p) {
-                    Word(w) => PropName::Ident(Ident::new(w.into(), span!(p, start))),
+                    Word(w) => {
+                        match w {
+                            Word::Keyword(Keyword::Function) => {
+                                // See https://github.com/swc-project/swc/issues/2075
+                                let ctx = p.input.token_context_mut();
+                                let token_ctx = ctx.pop();
+
+                                debug_assert_eq!(token_ctx, Some(TokenContext::FnExpr));
+                            }
+                            _ => {}
+                        }
+                        PropName::Ident(Ident::new(w.into(), span!(p, start)))
+                    }
                     _ => unreachable!(),
                 },
                 tok!('[') => {


Test patch:
diff --git a/ecmascript/parser/tests/jsx/basic/issue-2075/case1/input.js b/ecmascript/parser/tests/jsx/basic/issue-2075/case1/input.js
new file mode 100644
index 0000000000..a1ab8d1126
--- /dev/null
+++ b/ecmascript/parser/tests/jsx/basic/issue-2075/case1/input.js
@@ -0,0 +1,1 @@
+const x = <div onclick={{ function: 123 }}></div>;
\ No newline at end of file
diff --git a/ecmascript/parser/tests/jsx/basic/issue-2075/case1/input.js.json b/ecmascript/parser/tests/jsx/basic/issue-2075/case1/input.js.json
new file mode 100644
index 0000000000..e36b4bc7a9
--- /dev/null
+++ b/ecmascript/parser/tests/jsx/basic/issue-2075/case1/input.js.json
@@ -0,0 +1,150 @@
+{
+  "type": "Module",
+  "span": {
+    "start": 0,
+    "end": 50,
+    "ctxt": 0
+  },
+  "body": [
+    {
+      "type": "VariableDeclaration",
+      "span": {
+        "start": 0,
+        "end": 50,
+        "ctxt": 0
+      },
+      "kind": "const",
+      "declare": false,
+      "declarations": [
+        {
+          "type": "VariableDeclarator",
+          "span": {
+            "start": 6,
+            "end": 49,
+            "ctxt": 0
+          },
+          "id": {
+            "type": "Identifier",
+            "span": {
+              "start": 6,
+              "end": 7,
+              "ctxt": 0
+            },
+            "value": "x",
+            "optional": false,
+            "typeAnnotation": null
+          },
+          "init": {
+            "type": "JSXElement",
+            "span": {
+              "start": 10,
+              "end": 49,
+              "ctxt": 0
+            },
+            "opening": {
+              "type": "JSXOpeningElement",
+              "name": {
+                "type": "Identifier",
+                "span": {
+                  "start": 11,
+                  "end": 14,
+                  "ctxt": 0
+                },
+                "value": "div",
+                "optional": false
+              },
+              "span": {
+                "start": 10,
+                "end": 43,
+                "ctxt": 0
+              },
+              "attributes": [
+                {
+                  "type": "JSXAttribute",
+                  "span": {
+                    "start": 15,
+                    "end": 42,
+                    "ctxt": 0
+                  },
+                  "name": {
+                    "type": "Identifier",
+                    "span": {
+                      "start": 15,
+                      "end": 22,
+                      "ctxt": 0
+                    },
+                    "value": "onclick",
+                    "optional": false
+                  },
+                  "value": {
+                    "type": "JSXExpressionContainer",
+                    "span": {
+                      "start": 23,
+                      "end": 42,
+                      "ctxt": 0
+                    },
+                    "expression": {
+                      "type": "ObjectExpression",
+                      "span": {
+                        "start": 24,
+                        "end": 41,
+                        "ctxt": 0
+                      },
+                      "properties": [
+                        {
+                          "type": "KeyValueProperty",
+                          "key": {
+                            "type": "Identifier",
+                            "span": {
+                              "start": 26,
+                              "end": 34,
+                              "ctxt": 0
+                            },
+                            "value": "function",
+                            "optional": false
+                          },
+                          "value": {
+                            "type": "NumericLiteral",
+                            "span": {
+                              "start": 36,
+                              "end": 39,
+                              "ctxt": 0
+                            },
+                            "value": 123.0
+                          }
+                        }
+                      ]
+                    }
+                  }
+                }
+              ],
+              "selfClosing": false,
+              "typeArguments": null
+            },
+            "children": [],
+            "closing": {
+              "type": "JSXClosingElement",
+              "span": {
+                "start": 43,
+                "end": 49,
+                "ctxt": 0
+              },
+              "name": {
+                "type": "Identifier",
+                "span": {
+                  "start": 45,
+                  "end": 48,
+                  "ctxt": 0
+                },
+                "value": "div",
+                "optional": false
+              }
+            }
+          },
+          "definite": false
+        }
+      ]
+    }
+  ],
+  "interpreter": null
+}
diff --git a/ecmascript/parser/tests/jsx/basic/issue-2075/not-keyword/input.js b/ecmascript/parser/tests/jsx/basic/issue-2075/not-keyword/input.js
new file mode 100644
index 0000000000..5e87d613c6
--- /dev/null
+++ b/ecmascript/parser/tests/jsx/basic/issue-2075/not-keyword/input.js
@@ -0,0 +1,1 @@
+const x = <div onclick={{ foo: 123 }}></div>;
\ No newline at end of file
diff --git a/ecmascript/parser/tests/jsx/basic/issue-2075/not-keyword/input.js.json b/ecmascript/parser/tests/jsx/basic/issue-2075/not-keyword/input.js.json
new file mode 100644
index 0000000000..66ce87967b
--- /dev/null
+++ b/ecmascript/parser/tests/jsx/basic/issue-2075/not-keyword/input.js.json
@@ -0,0 +1,150 @@
+{
+  "type": "Module",
+  "span": {
+    "start": 0,
+    "end": 45,
+    "ctxt": 0
+  },
+  "body": [
+    {
+      "type": "VariableDeclaration",
+      "span": {
+        "start": 0,
+        "end": 45,
+        "ctxt": 0
+      },
+      "kind": "const",
+      "declare": false,
+      "declarations": [
+        {
+          "type": "VariableDeclarator",
+          "span": {
+            "start": 6,
+            "end": 44,
+            "ctxt": 0
+          },
+          "id": {
+            "type": "Identifier",
+            "span": {
+              "start": 6,
+              "end": 7,
+              "ctxt": 0
+            },
+            "value": "x",
+            "optional": false,
+            "typeAnnotation": null
+          },
+          "init": {
+            "type": "JSXElement",
+            "span": {
+              "start": 10,
+              "end": 44,
+              "ctxt": 0
+            },
+            "opening": {
+              "type": "JSXOpeningElement",
+              "name": {
+                "type": "Identifier",
+                "span": {
+                  "start": 11,
+                  "end": 14,
+                  "ctxt": 0
+                },
+                "value": "div",
+                "optional": false
+              },
+              "span": {
+                "start": 10,
+                "end": 38,
+                "ctxt": 0
+              },
+              "attributes": [
+                {
+                  "type": "JSXAttribute",
+                  "span": {
+                    "start": 15,
+                    "end": 37,
+                    "ctxt": 0
+                  },
+                  "name": {
+                    "type": "Identifier",
+                    "span": {
+                      "start": 15,
+                      "end": 22,
+                      "ctxt": 0
+                    },
+                    "value": "onclick",
+                    "optional": false
+                  },
+                  "value": {
+                    "type": "JSXExpressionContainer",
+                    "span": {
+                      "start": 23,
+                      "end": 37,
+                      "ctxt": 0
+                    },
+                    "expression": {
+                      "type": "ObjectExpression",
+                      "span": {
+                        "start": 24,
+                        "end": 36,
+                        "ctxt": 0
+                      },
+                      "properties": [
+                        {
+                          "type": "KeyValueProperty",
+                          "key": {
+                            "type": "Identifier",
+                            "span": {
+                              "start": 26,
+                              "end": 29,
+                              "ctxt": 0
+                            },
+                            "value": "foo",
+                            "optional": false
+                          },
+                          "value": {
+                            "type": "NumericLiteral",
+                            "span": {
+                              "start": 31,
+                              "end": 34,
+                              "ctxt": 0
+                            },
+                            "value": 123.0
+                          }
+                        }
+                      ]
+                    }
+                  }
+                }
+              ],
+              "selfClosing": false,
+              "typeArguments": null
+            },
+            "children": [],
+            "closing": {
+              "type": "JSXClosingElement",
+              "span": {
+                "start": 38,
+                "end": 44,
+                "ctxt": 0
+              },
+              "name": {
+                "type": "Identifier",
+                "span": {
+                  "start": 40,
+                  "end": 43,
+                  "ctxt": 0
+                },
+                "value": "div",
+                "optional": false
+              }
+            }
+          },
+          "definite": false
+        }
+      ]
+    }
+  ],
+  "interpreter": null
+}