golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.94k stars 17.66k forks source link

x/tools/goimports: github.com/google/go-github resolves to v17.0.0+incompatible rather than v45 #53767

Closed shabbyrobe closed 2 years ago

shabbyrobe commented 2 years ago

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

$ go version
go version go1.19rc1 linux/amd64

Does this issue reproduce with the latest release?

Yes, latest 1.18 and 1.19rc1 are the same.

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

go env Output
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="$HOME/.cache/go-build"
GOENV="$HOME/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="$HOME/code/gopath/pkg/mod"
GOOS="linux"
GOPATH="$HOME/code/gopath"
GOPROXY="direct"
GOROOT="$HOME/apps/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="$HOME/apps/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.19rc1"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2931071312=/tmp/go-build -gno-record-gcc-switches"

What did you do?

$ go mod init example.com/modbug
$ go get github.com/google/go-github/v45
$ echo 'package main
func main() {
    _ = github.NewClient(nil)
}' > main.go

$ go run golang.org/x/tools/cmd/goimports@latest -d main.go

I also completely cleared out my $GOPATH/pkg directory and the issue reproduced.

What did you expect to see?

diff -u main.go.orig main.go
--- main.go.orig    2022-07-10 12:33:48.039814180 +1000
+++ main.go 2022-07-10 12:33:48.039814180 +1000
@@ -1,5 +1,7 @@
 package main

+import "github.com/google/go-github/v45/github"
+
 func main() {
    _ = github.NewClient(nil)
 }

What did you see instead?

diff -u main.go.orig main.go
--- main.go.orig    2022-07-10 12:33:48.039814180 +1000
+++ main.go 2022-07-10 12:33:48.039814180 +1000
@@ -1,5 +1,7 @@
 package main

+import "github.com/google/go-github/github"
+
 func main() {
    _ = github.NewClient(nil)
 }

This persists even if other source files in the module already contain imports for the correct v45 version, and after the package has been successfully built.

Full goimports -v Output
2022/07/10 12:41:04.166465 fixImports(filename="main.go"), abs="/tmp/modbug/main.go", srcDir="/tmp/modbug" ...
2022/07/10 12:41:04.169772 3.115624ms for GOROOT=/home/user/apps/go GOPATH=/home/user/code/gopath GO111MODULE=on GOPROXY=direct PWD=/tmp/modbug go env -json GO111MODULE GOFLAGS GOINSECURE GOMOD GOMODCACHE GONOPROXY GONOSUMDB GOPATH GOPROXY GOROOT GOSUMDB
2022/07/10 12:41:04.169821 loading package names for 0 packages
2022/07/10 12:41:04.173057 3.065714ms for GOROOT=/home/user/apps/go GOPATH=/home/user/code/gopath GO111MODULE=on GOPROXY=direct PWD=/tmp/modbug go list -m -f "{{.Path}}\n{{.Dir}}\n{{.GoMod}}\n{{.GoVersion}}\n{{range context.ReleaseTags}}{{if eq . \"go1.14\"}}{{.}}{{end}}{{end}}\n"
2022/07/10 12:41:04.176110 2.923436ms for GOROOT=/home/user/apps/go GOPATH=/home/user/code/gopath GO111MODULE=on GOPROXY=direct PWD=/tmp/modbug go env GOFLAGS
2022/07/10 12:41:04.179616 3.373711ms for GOROOT=/home/user/apps/go GOPATH=/home/user/code/gopath GO111MODULE=on GOPROXY=direct PWD=/tmp/modbug go list -m -e -json ...
2022/07/10 12:41:04.179733 module github.com/golang/protobuf has not been downloaded and will be ignored
2022/07/10 12:41:04.179744 module github.com/google/go-cmp has not been downloaded and will be ignored
2022/07/10 12:41:04.179763 module github.com/google/go-querystring has not been downloaded and will be ignored
2022/07/10 12:41:04.179771 module golang.org/x/crypto has not been downloaded and will be ignored
2022/07/10 12:41:04.179778 module golang.org/x/net has not been downloaded and will be ignored
2022/07/10 12:41:04.179787 module golang.org/x/oauth2 has not been downloaded and will be ignored
2022/07/10 12:41:04.179795 module google.golang.org/appengine has not been downloaded and will be ignored
2022/07/10 12:41:04.179807 done loading package names for 0 packages
2022/07/10 12:41:04.179861 gopathwalk: scanning /home/user/apps/go/src
2022/07/10 12:41:04.183136 gopathwalk: scanned /home/user/apps/go/src in 3.288852ms
2022/07/10 12:41:04.183152 gopathwalk: scanning /tmp/modbug
2022/07/10 12:41:04.183292 gopathwalk: scanned /tmp/modbug in 138.384µs
2022/07/10 12:41:04.183304 gopathwalk: scanning /home/user/code/gopath/pkg/mod/github.com/google/go-github/v45@v45.2.0
2022/07/10 12:41:04.183316 Error statting ignored directory: stat /home/user/code/gopath/pkg/mod/github.com/google/go-github/v45@v45.2.0/cache: no such file or directory
2022/07/10 12:41:04.183613 gopathwalk: scanned /home/user/code/gopath/pkg/mod/github.com/google/go-github/v45@v45.2.0 in 306.818µs
2022/07/10 12:41:04.183621 gopathwalk: scanning /home/user/code/gopath/pkg/mod
2022/07/10 12:41:04.183626 Directory added to ignore list: /home/user/code/gopath/pkg/mod/cache
2022/07/10 12:41:04.186568 gopathwalk: scanned /home/user/code/gopath/pkg/mod in 2.945047ms
2022/07/10 12:41:04.186596 github candidate 1/2: github.com/google/go-github/github in /home/user/code/gopath/pkg/mod/github.com/google/go-github@v17.0.0+incompatible/github
2022/07/10 12:41:04.186601 github candidate 2/2: github.com/google/go-github/v45/github in /home/user/code/gopath/pkg/mod/github.com/google/go-github/v45@v45.2.0/github
2022/07/10 12:41:04.186652 loading exports in dir /home/user/code/gopath/pkg/mod/github.com/google/go-github/v45@v45.2.0/github (seeking package github)
2022/07/10 12:41:04.186731 loading exports in dir /home/user/code/gopath/pkg/mod/github.com/google/go-github@v17.0.0+incompatible/github (seeking package github)
2022/07/10 12:41:04.231329 loaded exports in dir /home/user/code/gopath/pkg/mod/github.com/google/go-github@v17.0.0+incompatible/github (package github): APIMeta, AbuseRateLimitError, AcceptedError, ActivityListStarredOptions, ActivityService, AdminEnforcement, AdminService, AdminStats, App, AppsService, Authorization, AuthorizationApp, AuthorizationRequest, AuthorizationUpdateRequest, AuthorizationsService, AutoTriggerCheck, BasicAuthTransport, Blob, Bool, Branch, BranchRestrictions, BranchRestrictionsRequest, CheckResponse, CheckRun, CheckRunAnnotation, CheckRunEvent, CheckRunImage, CheckRunOutput, CheckSuite, CheckSuiteEvent, CheckSuitePreferenceOptions, CheckSuitePreferenceResults, ChecksService, Client, CodeOfConduct, CodeResult, CodeSearchResult, CombinedStatus, CommentStats, Commit, CommitAuthor, CommitCommentEvent, CommitFile, CommitResult, CommitStats, CommitsComparison, CommitsListOptions, CommitsSearchResult, CommunityHealthFiles, CommunityHealthMetrics, Contributor, ContributorStats, CreateCheckRunOptions, CreateCheckSuiteOptions, CreateEvent, CreateOrgInvitationOptions, DeleteEvent, DeliveryID, Deployment, DeploymentEvent, DeploymentRequest, DeploymentStatus, DeploymentStatusEvent, DeploymentStatusRequest, DeploymentsListOptions, Diff, DiscussionComment, DiscussionCommentListOptions, DiscussionListOptions, DismissalRestrictions, DismissalRestrictionsRequest, DraftReviewComment, EditChange, Error, ErrorResponse, Event, FeedLink, Feeds, ForkEvent, GPGEmail, GPGKey, Gist, GistComment, GistCommit, GistFile, GistFilename, GistFork, GistListOptions, GistStats, GistsService, GitObject, GitService, Gitignore, GitignoresService, GollumEvent, Grant, Hook, HookStats, Hovercard, HovercardOptions, Import, Installation, InstallationEvent, InstallationPermissions, InstallationRepositoriesEvent, InstallationToken, Int, Int64, Invitation, Issue, IssueComment, IssueCommentEvent, IssueEvent, IssueListByRepoOptions, IssueListCommentsOptions, IssueListOptions, IssueRequest, IssueStats, IssuesEvent, IssuesSearchResult, IssuesService, Key, Label, LabelEvent, LabelResult, LabelsSearchResult, LargeFile, License, LicensesService, ListCheckRunsOptions, ListCheckRunsResults, ListCheckSuiteOptions, ListCheckSuiteResults, ListCollaboratorsOptions, ListContributorsOptions, ListMembersOptions, ListOptions, ListOrgMembershipsOptions, ListOutsideCollaboratorsOptions, LockIssueOptions, MarkdownOptions, MarketplacePlan, MarketplacePlanAccount, MarketplacePurchase, MarketplacePurchaseEvent, MarketplaceService, Match, MemberEvent, Membership, MembershipEvent, Metric, Migration, MigrationOptions, MigrationService, Milestone, MilestoneEvent, MilestoneListOptions, MilestoneStats, NewClient, NewEnterpriseClient, NewPullRequest, NewTeam, Notification, NotificationListOptions, NotificationSubject, OrgBlockEvent, OrgStats, Organization, OrganizationEvent, OrganizationsListOptions, OrganizationsService, Page, PageBuildEvent, PageStats, Pages, PagesBuild, PagesError, ParseWebHook, Patch, PingEvent, Plan, PreReceiveHook, PreferenceList, Project, ProjectCard, ProjectCardChange, ProjectCardEvent, ProjectCardListOptions, ProjectCardMoveOptions, ProjectCardOptions, ProjectChange, ProjectColumn, ProjectColumnChange, ProjectColumnEvent, ProjectColumnMoveOptions, ProjectColumnOptions, ProjectEvent, ProjectListOptions, ProjectOptions, ProjectsService, Protection, ProtectionRequest, PublicEvent, PullRequest, PullRequestBranch, PullRequestComment, PullRequestEvent, PullRequestLinks, PullRequestListCommentsOptions, PullRequestListOptions, PullRequestMergeResult, PullRequestOptions, PullRequestReview, PullRequestReviewCommentEvent, PullRequestReviewDismissalRequest, PullRequestReviewEvent, PullRequestReviewRequest, PullRequestReviewsEnforcement, PullRequestReviewsEnforcementRequest, PullRequestReviewsEnforcementUpdate, PullRequestsService, PullStats, PunchCard, PushEvent, PushEventCommit, PushEventRepoOwner, PushEventRepository, Rate, RateLimitError, RateLimits, RawOptions, RawType, Reaction, Reactions, ReactionsService, Reference, ReferenceListOptions, ReleaseAsset, ReleaseEvent, Rename, RepoStats, RepoStatus, RepositoriesSearchResult, RepositoriesService, Repository, RepositoryAddCollaboratorOptions, RepositoryComment, RepositoryCommit, RepositoryContent, RepositoryContentFileOptions, RepositoryContentGetOptions, RepositoryContentResponse, RepositoryCreateForkOptions, RepositoryEvent, RepositoryInvitation, RepositoryLicense, RepositoryListAllOptions, RepositoryListByOrgOptions, RepositoryListForksOptions, RepositoryListOptions, RepositoryMergeRequest, RepositoryParticipation, RepositoryPermissionLevel, RepositoryRelease, RepositoryTag, RequestCheckSuiteOptions, RequiredStatusChecks, RequiredStatusChecksRequest, Response, Reviewers, ReviewersRequest, Scope, ScopeAdminGPGKey, ScopeAdminOrg, ScopeAdminOrgHook, ScopeAdminPublicKey, ScopeAdminRepoHook, ScopeDeleteRepo, ScopeGist, ScopeNone, ScopeNotifications, ScopePublicRepo, ScopeReadGPGKey, ScopeReadOrg, ScopeReadPublicKey, ScopeReadRepoHook, ScopeRepo, ScopeRepoDeployment, ScopeRepoStatus, ScopeUser, ScopeUserEmail, ScopeUserFollow, ScopeWriteGPGKey, ScopeWriteOrg, ScopeWritePublicKey, ScopeWriteRepoHook, SearchOptions, SearchService, ServiceHook, SignatureVerification, Source, SourceImportAuthor, Stargazer, StarredRepository, StatusEvent, String, Stringify, Subscription, Tag, Tarball, Team, TeamAddEvent, TeamAddTeamMembershipOptions, TeamAddTeamRepoOptions, TeamChange, TeamDiscussion, TeamEvent, TeamLDAPMapping, TeamListTeamMembersOptions, TeamsService, TextMatch, Timeline, Timestamp, TrafficBreakdownOptions, TrafficClones, TrafficData, TrafficPath, TrafficReferrer, TrafficViews, TransferRequest, Tree, TreeEntry, TwoFactorAuthError, UnauthenticatedRateLimitedTransport, UpdateCheckRunOptions, UploadOptions, User, UserContext, UserEmail, UserLDAPMapping, UserListOptions, UserMigration, UserMigrationOptions, UserStats, UsersSearchResult, UsersService, ValidatePayload, WatchEvent, WebHookAuthor, WebHookCommit, WebHookPayload, WebHookType, WeeklyCommitActivity, WeeklyStats, Zipball
2022/07/10 12:41:04.231527 loading exports in dir /home/user/code/gopath/pkg/mod/github.com/google/go-github/v45@v45.2.0/github (seeking package github): context canceled

seankhliao commented 2 years ago

Duplicate of #41800

shabbyrobe commented 2 years ago

Just in case someone else comes across this who is looking for a solution, I have a hacky workaround that seems to be working so far.

Clone https://github.com/golang/tools, apply the following patch, then go install ./cmd/goimports && go install ./gopls. YMMV, but this is working OK for me at the moment. I will update the patch if I find issues with it.

diff --git a/internal/imports/fix.go b/internal/imports/fix.go
index 9e373d64e..049811e89 100644
--- a/internal/imports/fix.go
+++ b/internal/imports/fix.go
@@ -18,6 +18,7 @@ import (
    "path"
    "path/filepath"
    "reflect"
+   "regexp"
    "sort"
    "strconv"
    "strings"
@@ -1047,7 +1048,14 @@ func addExternalCandidates(pass *pass, refs references, filename string) error {
            }
            mu.Lock()
            defer mu.Unlock()
-           found[pkg.packageName] = append(found[pkg.packageName], pkgDistance{pkg, distance(pass.srcDir, pkg.dir)})
+           importPathShortSansVersion, version, hasVersion := extractVersion(pkg.importPathShort)
+           found[pkg.packageName] = append(found[pkg.packageName], pkgDistance{
+               pkg:                        pkg,
+               distance:                   distance(pass.srcDir, pkg.dir),
+               importPathShortSansVersion: importPathShortSansVersion,
+               version:                    version,
+               hasVersion:                 hasVersion,
+           })
            return false // We'll do our own loading after we sort.
        },
    }
@@ -1271,8 +1279,11 @@ type pkg struct {
 }

 type pkgDistance struct {
-   pkg      *pkg
-   distance int // relative distance to target
+   pkg                        *pkg
+   distance                   int  // relative distance to target
+   importPathShortSansVersion string
+   version                    int
+   hasVersion                 bool
 }

 // byDistanceOrImportPathShortLength sorts by relative distance breaking ties
@@ -1281,6 +1292,16 @@ type byDistanceOrImportPathShortLength []pkgDistance

 func (s byDistanceOrImportPathShortLength) Len() int { return len(s) }
 func (s byDistanceOrImportPathShortLength) Less(i, j int) bool {
+   if s[i].importPathShortSansVersion == s[j].importPathShortSansVersion {
+       if s[i].hasVersion && s[j].hasVersion {
+           return s[i].version > s[j].version
+       } else if s[i].hasVersion {
+           return true
+       } else if s[j].hasVersion {
+           return false
+       }
+   }
+
    di, dj := s[i].distance, s[j].distance
    if di == -1 {
        return false
@@ -1298,8 +1319,22 @@ func (s byDistanceOrImportPathShortLength) Less(i, j int) bool {
    }
    return vi < vj
 }
+
 func (s byDistanceOrImportPathShortLength) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

+var extractVersionPattern = regexp.MustCompile(`^(.*)/v([0-9]+)(/.*)?$`)
+
+func extractVersion(importPathShort string) (importPathShortSansVersion string, version int, found bool) {
+   matches := extractVersionPattern.FindAllStringSubmatch(importPathShort, 2)
+   if len(matches) != 1 {
+       return importPathShort, 0, false
+   }
+   match := matches[0]
+   version, _ = strconv.Atoi(match[2])
+   importPathShortSansVersion = match[1] + match[3]
+   return importPathShortSansVersion, version, true
+}
+
 func distance(basepath, targetpath string) int {
    p, err := filepath.Rel(basepath, targetpath)
    if err != nil {