aboutsummaryrefslogtreecommitdiff
path: root/cmp/compare_test.go
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2020-06-08 16:37:31 -0700
committerGitHub <noreply@github.com>2020-06-08 16:37:31 -0700
commit11c4583a280337c7ac34c591b4321552981a7cc0 (patch)
tree665edc6d6efa7416baba1daa03dfb819c41da76a /cmp/compare_test.go
parent1776240f8f841dfa00cb72d811301dbb0298f983 (diff)
downloadgo-cmp-11c4583a280337c7ac34c591b4321552981a7cc0.tar.gz
Avoid leaking implementation details of the exporter (#206)
The current implementation for forcibly exporting fields relies on the reflect.Value.Addr method for the parent struct value, where it copies a non-addressable struct onto to the heap so that it is addressable. However, this action leaks a subtle detail of how the internal implementation works since the accessed field for within a struct is only addressable if and only if the parent struct is also addressable. Modify the implementation to avoid leaking this implementation detail by shallow copying the accessed unexported field to remove any notion of addressability if the parent struct is also unaddressable. Fixes #181
Diffstat (limited to 'cmp/compare_test.go')
-rw-r--r--cmp/compare_test.go29
1 files changed, 29 insertions, 0 deletions
diff --git a/cmp/compare_test.go b/cmp/compare_test.go
index f305b80..a70103f 100644
--- a/cmp/compare_test.go
+++ b/cmp/compare_test.go
@@ -624,6 +624,35 @@ func comparerTests() []test {
y: struct{ a int }{},
wantPanic: strconv.Quote(reflect.TypeOf(namedWithUnexported{}).PkgPath()) + ".(struct { a int })",
reason: "panic on unnamed struct type with unexported field",
+ }, {
+ label: label,
+ x: struct{ s fmt.Stringer }{new(bytes.Buffer)},
+ y: struct{ s fmt.Stringer }{new(bytes.Buffer)},
+ opts: []cmp.Option{
+ cmp.AllowUnexported(struct{ s fmt.Stringer }{}),
+ cmp.FilterPath(func(p cmp.Path) bool {
+ if _, ok := p.Last().(cmp.StructField); !ok {
+ return false
+ }
+
+ t := p.Index(-1).Type()
+ vx, vy := p.Index(-1).Values()
+ pvx, pvy := p.Index(-2).Values()
+ switch {
+ case vx.Type() != t:
+ panic(fmt.Sprintf("inconsistent type: %v != %v", vx.Type(), t))
+ case vy.Type() != t:
+ panic(fmt.Sprintf("inconsistent type: %v != %v", vy.Type(), t))
+ case vx.CanAddr() != pvx.CanAddr():
+ panic(fmt.Sprintf("inconsistent addressability: %v != %v", vx.CanAddr(), pvx.CanAddr()))
+ case vy.CanAddr() != pvy.CanAddr():
+ panic(fmt.Sprintf("inconsistent addressability: %v != %v", vy.CanAddr(), pvy.CanAddr()))
+ }
+ return true
+ }, cmp.Ignore()),
+ },
+ wantEqual: true,
+ reason: "verify that exporter does not leak implementation details",
}}
}