Skip to content

proposal: cmd/vet: reject uses of reflect.DeepEqual on imported types with unexported fields #71732

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ianlancetaylor opened this issue Feb 13, 2025 · 5 comments
Labels
Milestone

Comments

@ianlancetaylor
Copy link
Contributor

Proposal Details

The reflect.DeepEqual function is simple, and people often use it in tests in preference to more complex packages like github.com/google/go-cmp/cmp. But this can be misleading, and leads to issues like #43986 and #71702.

I propose that we add a vet check warning about uses of reflect.DeepEqual with values of types that are 1) imported; 2) contain references to unexported fields. Those are cases where reflect.DeepEqual is testing the internal state of a type defined in a different package, which means that the call is subject to breaking if the other package changes the type definition.

I believe that this meets vet's requirements for correctness and precision. However, I don't know how frequent the problem is.

I note that there is already a check in vet that warns about uses of reflect.DeepEqual specifically for the type reflect.Value (though I don't know how to actually run that check; it's not part of standard vet). This more general check could perhaps subsume that one.

CC @golang/tools-team

@dsnet
Copy link
Member

dsnet commented Feb 13, 2025

I'm generally in favor of this, but I think there are still many false positives:

  • I often use reflect.DeepEqual on errors from my own package and today deepequalerrors often complains at me.
  • I might have a group of packages in a module and I'm the author of all of them, using reflect.DeepEqual is reasonable across those package boundaries. Might we instead want to say that reflect.DeepEqual is permitted on any types in the same module? While a module is worked on by potentially multiple teams and a module-level boundary might be too broad, at least breakages within the module can usually be fixed in the same commit, reducing the harm of a poorly made reflect.DeepEqual check.
  • Should we permit reflect.DeepEqual on any type that is comparable? For example, it seems entirely reasonable to use reflect.DeepEqual on netip.Addr since the type is deliberately designed to be comparable.

@zigo101
Copy link

zigo101 commented Feb 14, 2025

This seems too strong. Maybe it is better to just mention the behavior a bit in DeepEqual's doc.

@adonovan
Copy link
Member

adonovan commented Feb 14, 2025

@randall77 wrote a reflectvaluecompare checker but says he didn't launch it because it had too many false positives; he also has an unfinished CL https://go.dev/cl/308749 to improve it.

@adonovan
Copy link
Member

adonovan commented Feb 14, 2025

FYI, I plan to write a modernizer to strength-reduce calls to reflect.Value.Compare to use slices.Equal instead when the operands are comparable.

Update: it's https://go.dev/cl/649457

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/649615 mentions this issue: go/analysis/passes/reflectvaluecompare: add main.go

gopherbot pushed a commit to golang/tools that referenced this issue Feb 25, 2025
…ain.go

This makes it easier to play with.

Updates golang/go#71732

Change-Id: If5ec810c051b0c12ec30891c9a431cc5ca06dcd9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/649615
Reviewed-by: Keith Randall <[email protected]>
Reviewed-by: Keith Randall <[email protected]>
Auto-Submit: Alan Donovan <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Incoming
Development

No branches or pull requests

5 participants