Skip to content
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

x/tools/gopls: very poor quality of finding implementations feature #57898

Closed
inliquid opened this issue Jan 18, 2023 · 21 comments
Closed

x/tools/gopls: very poor quality of finding implementations feature #57898

inliquid opened this issue Jan 18, 2023 · 21 comments
Assignees
Labels
FrozenDueToAge gopls/incremental gopls Issues related to the Go language server, gopls. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Tools This label describes issues relating to any tools in the x/tools repository.
Milestone

Comments

@inliquid
Copy link

inliquid commented Jan 18, 2023

gopls version

gopls@master (most recent)

go env

>go env
set GO111MODULE=on
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\***\AppData\Local\go-build
set GOENV=C:\Users\***\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=C:\Users\***\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\***\go
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
set GOROOT=C:\Program Files\Go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.19.5
set GCCGO=gccgo
set GOAMD64=v1
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=NUL
set GOWORK=
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=C:\Users\***\AppData\Local\Temp\go-build3216761985=/tmp/go-build -gno-record-gcc-switches

What did you do?

In recent versions I start noticing that gopls can't find that some struct implements an interface or, in reverse, that there are implementations of some interface. This didn't happen before and looks like regression.

For example this compressWriter implements http.ResposeWriter because it embeds it, but it's not shown:
изображение

In this example, gopls doesn't see embedded jwt.RegisteredClaims (from github.com/golang-jwt/jwt/v4) struct, which in turn implements jwt.Claims:

изображение

Same happens when I try to find implementations of a particular interface.

What did you expect to see?

Find "interfaces implemented by a struct" and "structs which implement an interface" feature finds all relevant objects/interfaces.

What did you see instead?

Sometimes it works, sometimes it doesn't. This inconsistent behavior creates quite negative user experience and leads to mistakes in code.

@gopherbot gopherbot added Tools This label describes issues relating to any tools in the x/tools repository. gopls Issues related to the Go language server, gopls. labels Jan 18, 2023
@gopherbot gopherbot added this to the Unreleased milestone Jan 18, 2023
@findleyr findleyr modified the milestones: Unreleased, gopls/v0.12.0 Jan 18, 2023
@findleyr
Copy link
Contributor

Thank you for the report. We will investigate (for starters, we should bisect the regression).

CC @adonovan who is working on implementations currently. This may be related to recent changes in master.

@adonovan
Copy link
Member

adonovan commented Jan 18, 2023

As chance would have it, we just merged a new implementation of 'implementations' this morning, and I tried it on the example in your bug report.

jwt$ git remote get-url origin
https://github.com/golang-jwt/jwt
jwt$ cat foo/foo.go 
package foo

import "github.com/golang-jwt/jwt/v4"

type embedSomeStruct struct {
	jwt.RegisteredClaims

	Str string
	I   int
}
  1. When I run 'implementations' on embedSomeStruct, the sole answer is type "Claims", which seems right.

  2. When I run 'implementations' on the jwt.RegisteredClaims, I get an error ("RegisteredClaims is a var not a type"), which is a bug, because an embedded field is both a var and a type.

  3. When I run implementations' on type Claims in claims.go, I get 7 types, including embedSomeStruct, so that's good.

I compared it to v0.11.0, and it gave essentially the same answers to all three queries, except that v0.11.0 returned an additional 2 elements in query number 3, for a total of 9. The additional ones were:

example_test.go
36: 	type MyCustomClaims struct {
78: 	type MyCustomClaims struct {

Both of these are function-local type declarations, which are only reported by the new algorithm if they appear in the same package as the query type. In this case, they do (specifically, a test variant), so that's another regression.

So your example has uncovered one bug in old and new algorithms, and one regression in the new algorithm, both of which I will fix. But I can't reproduce the specific problem you mention about embedSomeStruct not having any implementations. (I'm using Emacs+eglot as my LSP client, but I don't think that should matter much.)

Also, could you provide more information about compressWriter? I can't see the declaration of that type in your screenshot, nor does it appear to be part of jwt. Thanks.

@inliquid
Copy link
Author

Also, could you provide more information about compressWriter? I can't see the declaration of that type in your screenshot, nor does it appear to be part of jwt. Thanks.

It's under that wide popup with Writer in it:

type compressWriter struct {
	http.ResponseWriter
	io.Writer
}

// In case if it matters, that's the only method which redeclares Write:
func (cw *compressWriter) Write(b []byte) (int, error) {
	return cw.Writer.Write(b)
}

@gopherbot
Copy link

Change https://go.dev/cl/462658 mentions this issue: gopls/internal/lsp/source: make implementations2 work on embedded fields

@adonovan
Copy link
Member

It's under that wide popup with Writer in it:

Ah, so it is! I had assumed it was occluded by the popup, bit it was merely displaced downwards.

// In case if it matters, that's the only method which redeclares Write:

It does matter. Without this method, compressWriter would not satisfy the Writer interface because it would be ambiguous which of the two Write methods (one from each field, each at the same embedding depth) should "win". The explicit method essentially resolves in favor of "the second".

I tested the compressWriter example using three versions--the new implementation of 'implementations', the previous one from earlier this week, and the v0.11.0 release--and it correctly reports a result of two elements, ResponseWriter and Writer. So unfortunately I'm not able to reproduce the failure yet.

@adonovan
Copy link
Member

[adonovan] Both of these are function-local type declarations, which are only reported by the new algorithm if they appear in the same package as the query type. In this case, they do (specifically, a test variant), so that's another regression.

On closer inspection, the example_test.go file is in a different package--witness the package's _test suffix--so the fact that its function-local types are not among the query results is the intended behavior (even though it does differ in this special case from the previous implementation).

@adonovan
Copy link
Member

@findleyr and I just observed the correct behavior on the compressWriter example using VS Code, so that issue remains elusive. If you're able to provide any more details that might help us reproduce it, I'd be happy to fix it. Otherwise I plan to close this issue when https://go.dev/cl/462658 is merged. Thanks again.

gopherbot pushed a commit to golang/tools that referenced this issue Jan 19, 2023
Previously, the operation on struct{T} would fail with the error
"T is a var, not a type".

I don't like the way that each new testcase in the marker tests
incurs a quadratic logical complexity. Convenient though it is
to edit testdata/*.go files, it tends to encourage a kind of sprawl.
Do we have a precedent for self-contained marker tests that use
a txtar literal?

Updates golang/go#57898

Change-Id: I39a5cb1d96452b621bc6661094f1d735f0e32d3f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/462658
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
@inliquid
Copy link
Author

inliquid commented Jan 25, 2023

This problem still persists, f.e. pb.GreeterServer is "classic" gRPC server with SayHello method, and it's embedded in outer GreeterLogging:
изображение

It actually doesn't matter which methods are exposed by embedded interface, more important is that it's already an interface.

@inliquid
Copy link
Author

Another example:
изображение

@findleyr findleyr reopened this Jan 26, 2023
@findleyr findleyr added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Jan 26, 2023
@findleyr
Copy link
Contributor

We will get to the bottom of this.

@tooltitude-support
Copy link

@inliquid In our extension we have an alternative implementation of finding implementations. You could try it by installing from here: https://marketplace.visualstudio.com/items?itemName=tooltitudeteam.tooltitude

Note: you need to use code lens to find usages, not shortcut for it to work. We work as an augmentation for gopls, so we need to work in this way.

@findleyr
Copy link
Contributor

@tooltitude-support while we welcome additions to the Go tools ecosystem, this issue is about a bug in gopls. Advocating for an alternative does not help us fix the bug. Please refrain from such comments in the future: they are not constructive, and in fact may be counter-productive if they distract from root-causing the bug in question. Furthermore, it makes me uncomfortable that users may be directed from this issue to (what appears to be) a closed-source extension. Do you publish your source code anywhere?

You are of course welcome to suggest solutions to the problem here, based on your experience writing your extension.

@tooltitude-support
Copy link

@findleyr Ok, I am sorry for the comment and that it distracts from the fixing of the issue. The extension is closed source and we don't have plan to open source it. However, I don't think it's completely counter productive. Having two implementations of the same functionality allows us to compare the results, and try to find the place where the issue reproduces.

Concerning the issue, I could add that I tried the results of our search compared to gopls search, and in all cases where I tried, they were consistent, i.e. all derived types were the same. There was a difference in library types, but I guess, that we collect results here differently. We are more aggressive in collecting files with non current build tags.

Also, feel free to do the same on some projects, in this way you might find inconsistencies, though, I tried hard to find at least one of them and failed at it. Maybe @inliquid could try doing the same.

@tooltitude-support
Copy link

@findleyr Concerning the projects, I tried, they were k8, and esbuild. k8 is a bit outlier since most of the dependencies are vendored. Esbuild, doesn't have that many interfaces.

@tooltitude-support
Copy link

tooltitude-support commented Feb 27, 2023

@findleyr Actually, I looked into library files on k8.

Here's comparison of usages of io.WriteSeeker:

Screenshot 2023-02-27 at 9 51 12 AM

Screenshot 2023-02-27 at 9 51 31 AM

You miss closeOnce from exec.go which implements io.WriteSeeker via inclusion of os.File (we miss file from fd_unix due to attempt to load all tags).

@findleyr
Copy link
Contributor

@tooltitude-support thank you for investigating, and for the screenshots.

@adonovan
Copy link
Member

adonovan commented Feb 27, 2023

The extension is closed source and we don't have plan to open source it.

Without wishing to prolong this off-topic discussion, I think you @tooltitude-support would do well to recognize that it is not reasonable in 2023 to ask developers to install closed-source software from unknown organizations. Open source software such as Chrome, Emacs, Bash, GNU, or gopls allows a vigilant community of users to provide some collective (if imperfect) assurance that the software is not malicious. Closed-source software doesn't enable scrutiny of the code, but vendors (e.g. Apple, JetBrains, Adobe) may nonetheless demonstrate a good track record for security, and they risk losing customers and income when they screw up.

Tooltitude is closed source, not affiliated with a reputable organization, and, as far as I can tell, is the work of a single person. As a result it would be foolish of me to run your code on my machine. For all I know, the entire project could be a ruse to steal SSH keys from Go developers. I'm pretty sure that's not your intent, but I can't really be sure.

As @findleyr said, we welcome better tools for the Go ecosystem, but we also encourage secure development practices. Your users shouldn't have to choose between them.

@tooltitude-support
Copy link

tooltitude-support commented Feb 27, 2023

For all I know, the entire project could be a ruse to steal SSH keys from Go developers. I'm pretty sure that's not your intent, but I can't really be sure.

We develop software in a secure way using best practices. We intend to be a trusted software vendor, and not a criminal enterprise. It's pretty disappointing to hear this gross description from you instead of just mentioning that you don't trust us enough to install the software.

P.S. If you don't trust software enough, you could always run it in a VM, either on your computer or in the cloud.

@adonovan
Copy link
Member

I don't believe providing closed source software is insecure development practice (look how many closed sourced programs you have on your personal computers, mobile devices, including from small businesses like ours, I bet there're plenty of them, but may be I am wrong).

Perhaps I'm unusual, but there are in fact very few closed-source programs on my (actually Google's) developer machine: only Spotify and macOS itself. Google's sysadmins may have installed more, but I trust them. (My personal phone is a different story, but mobiles have a different capability model that doesn't make it so trivial to exfiltrate any file).

Yep, we are pretty new and small, but it doesn't mean that we won't be able to establish a track record with time. We are working on it. Also, if you are concerned about security, you could install the software in an isolated VM without any problem.

I could, but that's a pretty big disincentive to adoption. And I'd still be trusting your program with the privacy of my source code.

Of course it's very disappointing to hear from you about stealing keys. Our company intends to develop secure software using best practices and sell it and not to perpetrate criminal activity. You could have just said that you don't trust it to be secure instead of explaining possibility of it in such a gross way.

To be clear, I don't actually believe you have any malicious intent; I was just inviting you to put yourself in the shoes of your more skeptical and security conscious users at a time when vulnerabilities are always making headlines.

Also, one question. Is there something which would help you have more trust in the software? Does providing information about who we are, our experience, our investors, or some certificates help you? Would you be more eager to install it if it was provided with some form of shared source instead of full open source? We plan to provide this information, but we have limited time, and we would rather invest it in the project than in explanations.

P.S. We would love to open source the code if we could find a sustainable way to develop it while it's open source. May be in the future we will find such an option, i.e. some form of OSS/community edition, but not now. We need to at least understand what premium and non premium functionality will be before doing anything in this direction. It will take a while.

Sorry, I'm not much of a business person, and I don't have any good ideas for how to cross the chasm between enticing users with an early product and running a mature trustworthy business that sells closed developer tools (such as JetBrains, Sublime). Developer tools has always been a tough business because so much is free. Best of luck though.

Thanks for reporting the gopls bug. It is much appreciated.

@tooltitude-support
Copy link

tooltitude-support commented Feb 27, 2023

@adonovan Thanks for your answers! Really appreciate your feedback!

Best of luck though.

Thanks a lot!

P.S. Will stop the discussion to stop off-topic.

@adonovan
Copy link
Member

adonovan commented Apr 10, 2023

@tooltitude-support, I can't reproduce the closeOnce problem using gopls@master, go@1.19.5:

xtools$ git log | head -n 1
commit 40fb89cd55bdb68faa88c52593caafb733cdc880

# implementations on io.WriteSeeker
xtools$ ( PATH=$HOME/sdk/go1.20/bin:$PATH  go run ./gopls implementation ~/sdk/go1.19.5/src/io/io.go:#6010 )
/Users/adonovan/sdk/go1.19.5/src/internal/poll/fd_unix.go:18:6-8
/Users/adonovan/sdk/go1.19.5/src/os/exec/exec.go:735:6-15
/Users/adonovan/sdk/go1.19.5/src/os/types.go:16:6-10
/Users/adonovan/w/xtools/internal/lockedfile/lockedfile.go:24:6-10
/Users/adonovan/w/xtools/internal/lockedfile/lockedfile.go:31:6-12
xtools$ nl -ba /Users/adonovan/sdk/go1.19.5/src/os/exec/exec.go | grep 735
   735	type closeOnce struct {

@inliquid: I can't reproduce the GreeterServer issue by using type S struct { io.Reader } and doing an implementation query on S. Was that a sufficient test case for you?

@inliquid: The built-in error interface is a special case that is indeed not supported. I'll file a separate issue for that one.

I'm going to close this issue. Please file a new issue if you still believe any of these problems (or others) exist at master, as this issue refers to a number of separate problems.

@golang golang locked and limited conversation to collaborators Apr 9, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge gopls/incremental gopls Issues related to the Go language server, gopls. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Tools This label describes issues relating to any tools in the x/tools repository.
Projects
None yet
Development

No branches or pull requests

5 participants