Repository: swc-project/swc (rust)

Issue:
Use Strict directive in function with non-simple paramaters should throw error
### Describe the bug

In accordance with ES standards, the "use strict" directive is not allowed to be used within functions with non-simple paramaters. The [input script](https://github.com/tc39/test262/blob/6e61dd7754e7c94ebcf3ee679cb8db3c54a37b50/test/language/statements/function/use-strict-with-non-simple-param.js) was taken from this test262 script.

### Input code

```typescript
function f(a = 0) {
  "use strict";
}
```


### Config

_No response_

### Playground link

https://play.swc.rs/?version=1.2.124&code=H4sIAAAAAAAAA0srzUsuyczPU0jTSFSwVTDQVKjmUlBQKi1OVSguKcpMLlGy5qoFAHt1YaIlAAAA&config=H4sIAAAAAAAAA0WMSwrDMAxE76K1t93kDj2EcJXg4B8aBWqM714bUrKSmPdmOp3wtHWqrBBdH1o2%2FtJG4hPDa6hGbmoz2jlChiNjPcSWgtdksRTITR2lkMPe1pIvqaoAD%2BJ8xL855lAqn2sFnazVeVcllXyCxjN0lwPet216yfgBCKaI9boAAAA%3D

### Expected behavior

An error similar to this one from babel should be thrown

Illegal 'use strict' directive in function with non-simple parameter list. (1:0)

> 1 | function f(a = 0) {
    | ^
  2 |   "use strict";
  3 | }

### Actual behavior

The script transpiles with no exceptions and allows the "use strict" directive within the function.

### Version

1.2.127

### Additional context

_No response_

Pull request description:
fix(ecma/parser): throw error when function body has use strict and paramaters is not simple
**Description:**
throw SyntaxError: Illegal 'use strict' directive in function with non-simple parameter list,
when function body has a 'use strict' and function paramaters is not simple

**Related issue (if exists):**
fix #3236

Patch:
diff --git a/crates/swc_ecma_parser/src/error.rs b/crates/swc_ecma_parser/src/error.rs
index 10773cb0dc..9ae997b5f7 100644
--- a/crates/swc_ecma_parser/src/error.rs
+++ b/crates/swc_ecma_parser/src/error.rs
@@ -88,6 +88,7 @@ pub enum SyntaxError {
     InvalidIdentInStrict,
     /// 'eval' and 'arguments' are invalid identifier in strict mode.
     EvalAndArgumentsInStrict,
+    IllegalLanguageModeDirective,
     UnaryInExp {
         left: String,
         left_span: Span,
@@ -300,6 +301,9 @@ impl SyntaxError {
             SyntaxError::EvalAndArgumentsInStrict => "'eval' and 'arguments' cannot be used as a \
                                                       binding identifier in strict mode"
                 .into(),
+            SyntaxError::IllegalLanguageModeDirective => {
+                "Illegal 'use strict' directive in function with non-simple parameter list.".into()
+            }
             SyntaxError::UnaryInExp { .. } => "** cannot be applied to unary expression".into(),
             SyntaxError::Hash => "Unexpected token '#'".into(),
             SyntaxError::LineBreakInThrow => "LineBreak cannot follow 'throw'".into(),
diff --git a/crates/swc_ecma_parser/src/parser/class_and_fn.rs b/crates/swc_ecma_parser/src/parser/class_and_fn.rs
index f3fad4db89..03434c254c 100644
--- a/crates/swc_ecma_parser/src/parser/class_and_fn.rs
+++ b/crates/swc_ecma_parser/src/parser/class_and_fn.rs
@@ -1,5 +1,5 @@
 use super::{ident::MaybeOptionalIdentParser, *};
-use crate::{error::SyntaxError, lexer::TokenContext, Tokens};
+use crate::{error::SyntaxError, lexer::TokenContext, parser::stmt::IsDirective, Tokens};
 use either::Either;
 use swc_atoms::js_word;
 use swc_common::{Spanned, SyntaxContext};
@@ -667,7 +667,8 @@ impl<'a, I: Tokens> Parser<I> {
                     self.emit_err(type_ann.type_ann.span(), SyntaxError::TS1093);
                 }
 
-                let body: Option<_> = self.parse_fn_body(false, false)?;
+                let body: Option<_> =
+                    self.parse_fn_body(false, false, params.is_simple_parameter_list())?;
 
                 if self.syntax().typescript() && body.is_none() {
                     // Declare constructors cannot have assignment pattern in parameters
@@ -1110,7 +1111,8 @@ impl<'a, I: Tokens> Parser<I> {
                 None
             };
 
-            let body: Option<_> = p.parse_fn_body(is_async, is_generator)?;
+            let body: Option<_> =
+                p.parse_fn_body(is_async, is_generator, params.is_simple_parameter_list())?;
 
             if p.syntax().typescript() && body.is_none() {
                 // Declare functions cannot have assignment pattern in parameters
@@ -1149,7 +1151,12 @@ impl<'a, I: Tokens> Parser<I> {
         }
     }
 
-    pub(super) fn parse_fn_body<T>(&mut self, is_async: bool, is_generator: bool) -> PResult<T>
+    pub(super) fn parse_fn_body<T>(
+        &mut self,
+        is_async: bool,
+        is_generator: bool,
+        is_simple_parameter_list: bool,
+    ) -> PResult<T>
     where
         Self: FnBodyParser<T>,
     {
@@ -1173,7 +1180,9 @@ impl<'a, I: Tokens> Parser<I> {
             labels: vec![],
             ..Default::default()
         };
-        self.with_ctx(ctx).with_state(state).parse_fn_body_inner()
+        self.with_ctx(ctx)
+            .with_state(state)
+            .parse_fn_body_inner(is_simple_parameter_list)
     }
 }
 
@@ -1373,13 +1382,25 @@ impl OutputType for Decl {
 }
 
 pub(super) trait FnBodyParser<Body> {
-    fn parse_fn_body_inner(&mut self) -> PResult<Body>;
+    fn parse_fn_body_inner(&mut self, is_simple_parameter_list: bool) -> PResult<Body>;
+}
+fn has_use_strict(block: &BlockStmt) -> Option<Span> {
+    match block.stmts.iter().find(|stmt| stmt.is_use_strict()) {
+        Some(Stmt::Expr(ExprStmt { span, expr: _ })) => Some(*span),
+        _ => None,
+    }
 }
-
 impl<I: Tokens> FnBodyParser<BlockStmtOrExpr> for Parser<I> {
-    fn parse_fn_body_inner(&mut self) -> PResult<BlockStmtOrExpr> {
+    fn parse_fn_body_inner(&mut self, is_simple_parameter_list: bool) -> PResult<BlockStmtOrExpr> {
         if is!(self, '{') {
-            self.parse_block(false).map(BlockStmtOrExpr::BlockStmt)
+            self.parse_block(false).map(|block_stmt| {
+                if !self.input.syntax().typescript() && !is_simple_parameter_list {
+                    if let Some(span) = has_use_strict(&block_stmt) {
+                        self.emit_err(span, SyntaxError::IllegalLanguageModeDirective);
+                    }
+                }
+                BlockStmtOrExpr::BlockStmt(block_stmt)
+            })
         } else {
             self.parse_assignment_expr().map(BlockStmtOrExpr::Expr)
         }
@@ -1387,12 +1408,51 @@ impl<I: Tokens> FnBodyParser<BlockStmtOrExpr> for Parser<I> {
 }
 
 impl<I: Tokens> FnBodyParser<Option<BlockStmt>> for Parser<I> {
-    fn parse_fn_body_inner(&mut self) -> PResult<Option<BlockStmt>> {
+    fn parse_fn_body_inner(
+        &mut self,
+        is_simple_parameter_list: bool,
+    ) -> PResult<Option<BlockStmt>> {
         // allow omitting body and allow placing `{` on next line
         if self.input.syntax().typescript() && !is!(self, '{') && eat!(self, ';') {
             return Ok(None);
         }
-        self.include_in_expr(true).parse_block(true).map(Some)
+        let block = self.include_in_expr(true).parse_block(true);
+        block.map(|block_stmt| {
+            if !self.input.syntax().typescript() && !is_simple_parameter_list {
+                if let Some(span) = has_use_strict(&block_stmt) {
+                    self.emit_err(span, SyntaxError::IllegalLanguageModeDirective);
+                }
+            }
+            Some(block_stmt)
+        })
+    }
+}
+
+pub(super) trait IsSimpleParameterList {
+    fn is_simple_parameter_list(&self) -> bool;
+}
+impl IsSimpleParameterList for Vec<Param> {
+    fn is_simple_parameter_list(&self) -> bool {
+        self.iter().all(|param| matches!(param.pat, Pat::Ident(_)))
+    }
+}
+impl IsSimpleParameterList for Vec<Pat> {
+    fn is_simple_parameter_list(&self) -> bool {
+        self.iter().all(|pat| matches!(pat, Pat::Ident(_)))
+    }
+}
+impl IsSimpleParameterList for Vec<ParamOrTsParamProp> {
+    fn is_simple_parameter_list(&self) -> bool {
+        self.iter().all(|param| {
+            matches!(
+                param,
+                ParamOrTsParamProp::TsParamProp(..)
+                    | ParamOrTsParamProp::Param(Param {
+                        pat: Pat::Ident(_),
+                        ..
+                    })
+            )
+        })
     }
 }
 
diff --git a/crates/swc_ecma_parser/src/parser/expr.rs b/crates/swc_ecma_parser/src/parser/expr.rs
index 3a99338471..461d50fc45 100644
--- a/crates/swc_ecma_parser/src/parser/expr.rs
+++ b/crates/swc_ecma_parser/src/parser/expr.rs
@@ -1,5 +1,7 @@
 use super::{pat::PatType, util::ExprExt, *};
-use crate::{lexer::TokenContext, token::AssignOpToken};
+use crate::{
+    lexer::TokenContext, parser::class_and_fn::IsSimpleParameterList, token::AssignOpToken,
+};
 use either::Either;
 use swc_atoms::js_word;
 use swc_common::{ast_node, util::take::Take, Spanned};
@@ -394,7 +396,7 @@ impl<'a, I: Tokens> Parser<I> {
                 let arg = Pat::from(ident);
                 let params = vec![arg];
                 expect!(self, "=>");
-                let body = self.parse_fn_body(true, false)?;
+                let body = self.parse_fn_body(true, false, params.is_simple_parameter_list())?;
 
                 return Ok(Box::new(Expr::Arrow(ArrowExpr {
                     span: span!(self, start),
@@ -407,7 +409,7 @@ impl<'a, I: Tokens> Parser<I> {
                 })));
             } else if can_be_arrow && !self.input.had_line_break_before_cur() && eat!(self, "=>") {
                 let params = vec![id.into()];
-                let body = self.parse_fn_body(false, false)?;
+                let body = self.parse_fn_body(false, false, params.is_simple_parameter_list())?;
 
                 return Ok(Box::new(Expr::Arrow(ArrowExpr {
                     span: span!(self, start),
@@ -681,12 +683,16 @@ impl<'a, I: Tokens> Parser<I> {
 
                 expect!(p, "=>");
 
-                let params = p
+                let params: Vec<Pat> = p
                     .parse_paren_items_as_params(items_ref.clone())?
                     .into_iter()
                     .collect();
 
-                let body: BlockStmtOrExpr = p.parse_fn_body(async_span.is_some(), false)?;
+                let body: BlockStmtOrExpr = p.parse_fn_body(
+                    async_span.is_some(),
+                    false,
+                    params.is_simple_parameter_list(),
+                )?;
 
                 if is_direct_child_of_cond {
                     if !is_one_of!(p, ':', ';') {
@@ -733,12 +739,16 @@ impl<'a, I: Tokens> Parser<I> {
             }
             expect!(self, "=>");
 
-            let params = self
+            let params: Vec<Pat> = self
                 .parse_paren_items_as_params(paren_items)?
                 .into_iter()
                 .collect();
 
-            let body: BlockStmtOrExpr = self.parse_fn_body(async_span.is_some(), false)?;
+            let body: BlockStmtOrExpr = self.parse_fn_body(
+                async_span.is_some(),
+                false,
+                params.is_simple_parameter_list(),
+            )?;
             let arrow_expr = ArrowExpr {
                 span: span!(self, expr_start),
                 is_async: async_span.is_some(),
@@ -1633,12 +1643,13 @@ impl<'a, I: Tokens> Parser<I> {
                     _ => false,
                 }
             } {
-                let params = self
+                let params: Vec<Pat> = self
                     .parse_paren_items_as_params(items.clone())?
                     .into_iter()
                     .collect();
 
-                let body: BlockStmtOrExpr = self.parse_fn_body(false, false)?;
+                let body: BlockStmtOrExpr =
+                    self.parse_fn_body(false, false, params.is_simple_parameter_list())?;
                 let span = span!(self, start);
 
                 items.push(PatOrExprOrSpread::ExprOrSpread(ExprOrSpread {
diff --git a/crates/swc_ecma_parser/src/parser/typescript.rs b/crates/swc_ecma_parser/src/parser/typescript.rs
index 77c6688236..1bd89a69f8 100644
--- a/crates/swc_ecma_parser/src/parser/typescript.rs
+++ b/crates/swc_ecma_parser/src/parser/typescript.rs
@@ -1,5 +1,5 @@
 use super::*;
-use crate::lexer::TokenContexts;
+use crate::{lexer::TokenContexts, parser::class_and_fn::IsSimpleParameterList};
 use either::Either;
 use swc_atoms::js_word;
 use swc_common::{Spanned, SyntaxContext};
@@ -2474,7 +2474,7 @@ impl<I: Tokens> Parser<I> {
                 let type_params = p.parse_ts_type_params()?;
                 // Don't use overloaded parseFunctionParams which would look for "<" again.
                 expect!(p, '(');
-                let params = p
+                let params: Vec<Pat> = p
                     .parse_formal_params()?
                     .into_iter()
                     .map(|p| p.pat)
@@ -2502,7 +2502,7 @@ impl<I: Tokens> Parser<I> {
         self.with_ctx(ctx).parse_with(|p| {
             let is_generator = false;
             let is_async = true;
-            let body = p.parse_fn_body(true, false)?;
+            let body = p.parse_fn_body(true, false, params.is_simple_parameter_list())?;
             Ok(Some(ArrowExpr {
                 span: span!(p, start),
                 body,


Test patch:
diff --git a/crates/swc_ecma_parser/src/parser/tests.rs b/crates/swc_ecma_parser/src/parser/tests.rs
index b27151428f..e13b9b708c 100644
--- a/crates/swc_ecma_parser/src/parser/tests.rs
+++ b/crates/swc_ecma_parser/src/parser/tests.rs
@@ -301,3 +301,58 @@ fn issue_2853_2() {
         Ok(program)
     });
 }
+
+#[test]
+fn illegal_language_mode_directive1() {
+    test_parser(
+        r#"function f(a = 0) { "use strict"; }"#,
+        Default::default(),
+        |p| {
+            let program = p.parse_program()?;
+
+            let errors = p.take_errors();
+            assert_eq!(
+                errors,
+                vec![Error {
+                    error: Box::new((
+                        Span {
+                            lo: BytePos(20),
+                            hi: BytePos(33),
+                            ctxt: swc_common::SyntaxContext::empty()
+                        },
+                        crate::parser::SyntaxError::IllegalLanguageModeDirective
+                    ))
+                }]
+            );
+
+            Ok(program)
+        },
+    );
+}
+#[test]
+fn illegal_language_mode_directive2() {
+    test_parser(
+        r#"let f = (a = 0) => { "use strict"; }"#,
+        Default::default(),
+        |p| {
+            let program = p.parse_program()?;
+
+            let errors = p.take_errors();
+            assert_eq!(
+                errors,
+                vec![Error {
+                    error: Box::new((
+                        Span {
+                            lo: BytePos(21),
+                            hi: BytePos(34),
+                            ctxt: swc_common::SyntaxContext::empty()
+                        },
+                        crate::parser::SyntaxError::IllegalLanguageModeDirective
+                    ))
+                }]
+            );
+
+            Ok(program)
+        },
+    );
+}
diff --git a/crates/swc_ecma_parser/tests/span/js/issue-3236/arrow_function.js b/crates/swc_ecma_parser/tests/span/js/issue-3236/arrow_function.js
new file mode 100644
index 0000000000..ab95598da6
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/span/js/issue-3236/arrow_function.js
@@ -0,0 +1,3 @@
+let f = (a = 0) => {
+    "use strict";
+}
\ No newline at end of file
diff --git a/crates/swc_ecma_parser/tests/span/js/issue-3236/arrow_function.js.spans b/crates/swc_ecma_parser/tests/span/js/issue-3236/arrow_function.js.spans
new file mode 100644
index 0000000000..3daff15aa2
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/span/js/issue-3236/arrow_function.js.spans
@@ -0,0 +1,169 @@
+warning: Module
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:1
+  |
+1 | / let f = (a = 0) => {
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: ModuleItem
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:1
+  |
+1 | / let f = (a = 0) => {
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: Stmt
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:1
+  |
+1 | / let f = (a = 0) => {
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: Decl
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:1
+  |
+1 | / let f = (a = 0) => {
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: VarDecl
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:1
+  |
+1 | / let f = (a = 0) => {
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: VarDeclarator
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:5
+  |
+1 |   let f = (a = 0) => {
+  |  _____^
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: Pat
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:5
+  |
+1 | let f = (a = 0) => {
+  |     ^
+
+warning: Ident
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:5
+  |
+1 | let f = (a = 0) => {
+  |     ^
+
+warning: Expr
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:9
+  |
+1 |   let f = (a = 0) => {
+  |  _________^
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: ArrowExpr
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:9
+  |
+1 |   let f = (a = 0) => {
+  |  _________^
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: Pat
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:10
+  |
+1 | let f = (a = 0) => {
+  |          ^^^^^
+
+warning: AssignPat
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:10
+  |
+1 | let f = (a = 0) => {
+  |          ^^^^^
+
+warning: Pat
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:10
+  |
+1 | let f = (a = 0) => {
+  |          ^
+
+warning: Ident
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:10
+  |
+1 | let f = (a = 0) => {
+  |          ^
+
+warning: Expr
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:14
+  |
+1 | let f = (a = 0) => {
+  |              ^
+
+warning: Lit
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:14
+  |
+1 | let f = (a = 0) => {
+  |              ^
+
+warning: Number
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:14
+  |
+1 | let f = (a = 0) => {
+  |              ^
+
+warning: BlockStmtOrExpr
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:20
+  |
+1 |   let f = (a = 0) => {
+  |  ____________________^
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: BlockStmt
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:1:20
+  |
+1 |   let f = (a = 0) => {
+  |  ____________________^
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: Stmt
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:2:5
+  |
+2 |     "use strict";
+  |     ^^^^^^^^^^^^^
+
+warning: ExprStmt
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:2:5
+  |
+2 |     "use strict";
+  |     ^^^^^^^^^^^^^
+
+warning: Expr
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:2:5
+  |
+2 |     "use strict";
+  |     ^^^^^^^^^^^^
+
+warning: Lit
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:2:5
+  |
+2 |     "use strict";
+  |     ^^^^^^^^^^^^
+
+warning: Str
+ --> $DIR/tests/span/js/issue-3236/arrow_function.js:2:5
+  |
+2 |     "use strict";
+  |     ^^^^^^^^^^^^
+
diff --git a/crates/swc_ecma_parser/tests/span/js/issue-3236/function.js b/crates/swc_ecma_parser/tests/span/js/issue-3236/function.js
new file mode 100644
index 0000000000..2cdd2686e4
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/span/js/issue-3236/function.js
@@ -0,0 +1,3 @@
+function f(a = 0) {
+    "use strict";
+}
\ No newline at end of file
diff --git a/crates/swc_ecma_parser/tests/span/js/issue-3236/function.js.spans b/crates/swc_ecma_parser/tests/span/js/issue-3236/function.js.spans
new file mode 100644
index 0000000000..a8d9d59fc4
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/span/js/issue-3236/function.js.spans
@@ -0,0 +1,141 @@
+warning: Module
+ --> $DIR/tests/span/js/issue-3236/function.js:1:1
+  |
+1 | / function f(a = 0) {
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: ModuleItem
+ --> $DIR/tests/span/js/issue-3236/function.js:1:1
+  |
+1 | / function f(a = 0) {
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: Stmt
+ --> $DIR/tests/span/js/issue-3236/function.js:1:1
+  |
+1 | / function f(a = 0) {
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: Decl
+ --> $DIR/tests/span/js/issue-3236/function.js:1:1
+  |
+1 | / function f(a = 0) {
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: FnDecl
+ --> $DIR/tests/span/js/issue-3236/function.js:1:1
+  |
+1 | / function f(a = 0) {
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: Ident
+ --> $DIR/tests/span/js/issue-3236/function.js:1:10
+  |
+1 | function f(a = 0) {
+  |          ^
+
+warning: Function
+ --> $DIR/tests/span/js/issue-3236/function.js:1:1
+  |
+1 | / function f(a = 0) {
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: Param
+ --> $DIR/tests/span/js/issue-3236/function.js:1:12
+  |
+1 | function f(a = 0) {
+  |            ^^^^^
+
+warning: Pat
+ --> $DIR/tests/span/js/issue-3236/function.js:1:12
+  |
+1 | function f(a = 0) {
+  |            ^^^^^
+
+warning: AssignPat
+ --> $DIR/tests/span/js/issue-3236/function.js:1:12
+  |
+1 | function f(a = 0) {
+  |            ^^^^^
+
+warning: Pat
+ --> $DIR/tests/span/js/issue-3236/function.js:1:12
+  |
+1 | function f(a = 0) {
+  |            ^
+
+warning: Ident
+ --> $DIR/tests/span/js/issue-3236/function.js:1:12
+  |
+1 | function f(a = 0) {
+  |            ^
+
+warning: Expr
+ --> $DIR/tests/span/js/issue-3236/function.js:1:16
+  |
+1 | function f(a = 0) {
+  |                ^
+
+warning: Lit
+ --> $DIR/tests/span/js/issue-3236/function.js:1:16
+  |
+1 | function f(a = 0) {
+  |                ^
+
+warning: Number
+ --> $DIR/tests/span/js/issue-3236/function.js:1:16
+  |
+1 | function f(a = 0) {
+  |                ^
+
+warning: BlockStmt
+ --> $DIR/tests/span/js/issue-3236/function.js:1:19
+  |
+1 |   function f(a = 0) {
+  |  ___________________^
+2 | |     "use strict";
+3 | | }
+  | |_^
+
+warning: Stmt
+ --> $DIR/tests/span/js/issue-3236/function.js:2:5
+  |
+2 |     "use strict";
+  |     ^^^^^^^^^^^^^
+
+warning: ExprStmt
+ --> $DIR/tests/span/js/issue-3236/function.js:2:5
+  |
+2 |     "use strict";
+  |     ^^^^^^^^^^^^^
+
+warning: Expr
+ --> $DIR/tests/span/js/issue-3236/function.js:2:5
+  |
+2 |     "use strict";
+  |     ^^^^^^^^^^^^
+
+warning: Lit
+ --> $DIR/tests/span/js/issue-3236/function.js:2:5
+  |
+2 |     "use strict";
+  |     ^^^^^^^^^^^^
+
+warning: Str
+ --> $DIR/tests/span/js/issue-3236/function.js:2:5
+  |
+2 |     "use strict";
+  |     ^^^^^^^^^^^^
+
diff --git a/crates/swc_ecma_parser/tests/typescript/issue-3236/input.ts b/crates/swc_ecma_parser/tests/typescript/issue-3236/input.ts
new file mode 100644
index 0000000000..2cdd2686e4
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/issue-3236/input.ts
@@ -0,0 +1,3 @@
+function f(a = 0) {
+    "use strict";
+}
\ No newline at end of file
diff --git a/crates/swc_ecma_parser/tests/typescript/issue-3236/input.ts.json b/crates/swc_ecma_parser/tests/typescript/issue-3236/input.ts.json
new file mode 100644
index 0000000000..66d5e49a13
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/issue-3236/input.ts.json
@@ -0,0 +1,107 @@
+{
+  "type": "Script",
+  "span": {
+    "start": 0,
+    "end": 39,
+    "ctxt": 0
+  },
+  "body": [
+    {
+      "type": "FunctionDeclaration",
+      "identifier": {
+        "type": "Identifier",
+        "span": {
+          "start": 9,
+          "end": 10,
+          "ctxt": 0
+        },
+        "value": "f",
+        "optional": false
+      },
+      "declare": false,
+      "params": [
+        {
+          "type": "Parameter",
+          "span": {
+            "start": 11,
+            "end": 16,
+            "ctxt": 0
+          },
+          "decorators": [],
+          "pat": {
+            "type": "AssignmentPattern",
+            "span": {
+              "start": 11,
+              "end": 16,
+              "ctxt": 0
+            },
+            "left": {
+              "type": "Identifier",
+              "span": {
+                "start": 11,
+                "end": 12,
+                "ctxt": 0
+              },
+              "value": "a",
+              "optional": false,
+              "typeAnnotation": null
+            },
+            "right": {
+              "type": "NumericLiteral",
+              "span": {
+                "start": 15,
+                "end": 16,
+                "ctxt": 0
+              },
+              "value": 0.0
+            },
+            "typeAnnotation": null
+          }
+        }
+      ],
+      "decorators": [],
+      "span": {
+        "start": 0,
+        "end": 39,
+        "ctxt": 0
+      },
+      "body": {
+        "type": "BlockStatement",
+        "span": {
+          "start": 18,
+          "end": 39,
+          "ctxt": 0
+        },
+        "stmts": [
+          {
+            "type": "ExpressionStatement",
+            "span": {
+              "start": 24,
+              "end": 37,
+              "ctxt": 0
+            },
+            "expression": {
+              "type": "StringLiteral",
+              "span": {
+                "start": 24,
+                "end": 36,
+                "ctxt": 0
+              },
+              "value": "use strict",
+              "hasEscape": false,
+              "kind": {
+                "type": "normal",
+                "containsQuote": true
+              }
+            }
+          }
+        ]
+      },
+      "generator": false,
+      "async": false,
+      "typeParameters": null,
+      "returnType": null
+    }
+  ],
+  "interpreter": null
+}