nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: DmitrySoshnikov/regexp-tree
Pull request #98 (2017-11-12 11:15:41)

Add support for {0,} -> * in optimizer's quantifier-range-to-symbol transform
This adds support for `/a{0,}/` -> `/a*/` in Optimizer.


Patch:
diff --git a/src/optimizer/transforms/quantifier-range-to-symbol-transform.js b/src/optimizer/transforms/quantifier-range-to-symbol-transform.js
index 1da04fc..42503f5 100644
--- a/src/optimizer/transforms/quantifier-range-to-symbol-transform.js
+++ b/src/optimizer/transforms/quantifier-range-to-symbol-transform.js
@@ -9,6 +9,7 @@
  * A regexp-tree plugin to replace different range-based quantifiers
  * with their symbol equivalents.
  *
+ * a{0,} -> a*
  * a{1,} -> a+
  * a{1} -> a
  *
@@ -24,6 +25,9 @@ module.exports = {
       return;
     }
 
+    // a{0,} -> a*
+    rewriteOpenZero(path);
+
     // a{1,} -> a+
     rewriteOpenOne(path);
 
@@ -32,6 +36,17 @@ module.exports = {
   }
 };
 
+function rewriteOpenZero(path) {
+  const {node} = path;
+
+  if (node.from !== 0 || node.to) {
+    return;
+  }
+
+  node.kind = '*';
+  delete node.from;
+}
+
 function rewriteOpenOne(path) {
   const {node} = path;
 


Test patch:
diff --git a/src/optimizer/transforms/__tests__/quantifier-range-to-symbol-transform-test.js b/src/optimizer/transforms/__tests__/quantifier-range-to-symbol-transform-test.js
index 61a9314..5cc0428 100644
--- a/src/optimizer/transforms/__tests__/quantifier-range-to-symbol-transform-test.js
+++ b/src/optimizer/transforms/__tests__/quantifier-range-to-symbol-transform-test.js
@@ -10,6 +10,13 @@ const quantifierRangeToSymbol = require('../quantifier-range-to-symbol-transform
 
 describe('quantifier range to symbol', () => {
 
+  it('a{0,} -> a*', () => {
+    const re = transform(/[a-z]{0,}/, [
+      quantifierRangeToSymbol,
+    ]);
+    expect(re.toString()).toBe('/[a-z]*/');
+  });
+
   it('a{1,} -> a+', () => {
     const re = transform(/[a-z]{1,}/, [
       quantifierRangeToSymbol,
Repository: DmitrySoshnikov/regexp-tree
Pull request #101 (2017-11-12 13:55:58)

Optimizer should run until it finds the best optimization
Currently, optimizing `/a|a/` gives `/[a]/` and optimizing again `/[a]/` gives `/a/`.
We should get the most optimized result directly.

This PR lets the optimizer run until it finds the best optimization. The drawback is that it has to run minimum twice to know...


Patch:
diff --git a/src/optimizer/index.js b/src/optimizer/index.js
index 8052655..249e80f 100644
--- a/src/optimizer/index.js
+++ b/src/optimizer/index.js
@@ -26,11 +26,19 @@ module.exports = {
    *   /\w+e+/
    */
   optimize(regexp) {
+    let prevResult;
     let result;
-    optimizationTransforms.forEach(transformer => {
-      result = transform.transform(regexp, transformer);
-      regexp = result.getAST();
-    });
+    do {
+      if (result) {
+        prevResult = result.toRegExp().toString();
+        regexp = result.toRegExp();
+      }
+      optimizationTransforms.forEach(transformer => {
+        result = transform.transform(regexp, transformer);
+        regexp = result.getAST();
+      });
+    } while (result.toRegExp().toString() !== prevResult);
+
     return result;
   },
 };
\ No newline at end of file


Test patch:
diff --git a/src/optimizer/__tests__/optimizer-integration-test.js b/src/optimizer/__tests__/optimizer-integration-test.js
index 57fc2cf..7429c66 100644
--- a/src/optimizer/__tests__/optimizer-integration-test.js
+++ b/src/optimizer/__tests__/optimizer-integration-test.js
@@ -73,4 +73,12 @@ describe('optimizer-integration-test', () => {
       .toBe(optimized.toString());
   });
 
+  it('finds the best optimization', () => {
+    const original = /a|a/;
+    const optimized = /a/;
+
+    expect(optimizer.optimize(original).toString())
+      .toBe(optimized.toString());
+  });
+
 });
\ No newline at end of file
Repository: DmitrySoshnikov/regexp-tree
Pull request #104 (2017-11-14 18:36:43)

Optimizer: remove non-capturing empty groups
This PR provides a transform to remove empty groups in optimizer:

`/a(?:)b/` -> `/ab/`
`/a|(?:)|b/` -> `/a||b/`

It does not remove top-level empty group if alone (a.k.a the "empty regexp"):

`/(?:)/` is left unchanged.
`/(?:)+/` -> `/(?:)/`


Patch:
diff --git a/src/optimizer/transforms/index.js b/src/optimizer/transforms/index.js
index 2027ccd..e04f411 100644
--- a/src/optimizer/transforms/index.js
+++ b/src/optimizer/transforms/index.js
@@ -30,6 +30,9 @@ module.exports = [
   // (a|b|c) -> [abc]
   require('./group-single-chars-to-char-class'),
 
+  // (?:)a -> a
+  require('./remove-empty-group-transform'),
+
   // (?:a) -> a
   require('./ungroup-transform')
 ];
\ No newline at end of file
diff --git a/src/optimizer/transforms/remove-empty-group-transform.js b/src/optimizer/transforms/remove-empty-group-transform.js
new file mode 100644
index 0000000..bd8ae34
--- /dev/null
+++ b/src/optimizer/transforms/remove-empty-group-transform.js
@@ -0,0 +1,34 @@
+/**
+ * The MIT License (MIT)
+ * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
+ */
+
+'use strict';
+
+/**
+ * A regexp-tree plugin to remove non-capturing empty groups.
+ *
+ * /(?:)a/ -> /a/
+ * /a|(?:)/ -> /a|/
+ */
+
+module.exports = {
+  Group(path) {
+    const {node, parent} = path;
+    const childPath = path.getChild();
+
+    if (node.capturing || childPath) {
+      return;
+    }
+
+    if (parent.type === 'Repetition') {
+
+      path.getParent().replace(node);
+
+    } else if (parent.type !== 'RegExp') {
+
+      path.remove();
+
+    }
+  }
+};


Test patch:
diff --git a/src/optimizer/transforms/__tests__/remove-empty-group-transform-test.js b/src/optimizer/transforms/__tests__/remove-empty-group-transform-test.js
new file mode 100644
index 0000000..dfcbe8f
--- /dev/null
+++ b/src/optimizer/transforms/__tests__/remove-empty-group-transform-test.js
@@ -0,0 +1,39 @@
+/**
+ * The MIT License (MIT)
+ * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
+ */
+
+'use strict';
+
+const {transform} = require('../../../transform');
+const removeEmptyGroup = require('../remove-empty-group-transform');
+
+describe('remove empty groups', () => {
+
+  it('removes empty groups', () => {
+    const re = transform(/a(?:)b/, [
+      removeEmptyGroup
+    ]);
+    expect(re.toString()).toBe('/ab/');
+
+    const re2 = transform(/((?:))/, [
+      removeEmptyGroup
+    ]);
+    expect(re2.toString()).toBe('/()/');
+  });
+
+  it('does not remove empty regexp', () => {
+    const re = transform(/(?:)/, [
+      removeEmptyGroup
+    ]);
+    expect(re.toString()).toBe('/(?:)/');
+  });
+
+  it('removes empty group quantifier', () => {
+    const re = transform(/(?:)+/, [
+      removeEmptyGroup
+    ]);
+    expect(re.toString()).toBe('/(?:)/');
+  });
+
+});
\ No newline at end of file
Repository: DmitrySoshnikov/regexp-tree
Pull request #123 (2017-11-23 07:50:36)

Optimizer: Simplifies class ranges spanning only one or two chars
The transform is this PR handles the following optimizations:

`/[a-a$]/` -> `/[a$]/`
`/[a-b]/` -> `/[ab]/`


Patch:
diff --git a/src/optimizer/transforms/char-class-classranges-to-chars-transform.js b/src/optimizer/transforms/char-class-classranges-to-chars-transform.js
new file mode 100644
index 0000000..fae4abe
--- /dev/null
+++ b/src/optimizer/transforms/char-class-classranges-to-chars-transform.js
@@ -0,0 +1,30 @@
+/**
+ * The MIT License (MIT)
+ * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
+ */
+
+'use strict';
+
+/**
+ * A regexp-tree plugin to simplify character classes
+ * spanning only one or two chars.
+ *
+ * [a-a] -> [a]
+ * [a-b] -> [ab]
+ */
+module.exports = {
+  ClassRange(path) {
+    const {node} = path;
+
+    if (node.from.codePoint === node.to.codePoint) {
+
+      path.replace(node.from);
+
+    } else if (node.from.codePoint === node.to.codePoint - 1) {
+
+      path.getParent().insertChildAt(node.to, path.index + 1);
+      path.replace(node.from);
+
+    }
+  }
+};
diff --git a/src/optimizer/transforms/index.js b/src/optimizer/transforms/index.js
index 790c377..56082bf 100644
--- a/src/optimizer/transforms/index.js
+++ b/src/optimizer/transforms/index.js
@@ -15,6 +15,9 @@ module.exports = {
   // a{1,} -> a+, a{3,3} -> a{3}, a{1} -> a
   'quantifierRangeToSymbol': require('./quantifier-range-to-symbol-transform'),
 
+  // [a-a] -> [a], [a-b] -> [ab]
+  'charClassClassrangesToChars': require('./char-class-classranges-to-chars-transform'),
+
   // [a-de-f] -> [a-f]
   'charClassClassrangesMerge': require('./char-class-classranges-merge-transform'),
 


Test patch:
diff --git a/src/optimizer/transforms/__tests__/char-class-classranges-to-chars-transform-test.js b/src/optimizer/transforms/__tests__/char-class-classranges-to-chars-transform-test.js
new file mode 100644
index 0000000..b3c34e4
--- /dev/null
+++ b/src/optimizer/transforms/__tests__/char-class-classranges-to-chars-transform-test.js
@@ -0,0 +1,27 @@
+/**
+ * The MIT License (MIT)
+ * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
+ */
+
+'use strict';
+
+const {transform} = require('../../../transform');
+const charClassClassrangesToChars = require('../char-class-classranges-to-chars-transform');
+
+describe('char-class-classranges-to-chars', () => {
+
+  it('[a-a] -> [a]', () => {
+    const re = transform('/[a-a]/', [
+      charClassClassrangesToChars,
+    ]);
+    expect(re.toString()).toBe('/[a]/');
+  });
+
+  it('[a-b] -> [ab]', () => {
+    const re = transform('/[a-b]/', [
+      charClassClassrangesToChars,
+    ]);
+    expect(re.toString()).toBe('/[ab]/');
+  });
+
+});
\ No newline at end of file
Repository: DmitrySoshnikov/regexp-tree
Pull request #124 (2017-11-24 18:33:38)

Optimizer: When u flag is set, convert surrogate pairs to single unicode code point
`/\ud83d\ude80/u` -> `/\u{1f680}/u`


Patch:
diff --git a/src/optimizer/transforms/char-surrogate-pair-to-single-unicode-transform.js b/src/optimizer/transforms/char-surrogate-pair-to-single-unicode-transform.js
new file mode 100644
index 0000000..1165123
--- /dev/null
+++ b/src/optimizer/transforms/char-surrogate-pair-to-single-unicode-transform.js
@@ -0,0 +1,25 @@
+/**
+ * The MIT License (MIT)
+ * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
+ */
+
+'use strict';
+
+/**
+ * A regexp-tree plugin to transform surrogate pairs into single unicode code point
+ *
+ * \ud83d\ude80 -> \u{1f680}
+ */
+module.exports = {
+  shouldRun(ast) {
+    return ast.flags.includes('u');
+  },
+  Char(path) {
+    const {node} = path;
+    if (node.kind !== 'unicode' || !node.isSurrogatePair || isNaN(node.codePoint)) {
+      return;
+    }
+    node.value = `\\u{${node.codePoint.toString(16)}}`;
+    delete node.isSurrogatePair;
+  }
+};
\ No newline at end of file
diff --git a/src/optimizer/transforms/index.js b/src/optimizer/transforms/index.js
index 56082bf..9704a2a 100644
--- a/src/optimizer/transforms/index.js
+++ b/src/optimizer/transforms/index.js
@@ -6,6 +6,9 @@
 'use strict';
 
 module.exports = {
+  // \ud83d\ude80 -> \u{1f680}
+  'charSurrogatePairToSingleUnicode': require('./char-surrogate-pair-to-single-unicode-transform'),
+
   // [\d\d] -> [\d]
   'charClassRemoveDuplicates': require('./char-class-remove-duplicates-transform'),
 


Test patch:
diff --git a/src/optimizer/transforms/__tests__/char-surrogate-pair-to-single-unicode-transform-test.js b/src/optimizer/transforms/__tests__/char-surrogate-pair-to-single-unicode-transform-test.js
new file mode 100644
index 0000000..a1890fb
--- /dev/null
+++ b/src/optimizer/transforms/__tests__/char-surrogate-pair-to-single-unicode-transform-test.js
@@ -0,0 +1,27 @@
+/**
+ * The MIT License (MIT)
+ * Copyright (c) 2017-present Dmitry Soshnikov <dmitry.soshnikov@gmail.com>
+ */
+
+'use strict';
+
+const {transform} = require('../../../transform');
+const charSurrogatePairToSingleUnicode = require('../char-surrogate-pair-to-single-unicode-transform');
+
+describe('\\ud83d\\ude80 -> \\u{1f680}', () => {
+
+  it('\\ud83d\\ude80 -> \\u{1f680}', () => {
+    const re = transform(/\ud83d\ude80/u, [
+      charSurrogatePairToSingleUnicode
+    ]);
+    expect(re.toString()).toBe('/\\u{1f680}/u');
+  });
+
+  it('does not run when unicode flag is absent', () => {
+    const re = transform(/\ud83d\ude80/, [
+      charSurrogatePairToSingleUnicode
+    ]);
+    expect(re.toString()).toBe('/\\ud83d\\ude80/');
+  });
+
+});
\ No newline at end of file
Repository: zeromicro/go-zero
Pull request #3662 (2023-10-23 12:38:02)

chore: update restful/grpc servers shutdown stages
update restful/grpc servers shutdown from wrap up to to shutdown stage.


Patch:
diff --git a/rest/internal/starter.go b/rest/internal/starter.go
index 08cca832..17430334 100644
--- a/rest/internal/starter.go
+++ b/rest/internal/starter.go
@@ -43,7 +43,7 @@ func start(host string, port int, handler http.Handler, run func(svr *http.Serve
 	}
 	healthManager := health.NewHealthManager(fmt.Sprintf("%s-%s:%d", probeNamePrefix, host, port))
 
-	waitForCalled := proc.AddWrapUpListener(func() {
+	waitForCalled := proc.AddShutdownListener(func() {
 		healthManager.MarkNotReady()
 		if e := server.Shutdown(context.Background()); e != nil {
 			logx.Error(e)
diff --git a/zrpc/internal/rpcserver.go b/zrpc/internal/rpcserver.go
index f1b90e42..ed4aa235 100644
--- a/zrpc/internal/rpcserver.go
+++ b/zrpc/internal/rpcserver.go
@@ -76,7 +76,7 @@ func (s *rpcServer) Start(register RegisterFn) error {
 
 	// we need to make sure all others are wrapped up,
 	// so we do graceful stop at shutdown phase instead of wrap up phase
-	waitForCalled := proc.AddWrapUpListener(func() {
+	waitForCalled := proc.AddShutdownListener(func() {
 		if s.health != nil {
 			s.health.Shutdown()
 		}


Test patch:
diff --git a/zrpc/internal/rpcserver_test.go b/zrpc/internal/rpcserver_test.go
index 99b48eb4..7af40110 100644
--- a/zrpc/internal/rpcserver_test.go
+++ b/zrpc/internal/rpcserver_test.go
@@ -47,7 +47,7 @@ func TestRpcServer(t *testing.T) {
 	grpcServer.GracefulStop()
 	lock.Unlock()
 
-	proc.WrapUp()
+	proc.Shutdown()
 	wgDone.Wait()
 }
Repository: zeromicro/go-zero
Pull request #3676 (2023-10-28 06:15:54)

fix: adjust log encode output mode

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/zeromicro/go-zero/pull/3676?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro) Report
> Merging [#3676](https://app.codecov.io/gh/zeromicro/go-zero/pull/3676?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro) (b2b998d) into [master](https://app.codecov.io/gh/zeromicro/go-zero/commit/b07df1c3447c495fdcbd42e3579ba14e0c595472?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro) (b07df1c) will **decrease** coverage by `0.05%`.
> The diff coverage is `86.11%`.


| [Files](https://app.codecov.io/gh/zeromicro/go-zero/pull/3676?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro) | Coverage Δ | |
|---|---|---|
| [core/logx/writer.go](https://app.codecov.io/gh/zeromicro/go-zero/pull/3676?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro#diff-Y29yZS9sb2d4L3dyaXRlci5nbw==) | `93.04% <86.11%> (-2.48%)` | :arrow_down: |


Patch:
diff --git a/core/logx/writer.go b/core/logx/writer.go
index 1d60a5bd..ac19aa24 100644
--- a/core/logx/writer.go
+++ b/core/logx/writer.go
@@ -7,7 +7,6 @@ import (
 	"io"
 	"log"
 	"path"
-	"reflect"
 	"runtime/debug"
 	"sync"
 	"sync/atomic"
@@ -334,11 +333,13 @@ func wrapLevelWithColor(level string) string {
 
 func writeJson(writer io.Writer, info any) {
 	if content, err := json.Marshal(info); err != nil {
-		log.Printf("err: %s, type: %s\n\n%s\n", err.Error(), reflect.TypeOf(info).Name(), debug.Stack())
+		log.Printf("err: %s\n\n%s", err.Error(), debug.Stack())
 	} else if writer == nil {
 		log.Println(string(content))
 	} else {
-		writer.Write(append(content, '\n'))
+		if _, err := writer.Write(append(content, '\n')); err != nil {
+			log.Println(err.Error())
+		}
 	}
 }
 
@@ -386,7 +387,7 @@ func writePlainValue(writer io.Writer, level string, val any, fields ...string)
 	buf.WriteString(level)
 	buf.WriteByte(plainEncodingSep)
 	if err := json.NewEncoder(&buf).Encode(val); err != nil {
-		log.Printf("err: %s, type: %s\n\n%s\n", err.Error(), reflect.TypeOf(val).Name(), debug.Stack())
+		log.Printf("err: %s\n\n%s", err.Error(), debug.Stack())
 		return
 	}
 


Test patch:
diff --git a/core/logx/writer_test.go b/core/logx/writer_test.go
index c2d55aec..0f810975 100644
--- a/core/logx/writer_test.go
+++ b/core/logx/writer_test.go
@@ -126,6 +126,11 @@ func TestWriteJson(t *testing.T) {
 	log.SetOutput(&buf)
 	writeJson(nil, "foo")
 	assert.Contains(t, buf.String(), "foo")
+
+	buf.Reset()
+	writeJson(hardToWriteWriter{}, "foo")
+	assert.Contains(t, buf.String(), "write error")
+
 	buf.Reset()
 	writeJson(nil, make(chan int))
 	assert.Contains(t, buf.String(), "unsupported type")
Repository: zeromicro/go-zero
Pull request #3839 (2024-01-12 08:38:15)

fix(mapping): call fillSliceValue  panic if the value is nil
[issue3838](https://github.com/zeromicro/go-zero/issues/3838)

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/zeromicro/go-zero/pull/3839?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`7ba8adf`)](https://app.codecov.io/gh/zeromicro/go-zero/commit/7ba8adfc74a19ea62faccf0674c58062dc41bd22?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro) 94.67% compared to head [(`08545a0`)](https://app.codecov.io/gh/zeromicro/go-zero/pull/3839?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro) 94.67%.

<details><summary>Additional details and impacted files</summary>



| [Files](https://app.codecov.io/gh/zeromicro/go-zero/pull/3839?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro) | Coverage Δ | |
|---|---|---|
| [core/mapping/unmarshaler.go](https://app.codecov.io/gh/zeromicro/go-zero/pull/3839?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro#diff-Y29yZS9tYXBwaW5nL3VubWFyc2hhbGVyLmdv) | `96.45% <100.00%> (+0.01%)` | :arrow_up: |

</details>

Patch:
diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go
index 7df118e9..b06385b4 100644
--- a/core/mapping/unmarshaler.go
+++ b/core/mapping/unmarshaler.go
@@ -249,6 +249,9 @@ func (u *Unmarshaler) fillSliceFromString(fieldType reflect.Type, value reflect.
 
 func (u *Unmarshaler) fillSliceValue(slice reflect.Value, index int,
 	baseKind reflect.Kind, value any, fullName string) error {
+	if value == nil {
+		return errUnsupportedType
+	}
 	ithVal := slice.Index(index)
 	switch v := value.(type) {
 	case fmt.Stringer:


Test patch:
diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go
index e0bd07d3..18104537 100644
--- a/core/mapping/unmarshaler_test.go
+++ b/core/mapping/unmarshaler_test.go
@@ -1329,6 +1329,20 @@ func TestUnmarshalInt64Slice(t *testing.T) {
 	}
 }
 
+func TestUnmarshalNullableSlice(t *testing.T) {
+	var v struct {
+		Ages  []int64 `key:"ages"`
+		Slice []int8  `key:"slice"`
+	}
+	m := map[string]any{
+		"ages":  []int64{1, 2},
+		"slice": `[null,2]`,
+	}
+
+	ast := assert.New(t)
+	ast.Equal(UnmarshalKey(m, &v), errUnsupportedType)
+}
+
 func TestUnmarshalIntSlice(t *testing.T) {
 	var v struct {
 		Ages  []int `key:"ages"`
Repository: zeromicro/go-zero
Pull request #3940 (2024-02-27 10:46:10)

fix: mapping FillDefault is optional! bug

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/zeromicro/go-zero/pull/3940?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 94.55%. Comparing base [(`1f63cbe`)](https://app.codecov.io/gh/zeromicro/go-zero/commit/1f63cbe9c6ff89efb2aeeb816fc744a0fdb72325?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro) to head [(`2dac343`)](https://app.codecov.io/gh/zeromicro/go-zero/pull/3940?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro).

<details><summary>Additional details and impacted files</summary>



| [Files](https://app.codecov.io/gh/zeromicro/go-zero/pull/3940?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro) | Coverage Δ | |
|---|---|---|
| [core/mapping/unmarshaler.go](https://app.codecov.io/gh/zeromicro/go-zero/pull/3940?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro#diff-Y29yZS9tYXBwaW5nL3VubWFyc2hhbGVyLmdv) | `96.48% <100.00%> (+0.01%)` | :arrow_up: |

... and [1 file with indirect coverage changes](https://app.codecov.io/gh/zeromicro/go-zero/pull/3940/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zeromicro)

</details>

Patch:
diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go
index 982d69e5..fbaeb69a 100644
--- a/core/mapping/unmarshaler.go
+++ b/core/mapping/unmarshaler.go
@@ -428,6 +428,10 @@ func (u *Unmarshaler) parseOptionsWithContext(field reflect.StructField, m Value
 		}
 	}
 
+	if u.opts.fillDefault {
+		return key, &options.fieldOptionsWithContext, nil
+	}
+
 	optsWithContext, err := options.toOptionsWithContext(key, m, fullName)
 	if err != nil {
 		return "", nil, err


Test patch:
diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go
index fdbdf4e0..c258e3a6 100644
--- a/core/mapping/unmarshaler_test.go
+++ b/core/mapping/unmarshaler_test.go
@@ -12,6 +12,7 @@ import (
 
 	"github.com/google/uuid"
 	"github.com/stretchr/testify/assert"
+
 	"github.com/zeromicro/go-zero/core/stringx"
 )
 
@@ -5411,6 +5412,16 @@ func TestFillDefaultUnmarshal(t *testing.T) {
 		assert.Equal(t, "c", st.C)
 	})
 
+	t.Run("optional !", func(t *testing.T) {
+		type St struct {
+			A string `json:",optional"`
+			B string `json:",optional=!A"`
+		}
+		var st = St{}
+		err := fillDefaultUnmarshal.Unmarshal(map[string]any{}, &st)
+		assert.NoError(t, err)
+	})
+
 	t.Run("has value", func(t *testing.T) {
 		type St struct {
 			A string `json:",default=a"`