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: rename reports spurious conflict between parameter and package-level decl ("unexpected var object") #61294

Closed
jan-xyz opened this issue Jul 11, 2023 · 7 comments
Assignees
Labels
gopls Issues related to the Go language server, gopls. NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@jan-xyz
Copy link

jan-xyz commented Jul 11, 2023

What version of Go are you using (go version)?

❯ go version
go version go1.20.5 darwin/arm64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
❯ go env
GO111MODULE=""
GOARCH="arm64"
GOBIN=""
GOCACHE="/Users/jan.steinke/Library/Caches/go-build"
GOENV="/Users/jan.steinke/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/jan.steinke/Go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/jan.steinke/Go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/opt/homebrew/Cellar/go/1.20.5/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/opt/homebrew/Cellar/go/1.20.5/libexec/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.20.5"
GCCGO="gccgo"
AR="ar"
CC="cc"
CXX="c++"
CGO_ENABLED="1"
GOMOD="/Users/jan.steinke/code/github.com//go.mod"
GOWORK="/Users/jan.steinke/code/github.com//go.work"
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/gc/v8gpyn8x0vgfqjzzqcw1g5w40000gp/T/go-build2228193324=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

Trying to run rename command on the Decode parameter with gopls:

package main

import (
	"context"
	"encoding/base64"
	"fmt"

	"github.com/aws/aws-lambda-go/events"
	"google.golang.org/protobuf/proto"
)

// APIGatewayEventHandler is a function type that expresses how a APIGatewayEvent is handled.
// This can be used for convenience to build Middlewares for this handler.
type APIGatewayEventHandler func(ctx context.Context, e events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error)

// New returns a new DynamoDBEvent handler.
func New[TIn any](
	Decode func(events.APIGatewayProxyRequest) (TIn, error),
	endpoint func(context.Context, TIn) (proto.Message, error),
) APIGatewayEventHandler {
	return func(ctx context.Context, apiGatewayEvent events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error) {
		tripID, err := Decode(apiGatewayEvent)
		if err != nil {
			return nil, err
		}
		dto, err := endpoint(ctx, tripID)
		if err != nil {
			return nil, err
		}

		// this might work better in an encode function
		protoMessage, err := proto.Marshal(dto)
		if err != nil {
			return nil, err
		}

		body := base64.StdEncoding.EncodeToString(protoMessage)

		return &events.APIGatewayProxyResponse{
			StatusCode:        200,
			Headers:           map[string]string{},
			MultiValueHeaders: map[string][]string{},
			Body:              body,
			IsBase64Encoded:   true,
		}, nil
	}
}

I cannot reproduce it when I isolate that code so it seems it has something to do with the module in general. I cannot share it because it's a private code base.

What did you expect to see?

Being able to rename the parameter

What did you see instead?

An error by gopls:

gopls: 0: handler/handler.go:18:2: unexpected var object "var Decode func(github.com/aws/aws-lambda-go/events.APIGatewayProxyRequest) (TIn, error)" (please report a bug)
@gopherbot gopherbot added the gopls Issues related to the Go language server, gopls. label Jul 11, 2023
@adonovan
Copy link
Member

Based on the logic of renamer.check:

// check performs safety checks of the renaming of the 'from' object to r.to.
func (r *renamer) check(from types.Object) {
	if r.objsToUpdate[from] {
		return
	}
	r.objsToUpdate[from] = true

	// NB: order of conditions is important.
	if from_, ok := from.(*types.PkgName); ok {
		r.checkInFileBlock(from_)
	} else if from_, ok := from.(*types.Label); ok {
		r.checkLabel(from_)
	} else if isPackageLevel(from) {
		r.checkInPackageBlock(from)
	} else if v, ok := from.(*types.Var); ok && v.IsField() {
		r.checkStructField(v)
	} else if f, ok := from.(*types.Func); ok && recv(f) != nil {
		r.checkMethod(f)
	} else if isLocal(from) {
		r.checkInLexicalScope(from)
	} else {
		r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n",
			objectKind(from), from)
	}
}

and the fact that Decode is not a package name, label, package-level decl, field, or method, it must be that the isLocal function is spuriously returning false for this parameter var, likely somehow related to generics. But so far I have not been able to reproduce it in golang.org/x/tools/go/types/internal/play modified to report isLocal, trying minimizations such as:

package main

import "fmt"

func New[T any](g func() (T, error)) {
	g()
}

@adonovan adonovan added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Jul 13, 2023
@adonovan adonovan added this to the gopls/v0.13.0 milestone Jul 13, 2023
@jan-xyz
Copy link
Author

jan-xyz commented Jul 13, 2023

I had the same problem. As soon as I take it out of context it stops being broken. I feel kind of stupid reporting by it 🙈

@jan-xyz
Copy link
Author

jan-xyz commented Jul 14, 2023

Ha! I now have a minimal reproduction of it

package cmd

import "errors"

var Foo = errors.New("oops")

func Bar() {
}

const Baz = 42

func New(Foo int, Bar string, Baz float64) string {
	return ""
}

The problem seems to happen when the parameter is shadowing an exported token. Non of the parameters to New can be renamed and fail with gopls: 0: ...: unexpected var object "var ...)" (please report a bug)

That's also why it stoped to break once I took it out of its context, because it stopped shadowing the exported token called Decode.

@adonovan
Copy link
Member

Well done for isolating this test case: I can reproduce it locally and will prepare a fix presently.

@adonovan adonovan self-assigned this Jul 14, 2023
@adonovan adonovan added NeedsFix The path to resolution is known, but the work has not been done. and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Jul 14, 2023
@adonovan adonovan changed the title tools/gopls: "unexpected var object" when trying to rename a function parameter gopls: rename reports spurious conflict between parameter and package-level decl ("unexpected var object") Jul 14, 2023
@gopherbot
Copy link

Change https://go.dev/cl/509875 mentions this issue: gopls/internal/lsp/source: fix spurious "unexpected var object" error

@adonovan
Copy link
Member

adonovan commented Jul 14, 2023

That's also why it stoped to break once I took it out of its context, because it stopped shadowing the exported token called Decode.

The reason it was tricky is that the package must be imported for the bug to appear, even though the object is local to the function. The explanation is that, from the type-checker's perspective, function parameter vars are exported, as they may affect error messages emitted by the compiler in other packages, and this property of being exported causes rename to use a different algorithm.

@findleyr We should bear in mind that being imported may be significant when trying to reproduce future rename bugs.

@jan-xyz
Copy link
Author

jan-xyz commented Jul 14, 2023

I guess it doesn't happen that much because it's very unusual for function parameter vars to be uppercase. That's also how I encountered it, I saw the code and wanted to lowercase it. The Decode function was though quite far away from this in a completely different file.

@seankhliao seankhliao changed the title gopls: rename reports spurious conflict between parameter and package-level decl ("unexpected var object") x/tools/gopls: rename reports spurious conflict between parameter and package-level decl ("unexpected var object") Jul 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
gopls Issues related to the Go language server, gopls. NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

No branches or pull requests

3 participants