diff options
author | Joe Tsai <joetsai@digital-static.net> | 2020-06-08 16:37:31 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-08 16:37:31 -0700 |
commit | 11c4583a280337c7ac34c591b4321552981a7cc0 (patch) | |
tree | 665edc6d6efa7416baba1daa03dfb819c41da76a /cmp/compare_test.go | |
parent | 1776240f8f841dfa00cb72d811301dbb0298f983 (diff) | |
download | go-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.go | 29 |
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", }} } |