Bump the extractor-dependencies group in /go/extractor with 2 updates

Bumps the extractor-dependencies group in /go/extractor with 2 updates: [golang.org/x/mod](https://github.com/golang/mod) and [golang.org/x/tools](https://github.com/golang/tools).


Updates `golang.org/x/mod` from 0.15.0 to 0.18.0
- [Commits](https://github.com/golang/mod/compare/v0.15.0...v0.18.0)

Updates `golang.org/x/tools` from 0.18.0 to 0.22.0
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.18.0...v0.22.0)

---
updated-dependencies:
- dependency-name: golang.org/x/mod
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: extractor-dependencies
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: extractor-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2024-06-14 03:34:10 +00:00
committed by GitHub
parent 3d037e73f0
commit 14842d4ba7
77 changed files with 18702 additions and 1600 deletions

View File

@@ -3,6 +3,8 @@ module github.com/github/codeql-go/extractor
go 1.22.0
require (
golang.org/x/mod v0.15.0
golang.org/x/tools v0.18.0
golang.org/x/mod v0.18.0
golang.org/x/tools v0.22.0
)
require golang.org/x/sync v0.7.0 // indirect

View File

@@ -1,6 +1,6 @@
golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=

View File

@@ -1,11 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "lazyregexp",
srcs = ["lazyre.go"],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/mod/internal/lazyregexp",
importpath = "golang.org/x/mod/internal/lazyregexp",
visibility = ["//go/extractor/vendor/golang.org/x/mod:__subpackages__"],
)

View File

@@ -1,21 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "modfile",
srcs = [
"print.go",
"read.go",
"rule.go",
"work.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/mod/modfile",
importpath = "golang.org/x/mod/modfile",
visibility = ["//visibility:public"],
deps = [
"//go/extractor/vendor/golang.org/x/mod/internal/lazyregexp",
"//go/extractor/vendor/golang.org/x/mod/module",
"//go/extractor/vendor/golang.org/x/mod/semver",
],
)

View File

@@ -225,7 +225,7 @@ func (x *FileSyntax) Cleanup() {
if ww == 0 {
continue
}
if ww == 1 {
if ww == 1 && len(stmt.RParen.Comments.Before) == 0 {
// Collapse block into single line.
line := &Line{
Comments: Comments{

View File

@@ -38,6 +38,7 @@ type File struct {
Module *Module
Go *Go
Toolchain *Toolchain
Godebug []*Godebug
Require []*Require
Exclude []*Exclude
Replace []*Replace
@@ -65,6 +66,13 @@ type Toolchain struct {
Syntax *Line
}
// A Godebug is a single godebug key=value statement.
type Godebug struct {
Key string
Value string
Syntax *Line
}
// An Exclude is a single exclude statement.
type Exclude struct {
Mod module.Version
@@ -289,7 +297,7 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parse
})
}
continue
case "module", "require", "exclude", "replace", "retract":
case "module", "godebug", "require", "exclude", "replace", "retract":
for _, l := range x.Line {
f.add(&errs, x, l, x.Token[0], l.Token, fix, strict)
}
@@ -308,6 +316,9 @@ var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].
// Toolchains must be named beginning with `go1`,
// like "go1.20.3" or "go1.20.3-gccgo". As a special case, "default" is also permitted.
// Note that this regexp is a much looser condition than go/version.IsValid,
// for forward compatibility.
// (This code has to be work to identify new toolchains even if we tweak the syntax in the future.)
var ToolchainRE = lazyregexp.New(`^default$|^go1($|\.)`)
func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
@@ -383,8 +394,8 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a
if len(args) != 1 {
errorf("toolchain directive expects exactly one argument")
return
} else if strict && !ToolchainRE.MatchString(args[0]) {
errorf("invalid toolchain version '%s': must match format go1.23.0 or local", args[0])
} else if !ToolchainRE.MatchString(args[0]) {
errorf("invalid toolchain version '%s': must match format go1.23.0 or default", args[0])
return
}
f.Toolchain = &Toolchain{Syntax: line}
@@ -411,6 +422,22 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a
}
f.Module.Mod = module.Version{Path: s}
case "godebug":
if len(args) != 1 || strings.ContainsAny(args[0], "\"`',") {
errorf("usage: godebug key=value")
return
}
key, value, ok := strings.Cut(args[0], "=")
if !ok {
errorf("usage: godebug key=value")
return
}
f.Godebug = append(f.Godebug, &Godebug{
Key: key,
Value: value,
Syntax: line,
})
case "require", "exclude":
if len(args) != 2 {
errorf("usage: %s module/path v1.2.3", verb)
@@ -630,7 +657,7 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string,
errorf("go directive expects exactly one argument")
return
} else if !GoVersionRE.MatchString(args[0]) {
errorf("invalid go version '%s': must match format 1.23", args[0])
errorf("invalid go version '%s': must match format 1.23.0", args[0])
return
}
@@ -646,13 +673,29 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string,
errorf("toolchain directive expects exactly one argument")
return
} else if !ToolchainRE.MatchString(args[0]) {
errorf("invalid toolchain version '%s': must match format go1.23 or local", args[0])
errorf("invalid toolchain version '%s': must match format go1.23.0 or default", args[0])
return
}
f.Toolchain = &Toolchain{Syntax: line}
f.Toolchain.Name = args[0]
case "godebug":
if len(args) != 1 || strings.ContainsAny(args[0], "\"`',") {
errorf("usage: godebug key=value")
return
}
key, value, ok := strings.Cut(args[0], "=")
if !ok {
errorf("usage: godebug key=value")
return
}
f.Godebug = append(f.Godebug, &Godebug{
Key: key,
Value: value,
Syntax: line,
})
case "use":
if len(args) != 1 {
errorf("usage: %s local/dir", verb)
@@ -928,6 +971,15 @@ func (f *File) Format() ([]byte, error) {
// Cleanup cleans out all the cleared entries.
func (f *File) Cleanup() {
w := 0
for _, g := range f.Godebug {
if g.Key != "" {
f.Godebug[w] = g
w++
}
}
f.Godebug = f.Godebug[:w]
w = 0
for _, r := range f.Require {
if r.Mod.Path != "" {
f.Require[w] = r
@@ -974,6 +1026,8 @@ func (f *File) AddGoStmt(version string) error {
var hint Expr
if f.Module != nil && f.Module.Syntax != nil {
hint = f.Module.Syntax
} else if f.Syntax == nil {
f.Syntax = new(FileSyntax)
}
f.Go = &Go{
Version: version,
@@ -1024,6 +1078,45 @@ func (f *File) AddToolchainStmt(name string) error {
return nil
}
// AddGodebug sets the first godebug line for key to value,
// preserving any existing comments for that line and removing all
// other godebug lines for key.
//
// If no line currently exists for key, AddGodebug adds a new line
// at the end of the last godebug block.
func (f *File) AddGodebug(key, value string) error {
need := true
for _, g := range f.Godebug {
if g.Key == key {
if need {
g.Value = value
f.Syntax.updateLine(g.Syntax, "godebug", key+"="+value)
need = false
} else {
g.Syntax.markRemoved()
*g = Godebug{}
}
}
}
if need {
f.addNewGodebug(key, value)
}
return nil
}
// addNewGodebug adds a new godebug key=value line at the end
// of the last godebug block, regardless of any existing godebug lines for key.
func (f *File) addNewGodebug(key, value string) {
line := f.Syntax.addLine(nil, "godebug", key+"="+value)
g := &Godebug{
Key: key,
Value: value,
Syntax: line,
}
f.Godebug = append(f.Godebug, g)
}
// AddRequire sets the first require line for path to version vers,
// preserving any existing comments for that line and removing all
// other lines for path.
@@ -1331,6 +1424,16 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
f.SortBlocks()
}
func (f *File) DropGodebug(key string) error {
for _, g := range f.Godebug {
if g.Key == key {
g.Syntax.markRemoved()
*g = Godebug{}
}
}
return nil
}
func (f *File) DropRequire(path string) error {
for _, r := range f.Require {
if r.Mod.Path == path {

View File

@@ -14,6 +14,7 @@ import (
type WorkFile struct {
Go *Go
Toolchain *Toolchain
Godebug []*Godebug
Use []*Use
Replace []*Replace
@@ -68,7 +69,7 @@ func ParseWork(file string, data []byte, fix VersionFixer) (*WorkFile, error) {
Err: fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")),
})
continue
case "use", "replace":
case "godebug", "use", "replace":
for _, l := range x.Line {
f.add(&errs, l, x.Token[0], l.Token, fix)
}
@@ -184,6 +185,55 @@ func (f *WorkFile) DropToolchainStmt() {
}
}
// AddGodebug sets the first godebug line for key to value,
// preserving any existing comments for that line and removing all
// other godebug lines for key.
//
// If no line currently exists for key, AddGodebug adds a new line
// at the end of the last godebug block.
func (f *WorkFile) AddGodebug(key, value string) error {
need := true
for _, g := range f.Godebug {
if g.Key == key {
if need {
g.Value = value
f.Syntax.updateLine(g.Syntax, "godebug", key+"="+value)
need = false
} else {
g.Syntax.markRemoved()
*g = Godebug{}
}
}
}
if need {
f.addNewGodebug(key, value)
}
return nil
}
// addNewGodebug adds a new godebug key=value line at the end
// of the last godebug block, regardless of any existing godebug lines for key.
func (f *WorkFile) addNewGodebug(key, value string) {
line := f.Syntax.addLine(nil, "godebug", key+"="+value)
g := &Godebug{
Key: key,
Value: value,
Syntax: line,
}
f.Godebug = append(f.Godebug, g)
}
func (f *WorkFile) DropGodebug(key string) error {
for _, g := range f.Godebug {
if g.Key == key {
g.Syntax.markRemoved()
*g = Godebug{}
}
}
return nil
}
func (f *WorkFile) AddUse(diskPath, modulePath string) error {
need := true
for _, d := range f.Use {

View File

@@ -1,18 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "module",
srcs = [
"module.go",
"pseudo.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/mod/module",
importpath = "golang.org/x/mod/module",
visibility = ["//visibility:public"],
deps = [
"//go/extractor/vendor/golang.org/x/mod/internal/lazyregexp",
"//go/extractor/vendor/golang.org/x/mod/semver",
],
)

View File

@@ -506,6 +506,7 @@ var badWindowsNames = []string{
"PRN",
"AUX",
"NUL",
"COM0",
"COM1",
"COM2",
"COM3",
@@ -515,6 +516,7 @@ var badWindowsNames = []string{
"COM7",
"COM8",
"COM9",
"LPT0",
"LPT1",
"LPT2",
"LPT3",

View File

@@ -1,11 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "semver",
srcs = ["semver.go"],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/mod/semver",
importpath = "golang.org/x/mod/semver",
visibility = ["//visibility:public"],
)

27
go/extractor/vendor/golang.org/x/sync/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
go/extractor/vendor/golang.org/x/sync/PATENTS generated vendored Normal file
View File

@@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

View File

@@ -0,0 +1,135 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package errgroup provides synchronization, error propagation, and Context
// cancelation for groups of goroutines working on subtasks of a common task.
//
// [errgroup.Group] is related to [sync.WaitGroup] but adds handling of tasks
// returning errors.
package errgroup
import (
"context"
"fmt"
"sync"
)
type token struct{}
// A Group is a collection of goroutines working on subtasks that are part of
// the same overall task.
//
// A zero Group is valid, has no limit on the number of active goroutines,
// and does not cancel on error.
type Group struct {
cancel func(error)
wg sync.WaitGroup
sem chan token
errOnce sync.Once
err error
}
func (g *Group) done() {
if g.sem != nil {
<-g.sem
}
g.wg.Done()
}
// WithContext returns a new Group and an associated Context derived from ctx.
//
// The derived Context is canceled the first time a function passed to Go
// returns a non-nil error or the first time Wait returns, whichever occurs
// first.
func WithContext(ctx context.Context) (*Group, context.Context) {
ctx, cancel := withCancelCause(ctx)
return &Group{cancel: cancel}, ctx
}
// Wait blocks until all function calls from the Go method have returned, then
// returns the first non-nil error (if any) from them.
func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel(g.err)
}
return g.err
}
// Go calls the given function in a new goroutine.
// It blocks until the new goroutine can be added without the number of
// active goroutines in the group exceeding the configured limit.
//
// The first call to return a non-nil error cancels the group's context, if the
// group was created by calling WithContext. The error will be returned by Wait.
func (g *Group) Go(f func() error) {
if g.sem != nil {
g.sem <- token{}
}
g.wg.Add(1)
go func() {
defer g.done()
if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel(g.err)
}
})
}
}()
}
// TryGo calls the given function in a new goroutine only if the number of
// active goroutines in the group is currently below the configured limit.
//
// The return value reports whether the goroutine was started.
func (g *Group) TryGo(f func() error) bool {
if g.sem != nil {
select {
case g.sem <- token{}:
// Note: this allows barging iff channels in general allow barging.
default:
return false
}
}
g.wg.Add(1)
go func() {
defer g.done()
if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel(g.err)
}
})
}
}()
return true
}
// SetLimit limits the number of active goroutines in this group to at most n.
// A negative value indicates no limit.
//
// Any subsequent call to the Go method will block until it can add an active
// goroutine without exceeding the configured limit.
//
// The limit must not be modified while any goroutines in the group are active.
func (g *Group) SetLimit(n int) {
if n < 0 {
g.sem = nil
return
}
if len(g.sem) != 0 {
panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem)))
}
g.sem = make(chan token, n)
}

View File

@@ -0,0 +1,13 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.20
package errgroup
import "context"
func withCancelCause(parent context.Context) (context.Context, func(error)) {
return context.WithCancelCause(parent)
}

View File

@@ -0,0 +1,14 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.20
package errgroup
import "context"
func withCancelCause(parent context.Context) (context.Context, func(error)) {
ctx, cancel := context.WithCancel(parent)
return ctx, func(error) { cancel() }
}

View File

@@ -1,15 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "gcexportdata",
srcs = [
"gcexportdata.go",
"importer.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/go/gcexportdata",
importpath = "golang.org/x/tools/go/gcexportdata",
visibility = ["//visibility:public"],
deps = ["//go/extractor/vendor/golang.org/x/tools/internal/gcimporter"],
)

View File

@@ -47,7 +47,7 @@ import (
func Find(importPath, srcDir string) (filename, path string) {
cmd := exec.Command("go", "list", "-json", "-export", "--", importPath)
cmd.Dir = srcDir
out, err := cmd.CombinedOutput()
out, err := cmd.Output()
if err != nil {
return "", ""
}

View File

@@ -1,12 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "packagesdriver",
srcs = ["sizes.go"],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/go/internal/packagesdriver",
importpath = "golang.org/x/tools/go/internal/packagesdriver",
visibility = ["//go/extractor/vendor/golang.org/x/tools/go:__subpackages__"],
deps = ["//go/extractor/vendor/golang.org/x/tools/internal/gocommand"],
)

View File

@@ -13,6 +13,7 @@ import (
"golang.org/x/tools/internal/gocommand"
)
// TODO(adonovan): move back into go/packages.
func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) {
inv.Verb = "list"
inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"}

View File

@@ -1,27 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "packages",
srcs = [
"doc.go",
"external.go",
"golist.go",
"golist_overlay.go",
"loadmode_string.go",
"packages.go",
"visit.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/go/packages",
importpath = "golang.org/x/tools/go/packages",
visibility = ["//visibility:public"],
deps = [
"//go/extractor/vendor/golang.org/x/tools/go/gcexportdata",
"//go/extractor/vendor/golang.org/x/tools/go/internal/packagesdriver",
"//go/extractor/vendor/golang.org/x/tools/internal/gocommand",
"//go/extractor/vendor/golang.org/x/tools/internal/packagesinternal",
"//go/extractor/vendor/golang.org/x/tools/internal/typesinternal",
"//go/extractor/vendor/golang.org/x/tools/internal/versions",
],
)

View File

@@ -198,14 +198,6 @@ Instead, ssadump no longer requests the runtime package,
but seeks it among the dependencies of the user-specified packages,
and emits an error if it is not found.
Overlays: The Overlay field in the Config allows providing alternate contents
for Go source files, by providing a mapping from file path to contents.
go/packages will pull in new imports added in overlay files when go/packages
is run in LoadImports mode or greater.
Overlay support for the go list driver isn't complete yet: if the file doesn't
exist on disk, it will only be recognized in an overlay if it is a non-test file
and the package would be reported even without the overlay.
Questions & Tasks
- Add GOARCH/GOOS?

View File

@@ -34,8 +34,8 @@ type DriverRequest struct {
// Tests specifies whether the patterns should also return test packages.
Tests bool `json:"tests"`
// Overlay maps file paths (relative to the driver's working directory) to the byte contents
// of overlay files.
// Overlay maps file paths (relative to the driver's working directory)
// to the contents of overlay files (see Config.Overlay).
Overlay map[string][]byte `json:"overlay"`
}
@@ -119,7 +119,19 @@ func findExternalDriver(cfg *Config) driver {
stderr := new(bytes.Buffer)
cmd := exec.CommandContext(cfg.Context, tool, words...)
cmd.Dir = cfg.Dir
cmd.Env = cfg.Env
// The cwd gets resolved to the real path. On Darwin, where
// /tmp is a symlink, this breaks anything that expects the
// working directory to keep the original path, including the
// go command when dealing with modules.
//
// os.Getwd stdlib has a special feature where if the
// cwd and the PWD are the same node then it trusts
// the PWD, so by setting it in the env for the child
// process we fix up all the paths returned by the go
// command.
//
// (See similar trick in Invocation.run in ../../internal/gocommand/invoke.go)
cmd.Env = append(slicesClip(cfg.Env), "PWD="+cfg.Dir)
cmd.Stdin = bytes.NewReader(req)
cmd.Stdout = buf
cmd.Stderr = stderr
@@ -138,3 +150,7 @@ func findExternalDriver(cfg *Config) driver {
return &response, nil
}
}
// slicesClip removes unused capacity from the slice, returning s[:len(s):len(s)].
// TODO(adonovan): use go1.21 slices.Clip.
func slicesClip[S ~[]E, E any](s S) S { return s[:len(s):len(s)] }

View File

@@ -841,6 +841,7 @@ func (state *golistState) cfgInvocation() gocommand.Invocation {
Env: cfg.Env,
Logf: cfg.Logf,
WorkingDir: cfg.Dir,
Overlay: cfg.goListOverlayFile,
}
}
@@ -849,26 +850,6 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
cfg := state.cfg
inv := state.cfgInvocation()
// For Go versions 1.16 and above, `go list` accepts overlays directly via
// the -overlay flag. Set it, if it's available.
//
// The check for "list" is not necessarily required, but we should avoid
// getting the go version if possible.
if verb == "list" {
goVersion, err := state.getGoVersion()
if err != nil {
return nil, err
}
if goVersion >= 16 {
filename, cleanup, err := state.writeOverlays()
if err != nil {
return nil, err
}
defer cleanup()
inv.Overlay = filename
}
}
inv.Verb = verb
inv.Args = args
gocmdRunner := cfg.gocmdRunner
@@ -1015,67 +996,6 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
return stdout, nil
}
// OverlayJSON is the format overlay files are expected to be in.
// The Replace map maps from overlaid paths to replacement paths:
// the Go command will forward all reads trying to open
// each overlaid path to its replacement path, or consider the overlaid
// path not to exist if the replacement path is empty.
//
// From golang/go#39958.
type OverlayJSON struct {
Replace map[string]string `json:"replace,omitempty"`
}
// writeOverlays writes out files for go list's -overlay flag, as described
// above.
func (state *golistState) writeOverlays() (filename string, cleanup func(), err error) {
// Do nothing if there are no overlays in the config.
if len(state.cfg.Overlay) == 0 {
return "", func() {}, nil
}
dir, err := os.MkdirTemp("", "gopackages-*")
if err != nil {
return "", nil, err
}
// The caller must clean up this directory, unless this function returns an
// error.
cleanup = func() {
os.RemoveAll(dir)
}
defer func() {
if err != nil {
cleanup()
}
}()
overlays := map[string]string{}
for k, v := range state.cfg.Overlay {
// Create a unique filename for the overlaid files, to avoid
// creating nested directories.
noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "")
f, err := os.CreateTemp(dir, fmt.Sprintf("*-%s", noSeparator))
if err != nil {
return "", func() {}, err
}
if _, err := f.Write(v); err != nil {
return "", func() {}, err
}
if err := f.Close(); err != nil {
return "", func() {}, err
}
overlays[k] = f.Name()
}
b, err := json.Marshal(OverlayJSON{Replace: overlays})
if err != nil {
return "", func() {}, err
}
// Write out the overlay file that contains the filepath mappings.
filename = filepath.Join(dir, "overlay.json")
if err := os.WriteFile(filename, b, 0665); err != nil {
return "", func() {}, err
}
return filename, cleanup, nil
}
func containsGoFile(s []string) bool {
for _, f := range s {
if strings.HasSuffix(f, ".go") {

View File

@@ -9,6 +9,7 @@ package packages
import (
"context"
"encoding/json"
"errors"
"fmt"
"go/ast"
"go/parser"
@@ -24,6 +25,8 @@ import (
"sync"
"time"
"golang.org/x/sync/errgroup"
"golang.org/x/tools/go/gcexportdata"
"golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/packagesinternal"
@@ -34,10 +37,20 @@ import (
// A LoadMode controls the amount of detail to return when loading.
// The bits below can be combined to specify which fields should be
// filled in the result packages.
//
// The zero value is a special case, equivalent to combining
// the NeedName, NeedFiles, and NeedCompiledGoFiles bits.
//
// ID and Errors (if present) will always be filled.
// Load may return more information than requested.
// [Load] may return more information than requested.
//
// Unfortunately there are a number of open bugs related to
// interactions among the LoadMode bits:
// - https://github.com/golang/go/issues/48226
// - https://github.com/golang/go/issues/56633
// - https://github.com/golang/go/issues/56677
// - https://github.com/golang/go/issues/58726
// - https://github.com/golang/go/issues/63517
type LoadMode int
const (
@@ -120,15 +133,21 @@ const (
// A Config specifies details about how packages should be loaded.
// The zero value is a valid configuration.
//
// Calls to Load do not modify this struct.
//
// TODO(adonovan): #67702: this is currently false: in fact,
// calls to [Load] do not modify the public fields of this struct, but
// may modify hidden fields, so concurrent calls to [Load] must not
// use the same Config. But perhaps we should reestablish the
// documented invariant.
type Config struct {
// Mode controls the level of information returned for each package.
Mode LoadMode
// Context specifies the context for the load operation.
// If the context is cancelled, the loader may stop early
// and return an ErrCancelled error.
// If Context is nil, the load cannot be cancelled.
// Cancelling the context may cause [Load] to abort and
// return an error.
Context context.Context
// Logf is the logger for the config.
@@ -197,13 +216,23 @@ type Config struct {
// setting Tests may have no effect.
Tests bool
// Overlay provides a mapping of absolute file paths to file contents.
// If the file with the given path already exists, the parser will use the
// alternative file contents provided by the map.
// Overlay is a mapping from absolute file paths to file contents.
//
// Overlays provide incomplete support for when a given file doesn't
// already exist on disk. See the package doc above for more details.
// For each map entry, [Load] uses the alternative file
// contents provided by the overlay mapping instead of reading
// from the file system. This mechanism can be used to enable
// editor-integrated tools to correctly analyze the contents
// of modified but unsaved buffers, for example.
//
// The overlay mapping is passed to the build system's driver
// (see "The driver protocol") so that it too can report
// consistent package metadata about unsaved files. However,
// drivers may vary in their level of support for overlays.
Overlay map[string][]byte
// goListOverlayFile is the JSON file that encodes the Overlay
// mapping, used by 'go list -overlay=...'
goListOverlayFile string
}
// Load loads and returns the Go packages named by the given patterns.
@@ -211,8 +240,22 @@ type Config struct {
// Config specifies loading options;
// nil behaves the same as an empty Config.
//
// Load returns an error if any of the patterns was invalid
// as defined by the underlying build system.
// The [Config.Mode] field is a set of bits that determine what kinds
// of information should be computed and returned. Modes that require
// more information tend to be slower. See [LoadMode] for details
// and important caveats. Its zero value is equivalent to
// NeedName | NeedFiles | NeedCompiledGoFiles.
//
// Each call to Load returns a new set of [Package] instances.
// The Packages and their Imports form a directed acyclic graph.
//
// If the [NeedTypes] mode flag was set, each call to Load uses a new
// [types.Importer], so [types.Object] and [types.Type] values from
// different calls to Load must not be mixed as they will have
// inconsistent notions of type identity.
//
// If any of the patterns was invalid as defined by the
// underlying build system, Load returns an error.
// It may return an empty list of packages without an error,
// for instance for an empty expansion of a valid wildcard.
// Errors associated with a particular package are recorded in the
@@ -255,8 +298,27 @@ func Load(cfg *Config, patterns ...string) ([]*Package, error) {
// defaultDriver will fall back to the go list driver.
// The boolean result indicates that an external driver handled the request.
func defaultDriver(cfg *Config, patterns ...string) (*DriverResponse, bool, error) {
const (
// windowsArgMax specifies the maximum command line length for
// the Windows' CreateProcess function.
windowsArgMax = 32767
// maxEnvSize is a very rough estimation of the maximum environment
// size of a user.
maxEnvSize = 16384
// safeArgMax specifies the maximum safe command line length to use
// by the underlying driver excl. the environment. We choose the Windows'
// ARG_MAX as the starting point because it's one of the lowest ARG_MAX
// constants out of the different supported platforms,
// e.g., https://www.in-ulm.de/~mascheck/various/argmax/#results.
safeArgMax = windowsArgMax - maxEnvSize
)
chunks, err := splitIntoChunks(patterns, safeArgMax)
if err != nil {
return nil, false, err
}
if driver := findExternalDriver(cfg); driver != nil {
response, err := driver(cfg, patterns...)
response, err := callDriverOnChunks(driver, cfg, chunks)
if err != nil {
return nil, false, err
} else if !response.NotHandled {
@@ -265,14 +327,99 @@ func defaultDriver(cfg *Config, patterns ...string) (*DriverResponse, bool, erro
// (fall through)
}
response, err := goListDriver(cfg, patterns...)
// go list fallback
//
// Write overlays once, as there are many calls
// to 'go list' (one per chunk plus others too).
overlay, cleanupOverlay, err := gocommand.WriteOverlays(cfg.Overlay)
if err != nil {
return nil, false, err
}
return response, false, nil
defer cleanupOverlay()
cfg.goListOverlayFile = overlay
response, err := callDriverOnChunks(goListDriver, cfg, chunks)
if err != nil {
return nil, false, err
}
return response, false, err
}
// splitIntoChunks chunks the slice so that the total number of characters
// in a chunk is no longer than argMax.
func splitIntoChunks(patterns []string, argMax int) ([][]string, error) {
if argMax <= 0 {
return nil, errors.New("failed to split patterns into chunks, negative safe argMax value")
}
var chunks [][]string
charsInChunk := 0
nextChunkStart := 0
for i, v := range patterns {
vChars := len(v)
if vChars > argMax {
// a single pattern is longer than the maximum safe ARG_MAX, hardly should happen
return nil, errors.New("failed to split patterns into chunks, a pattern is too long")
}
charsInChunk += vChars + 1 // +1 is for a whitespace between patterns that has to be counted too
if charsInChunk > argMax {
chunks = append(chunks, patterns[nextChunkStart:i])
nextChunkStart = i
charsInChunk = vChars
}
}
// add the last chunk
if nextChunkStart < len(patterns) {
chunks = append(chunks, patterns[nextChunkStart:])
}
return chunks, nil
}
func callDriverOnChunks(driver driver, cfg *Config, chunks [][]string) (*DriverResponse, error) {
if len(chunks) == 0 {
return driver(cfg)
}
responses := make([]*DriverResponse, len(chunks))
errNotHandled := errors.New("driver returned NotHandled")
var g errgroup.Group
for i, chunk := range chunks {
i := i
chunk := chunk
g.Go(func() (err error) {
responses[i], err = driver(cfg, chunk...)
if responses[i] != nil && responses[i].NotHandled {
err = errNotHandled
}
return err
})
}
if err := g.Wait(); err != nil {
if errors.Is(err, errNotHandled) {
return &DriverResponse{NotHandled: true}, nil
}
return nil, err
}
return mergeResponses(responses...), nil
}
func mergeResponses(responses ...*DriverResponse) *DriverResponse {
if len(responses) == 0 {
return nil
}
response := newDeduper()
response.dr.NotHandled = false
response.dr.Compiler = responses[0].Compiler
response.dr.Arch = responses[0].Arch
response.dr.GoVersion = responses[0].GoVersion
for _, v := range responses {
response.addAll(v)
}
return response.dr
}
// A Package describes a loaded Go package.
//
// It also defines part of the JSON schema of [DriverResponse].
// See the package documentation for an overview.
type Package struct {
// ID is a unique identifier for a package,
// in a syntax provided by the underlying build system.
@@ -331,19 +478,30 @@ type Package struct {
// to corresponding loaded Packages.
Imports map[string]*Package
// Module is the module information for the package if it exists.
//
// Note: it may be missing for std and cmd; see Go issue #65816.
Module *Module
// -- The following fields are not part of the driver JSON schema. --
// Types provides type information for the package.
// The NeedTypes LoadMode bit sets this field for packages matching the
// patterns; type information for dependencies may be missing or incomplete,
// unless NeedDeps and NeedImports are also set.
Types *types.Package
//
// Each call to [Load] returns a consistent set of type
// symbols, as defined by the comment at [types.Identical].
// Avoid mixing type information from two or more calls to [Load].
Types *types.Package `json:"-"`
// Fset provides position information for Types, TypesInfo, and Syntax.
// It is set only when Types is set.
Fset *token.FileSet
Fset *token.FileSet `json:"-"`
// IllTyped indicates whether the package or any dependency contains errors.
// It is set only when Types is set.
IllTyped bool
IllTyped bool `json:"-"`
// Syntax is the package's syntax trees, for the files listed in CompiledGoFiles.
//
@@ -353,26 +511,28 @@ type Package struct {
//
// Syntax is kept in the same order as CompiledGoFiles, with the caveat that nils are
// removed. If parsing returned nil, Syntax may be shorter than CompiledGoFiles.
Syntax []*ast.File
Syntax []*ast.File `json:"-"`
// TypesInfo provides type information about the package's syntax trees.
// It is set only when Syntax is set.
TypesInfo *types.Info
TypesInfo *types.Info `json:"-"`
// TypesSizes provides the effective size function for types in TypesInfo.
TypesSizes types.Sizes
TypesSizes types.Sizes `json:"-"`
// -- internal --
// forTest is the package under test, if any.
forTest string
// depsErrors is the DepsErrors field from the go list response, if any.
depsErrors []*packagesinternal.PackageError
// module is the module information for the package if it exists.
Module *Module
}
// Module provides module information for a package.
//
// It also defines part of the JSON schema of [DriverResponse].
// See the package documentation for an overview.
type Module struct {
Path string // module path
Version string // module version
@@ -505,6 +665,7 @@ func (p *Package) UnmarshalJSON(b []byte) error {
OtherFiles: flat.OtherFiles,
EmbedFiles: flat.EmbedFiles,
EmbedPatterns: flat.EmbedPatterns,
IgnoredFiles: flat.IgnoredFiles,
ExportFile: flat.ExportFile,
}
if len(flat.Imports) > 0 {
@@ -761,6 +922,12 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
wg.Wait()
}
// If the context is done, return its error and
// throw out [likely] incomplete packages.
if err := ld.Context.Err(); err != nil {
return nil, err
}
result := make([]*Package, len(initial))
for i, lpkg := range initial {
result[i] = lpkg.Package
@@ -856,6 +1023,14 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
lpkg.Types = types.NewPackage(lpkg.PkgPath, lpkg.Name)
lpkg.Fset = ld.Fset
// Start shutting down if the context is done and do not load
// source or export data files.
// Packages that import this one will have ld.Context.Err() != nil.
// ld.Context.Err() will be returned later by refine.
if ld.Context.Err() != nil {
return
}
// Subtle: we populate all Types fields with an empty Package
// before loading export data so that export data processing
// never has to create a types.Package for an indirect dependency,
@@ -975,6 +1150,13 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
return
}
// Start shutting down if the context is done and do not type check.
// Packages that import this one will have ld.Context.Err() != nil.
// ld.Context.Err() will be returned later by refine.
if ld.Context.Err() != nil {
return
}
lpkg.TypesInfo = &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
@@ -1025,7 +1207,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
Sizes: ld.sizes, // may be nil
}
if lpkg.Module != nil && lpkg.Module.GoVersion != "" {
typesinternal.SetGoVersion(tc, "go"+lpkg.Module.GoVersion)
tc.GoVersion = "go" + lpkg.Module.GoVersion
}
if (ld.Mode & typecheckCgo) != 0 {
if !typesinternal.SetUsesCgo(tc) {
@@ -1036,10 +1218,24 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
return
}
}
types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
typErr := types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
lpkg.importErrors = nil // no longer needed
// In go/types go1.21 and go1.22, Checker.Files failed fast with a
// a "too new" error, without calling tc.Error and without
// proceeding to type-check the package (#66525).
// We rely on the runtimeVersion error to give the suggested remedy.
if typErr != nil && len(lpkg.Errors) == 0 && len(lpkg.Syntax) > 0 {
if msg := typErr.Error(); strings.HasPrefix(msg, "package requires newer Go version") {
appendError(types.Error{
Fset: ld.Fset,
Pos: lpkg.Syntax[0].Package,
Msg: msg,
})
}
}
// If !Cgo, the type-checker uses FakeImportC mode, so
// it doesn't invoke the importer for import "C",
// nor report an error for the import,
@@ -1061,6 +1257,12 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
}
}
// If types.Checker.Files had an error that was unreported,
// make sure to report the unknown error so the package is illTyped.
if typErr != nil && len(lpkg.Errors) == 0 {
appendError(typErr)
}
// Record accumulated errors.
illTyped := len(lpkg.Errors) > 0
if !illTyped {
@@ -1132,11 +1334,6 @@ func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) {
parsed := make([]*ast.File, n)
errors := make([]error, n)
for i, file := range filenames {
if ld.Config.Context.Err() != nil {
parsed[i] = nil
errors[i] = ld.Config.Context.Err()
continue
}
wg.Add(1)
go func(i int, filename string) {
parsed[i], errors[i] = ld.parseFile(filename)

View File

@@ -1,12 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "objectpath",
srcs = ["objectpath.go"],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/go/types/objectpath",
importpath = "golang.org/x/tools/go/types/objectpath",
visibility = ["//visibility:public"],
deps = ["//go/extractor/vendor/golang.org/x/tools/internal/typeparams"],
)

View File

@@ -29,9 +29,12 @@ import (
"strconv"
"strings"
"golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typesinternal"
)
// TODO(adonovan): think about generic aliases.
// A Path is an opaque name that identifies a types.Object
// relative to its package. Conceptually, the name consists of a
// sequence of destructuring operations applied to the package scope
@@ -223,7 +226,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
// Reject obviously non-viable cases.
switch obj := obj.(type) {
case *types.TypeName:
if _, ok := obj.Type().(*types.TypeParam); !ok {
if _, ok := aliases.Unalias(obj.Type()).(*types.TypeParam); !ok {
// With the exception of type parameters, only package-level type names
// have a path.
return "", fmt.Errorf("no path for %v", obj)
@@ -310,7 +313,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
}
// Inspect declared methods of defined types.
if T, ok := o.Type().(*types.Named); ok {
if T, ok := aliases.Unalias(o.Type()).(*types.Named); ok {
path = append(path, opType)
// The method index here is always with respect
// to the underlying go/types data structures,
@@ -391,17 +394,12 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
// of objectpath will only be giving us origin methods, anyway, as referring
// to instantiated methods is usually not useful.
if typeparams.OriginMethod(meth) != meth {
if meth.Origin() != meth {
return "", false
}
recvT := meth.Type().(*types.Signature).Recv().Type()
if ptr, ok := recvT.(*types.Pointer); ok {
recvT = ptr.Elem()
}
named, ok := recvT.(*types.Named)
if !ok {
_, named := typesinternal.ReceiverNamed(meth.Type().(*types.Signature).Recv())
if named == nil {
return "", false
}
@@ -444,6 +442,8 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
// nil, it will be allocated as necessary.
func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte {
switch T := T.(type) {
case *aliases.Alias:
return find(obj, aliases.Unalias(T), path, seen)
case *types.Basic, *types.Named:
// Named types belonging to pkg were handled already,
// so T must belong to another package. No path.
@@ -616,6 +616,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
// Inv: t != nil, obj == nil
t = aliases.Unalias(t)
switch code {
case opElem:
hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map

View File

@@ -0,0 +1,32 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package aliases
import (
"go/token"
"go/types"
)
// Package aliases defines backward compatible shims
// for the types.Alias type representation added in 1.22.
// This defines placeholders for x/tools until 1.26.
// NewAlias creates a new TypeName in Package pkg that
// is an alias for the type rhs.
//
// The enabled parameter determines whether the resulting [TypeName]'s
// type is an [types.Alias]. Its value must be the result of a call to
// [Enabled], which computes the effective value of
// GODEBUG=gotypesalias=... by invoking the type checker. The Enabled
// function is expensive and should be called once per task (e.g.
// package import), not once per call to NewAlias.
func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName {
if enabled {
tname := types.NewTypeName(pos, pkg, name, nil)
newAlias(tname, rhs)
return tname
}
return types.NewTypeName(pos, pkg, name, rhs)
}

View File

@@ -0,0 +1,31 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.22
// +build !go1.22
package aliases
import (
"go/types"
)
// Alias is a placeholder for a go/types.Alias for <=1.21.
// It will never be created by go/types.
type Alias struct{}
func (*Alias) String() string { panic("unreachable") }
func (*Alias) Underlying() types.Type { panic("unreachable") }
func (*Alias) Obj() *types.TypeName { panic("unreachable") }
func Rhs(alias *Alias) types.Type { panic("unreachable") }
// Unalias returns the type t for go <=1.21.
func Unalias(t types.Type) types.Type { return t }
func newAlias(name *types.TypeName, rhs types.Type) *Alias { panic("unreachable") }
// Enabled reports whether [NewAlias] should create [types.Alias] types.
//
// Before go1.22, this function always returns false.
func Enabled() bool { return false }

View File

@@ -0,0 +1,63 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.22
// +build go1.22
package aliases
import (
"go/ast"
"go/parser"
"go/token"
"go/types"
)
// Alias is an alias of types.Alias.
type Alias = types.Alias
// Rhs returns the type on the right-hand side of the alias declaration.
func Rhs(alias *Alias) types.Type {
if alias, ok := any(alias).(interface{ Rhs() types.Type }); ok {
return alias.Rhs() // go1.23+
}
// go1.22's Alias didn't have the Rhs method,
// so Unalias is the best we can do.
return Unalias(alias)
}
// Unalias is a wrapper of types.Unalias.
func Unalias(t types.Type) types.Type { return types.Unalias(t) }
// newAlias is an internal alias around types.NewAlias.
// Direct usage is discouraged as the moment.
// Try to use NewAlias instead.
func newAlias(tname *types.TypeName, rhs types.Type) *Alias {
a := types.NewAlias(tname, rhs)
// TODO(go.dev/issue/65455): Remove kludgy workaround to set a.actual as a side-effect.
Unalias(a)
return a
}
// Enabled reports whether [NewAlias] should create [types.Alias] types.
//
// This function is expensive! Call it sparingly.
func Enabled() bool {
// The only reliable way to compute the answer is to invoke go/types.
// We don't parse the GODEBUG environment variable, because
// (a) it's tricky to do so in a manner that is consistent
// with the godebug package; in particular, a simple
// substring check is not good enough. The value is a
// rightmost-wins list of options. But more importantly:
// (b) it is impossible to detect changes to the effective
// setting caused by os.Setenv("GODEBUG"), as happens in
// many tests. Therefore any attempt to cache the result
// is just incorrect.
fset := token.NewFileSet()
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0)
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
_, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias)
return enabled
}

View File

@@ -1,19 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "event",
srcs = [
"doc.go",
"event.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/event",
importpath = "golang.org/x/tools/internal/event",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
deps = [
"//go/extractor/vendor/golang.org/x/tools/internal/event/core",
"//go/extractor/vendor/golang.org/x/tools/internal/event/keys",
"//go/extractor/vendor/golang.org/x/tools/internal/event/label",
],
)

View File

@@ -1,19 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "core",
srcs = [
"event.go",
"export.go",
"fast.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/event/core",
importpath = "golang.org/x/tools/internal/event/core",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
deps = [
"//go/extractor/vendor/golang.org/x/tools/internal/event/keys",
"//go/extractor/vendor/golang.org/x/tools/internal/event/label",
],
)

View File

@@ -1,16 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "keys",
srcs = [
"keys.go",
"standard.go",
"util.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/event/keys",
importpath = "golang.org/x/tools/internal/event/keys",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
deps = ["//go/extractor/vendor/golang.org/x/tools/internal/event/label"],
)

View File

@@ -1,11 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "label",
srcs = ["label.go"],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/event/label",
importpath = "golang.org/x/tools/internal/event/label",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
)

View File

@@ -1,12 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "tag",
srcs = ["tag.go"],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/event/tag",
importpath = "golang.org/x/tools/internal/event/tag",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
deps = ["//go/extractor/vendor/golang.org/x/tools/internal/event/keys"],
)

View File

@@ -1,59 +0,0 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package tag provides the labels used for telemetry throughout gopls.
package tag
import (
"golang.org/x/tools/internal/event/keys"
)
var (
// create the label keys we use
Method = keys.NewString("method", "")
StatusCode = keys.NewString("status.code", "")
StatusMessage = keys.NewString("status.message", "")
RPCID = keys.NewString("id", "")
RPCDirection = keys.NewString("direction", "")
File = keys.NewString("file", "")
Directory = keys.New("directory", "")
URI = keys.New("URI", "")
Package = keys.NewString("package", "") // sorted comma-separated list of Package IDs
PackagePath = keys.NewString("package_path", "")
Query = keys.New("query", "")
Snapshot = keys.NewUInt64("snapshot", "")
Operation = keys.NewString("operation", "")
Position = keys.New("position", "")
Category = keys.NewString("category", "")
PackageCount = keys.NewInt("packages", "")
Files = keys.New("files", "")
Port = keys.NewInt("port", "")
Type = keys.New("type", "")
HoverKind = keys.NewString("hoverkind", "")
NewServer = keys.NewString("new_server", "A new server was added")
EndServer = keys.NewString("end_server", "A server was shut down")
ServerID = keys.NewString("server", "The server ID an event is related to")
Logfile = keys.NewString("logfile", "")
DebugAddress = keys.NewString("debug_address", "")
GoplsPath = keys.NewString("gopls_path", "")
ClientID = keys.NewString("client_id", "")
Level = keys.NewInt("level", "The logging level")
)
var (
// create the stats we measure
Started = keys.NewInt64("started", "Count of started RPCs.")
ReceivedBytes = keys.NewInt64("received_bytes", "Bytes received.") //, unit.Bytes)
SentBytes = keys.NewInt64("sent_bytes", "Bytes sent.") //, unit.Bytes)
Latency = keys.NewFloat64("latency_ms", "Elapsed time in milliseconds") //, unit.Milliseconds)
)
const (
Inbound = "in"
Outbound = "out"
)

View File

@@ -1,29 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "gcimporter",
srcs = [
"bimport.go",
"exportdata.go",
"gcimporter.go",
"iexport.go",
"iimport.go",
"newInterface10.go",
"newInterface11.go",
"support_go117.go",
"support_go118.go",
"unified_no.go",
"ureader_no.go",
"ureader_yes.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/gcimporter",
importpath = "golang.org/x/tools/internal/gcimporter",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
deps = [
"//go/extractor/vendor/golang.org/x/tools/go/types/objectpath",
"//go/extractor/vendor/golang.org/x/tools/internal/pkgbits",
"//go/extractor/vendor/golang.org/x/tools/internal/tokeninternal",
],
)

View File

@@ -259,13 +259,6 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
return
}
func deref(typ types.Type) types.Type {
if p, _ := typ.(*types.Pointer); p != nil {
return p.Elem()
}
return typ
}
type byPath []*types.Package
func (a byPath) Len() int { return len(a) }

View File

@@ -23,6 +23,7 @@ import (
"strings"
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/tokeninternal"
)
@@ -463,7 +464,7 @@ func (p *iexporter) doDecl(obj types.Object) {
switch obj := obj.(type) {
case *types.Var:
w.tag('V')
w.tag(varTag)
w.pos(obj.Pos())
w.typ(obj.Type(), obj.Pkg())
@@ -481,9 +482,9 @@ func (p *iexporter) doDecl(obj types.Object) {
// Function.
if sig.TypeParams().Len() == 0 {
w.tag('F')
w.tag(funcTag)
} else {
w.tag('G')
w.tag(genericFuncTag)
}
w.pos(obj.Pos())
// The tparam list of the function type is the declaration of the type
@@ -499,20 +500,20 @@ func (p *iexporter) doDecl(obj types.Object) {
w.signature(sig)
case *types.Const:
w.tag('C')
w.tag(constTag)
w.pos(obj.Pos())
w.value(obj.Type(), obj.Val())
case *types.TypeName:
t := obj.Type()
if tparam, ok := t.(*types.TypeParam); ok {
w.tag('P')
if tparam, ok := aliases.Unalias(t).(*types.TypeParam); ok {
w.tag(typeParamTag)
w.pos(obj.Pos())
constraint := tparam.Constraint()
if p.version >= iexportVersionGo1_18 {
implicit := false
if iface, _ := constraint.(*types.Interface); iface != nil {
if iface, _ := aliases.Unalias(constraint).(*types.Interface); iface != nil {
implicit = iface.IsImplicit()
}
w.bool(implicit)
@@ -522,8 +523,13 @@ func (p *iexporter) doDecl(obj types.Object) {
}
if obj.IsAlias() {
w.tag('A')
w.tag(aliasTag)
w.pos(obj.Pos())
if alias, ok := t.(*aliases.Alias); ok {
// Preserve materialized aliases,
// even of non-exported types.
t = aliases.Rhs(alias)
}
w.typ(t, obj.Pkg())
break
}
@@ -535,9 +541,9 @@ func (p *iexporter) doDecl(obj types.Object) {
}
if named.TypeParams().Len() == 0 {
w.tag('T')
w.tag(typeTag)
} else {
w.tag('U')
w.tag(genericTypeTag)
}
w.pos(obj.Pos())
@@ -547,7 +553,7 @@ func (p *iexporter) doDecl(obj types.Object) {
w.tparamList(obj.Name(), named.TypeParams(), obj.Pkg())
}
underlying := obj.Type().Underlying()
underlying := named.Underlying()
w.typ(underlying, obj.Pkg())
if types.IsInterface(t) {
@@ -738,6 +744,11 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
}()
}
switch t := t.(type) {
case *aliases.Alias:
// TODO(adonovan): support parameterized aliases, following *types.Named.
w.startType(aliasType)
w.qualifiedType(t.Obj())
case *types.Named:
if targs := t.TypeArgs(); targs.Len() > 0 {
w.startType(instanceType)
@@ -843,7 +854,7 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
for i := 0; i < n; i++ {
ft := t.EmbeddedType(i)
tPkg := pkg
if named, _ := ft.(*types.Named); named != nil {
if named, _ := aliases.Unalias(ft).(*types.Named); named != nil {
w.pos(named.Obj().Pos())
} else {
w.pos(token.NoPos)

View File

@@ -22,6 +22,8 @@ import (
"strings"
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typesinternal"
)
type intReader struct {
@@ -78,6 +80,20 @@ const (
typeParamType
instanceType
unionType
aliasType
)
// Object tags
const (
varTag = 'V'
funcTag = 'F'
genericFuncTag = 'G'
constTag = 'C'
aliasTag = 'A'
genericAliasTag = 'B'
typeParamTag = 'P'
typeTag = 'T'
genericTypeTag = 'U'
)
// IImportData imports a package from the serialized package data
@@ -194,6 +210,7 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte
p := iimporter{
version: int(version),
ipath: path,
aliases: aliases.Enabled(),
shallow: shallow,
reportf: reportf,
@@ -322,7 +339,7 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte
}
// SetConstraint can't be called if the constraint type is not yet complete.
// When type params are created in the 'P' case of (*importReader).obj(),
// When type params are created in the typeParamTag case of (*importReader).obj(),
// the associated constraint type may not be complete due to recursion.
// Therefore, we defer calling SetConstraint there, and call it here instead
// after all types are complete.
@@ -353,6 +370,7 @@ type iimporter struct {
version int
ipath string
aliases bool
shallow bool
reportf ReportFunc // if non-nil, used to report bugs
@@ -522,7 +540,7 @@ func canReuse(def *types.Named, rhs types.Type) bool {
if def == nil {
return true
}
iface, _ := rhs.(*types.Interface)
iface, _ := aliases.Unalias(rhs).(*types.Interface)
if iface == nil {
return true
}
@@ -544,25 +562,29 @@ func (r *importReader) obj(name string) {
pos := r.pos()
switch tag {
case 'A':
case aliasTag:
typ := r.typ()
// TODO(adonovan): support generic aliases:
// if tag == genericAliasTag {
// tparams := r.tparamList()
// alias.SetTypeParams(tparams)
// }
r.declare(aliases.NewAlias(r.p.aliases, pos, r.currPkg, name, typ))
r.declare(types.NewTypeName(pos, r.currPkg, name, typ))
case 'C':
case constTag:
typ, val := r.value()
r.declare(types.NewConst(pos, r.currPkg, name, typ, val))
case 'F', 'G':
case funcTag, genericFuncTag:
var tparams []*types.TypeParam
if tag == 'G' {
if tag == genericFuncTag {
tparams = r.tparamList()
}
sig := r.signature(nil, nil, tparams)
r.declare(types.NewFunc(pos, r.currPkg, name, sig))
case 'T', 'U':
case typeTag, genericTypeTag:
// Types can be recursive. We need to setup a stub
// declaration before recursing.
obj := types.NewTypeName(pos, r.currPkg, name, nil)
@@ -570,7 +592,7 @@ func (r *importReader) obj(name string) {
// Declare obj before calling r.tparamList, so the new type name is recognized
// if used in the constraint of one of its own typeparams (see #48280).
r.declare(obj)
if tag == 'U' {
if tag == genericTypeTag {
tparams := r.tparamList()
named.SetTypeParams(tparams)
}
@@ -587,14 +609,13 @@ func (r *importReader) obj(name string) {
// If the receiver has any targs, set those as the
// rparams of the method (since those are the
// typeparams being used in the method sig/body).
base := baseType(recv.Type())
assert(base != nil)
targs := base.TypeArgs()
_, recvNamed := typesinternal.ReceiverNamed(recv)
targs := recvNamed.TypeArgs()
var rparams []*types.TypeParam
if targs.Len() > 0 {
rparams = make([]*types.TypeParam, targs.Len())
for i := range rparams {
rparams[i] = targs.At(i).(*types.TypeParam)
rparams[i] = aliases.Unalias(targs.At(i)).(*types.TypeParam)
}
}
msig := r.signature(recv, rparams, nil)
@@ -603,7 +624,7 @@ func (r *importReader) obj(name string) {
}
}
case 'P':
case typeParamTag:
// We need to "declare" a typeparam in order to have a name that
// can be referenced recursively (if needed) in the type param's
// bound.
@@ -624,7 +645,7 @@ func (r *importReader) obj(name string) {
}
constraint := r.typ()
if implicit {
iface, _ := constraint.(*types.Interface)
iface, _ := aliases.Unalias(constraint).(*types.Interface)
if iface == nil {
errorf("non-interface constraint marked implicit")
}
@@ -636,7 +657,7 @@ func (r *importReader) obj(name string) {
// completely set up all types in ImportData.
r.p.later = append(r.p.later, setConstraintArgs{t: t, constraint: constraint})
case 'V':
case varTag:
typ := r.typ()
r.declare(types.NewVar(pos, r.currPkg, name, typ))
@@ -831,7 +852,7 @@ func (r *importReader) typ() types.Type {
}
func isInterface(t types.Type) bool {
_, ok := t.(*types.Interface)
_, ok := aliases.Unalias(t).(*types.Interface)
return ok
}
@@ -853,7 +874,7 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
errorf("unexpected kind tag in %q: %v", r.p.ipath, k)
return nil
case definedType:
case aliasType, definedType:
pkg, name := r.qualifiedIdent()
r.p.doDecl(pkg, name)
return pkg.Scope().Lookup(name).(*types.TypeName).Type()
@@ -1030,7 +1051,7 @@ func (r *importReader) tparamList() []*types.TypeParam {
for i := range xs {
// Note: the standard library importer is tolerant of nil types here,
// though would panic in SetTypeParams.
xs[i] = r.typ().(*types.TypeParam)
xs[i] = aliases.Unalias(r.typ()).(*types.TypeParam)
}
return xs
}
@@ -1077,13 +1098,3 @@ func (r *importReader) byte() byte {
}
return x
}
func baseType(typ types.Type) *types.Named {
// pointer receivers are never types.Named types
if p, _ := typ.(*types.Pointer); p != nil {
typ = p.Elem()
}
// receiver base types are always (possibly generic) types.Named types
n, _ := typ.(*types.Named)
return n
}

View File

@@ -1,16 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
package gcimporter
import "go/types"
const iexportVersion = iexportVersionGo1_11
func additionalPredeclared() []types.Type {
return nil
}

View File

@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
package gcimporter
import "go/types"

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !(go1.18 && goexperiment.unified)
// +build !go1.18 !goexperiment.unified
//go:build !goexperiment.unified
// +build !goexperiment.unified
package gcimporter

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18 && goexperiment.unified
// +build go1.18,goexperiment.unified
//go:build goexperiment.unified
// +build goexperiment.unified
package gcimporter

View File

@@ -1,19 +0,0 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
package gcimporter
import (
"fmt"
"go/token"
"go/types"
)
func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
err = fmt.Errorf("go/tools compiled with a Go version earlier than 1.18 cannot read unified IR export data")
return
}

View File

@@ -4,9 +4,6 @@
// Derived from go/internal/gcimporter/ureader.go
//go:build go1.18
// +build go1.18
package gcimporter
import (
@@ -16,6 +13,7 @@ import (
"sort"
"strings"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/pkgbits"
)
@@ -28,6 +26,7 @@ type pkgReader struct {
ctxt *types.Context
imports map[string]*types.Package // previously imported packages, indexed by path
aliases bool // create types.Alias nodes
// lazily initialized arrays corresponding to the unified IR
// PosBase, Pkg, and Type sections, respectively.
@@ -101,6 +100,7 @@ func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[st
ctxt: ctxt,
imports: imports,
aliases: aliases.Enabled(),
posBases: make([]string, input.NumElems(pkgbits.RelocPosBase)),
pkgs: make([]*types.Package, input.NumElems(pkgbits.RelocPkg)),
@@ -526,7 +526,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
case pkgbits.ObjAlias:
pos := r.pos()
typ := r.typ()
declare(types.NewTypeName(pos, objPkg, objName, typ))
declare(aliases.NewAlias(r.p.aliases, pos, objPkg, objName, typ))
case pkgbits.ObjConst:
pos := r.pos()
@@ -553,7 +553,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
// If the underlying type is an interface, we need to
// duplicate its methods so we can replace the receiver
// parameter's type (#49906).
if iface, ok := underlying.(*types.Interface); ok && iface.NumExplicitMethods() != 0 {
if iface, ok := aliases.Unalias(underlying).(*types.Interface); ok && iface.NumExplicitMethods() != 0 {
methods := make([]*types.Func, iface.NumExplicitMethods())
for i := range methods {
fn := iface.ExplicitMethod(i)

View File

@@ -1,22 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "gocommand",
srcs = [
"invoke.go",
"vendor.go",
"version.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/gocommand",
importpath = "golang.org/x/tools/internal/gocommand",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
deps = [
"//go/extractor/vendor/golang.org/x/mod/semver",
"//go/extractor/vendor/golang.org/x/tools/internal/event",
"//go/extractor/vendor/golang.org/x/tools/internal/event/keys",
"//go/extractor/vendor/golang.org/x/tools/internal/event/label",
"//go/extractor/vendor/golang.org/x/tools/internal/event/tag",
],
)

View File

@@ -8,12 +8,14 @@ package gocommand
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"reflect"
"regexp"
"runtime"
@@ -25,7 +27,6 @@ import (
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/keys"
"golang.org/x/tools/internal/event/label"
"golang.org/x/tools/internal/event/tag"
)
// An Runner will run go command invocations and serialize
@@ -55,11 +56,14 @@ func (runner *Runner) initialize() {
// 1.14: go: updating go.mod: existing contents have changed since last read
var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`)
// verb is an event label for the go command verb.
var verb = keys.NewString("verb", "go command verb")
// event keys for go command invocations
var (
verb = keys.NewString("verb", "go command verb")
directory = keys.NewString("directory", "")
)
func invLabels(inv Invocation) []label.Label {
return []label.Label{verb.Of(inv.Verb), tag.Directory.Of(inv.WorkingDir)}
return []label.Label{verb.Of(inv.Verb), directory.Of(inv.WorkingDir)}
}
// Run is a convenience wrapper around RunRaw.
@@ -158,12 +162,17 @@ type Invocation struct {
BuildFlags []string
// If ModFlag is set, the go command is invoked with -mod=ModFlag.
// TODO(rfindley): remove, in favor of Args.
ModFlag string
// If ModFile is set, the go command is invoked with -modfile=ModFile.
// TODO(rfindley): remove, in favor of Args.
ModFile string
// If Overlay is set, the go command is invoked with -overlay=Overlay.
// Overlay is the name of the JSON overlay file that describes
// unsaved editor buffers; see [WriteOverlays].
// If set, the go command is invoked with -overlay=Overlay.
// TODO(rfindley): remove, in favor of Args.
Overlay string
// If CleanEnv is set, the invocation will run only with the environment
@@ -250,12 +259,15 @@ func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
waitDelay.Set(reflect.ValueOf(30 * time.Second))
}
// On darwin the cwd gets resolved to the real path, which breaks anything that
// expects the working directory to keep the original path, including the
// The cwd gets resolved to the real path. On Darwin, where
// /tmp is a symlink, this breaks anything that expects the
// working directory to keep the original path, including the
// go command when dealing with modules.
// The Go stdlib has a special feature where if the cwd and the PWD are the
// same node then it trusts the PWD, so by setting it in the env for the child
// process we fix up all the paths returned by the go command.
//
// os.Getwd has a special feature where if the cwd and the PWD
// are the same node then it trusts the PWD, so by setting it
// in the env for the child process we fix up all the paths
// returned by the go command.
if !i.CleanEnv {
cmd.Env = os.Environ()
}
@@ -346,6 +358,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
}
}
startTime := time.Now()
err = cmd.Start()
if stdoutW != nil {
// The child process has inherited the pipe file,
@@ -372,7 +385,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
case err := <-resChan:
return err
case <-timer.C:
HandleHangingGoCommand(cmd.Process)
HandleHangingGoCommand(startTime, cmd)
case <-ctx.Done():
}
} else {
@@ -406,7 +419,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
return <-resChan
}
func HandleHangingGoCommand(proc *os.Process) {
func HandleHangingGoCommand(start time.Time, cmd *exec.Cmd) {
switch runtime.GOOS {
case "linux", "darwin", "freebsd", "netbsd":
fmt.Fprintln(os.Stderr, `DETECTED A HANGING GO COMMAND
@@ -439,7 +452,7 @@ See golang/go#54461 for more details.`)
panic(fmt.Sprintf("running %s: %v", listFiles, err))
}
}
panic(fmt.Sprintf("detected hanging go command (pid %d): see golang/go#54461 for more details", proc.Pid))
panic(fmt.Sprintf("detected hanging go command (golang/go#54461); waited %s\n\tcommand:%s\n\tpid:%d", time.Since(start), cmd, cmd.Process.Pid))
}
func cmdDebugStr(cmd *exec.Cmd) string {
@@ -463,3 +476,73 @@ func cmdDebugStr(cmd *exec.Cmd) string {
}
return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], strings.Join(args, " "))
}
// WriteOverlays writes each value in the overlay (see the Overlay
// field of go/packages.Config) to a temporary file and returns the name
// of a JSON file describing the mapping that is suitable for the "go
// list -overlay" flag.
//
// On success, the caller must call the cleanup function exactly once
// when the files are no longer needed.
func WriteOverlays(overlay map[string][]byte) (filename string, cleanup func(), err error) {
// Do nothing if there are no overlays in the config.
if len(overlay) == 0 {
return "", func() {}, nil
}
dir, err := os.MkdirTemp("", "gocommand-*")
if err != nil {
return "", nil, err
}
// The caller must clean up this directory,
// unless this function returns an error.
// (The cleanup operand of each return
// statement below is ignored.)
defer func() {
cleanup = func() {
os.RemoveAll(dir)
}
if err != nil {
cleanup()
cleanup = nil
}
}()
// Write each map entry to a temporary file.
overlays := make(map[string]string)
for k, v := range overlay {
// Use a unique basename for each file (001-foo.go),
// to avoid creating nested directories.
base := fmt.Sprintf("%d-%s.go", 1+len(overlays), filepath.Base(k))
filename := filepath.Join(dir, base)
err := os.WriteFile(filename, v, 0666)
if err != nil {
return "", nil, err
}
overlays[k] = filename
}
// Write the JSON overlay file that maps logical file names to temp files.
//
// OverlayJSON is the format overlay files are expected to be in.
// The Replace map maps from overlaid paths to replacement paths:
// the Go command will forward all reads trying to open
// each overlaid path to its replacement path, or consider the overlaid
// path not to exist if the replacement path is empty.
//
// From golang/go#39958.
type OverlayJSON struct {
Replace map[string]string `json:"replace,omitempty"`
}
b, err := json.Marshal(OverlayJSON{Replace: overlays})
if err != nil {
return "", nil, err
}
filename = filepath.Join(dir, "overlay.json")
if err := os.WriteFile(filename, b, 0666); err != nil {
return "", nil, err
}
return filename, nil, nil
}

View File

@@ -107,3 +107,57 @@ func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*Modul
}
return mod, lines[4] == "go1.14", nil
}
// WorkspaceVendorEnabled reports whether workspace vendoring is enabled. It takes a *Runner to execute Go commands
// with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields,
// of which only Verb and Args are modified to run the appropriate Go command.
// Inspired by setDefaultBuildMod in modload/init.go
func WorkspaceVendorEnabled(ctx context.Context, inv Invocation, r *Runner) (bool, []*ModuleJSON, error) {
inv.Verb = "env"
inv.Args = []string{"GOWORK"}
stdout, err := r.Run(ctx, inv)
if err != nil {
return false, nil, err
}
goWork := string(bytes.TrimSpace(stdout.Bytes()))
if fi, err := os.Stat(filepath.Join(filepath.Dir(goWork), "vendor")); err == nil && fi.IsDir() {
mainMods, err := getWorkspaceMainModules(ctx, inv, r)
if err != nil {
return false, nil, err
}
return true, mainMods, nil
}
return false, nil, nil
}
// getWorkspaceMainModules gets the main modules' information.
// This is the information needed to figure out if vendoring should be enabled.
func getWorkspaceMainModules(ctx context.Context, inv Invocation, r *Runner) ([]*ModuleJSON, error) {
const format = `{{.Path}}
{{.Dir}}
{{.GoMod}}
{{.GoVersion}}
`
inv.Verb = "list"
inv.Args = []string{"-m", "-f", format}
stdout, err := r.Run(ctx, inv)
if err != nil {
return nil, err
}
lines := strings.Split(strings.TrimSuffix(stdout.String(), "\n"), "\n")
if len(lines) < 4 {
return nil, fmt.Errorf("unexpected stdout: %q", stdout.String())
}
mods := make([]*ModuleJSON, 0, len(lines)/4)
for i := 0; i < len(lines); i += 4 {
mods = append(mods, &ModuleJSON{
Path: lines[i],
Dir: lines[i+1],
GoMod: lines[i+2],
GoVersion: lines[i+3],
Main: true,
})
}
return mods, nil
}

View File

@@ -1,11 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "packagesinternal",
srcs = ["packages.go"],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/packagesinternal",
importpath = "golang.org/x/tools/internal/packagesinternal",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
)

View File

@@ -1,23 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "pkgbits",
srcs = [
"codes.go",
"decoder.go",
"doc.go",
"encoder.go",
"flags.go",
"frames_go1.go",
"frames_go17.go",
"reloc.go",
"support.go",
"sync.go",
"syncmarker_string.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/pkgbits",
importpath = "golang.org/x/tools/internal/pkgbits",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
)

View File

@@ -23,6 +23,9 @@ type PkgDecoder struct {
// version is the file format version.
version uint32
// aliases determines whether types.Aliases should be created
aliases bool
// sync indicates whether the file uses sync markers.
sync bool
@@ -73,6 +76,7 @@ func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync }
func NewPkgDecoder(pkgPath, input string) PkgDecoder {
pr := PkgDecoder{
pkgPath: pkgPath,
//aliases: aliases.Enabled(),
}
// TODO(mdempsky): Implement direct indexing of input string to

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,97 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate go run generate.go
// Package stdlib provides a table of all exported symbols in the
// standard library, along with the version at which they first
// appeared.
package stdlib
import (
"fmt"
"strings"
)
type Symbol struct {
Name string
Kind Kind
Version Version // Go version that first included the symbol
}
// A Kind indicates the kind of a symbol:
// function, variable, constant, type, and so on.
type Kind int8
const (
Invalid Kind = iota // Example name:
Type // "Buffer"
Func // "Println"
Var // "EOF"
Const // "Pi"
Field // "Point.X"
Method // "(*Buffer).Grow"
)
func (kind Kind) String() string {
return [...]string{
Invalid: "invalid",
Type: "type",
Func: "func",
Var: "var",
Const: "const",
Field: "field",
Method: "method",
}[kind]
}
// A Version represents a version of Go of the form "go1.%d".
type Version int8
// String returns a version string of the form "go1.23", without allocating.
func (v Version) String() string { return versions[v] }
var versions [30]string // (increase constant as needed)
func init() {
for i := range versions {
versions[i] = fmt.Sprintf("go1.%d", i)
}
}
// HasPackage reports whether the specified package path is part of
// the standard library's public API.
func HasPackage(path string) bool {
_, ok := PackageSymbols[path]
return ok
}
// SplitField splits the field symbol name into type and field
// components. It must be called only on Field symbols.
//
// Example: "File.Package" -> ("File", "Package")
func (sym *Symbol) SplitField() (typename, name string) {
if sym.Kind != Field {
panic("not a field")
}
typename, name, _ = strings.Cut(sym.Name, ".")
return
}
// SplitMethod splits the method symbol name into pointer, receiver,
// and method components. It must be called only on Method symbols.
//
// Example: "(*Buffer).Grow" -> (true, "Buffer", "Grow")
func (sym *Symbol) SplitMethod() (ptr bool, recv, name string) {
if sym.Kind != Method {
panic("not a method")
}
recv, name, _ = strings.Cut(sym.Name, ".")
recv = recv[len("(") : len(recv)-len(")")]
ptr = recv[0] == '*'
if ptr {
recv = recv[len("*"):]
}
return
}

View File

@@ -1,11 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "tokeninternal",
srcs = ["tokeninternal.go"],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/tokeninternal",
importpath = "golang.org/x/tools/internal/tokeninternal",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
)

View File

@@ -34,30 +34,16 @@ func GetLines(file *token.File) []int {
lines []int
_ []struct{}
}
type tokenFile118 struct {
_ *token.FileSet // deleted in go1.19
tokenFile119
}
type uP = unsafe.Pointer
switch unsafe.Sizeof(*file) {
case unsafe.Sizeof(tokenFile118{}):
var ptr *tokenFile118
*(*uP)(uP(&ptr)) = uP(file)
ptr.mu.Lock()
defer ptr.mu.Unlock()
return ptr.lines
case unsafe.Sizeof(tokenFile119{}):
var ptr *tokenFile119
*(*uP)(uP(&ptr)) = uP(file)
ptr.mu.Lock()
defer ptr.mu.Unlock()
return ptr.lines
default:
if unsafe.Sizeof(*file) != unsafe.Sizeof(tokenFile119{}) {
panic("unexpected token.File size")
}
var ptr *tokenFile119
type uP = unsafe.Pointer
*(*uP)(uP(&ptr)) = uP(file)
ptr.mu.Lock()
defer ptr.mu.Unlock()
return ptr.lines
}
// AddExistingFiles adds the specified files to the FileSet if they

View File

@@ -1,17 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "typeparams",
srcs = [
"common.go",
"coretype.go",
"normalize.go",
"termlist.go",
"typeterm.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/typeparams",
importpath = "golang.org/x/tools/internal/typeparams",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
)

View File

@@ -1,204 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package typeparams contains common utilities for writing tools that interact
// with generic Go code, as introduced with Go 1.18.
//
// Many of the types and functions in this package are proxies for the new APIs
// introduced in the standard library with Go 1.18. For example, the
// typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec
// function returns the value of the go/ast.TypeSpec.TypeParams field. At Go
// versions older than 1.18 these helpers are implemented as stubs, allowing
// users of this package to write code that handles generic constructs inline,
// even if the Go version being used to compile does not support generics.
//
// Additionally, this package contains common utilities for working with the
// new generic constructs, to supplement the standard library APIs. Notably,
// the StructuralTerms API computes a minimal representation of the structural
// restrictions on a type parameter.
//
// An external version of these APIs is available in the
// golang.org/x/exp/typeparams module.
package typeparams
import (
"fmt"
"go/ast"
"go/token"
"go/types"
)
// UnpackIndexExpr extracts data from AST nodes that represent index
// expressions.
//
// For an ast.IndexExpr, the resulting indices slice will contain exactly one
// index expression. For an ast.IndexListExpr (go1.18+), it may have a variable
// number of index expressions.
//
// For nodes that don't represent index expressions, the first return value of
// UnpackIndexExpr will be nil.
func UnpackIndexExpr(n ast.Node) (x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) {
switch e := n.(type) {
case *ast.IndexExpr:
return e.X, e.Lbrack, []ast.Expr{e.Index}, e.Rbrack
case *ast.IndexListExpr:
return e.X, e.Lbrack, e.Indices, e.Rbrack
}
return nil, token.NoPos, nil, token.NoPos
}
// PackIndexExpr returns an *ast.IndexExpr or *ast.IndexListExpr, depending on
// the cardinality of indices. Calling PackIndexExpr with len(indices) == 0
// will panic.
func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack token.Pos) ast.Expr {
switch len(indices) {
case 0:
panic("empty indices")
case 1:
return &ast.IndexExpr{
X: x,
Lbrack: lbrack,
Index: indices[0],
Rbrack: rbrack,
}
default:
return &ast.IndexListExpr{
X: x,
Lbrack: lbrack,
Indices: indices,
Rbrack: rbrack,
}
}
}
// IsTypeParam reports whether t is a type parameter.
func IsTypeParam(t types.Type) bool {
_, ok := t.(*types.TypeParam)
return ok
}
// OriginMethod returns the origin method associated with the method fn.
// For methods on a non-generic receiver base type, this is just
// fn. However, for methods with a generic receiver, OriginMethod returns the
// corresponding method in the method set of the origin type.
//
// As a special case, if fn is not a method (has no receiver), OriginMethod
// returns fn.
func OriginMethod(fn *types.Func) *types.Func {
recv := fn.Type().(*types.Signature).Recv()
if recv == nil {
return fn
}
base := recv.Type()
p, isPtr := base.(*types.Pointer)
if isPtr {
base = p.Elem()
}
named, isNamed := base.(*types.Named)
if !isNamed {
// Receiver is a *types.Interface.
return fn
}
if named.TypeParams().Len() == 0 {
// Receiver base has no type parameters, so we can avoid the lookup below.
return fn
}
orig := named.Origin()
gfn, _, _ := types.LookupFieldOrMethod(orig, true, fn.Pkg(), fn.Name())
// This is a fix for a gopls crash (#60628) due to a go/types bug (#60634). In:
// package p
// type T *int
// func (*T) f() {}
// LookupFieldOrMethod(T, true, p, f)=nil, but NewMethodSet(*T)={(*T).f}.
// Here we make them consistent by force.
// (The go/types bug is general, but this workaround is reached only
// for generic T thanks to the early return above.)
if gfn == nil {
mset := types.NewMethodSet(types.NewPointer(orig))
for i := 0; i < mset.Len(); i++ {
m := mset.At(i)
if m.Obj().Id() == fn.Id() {
gfn = m.Obj()
break
}
}
}
// In golang/go#61196, we observe another crash, this time inexplicable.
if gfn == nil {
panic(fmt.Sprintf("missing origin method for %s.%s; named == origin: %t, named.NumMethods(): %d, origin.NumMethods(): %d", named, fn, named == orig, named.NumMethods(), orig.NumMethods()))
}
return gfn.(*types.Func)
}
// GenericAssignableTo is a generalization of types.AssignableTo that
// implements the following rule for uninstantiated generic types:
//
// If V and T are generic named types, then V is considered assignable to T if,
// for every possible instantation of V[A_1, ..., A_N], the instantiation
// T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N].
//
// If T has structural constraints, they must be satisfied by V.
//
// For example, consider the following type declarations:
//
// type Interface[T any] interface {
// Accept(T)
// }
//
// type Container[T any] struct {
// Element T
// }
//
// func (c Container[T]) Accept(t T) { c.Element = t }
//
// In this case, GenericAssignableTo reports that instantiations of Container
// are assignable to the corresponding instantiation of Interface.
func GenericAssignableTo(ctxt *types.Context, V, T types.Type) bool {
// If V and T are not both named, or do not have matching non-empty type
// parameter lists, fall back on types.AssignableTo.
VN, Vnamed := V.(*types.Named)
TN, Tnamed := T.(*types.Named)
if !Vnamed || !Tnamed {
return types.AssignableTo(V, T)
}
vtparams := VN.TypeParams()
ttparams := TN.TypeParams()
if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || VN.TypeArgs().Len() != 0 || TN.TypeArgs().Len() != 0 {
return types.AssignableTo(V, T)
}
// V and T have the same (non-zero) number of type params. Instantiate both
// with the type parameters of V. This must always succeed for V, and will
// succeed for T if and only if the type set of each type parameter of V is a
// subset of the type set of the corresponding type parameter of T, meaning
// that every instantiation of V corresponds to a valid instantiation of T.
// Minor optimization: ensure we share a context across the two
// instantiations below.
if ctxt == nil {
ctxt = types.NewContext()
}
var targs []types.Type
for i := 0; i < vtparams.Len(); i++ {
targs = append(targs, vtparams.At(i))
}
vinst, err := types.Instantiate(ctxt, V, targs, true)
if err != nil {
panic("type parameters should satisfy their own constraints")
}
tinst, err := types.Instantiate(ctxt, T, targs, true)
if err != nil {
return false
}
return types.AssignableTo(vinst, tinst)
}

View File

@@ -1,122 +0,0 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typeparams
import (
"go/types"
)
// CoreType returns the core type of T or nil if T does not have a core type.
//
// See https://go.dev/ref/spec#Core_types for the definition of a core type.
func CoreType(T types.Type) types.Type {
U := T.Underlying()
if _, ok := U.(*types.Interface); !ok {
return U // for non-interface types,
}
terms, err := _NormalTerms(U)
if len(terms) == 0 || err != nil {
// len(terms) -> empty type set of interface.
// err != nil => U is invalid, exceeds complexity bounds, or has an empty type set.
return nil // no core type.
}
U = terms[0].Type().Underlying()
var identical int // i in [0,identical) => Identical(U, terms[i].Type().Underlying())
for identical = 1; identical < len(terms); identical++ {
if !types.Identical(U, terms[identical].Type().Underlying()) {
break
}
}
if identical == len(terms) {
// https://go.dev/ref/spec#Core_types
// "There is a single type U which is the underlying type of all types in the type set of T"
return U
}
ch, ok := U.(*types.Chan)
if !ok {
return nil // no core type as identical < len(terms) and U is not a channel.
}
// https://go.dev/ref/spec#Core_types
// "the type chan E if T contains only bidirectional channels, or the type chan<- E or
// <-chan E depending on the direction of the directional channels present."
for chans := identical; chans < len(terms); chans++ {
curr, ok := terms[chans].Type().Underlying().(*types.Chan)
if !ok {
return nil
}
if !types.Identical(ch.Elem(), curr.Elem()) {
return nil // channel elements are not identical.
}
if ch.Dir() == types.SendRecv {
// ch is bidirectional. We can safely always use curr's direction.
ch = curr
} else if curr.Dir() != types.SendRecv && ch.Dir() != curr.Dir() {
// ch and curr are not bidirectional and not the same direction.
return nil
}
}
return ch
}
// _NormalTerms returns a slice of terms representing the normalized structural
// type restrictions of a type, if any.
//
// For all types other than *types.TypeParam, *types.Interface, and
// *types.Union, this is just a single term with Tilde() == false and
// Type() == typ. For *types.TypeParam, *types.Interface, and *types.Union, see
// below.
//
// Structural type restrictions of a type parameter are created via
// non-interface types embedded in its constraint interface (directly, or via a
// chain of interface embeddings). For example, in the declaration type
// T[P interface{~int; m()}] int the structural restriction of the type
// parameter P is ~int.
//
// With interface embedding and unions, the specification of structural type
// restrictions may be arbitrarily complex. For example, consider the
// following:
//
// type A interface{ ~string|~[]byte }
//
// type B interface{ int|string }
//
// type C interface { ~string|~int }
//
// type T[P interface{ A|B; C }] int
//
// In this example, the structural type restriction of P is ~string|int: A|B
// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
// which when intersected with C (~string|~int) yields ~string|int.
//
// _NormalTerms computes these expansions and reductions, producing a
// "normalized" form of the embeddings. A structural restriction is normalized
// if it is a single union containing no interface terms, and is minimal in the
// sense that removing any term changes the set of types satisfying the
// constraint. It is left as a proof for the reader that, modulo sorting, there
// is exactly one such normalized form.
//
// Because the minimal representation always takes this form, _NormalTerms
// returns a slice of tilde terms corresponding to the terms of the union in
// the normalized structural restriction. An error is returned if the type is
// invalid, exceeds complexity bounds, or has an empty type set. In the latter
// case, _NormalTerms returns ErrEmptyTypeSet.
//
// _NormalTerms makes no guarantees about the order of terms, except that it
// is deterministic.
func _NormalTerms(typ types.Type) ([]*types.Term, error) {
switch typ := typ.(type) {
case *types.TypeParam:
return StructuralTerms(typ)
case *types.Union:
return UnionTermSet(typ)
case *types.Interface:
return InterfaceTermSet(typ)
default:
return []*types.Term{types.NewTerm(false, typ)}, nil
}
}

View File

@@ -1,218 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typeparams
import (
"errors"
"fmt"
"go/types"
"os"
"strings"
)
//go:generate go run copytermlist.go
const debug = false
var ErrEmptyTypeSet = errors.New("empty type set")
// StructuralTerms returns a slice of terms representing the normalized
// structural type restrictions of a type parameter, if any.
//
// Structural type restrictions of a type parameter are created via
// non-interface types embedded in its constraint interface (directly, or via a
// chain of interface embeddings). For example, in the declaration
//
// type T[P interface{~int; m()}] int
//
// the structural restriction of the type parameter P is ~int.
//
// With interface embedding and unions, the specification of structural type
// restrictions may be arbitrarily complex. For example, consider the
// following:
//
// type A interface{ ~string|~[]byte }
//
// type B interface{ int|string }
//
// type C interface { ~string|~int }
//
// type T[P interface{ A|B; C }] int
//
// In this example, the structural type restriction of P is ~string|int: A|B
// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
// which when intersected with C (~string|~int) yields ~string|int.
//
// StructuralTerms computes these expansions and reductions, producing a
// "normalized" form of the embeddings. A structural restriction is normalized
// if it is a single union containing no interface terms, and is minimal in the
// sense that removing any term changes the set of types satisfying the
// constraint. It is left as a proof for the reader that, modulo sorting, there
// is exactly one such normalized form.
//
// Because the minimal representation always takes this form, StructuralTerms
// returns a slice of tilde terms corresponding to the terms of the union in
// the normalized structural restriction. An error is returned if the
// constraint interface is invalid, exceeds complexity bounds, or has an empty
// type set. In the latter case, StructuralTerms returns ErrEmptyTypeSet.
//
// StructuralTerms makes no guarantees about the order of terms, except that it
// is deterministic.
func StructuralTerms(tparam *types.TypeParam) ([]*types.Term, error) {
constraint := tparam.Constraint()
if constraint == nil {
return nil, fmt.Errorf("%s has nil constraint", tparam)
}
iface, _ := constraint.Underlying().(*types.Interface)
if iface == nil {
return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying())
}
return InterfaceTermSet(iface)
}
// InterfaceTermSet computes the normalized terms for a constraint interface,
// returning an error if the term set cannot be computed or is empty. In the
// latter case, the error will be ErrEmptyTypeSet.
//
// See the documentation of StructuralTerms for more information on
// normalization.
func InterfaceTermSet(iface *types.Interface) ([]*types.Term, error) {
return computeTermSet(iface)
}
// UnionTermSet computes the normalized terms for a union, returning an error
// if the term set cannot be computed or is empty. In the latter case, the
// error will be ErrEmptyTypeSet.
//
// See the documentation of StructuralTerms for more information on
// normalization.
func UnionTermSet(union *types.Union) ([]*types.Term, error) {
return computeTermSet(union)
}
func computeTermSet(typ types.Type) ([]*types.Term, error) {
tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0)
if err != nil {
return nil, err
}
if tset.terms.isEmpty() {
return nil, ErrEmptyTypeSet
}
if tset.terms.isAll() {
return nil, nil
}
var terms []*types.Term
for _, term := range tset.terms {
terms = append(terms, types.NewTerm(term.tilde, term.typ))
}
return terms, nil
}
// A termSet holds the normalized set of terms for a given type.
//
// The name termSet is intentionally distinct from 'type set': a type set is
// all types that implement a type (and includes method restrictions), whereas
// a term set just represents the structural restrictions on a type.
type termSet struct {
complete bool
terms termlist
}
func indentf(depth int, format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...)
}
func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) {
if t == nil {
panic("nil type")
}
if debug {
indentf(depth, "%s", t.String())
defer func() {
if err != nil {
indentf(depth, "=> %s", err)
} else {
indentf(depth, "=> %s", res.terms.String())
}
}()
}
const maxTermCount = 100
if tset, ok := seen[t]; ok {
if !tset.complete {
return nil, fmt.Errorf("cycle detected in the declaration of %s", t)
}
return tset, nil
}
// Mark the current type as seen to avoid infinite recursion.
tset := new(termSet)
defer func() {
tset.complete = true
}()
seen[t] = tset
switch u := t.Underlying().(type) {
case *types.Interface:
// The term set of an interface is the intersection of the term sets of its
// embedded types.
tset.terms = allTermlist
for i := 0; i < u.NumEmbeddeds(); i++ {
embedded := u.EmbeddedType(i)
if _, ok := embedded.Underlying().(*types.TypeParam); ok {
return nil, fmt.Errorf("invalid embedded type %T", embedded)
}
tset2, err := computeTermSetInternal(embedded, seen, depth+1)
if err != nil {
return nil, err
}
tset.terms = tset.terms.intersect(tset2.terms)
}
case *types.Union:
// The term set of a union is the union of term sets of its terms.
tset.terms = nil
for i := 0; i < u.Len(); i++ {
t := u.Term(i)
var terms termlist
switch t.Type().Underlying().(type) {
case *types.Interface:
tset2, err := computeTermSetInternal(t.Type(), seen, depth+1)
if err != nil {
return nil, err
}
terms = tset2.terms
case *types.TypeParam, *types.Union:
// A stand-alone type parameter or union is not permitted as union
// term.
return nil, fmt.Errorf("invalid union term %T", t)
default:
if t.Type() == types.Typ[types.Invalid] {
continue
}
terms = termlist{{t.Tilde(), t.Type()}}
}
tset.terms = tset.terms.union(terms)
if len(tset.terms) > maxTermCount {
return nil, fmt.Errorf("exceeded max term count %d", maxTermCount)
}
}
case *types.TypeParam:
panic("unreachable")
default:
// For all other types, the term set is just a single non-tilde term
// holding the type itself.
if u != types.Typ[types.Invalid] {
tset.terms = termlist{{false, t}}
}
}
return tset, nil
}
// under is a facade for the go/types internal function of the same name. It is
// used by typeterm.go.
func under(t types.Type) types.Type {
return t.Underlying()
}

View File

@@ -1,163 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by copytermlist.go DO NOT EDIT.
package typeparams
import (
"bytes"
"go/types"
)
// A termlist represents the type set represented by the union
// t1 y2 ... tn of the type sets of the terms t1 to tn.
// A termlist is in normal form if all terms are disjoint.
// termlist operations don't require the operands to be in
// normal form.
type termlist []*term
// allTermlist represents the set of all types.
// It is in normal form.
var allTermlist = termlist{new(term)}
// String prints the termlist exactly (without normalization).
func (xl termlist) String() string {
if len(xl) == 0 {
return "∅"
}
var buf bytes.Buffer
for i, x := range xl {
if i > 0 {
buf.WriteString(" | ")
}
buf.WriteString(x.String())
}
return buf.String()
}
// isEmpty reports whether the termlist xl represents the empty set of types.
func (xl termlist) isEmpty() bool {
// If there's a non-nil term, the entire list is not empty.
// If the termlist is in normal form, this requires at most
// one iteration.
for _, x := range xl {
if x != nil {
return false
}
}
return true
}
// isAll reports whether the termlist xl represents the set of all types.
func (xl termlist) isAll() bool {
// If there's a 𝓤 term, the entire list is 𝓤.
// If the termlist is in normal form, this requires at most
// one iteration.
for _, x := range xl {
if x != nil && x.typ == nil {
return true
}
}
return false
}
// norm returns the normal form of xl.
func (xl termlist) norm() termlist {
// Quadratic algorithm, but good enough for now.
// TODO(gri) fix asymptotic performance
used := make([]bool, len(xl))
var rl termlist
for i, xi := range xl {
if xi == nil || used[i] {
continue
}
for j := i + 1; j < len(xl); j++ {
xj := xl[j]
if xj == nil || used[j] {
continue
}
if u1, u2 := xi.union(xj); u2 == nil {
// If we encounter a 𝓤 term, the entire list is 𝓤.
// Exit early.
// (Note that this is not just an optimization;
// if we continue, we may end up with a 𝓤 term
// and other terms and the result would not be
// in normal form.)
if u1.typ == nil {
return allTermlist
}
xi = u1
used[j] = true // xj is now unioned into xi - ignore it in future iterations
}
}
rl = append(rl, xi)
}
return rl
}
// union returns the union xl yl.
func (xl termlist) union(yl termlist) termlist {
return append(xl, yl...).norm()
}
// intersect returns the intersection xl ∩ yl.
func (xl termlist) intersect(yl termlist) termlist {
if xl.isEmpty() || yl.isEmpty() {
return nil
}
// Quadratic algorithm, but good enough for now.
// TODO(gri) fix asymptotic performance
var rl termlist
for _, x := range xl {
for _, y := range yl {
if r := x.intersect(y); r != nil {
rl = append(rl, r)
}
}
}
return rl.norm()
}
// equal reports whether xl and yl represent the same type set.
func (xl termlist) equal(yl termlist) bool {
// TODO(gri) this should be more efficient
return xl.subsetOf(yl) && yl.subsetOf(xl)
}
// includes reports whether t ∈ xl.
func (xl termlist) includes(t types.Type) bool {
for _, x := range xl {
if x.includes(t) {
return true
}
}
return false
}
// supersetOf reports whether y ⊆ xl.
func (xl termlist) supersetOf(y *term) bool {
for _, x := range xl {
if y.subsetOf(x) {
return true
}
}
return false
}
// subsetOf reports whether xl ⊆ yl.
func (xl termlist) subsetOf(yl termlist) bool {
if yl.isEmpty() {
return xl.isEmpty()
}
// each term x of xl must be a subset of yl
for _, x := range xl {
if !yl.supersetOf(x) {
return false // x is not a subset yl
}
}
return true
}

View File

@@ -1,169 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Code generated by copytermlist.go DO NOT EDIT.
package typeparams
import "go/types"
// A term describes elementary type sets:
//
// ∅: (*term)(nil) == ∅ // set of no types (empty set)
// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
// T: &term{false, T} == {T} // set of type T
// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
type term struct {
tilde bool // valid if typ != nil
typ types.Type
}
func (x *term) String() string {
switch {
case x == nil:
return "∅"
case x.typ == nil:
return "𝓤"
case x.tilde:
return "~" + x.typ.String()
default:
return x.typ.String()
}
}
// equal reports whether x and y represent the same type set.
func (x *term) equal(y *term) bool {
// easy cases
switch {
case x == nil || y == nil:
return x == y
case x.typ == nil || y.typ == nil:
return x.typ == y.typ
}
// ∅ ⊂ x, y ⊂ 𝓤
return x.tilde == y.tilde && types.Identical(x.typ, y.typ)
}
// union returns the union x y: zero, one, or two non-nil terms.
func (x *term) union(y *term) (_, _ *term) {
// easy cases
switch {
case x == nil && y == nil:
return nil, nil // ∅ ∅ == ∅
case x == nil:
return y, nil // ∅ y == y
case y == nil:
return x, nil // x ∅ == x
case x.typ == nil:
return x, nil // 𝓤 y == 𝓤
case y.typ == nil:
return y, nil // x 𝓤 == 𝓤
}
// ∅ ⊂ x, y ⊂ 𝓤
if x.disjoint(y) {
return x, y // x y == (x, y) if x ∩ y == ∅
}
// x.typ == y.typ
// ~t ~t == ~t
// ~t T == ~t
// T ~t == ~t
// T T == T
if x.tilde || !y.tilde {
return x, nil
}
return y, nil
}
// intersect returns the intersection x ∩ y.
func (x *term) intersect(y *term) *term {
// easy cases
switch {
case x == nil || y == nil:
return nil // ∅ ∩ y == ∅ and ∩ ∅ == ∅
case x.typ == nil:
return y // 𝓤 ∩ y == y
case y.typ == nil:
return x // x ∩ 𝓤 == x
}
// ∅ ⊂ x, y ⊂ 𝓤
if x.disjoint(y) {
return nil // x ∩ y == ∅ if x ∩ y == ∅
}
// x.typ == y.typ
// ~t ∩ ~t == ~t
// ~t ∩ T == T
// T ∩ ~t == T
// T ∩ T == T
if !x.tilde || y.tilde {
return x
}
return y
}
// includes reports whether t ∈ x.
func (x *term) includes(t types.Type) bool {
// easy cases
switch {
case x == nil:
return false // t ∈ ∅ == false
case x.typ == nil:
return true // t ∈ 𝓤 == true
}
// ∅ ⊂ x ⊂ 𝓤
u := t
if x.tilde {
u = under(u)
}
return types.Identical(x.typ, u)
}
// subsetOf reports whether x ⊆ y.
func (x *term) subsetOf(y *term) bool {
// easy cases
switch {
case x == nil:
return true // ∅ ⊆ y == true
case y == nil:
return false // x ⊆ ∅ == false since x != ∅
case y.typ == nil:
return true // x ⊆ 𝓤 == true
case x.typ == nil:
return false // 𝓤 ⊆ y == false since y != 𝓤
}
// ∅ ⊂ x, y ⊂ 𝓤
if x.disjoint(y) {
return false // x ⊆ y == false if x ∩ y == ∅
}
// x.typ == y.typ
// ~t ⊆ ~t == true
// ~t ⊆ T == false
// T ⊆ ~t == true
// T ⊆ T == true
return !x.tilde || y.tilde
}
// disjoint reports whether x ∩ y == ∅.
// x.typ and y.typ must not be nil.
func (x *term) disjoint(y *term) bool {
if debug && (x.typ == nil || y.typ == nil) {
panic("invalid argument(s)")
}
ux := x.typ
if y.tilde {
ux = under(ux)
}
uy := y.typ
if x.tilde {
uy = under(uy)
}
return !types.Identical(ux, uy)
}

View File

@@ -1,16 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "typesinternal",
srcs = [
"errorcode.go",
"errorcode_string.go",
"types.go",
"types_118.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/typesinternal",
importpath = "golang.org/x/tools/internal/typesinternal",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
)

View File

@@ -167,7 +167,7 @@ const (
UntypedNilUse
// WrongAssignCount occurs when the number of values on the right-hand side
// of an assignment or or initialization expression does not match the number
// of an assignment or initialization expression does not match the number
// of variables on the left-hand side.
//
// Example:
@@ -1449,10 +1449,10 @@ const (
NotAGenericType
// WrongTypeArgCount occurs when a type or function is instantiated with an
// incorrent number of type arguments, including when a generic type or
// incorrect number of type arguments, including when a generic type or
// function is used without instantiation.
//
// Errors inolving failed type inference are assigned other error codes.
// Errors involving failed type inference are assigned other error codes.
//
// Example:
// type T[p any] int

View File

@@ -0,0 +1,43 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typesinternal
import (
"go/types"
"golang.org/x/tools/internal/aliases"
)
// ReceiverNamed returns the named type (if any) associated with the
// type of recv, which may be of the form N or *N, or aliases thereof.
// It also reports whether a Pointer was present.
func ReceiverNamed(recv *types.Var) (isPtr bool, named *types.Named) {
t := recv.Type()
if ptr, ok := aliases.Unalias(t).(*types.Pointer); ok {
isPtr = true
t = ptr.Elem()
}
named, _ = aliases.Unalias(t).(*types.Named)
return
}
// Unpointer returns T given *T or an alias thereof.
// For all other types it is the identity function.
// It does not look at underlying types.
// The result may be an alias.
//
// Use this function to strip off the optional pointer on a receiver
// in a field or method selection, without losing the named type
// (which is needed to compute the method set).
//
// See also [typeparams.MustDeref], which removes one level of
// indirection from the type, regardless of named types (analogous to
// a LOAD instruction).
func Unpointer(t types.Type) types.Type {
if ptr, ok := aliases.Unalias(t).(*types.Pointer); ok {
return ptr.Elem()
}
return t
}

View File

@@ -0,0 +1,89 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typesinternal
import (
"go/types"
"golang.org/x/tools/internal/stdlib"
"golang.org/x/tools/internal/versions"
)
// TooNewStdSymbols computes the set of package-level symbols
// exported by pkg that are not available at the specified version.
// The result maps each symbol to its minimum version.
//
// The pkg is allowed to contain type errors.
func TooNewStdSymbols(pkg *types.Package, version string) map[types.Object]string {
disallowed := make(map[types.Object]string)
// Pass 1: package-level symbols.
symbols := stdlib.PackageSymbols[pkg.Path()]
for _, sym := range symbols {
symver := sym.Version.String()
if versions.Before(version, symver) {
switch sym.Kind {
case stdlib.Func, stdlib.Var, stdlib.Const, stdlib.Type:
disallowed[pkg.Scope().Lookup(sym.Name)] = symver
}
}
}
// Pass 2: fields and methods.
//
// We allow fields and methods if their associated type is
// disallowed, as otherwise we would report false positives
// for compatibility shims. Consider:
//
// //go:build go1.22
// type T struct { F std.Real } // correct new API
//
// //go:build !go1.22
// type T struct { F fake } // shim
// type fake struct { ... }
// func (fake) M () {}
//
// These alternative declarations of T use either the std.Real
// type, introduced in go1.22, or a fake type, for the field
// F. (The fakery could be arbitrarily deep, involving more
// nested fields and methods than are shown here.) Clients
// that use the compatibility shim T will compile with any
// version of go, whether older or newer than go1.22, but only
// the newer version will use the std.Real implementation.
//
// Now consider a reference to method M in new(T).F.M() in a
// module that requires a minimum of go1.21. The analysis may
// occur using a version of Go higher than 1.21, selecting the
// first version of T, so the method M is Real.M. This would
// spuriously cause the analyzer to report a reference to a
// too-new symbol even though this expression compiles just
// fine (with the fake implementation) using go1.21.
for _, sym := range symbols {
symVersion := sym.Version.String()
if !versions.Before(version, symVersion) {
continue // allowed
}
var obj types.Object
switch sym.Kind {
case stdlib.Field:
typename, name := sym.SplitField()
if t := pkg.Scope().Lookup(typename); t != nil && disallowed[t] == "" {
obj, _, _ = types.LookupFieldOrMethod(t.Type(), false, pkg, name)
}
case stdlib.Method:
ptr, recvname, name := sym.SplitMethod()
if t := pkg.Scope().Lookup(recvname); t != nil && disallowed[t] == "" {
obj, _, _ = types.LookupFieldOrMethod(t.Type(), ptr, pkg, name)
}
}
if obj != nil {
disallowed[obj] = symVersion
}
}
return disallowed
}

View File

@@ -48,5 +48,3 @@ func ReadGo116ErrorData(err types.Error) (code ErrorCode, start, end token.Pos,
}
return ErrorCode(data[0]), token.Pos(data[1]), token.Pos(data[2]), true
}
var SetGoVersion = func(conf *types.Config, version string) bool { return false }

View File

@@ -1,19 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
package typesinternal
import (
"go/types"
)
func init() {
SetGoVersion = func(conf *types.Config, version string) bool {
conf.GoVersion = version
return true
}
}

View File

@@ -1,17 +0,0 @@
# generated running `bazel run //go/gazelle`, do not edit
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "versions",
srcs = [
"gover.go",
"types.go",
"types_go121.go",
"types_go122.go",
"versions.go",
],
importmap = "github.com/github/codeql-go/extractor/vendor/golang.org/x/tools/internal/versions",
importpath = "golang.org/x/tools/internal/versions",
visibility = ["//go/extractor/vendor/golang.org/x/tools:__subpackages__"],
)

View File

@@ -0,0 +1,43 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package versions
// This file contains predicates for working with file versions to
// decide when a tool should consider a language feature enabled.
// GoVersions that features in x/tools can be gated to.
const (
Go1_18 = "go1.18"
Go1_19 = "go1.19"
Go1_20 = "go1.20"
Go1_21 = "go1.21"
Go1_22 = "go1.22"
)
// Future is an invalid unknown Go version sometime in the future.
// Do not use directly with Compare.
const Future = ""
// AtLeast reports whether the file version v comes after a Go release.
//
// Use this predicate to enable a behavior once a certain Go release
// has happened (and stays enabled in the future).
func AtLeast(v, release string) bool {
if v == Future {
return true // an unknown future version is always after y.
}
return Compare(Lang(v), Lang(release)) >= 0
}
// Before reports whether the file version v is strictly before a Go release.
//
// Use this predicate to disable a behavior once a certain Go release
// has happened (and stays enabled in the future).
func Before(v, release string) bool {
if v == Future {
return false // an unknown future version happens after y.
}
return Compare(Lang(v), Lang(release)) < 0
}

View File

@@ -0,0 +1,14 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package versions
// toolchain is maximum version (<1.22) that the go toolchain used
// to build the current tool is known to support.
//
// When a tool is built with >=1.22, the value of toolchain is unused.
//
// x/tools does not support building with go <1.18. So we take this
// as the minimum possible maximum.
var toolchain string = Go1_18

View File

@@ -0,0 +1,14 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.19
// +build go1.19
package versions
func init() {
if Compare(toolchain, Go1_19) < 0 {
toolchain = Go1_19
}
}

View File

@@ -0,0 +1,14 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.20
// +build go1.20
package versions
func init() {
if Compare(toolchain, Go1_20) < 0 {
toolchain = Go1_20
}
}

View File

@@ -0,0 +1,14 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.21
// +build go1.21
package versions
func init() {
if Compare(toolchain, Go1_21) < 0 {
toolchain = Go1_21
}
}

View File

@@ -12,9 +12,19 @@ import (
"go/types"
)
// FileVersions always reports the a file's Go version as the
// zero version at this Go version.
func FileVersions(info *types.Info, file *ast.File) string { return "" }
// FileVersion returns a language version (<=1.21) derived from runtime.Version()
// or an unknown future version.
func FileVersion(info *types.Info, file *ast.File) string {
// In x/tools built with Go <= 1.21, we do not have Info.FileVersions
// available. We use a go version derived from the toolchain used to
// compile the tool by default.
// This will be <= go1.21. We take this as the maximum version that
// this tool can support.
//
// There are no features currently in x/tools that need to tell fine grained
// differences for versions <1.22.
return toolchain
}
// InitFileVersions is a noop at this Go version.
// InitFileVersions is a noop when compiled with this Go version.
func InitFileVersions(*types.Info) {}

View File

@@ -12,10 +12,27 @@ import (
"go/types"
)
// FileVersions maps a file to the file's semantic Go version.
// The reported version is the zero version if a version cannot be determined.
func FileVersions(info *types.Info, file *ast.File) string {
return info.FileVersions[file]
// FileVersion returns a file's Go version.
// The reported version is an unknown Future version if a
// version cannot be determined.
func FileVersion(info *types.Info, file *ast.File) string {
// In tools built with Go >= 1.22, the Go version of a file
// follow a cascades of sources:
// 1) types.Info.FileVersion, which follows the cascade:
// 1.a) file version (ast.File.GoVersion),
// 1.b) the package version (types.Config.GoVersion), or
// 2) is some unknown Future version.
//
// File versions require a valid package version to be provided to types
// in Config.GoVersion. Config.GoVersion is either from the package's module
// or the toolchain (go run). This value should be provided by go/packages
// or unitchecker.Config.GoVersion.
if v := info.FileVersions[file]; IsValid(v) {
return v
}
// Note: we could instead return runtime.Version() [if valid].
// This would act as a max version on what a tool can support.
return Future
}
// InitFileVersions initializes info to record Go versions for Go files.

View File

@@ -4,6 +4,10 @@
package versions
import (
"strings"
)
// Note: If we use build tags to use go/versions when go >=1.22,
// we run into go.dev/issue/53737. Under some operations users would see an
// import of "go/versions" even if they would not compile the file.
@@ -45,6 +49,7 @@ func IsValid(x string) bool { return isValid(stripGo(x)) }
// stripGo converts from a "go1.21" version to a "1.21" version.
// If v does not start with "go", stripGo returns the empty string (a known invalid version).
func stripGo(v string) string {
v, _, _ = strings.Cut(v, "-") // strip -bigcorp suffix.
if len(v) < 2 || v[:2] != "go" {
return ""
}

View File

@@ -1,25 +1,28 @@
# golang.org/x/mod v0.15.0
# golang.org/x/mod v0.18.0
## explicit; go 1.18
golang.org/x/mod/internal/lazyregexp
golang.org/x/mod/modfile
golang.org/x/mod/module
golang.org/x/mod/semver
# golang.org/x/tools v0.18.0
# golang.org/x/sync v0.7.0
## explicit; go 1.18
golang.org/x/sync/errgroup
# golang.org/x/tools v0.22.0
## explicit; go 1.19
golang.org/x/tools/go/gcexportdata
golang.org/x/tools/go/internal/packagesdriver
golang.org/x/tools/go/packages
golang.org/x/tools/go/types/objectpath
golang.org/x/tools/internal/aliases
golang.org/x/tools/internal/event
golang.org/x/tools/internal/event/core
golang.org/x/tools/internal/event/keys
golang.org/x/tools/internal/event/label
golang.org/x/tools/internal/event/tag
golang.org/x/tools/internal/gcimporter
golang.org/x/tools/internal/gocommand
golang.org/x/tools/internal/packagesinternal
golang.org/x/tools/internal/pkgbits
golang.org/x/tools/internal/stdlib
golang.org/x/tools/internal/tokeninternal
golang.org/x/tools/internal/typeparams
golang.org/x/tools/internal/typesinternal
golang.org/x/tools/internal/versions