Repository: swc-project/swc (rust)

Issue:
CSS parser error recovery without trailing semicolon
When parsing CSS, `a{*b:c}` currently returns a non-recoverable error while `a{*b:c;}` (with a semicolon) returns a recoverable error.

Both should return a recoverable error.

Pull request:
fix: #3496
<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

Fixed #3496, we still need some refactor when we will implement grammar parsing, but let's fix it currently to avoid future regressions

**BREAKING CHANGE:**

No

**Related issue (if exists):**

No

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b153dba1fa..2e9c721209 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,15 @@
 
 - **(node/helpers)** Fix `isNativeReflectConstruct` (#3530) ([d14d3eb](https://github.com/swc-project/swc/commit/d14d3ebbc8f24a6bed61ce9e534ab06b32c08e34))
 
+
+- **(plugin)** Allow testing (#3542) ([fbe9196](https://github.com/swc-project/swc/commit/fbe91963bfb80976eebadc9f0e94e080ed81d624))
+
+### Documentation
+
+
+
+- **(plugin)** Fix install command (#3541) ([72b3197](https://github.com/swc-project/swc/commit/72b31977e49417cf9e4c95ad1365e2e212cac4d4))
+
 ### Features
 
 
diff --git a/Cargo.lock b/Cargo.lock
index 7269ee1e5b..3028177142 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3524,7 +3524,7 @@ dependencies = [
 
 [[package]]
 name = "swc_plugin"
-version = "0.26.0"
+version = "0.26.1"
 dependencies = [
  "once_cell",
  "swc_atoms",
@@ -3536,7 +3536,7 @@ dependencies = [
 
 [[package]]
 name = "swc_plugin_macro"
-version = "0.2.0"
+version = "0.2.1"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/crates/swc_css_parser/src/parser/base.rs b/crates/swc_css_parser/src/parser/base.rs
index 3dfcd890c5..6cc10b36bc 100644
--- a/crates/swc_css_parser/src/parser/base.rs
+++ b/crates/swc_css_parser/src/parser/base.rs
@@ -336,7 +336,8 @@ where
 
                     let mut tokens = vec![];
 
-                    while !is_one_of!(self, EOF, ";") {
+                    // TODO fix me
+                    while !is_one_of!(self, EOF, ";", "}") {
                         tokens.extend(self.input.bump()?);
                     }
 
diff --git a/crates/swc_plugin/Cargo.toml b/crates/swc_plugin/Cargo.toml
index 14c6c6e89d..1297438a03 100644
--- a/crates/swc_plugin/Cargo.toml
+++ b/crates/swc_plugin/Cargo.toml
@@ -6,12 +6,12 @@ edition = "2018"
 license = "Apache-2.0"
 name = "swc_plugin"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.26.0"
+version = "0.26.1"
 
 [dependencies]
 swc_common = {version = "0.17.0", path = "../swc_common", features = ["plugin-mode"]}
 swc_ecma_ast = {version = "0.65.0", path = "../swc_ecma_ast", features = ["rkyv-impl"]}
 swc_ecma_visit = {version = "0.51.0", path = "../swc_ecma_visit"}
 swc_atoms = {version = "0.2.0", path = "../swc_atoms"}
-swc_plugin_macro = {version = "0.2.0", path = "../swc_plugin_macro"}
+swc_plugin_macro = {version = "0.2.1", path = "../swc_plugin_macro"}
 once_cell = "1.9.0"
diff --git a/crates/swc_plugin/README.md b/crates/swc_plugin/README.md
index 3d41e6f44d..69094c15c5 100644
--- a/crates/swc_plugin/README.md
+++ b/crates/swc_plugin/README.md
@@ -12,7 +12,7 @@ All plugin require adding crate-type = ['cdylib'] to the Cargo.toml. For a quick
 // if you haven't, add build targets for webassembly
 rustup target add wasm32-wasi wasm32-unknown-unknown
 
-cargo install swc
+cargo install swc_cli
 
 swc plugin new ${plugin_name} --target-type wasm32-wasi
 ```
diff --git a/crates/swc_plugin/src/context.rs b/crates/swc_plugin/src/context.rs
index 49ff4f5882..78d8667d58 100644
--- a/crates/swc_plugin/src/context.rs
+++ b/crates/swc_plugin/src/context.rs
@@ -1,5 +1,6 @@
 use swc_common::plugin::Serialized;
 
+#[cfg(target_arch = "wasm32")] // Allow testing
 extern "C" {
     fn __emit_diagnostics(bytes_ptr: i32, bytes_ptr_len: i32);
     fn __free(bytes_ptr: i32, size: i32) -> i32;
@@ -13,14 +14,16 @@ extern "C" {
 pub struct PluginDiagnosticsEmitter {}
 
 impl swc_common::errors::Emitter for PluginDiagnosticsEmitter {
+    #[cfg_attr(not(target_arch = "wasm32"), allow(unused))]
     fn emit(&mut self, db: &swc_common::errors::DiagnosticBuilder<'_>) {
         let diag =
             Serialized::serialize(&*db.diagnostic).expect("Should able to serialize Diagnostic");
         let diag_ref = diag.as_ref();
 
-        let ptr = diag_ref.as_ptr() as _;
+        let ptr = diag_ref.as_ptr() as i32;
         let len = diag_ref.len() as i32;
 
+        #[cfg(target_arch = "wasm32")] // Allow testing
         unsafe {
             __emit_diagnostics(ptr, len);
         }
diff --git a/crates/swc_plugin_macro/Cargo.toml b/crates/swc_plugin_macro/Cargo.toml
index f21caa2f62..08120e66c4 100644
--- a/crates/swc_plugin_macro/Cargo.toml
+++ b/crates/swc_plugin_macro/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
 license = "Apache-2.0"
 name = "swc_plugin_macro"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.2.0"
+version = "0.2.1"
 
 [lib]
 proc-macro = true
diff --git a/crates/swc_plugin_macro/src/lib.rs b/crates/swc_plugin_macro/src/lib.rs
index 217aceaf02..0bcdf19c17 100644
--- a/crates/swc_plugin_macro/src/lib.rs
+++ b/crates/swc_plugin_macro/src/lib.rs
@@ -26,6 +26,7 @@ fn handle_func(func: ItemFn) -> TokenStream {
 
         // Declaration for imported function from swc host.
         // Refer swc_plugin_runner for the actual implementation.
+        #[cfg(target_arch = "wasm32")] // Allow testing
         extern "C" {
             fn __set_transform_result(bytes_ptr: i32, bytes_ptr_len: i32);
             fn __free(bytes_ptr: i32, size: i32) -> i32;
@@ -36,6 +37,7 @@ fn handle_func(func: ItemFn) -> TokenStream {
         /// When guest calls __set_transform_result host should've completed read guest's memory and allocates its byte
         /// into host's enviroment so guest can free its memory later.
         fn set_transform_result_volatile(bytes_ptr: i32, bytes_ptr_len: i32) {
+            #[cfg(target_arch = "wasm32")] // Allow testing
             unsafe {
                 __set_transform_result(bytes_ptr, bytes_ptr_len);
                 __free(bytes_ptr, bytes_ptr_len);


Test patch:
diff --git a/crates/swc_css_parser/tests/recovery/hacks/input.css b/crates/swc_css_parser/tests/recovery/hacks/input.css
index c5d264bd0b..b924fad5ec 100644
--- a/crates/swc_css_parser/tests/recovery/hacks/input.css
+++ b/crates/swc_css_parser/tests/recovery/hacks/input.css
@@ -246,3 +246,17 @@ a {
     font-size/**/: big;
     $(var)-size: 100%;
 }
+
+a{*b:c}
+
+div {
+    color: red;
+}
+
+/*\*//*/ selector { property:value; } /**/
+selector { property:value; p\roperty: normal-value; }
+
+
+div {
+    color: red;
+}
diff --git a/crates/swc_css_parser/tests/recovery/hacks/output.json b/crates/swc_css_parser/tests/recovery/hacks/output.json
index 99572e312b..bd6b544a7e 100644
--- a/crates/swc_css_parser/tests/recovery/hacks/output.json
+++ b/crates/swc_css_parser/tests/recovery/hacks/output.json
@@ -2,7 +2,7 @@
   "type": "Stylesheet",
   "span": {
     "start": 13,
-    "end": 4857,
+    "end": 5015,
     "ctxt": 0
   },
   "rules": [
@@ -14570,6 +14570,494 @@
           }
         ]
       }
+    },
+    {
+      "type": "QualifiedRule",
+      "span": {
+        "start": 4858,
+        "end": 4865,
+        "ctxt": 0
+      },
+      "prelude": {
+        "type": "SelectorList",
+        "span": {
+          "start": 4858,
+          "end": 4859,
+          "ctxt": 0
+        },
+        "children": [
+          {
+            "type": "ComplexSelector",
+            "span": {
+              "start": 4858,
+              "end": 4859,
+              "ctxt": 0
+            },
+            "children": [
+              {
+                "type": "CompoundSelector",
+                "span": {
+                  "start": 4858,
+                  "end": 4859,
+                  "ctxt": 0
+                },
+                "nestingSelector": null,
+                "typeSelector": {
+                  "type": "TagNameSelector",
+                  "span": {
+                    "start": 4858,
+                    "end": 4859,
+                    "ctxt": 0
+                  },
+                  "name": {
+                    "type": "WqName",
+                    "span": {
+                      "start": 4858,
+                      "end": 4859,
+                      "ctxt": 0
+                    },
+                    "prefix": null,
+                    "value": {
+                      "type": "Ident",
+                      "span": {
+                        "start": 4858,
+                        "end": 4859,
+                        "ctxt": 0
+                      },
+                      "value": "a",
+                      "raw": "a"
+                    }
+                  }
+                },
+                "subclassSelectors": []
+              }
+            ]
+          }
+        ]
+      },
+      "block": {
+        "type": "SimpleBlock",
+        "span": {
+          "start": 4859,
+          "end": 4865,
+          "ctxt": 0
+        },
+        "name": "{",
+        "value": [
+          {
+            "type": "Tokens",
+            "span": {
+              "start": 4860,
+              "end": 4864,
+              "ctxt": 0
+            },
+            "tokens": [
+              {
+                "type": "PreservedToken",
+                "span": {
+                  "start": 4860,
+                  "end": 4861,
+                  "ctxt": 0
+                },
+                "token": {
+                  "Delim": {
+                    "value": "*"
+                  }
+                }
+              },
+              {
+                "type": "PreservedToken",
+                "span": {
+                  "start": 4861,
+                  "end": 4862,
+                  "ctxt": 0
+                },
+                "token": {
+                  "Ident": {
+                    "value": "b",
+                    "raw": "b"
+                  }
+                }
+              },
+              {
+                "type": "PreservedToken",
+                "span": {
+                  "start": 4862,
+                  "end": 4863,
+                  "ctxt": 0
+                },
+                "token": "Colon"
+              },
+              {
+                "type": "PreservedToken",
+                "span": {
+                  "start": 4863,
+                  "end": 4864,
+                  "ctxt": 0
+                },
+                "token": {
+                  "Ident": {
+                    "value": "c",
+                    "raw": "c"
+                  }
+                }
+              }
+            ]
+          }
+        ]
+      }
+    },
+    {
+      "type": "QualifiedRule",
+      "span": {
+        "start": 4867,
+        "end": 4890,
+        "ctxt": 0
+      },
+      "prelude": {
+        "type": "SelectorList",
+        "span": {
+          "start": 4867,
+          "end": 4870,
+          "ctxt": 0
+        },
+        "children": [
+          {
+            "type": "ComplexSelector",
+            "span": {
+              "start": 4867,
+              "end": 4870,
+              "ctxt": 0
+            },
+            "children": [
+              {
+                "type": "CompoundSelector",
+                "span": {
+                  "start": 4867,
+                  "end": 4870,
+                  "ctxt": 0
+                },
+                "nestingSelector": null,
+                "typeSelector": {
+                  "type": "TagNameSelector",
+                  "span": {
+                    "start": 4867,
+                    "end": 4870,
+                    "ctxt": 0
+                  },
+                  "name": {
+                    "type": "WqName",
+                    "span": {
+                      "start": 4867,
+                      "end": 4870,
+                      "ctxt": 0
+                    },
+                    "prefix": null,
+                    "value": {
+                      "type": "Ident",
+                      "span": {
+                        "start": 4867,
+                        "end": 4870,
+                        "ctxt": 0
+                      },
+                      "value": "div",
+                      "raw": "div"
+                    }
+                  }
+                },
+                "subclassSelectors": []
+              }
+            ]
+          }
+        ]
+      },
+      "block": {
+        "type": "SimpleBlock",
+        "span": {
+          "start": 4871,
+          "end": 4890,
+          "ctxt": 0
+        },
+        "name": "{",
+        "value": [
+          {
+            "type": "Declaration",
+            "span": {
+              "start": 4877,
+              "end": 4887,
+              "ctxt": 0
+            },
+            "name": {
+              "type": "Ident",
+              "span": {
+                "start": 4877,
+                "end": 4882,
+                "ctxt": 0
+              },
+              "value": "color",
+              "raw": "color"
+            },
+            "value": [
+              {
+                "type": "Ident",
+                "span": {
+                  "start": 4884,
+                  "end": 4887,
+                  "ctxt": 0
+                },
+                "value": "red",
+                "raw": "red"
+              }
+            ],
+            "important": null
+          }
+        ]
+      }
+    },
+    {
+      "type": "QualifiedRule",
+      "span": {
+        "start": 4935,
+        "end": 4988,
+        "ctxt": 0
+      },
+      "prelude": {
+        "type": "SelectorList",
+        "span": {
+          "start": 4935,
+          "end": 4943,
+          "ctxt": 0
+        },
+        "children": [
+          {
+            "type": "ComplexSelector",
+            "span": {
+              "start": 4935,
+              "end": 4943,
+              "ctxt": 0
+            },
+            "children": [
+              {
+                "type": "CompoundSelector",
+                "span": {
+                  "start": 4935,
+                  "end": 4943,
+                  "ctxt": 0
+                },
+                "nestingSelector": null,
+                "typeSelector": {
+                  "type": "TagNameSelector",
+                  "span": {
+                    "start": 4935,
+                    "end": 4943,
+                    "ctxt": 0
+                  },
+                  "name": {
+                    "type": "WqName",
+                    "span": {
+                      "start": 4935,
+                      "end": 4943,
+                      "ctxt": 0
+                    },
+                    "prefix": null,
+                    "value": {
+                      "type": "Ident",
+                      "span": {
+                        "start": 4935,
+                        "end": 4943,
+                        "ctxt": 0
+                      },
+                      "value": "selector",
+                      "raw": "selector"
+                    }
+                  }
+                },
+                "subclassSelectors": []
+              }
+            ]
+          }
+        ]
+      },
+      "block": {
+        "type": "SimpleBlock",
+        "span": {
+          "start": 4944,
+          "end": 4988,
+          "ctxt": 0
+        },
+        "name": "{",
+        "value": [
+          {
+            "type": "Declaration",
+            "span": {
+              "start": 4946,
+              "end": 4960,
+              "ctxt": 0
+            },
+            "name": {
+              "type": "Ident",
+              "span": {
+                "start": 4946,
+                "end": 4954,
+                "ctxt": 0
+              },
+              "value": "property",
+              "raw": "property"
+            },
+            "value": [
+              {
+                "type": "Ident",
+                "span": {
+                  "start": 4955,
+                  "end": 4960,
+                  "ctxt": 0
+                },
+                "value": "value",
+                "raw": "value"
+              }
+            ],
+            "important": null
+          },
+          {
+            "type": "Declaration",
+            "span": {
+              "start": 4962,
+              "end": 4985,
+              "ctxt": 0
+            },
+            "name": {
+              "type": "Ident",
+              "span": {
+                "start": 4962,
+                "end": 4971,
+                "ctxt": 0
+              },
+              "value": "property",
+              "raw": "p\\roperty"
+            },
+            "value": [
+              {
+                "type": "Ident",
+                "span": {
+                  "start": 4973,
+                  "end": 4985,
+                  "ctxt": 0
+                },
+                "value": "normal-value",
+                "raw": "normal-value"
+              }
+            ],
+            "important": null
+          }
+        ]
+      }
+    },
+    {
+      "type": "QualifiedRule",
+      "span": {
+        "start": 4991,
+        "end": 5014,
+        "ctxt": 0
+      },
+      "prelude": {
+        "type": "SelectorList",
+        "span": {
+          "start": 4991,
+          "end": 4994,
+          "ctxt": 0
+        },
+        "children": [
+          {
+            "type": "ComplexSelector",
+            "span": {
+              "start": 4991,
+              "end": 4994,
+              "ctxt": 0
+            },
+            "children": [
+              {
+                "type": "CompoundSelector",
+                "span": {
+                  "start": 4991,
+                  "end": 4994,
+                  "ctxt": 0
+                },
+                "nestingSelector": null,
+                "typeSelector": {
+                  "type": "TagNameSelector",
+                  "span": {
+                    "start": 4991,
+                    "end": 4994,
+                    "ctxt": 0
+                  },
+                  "name": {
+                    "type": "WqName",
+                    "span": {
+                      "start": 4991,
+                      "end": 4994,
+                      "ctxt": 0
+                    },
+                    "prefix": null,
+                    "value": {
+                      "type": "Ident",
+                      "span": {
+                        "start": 4991,
+                        "end": 4994,
+                        "ctxt": 0
+                      },
+                      "value": "div",
+                      "raw": "div"
+                    }
+                  }
+                },
+                "subclassSelectors": []
+              }
+            ]
+          }
+        ]
+      },
+      "block": {
+        "type": "SimpleBlock",
+        "span": {
+          "start": 4995,
+          "end": 5014,
+          "ctxt": 0
+        },
+        "name": "{",
+        "value": [
+          {
+            "type": "Declaration",
+            "span": {
+              "start": 5001,
+              "end": 5011,
+              "ctxt": 0
+            },
+            "name": {
+              "type": "Ident",
+              "span": {
+                "start": 5001,
+                "end": 5006,
+                "ctxt": 0
+              },
+              "value": "color",
+              "raw": "color"
+            },
+            "value": [
+              {
+                "type": "Ident",
+                "span": {
+                  "start": 5008,
+                  "end": 5011,
+                  "ctxt": 0
+                },
+                "value": "red",
+                "raw": "red"
+              }
+            ],
+            "important": null
+          }
+        ]
+      }
     }
   ]
 }
diff --git a/crates/swc_css_parser/tests/recovery/hacks/output.swc-stderr b/crates/swc_css_parser/tests/recovery/hacks/output.swc-stderr
index d74123ea9d..f98041af99 100644
--- a/crates/swc_css_parser/tests/recovery/hacks/output.swc-stderr
+++ b/crates/swc_css_parser/tests/recovery/hacks/output.swc-stderr
@@ -214,3 +214,9 @@ error: Expected whitespace, semicolon, EOF, at-keyword or ident token
 247 |     $(var)-size: 100%;
     |     ^
 
+error: Expected whitespace, semicolon, EOF, at-keyword or ident token
+   --> $DIR/tests/recovery/hacks/input.css:250:3
+    |
+250 | a{*b:c}
+    |   ^
+