mirror of
https://github.com/containers/podman.git
synced 2026-10-25 19:29:32 -04:00
fix(deps): update module github.com/onsi/ginkgo/v2 to v2.26.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
9570f99790
commit
72ffd00c5a
91 files changed
+35620
-8
No files matched your search
@@ -47,7 +47,7 @@ require (
|
||||
github.com/moby/sys/user v0.4.0
|
||||
github.com/moby/term v0.5.2
|
||||
github.com/nxadm/tail v1.4.11
|
||||
github.com/onsi/ginkgo/v2 v2.25.3
|
||||
github.com/onsi/ginkgo/v2 v2.26.0
|
||||
github.com/onsi/gomega v1.38.2
|
||||
github.com/opencontainers/cgroups v0.0.5
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
|
||||
@@ -131,6 +131,12 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fsouza/go-dockerclient v1.12.1 h1:FMoLq+Zhv9Oz/rFmu6JWkImfr6CBgZOPcL+bHW4gS0o=
|
||||
github.com/fsouza/go-dockerclient v1.12.1/go.mod h1:OqsgJJcpCwqyM3JED7TdfM9QVWS5O7jSYwXxYKmOooY=
|
||||
github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
|
||||
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
|
||||
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
|
||||
github.com/gkampitakis/go-snaps v0.5.14 h1:3fAqdB6BCPKHDMHAKRwtPUwYexKtGrNuw8HX/T/4neo=
|
||||
github.com/gkampitakis/go-snaps v0.5.14/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -147,6 +153,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
|
||||
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/godbus/dbus/v5 v5.1.1-0.20241109141217-c266b19b28e9 h1:Kzr9J0S0V2PRxiX6B6xw1kWjzsIyjLO2Ibi4fNTaYBM=
|
||||
github.com/godbus/dbus/v5 v5.1.1-0.20241109141217-c266b19b28e9/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
@@ -205,6 +213,8 @@ github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs=
|
||||
github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE=
|
||||
github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
|
||||
@@ -229,6 +239,8 @@ github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMD
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
|
||||
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
@@ -245,6 +257,8 @@ github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos
|
||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||
github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
|
||||
github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
|
||||
github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE=
|
||||
github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A=
|
||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPnkFiU=
|
||||
@@ -282,8 +296,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
|
||||
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
|
||||
github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw=
|
||||
github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE=
|
||||
github.com/onsi/ginkgo/v2 v2.26.0 h1:1J4Wut1IlYZNEAWIV3ALrT9NfiaGW2cDCJQSFQMs/gE=
|
||||
github.com/onsi/ginkgo/v2 v2.26.0/go.mod h1:qhEywmzWTBUY88kfO0BRvX4py7scov9yR+Az2oavUzw=
|
||||
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
|
||||
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
|
||||
github.com/opencontainers/cgroups v0.0.5 h1:DRITAqcOnY0uSBzIpt1RYWLjh5DPDiqUs4fY6Y0ktls=
|
||||
@@ -383,6 +397,14 @@ github.com/sylabs/sif/v2 v2.21.1 h1:GZ0b5//AFAqJEChd8wHV/uSKx/l1iuGYwjR8nx+4wPI=
|
||||
github.com/sylabs/sif/v2 v2.21.1/go.mod h1:YoqEGQnb5x/ItV653bawXHZJOXQaEWpGwHsSD3YePJI=
|
||||
github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc=
|
||||
github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
|
||||
+6
@@ -1,3 +1,9 @@
|
||||
## 2.26.0
|
||||
|
||||
### Features
|
||||
|
||||
Ginkgo can now generate json-formatted reports that are compatible with the `go test` json format. Use `ginkgo --gojson-report=report.go.json`. This is not intended to be a replacement for Ginkgo's native json format which is more information rich and better models Ginkgo's test structure semantics.
|
||||
|
||||
## 2.25.3
|
||||
|
||||
### Fixes
|
||||
|
||||
+10
@@ -113,3 +113,13 @@ Ginkgo is MIT-Licensed
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
## Sponsors
|
||||
|
||||
Sponsors commit to a [sponsorship](https://github.com/sponsors/onsi) for a year. If you're an organization that makes use of Ginkgo please consider becoming a sponsor!
|
||||
|
||||
<p style="font-size:21px; color:black;">Browser testing via
|
||||
<a href="https://www.lambdatest.com/" target="_blank">
|
||||
<img src="https://www.lambdatest.com/blue-logo.png" style="vertical-align: middle;" width="250" height="45" />
|
||||
</a>
|
||||
</p>
|
||||
+3
@@ -90,6 +90,9 @@ func FinalizeProfilesAndReportsForSuites(suites TestSuites, cliConfig types.CLIC
|
||||
if reporterConfig.JSONReport != "" {
|
||||
reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.JSONReport, GenerateFunc: reporters.GenerateJSONReport, MergeFunc: reporters.MergeAndCleanupJSONReports})
|
||||
}
|
||||
if reporterConfig.GoJSONReport != "" {
|
||||
reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.GoJSONReport, GenerateFunc: reporters.GenerateGoTestJSONReport, MergeFunc: reporters.MergeAndCleanupGoTestJSONReports})
|
||||
}
|
||||
if reporterConfig.JUnitReport != "" {
|
||||
reportFormats = append(reportFormats, reportFormat{ReportName: reporterConfig.JUnitReport, GenerateFunc: reporters.GenerateJUnitReport, MergeFunc: reporters.MergeAndCleanupJUnitReports})
|
||||
}
|
||||
|
||||
+6
@@ -107,6 +107,9 @@ func runSerial(suite TestSuite, ginkgoConfig types.SuiteConfig, reporterConfig t
|
||||
if reporterConfig.JSONReport != "" {
|
||||
reporterConfig.JSONReport = AbsPathForGeneratedAsset(reporterConfig.JSONReport, suite, cliConfig, 0)
|
||||
}
|
||||
if reporterConfig.GoJSONReport != "" {
|
||||
reporterConfig.GoJSONReport = AbsPathForGeneratedAsset(reporterConfig.GoJSONReport, suite, cliConfig, 0)
|
||||
}
|
||||
if reporterConfig.JUnitReport != "" {
|
||||
reporterConfig.JUnitReport = AbsPathForGeneratedAsset(reporterConfig.JUnitReport, suite, cliConfig, 0)
|
||||
}
|
||||
@@ -179,6 +182,9 @@ func runParallel(suite TestSuite, ginkgoConfig types.SuiteConfig, reporterConfig
|
||||
if reporterConfig.JSONReport != "" {
|
||||
reporterConfig.JSONReport = AbsPathForGeneratedAsset(reporterConfig.JSONReport, suite, cliConfig, 0)
|
||||
}
|
||||
if reporterConfig.GoJSONReport != "" {
|
||||
reporterConfig.GoJSONReport = AbsPathForGeneratedAsset(reporterConfig.GoJSONReport, suite, cliConfig, 0)
|
||||
}
|
||||
if reporterConfig.JUnitReport != "" {
|
||||
reporterConfig.JUnitReport = AbsPathForGeneratedAsset(reporterConfig.JUnitReport, suite, cliConfig, 0)
|
||||
}
|
||||
|
||||
-1
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
_ "go.uber.org/automaxprocs"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/build"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/command"
|
||||
"github.com/onsi/ginkgo/v2/ginkgo/generators"
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
//go:build !go1.25
|
||||
// +build !go1.25
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "go.uber.org/automaxprocs"
|
||||
)
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
package reporters
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
func ptr[T any](in T) *T {
|
||||
return &in
|
||||
}
|
||||
|
||||
type encoder interface {
|
||||
Encode(v any) error
|
||||
}
|
||||
|
||||
// gojsonEvent matches the format from go internals
|
||||
// https://github.com/golang/go/blob/master/src/cmd/internal/test2json/test2json.go#L31-L41
|
||||
// https://pkg.go.dev/cmd/test2json
|
||||
type gojsonEvent struct {
|
||||
Time *time.Time `json:",omitempty"`
|
||||
Action GoJSONAction
|
||||
Package string `json:",omitempty"`
|
||||
Test string `json:",omitempty"`
|
||||
Elapsed *float64 `json:",omitempty"`
|
||||
Output *string `json:",omitempty"`
|
||||
FailedBuild string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type GoJSONAction string
|
||||
|
||||
const (
|
||||
// start - the test binary is about to be executed
|
||||
GoJSONStart GoJSONAction = "start"
|
||||
// run - the test has started running
|
||||
GoJSONRun GoJSONAction = "run"
|
||||
// pause - the test has been paused
|
||||
GoJSONPause GoJSONAction = "pause"
|
||||
// cont - the test has continued running
|
||||
GoJSONCont GoJSONAction = "cont"
|
||||
// pass - the test passed
|
||||
GoJSONPass GoJSONAction = "pass"
|
||||
// bench - the benchmark printed log output but did not fail
|
||||
GoJSONBench GoJSONAction = "bench"
|
||||
// fail - the test or benchmark failed
|
||||
GoJSONFail GoJSONAction = "fail"
|
||||
// output - the test printed output
|
||||
GoJSONOutput GoJSONAction = "output"
|
||||
// skip - the test was skipped or the package contained no tests
|
||||
GoJSONSkip GoJSONAction = "skip"
|
||||
)
|
||||
|
||||
func goJSONActionFromSpecState(state types.SpecState) GoJSONAction {
|
||||
switch state {
|
||||
case types.SpecStateInvalid:
|
||||
return GoJSONFail
|
||||
case types.SpecStatePending:
|
||||
return GoJSONSkip
|
||||
case types.SpecStateSkipped:
|
||||
return GoJSONSkip
|
||||
case types.SpecStatePassed:
|
||||
return GoJSONPass
|
||||
case types.SpecStateFailed:
|
||||
return GoJSONFail
|
||||
case types.SpecStateAborted:
|
||||
return GoJSONFail
|
||||
case types.SpecStatePanicked:
|
||||
return GoJSONFail
|
||||
case types.SpecStateInterrupted:
|
||||
return GoJSONFail
|
||||
case types.SpecStateTimedout:
|
||||
return GoJSONFail
|
||||
default:
|
||||
panic("unexpected state should not happen")
|
||||
}
|
||||
}
|
||||
|
||||
// gojsonReport wraps types.Report and calcualtes extra fields requires by gojson
|
||||
type gojsonReport struct {
|
||||
o types.Report
|
||||
// Extra calculated fields
|
||||
goPkg string
|
||||
elapsed float64
|
||||
}
|
||||
|
||||
func newReport(in types.Report) *gojsonReport {
|
||||
return &gojsonReport{
|
||||
o: in,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *gojsonReport) Fill() error {
|
||||
// NOTE: could the types.Report include the go package name?
|
||||
goPkg, err := suitePathToPkg(r.o.SuitePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.goPkg = goPkg
|
||||
r.elapsed = r.o.RunTime.Seconds()
|
||||
return nil
|
||||
}
|
||||
|
||||
// gojsonSpecReport wraps types.SpecReport and calculates extra fields required by gojson
|
||||
type gojsonSpecReport struct {
|
||||
o types.SpecReport
|
||||
// extra calculated fields
|
||||
testName string
|
||||
elapsed float64
|
||||
action GoJSONAction
|
||||
}
|
||||
|
||||
func newSpecReport(in types.SpecReport) *gojsonSpecReport {
|
||||
return &gojsonSpecReport{
|
||||
o: in,
|
||||
}
|
||||
}
|
||||
|
||||
func (sr *gojsonSpecReport) Fill() error {
|
||||
sr.elapsed = sr.o.RunTime.Seconds()
|
||||
sr.testName = createTestName(sr.o)
|
||||
sr.action = goJSONActionFromSpecState(sr.o.State)
|
||||
return nil
|
||||
}
|
||||
|
||||
func suitePathToPkg(dir string) (string, error) {
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.NeedFiles | packages.NeedSyntax,
|
||||
}
|
||||
pkgs, err := packages.Load(cfg, dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(pkgs) != 1 {
|
||||
return "", errors.New("error")
|
||||
}
|
||||
return pkgs[0].ID, nil
|
||||
}
|
||||
|
||||
func createTestName(spec types.SpecReport) string {
|
||||
name := fmt.Sprintf("[%s]", spec.LeafNodeType)
|
||||
if spec.FullText() != "" {
|
||||
name = name + " " + spec.FullText()
|
||||
}
|
||||
labels := spec.Labels()
|
||||
if len(labels) > 0 {
|
||||
name = name + " [" + strings.Join(labels, ", ") + "]"
|
||||
}
|
||||
semVerConstraints := spec.SemVerConstraints()
|
||||
if len(semVerConstraints) > 0 {
|
||||
name = name + " [" + strings.Join(semVerConstraints, ", ") + "]"
|
||||
}
|
||||
name = strings.TrimSpace(name)
|
||||
return name
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package reporters
|
||||
|
||||
type GoJSONEventWriter struct {
|
||||
enc encoder
|
||||
specSystemErrFn specSystemExtractFn
|
||||
specSystemOutFn specSystemExtractFn
|
||||
}
|
||||
|
||||
func NewGoJSONEventWriter(enc encoder, errFn specSystemExtractFn, outFn specSystemExtractFn) *GoJSONEventWriter {
|
||||
return &GoJSONEventWriter{
|
||||
enc: enc,
|
||||
specSystemErrFn: errFn,
|
||||
specSystemOutFn: outFn,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *GoJSONEventWriter) writeEvent(e *gojsonEvent) error {
|
||||
return r.enc.Encode(e)
|
||||
}
|
||||
|
||||
func (r *GoJSONEventWriter) WriteSuiteStart(report *gojsonReport) error {
|
||||
e := &gojsonEvent{
|
||||
Time: &report.o.StartTime,
|
||||
Action: GoJSONStart,
|
||||
Package: report.goPkg,
|
||||
Output: nil,
|
||||
FailedBuild: "",
|
||||
}
|
||||
return r.writeEvent(e)
|
||||
}
|
||||
|
||||
func (r *GoJSONEventWriter) WriteSuiteResult(report *gojsonReport) error {
|
||||
var action GoJSONAction
|
||||
switch {
|
||||
case report.o.PreRunStats.SpecsThatWillRun == 0:
|
||||
action = GoJSONSkip
|
||||
case report.o.SuiteSucceeded:
|
||||
action = GoJSONPass
|
||||
default:
|
||||
action = GoJSONFail
|
||||
}
|
||||
e := &gojsonEvent{
|
||||
Time: &report.o.EndTime,
|
||||
Action: action,
|
||||
Package: report.goPkg,
|
||||
Output: nil,
|
||||
FailedBuild: "",
|
||||
Elapsed: ptr(report.elapsed),
|
||||
}
|
||||
return r.writeEvent(e)
|
||||
}
|
||||
|
||||
func (r *GoJSONEventWriter) WriteSpecStart(report *gojsonReport, specReport *gojsonSpecReport) error {
|
||||
e := &gojsonEvent{
|
||||
Time: &specReport.o.StartTime,
|
||||
Action: GoJSONRun,
|
||||
Test: specReport.testName,
|
||||
Package: report.goPkg,
|
||||
Output: nil,
|
||||
FailedBuild: "",
|
||||
}
|
||||
return r.writeEvent(e)
|
||||
}
|
||||
|
||||
func (r *GoJSONEventWriter) WriteSpecOut(report *gojsonReport, specReport *gojsonSpecReport) error {
|
||||
events := []*gojsonEvent{}
|
||||
|
||||
stdErr := r.specSystemErrFn(specReport.o)
|
||||
if stdErr != "" {
|
||||
events = append(events, &gojsonEvent{
|
||||
Time: &specReport.o.EndTime,
|
||||
Action: GoJSONOutput,
|
||||
Test: specReport.testName,
|
||||
Package: report.goPkg,
|
||||
Output: ptr(stdErr),
|
||||
FailedBuild: "",
|
||||
})
|
||||
}
|
||||
stdOut := r.specSystemOutFn(specReport.o)
|
||||
if stdOut != "" {
|
||||
events = append(events, &gojsonEvent{
|
||||
Time: &specReport.o.EndTime,
|
||||
Action: GoJSONOutput,
|
||||
Test: specReport.testName,
|
||||
Package: report.goPkg,
|
||||
Output: ptr(stdOut),
|
||||
FailedBuild: "",
|
||||
})
|
||||
}
|
||||
|
||||
for _, ev := range events {
|
||||
err := r.writeEvent(ev)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *GoJSONEventWriter) WriteSpecResult(report *gojsonReport, specReport *gojsonSpecReport) error {
|
||||
e := &gojsonEvent{
|
||||
Time: &specReport.o.EndTime,
|
||||
Action: specReport.action,
|
||||
Test: specReport.testName,
|
||||
Package: report.goPkg,
|
||||
Elapsed: ptr(specReport.elapsed),
|
||||
Output: nil,
|
||||
FailedBuild: "",
|
||||
}
|
||||
return r.writeEvent(e)
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package reporters
|
||||
|
||||
import (
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
type GoJSONReporter struct {
|
||||
ev *GoJSONEventWriter
|
||||
}
|
||||
|
||||
type specSystemExtractFn func (spec types.SpecReport) string
|
||||
|
||||
func NewGoJSONReporter(enc encoder, errFn specSystemExtractFn, outFn specSystemExtractFn) *GoJSONReporter {
|
||||
return &GoJSONReporter{
|
||||
ev: NewGoJSONEventWriter(enc, errFn, outFn),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *GoJSONReporter) Write(originalReport types.Report) error {
|
||||
// suite start events
|
||||
report := newReport(originalReport)
|
||||
err := report.Fill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.ev.WriteSuiteStart(report)
|
||||
for _, originalSpecReport := range originalReport.SpecReports {
|
||||
specReport := newSpecReport(originalSpecReport)
|
||||
err := specReport.Fill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if specReport.o.LeafNodeType == types.NodeTypeIt {
|
||||
// handle any It leaf node as a spec
|
||||
r.ev.WriteSpecStart(report, specReport)
|
||||
r.ev.WriteSpecOut(report, specReport)
|
||||
r.ev.WriteSpecResult(report, specReport)
|
||||
} else {
|
||||
// handle any other leaf node as generic output
|
||||
r.ev.WriteSpecOut(report, specReport)
|
||||
}
|
||||
}
|
||||
r.ev.WriteSuiteResult(report)
|
||||
return nil
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package reporters
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/onsi/ginkgo/v2/internal/reporters"
|
||||
"github.com/onsi/ginkgo/v2/types"
|
||||
)
|
||||
|
||||
// GenerateGoTestJSONReport produces a JSON-formatted in the test2json format used by `go test -json`
|
||||
func GenerateGoTestJSONReport(report types.Report, destination string) error {
|
||||
// walk report and generate test2json-compatible objects
|
||||
// JSON-encode the objects into filename
|
||||
if err := os.MkdirAll(path.Dir(destination), 0770); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
enc := json.NewEncoder(f)
|
||||
r := reporters.NewGoJSONReporter(
|
||||
enc,
|
||||
systemErrForUnstructuredReporters,
|
||||
systemOutForUnstructuredReporters,
|
||||
)
|
||||
return r.Write(report)
|
||||
}
|
||||
|
||||
// MergeJSONReports produces a single JSON-formatted report at the passed in destination by merging the JSON-formatted reports provided in sources
|
||||
// It skips over reports that fail to decode but reports on them via the returned messages []string
|
||||
func MergeAndCleanupGoTestJSONReports(sources []string, destination string) ([]string, error) {
|
||||
messages := []string{}
|
||||
if err := os.MkdirAll(path.Dir(destination), 0770); err != nil {
|
||||
return messages, err
|
||||
}
|
||||
f, err := os.Create(destination)
|
||||
if err != nil {
|
||||
return messages, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
for _, source := range sources {
|
||||
data, err := os.ReadFile(source)
|
||||
if err != nil {
|
||||
messages = append(messages, fmt.Sprintf("Could not open %s:\n%s", source, err.Error()))
|
||||
continue
|
||||
}
|
||||
_, err = f.Write(data)
|
||||
if err != nil {
|
||||
messages = append(messages, fmt.Sprintf("Could not write to %s:\n%s", destination, err.Error()))
|
||||
continue
|
||||
}
|
||||
os.Remove(source)
|
||||
}
|
||||
return messages, nil
|
||||
}
|
||||
+10
-1
@@ -165,7 +165,7 @@ ReportAfterSuite nodes must be created at the top-level (i.e. not nested in a Co
|
||||
When running in parallel, Ginkgo ensures that only one of the parallel nodes runs the ReportAfterSuite and that it is passed a report that is aggregated across
|
||||
all parallel nodes
|
||||
|
||||
In addition to using ReportAfterSuite to programmatically generate suite reports, you can also generate JSON, JUnit, and Teamcity formatted reports using the --json-report, --junit-report, and --teamcity-report ginkgo CLI flags.
|
||||
In addition to using ReportAfterSuite to programmatically generate suite reports, you can also generate JSON, GoJSON, JUnit, and Teamcity formatted reports using the --json-report, --gojson-report, --junit-report, and --teamcity-report ginkgo CLI flags.
|
||||
|
||||
You cannot nest any other Ginkgo nodes within a ReportAfterSuite node's closure.
|
||||
You can learn more about ReportAfterSuite here: https://onsi.github.io/ginkgo/#generating-reports-programmatically
|
||||
@@ -188,6 +188,12 @@ func registerReportAfterSuiteNodeForAutogeneratedReports(reporterConfig types.Re
|
||||
Fail(fmt.Sprintf("Failed to generate JSON report:\n%s", err.Error()))
|
||||
}
|
||||
}
|
||||
if reporterConfig.GoJSONReport != "" {
|
||||
err := reporters.GenerateGoTestJSONReport(report, reporterConfig.GoJSONReport)
|
||||
if err != nil {
|
||||
Fail(fmt.Sprintf("Failed to generate Go JSON report:\n%s", err.Error()))
|
||||
}
|
||||
}
|
||||
if reporterConfig.JUnitReport != "" {
|
||||
err := reporters.GenerateJUnitReport(report, reporterConfig.JUnitReport)
|
||||
if err != nil {
|
||||
@@ -206,6 +212,9 @@ func registerReportAfterSuiteNodeForAutogeneratedReports(reporterConfig types.Re
|
||||
if reporterConfig.JSONReport != "" {
|
||||
flags = append(flags, "--json-report")
|
||||
}
|
||||
if reporterConfig.GoJSONReport != "" {
|
||||
flags = append(flags, "--gojson-report")
|
||||
}
|
||||
if reporterConfig.JUnitReport != "" {
|
||||
flags = append(flags, "--junit-report")
|
||||
}
|
||||
|
||||
+4
-1
@@ -96,6 +96,7 @@ type ReporterConfig struct {
|
||||
ForceNewlines bool
|
||||
|
||||
JSONReport string
|
||||
GoJSONReport string
|
||||
JUnitReport string
|
||||
TeamcityReport string
|
||||
}
|
||||
@@ -112,7 +113,7 @@ func (rc ReporterConfig) Verbosity() VerbosityLevel {
|
||||
}
|
||||
|
||||
func (rc ReporterConfig) WillGenerateReport() bool {
|
||||
return rc.JSONReport != "" || rc.JUnitReport != "" || rc.TeamcityReport != ""
|
||||
return rc.JSONReport != "" || rc.GoJSONReport != "" || rc.JUnitReport != "" || rc.TeamcityReport != ""
|
||||
}
|
||||
|
||||
func NewDefaultReporterConfig() ReporterConfig {
|
||||
@@ -359,6 +360,8 @@ var ReporterConfigFlags = GinkgoFlags{
|
||||
|
||||
{KeyPath: "R.JSONReport", Name: "json-report", UsageArgument: "filename.json", SectionKey: "output",
|
||||
Usage: "If set, Ginkgo will generate a JSON-formatted test report at the specified location."},
|
||||
{KeyPath: "R.GoJSONReport", Name: "gojson-report", UsageArgument: "filename.json", SectionKey: "output",
|
||||
Usage: "If set, Ginkgo will generate a Go JSON-formatted test report at the specified location."},
|
||||
{KeyPath: "R.JUnitReport", Name: "junit-report", UsageArgument: "filename.xml", SectionKey: "output", DeprecatedName: "reportFile", DeprecatedDocLink: "improved-reporting-infrastructure",
|
||||
Usage: "If set, Ginkgo will generate a conformant junit test report in the specified file."},
|
||||
{KeyPath: "R.TeamcityReport", Name: "teamcity-report", UsageArgument: "filename", SectionKey: "output",
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
package types
|
||||
|
||||
const VERSION = "2.25.3"
|
||||
const VERSION = "2.26.0"
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
// 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 gcexportdata provides functions for reading and writing
|
||||
// export data, which is a serialized description of the API of a Go
|
||||
// package including the names, kinds, types, and locations of all
|
||||
// exported declarations.
|
||||
//
|
||||
// The standard Go compiler (cmd/compile) writes an export data file
|
||||
// for each package it compiles, which it later reads when compiling
|
||||
// packages that import the earlier one. The compiler must thus
|
||||
// contain logic to both write and read export data.
|
||||
// (See the "Export" section in the cmd/compile/README file.)
|
||||
//
|
||||
// The [Read] function in this package can read files produced by the
|
||||
// compiler, producing [go/types] data structures. As a matter of
|
||||
// policy, Read supports export data files produced by only the last
|
||||
// two Go releases plus tip; see https://go.dev/issue/68898. The
|
||||
// export data files produced by the compiler contain additional
|
||||
// details related to generics, inlining, and other optimizations that
|
||||
// cannot be decoded by the [Read] function.
|
||||
//
|
||||
// In files written by the compiler, the export data is not at the
|
||||
// start of the file. Before calling Read, use [NewReader] to locate
|
||||
// the desired portion of the file.
|
||||
//
|
||||
// The [Write] function in this package encodes the exported API of a
|
||||
// Go package ([types.Package]) as a file. Such files can be later
|
||||
// decoded by Read, but cannot be consumed by the compiler.
|
||||
//
|
||||
// # Future changes
|
||||
//
|
||||
// Although Read supports the formats written by both Write and the
|
||||
// compiler, the two are quite different, and there is an open
|
||||
// proposal (https://go.dev/issue/69491) to separate these APIs.
|
||||
//
|
||||
// Under that proposal, this package would ultimately provide only the
|
||||
// Read operation for compiler export data, which must be defined in
|
||||
// this module (golang.org/x/tools), not in the standard library, to
|
||||
// avoid version skew for developer tools that need to read compiler
|
||||
// export data both before and after a Go release, such as from Go
|
||||
// 1.23 to Go 1.24. Because this package lives in the tools module,
|
||||
// clients can update their version of the module some time before the
|
||||
// Go 1.24 release and rebuild and redeploy their tools, which will
|
||||
// then be able to consume both Go 1.23 and Go 1.24 export data files,
|
||||
// so they will work before and after the Go update. (See discussion
|
||||
// at https://go.dev/issue/15651.)
|
||||
//
|
||||
// The operations to import and export [go/types] data structures
|
||||
// would be defined in the go/types package as Import and Export.
|
||||
// [Write] would (eventually) delegate to Export,
|
||||
// and [Read], when it detects a file produced by Export,
|
||||
// would delegate to Import.
|
||||
//
|
||||
// # Deprecations
|
||||
//
|
||||
// The [NewImporter] and [Find] functions are deprecated and should
|
||||
// not be used in new code. The [WriteBundle] and [ReadBundle]
|
||||
// functions are experimental, and there is an open proposal to
|
||||
// deprecate them (https://go.dev/issue/69573).
|
||||
package gcexportdata
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"os/exec"
|
||||
|
||||
"golang.org/x/tools/internal/gcimporter"
|
||||
)
|
||||
|
||||
// Find returns the name of an object (.o) or archive (.a) file
|
||||
// containing type information for the specified import path,
|
||||
// using the go command.
|
||||
// If no file was found, an empty filename is returned.
|
||||
//
|
||||
// A relative srcDir is interpreted relative to the current working directory.
|
||||
//
|
||||
// Find also returns the package's resolved (canonical) import path,
|
||||
// reflecting the effects of srcDir and vendoring on importPath.
|
||||
//
|
||||
// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
|
||||
// which is more efficient.
|
||||
func Find(importPath, srcDir string) (filename, path string) {
|
||||
cmd := exec.Command("go", "list", "-json", "-export", "--", importPath)
|
||||
cmd.Dir = srcDir
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
var data struct {
|
||||
ImportPath string
|
||||
Export string
|
||||
}
|
||||
json.Unmarshal(out, &data)
|
||||
return data.Export, data.ImportPath
|
||||
}
|
||||
|
||||
// NewReader returns a reader for the export data section of an object
|
||||
// (.o) or archive (.a) file read from r. The new reader may provide
|
||||
// additional trailing data beyond the end of the export data.
|
||||
func NewReader(r io.Reader) (io.Reader, error) {
|
||||
buf := bufio.NewReader(r)
|
||||
size, err := gcimporter.FindExportData(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We were given an archive and found the __.PKGDEF in it.
|
||||
// This tells us the size of the export data, and we don't
|
||||
// need to return the entire file.
|
||||
return &io.LimitedReader{
|
||||
R: buf,
|
||||
N: size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// readAll works the same way as io.ReadAll, but avoids allocations and copies
|
||||
// by preallocating a byte slice of the necessary size if the size is known up
|
||||
// front. This is always possible when the input is an archive. In that case,
|
||||
// NewReader will return the known size using an io.LimitedReader.
|
||||
func readAll(r io.Reader) ([]byte, error) {
|
||||
if lr, ok := r.(*io.LimitedReader); ok {
|
||||
data := make([]byte, lr.N)
|
||||
_, err := io.ReadFull(lr, data)
|
||||
return data, err
|
||||
}
|
||||
return io.ReadAll(r)
|
||||
}
|
||||
|
||||
// Read reads export data from in, decodes it, and returns type
|
||||
// information for the package.
|
||||
//
|
||||
// Read is capable of reading export data produced by [Write] at the
|
||||
// same source code version, or by the last two Go releases (plus tip)
|
||||
// of the standard Go compiler. Reading files from older compilers may
|
||||
// produce an error.
|
||||
//
|
||||
// The package path (effectively its linker symbol prefix) is
|
||||
// specified by path, since unlike the package name, this information
|
||||
// may not be recorded in the export data.
|
||||
//
|
||||
// File position information is added to fset.
|
||||
//
|
||||
// Read may inspect and add to the imports map to ensure that references
|
||||
// within the export data to other packages are consistent. The caller
|
||||
// must ensure that imports[path] does not exist, or exists but is
|
||||
// incomplete (see types.Package.Complete), and Read inserts the
|
||||
// resulting package into this map entry.
|
||||
//
|
||||
// On return, the state of the reader is undefined.
|
||||
func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
|
||||
data, err := readAll(in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading export data for %q: %v", path, err)
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(data, []byte("!<arch>")) {
|
||||
return nil, fmt.Errorf("can't read export data for %q directly from an archive file (call gcexportdata.NewReader first to extract export data)", path)
|
||||
}
|
||||
|
||||
// The indexed export format starts with an 'i'; the older
|
||||
// binary export format starts with a 'c', 'd', or 'v'
|
||||
// (from "version"). Select appropriate importer.
|
||||
if len(data) > 0 {
|
||||
switch data[0] {
|
||||
case 'v', 'c', 'd':
|
||||
// binary, produced by cmd/compile till go1.10
|
||||
return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0])
|
||||
|
||||
case 'i':
|
||||
// indexed, produced by cmd/compile till go1.19,
|
||||
// and also by [Write].
|
||||
//
|
||||
// If proposal #69491 is accepted, go/types
|
||||
// serialization will be implemented by
|
||||
// types.Export, to which Write would eventually
|
||||
// delegate (explicitly dropping any pretence at
|
||||
// inter-version Write-Read compatibility).
|
||||
// This [Read] function would delegate to types.Import
|
||||
// when it detects that the file was produced by Export.
|
||||
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
|
||||
return pkg, err
|
||||
|
||||
case 'u':
|
||||
// unified, produced by cmd/compile since go1.20
|
||||
_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path)
|
||||
return pkg, err
|
||||
|
||||
default:
|
||||
l := min(len(data), 10)
|
||||
return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), path)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("empty export data for %s", path)
|
||||
}
|
||||
|
||||
// Write writes encoded type information for the specified package to out.
|
||||
// The FileSet provides file position information for named objects.
|
||||
func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
|
||||
if _, err := io.WriteString(out, "i"); err != nil {
|
||||
return err
|
||||
}
|
||||
return gcimporter.IExportData(out, fset, pkg)
|
||||
}
|
||||
|
||||
// ReadBundle reads an export bundle from in, decodes it, and returns type
|
||||
// information for the packages.
|
||||
// File position information is added to fset.
|
||||
//
|
||||
// ReadBundle may inspect and add to the imports map to ensure that references
|
||||
// within the export bundle to other packages are consistent.
|
||||
//
|
||||
// On return, the state of the reader is undefined.
|
||||
//
|
||||
// Experimental: This API is experimental and may change in the future.
|
||||
func ReadBundle(in io.Reader, fset *token.FileSet, imports map[string]*types.Package) ([]*types.Package, error) {
|
||||
data, err := readAll(in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading export bundle: %v", err)
|
||||
}
|
||||
return gcimporter.IImportBundle(fset, imports, data)
|
||||
}
|
||||
|
||||
// WriteBundle writes encoded type information for the specified packages to out.
|
||||
// The FileSet provides file position information for named objects.
|
||||
//
|
||||
// Experimental: This API is experimental and may change in the future.
|
||||
func WriteBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error {
|
||||
return gcimporter.IExportBundle(out, fset, pkgs)
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// 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 gcexportdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
)
|
||||
|
||||
// NewImporter returns a new instance of the types.Importer interface
|
||||
// that reads type information from export data files written by gc.
|
||||
// The Importer also satisfies types.ImporterFrom.
|
||||
//
|
||||
// Export data files are located using "go build" workspace conventions
|
||||
// and the build.Default context.
|
||||
//
|
||||
// Use this importer instead of go/importer.For("gc", ...) to avoid the
|
||||
// version-skew problems described in the documentation of this package,
|
||||
// or to control the FileSet or access the imports map populated during
|
||||
// package loading.
|
||||
//
|
||||
// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
|
||||
// which is more efficient.
|
||||
func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom {
|
||||
return importer{fset, imports}
|
||||
}
|
||||
|
||||
type importer struct {
|
||||
fset *token.FileSet
|
||||
imports map[string]*types.Package
|
||||
}
|
||||
|
||||
func (imp importer) Import(importPath string) (*types.Package, error) {
|
||||
return imp.ImportFrom(importPath, "", 0)
|
||||
}
|
||||
|
||||
func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) {
|
||||
filename, path := Find(importPath, srcDir)
|
||||
if filename == "" {
|
||||
if importPath == "unsafe" {
|
||||
// Even for unsafe, call Find first in case
|
||||
// the package was vendored.
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
return nil, fmt.Errorf("can't find import: %s", importPath)
|
||||
}
|
||||
|
||||
if pkg, ok := imp.imports[path]; ok && pkg.Complete() {
|
||||
return pkg, nil // cache hit
|
||||
}
|
||||
|
||||
// open file
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
f.Close()
|
||||
if err != nil {
|
||||
// add file name to error
|
||||
err = fmt.Errorf("reading export data: %s: %v", filename, err)
|
||||
}
|
||||
}()
|
||||
|
||||
r, err := NewReader(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Read(r, imp.fset, imp.imports, path)
|
||||
}
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
// Copyright 2018 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 packages loads Go packages for inspection and analysis.
|
||||
|
||||
The [Load] function takes as input a list of patterns and returns a
|
||||
list of [Package] values describing individual packages matched by those
|
||||
patterns.
|
||||
A [Config] specifies configuration options, the most important of which is
|
||||
the [LoadMode], which controls the amount of detail in the loaded packages.
|
||||
|
||||
Load passes most patterns directly to the underlying build tool.
|
||||
The default build tool is the go command.
|
||||
Its supported patterns are described at
|
||||
https://pkg.go.dev/cmd/go#hdr-Package_lists_and_patterns.
|
||||
Other build systems may be supported by providing a "driver";
|
||||
see [The driver protocol].
|
||||
|
||||
All patterns with the prefix "query=", where query is a
|
||||
non-empty string of letters from [a-z], are reserved and may be
|
||||
interpreted as query operators.
|
||||
|
||||
Two query operators are currently supported: "file" and "pattern".
|
||||
|
||||
The query "file=path/to/file.go" matches the package or packages enclosing
|
||||
the Go source file path/to/file.go. For example "file=~/go/src/fmt/print.go"
|
||||
might return the packages "fmt" and "fmt [fmt.test]".
|
||||
|
||||
The query "pattern=string" causes "string" to be passed directly to
|
||||
the underlying build tool. In most cases this is unnecessary,
|
||||
but an application can use Load("pattern=" + x) as an escaping mechanism
|
||||
to ensure that x is not interpreted as a query operator if it contains '='.
|
||||
|
||||
All other query operators are reserved for future use and currently
|
||||
cause Load to report an error.
|
||||
|
||||
The Package struct provides basic information about the package, including
|
||||
|
||||
- ID, a unique identifier for the package in the returned set;
|
||||
- GoFiles, the names of the package's Go source files;
|
||||
- Imports, a map from source import strings to the Packages they name;
|
||||
- Types, the type information for the package's exported symbols;
|
||||
- Syntax, the parsed syntax trees for the package's source code; and
|
||||
- TypesInfo, the result of a complete type-check of the package syntax trees.
|
||||
|
||||
(See the documentation for type Package for the complete list of fields
|
||||
and more detailed descriptions.)
|
||||
|
||||
For example,
|
||||
|
||||
Load(nil, "bytes", "unicode...")
|
||||
|
||||
returns four Package structs describing the standard library packages
|
||||
bytes, unicode, unicode/utf16, and unicode/utf8. Note that one pattern
|
||||
can match multiple packages and that a package might be matched by
|
||||
multiple patterns: in general it is not possible to determine which
|
||||
packages correspond to which patterns.
|
||||
|
||||
Note that the list returned by Load contains only the packages matched
|
||||
by the patterns. Their dependencies can be found by walking the import
|
||||
graph using the Imports fields.
|
||||
|
||||
The Load function can be configured by passing a pointer to a Config as
|
||||
the first argument. A nil Config is equivalent to the zero Config, which
|
||||
causes Load to run in [LoadFiles] mode, collecting minimal information.
|
||||
See the documentation for type Config for details.
|
||||
|
||||
As noted earlier, the Config.Mode controls the amount of detail
|
||||
reported about the loaded packages. See the documentation for type LoadMode
|
||||
for details.
|
||||
|
||||
Most tools should pass their command-line arguments (after any flags)
|
||||
uninterpreted to Load, so that it can interpret them
|
||||
according to the conventions of the underlying build system.
|
||||
|
||||
See the Example function for typical usage.
|
||||
See also [golang.org/x/tools/go/packages/internal/linecount]
|
||||
for an example application.
|
||||
|
||||
# The driver protocol
|
||||
|
||||
Load may be used to load Go packages even in Go projects that use
|
||||
alternative build systems, by installing an appropriate "driver"
|
||||
program for the build system and specifying its location in the
|
||||
GOPACKAGESDRIVER environment variable.
|
||||
For example,
|
||||
https://github.com/bazelbuild/rules_go/wiki/Editor-and-tool-integration
|
||||
explains how to use the driver for Bazel.
|
||||
|
||||
The driver program is responsible for interpreting patterns in its
|
||||
preferred notation and reporting information about the packages that
|
||||
those patterns identify. Drivers must also support the special "file="
|
||||
and "pattern=" patterns described above.
|
||||
|
||||
The patterns are provided as positional command-line arguments. A
|
||||
JSON-encoded [DriverRequest] message providing additional information
|
||||
is written to the driver's standard input. The driver must write a
|
||||
JSON-encoded [DriverResponse] message to its standard output. (This
|
||||
message differs from the JSON schema produced by 'go list'.)
|
||||
|
||||
The value of the PWD environment variable seen by the driver process
|
||||
is the preferred name of its working directory. (The working directory
|
||||
may have other aliases due to symbolic links; see the comment on the
|
||||
Dir field of [exec.Cmd] for related information.)
|
||||
When the driver process emits in its response the name of a file
|
||||
that is a descendant of this directory, it must use an absolute path
|
||||
that has the value of PWD as a prefix, to ensure that the returned
|
||||
filenames satisfy the original query.
|
||||
*/
|
||||
package packages // import "golang.org/x/tools/go/packages"
|
||||
|
||||
/*
|
||||
|
||||
Motivation and design considerations
|
||||
|
||||
The new package's design solves problems addressed by two existing
|
||||
packages: go/build, which locates and describes packages, and
|
||||
golang.org/x/tools/go/loader, which loads, parses and type-checks them.
|
||||
The go/build.Package structure encodes too much of the 'go build' way
|
||||
of organizing projects, leaving us in need of a data type that describes a
|
||||
package of Go source code independent of the underlying build system.
|
||||
We wanted something that works equally well with go build and vgo, and
|
||||
also other build systems such as Bazel and Blaze, making it possible to
|
||||
construct analysis tools that work in all these environments.
|
||||
Tools such as errcheck and staticcheck were essentially unavailable to
|
||||
the Go community at Google, and some of Google's internal tools for Go
|
||||
are unavailable externally.
|
||||
This new package provides a uniform way to obtain package metadata by
|
||||
querying each of these build systems, optionally supporting their
|
||||
preferred command-line notations for packages, so that tools integrate
|
||||
neatly with users' build environments. The Metadata query function
|
||||
executes an external query tool appropriate to the current workspace.
|
||||
|
||||
Loading packages always returns the complete import graph "all the way down",
|
||||
even if all you want is information about a single package, because the query
|
||||
mechanisms of all the build systems we currently support ({go,vgo} list, and
|
||||
blaze/bazel aspect-based query) cannot provide detailed information
|
||||
about one package without visiting all its dependencies too, so there is
|
||||
no additional asymptotic cost to providing transitive information.
|
||||
(This property might not be true of a hypothetical 5th build system.)
|
||||
|
||||
In calls to TypeCheck, all initial packages, and any package that
|
||||
transitively depends on one of them, must be loaded from source.
|
||||
Consider A->B->C->D->E: if A,C are initial, A,B,C must be loaded from
|
||||
source; D may be loaded from export data, and E may not be loaded at all
|
||||
(though it's possible that D's export data mentions it, so a
|
||||
types.Package may be created for it and exposed.)
|
||||
|
||||
The old loader had a feature to suppress type-checking of function
|
||||
bodies on a per-package basis, primarily intended to reduce the work of
|
||||
obtaining type information for imported packages. Now that imports are
|
||||
satisfied by export data, the optimization no longer seems necessary.
|
||||
|
||||
Despite some early attempts, the old loader did not exploit export data,
|
||||
instead always using the equivalent of WholeProgram mode. This was due
|
||||
to the complexity of mixing source and export data packages (now
|
||||
resolved by the upward traversal mentioned above), and because export data
|
||||
files were nearly always missing or stale. Now that 'go build' supports
|
||||
caching, all the underlying build systems can guarantee to produce
|
||||
export data in a reasonable (amortized) time.
|
||||
|
||||
Test "main" packages synthesized by the build system are now reported as
|
||||
first-class packages, avoiding the need for clients (such as go/ssa) to
|
||||
reinvent this generation logic.
|
||||
|
||||
One way in which go/packages is simpler than the old loader is in its
|
||||
treatment of in-package tests. In-package tests are packages that
|
||||
consist of all the files of the library under test, plus the test files.
|
||||
The old loader constructed in-package tests by a two-phase process of
|
||||
mutation called "augmentation": first it would construct and type check
|
||||
all the ordinary library packages and type-check the packages that
|
||||
depend on them; then it would add more (test) files to the package and
|
||||
type-check again. This two-phase approach had four major problems:
|
||||
1) in processing the tests, the loader modified the library package,
|
||||
leaving no way for a client application to see both the test
|
||||
package and the library package; one would mutate into the other.
|
||||
2) because test files can declare additional methods on types defined in
|
||||
the library portion of the package, the dispatch of method calls in
|
||||
the library portion was affected by the presence of the test files.
|
||||
This should have been a clue that the packages were logically
|
||||
different.
|
||||
3) this model of "augmentation" assumed at most one in-package test
|
||||
per library package, which is true of projects using 'go build',
|
||||
but not other build systems.
|
||||
4) because of the two-phase nature of test processing, all packages that
|
||||
import the library package had to be processed before augmentation,
|
||||
forcing a "one-shot" API and preventing the client from calling Load
|
||||
in several times in sequence as is now possible in WholeProgram mode.
|
||||
(TypeCheck mode has a similar one-shot restriction for a different reason.)
|
||||
|
||||
Early drafts of this package supported "multi-shot" operation.
|
||||
Although it allowed clients to make a sequence of calls (or concurrent
|
||||
calls) to Load, building up the graph of Packages incrementally,
|
||||
it was of marginal value: it complicated the API
|
||||
(since it allowed some options to vary across calls but not others),
|
||||
it complicated the implementation,
|
||||
it cannot be made to work in Types mode, as explained above,
|
||||
and it was less efficient than making one combined call (when this is possible).
|
||||
Among the clients we have inspected, none made multiple calls to load
|
||||
but could not be easily and satisfactorily modified to make only a single call.
|
||||
However, applications changes may be required.
|
||||
For example, the ssadump command loads the user-specified packages
|
||||
and in addition the runtime package. It is tempting to simply append
|
||||
"runtime" to the user-provided list, but that does not work if the user
|
||||
specified an ad-hoc package such as [a.go b.go].
|
||||
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.
|
||||
|
||||
Questions & Tasks
|
||||
|
||||
- Add GOARCH/GOOS?
|
||||
They are not portable concepts, but could be made portable.
|
||||
Our goal has been to allow users to express themselves using the conventions
|
||||
of the underlying build system: if the build system honors GOARCH
|
||||
during a build and during a metadata query, then so should
|
||||
applications built atop that query mechanism.
|
||||
Conversely, if the target architecture of the build is determined by
|
||||
command-line flags, the application can pass the relevant
|
||||
flags through to the build system using a command such as:
|
||||
myapp -query_flag="--cpu=amd64" -query_flag="--os=darwin"
|
||||
However, this approach is low-level, unwieldy, and non-portable.
|
||||
GOOS and GOARCH seem important enough to warrant a dedicated option.
|
||||
|
||||
- How should we handle partial failures such as a mixture of good and
|
||||
malformed patterns, existing and non-existent packages, successful and
|
||||
failed builds, import failures, import cycles, and so on, in a call to
|
||||
Load?
|
||||
|
||||
- Support bazel, blaze, and go1.10 list, not just go1.11 list.
|
||||
|
||||
- Handle (and test) various partial success cases, e.g.
|
||||
a mixture of good packages and:
|
||||
invalid patterns
|
||||
nonexistent packages
|
||||
empty packages
|
||||
packages with malformed package or import declarations
|
||||
unreadable files
|
||||
import cycles
|
||||
other parse errors
|
||||
type errors
|
||||
Make sure we record errors at the correct place in the graph.
|
||||
|
||||
- Missing packages among initial arguments are not reported.
|
||||
Return bogus packages for them, like golist does.
|
||||
|
||||
- "undeclared name" errors (for example) are reported out of source file
|
||||
order. I suspect this is due to the breadth-first resolution now used
|
||||
by go/types. Is that a bug? Discuss with gri.
|
||||
|
||||
*/
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
// Copyright 2018 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 packages
|
||||
|
||||
// This file defines the protocol that enables an external "driver"
|
||||
// tool to supply package metadata in place of 'go list'.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DriverRequest defines the schema of a request for package metadata
|
||||
// from an external driver program. The JSON-encoded DriverRequest
|
||||
// message is provided to the driver program's standard input. The
|
||||
// query patterns are provided as command-line arguments.
|
||||
//
|
||||
// See the package documentation for an overview.
|
||||
type DriverRequest struct {
|
||||
Mode LoadMode `json:"mode"`
|
||||
|
||||
// Env specifies the environment the underlying build system should be run in.
|
||||
Env []string `json:"env"`
|
||||
|
||||
// BuildFlags are flags that should be passed to the underlying build system.
|
||||
BuildFlags []string `json:"build_flags"`
|
||||
|
||||
// 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 contents of overlay files (see Config.Overlay).
|
||||
Overlay map[string][]byte `json:"overlay"`
|
||||
}
|
||||
|
||||
// DriverResponse defines the schema of a response from an external
|
||||
// driver program, providing the results of a query for package
|
||||
// metadata. The driver program must write a JSON-encoded
|
||||
// DriverResponse message to its standard output.
|
||||
//
|
||||
// See the package documentation for an overview.
|
||||
type DriverResponse struct {
|
||||
// NotHandled is returned if the request can't be handled by the current
|
||||
// driver. If an external driver returns a response with NotHandled, the
|
||||
// rest of the DriverResponse is ignored, and go/packages will fallback
|
||||
// to the next driver. If go/packages is extended in the future to support
|
||||
// lists of multiple drivers, go/packages will fall back to the next driver.
|
||||
NotHandled bool
|
||||
|
||||
// Compiler and Arch are the arguments pass of types.SizesFor
|
||||
// to get a types.Sizes to use when type checking.
|
||||
Compiler string
|
||||
Arch string
|
||||
|
||||
// Roots is the set of package IDs that make up the root packages.
|
||||
// We have to encode this separately because when we encode a single package
|
||||
// we cannot know if it is one of the roots as that requires knowledge of the
|
||||
// graph it is part of.
|
||||
Roots []string `json:",omitempty"`
|
||||
|
||||
// Packages is the full set of packages in the graph.
|
||||
// The packages are not connected into a graph.
|
||||
// The Imports if populated will be stubs that only have their ID set.
|
||||
// Imports will be connected and then type and syntax information added in a
|
||||
// later pass (see refine).
|
||||
Packages []*Package
|
||||
|
||||
// GoVersion is the minor version number used by the driver
|
||||
// (e.g. the go command on the PATH) when selecting .go files.
|
||||
// Zero means unknown.
|
||||
GoVersion int
|
||||
}
|
||||
|
||||
// driver is the type for functions that query the build system for the
|
||||
// packages named by the patterns.
|
||||
type driver func(cfg *Config, patterns []string) (*DriverResponse, error)
|
||||
|
||||
// findExternalDriver returns the file path of a tool that supplies
|
||||
// the build system package structure, or "" if not found.
|
||||
// If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
|
||||
// value, otherwise it searches for a binary named gopackagesdriver on the PATH.
|
||||
func findExternalDriver(cfg *Config) driver {
|
||||
const toolPrefix = "GOPACKAGESDRIVER="
|
||||
tool := ""
|
||||
for _, env := range cfg.Env {
|
||||
if val, ok := strings.CutPrefix(env, toolPrefix); ok {
|
||||
tool = val
|
||||
}
|
||||
}
|
||||
if tool != "" && tool == "off" {
|
||||
return nil
|
||||
}
|
||||
if tool == "" {
|
||||
var err error
|
||||
tool, err = exec.LookPath("gopackagesdriver")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return func(cfg *Config, patterns []string) (*DriverResponse, error) {
|
||||
req, err := json.Marshal(DriverRequest{
|
||||
Mode: cfg.Mode,
|
||||
Env: cfg.Env,
|
||||
BuildFlags: cfg.BuildFlags,
|
||||
Tests: cfg.Tests,
|
||||
Overlay: cfg.Overlay,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode message to driver tool: %v", err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
cmd := exec.CommandContext(cfg.Context, tool, patterns...)
|
||||
cmd.Dir = cfg.Dir
|
||||
// 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(slices.Clip(cfg.Env), "PWD="+cfg.Dir)
|
||||
cmd.Stdin = bytes.NewReader(req)
|
||||
cmd.Stdout = buf
|
||||
cmd.Stderr = stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
|
||||
}
|
||||
if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" {
|
||||
fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr)
|
||||
}
|
||||
|
||||
var response DriverResponse
|
||||
if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &response, nil
|
||||
}
|
||||
}
|
||||
+1092
File diff suppressed because it is too large.
Load diff
+83
@@ -0,0 +1,83 @@
|
||||
// Copyright 2018 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 packages
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/tools/internal/gocommand"
|
||||
)
|
||||
|
||||
// determineRootDirs returns a mapping from absolute directories that could
|
||||
// contain code to their corresponding import path prefixes.
|
||||
func (state *golistState) determineRootDirs() (map[string]string, error) {
|
||||
env, err := state.getEnv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if env["GOMOD"] != "" {
|
||||
state.rootsOnce.Do(func() {
|
||||
state.rootDirs, state.rootDirsError = state.determineRootDirsModules()
|
||||
})
|
||||
} else {
|
||||
state.rootsOnce.Do(func() {
|
||||
state.rootDirs, state.rootDirsError = state.determineRootDirsGOPATH()
|
||||
})
|
||||
}
|
||||
return state.rootDirs, state.rootDirsError
|
||||
}
|
||||
|
||||
func (state *golistState) determineRootDirsModules() (map[string]string, error) {
|
||||
// List all of the modules--the first will be the directory for the main
|
||||
// module. Any replaced modules will also need to be treated as roots.
|
||||
// Editing files in the module cache isn't a great idea, so we don't
|
||||
// plan to ever support that.
|
||||
out, err := state.invokeGo("list", "-m", "-json", "all")
|
||||
if err != nil {
|
||||
// 'go list all' will fail if we're outside of a module and
|
||||
// GO111MODULE=on. Try falling back without 'all'.
|
||||
var innerErr error
|
||||
out, innerErr = state.invokeGo("list", "-m", "-json")
|
||||
if innerErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
roots := map[string]string{}
|
||||
modules := map[string]string{}
|
||||
var i int
|
||||
for dec := json.NewDecoder(out); dec.More(); {
|
||||
mod := new(gocommand.ModuleJSON)
|
||||
if err := dec.Decode(mod); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if mod.Dir != "" && mod.Path != "" {
|
||||
// This is a valid module; add it to the map.
|
||||
absDir, err := state.cfg.abs(mod.Dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
modules[absDir] = mod.Path
|
||||
// The first result is the main module.
|
||||
if i == 0 || mod.Replace != nil && mod.Replace.Path != "" {
|
||||
roots[absDir] = mod.Path
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) {
|
||||
m := map[string]string{}
|
||||
for _, dir := range filepath.SplitList(state.mustGetEnv()["GOPATH"]) {
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[filepath.Join(absDir, "src")] = ""
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// 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 packages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var modes = [...]struct {
|
||||
mode LoadMode
|
||||
name string
|
||||
}{
|
||||
{NeedName, "NeedName"},
|
||||
{NeedFiles, "NeedFiles"},
|
||||
{NeedCompiledGoFiles, "NeedCompiledGoFiles"},
|
||||
{NeedImports, "NeedImports"},
|
||||
{NeedDeps, "NeedDeps"},
|
||||
{NeedExportFile, "NeedExportFile"},
|
||||
{NeedTypes, "NeedTypes"},
|
||||
{NeedSyntax, "NeedSyntax"},
|
||||
{NeedTypesInfo, "NeedTypesInfo"},
|
||||
{NeedTypesSizes, "NeedTypesSizes"},
|
||||
{NeedForTest, "NeedForTest"},
|
||||
{NeedModule, "NeedModule"},
|
||||
{NeedEmbedFiles, "NeedEmbedFiles"},
|
||||
{NeedEmbedPatterns, "NeedEmbedPatterns"},
|
||||
{NeedTarget, "NeedTarget"},
|
||||
}
|
||||
|
||||
func (mode LoadMode) String() string {
|
||||
if mode == 0 {
|
||||
return "LoadMode(0)"
|
||||
}
|
||||
var out []string
|
||||
// named bits
|
||||
for _, item := range modes {
|
||||
if (mode & item.mode) != 0 {
|
||||
mode ^= item.mode
|
||||
out = append(out, item.name)
|
||||
}
|
||||
}
|
||||
// unnamed residue
|
||||
if mode != 0 {
|
||||
if out == nil {
|
||||
return fmt.Sprintf("LoadMode(%#x)", int(mode))
|
||||
}
|
||||
out = append(out, fmt.Sprintf("%#x", int(mode)))
|
||||
}
|
||||
if len(out) == 1 {
|
||||
return out[0]
|
||||
}
|
||||
return "(" + strings.Join(out, "|") + ")"
|
||||
}
|
||||
+1559
File diff suppressed because it is too large.
Load diff
+68
@@ -0,0 +1,68 @@
|
||||
// Copyright 2018 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 packages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Visit visits all the packages in the import graph whose roots are
|
||||
// pkgs, calling the optional pre function the first time each package
|
||||
// is encountered (preorder), and the optional post function after a
|
||||
// package's dependencies have been visited (postorder).
|
||||
// The boolean result of pre(pkg) determines whether
|
||||
// the imports of package pkg are visited.
|
||||
func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) {
|
||||
seen := make(map[*Package]bool)
|
||||
var visit func(*Package)
|
||||
visit = func(pkg *Package) {
|
||||
if !seen[pkg] {
|
||||
seen[pkg] = true
|
||||
|
||||
if pre == nil || pre(pkg) {
|
||||
paths := make([]string, 0, len(pkg.Imports))
|
||||
for path := range pkg.Imports {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
sort.Strings(paths) // Imports is a map, this makes visit stable
|
||||
for _, path := range paths {
|
||||
visit(pkg.Imports[path])
|
||||
}
|
||||
}
|
||||
|
||||
if post != nil {
|
||||
post(pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, pkg := range pkgs {
|
||||
visit(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintErrors prints to os.Stderr the accumulated errors of all
|
||||
// packages in the import graph rooted at pkgs, dependencies first.
|
||||
// PrintErrors returns the number of errors printed.
|
||||
func PrintErrors(pkgs []*Package) int {
|
||||
var n int
|
||||
errModules := make(map[*Module]bool)
|
||||
Visit(pkgs, nil, func(pkg *Package) {
|
||||
for _, err := range pkg.Errors {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
n++
|
||||
}
|
||||
|
||||
// Print pkg.Module.Error once if present.
|
||||
mod := pkg.Module
|
||||
if mod != nil && mod.Error != nil && !errModules[mod] {
|
||||
errModules[mod] = true
|
||||
fmt.Fprintln(os.Stderr, mod.Error.Err)
|
||||
n++
|
||||
}
|
||||
})
|
||||
return n
|
||||
}
|
||||
+817
@@ -0,0 +1,817 @@
|
||||
// Copyright 2018 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 objectpath defines a naming scheme for types.Objects
|
||||
// (that is, named entities in Go programs) relative to their enclosing
|
||||
// package.
|
||||
//
|
||||
// Type-checker objects are canonical, so they are usually identified by
|
||||
// their address in memory (a pointer), but a pointer has meaning only
|
||||
// within one address space. By contrast, objectpath names allow the
|
||||
// identity of an object to be sent from one program to another,
|
||||
// establishing a correspondence between types.Object variables that are
|
||||
// distinct but logically equivalent.
|
||||
//
|
||||
// A single object may have multiple paths. In this example,
|
||||
//
|
||||
// type A struct{ X int }
|
||||
// type B A
|
||||
//
|
||||
// the field X has two paths due to its membership of both A and B.
|
||||
// The For(obj) function always returns one of these paths, arbitrarily
|
||||
// but consistently.
|
||||
package objectpath
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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
|
||||
// to obtain the original object.
|
||||
// The name does not include the package itself.
|
||||
type Path string
|
||||
|
||||
// Encoding
|
||||
//
|
||||
// An object path is a textual and (with training) human-readable encoding
|
||||
// of a sequence of destructuring operators, starting from a types.Package.
|
||||
// The sequences represent a path through the package/object/type graph.
|
||||
// We classify these operators by their type:
|
||||
//
|
||||
// PO package->object Package.Scope.Lookup
|
||||
// OT object->type Object.Type
|
||||
// TT type->type Type.{Elem,Key,{,{,Recv}Type}Params,Results,Underlying,Rhs} [EKPRUTrCa]
|
||||
// TO type->object Type.{At,Field,Method,Obj} [AFMO]
|
||||
//
|
||||
// All valid paths start with a package and end at an object
|
||||
// and thus may be defined by the regular language:
|
||||
//
|
||||
// objectpath = PO (OT TT* TO)*
|
||||
//
|
||||
// The concrete encoding follows directly:
|
||||
// - The only PO operator is Package.Scope.Lookup, which requires an identifier.
|
||||
// - The only OT operator is Object.Type,
|
||||
// which we encode as '.' because dot cannot appear in an identifier.
|
||||
// - The TT operators are encoded as [EKPRUTrCa];
|
||||
// two of these ({,Recv}TypeParams) require an integer operand,
|
||||
// which is encoded as a string of decimal digits.
|
||||
// - The TO operators are encoded as [AFMO];
|
||||
// three of these (At,Field,Method) require an integer operand,
|
||||
// which is encoded as a string of decimal digits.
|
||||
// These indices are stable across different representations
|
||||
// of the same package, even source and export data.
|
||||
// The indices used are implementation specific and may not correspond to
|
||||
// the argument to the go/types function.
|
||||
//
|
||||
// In the example below,
|
||||
//
|
||||
// package p
|
||||
//
|
||||
// type T interface {
|
||||
// f() (a string, b struct{ X int })
|
||||
// }
|
||||
//
|
||||
// field X has the path "T.UM0.RA1.F0",
|
||||
// representing the following sequence of operations:
|
||||
//
|
||||
// p.Lookup("T") T
|
||||
// .Type().Underlying().Method(0). f
|
||||
// .Type().Results().At(1) b
|
||||
// .Type().Field(0) X
|
||||
//
|
||||
// The encoding is not maximally compact---every R or P is
|
||||
// followed by an A, for example---but this simplifies the
|
||||
// encoder and decoder.
|
||||
const (
|
||||
// object->type operators
|
||||
opType = '.' // .Type() (Object)
|
||||
|
||||
// type->type operators
|
||||
opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map)
|
||||
opKey = 'K' // .Key() (Map)
|
||||
opParams = 'P' // .Params() (Signature)
|
||||
opResults = 'R' // .Results() (Signature)
|
||||
opUnderlying = 'U' // .Underlying() (Named)
|
||||
opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature)
|
||||
opRecvTypeParam = 'r' // .RecvTypeParams.At(i) (Signature)
|
||||
opConstraint = 'C' // .Constraint() (TypeParam)
|
||||
opRhs = 'a' // .Rhs() (Alias)
|
||||
|
||||
// type->object operators
|
||||
opAt = 'A' // .At(i) (Tuple)
|
||||
opField = 'F' // .Field(i) (Struct)
|
||||
opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored)
|
||||
opObj = 'O' // .Obj() (Named, TypeParam)
|
||||
)
|
||||
|
||||
// For is equivalent to new(Encoder).For(obj).
|
||||
//
|
||||
// It may be more efficient to reuse a single Encoder across several calls.
|
||||
func For(obj types.Object) (Path, error) {
|
||||
return new(Encoder).For(obj)
|
||||
}
|
||||
|
||||
// An Encoder amortizes the cost of encoding the paths of multiple objects.
|
||||
// The zero value of an Encoder is ready to use.
|
||||
type Encoder struct {
|
||||
scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects
|
||||
}
|
||||
|
||||
// For returns the path to an object relative to its package,
|
||||
// or an error if the object is not accessible from the package's Scope.
|
||||
//
|
||||
// The For function guarantees to return a path only for the following objects:
|
||||
// - package-level types
|
||||
// - exported package-level non-types
|
||||
// - methods
|
||||
// - parameter and result variables
|
||||
// - struct fields
|
||||
// These objects are sufficient to define the API of their package.
|
||||
// The objects described by a package's export data are drawn from this set.
|
||||
//
|
||||
// The set of objects accessible from a package's Scope depends on
|
||||
// whether the package was produced by type-checking syntax, or
|
||||
// reading export data; the latter may have a smaller Scope since
|
||||
// export data trims objects that are not reachable from an exported
|
||||
// declaration. For example, the For function will return a path for
|
||||
// an exported method of an unexported type that is not reachable
|
||||
// from any public declaration; this path will cause the Object
|
||||
// function to fail if called on a package loaded from export data.
|
||||
// TODO(adonovan): is this a bug or feature? Should this package
|
||||
// compute accessibility in the same way?
|
||||
//
|
||||
// For does not return a path for predeclared names, imported package
|
||||
// names, local names, and unexported package-level names (except
|
||||
// types).
|
||||
//
|
||||
// Example: given this definition,
|
||||
//
|
||||
// package p
|
||||
//
|
||||
// type T interface {
|
||||
// f() (a string, b struct{ X int })
|
||||
// }
|
||||
//
|
||||
// For(X) would return a path that denotes the following sequence of operations:
|
||||
//
|
||||
// p.Scope().Lookup("T") (TypeName T)
|
||||
// .Type().Underlying().Method(0). (method Func f)
|
||||
// .Type().Results().At(1) (field Var b)
|
||||
// .Type().Field(0) (field Var X)
|
||||
//
|
||||
// where p is the package (*types.Package) to which X belongs.
|
||||
func (enc *Encoder) For(obj types.Object) (Path, error) {
|
||||
pkg := obj.Pkg()
|
||||
|
||||
// This table lists the cases of interest.
|
||||
//
|
||||
// Object Action
|
||||
// ------ ------
|
||||
// nil reject
|
||||
// builtin reject
|
||||
// pkgname reject
|
||||
// label reject
|
||||
// var
|
||||
// package-level accept
|
||||
// func param/result accept
|
||||
// local reject
|
||||
// struct field accept
|
||||
// const
|
||||
// package-level accept
|
||||
// local reject
|
||||
// func
|
||||
// package-level accept
|
||||
// init functions reject
|
||||
// concrete method accept
|
||||
// interface method accept
|
||||
// type
|
||||
// package-level accept
|
||||
// local reject
|
||||
//
|
||||
// The only accessible package-level objects are members of pkg itself.
|
||||
//
|
||||
// The cases are handled in four steps:
|
||||
//
|
||||
// 1. reject nil and builtin
|
||||
// 2. accept package-level objects
|
||||
// 3. reject obviously invalid objects
|
||||
// 4. search the API for the path to the param/result/field/method.
|
||||
|
||||
// 1. reference to nil or builtin?
|
||||
if pkg == nil {
|
||||
return "", fmt.Errorf("predeclared %s has no path", obj)
|
||||
}
|
||||
scope := pkg.Scope()
|
||||
|
||||
// 2. package-level object?
|
||||
if scope.Lookup(obj.Name()) == obj {
|
||||
// Only exported objects (and non-exported types) have a path.
|
||||
// Non-exported types may be referenced by other objects.
|
||||
if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() {
|
||||
return "", fmt.Errorf("no path for non-exported %v", obj)
|
||||
}
|
||||
return Path(obj.Name()), nil
|
||||
}
|
||||
|
||||
// 3. Not a package-level object.
|
||||
// Reject obviously non-viable cases.
|
||||
switch obj := obj.(type) {
|
||||
case *types.TypeName:
|
||||
if _, ok := types.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)
|
||||
}
|
||||
case *types.Const, // Only package-level constants have a path.
|
||||
*types.Label, // Labels are function-local.
|
||||
*types.PkgName: // PkgNames are file-local.
|
||||
return "", fmt.Errorf("no path for %v", obj)
|
||||
|
||||
case *types.Var:
|
||||
// Could be:
|
||||
// - a field (obj.IsField())
|
||||
// - a func parameter or result
|
||||
// - a local var.
|
||||
// Sadly there is no way to distinguish
|
||||
// a param/result from a local
|
||||
// so we must proceed to the find.
|
||||
|
||||
case *types.Func:
|
||||
// A func, if not package-level, must be a method.
|
||||
if recv := obj.Type().(*types.Signature).Recv(); recv == nil {
|
||||
return "", fmt.Errorf("func is not a method: %v", obj)
|
||||
}
|
||||
|
||||
if path, ok := enc.concreteMethod(obj); ok {
|
||||
// Fast path for concrete methods that avoids looping over scope.
|
||||
return path, nil
|
||||
}
|
||||
|
||||
default:
|
||||
panic(obj)
|
||||
}
|
||||
|
||||
// 4. Search the API for the path to the var (field/param/result) or method.
|
||||
|
||||
// First inspect package-level named types.
|
||||
// In the presence of path aliases, these give
|
||||
// the best paths because non-types may
|
||||
// refer to types, but not the reverse.
|
||||
empty := make([]byte, 0, 48) // initial space
|
||||
objs := enc.scopeObjects(scope)
|
||||
for _, o := range objs {
|
||||
tname, ok := o.(*types.TypeName)
|
||||
if !ok {
|
||||
continue // handle non-types in second pass
|
||||
}
|
||||
|
||||
path := append(empty, o.Name()...)
|
||||
path = append(path, opType)
|
||||
|
||||
T := o.Type()
|
||||
if alias, ok := T.(*types.Alias); ok {
|
||||
if r := findTypeParam(obj, aliases.TypeParams(alias), path, opTypeParam); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
if r := find(obj, aliases.Rhs(alias), append(path, opRhs)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
|
||||
} else if tname.IsAlias() {
|
||||
// legacy alias
|
||||
if r := find(obj, T, path); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
|
||||
} else if named, ok := T.(*types.Named); ok {
|
||||
// defined (named) type
|
||||
if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
if r := find(obj, named.Underlying(), append(path, opUnderlying)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then inspect everything else:
|
||||
// non-types, and declared methods of defined types.
|
||||
for _, o := range objs {
|
||||
path := append(empty, o.Name()...)
|
||||
if _, ok := o.(*types.TypeName); !ok {
|
||||
if o.Exported() {
|
||||
// exported non-type (const, var, func)
|
||||
if r := find(obj, o.Type(), append(path, opType)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Inspect declared methods of defined types.
|
||||
if T, ok := types.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,
|
||||
// which ultimately derives from source order
|
||||
// and must be preserved by export data.
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
m := T.Method(i)
|
||||
path2 := appendOpArg(path, opMethod, i)
|
||||
if m == obj {
|
||||
return Path(path2), nil // found declared method
|
||||
}
|
||||
if r := find(obj, m.Type(), append(path2, opType)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path())
|
||||
}
|
||||
|
||||
func appendOpArg(path []byte, op byte, arg int) []byte {
|
||||
path = append(path, op)
|
||||
path = strconv.AppendInt(path, int64(arg), 10)
|
||||
return path
|
||||
}
|
||||
|
||||
// concreteMethod returns the path for meth, which must have a non-nil receiver.
|
||||
// The second return value indicates success and may be false if the method is
|
||||
// an interface method or if it is an instantiated method.
|
||||
//
|
||||
// This function is just an optimization that avoids the general scope walking
|
||||
// approach. You are expected to fall back to the general approach if this
|
||||
// function fails.
|
||||
func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
|
||||
// Concrete methods can only be declared on package-scoped named types. For
|
||||
// that reason we can skip the expensive walk over the package scope: the
|
||||
// path will always be package -> named type -> method. We can trivially get
|
||||
// the type name from the receiver, and only have to look over the type's
|
||||
// methods to find the method index.
|
||||
//
|
||||
// Methods on generic types require special consideration, however. Consider
|
||||
// the following package:
|
||||
//
|
||||
// L1: type S[T any] struct{}
|
||||
// L2: func (recv S[A]) Foo() { recv.Bar() }
|
||||
// L3: func (recv S[B]) Bar() { }
|
||||
// L4: type Alias = S[int]
|
||||
// L5: func _[T any]() { var s S[int]; s.Foo() }
|
||||
//
|
||||
// The receivers of methods on generic types are instantiations. L2 and L3
|
||||
// instantiate S with the type-parameters A and B, which are scoped to the
|
||||
// respective methods. L4 and L5 each instantiate S with int. Each of these
|
||||
// instantiations has its own method set, full of methods (and thus objects)
|
||||
// with receivers whose types are the respective instantiations. In other
|
||||
// words, we have
|
||||
//
|
||||
// S[A].Foo, S[A].Bar
|
||||
// S[B].Foo, S[B].Bar
|
||||
// S[int].Foo, S[int].Bar
|
||||
//
|
||||
// We may thus be trying to produce object paths for any of these objects.
|
||||
//
|
||||
// S[A].Foo and S[B].Bar are the origin methods, and their paths are S.Foo
|
||||
// and S.Bar, which are the paths that this function naturally produces.
|
||||
//
|
||||
// S[A].Bar, S[B].Foo, and both methods on S[int] are instantiations that
|
||||
// don't correspond to the origin methods. For S[int], this is significant.
|
||||
// The most precise object path for S[int].Foo, for example, is Alias.Foo,
|
||||
// not S.Foo. Our function, however, would produce S.Foo, which would
|
||||
// resolve to a different object.
|
||||
//
|
||||
// For S[A].Bar and S[B].Foo it could be argued that S.Bar and S.Foo are
|
||||
// still the correct paths, since only the origin methods have meaningful
|
||||
// paths. But this is likely only true for trivial cases and has edge cases.
|
||||
// Since this function is only an optimization, we err on the side of giving
|
||||
// up, deferring to the slower but definitely correct algorithm. Most users
|
||||
// of objectpath will only be giving us origin methods, anyway, as referring
|
||||
// to instantiated methods is usually not useful.
|
||||
|
||||
if meth.Origin() != meth {
|
||||
return "", false
|
||||
}
|
||||
|
||||
_, named := typesinternal.ReceiverNamed(meth.Type().(*types.Signature).Recv())
|
||||
if named == nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if types.IsInterface(named) {
|
||||
// Named interfaces don't have to be package-scoped
|
||||
//
|
||||
// TODO(dominikh): opt: if scope.Lookup(name) == named, then we can apply this optimization to interface
|
||||
// methods, too, I think.
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Preallocate space for the name, opType, opMethod, and some digits.
|
||||
name := named.Obj().Name()
|
||||
path := make([]byte, 0, len(name)+8)
|
||||
path = append(path, name...)
|
||||
path = append(path, opType)
|
||||
|
||||
// Method indices are w.r.t. the go/types data structures,
|
||||
// ultimately deriving from source order,
|
||||
// which is preserved by export data.
|
||||
for i := 0; i < named.NumMethods(); i++ {
|
||||
if named.Method(i) == meth {
|
||||
path = appendOpArg(path, opMethod, i)
|
||||
return Path(path), true
|
||||
}
|
||||
}
|
||||
|
||||
// Due to golang/go#59944, go/types fails to associate the receiver with
|
||||
// certain methods on cgo types.
|
||||
//
|
||||
// TODO(rfindley): replace this panic once golang/go#59944 is fixed in all Go
|
||||
// versions gopls supports.
|
||||
return "", false
|
||||
// panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named)))
|
||||
}
|
||||
|
||||
// find finds obj within type T, returning the path to it, or nil if not found.
|
||||
//
|
||||
// The seen map is used to short circuit cycles through type parameters. If
|
||||
// nil, it will be allocated as necessary.
|
||||
//
|
||||
// The seenMethods map is used internally to short circuit cycles through
|
||||
// interface methods, such as occur in the following example:
|
||||
//
|
||||
// type I interface { f() interface{I} }
|
||||
//
|
||||
// See golang/go#68046 for details.
|
||||
func find(obj types.Object, T types.Type, path []byte) []byte {
|
||||
return (&finder{obj: obj}).find(T, path)
|
||||
}
|
||||
|
||||
// finder closes over search state for a call to find.
|
||||
type finder struct {
|
||||
obj types.Object // the sought object
|
||||
seenTParamNames map[*types.TypeName]bool // for cycle breaking through type parameters
|
||||
seenMethods map[*types.Func]bool // for cycle breaking through recursive interfaces
|
||||
}
|
||||
|
||||
func (f *finder) find(T types.Type, path []byte) []byte {
|
||||
switch T := T.(type) {
|
||||
case *types.Alias:
|
||||
return f.find(types.Unalias(T), path)
|
||||
case *types.Basic, *types.Named:
|
||||
// Named types belonging to pkg were handled already,
|
||||
// so T must belong to another package. No path.
|
||||
return nil
|
||||
case *types.Pointer:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Slice:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Array:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Chan:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Map:
|
||||
if r := f.find(T.Key(), append(path, opKey)); r != nil {
|
||||
return r
|
||||
}
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Signature:
|
||||
if r := f.findTypeParam(T.RecvTypeParams(), path, opRecvTypeParam); r != nil {
|
||||
return r
|
||||
}
|
||||
if r := f.findTypeParam(T.TypeParams(), path, opTypeParam); r != nil {
|
||||
return r
|
||||
}
|
||||
if r := f.find(T.Params(), append(path, opParams)); r != nil {
|
||||
return r
|
||||
}
|
||||
return f.find(T.Results(), append(path, opResults))
|
||||
case *types.Struct:
|
||||
for i := 0; i < T.NumFields(); i++ {
|
||||
fld := T.Field(i)
|
||||
path2 := appendOpArg(path, opField, i)
|
||||
if fld == f.obj {
|
||||
return path2 // found field var
|
||||
}
|
||||
if r := f.find(fld.Type(), append(path2, opType)); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case *types.Tuple:
|
||||
for i := 0; i < T.Len(); i++ {
|
||||
v := T.At(i)
|
||||
path2 := appendOpArg(path, opAt, i)
|
||||
if v == f.obj {
|
||||
return path2 // found param/result var
|
||||
}
|
||||
if r := f.find(v.Type(), append(path2, opType)); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case *types.Interface:
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
m := T.Method(i)
|
||||
if f.seenMethods[m] {
|
||||
return nil
|
||||
}
|
||||
path2 := appendOpArg(path, opMethod, i)
|
||||
if m == f.obj {
|
||||
return path2 // found interface method
|
||||
}
|
||||
if f.seenMethods == nil {
|
||||
f.seenMethods = make(map[*types.Func]bool)
|
||||
}
|
||||
f.seenMethods[m] = true
|
||||
if r := f.find(m.Type(), append(path2, opType)); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case *types.TypeParam:
|
||||
name := T.Obj()
|
||||
if f.seenTParamNames[name] {
|
||||
return nil
|
||||
}
|
||||
if name == f.obj {
|
||||
return append(path, opObj)
|
||||
}
|
||||
if f.seenTParamNames == nil {
|
||||
f.seenTParamNames = make(map[*types.TypeName]bool)
|
||||
}
|
||||
f.seenTParamNames[name] = true
|
||||
if r := f.find(T.Constraint(), append(path, opConstraint)); r != nil {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
panic(T)
|
||||
}
|
||||
|
||||
func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte) []byte {
|
||||
return (&finder{obj: obj}).findTypeParam(list, path, op)
|
||||
}
|
||||
|
||||
func (f *finder) findTypeParam(list *types.TypeParamList, path []byte, op byte) []byte {
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
tparam := list.At(i)
|
||||
path2 := appendOpArg(path, op, i)
|
||||
if r := f.find(tparam, path2); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Object returns the object denoted by path p within the package pkg.
|
||||
func Object(pkg *types.Package, p Path) (types.Object, error) {
|
||||
pathstr := string(p)
|
||||
if pathstr == "" {
|
||||
return nil, fmt.Errorf("empty path")
|
||||
}
|
||||
|
||||
var pkgobj, suffix string
|
||||
if dot := strings.IndexByte(pathstr, opType); dot < 0 {
|
||||
pkgobj = pathstr
|
||||
} else {
|
||||
pkgobj = pathstr[:dot]
|
||||
suffix = pathstr[dot:] // suffix starts with "."
|
||||
}
|
||||
|
||||
obj := pkg.Scope().Lookup(pkgobj)
|
||||
if obj == nil {
|
||||
return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj)
|
||||
}
|
||||
|
||||
// abstraction of *types.{Pointer,Slice,Array,Chan,Map}
|
||||
type hasElem interface {
|
||||
Elem() types.Type
|
||||
}
|
||||
// abstraction of *types.{Named,Signature}
|
||||
type hasTypeParams interface {
|
||||
TypeParams() *types.TypeParamList
|
||||
}
|
||||
// abstraction of *types.{Alias,Named,TypeParam}
|
||||
type hasObj interface {
|
||||
Obj() *types.TypeName
|
||||
}
|
||||
|
||||
// The loop state is the pair (t, obj),
|
||||
// exactly one of which is non-nil, initially obj.
|
||||
// All suffixes start with '.' (the only object->type operation),
|
||||
// followed by optional type->type operations,
|
||||
// then a type->object operation.
|
||||
// The cycle then repeats.
|
||||
var t types.Type
|
||||
for suffix != "" {
|
||||
code := suffix[0]
|
||||
suffix = suffix[1:]
|
||||
|
||||
// Codes [AFMTr] have an integer operand.
|
||||
var index int
|
||||
switch code {
|
||||
case opAt, opField, opMethod, opTypeParam, opRecvTypeParam:
|
||||
rest := strings.TrimLeft(suffix, "0123456789")
|
||||
numerals := suffix[:len(suffix)-len(rest)]
|
||||
suffix = rest
|
||||
i, err := strconv.Atoi(numerals)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code)
|
||||
}
|
||||
index = int(i)
|
||||
case opObj:
|
||||
// no operand
|
||||
default:
|
||||
// The suffix must end with a type->object operation.
|
||||
if suffix == "" {
|
||||
return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code)
|
||||
}
|
||||
}
|
||||
|
||||
if code == opType {
|
||||
if t != nil {
|
||||
return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType)
|
||||
}
|
||||
t = obj.Type()
|
||||
obj = nil
|
||||
continue
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return nil, fmt.Errorf("invalid path: code %q in object context", code)
|
||||
}
|
||||
|
||||
// Inv: t != nil, obj == nil
|
||||
|
||||
t = types.Unalias(t)
|
||||
switch code {
|
||||
case opElem:
|
||||
hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t)
|
||||
}
|
||||
t = hasElem.Elem()
|
||||
|
||||
case opKey:
|
||||
mapType, ok := t.(*types.Map)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t)
|
||||
}
|
||||
t = mapType.Key()
|
||||
|
||||
case opParams:
|
||||
sig, ok := t.(*types.Signature)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
|
||||
}
|
||||
t = sig.Params()
|
||||
|
||||
case opResults:
|
||||
sig, ok := t.(*types.Signature)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
|
||||
}
|
||||
t = sig.Results()
|
||||
|
||||
case opUnderlying:
|
||||
named, ok := t.(*types.Named)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t)
|
||||
}
|
||||
t = named.Underlying()
|
||||
|
||||
case opRhs:
|
||||
if alias, ok := t.(*types.Alias); ok {
|
||||
t = aliases.Rhs(alias)
|
||||
} else if false && aliases.Enabled() {
|
||||
// The Enabled check is too expensive, so for now we
|
||||
// simply assume that aliases are not enabled.
|
||||
// TODO(adonovan): replace with "if true {" when go1.24 is assured.
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want alias)", code, t, t)
|
||||
}
|
||||
|
||||
case opTypeParam:
|
||||
hasTypeParams, ok := t.(hasTypeParams) // Named, Signature
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t)
|
||||
}
|
||||
tparams := hasTypeParams.TypeParams()
|
||||
if n := tparams.Len(); index >= n {
|
||||
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
|
||||
}
|
||||
t = tparams.At(index)
|
||||
|
||||
case opRecvTypeParam:
|
||||
sig, ok := t.(*types.Signature) // Signature
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
|
||||
}
|
||||
rtparams := sig.RecvTypeParams()
|
||||
if n := rtparams.Len(); index >= n {
|
||||
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
|
||||
}
|
||||
t = rtparams.At(index)
|
||||
|
||||
case opConstraint:
|
||||
tparam, ok := t.(*types.TypeParam)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t)
|
||||
}
|
||||
t = tparam.Constraint()
|
||||
|
||||
case opAt:
|
||||
tuple, ok := t.(*types.Tuple)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t)
|
||||
}
|
||||
if n := tuple.Len(); index >= n {
|
||||
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
|
||||
}
|
||||
obj = tuple.At(index)
|
||||
t = nil
|
||||
|
||||
case opField:
|
||||
structType, ok := t.(*types.Struct)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t)
|
||||
}
|
||||
if n := structType.NumFields(); index >= n {
|
||||
return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n)
|
||||
}
|
||||
obj = structType.Field(index)
|
||||
t = nil
|
||||
|
||||
case opMethod:
|
||||
switch t := t.(type) {
|
||||
case *types.Interface:
|
||||
if index >= t.NumMethods() {
|
||||
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
|
||||
}
|
||||
obj = t.Method(index) // Id-ordered
|
||||
|
||||
case *types.Named:
|
||||
if index >= t.NumMethods() {
|
||||
return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
|
||||
}
|
||||
obj = t.Method(index)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t)
|
||||
}
|
||||
t = nil
|
||||
|
||||
case opObj:
|
||||
hasObj, ok := t.(hasObj)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t)
|
||||
}
|
||||
obj = hasObj.Obj()
|
||||
t = nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid path: unknown code %q", code)
|
||||
}
|
||||
}
|
||||
|
||||
if obj == nil {
|
||||
panic(p) // path does not end in an object-valued operator
|
||||
}
|
||||
|
||||
if obj.Pkg() != pkg {
|
||||
return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj)
|
||||
}
|
||||
|
||||
return obj, nil // success
|
||||
}
|
||||
|
||||
// scopeObjects is a memoization of scope objects.
|
||||
// Callers must not modify the result.
|
||||
func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object {
|
||||
m := enc.scopeMemo
|
||||
if m == nil {
|
||||
m = make(map[*types.Scope][]types.Object)
|
||||
enc.scopeMemo = m
|
||||
}
|
||||
objs, ok := m[scope]
|
||||
if !ok {
|
||||
names := scope.Names() // allocates and sorts
|
||||
objs = make([]types.Object, len(names))
|
||||
for i, name := range names {
|
||||
objs[i] = scope.Lookup(name)
|
||||
}
|
||||
m[scope] = objs
|
||||
}
|
||||
return objs
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
// Copyright 2018 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 typeutil
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
_ "unsafe" // for linkname
|
||||
)
|
||||
|
||||
// Callee returns the named target of a function call, if any:
|
||||
// a function, method, builtin, or variable.
|
||||
//
|
||||
// Functions and methods may potentially have type parameters.
|
||||
//
|
||||
// Note: for calls of instantiated functions and methods, Callee returns
|
||||
// the corresponding generic function or method on the generic type.
|
||||
func Callee(info *types.Info, call *ast.CallExpr) types.Object {
|
||||
obj := info.Uses[usedIdent(info, call.Fun)]
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
if _, ok := obj.(*types.TypeName); ok {
|
||||
return nil
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// StaticCallee returns the target (function or method) of a static function
|
||||
// call, if any. It returns nil for calls to builtins.
|
||||
//
|
||||
// Note: for calls of instantiated functions and methods, StaticCallee returns
|
||||
// the corresponding generic function or method on the generic type.
|
||||
func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func {
|
||||
obj := info.Uses[usedIdent(info, call.Fun)]
|
||||
fn, _ := obj.(*types.Func)
|
||||
if fn == nil || interfaceMethod(fn) {
|
||||
return nil
|
||||
}
|
||||
return fn
|
||||
}
|
||||
|
||||
// usedIdent is the implementation of [internal/typesinternal.UsedIdent].
|
||||
// It returns the identifier associated with e.
|
||||
// See typesinternal.UsedIdent for a fuller description.
|
||||
// This function should live in typesinternal, but cannot because it would
|
||||
// create an import cycle.
|
||||
//
|
||||
//go:linkname usedIdent golang.org/x/tools/go/types/typeutil.usedIdent
|
||||
func usedIdent(info *types.Info, e ast.Expr) *ast.Ident {
|
||||
if info.Types == nil || info.Uses == nil {
|
||||
panic("one of info.Types or info.Uses is nil; both must be populated")
|
||||
}
|
||||
// Look through type instantiation if necessary.
|
||||
switch d := ast.Unparen(e).(type) {
|
||||
case *ast.IndexExpr:
|
||||
if info.Types[d.Index].IsType() {
|
||||
e = d.X
|
||||
}
|
||||
case *ast.IndexListExpr:
|
||||
e = d.X
|
||||
}
|
||||
|
||||
switch e := ast.Unparen(e).(type) {
|
||||
// info.Uses always has the object we want, even for selector expressions.
|
||||
// We don't need info.Selections.
|
||||
// See go/types/recording.go:recordSelection.
|
||||
case *ast.Ident:
|
||||
return e
|
||||
case *ast.SelectorExpr:
|
||||
return e.Sel
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// interfaceMethod reports whether its argument is a method of an interface.
|
||||
// This function should live in typesinternal, but cannot because it would create an import cycle.
|
||||
//
|
||||
//go:linkname interfaceMethod golang.org/x/tools/go/types/typeutil.interfaceMethod
|
||||
func interfaceMethod(f *types.Func) bool {
|
||||
recv := f.Signature().Recv()
|
||||
return recv != nil && types.IsInterface(recv.Type())
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// Copyright 2014 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 typeutil
|
||||
|
||||
import "go/types"
|
||||
|
||||
// Dependencies returns all dependencies of the specified packages.
|
||||
//
|
||||
// Dependent packages appear in topological order: if package P imports
|
||||
// package Q, Q appears earlier than P in the result.
|
||||
// The algorithm follows import statements in the order they
|
||||
// appear in the source code, so the result is a total order.
|
||||
func Dependencies(pkgs ...*types.Package) []*types.Package {
|
||||
var result []*types.Package
|
||||
seen := make(map[*types.Package]bool)
|
||||
var visit func(pkgs []*types.Package)
|
||||
visit = func(pkgs []*types.Package) {
|
||||
for _, p := range pkgs {
|
||||
if !seen[p] {
|
||||
seen[p] = true
|
||||
visit(p.Imports())
|
||||
result = append(result, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(pkgs)
|
||||
return result
|
||||
}
|
||||
+475
@@ -0,0 +1,475 @@
|
||||
// Copyright 2014 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 typeutil defines various utilities for types, such as [Map],
|
||||
// a hash table that maps [types.Type] to any value.
|
||||
package typeutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"hash/maphash"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
// Map is a hash-table-based mapping from types (types.Type) to
|
||||
// arbitrary values. The concrete types that implement
|
||||
// the Type interface are pointers. Since they are not canonicalized,
|
||||
// == cannot be used to check for equivalence, and thus we cannot
|
||||
// simply use a Go map.
|
||||
//
|
||||
// Just as with map[K]V, a nil *Map is a valid empty map.
|
||||
//
|
||||
// Read-only map operations ([Map.At], [Map.Len], and so on) may
|
||||
// safely be called concurrently.
|
||||
//
|
||||
// TODO(adonovan): deprecate in favor of https://go.dev/issues/69420
|
||||
// and 69559, if the latter proposals for a generic hash-map type and
|
||||
// a types.Hash function are accepted.
|
||||
type Map struct {
|
||||
table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
|
||||
length int // number of map entries
|
||||
}
|
||||
|
||||
// entry is an entry (key/value association) in a hash bucket.
|
||||
type entry struct {
|
||||
key types.Type
|
||||
value any
|
||||
}
|
||||
|
||||
// SetHasher has no effect.
|
||||
//
|
||||
// It is a relic of an optimization that is no longer profitable. Do
|
||||
// not use [Hasher], [MakeHasher], or [SetHasher] in new code.
|
||||
func (m *Map) SetHasher(Hasher) {}
|
||||
|
||||
// Delete removes the entry with the given key, if any.
|
||||
// It returns true if the entry was found.
|
||||
func (m *Map) Delete(key types.Type) bool {
|
||||
if m != nil && m.table != nil {
|
||||
hash := hash(key)
|
||||
bucket := m.table[hash]
|
||||
for i, e := range bucket {
|
||||
if e.key != nil && types.Identical(key, e.key) {
|
||||
// We can't compact the bucket as it
|
||||
// would disturb iterators.
|
||||
bucket[i] = entry{}
|
||||
m.length--
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// At returns the map entry for the given key.
|
||||
// The result is nil if the entry is not present.
|
||||
func (m *Map) At(key types.Type) any {
|
||||
if m != nil && m.table != nil {
|
||||
for _, e := range m.table[hash(key)] {
|
||||
if e.key != nil && types.Identical(key, e.key) {
|
||||
return e.value
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set sets the map entry for key to val,
|
||||
// and returns the previous entry, if any.
|
||||
func (m *Map) Set(key types.Type, value any) (prev any) {
|
||||
if m.table != nil {
|
||||
hash := hash(key)
|
||||
bucket := m.table[hash]
|
||||
var hole *entry
|
||||
for i, e := range bucket {
|
||||
if e.key == nil {
|
||||
hole = &bucket[i]
|
||||
} else if types.Identical(key, e.key) {
|
||||
prev = e.value
|
||||
bucket[i].value = value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if hole != nil {
|
||||
*hole = entry{key, value} // overwrite deleted entry
|
||||
} else {
|
||||
m.table[hash] = append(bucket, entry{key, value})
|
||||
}
|
||||
} else {
|
||||
hash := hash(key)
|
||||
m.table = map[uint32][]entry{hash: {entry{key, value}}}
|
||||
}
|
||||
|
||||
m.length++
|
||||
return
|
||||
}
|
||||
|
||||
// Len returns the number of map entries.
|
||||
func (m *Map) Len() int {
|
||||
if m != nil {
|
||||
return m.length
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Iterate calls function f on each entry in the map in unspecified order.
|
||||
//
|
||||
// If f should mutate the map, Iterate provides the same guarantees as
|
||||
// Go maps: if f deletes a map entry that Iterate has not yet reached,
|
||||
// f will not be invoked for it, but if f inserts a map entry that
|
||||
// Iterate has not yet reached, whether or not f will be invoked for
|
||||
// it is unspecified.
|
||||
func (m *Map) Iterate(f func(key types.Type, value any)) {
|
||||
if m != nil {
|
||||
for _, bucket := range m.table {
|
||||
for _, e := range bucket {
|
||||
if e.key != nil {
|
||||
f(e.key, e.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keys returns a new slice containing the set of map keys.
|
||||
// The order is unspecified.
|
||||
func (m *Map) Keys() []types.Type {
|
||||
keys := make([]types.Type, 0, m.Len())
|
||||
m.Iterate(func(key types.Type, _ any) {
|
||||
keys = append(keys, key)
|
||||
})
|
||||
return keys
|
||||
}
|
||||
|
||||
func (m *Map) toString(values bool) string {
|
||||
if m == nil {
|
||||
return "{}"
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, "{")
|
||||
sep := ""
|
||||
m.Iterate(func(key types.Type, value any) {
|
||||
fmt.Fprint(&buf, sep)
|
||||
sep = ", "
|
||||
fmt.Fprint(&buf, key)
|
||||
if values {
|
||||
fmt.Fprintf(&buf, ": %q", value)
|
||||
}
|
||||
})
|
||||
fmt.Fprint(&buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// String returns a string representation of the map's entries.
|
||||
// Values are printed using fmt.Sprintf("%v", v).
|
||||
// Order is unspecified.
|
||||
func (m *Map) String() string {
|
||||
return m.toString(true)
|
||||
}
|
||||
|
||||
// KeysString returns a string representation of the map's key set.
|
||||
// Order is unspecified.
|
||||
func (m *Map) KeysString() string {
|
||||
return m.toString(false)
|
||||
}
|
||||
|
||||
// -- Hasher --
|
||||
|
||||
// hash returns the hash of type t.
|
||||
// TODO(adonovan): replace by types.Hash when Go proposal #69420 is accepted.
|
||||
func hash(t types.Type) uint32 {
|
||||
return theHasher.Hash(t)
|
||||
}
|
||||
|
||||
// A Hasher provides a [Hasher.Hash] method to map a type to its hash value.
|
||||
// Hashers are stateless, and all are equivalent.
|
||||
type Hasher struct{}
|
||||
|
||||
var theHasher Hasher
|
||||
|
||||
// MakeHasher returns Hasher{}.
|
||||
// Hashers are stateless; all are equivalent.
|
||||
func MakeHasher() Hasher { return theHasher }
|
||||
|
||||
// Hash computes a hash value for the given type t such that
|
||||
// Identical(t, t') => Hash(t) == Hash(t').
|
||||
func (h Hasher) Hash(t types.Type) uint32 {
|
||||
return hasher{inGenericSig: false}.hash(t)
|
||||
}
|
||||
|
||||
// hasher holds the state of a single Hash traversal: whether we are
|
||||
// inside the signature of a generic function; this is used to
|
||||
// optimize [hasher.hashTypeParam].
|
||||
type hasher struct{ inGenericSig bool }
|
||||
|
||||
// hashString computes the Fowler–Noll–Vo hash of s.
|
||||
func hashString(s string) uint32 {
|
||||
var h uint32
|
||||
for i := 0; i < len(s); i++ {
|
||||
h ^= uint32(s[i])
|
||||
h *= 16777619
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// hash computes the hash of t.
|
||||
func (h hasher) hash(t types.Type) uint32 {
|
||||
// See Identical for rationale.
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
return uint32(t.Kind())
|
||||
|
||||
case *types.Alias:
|
||||
return h.hash(types.Unalias(t))
|
||||
|
||||
case *types.Array:
|
||||
return 9043 + 2*uint32(t.Len()) + 3*h.hash(t.Elem())
|
||||
|
||||
case *types.Slice:
|
||||
return 9049 + 2*h.hash(t.Elem())
|
||||
|
||||
case *types.Struct:
|
||||
var hash uint32 = 9059
|
||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||
f := t.Field(i)
|
||||
if f.Anonymous() {
|
||||
hash += 8861
|
||||
}
|
||||
hash += hashString(t.Tag(i))
|
||||
hash += hashString(f.Name()) // (ignore f.Pkg)
|
||||
hash += h.hash(f.Type())
|
||||
}
|
||||
return hash
|
||||
|
||||
case *types.Pointer:
|
||||
return 9067 + 2*h.hash(t.Elem())
|
||||
|
||||
case *types.Signature:
|
||||
var hash uint32 = 9091
|
||||
if t.Variadic() {
|
||||
hash *= 8863
|
||||
}
|
||||
|
||||
tparams := t.TypeParams()
|
||||
if n := tparams.Len(); n > 0 {
|
||||
h.inGenericSig = true // affects constraints, params, and results
|
||||
|
||||
for i := range n {
|
||||
tparam := tparams.At(i)
|
||||
hash += 7 * h.hash(tparam.Constraint())
|
||||
}
|
||||
}
|
||||
|
||||
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
|
||||
|
||||
case *types.Union:
|
||||
return h.hashUnion(t)
|
||||
|
||||
case *types.Interface:
|
||||
// Interfaces are identical if they have the same set of methods, with
|
||||
// identical names and types, and they have the same set of type
|
||||
// restrictions. See go/types.identical for more details.
|
||||
var hash uint32 = 9103
|
||||
|
||||
// Hash methods.
|
||||
for i, n := 0, t.NumMethods(); i < n; i++ {
|
||||
// Method order is not significant.
|
||||
// Ignore m.Pkg().
|
||||
m := t.Method(i)
|
||||
// Use shallow hash on method signature to
|
||||
// avoid anonymous interface cycles.
|
||||
hash += 3*hashString(m.Name()) + 5*h.shallowHash(m.Type())
|
||||
}
|
||||
|
||||
// Hash type restrictions.
|
||||
terms, err := typeparams.InterfaceTermSet(t)
|
||||
// if err != nil t has invalid type restrictions.
|
||||
if err == nil {
|
||||
hash += h.hashTermSet(terms)
|
||||
}
|
||||
|
||||
return hash
|
||||
|
||||
case *types.Map:
|
||||
return 9109 + 2*h.hash(t.Key()) + 3*h.hash(t.Elem())
|
||||
|
||||
case *types.Chan:
|
||||
return 9127 + 2*uint32(t.Dir()) + 3*h.hash(t.Elem())
|
||||
|
||||
case *types.Named:
|
||||
hash := h.hashTypeName(t.Obj())
|
||||
targs := t.TypeArgs()
|
||||
for i := 0; i < targs.Len(); i++ {
|
||||
targ := targs.At(i)
|
||||
hash += 2 * h.hash(targ)
|
||||
}
|
||||
return hash
|
||||
|
||||
case *types.TypeParam:
|
||||
return h.hashTypeParam(t)
|
||||
|
||||
case *types.Tuple:
|
||||
return h.hashTuple(t)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("%T: %v", t, t))
|
||||
}
|
||||
|
||||
func (h hasher) hashTuple(tuple *types.Tuple) uint32 {
|
||||
// See go/types.identicalTypes for rationale.
|
||||
n := tuple.Len()
|
||||
hash := 9137 + 2*uint32(n)
|
||||
for i := range n {
|
||||
hash += 3 * h.hash(tuple.At(i).Type())
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
func (h hasher) hashUnion(t *types.Union) uint32 {
|
||||
// Hash type restrictions.
|
||||
terms, err := typeparams.UnionTermSet(t)
|
||||
// if err != nil t has invalid type restrictions. Fall back on a non-zero
|
||||
// hash.
|
||||
if err != nil {
|
||||
return 9151
|
||||
}
|
||||
return h.hashTermSet(terms)
|
||||
}
|
||||
|
||||
func (h hasher) hashTermSet(terms []*types.Term) uint32 {
|
||||
hash := 9157 + 2*uint32(len(terms))
|
||||
for _, term := range terms {
|
||||
// term order is not significant.
|
||||
termHash := h.hash(term.Type())
|
||||
if term.Tilde() {
|
||||
termHash *= 9161
|
||||
}
|
||||
hash += 3 * termHash
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
// hashTypeParam returns the hash of a type parameter.
|
||||
func (h hasher) hashTypeParam(t *types.TypeParam) uint32 {
|
||||
// Within the signature of a generic function, TypeParams are
|
||||
// identical if they have the same index and constraint, so we
|
||||
// hash them based on index.
|
||||
//
|
||||
// When we are outside a generic function, free TypeParams are
|
||||
// identical iff they are the same object, so we can use a
|
||||
// more discriminating hash consistent with object identity.
|
||||
// This optimization saves [Map] about 4% when hashing all the
|
||||
// types.Info.Types in the forward closure of net/http.
|
||||
if !h.inGenericSig {
|
||||
// Optimization: outside a generic function signature,
|
||||
// use a more discrimating hash consistent with object identity.
|
||||
return h.hashTypeName(t.Obj())
|
||||
}
|
||||
return 9173 + 3*uint32(t.Index())
|
||||
}
|
||||
|
||||
var theSeed = maphash.MakeSeed()
|
||||
|
||||
// hashTypeName hashes the pointer of tname.
|
||||
func (hasher) hashTypeName(tname *types.TypeName) uint32 {
|
||||
// Since types.Identical uses == to compare TypeNames,
|
||||
// the Hash function uses maphash.Comparable.
|
||||
// TODO(adonovan): or will, when it becomes available in go1.24.
|
||||
// In the meantime we use the pointer's numeric value.
|
||||
//
|
||||
// hash := maphash.Comparable(theSeed, tname)
|
||||
//
|
||||
// (Another approach would be to hash the name and package
|
||||
// path, and whether or not it is a package-level typename. It
|
||||
// is rare for a package to define multiple local types with
|
||||
// the same name.)
|
||||
ptr := uintptr(unsafe.Pointer(tname))
|
||||
if unsafe.Sizeof(ptr) == 8 {
|
||||
hash := uint64(ptr)
|
||||
return uint32(hash ^ (hash >> 32))
|
||||
} else {
|
||||
return uint32(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
// shallowHash computes a hash of t without looking at any of its
|
||||
// element Types, to avoid potential anonymous cycles in the types of
|
||||
// interface methods.
|
||||
//
|
||||
// When an unnamed non-empty interface type appears anywhere among the
|
||||
// arguments or results of an interface method, there is a potential
|
||||
// for endless recursion. Consider:
|
||||
//
|
||||
// type X interface { m() []*interface { X } }
|
||||
//
|
||||
// The problem is that the Methods of the interface in m's result type
|
||||
// include m itself; there is no mention of the named type X that
|
||||
// might help us break the cycle.
|
||||
// (See comment in go/types.identical, case *Interface, for more.)
|
||||
func (h hasher) shallowHash(t types.Type) uint32 {
|
||||
// t is the type of an interface method (Signature),
|
||||
// its params or results (Tuples), or their immediate
|
||||
// elements (mostly Slice, Pointer, Basic, Named),
|
||||
// so there's no need to optimize anything else.
|
||||
switch t := t.(type) {
|
||||
case *types.Alias:
|
||||
return h.shallowHash(types.Unalias(t))
|
||||
|
||||
case *types.Signature:
|
||||
var hash uint32 = 604171
|
||||
if t.Variadic() {
|
||||
hash *= 971767
|
||||
}
|
||||
// The Signature/Tuple recursion is always finite
|
||||
// and invariably shallow.
|
||||
return hash + 1062599*h.shallowHash(t.Params()) + 1282529*h.shallowHash(t.Results())
|
||||
|
||||
case *types.Tuple:
|
||||
n := t.Len()
|
||||
hash := 9137 + 2*uint32(n)
|
||||
for i := range n {
|
||||
hash += 53471161 * h.shallowHash(t.At(i).Type())
|
||||
}
|
||||
return hash
|
||||
|
||||
case *types.Basic:
|
||||
return 45212177 * uint32(t.Kind())
|
||||
|
||||
case *types.Array:
|
||||
return 1524181 + 2*uint32(t.Len())
|
||||
|
||||
case *types.Slice:
|
||||
return 2690201
|
||||
|
||||
case *types.Struct:
|
||||
return 3326489
|
||||
|
||||
case *types.Pointer:
|
||||
return 4393139
|
||||
|
||||
case *types.Union:
|
||||
return 562448657
|
||||
|
||||
case *types.Interface:
|
||||
return 2124679 // no recursion here
|
||||
|
||||
case *types.Map:
|
||||
return 9109
|
||||
|
||||
case *types.Chan:
|
||||
return 9127
|
||||
|
||||
case *types.Named:
|
||||
return h.hashTypeName(t.Obj())
|
||||
|
||||
case *types.TypeParam:
|
||||
return h.hashTypeParam(t)
|
||||
}
|
||||
panic(fmt.Sprintf("shallowHash: %T: %v", t, t))
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// This file implements a cache of method sets.
|
||||
|
||||
package typeutil
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A MethodSetCache records the method set of each type T for which
|
||||
// MethodSet(T) is called so that repeat queries are fast.
|
||||
// The zero value is a ready-to-use cache instance.
|
||||
type MethodSetCache struct {
|
||||
mu sync.Mutex
|
||||
named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
|
||||
others map[types.Type]*types.MethodSet // all other types
|
||||
}
|
||||
|
||||
// MethodSet returns the method set of type T. It is thread-safe.
|
||||
//
|
||||
// If cache is nil, this function is equivalent to types.NewMethodSet(T).
|
||||
// Utility functions can thus expose an optional *MethodSetCache
|
||||
// parameter to clients that care about performance.
|
||||
func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
|
||||
if cache == nil {
|
||||
return types.NewMethodSet(T)
|
||||
}
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
switch T := types.Unalias(T).(type) {
|
||||
case *types.Named:
|
||||
return cache.lookupNamed(T).value
|
||||
|
||||
case *types.Pointer:
|
||||
if N, ok := types.Unalias(T.Elem()).(*types.Named); ok {
|
||||
return cache.lookupNamed(N).pointer
|
||||
}
|
||||
}
|
||||
|
||||
// all other types
|
||||
// (The map uses pointer equivalence, not type identity.)
|
||||
mset := cache.others[T]
|
||||
if mset == nil {
|
||||
mset = types.NewMethodSet(T)
|
||||
if cache.others == nil {
|
||||
cache.others = make(map[types.Type]*types.MethodSet)
|
||||
}
|
||||
cache.others[T] = mset
|
||||
}
|
||||
return mset
|
||||
}
|
||||
|
||||
func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
|
||||
if cache.named == nil {
|
||||
cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
|
||||
}
|
||||
// Avoid recomputing mset(*T) for each distinct Pointer
|
||||
// instance whose underlying type is a named type.
|
||||
msets, ok := cache.named[named]
|
||||
if !ok {
|
||||
msets.value = types.NewMethodSet(named)
|
||||
msets.pointer = types.NewMethodSet(types.NewPointer(named))
|
||||
cache.named[named] = msets
|
||||
}
|
||||
return msets
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// Copyright 2014 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 typeutil
|
||||
|
||||
// This file defines utilities for user interfaces that display types.
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// IntuitiveMethodSet returns the intuitive method set of a type T,
|
||||
// which is the set of methods you can call on an addressable value of
|
||||
// that type.
|
||||
//
|
||||
// The result always contains MethodSet(T), and is exactly MethodSet(T)
|
||||
// for interface types and for pointer-to-concrete types.
|
||||
// For all other concrete types T, the result additionally
|
||||
// contains each method belonging to *T if there is no identically
|
||||
// named method on T itself.
|
||||
//
|
||||
// This corresponds to user intuition about method sets;
|
||||
// this function is intended only for user interfaces.
|
||||
//
|
||||
// The order of the result is as for types.MethodSet(T).
|
||||
func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
|
||||
isPointerToConcrete := func(T types.Type) bool {
|
||||
ptr, ok := types.Unalias(T).(*types.Pointer)
|
||||
return ok && !types.IsInterface(ptr.Elem())
|
||||
}
|
||||
|
||||
var result []*types.Selection
|
||||
mset := msets.MethodSet(T)
|
||||
if types.IsInterface(T) || isPointerToConcrete(T) {
|
||||
for i, n := 0, mset.Len(); i < n; i++ {
|
||||
result = append(result, mset.At(i))
|
||||
}
|
||||
} else {
|
||||
// T is some other concrete type.
|
||||
// Report methods of T and *T, preferring those of T.
|
||||
pmset := msets.MethodSet(types.NewPointer(T))
|
||||
for i, n := 0, pmset.Len(); i < n; i++ {
|
||||
meth := pmset.At(i)
|
||||
if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
|
||||
meth = m
|
||||
}
|
||||
result = append(result, meth)
|
||||
}
|
||||
|
||||
}
|
||||
return result
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// 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.
|
||||
//
|
||||
// Precondition: enabled || len(tparams)==0.
|
||||
// If materialized aliases are disabled, there must not be any type parameters.
|
||||
func NewAlias(enabled bool, pos token.Pos, pkg *types.Package, name string, rhs types.Type, tparams []*types.TypeParam) *types.TypeName {
|
||||
if enabled {
|
||||
tname := types.NewTypeName(pos, pkg, name, nil)
|
||||
SetTypeParams(types.NewAlias(tname, rhs), tparams)
|
||||
return tname
|
||||
}
|
||||
if len(tparams) > 0 {
|
||||
panic("cannot create an alias with type parameters when gotypesalias is not enabled")
|
||||
}
|
||||
return types.NewTypeName(pos, pkg, name, rhs)
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// 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/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// Rhs returns the type on the right-hand side of the alias declaration.
|
||||
func Rhs(alias *types.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 types.Unalias(alias)
|
||||
}
|
||||
|
||||
// TypeParams returns the type parameter list of the alias.
|
||||
func TypeParams(alias *types.Alias) *types.TypeParamList {
|
||||
if alias, ok := any(alias).(interface{ TypeParams() *types.TypeParamList }); ok {
|
||||
return alias.TypeParams() // go1.23+
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTypeParams sets the type parameters of the alias type.
|
||||
func SetTypeParams(alias *types.Alias, tparams []*types.TypeParam) {
|
||||
if alias, ok := any(alias).(interface {
|
||||
SetTypeParams(tparams []*types.TypeParam)
|
||||
}); ok {
|
||||
alias.SetTypeParams(tparams) // go1.23+
|
||||
} else if len(tparams) > 0 {
|
||||
panic("cannot set type parameters of an Alias type in go1.22")
|
||||
}
|
||||
}
|
||||
|
||||
// TypeArgs returns the type arguments used to instantiate the Alias type.
|
||||
func TypeArgs(alias *types.Alias) *types.TypeList {
|
||||
if alias, ok := any(alias).(interface{ TypeArgs() *types.TypeList }); ok {
|
||||
return alias.TypeArgs() // go1.23+
|
||||
}
|
||||
return nil // empty (go1.22)
|
||||
}
|
||||
|
||||
// Origin returns the generic Alias type of which alias is an instance.
|
||||
// If alias is not an instance of a generic alias, Origin returns alias.
|
||||
func Origin(alias *types.Alias) *types.Alias {
|
||||
if alias, ok := any(alias).(interface{ Origin() *types.Alias }); ok {
|
||||
return alias.Origin() // go1.23+
|
||||
}
|
||||
return alias // not an instance of a generic alias (go1.22)
|
||||
}
|
||||
|
||||
// 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", parser.SkipObjectResolution)
|
||||
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
|
||||
_, enabled := pkg.Scope().Lookup("A").Type().(*types.Alias)
|
||||
return enabled
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
// 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 core provides support for event based telemetry.
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/tools/internal/event/label"
|
||||
)
|
||||
|
||||
// Event holds the information about an event of note that occurred.
|
||||
type Event struct {
|
||||
at time.Time
|
||||
|
||||
// As events are often on the stack, storing the first few labels directly
|
||||
// in the event can avoid an allocation at all for the very common cases of
|
||||
// simple events.
|
||||
// The length needs to be large enough to cope with the majority of events
|
||||
// but no so large as to cause undue stack pressure.
|
||||
// A log message with two values will use 3 labels (one for each value and
|
||||
// one for the message itself).
|
||||
|
||||
static [3]label.Label // inline storage for the first few labels
|
||||
dynamic []label.Label // dynamically sized storage for remaining labels
|
||||
}
|
||||
|
||||
// eventLabelMap implements label.Map for a the labels of an Event.
|
||||
type eventLabelMap struct {
|
||||
event Event
|
||||
}
|
||||
|
||||
func (ev Event) At() time.Time { return ev.at }
|
||||
|
||||
func (ev Event) Format(f fmt.State, r rune) {
|
||||
if !ev.at.IsZero() {
|
||||
fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 "))
|
||||
}
|
||||
for index := 0; ev.Valid(index); index++ {
|
||||
if l := ev.Label(index); l.Valid() {
|
||||
fmt.Fprintf(f, "\n\t%v", l)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ev Event) Valid(index int) bool {
|
||||
return index >= 0 && index < len(ev.static)+len(ev.dynamic)
|
||||
}
|
||||
|
||||
func (ev Event) Label(index int) label.Label {
|
||||
if index < len(ev.static) {
|
||||
return ev.static[index]
|
||||
}
|
||||
return ev.dynamic[index-len(ev.static)]
|
||||
}
|
||||
|
||||
func (ev Event) Find(key label.Key) label.Label {
|
||||
for _, l := range ev.static {
|
||||
if l.Key() == key {
|
||||
return l
|
||||
}
|
||||
}
|
||||
for _, l := range ev.dynamic {
|
||||
if l.Key() == key {
|
||||
return l
|
||||
}
|
||||
}
|
||||
return label.Label{}
|
||||
}
|
||||
|
||||
func MakeEvent(static [3]label.Label, labels []label.Label) Event {
|
||||
return Event{
|
||||
static: static,
|
||||
dynamic: labels,
|
||||
}
|
||||
}
|
||||
|
||||
// CloneEvent event returns a copy of the event with the time adjusted to at.
|
||||
func CloneEvent(ev Event, at time.Time) Event {
|
||||
ev.at = at
|
||||
return ev
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// 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 core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/tools/internal/event/label"
|
||||
)
|
||||
|
||||
// Exporter is a function that handles events.
|
||||
// It may return a modified context and event.
|
||||
type Exporter func(context.Context, Event, label.Map) context.Context
|
||||
|
||||
var (
|
||||
exporter unsafe.Pointer
|
||||
)
|
||||
|
||||
// SetExporter sets the global exporter function that handles all events.
|
||||
// The exporter is called synchronously from the event call site, so it should
|
||||
// return quickly so as not to hold up user code.
|
||||
func SetExporter(e Exporter) {
|
||||
p := unsafe.Pointer(&e)
|
||||
if e == nil {
|
||||
// &e is always valid, and so p is always valid, but for the early abort
|
||||
// of ProcessEvent to be efficient it needs to make the nil check on the
|
||||
// pointer without having to dereference it, so we make the nil function
|
||||
// also a nil pointer
|
||||
p = nil
|
||||
}
|
||||
atomic.StorePointer(&exporter, p)
|
||||
}
|
||||
|
||||
// deliver is called to deliver an event to the supplied exporter.
|
||||
// it will fill in the time.
|
||||
func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context {
|
||||
// add the current time to the event
|
||||
ev.at = time.Now()
|
||||
// hand the event off to the current exporter
|
||||
return exporter(ctx, ev, ev)
|
||||
}
|
||||
|
||||
// Export is called to deliver an event to the global exporter if set.
|
||||
func Export(ctx context.Context, ev Event) context.Context {
|
||||
// get the global exporter and abort early if there is not one
|
||||
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
|
||||
if exporterPtr == nil {
|
||||
return ctx
|
||||
}
|
||||
return deliver(ctx, *exporterPtr, ev)
|
||||
}
|
||||
|
||||
// ExportPair is called to deliver a start event to the supplied exporter.
|
||||
// It also returns a function that will deliver the end event to the same
|
||||
// exporter.
|
||||
// It will fill in the time.
|
||||
func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) {
|
||||
// get the global exporter and abort early if there is not one
|
||||
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
|
||||
if exporterPtr == nil {
|
||||
return ctx, func() {}
|
||||
}
|
||||
ctx = deliver(ctx, *exporterPtr, begin)
|
||||
return ctx, func() { deliver(ctx, *exporterPtr, end) }
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// 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 core
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/tools/internal/event/keys"
|
||||
"golang.org/x/tools/internal/event/label"
|
||||
)
|
||||
|
||||
// Log1 takes a message and one label delivers a log event to the exporter.
|
||||
// It is a customized version of Print that is faster and does no allocation.
|
||||
func Log1(ctx context.Context, message string, t1 label.Label) {
|
||||
Export(ctx, MakeEvent([3]label.Label{
|
||||
keys.Msg.Of(message),
|
||||
t1,
|
||||
}, nil))
|
||||
}
|
||||
|
||||
// Log2 takes a message and two labels and delivers a log event to the exporter.
|
||||
// It is a customized version of Print that is faster and does no allocation.
|
||||
func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) {
|
||||
Export(ctx, MakeEvent([3]label.Label{
|
||||
keys.Msg.Of(message),
|
||||
t1,
|
||||
t2,
|
||||
}, nil))
|
||||
}
|
||||
|
||||
// Metric1 sends a label event to the exporter with the supplied labels.
|
||||
func Metric1(ctx context.Context, t1 label.Label) context.Context {
|
||||
return Export(ctx, MakeEvent([3]label.Label{
|
||||
keys.Metric.New(),
|
||||
t1,
|
||||
}, nil))
|
||||
}
|
||||
|
||||
// Metric2 sends a label event to the exporter with the supplied labels.
|
||||
func Metric2(ctx context.Context, t1, t2 label.Label) context.Context {
|
||||
return Export(ctx, MakeEvent([3]label.Label{
|
||||
keys.Metric.New(),
|
||||
t1,
|
||||
t2,
|
||||
}, nil))
|
||||
}
|
||||
|
||||
// Start1 sends a span start event with the supplied label list to the exporter.
|
||||
// It also returns a function that will end the span, which should normally be
|
||||
// deferred.
|
||||
func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) {
|
||||
return ExportPair(ctx,
|
||||
MakeEvent([3]label.Label{
|
||||
keys.Start.Of(name),
|
||||
t1,
|
||||
}, nil),
|
||||
MakeEvent([3]label.Label{
|
||||
keys.End.New(),
|
||||
}, nil))
|
||||
}
|
||||
|
||||
// Start2 sends a span start event with the supplied label list to the exporter.
|
||||
// It also returns a function that will end the span, which should normally be
|
||||
// deferred.
|
||||
func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) {
|
||||
return ExportPair(ctx,
|
||||
MakeEvent([3]label.Label{
|
||||
keys.Start.Of(name),
|
||||
t1,
|
||||
t2,
|
||||
}, nil),
|
||||
MakeEvent([3]label.Label{
|
||||
keys.End.New(),
|
||||
}, nil))
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// 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 event provides a set of packages that cover the main
|
||||
// concepts of telemetry in an implementation agnostic way.
|
||||
package event
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
// 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 event
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"golang.org/x/tools/internal/event/core"
|
||||
"golang.org/x/tools/internal/event/keys"
|
||||
"golang.org/x/tools/internal/event/label"
|
||||
)
|
||||
|
||||
// Exporter is a function that handles events.
|
||||
// It may return a modified context and event.
|
||||
type Exporter func(context.Context, core.Event, label.Map) context.Context
|
||||
|
||||
// SetExporter sets the global exporter function that handles all events.
|
||||
// The exporter is called synchronously from the event call site, so it should
|
||||
// return quickly so as not to hold up user code.
|
||||
func SetExporter(e Exporter) {
|
||||
core.SetExporter(core.Exporter(e))
|
||||
}
|
||||
|
||||
// Log takes a message and a label list and combines them into a single event
|
||||
// before delivering them to the exporter.
|
||||
func Log(ctx context.Context, message string, labels ...label.Label) {
|
||||
core.Export(ctx, core.MakeEvent([3]label.Label{
|
||||
keys.Msg.Of(message),
|
||||
}, labels))
|
||||
}
|
||||
|
||||
// IsLog returns true if the event was built by the Log function.
|
||||
// It is intended to be used in exporters to identify the semantics of the
|
||||
// event when deciding what to do with it.
|
||||
func IsLog(ev core.Event) bool {
|
||||
return ev.Label(0).Key() == keys.Msg
|
||||
}
|
||||
|
||||
// Error takes a message and a label list and combines them into a single event
|
||||
// before delivering them to the exporter. It captures the error in the
|
||||
// delivered event.
|
||||
func Error(ctx context.Context, message string, err error, labels ...label.Label) {
|
||||
core.Export(ctx, core.MakeEvent([3]label.Label{
|
||||
keys.Msg.Of(message),
|
||||
keys.Err.Of(err),
|
||||
}, labels))
|
||||
}
|
||||
|
||||
// IsError returns true if the event was built by the Error function.
|
||||
// It is intended to be used in exporters to identify the semantics of the
|
||||
// event when deciding what to do with it.
|
||||
func IsError(ev core.Event) bool {
|
||||
return ev.Label(0).Key() == keys.Msg &&
|
||||
ev.Label(1).Key() == keys.Err
|
||||
}
|
||||
|
||||
// Metric sends a label event to the exporter with the supplied labels.
|
||||
func Metric(ctx context.Context, labels ...label.Label) {
|
||||
core.Export(ctx, core.MakeEvent([3]label.Label{
|
||||
keys.Metric.New(),
|
||||
}, labels))
|
||||
}
|
||||
|
||||
// IsMetric returns true if the event was built by the Metric function.
|
||||
// It is intended to be used in exporters to identify the semantics of the
|
||||
// event when deciding what to do with it.
|
||||
func IsMetric(ev core.Event) bool {
|
||||
return ev.Label(0).Key() == keys.Metric
|
||||
}
|
||||
|
||||
// Label sends a label event to the exporter with the supplied labels.
|
||||
func Label(ctx context.Context, labels ...label.Label) context.Context {
|
||||
return core.Export(ctx, core.MakeEvent([3]label.Label{
|
||||
keys.Label.New(),
|
||||
}, labels))
|
||||
}
|
||||
|
||||
// IsLabel returns true if the event was built by the Label function.
|
||||
// It is intended to be used in exporters to identify the semantics of the
|
||||
// event when deciding what to do with it.
|
||||
func IsLabel(ev core.Event) bool {
|
||||
return ev.Label(0).Key() == keys.Label
|
||||
}
|
||||
|
||||
// Start sends a span start event with the supplied label list to the exporter.
|
||||
// It also returns a function that will end the span, which should normally be
|
||||
// deferred.
|
||||
func Start(ctx context.Context, name string, labels ...label.Label) (context.Context, func()) {
|
||||
return core.ExportPair(ctx,
|
||||
core.MakeEvent([3]label.Label{
|
||||
keys.Start.Of(name),
|
||||
}, labels),
|
||||
core.MakeEvent([3]label.Label{
|
||||
keys.End.New(),
|
||||
}, nil))
|
||||
}
|
||||
|
||||
// IsStart returns true if the event was built by the Start function.
|
||||
// It is intended to be used in exporters to identify the semantics of the
|
||||
// event when deciding what to do with it.
|
||||
func IsStart(ev core.Event) bool {
|
||||
return ev.Label(0).Key() == keys.Start
|
||||
}
|
||||
|
||||
// IsEnd returns true if the event was built by the End function.
|
||||
// It is intended to be used in exporters to identify the semantics of the
|
||||
// event when deciding what to do with it.
|
||||
func IsEnd(ev core.Event) bool {
|
||||
return ev.Label(0).Key() == keys.End
|
||||
}
|
||||
|
||||
// Detach returns a context without an associated span.
|
||||
// This allows the creation of spans that are not children of the current span.
|
||||
func Detach(ctx context.Context) context.Context {
|
||||
return core.Export(ctx, core.MakeEvent([3]label.Label{
|
||||
keys.Detach.New(),
|
||||
}, nil))
|
||||
}
|
||||
|
||||
// IsDetach returns true if the event was built by the Detach function.
|
||||
// It is intended to be used in exporters to identify the semantics of the
|
||||
// event when deciding what to do with it.
|
||||
func IsDetach(ev core.Event) bool {
|
||||
return ev.Label(0).Key() == keys.Detach
|
||||
}
|
||||
+564
@@ -0,0 +1,564 @@
|
||||
// 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 keys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/tools/internal/event/label"
|
||||
)
|
||||
|
||||
// Value represents a key for untyped values.
|
||||
type Value struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// New creates a new Key for untyped values.
|
||||
func New(name, description string) *Value {
|
||||
return &Value{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *Value) Name() string { return k.name }
|
||||
func (k *Value) Description() string { return k.description }
|
||||
|
||||
func (k *Value) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
fmt.Fprint(w, k.From(l))
|
||||
}
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *Value) Get(lm label.Map) any {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *Value) From(t label.Label) any { return t.UnpackValue() }
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *Value) Of(value any) label.Label { return label.OfValue(k, value) }
|
||||
|
||||
// Tag represents a key for tagging labels that have no value.
|
||||
// These are used when the existence of the label is the entire information it
|
||||
// carries, such as marking events to be of a specific kind, or from a specific
|
||||
// package.
|
||||
type Tag struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewTag creates a new Key for tagging labels.
|
||||
func NewTag(name, description string) *Tag {
|
||||
return &Tag{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *Tag) Name() string { return k.name }
|
||||
func (k *Tag) Description() string { return k.description }
|
||||
|
||||
func (k *Tag) Format(w io.Writer, buf []byte, l label.Label) {}
|
||||
|
||||
// New creates a new Label with this key.
|
||||
func (k *Tag) New() label.Label { return label.OfValue(k, nil) }
|
||||
|
||||
// Int represents a key
|
||||
type Int struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewInt creates a new Key for int values.
|
||||
func NewInt(name, description string) *Int {
|
||||
return &Int{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *Int) Name() string { return k.name }
|
||||
func (k *Int) Description() string { return k.description }
|
||||
|
||||
func (k *Int) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *Int) Of(v int) label.Label { return label.Of64(k, uint64(v)) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *Int) Get(lm label.Map) int {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *Int) From(t label.Label) int { return int(t.Unpack64()) }
|
||||
|
||||
// Int8 represents a key
|
||||
type Int8 struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewInt8 creates a new Key for int8 values.
|
||||
func NewInt8(name, description string) *Int8 {
|
||||
return &Int8{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *Int8) Name() string { return k.name }
|
||||
func (k *Int8) Description() string { return k.description }
|
||||
|
||||
func (k *Int8) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *Int8) Of(v int8) label.Label { return label.Of64(k, uint64(v)) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *Int8) Get(lm label.Map) int8 {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *Int8) From(t label.Label) int8 { return int8(t.Unpack64()) }
|
||||
|
||||
// Int16 represents a key
|
||||
type Int16 struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewInt16 creates a new Key for int16 values.
|
||||
func NewInt16(name, description string) *Int16 {
|
||||
return &Int16{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *Int16) Name() string { return k.name }
|
||||
func (k *Int16) Description() string { return k.description }
|
||||
|
||||
func (k *Int16) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *Int16) Of(v int16) label.Label { return label.Of64(k, uint64(v)) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *Int16) Get(lm label.Map) int16 {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *Int16) From(t label.Label) int16 { return int16(t.Unpack64()) }
|
||||
|
||||
// Int32 represents a key
|
||||
type Int32 struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewInt32 creates a new Key for int32 values.
|
||||
func NewInt32(name, description string) *Int32 {
|
||||
return &Int32{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *Int32) Name() string { return k.name }
|
||||
func (k *Int32) Description() string { return k.description }
|
||||
|
||||
func (k *Int32) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *Int32) Of(v int32) label.Label { return label.Of64(k, uint64(v)) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *Int32) Get(lm label.Map) int32 {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *Int32) From(t label.Label) int32 { return int32(t.Unpack64()) }
|
||||
|
||||
// Int64 represents a key
|
||||
type Int64 struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewInt64 creates a new Key for int64 values.
|
||||
func NewInt64(name, description string) *Int64 {
|
||||
return &Int64{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *Int64) Name() string { return k.name }
|
||||
func (k *Int64) Description() string { return k.description }
|
||||
|
||||
func (k *Int64) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendInt(buf, k.From(l), 10))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *Int64) Of(v int64) label.Label { return label.Of64(k, uint64(v)) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *Int64) Get(lm label.Map) int64 {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *Int64) From(t label.Label) int64 { return int64(t.Unpack64()) }
|
||||
|
||||
// UInt represents a key
|
||||
type UInt struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewUInt creates a new Key for uint values.
|
||||
func NewUInt(name, description string) *UInt {
|
||||
return &UInt{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *UInt) Name() string { return k.name }
|
||||
func (k *UInt) Description() string { return k.description }
|
||||
|
||||
func (k *UInt) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *UInt) Of(v uint) label.Label { return label.Of64(k, uint64(v)) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *UInt) Get(lm label.Map) uint {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *UInt) From(t label.Label) uint { return uint(t.Unpack64()) }
|
||||
|
||||
// UInt8 represents a key
|
||||
type UInt8 struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewUInt8 creates a new Key for uint8 values.
|
||||
func NewUInt8(name, description string) *UInt8 {
|
||||
return &UInt8{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *UInt8) Name() string { return k.name }
|
||||
func (k *UInt8) Description() string { return k.description }
|
||||
|
||||
func (k *UInt8) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *UInt8) Of(v uint8) label.Label { return label.Of64(k, uint64(v)) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *UInt8) Get(lm label.Map) uint8 {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *UInt8) From(t label.Label) uint8 { return uint8(t.Unpack64()) }
|
||||
|
||||
// UInt16 represents a key
|
||||
type UInt16 struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewUInt16 creates a new Key for uint16 values.
|
||||
func NewUInt16(name, description string) *UInt16 {
|
||||
return &UInt16{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *UInt16) Name() string { return k.name }
|
||||
func (k *UInt16) Description() string { return k.description }
|
||||
|
||||
func (k *UInt16) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *UInt16) Of(v uint16) label.Label { return label.Of64(k, uint64(v)) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *UInt16) Get(lm label.Map) uint16 {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *UInt16) From(t label.Label) uint16 { return uint16(t.Unpack64()) }
|
||||
|
||||
// UInt32 represents a key
|
||||
type UInt32 struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewUInt32 creates a new Key for uint32 values.
|
||||
func NewUInt32(name, description string) *UInt32 {
|
||||
return &UInt32{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *UInt32) Name() string { return k.name }
|
||||
func (k *UInt32) Description() string { return k.description }
|
||||
|
||||
func (k *UInt32) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *UInt32) Of(v uint32) label.Label { return label.Of64(k, uint64(v)) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *UInt32) Get(lm label.Map) uint32 {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *UInt32) From(t label.Label) uint32 { return uint32(t.Unpack64()) }
|
||||
|
||||
// UInt64 represents a key
|
||||
type UInt64 struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewUInt64 creates a new Key for uint64 values.
|
||||
func NewUInt64(name, description string) *UInt64 {
|
||||
return &UInt64{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *UInt64) Name() string { return k.name }
|
||||
func (k *UInt64) Description() string { return k.description }
|
||||
|
||||
func (k *UInt64) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendUint(buf, k.From(l), 10))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *UInt64) Of(v uint64) label.Label { return label.Of64(k, v) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *UInt64) Get(lm label.Map) uint64 {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *UInt64) From(t label.Label) uint64 { return t.Unpack64() }
|
||||
|
||||
// Float32 represents a key
|
||||
type Float32 struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewFloat32 creates a new Key for float32 values.
|
||||
func NewFloat32(name, description string) *Float32 {
|
||||
return &Float32{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *Float32) Name() string { return k.name }
|
||||
func (k *Float32) Description() string { return k.description }
|
||||
|
||||
func (k *Float32) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendFloat(buf, float64(k.From(l)), 'E', -1, 32))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *Float32) Of(v float32) label.Label {
|
||||
return label.Of64(k, uint64(math.Float32bits(v)))
|
||||
}
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *Float32) Get(lm label.Map) float32 {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *Float32) From(t label.Label) float32 {
|
||||
return math.Float32frombits(uint32(t.Unpack64()))
|
||||
}
|
||||
|
||||
// Float64 represents a key
|
||||
type Float64 struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewFloat64 creates a new Key for int64 values.
|
||||
func NewFloat64(name, description string) *Float64 {
|
||||
return &Float64{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *Float64) Name() string { return k.name }
|
||||
func (k *Float64) Description() string { return k.description }
|
||||
|
||||
func (k *Float64) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendFloat(buf, k.From(l), 'E', -1, 64))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *Float64) Of(v float64) label.Label {
|
||||
return label.Of64(k, math.Float64bits(v))
|
||||
}
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *Float64) Get(lm label.Map) float64 {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *Float64) From(t label.Label) float64 {
|
||||
return math.Float64frombits(t.Unpack64())
|
||||
}
|
||||
|
||||
// String represents a key
|
||||
type String struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewString creates a new Key for int64 values.
|
||||
func NewString(name, description string) *String {
|
||||
return &String{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *String) Name() string { return k.name }
|
||||
func (k *String) Description() string { return k.description }
|
||||
|
||||
func (k *String) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendQuote(buf, k.From(l)))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *String) Of(v string) label.Label { return label.OfString(k, v) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *String) Get(lm label.Map) string {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *String) From(t label.Label) string { return t.UnpackString() }
|
||||
|
||||
// Boolean represents a key
|
||||
type Boolean struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewBoolean creates a new Key for bool values.
|
||||
func NewBoolean(name, description string) *Boolean {
|
||||
return &Boolean{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *Boolean) Name() string { return k.name }
|
||||
func (k *Boolean) Description() string { return k.description }
|
||||
|
||||
func (k *Boolean) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
w.Write(strconv.AppendBool(buf, k.From(l)))
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *Boolean) Of(v bool) label.Label {
|
||||
if v {
|
||||
return label.Of64(k, 1)
|
||||
}
|
||||
return label.Of64(k, 0)
|
||||
}
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *Boolean) Get(lm label.Map) bool {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *Boolean) From(t label.Label) bool { return t.Unpack64() > 0 }
|
||||
|
||||
// Error represents a key
|
||||
type Error struct {
|
||||
name string
|
||||
description string
|
||||
}
|
||||
|
||||
// NewError creates a new Key for int64 values.
|
||||
func NewError(name, description string) *Error {
|
||||
return &Error{name: name, description: description}
|
||||
}
|
||||
|
||||
func (k *Error) Name() string { return k.name }
|
||||
func (k *Error) Description() string { return k.description }
|
||||
|
||||
func (k *Error) Format(w io.Writer, buf []byte, l label.Label) {
|
||||
io.WriteString(w, k.From(l).Error())
|
||||
}
|
||||
|
||||
// Of creates a new Label with this key and the supplied value.
|
||||
func (k *Error) Of(v error) label.Label { return label.OfValue(k, v) }
|
||||
|
||||
// Get can be used to get a label for the key from a label.Map.
|
||||
func (k *Error) Get(lm label.Map) error {
|
||||
if t := lm.Find(k); t.Valid() {
|
||||
return k.From(t)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// From can be used to get a value from a Label.
|
||||
func (k *Error) From(t label.Label) error {
|
||||
err, _ := t.UnpackValue().(error)
|
||||
return err
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// Copyright 2020 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 keys
|
||||
|
||||
var (
|
||||
// Msg is a key used to add message strings to label lists.
|
||||
Msg = NewString("message", "a readable message")
|
||||
// Label is a key used to indicate an event adds labels to the context.
|
||||
Label = NewTag("label", "a label context marker")
|
||||
// Start is used for things like traces that have a name.
|
||||
Start = NewString("start", "span start")
|
||||
// Metric is a key used to indicate an event records metrics.
|
||||
End = NewTag("end", "a span end marker")
|
||||
// Metric is a key used to indicate an event records metrics.
|
||||
Detach = NewTag("detach", "a span detach marker")
|
||||
// Err is a key used to add error values to label lists.
|
||||
Err = NewError("error", "an error that occurred")
|
||||
// Metric is a key used to indicate an event records metrics.
|
||||
Metric = NewTag("metric", "a metric event marker")
|
||||
)
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// 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 keys
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Join returns a canonical join of the keys in S:
|
||||
// a sorted comma-separated string list.
|
||||
func Join[S ~[]T, T ~string](s S) string {
|
||||
strs := make([]string, 0, len(s))
|
||||
for _, v := range s {
|
||||
strs = append(strs, string(v))
|
||||
}
|
||||
sort.Strings(strs)
|
||||
return strings.Join(strs, ",")
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
// 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 label
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"slices"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Key is used as the identity of a Label.
|
||||
// Keys are intended to be compared by pointer only, the name should be unique
|
||||
// for communicating with external systems, but it is not required or enforced.
|
||||
type Key interface {
|
||||
// Name returns the key name.
|
||||
Name() string
|
||||
// Description returns a string that can be used to describe the value.
|
||||
Description() string
|
||||
|
||||
// Format is used in formatting to append the value of the label to the
|
||||
// supplied buffer.
|
||||
// The formatter may use the supplied buf as a scratch area to avoid
|
||||
// allocations.
|
||||
Format(w io.Writer, buf []byte, l Label)
|
||||
}
|
||||
|
||||
// Label holds a key and value pair.
|
||||
// It is normally used when passing around lists of labels.
|
||||
type Label struct {
|
||||
key Key
|
||||
packed uint64
|
||||
untyped any
|
||||
}
|
||||
|
||||
// Map is the interface to a collection of Labels indexed by key.
|
||||
type Map interface {
|
||||
// Find returns the label that matches the supplied key.
|
||||
Find(key Key) Label
|
||||
}
|
||||
|
||||
// List is the interface to something that provides an iterable
|
||||
// list of labels.
|
||||
// Iteration should start from 0 and continue until Valid returns false.
|
||||
type List interface {
|
||||
// Valid returns true if the index is within range for the list.
|
||||
// It does not imply the label at that index will itself be valid.
|
||||
Valid(index int) bool
|
||||
// Label returns the label at the given index.
|
||||
Label(index int) Label
|
||||
}
|
||||
|
||||
// list implements LabelList for a list of Labels.
|
||||
type list struct {
|
||||
labels []Label
|
||||
}
|
||||
|
||||
// filter wraps a LabelList filtering out specific labels.
|
||||
type filter struct {
|
||||
keys []Key
|
||||
underlying List
|
||||
}
|
||||
|
||||
// listMap implements LabelMap for a simple list of labels.
|
||||
type listMap struct {
|
||||
labels []Label
|
||||
}
|
||||
|
||||
// mapChain implements LabelMap for a list of underlying LabelMap.
|
||||
type mapChain struct {
|
||||
maps []Map
|
||||
}
|
||||
|
||||
// OfValue creates a new label from the key and value.
|
||||
// This method is for implementing new key types, label creation should
|
||||
// normally be done with the Of method of the key.
|
||||
func OfValue(k Key, value any) Label { return Label{key: k, untyped: value} }
|
||||
|
||||
// UnpackValue assumes the label was built using LabelOfValue and returns the value
|
||||
// that was passed to that constructor.
|
||||
// This method is for implementing new key types, for type safety normal
|
||||
// access should be done with the From method of the key.
|
||||
func (t Label) UnpackValue() any { return t.untyped }
|
||||
|
||||
// Of64 creates a new label from a key and a uint64. This is often
|
||||
// used for non uint64 values that can be packed into a uint64.
|
||||
// This method is for implementing new key types, label creation should
|
||||
// normally be done with the Of method of the key.
|
||||
func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} }
|
||||
|
||||
// Unpack64 assumes the label was built using LabelOf64 and returns the value that
|
||||
// was passed to that constructor.
|
||||
// This method is for implementing new key types, for type safety normal
|
||||
// access should be done with the From method of the key.
|
||||
func (t Label) Unpack64() uint64 { return t.packed }
|
||||
|
||||
type stringptr unsafe.Pointer
|
||||
|
||||
// OfString creates a new label from a key and a string.
|
||||
// This method is for implementing new key types, label creation should
|
||||
// normally be done with the Of method of the key.
|
||||
func OfString(k Key, v string) Label {
|
||||
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
|
||||
return Label{
|
||||
key: k,
|
||||
packed: uint64(hdr.Len),
|
||||
untyped: stringptr(hdr.Data),
|
||||
}
|
||||
}
|
||||
|
||||
// UnpackString assumes the label was built using LabelOfString and returns the
|
||||
// value that was passed to that constructor.
|
||||
// This method is for implementing new key types, for type safety normal
|
||||
// access should be done with the From method of the key.
|
||||
func (t Label) UnpackString() string {
|
||||
var v string
|
||||
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
|
||||
hdr.Data = uintptr(t.untyped.(stringptr))
|
||||
hdr.Len = int(t.packed)
|
||||
return v
|
||||
}
|
||||
|
||||
// Valid returns true if the Label is a valid one (it has a key).
|
||||
func (t Label) Valid() bool { return t.key != nil }
|
||||
|
||||
// Key returns the key of this Label.
|
||||
func (t Label) Key() Key { return t.key }
|
||||
|
||||
// Format is used for debug printing of labels.
|
||||
func (t Label) Format(f fmt.State, r rune) {
|
||||
if !t.Valid() {
|
||||
io.WriteString(f, `nil`)
|
||||
return
|
||||
}
|
||||
io.WriteString(f, t.Key().Name())
|
||||
io.WriteString(f, "=")
|
||||
var buf [128]byte
|
||||
t.Key().Format(f, buf[:0], t)
|
||||
}
|
||||
|
||||
func (l *list) Valid(index int) bool {
|
||||
return index >= 0 && index < len(l.labels)
|
||||
}
|
||||
|
||||
func (l *list) Label(index int) Label {
|
||||
return l.labels[index]
|
||||
}
|
||||
|
||||
func (f *filter) Valid(index int) bool {
|
||||
return f.underlying.Valid(index)
|
||||
}
|
||||
|
||||
func (f *filter) Label(index int) Label {
|
||||
l := f.underlying.Label(index)
|
||||
if slices.Contains(f.keys, l.Key()) {
|
||||
return Label{}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (lm listMap) Find(key Key) Label {
|
||||
for _, l := range lm.labels {
|
||||
if l.Key() == key {
|
||||
return l
|
||||
}
|
||||
}
|
||||
return Label{}
|
||||
}
|
||||
|
||||
func (c mapChain) Find(key Key) Label {
|
||||
for _, src := range c.maps {
|
||||
l := src.Find(key)
|
||||
if l.Valid() {
|
||||
return l
|
||||
}
|
||||
}
|
||||
return Label{}
|
||||
}
|
||||
|
||||
var emptyList = &list{}
|
||||
|
||||
func NewList(labels ...Label) List {
|
||||
if len(labels) == 0 {
|
||||
return emptyList
|
||||
}
|
||||
return &list{labels: labels}
|
||||
}
|
||||
|
||||
func Filter(l List, keys ...Key) List {
|
||||
if len(keys) == 0 {
|
||||
return l
|
||||
}
|
||||
return &filter{keys: keys, underlying: l}
|
||||
}
|
||||
|
||||
func NewMap(labels ...Label) Map {
|
||||
return listMap{labels: labels}
|
||||
}
|
||||
|
||||
func MergeMaps(srcs ...Map) Map {
|
||||
var nonNil []Map
|
||||
for _, src := range srcs {
|
||||
if src != nil {
|
||||
nonNil = append(nonNil, src)
|
||||
}
|
||||
}
|
||||
if len(nonNil) == 1 {
|
||||
return nonNil[0]
|
||||
}
|
||||
return mapChain{maps: nonNil}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// This file contains the remaining vestiges of
|
||||
// $GOROOT/src/go/internal/gcimporter/bimport.go.
|
||||
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func errorf(format string, args ...any) {
|
||||
panic(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
|
||||
|
||||
// Synthesize a token.Pos
|
||||
type fakeFileSet struct {
|
||||
fset *token.FileSet
|
||||
files map[string]*fileInfo
|
||||
}
|
||||
|
||||
type fileInfo struct {
|
||||
file *token.File
|
||||
lastline int
|
||||
}
|
||||
|
||||
const maxlines = 64 * 1024
|
||||
|
||||
func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
|
||||
// TODO(mdempsky): Make use of column.
|
||||
|
||||
// Since we don't know the set of needed file positions, we reserve maxlines
|
||||
// positions per file. We delay calling token.File.SetLines until all
|
||||
// positions have been calculated (by way of fakeFileSet.setLines), so that
|
||||
// we can avoid setting unnecessary lines. See also golang/go#46586.
|
||||
f := s.files[file]
|
||||
if f == nil {
|
||||
f = &fileInfo{file: s.fset.AddFile(file, -1, maxlines)}
|
||||
s.files[file] = f
|
||||
}
|
||||
if line > maxlines {
|
||||
line = 1
|
||||
}
|
||||
if line > f.lastline {
|
||||
f.lastline = line
|
||||
}
|
||||
|
||||
// Return a fake position assuming that f.file consists only of newlines.
|
||||
return token.Pos(f.file.Base() + line - 1)
|
||||
}
|
||||
|
||||
func (s *fakeFileSet) setLines() {
|
||||
fakeLinesOnce.Do(func() {
|
||||
fakeLines = make([]int, maxlines)
|
||||
for i := range fakeLines {
|
||||
fakeLines[i] = i
|
||||
}
|
||||
})
|
||||
for _, f := range s.files {
|
||||
f.file.SetLines(fakeLines[:f.lastline])
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
fakeLines []int
|
||||
fakeLinesOnce sync.Once
|
||||
)
|
||||
|
||||
func chanDir(d int) types.ChanDir {
|
||||
// tag values must match the constants in cmd/compile/internal/gc/go.go
|
||||
switch d {
|
||||
case 1 /* Crecv */ :
|
||||
return types.RecvOnly
|
||||
case 2 /* Csend */ :
|
||||
return types.SendOnly
|
||||
case 3 /* Cboth */ :
|
||||
return types.SendRecv
|
||||
default:
|
||||
errorf("unexpected channel dir %d", d)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
+421
@@ -0,0 +1,421 @@
|
||||
// Copyright 2011 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.
|
||||
|
||||
// This file should be kept in sync with $GOROOT/src/internal/exportdata/exportdata.go.
|
||||
// This file also additionally implements FindExportData for gcexportdata.NewReader.
|
||||
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// FindExportData positions the reader r at the beginning of the
|
||||
// export data section of an underlying cmd/compile created archive
|
||||
// file by reading from it. The reader must be positioned at the
|
||||
// start of the file before calling this function.
|
||||
// This returns the length of the export data in bytes.
|
||||
//
|
||||
// This function is needed by [gcexportdata.Read], which must
|
||||
// accept inputs produced by the last two releases of cmd/compile,
|
||||
// plus tip.
|
||||
func FindExportData(r *bufio.Reader) (size int64, err error) {
|
||||
arsize, err := FindPackageDefinition(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
size = int64(arsize)
|
||||
|
||||
objapi, headers, err := ReadObjectHeaders(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
size -= int64(len(objapi))
|
||||
for _, h := range headers {
|
||||
size -= int64(len(h))
|
||||
}
|
||||
|
||||
// Check for the binary export data section header "$$B\n".
|
||||
// TODO(taking): Unify with ReadExportDataHeader so that it stops at the 'u' instead of reading
|
||||
line, err := r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
hdr := string(line)
|
||||
if hdr != "$$B\n" {
|
||||
err = fmt.Errorf("unknown export data header: %q", hdr)
|
||||
return
|
||||
}
|
||||
size -= int64(len(hdr))
|
||||
|
||||
// For files with a binary export data header "$$B\n",
|
||||
// these are always terminated by an end-of-section marker "\n$$\n".
|
||||
// So the last bytes must always be this constant.
|
||||
//
|
||||
// The end-of-section marker is not a part of the export data itself.
|
||||
// Do not include these in size.
|
||||
//
|
||||
// It would be nice to have sanity check that the final bytes after
|
||||
// the export data are indeed the end-of-section marker. The split
|
||||
// of gcexportdata.NewReader and gcexportdata.Read make checking this
|
||||
// ugly so gcimporter gives up enforcing this. The compiler and go/types
|
||||
// importer do enforce this, which seems good enough.
|
||||
const endofsection = "\n$$\n"
|
||||
size -= int64(len(endofsection))
|
||||
|
||||
if size < 0 {
|
||||
err = fmt.Errorf("invalid size (%d) in the archive file: %d bytes remain without section headers (recompile package)", arsize, size)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReadUnified reads the contents of the unified export data from a reader r
|
||||
// that contains the contents of a GC-created archive file.
|
||||
//
|
||||
// On success, the reader will be positioned after the end-of-section marker "\n$$\n".
|
||||
//
|
||||
// Supported GC-created archive files have 4 layers of nesting:
|
||||
// - An archive file containing a package definition file.
|
||||
// - The package definition file contains headers followed by a data section.
|
||||
// Headers are lines (≤ 4kb) that do not start with "$$".
|
||||
// - The data section starts with "$$B\n" followed by export data followed
|
||||
// by an end of section marker "\n$$\n". (The section start "$$\n" is no
|
||||
// longer supported.)
|
||||
// - The export data starts with a format byte ('u') followed by the <data> in
|
||||
// the given format. (See ReadExportDataHeader for older formats.)
|
||||
//
|
||||
// Putting this together, the bytes in a GC-created archive files are expected
|
||||
// to look like the following.
|
||||
// See cmd/internal/archive for more details on ar file headers.
|
||||
//
|
||||
// | <!arch>\n | ar file signature
|
||||
// | __.PKGDEF...size...\n | ar header for __.PKGDEF including size.
|
||||
// | go object <...>\n | objabi header
|
||||
// | <optional headers>\n | other headers such as build id
|
||||
// | $$B\n | binary format marker
|
||||
// | u<data>\n | unified export <data>
|
||||
// | $$\n | end-of-section marker
|
||||
// | [optional padding] | padding byte (0x0A) if size is odd
|
||||
// | [ar file header] | other ar files
|
||||
// | [ar file data] |
|
||||
func ReadUnified(r *bufio.Reader) (data []byte, err error) {
|
||||
// We historically guaranteed headers at the default buffer size (4096) work.
|
||||
// This ensures we can use ReadSlice throughout.
|
||||
const minBufferSize = 4096
|
||||
r = bufio.NewReaderSize(r, minBufferSize)
|
||||
|
||||
size, err := FindPackageDefinition(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n := size
|
||||
|
||||
objapi, headers, err := ReadObjectHeaders(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n -= len(objapi)
|
||||
for _, h := range headers {
|
||||
n -= len(h)
|
||||
}
|
||||
|
||||
hdrlen, err := ReadExportDataHeader(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n -= hdrlen
|
||||
|
||||
// size also includes the end of section marker. Remove that many bytes from the end.
|
||||
const marker = "\n$$\n"
|
||||
n -= len(marker)
|
||||
|
||||
if n < 0 {
|
||||
err = fmt.Errorf("invalid size (%d) in the archive file: %d bytes remain without section headers (recompile package)", size, n)
|
||||
return
|
||||
}
|
||||
|
||||
// Read n bytes from buf.
|
||||
data = make([]byte, n)
|
||||
_, err = io.ReadFull(r, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Check for marker at the end.
|
||||
var suffix [len(marker)]byte
|
||||
_, err = io.ReadFull(r, suffix[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if s := string(suffix[:]); s != marker {
|
||||
err = fmt.Errorf("read %q instead of end-of-section marker (%q)", s, marker)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindPackageDefinition positions the reader r at the beginning of a package
|
||||
// definition file ("__.PKGDEF") within a GC-created archive by reading
|
||||
// from it, and returns the size of the package definition file in the archive.
|
||||
//
|
||||
// The reader must be positioned at the start of the archive file before calling
|
||||
// this function, and "__.PKGDEF" is assumed to be the first file in the archive.
|
||||
//
|
||||
// See cmd/internal/archive for details on the archive format.
|
||||
func FindPackageDefinition(r *bufio.Reader) (size int, err error) {
|
||||
// Uses ReadSlice to limit risk of malformed inputs.
|
||||
|
||||
// Read first line to make sure this is an object file.
|
||||
line, err := r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can't find export data (%v)", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Is the first line an archive file signature?
|
||||
if string(line) != "!<arch>\n" {
|
||||
err = fmt.Errorf("not the start of an archive file (%q)", line)
|
||||
return
|
||||
}
|
||||
|
||||
// package export block should be first
|
||||
size = readArchiveHeader(r, "__.PKGDEF")
|
||||
if size <= 0 {
|
||||
err = fmt.Errorf("not a package file")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReadObjectHeaders reads object headers from the reader. Object headers are
|
||||
// lines that do not start with an end-of-section marker "$$". The first header
|
||||
// is the objabi header. On success, the reader will be positioned at the beginning
|
||||
// of the end-of-section marker.
|
||||
//
|
||||
// It returns an error if any header does not fit in r.Size() bytes.
|
||||
func ReadObjectHeaders(r *bufio.Reader) (objapi string, headers []string, err error) {
|
||||
// line is a temporary buffer for headers.
|
||||
// Use bounded reads (ReadSlice, Peek) to limit risk of malformed inputs.
|
||||
var line []byte
|
||||
|
||||
// objapi header should be the first line
|
||||
if line, err = r.ReadSlice('\n'); err != nil {
|
||||
err = fmt.Errorf("can't find export data (%v)", err)
|
||||
return
|
||||
}
|
||||
objapi = string(line)
|
||||
|
||||
// objapi header begins with "go object ".
|
||||
if !strings.HasPrefix(objapi, "go object ") {
|
||||
err = fmt.Errorf("not a go object file: %s", objapi)
|
||||
return
|
||||
}
|
||||
|
||||
// process remaining object header lines
|
||||
for {
|
||||
// check for an end of section marker "$$"
|
||||
line, err = r.Peek(2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if string(line) == "$$" {
|
||||
return // stop
|
||||
}
|
||||
|
||||
// read next header
|
||||
line, err = r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
headers = append(headers, string(line))
|
||||
}
|
||||
}
|
||||
|
||||
// ReadExportDataHeader reads the export data header and format from r.
|
||||
// It returns the number of bytes read, or an error if the format is no longer
|
||||
// supported or it failed to read.
|
||||
//
|
||||
// The only currently supported format is binary export data in the
|
||||
// unified export format.
|
||||
func ReadExportDataHeader(r *bufio.Reader) (n int, err error) {
|
||||
// Read export data header.
|
||||
line, err := r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
hdr := string(line)
|
||||
switch hdr {
|
||||
case "$$\n":
|
||||
err = fmt.Errorf("old textual export format no longer supported (recompile package)")
|
||||
return
|
||||
|
||||
case "$$B\n":
|
||||
var format byte
|
||||
format, err = r.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// The unified export format starts with a 'u'.
|
||||
switch format {
|
||||
case 'u':
|
||||
default:
|
||||
// Older no longer supported export formats include:
|
||||
// indexed export format which started with an 'i'; and
|
||||
// the older binary export format which started with a 'c',
|
||||
// 'd', or 'v' (from "version").
|
||||
err = fmt.Errorf("binary export format %q is no longer supported (recompile package)", format)
|
||||
return
|
||||
}
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("unknown export data header: %q", hdr)
|
||||
return
|
||||
}
|
||||
|
||||
n = len(hdr) + 1 // + 1 is for 'u'
|
||||
return
|
||||
}
|
||||
|
||||
// FindPkg returns the filename and unique package id for an import
|
||||
// path based on package information provided by build.Import (using
|
||||
// the build.Default build.Context). A relative srcDir is interpreted
|
||||
// relative to the current working directory.
|
||||
//
|
||||
// FindPkg is only used in tests within x/tools.
|
||||
func FindPkg(path, srcDir string) (filename, id string, err error) {
|
||||
// TODO(taking): Move internal/exportdata.FindPkg into its own file,
|
||||
// and then this copy into a _test package.
|
||||
if path == "" {
|
||||
return "", "", errors.New("path is empty")
|
||||
}
|
||||
|
||||
var noext string
|
||||
switch {
|
||||
default:
|
||||
// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
|
||||
// Don't require the source files to be present.
|
||||
if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282
|
||||
srcDir = abs
|
||||
}
|
||||
var bp *build.Package
|
||||
bp, err = build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
|
||||
if bp.PkgObj == "" {
|
||||
if bp.Goroot && bp.Dir != "" {
|
||||
filename, err = lookupGorootExport(bp.Dir)
|
||||
if err == nil {
|
||||
_, err = os.Stat(filename)
|
||||
}
|
||||
if err == nil {
|
||||
return filename, bp.ImportPath, nil
|
||||
}
|
||||
}
|
||||
goto notfound
|
||||
} else {
|
||||
noext = strings.TrimSuffix(bp.PkgObj, ".a")
|
||||
}
|
||||
id = bp.ImportPath
|
||||
|
||||
case build.IsLocalImport(path):
|
||||
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
|
||||
noext = filepath.Join(srcDir, path)
|
||||
id = noext
|
||||
|
||||
case filepath.IsAbs(path):
|
||||
// for completeness only - go/build.Import
|
||||
// does not support absolute imports
|
||||
// "/x" -> "/x.ext", "/x"
|
||||
noext = path
|
||||
id = path
|
||||
}
|
||||
|
||||
if false { // for debugging
|
||||
if path != id {
|
||||
fmt.Printf("%s -> %s\n", path, id)
|
||||
}
|
||||
}
|
||||
|
||||
// try extensions
|
||||
for _, ext := range pkgExts {
|
||||
filename = noext + ext
|
||||
f, statErr := os.Stat(filename)
|
||||
if statErr == nil && !f.IsDir() {
|
||||
return filename, id, nil
|
||||
}
|
||||
if err == nil {
|
||||
err = statErr
|
||||
}
|
||||
}
|
||||
|
||||
notfound:
|
||||
if err == nil {
|
||||
return "", path, fmt.Errorf("can't find import: %q", path)
|
||||
}
|
||||
return "", path, fmt.Errorf("can't find import: %q: %w", path, err)
|
||||
}
|
||||
|
||||
var pkgExts = [...]string{".a", ".o"} // a file from the build cache will have no extension
|
||||
|
||||
var exportMap sync.Map // package dir → func() (string, error)
|
||||
|
||||
// lookupGorootExport returns the location of the export data
|
||||
// (normally found in the build cache, but located in GOROOT/pkg
|
||||
// in prior Go releases) for the package located in pkgDir.
|
||||
//
|
||||
// (We use the package's directory instead of its import path
|
||||
// mainly to simplify handling of the packages in src/vendor
|
||||
// and cmd/vendor.)
|
||||
//
|
||||
// lookupGorootExport is only used in tests within x/tools.
|
||||
func lookupGorootExport(pkgDir string) (string, error) {
|
||||
f, ok := exportMap.Load(pkgDir)
|
||||
if !ok {
|
||||
var (
|
||||
listOnce sync.Once
|
||||
exportPath string
|
||||
err error
|
||||
)
|
||||
f, _ = exportMap.LoadOrStore(pkgDir, func() (string, error) {
|
||||
listOnce.Do(func() {
|
||||
cmd := exec.Command(filepath.Join(build.Default.GOROOT, "bin", "go"), "list", "-export", "-f", "{{.Export}}", pkgDir)
|
||||
cmd.Dir = build.Default.GOROOT
|
||||
cmd.Env = append(os.Environ(), "PWD="+cmd.Dir, "GOROOT="+build.Default.GOROOT)
|
||||
var output []byte
|
||||
output, err = cmd.Output()
|
||||
if err != nil {
|
||||
if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
|
||||
err = errors.New(string(ee.Stderr))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
exports := strings.Split(string(bytes.TrimSpace(output)), "\n")
|
||||
if len(exports) != 1 {
|
||||
err = fmt.Errorf("go list reported %d exports; expected 1", len(exports))
|
||||
return
|
||||
}
|
||||
|
||||
exportPath = exports[0]
|
||||
})
|
||||
|
||||
return exportPath, err
|
||||
})
|
||||
}
|
||||
|
||||
return f.(func() (string, error))()
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
// Copyright 2011 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.
|
||||
|
||||
// This file is a reduced copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go.
|
||||
|
||||
// Package gcimporter provides various functions for reading
|
||||
// gc-generated object files that can be used to implement the
|
||||
// Importer interface defined by the Go 1.5 standard library package.
|
||||
//
|
||||
// The encoding is deterministic: if the encoder is applied twice to
|
||||
// the same types.Package data structure, both encodings are equal.
|
||||
// This property may be important to avoid spurious changes in
|
||||
// applications such as build systems.
|
||||
//
|
||||
// However, the encoder is not necessarily idempotent. Importing an
|
||||
// exported package may yield a types.Package that, while it
|
||||
// represents the same set of Go types as the original, may differ in
|
||||
// the details of its internal representation. Because of these
|
||||
// differences, re-encoding the imported package may yield a
|
||||
// different, but equally valid, encoding of the package.
|
||||
package gcimporter // import "golang.org/x/tools/internal/gcimporter"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
// Enable debug during development: it adds some additional checks, and
|
||||
// prevents errors from being recovered.
|
||||
debug = false
|
||||
|
||||
// If trace is set, debugging output is printed to std out.
|
||||
trace = false
|
||||
)
|
||||
|
||||
// Import imports a gc-generated package given its import path and srcDir, adds
|
||||
// the corresponding package object to the packages map, and returns the object.
|
||||
// The packages map must contain all packages already imported.
|
||||
//
|
||||
// Import is only used in tests.
|
||||
func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) {
|
||||
var rc io.ReadCloser
|
||||
var id string
|
||||
if lookup != nil {
|
||||
// With custom lookup specified, assume that caller has
|
||||
// converted path to a canonical import path for use in the map.
|
||||
if path == "unsafe" {
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
id = path
|
||||
|
||||
// No need to re-import if the package was imported completely before.
|
||||
if pkg = packages[id]; pkg != nil && pkg.Complete() {
|
||||
return
|
||||
}
|
||||
f, err := lookup(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc = f
|
||||
} else {
|
||||
var filename string
|
||||
filename, id, err = FindPkg(path, srcDir)
|
||||
if filename == "" {
|
||||
if path == "unsafe" {
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// no need to re-import if the package was imported completely before
|
||||
if pkg = packages[id]; pkg != nil && pkg.Complete() {
|
||||
return
|
||||
}
|
||||
|
||||
// open file
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// add file name to error
|
||||
err = fmt.Errorf("%s: %v", filename, err)
|
||||
}
|
||||
}()
|
||||
rc = f
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
buf := bufio.NewReader(rc)
|
||||
data, err := ReadUnified(buf)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("import %q: %v", path, err)
|
||||
return
|
||||
}
|
||||
|
||||
// unified: emitted by cmd/compile since go1.20.
|
||||
_, pkg, err = UImportData(fset, packages, data, id)
|
||||
|
||||
return
|
||||
}
|
||||
+1596
File diff suppressed because it is too large.
Load diff
+1120
File diff suppressed because it is too large.
Load diff
+53
@@ -0,0 +1,53 @@
|
||||
// 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 && !go1.24
|
||||
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// TODO(rfindley): delete this workaround once go1.24 is assured.
|
||||
|
||||
func init() {
|
||||
// Update markBlack so that it correctly sets the color
|
||||
// of imported TypeNames.
|
||||
//
|
||||
// See the doc comment for markBlack for details.
|
||||
|
||||
type color uint32
|
||||
const (
|
||||
white color = iota
|
||||
black
|
||||
grey
|
||||
)
|
||||
type object struct {
|
||||
_ *types.Scope
|
||||
_ token.Pos
|
||||
_ *types.Package
|
||||
_ string
|
||||
_ types.Type
|
||||
_ uint32
|
||||
color_ color
|
||||
_ token.Pos
|
||||
}
|
||||
type typeName struct {
|
||||
object
|
||||
}
|
||||
|
||||
// If the size of types.TypeName changes, this will fail to compile.
|
||||
const delta = int64(unsafe.Sizeof(typeName{})) - int64(unsafe.Sizeof(types.TypeName{}))
|
||||
var _ [-delta * delta]int
|
||||
|
||||
markBlack = func(obj *types.TypeName) {
|
||||
type uP = unsafe.Pointer
|
||||
var ptr *typeName
|
||||
*(*uP)(uP(&ptr)) = uP(obj)
|
||||
ptr.color_ = black
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
// 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 gcimporter
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// predecl is a cache for the predeclared types in types.Universe.
|
||||
//
|
||||
// Cache a distinct result based on the runtime value of any.
|
||||
// The pointer value of the any type varies based on GODEBUG settings.
|
||||
var predeclMu sync.Mutex
|
||||
var predecl map[types.Type][]types.Type
|
||||
|
||||
func predeclared() []types.Type {
|
||||
anyt := types.Universe.Lookup("any").Type()
|
||||
|
||||
predeclMu.Lock()
|
||||
defer predeclMu.Unlock()
|
||||
|
||||
if pre, ok := predecl[anyt]; ok {
|
||||
return pre
|
||||
}
|
||||
|
||||
if predecl == nil {
|
||||
predecl = make(map[types.Type][]types.Type)
|
||||
}
|
||||
|
||||
decls := []types.Type{ // basic types
|
||||
types.Typ[types.Bool],
|
||||
types.Typ[types.Int],
|
||||
types.Typ[types.Int8],
|
||||
types.Typ[types.Int16],
|
||||
types.Typ[types.Int32],
|
||||
types.Typ[types.Int64],
|
||||
types.Typ[types.Uint],
|
||||
types.Typ[types.Uint8],
|
||||
types.Typ[types.Uint16],
|
||||
types.Typ[types.Uint32],
|
||||
types.Typ[types.Uint64],
|
||||
types.Typ[types.Uintptr],
|
||||
types.Typ[types.Float32],
|
||||
types.Typ[types.Float64],
|
||||
types.Typ[types.Complex64],
|
||||
types.Typ[types.Complex128],
|
||||
types.Typ[types.String],
|
||||
|
||||
// basic type aliases
|
||||
types.Universe.Lookup("byte").Type(),
|
||||
types.Universe.Lookup("rune").Type(),
|
||||
|
||||
// error
|
||||
types.Universe.Lookup("error").Type(),
|
||||
|
||||
// untyped types
|
||||
types.Typ[types.UntypedBool],
|
||||
types.Typ[types.UntypedInt],
|
||||
types.Typ[types.UntypedRune],
|
||||
types.Typ[types.UntypedFloat],
|
||||
types.Typ[types.UntypedComplex],
|
||||
types.Typ[types.UntypedString],
|
||||
types.Typ[types.UntypedNil],
|
||||
|
||||
// package unsafe
|
||||
types.Typ[types.UnsafePointer],
|
||||
|
||||
// invalid type
|
||||
types.Typ[types.Invalid], // only appears in packages with errors
|
||||
|
||||
// used internally by gc; never used by this package or in .a files
|
||||
anyType{},
|
||||
|
||||
// comparable
|
||||
types.Universe.Lookup("comparable").Type(),
|
||||
|
||||
// any
|
||||
anyt,
|
||||
}
|
||||
|
||||
predecl[anyt] = decls
|
||||
return decls
|
||||
}
|
||||
|
||||
type anyType struct{}
|
||||
|
||||
func (t anyType) Underlying() types.Type { return t }
|
||||
func (t anyType) String() string { return "any" }
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// 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 gcimporter
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Copy of $GOROOT/src/cmd/internal/archive.ReadHeader.
|
||||
func readArchiveHeader(b *bufio.Reader, name string) int {
|
||||
// architecture-independent object file output
|
||||
const HeaderSize = 60
|
||||
|
||||
var buf [HeaderSize]byte
|
||||
if _, err := io.ReadFull(b, buf[:]); err != nil {
|
||||
return -1
|
||||
}
|
||||
aname := strings.Trim(string(buf[0:16]), " ")
|
||||
if !strings.HasPrefix(aname, name) {
|
||||
return -1
|
||||
}
|
||||
asize := strings.Trim(string(buf[48:58]), " ")
|
||||
i, _ := strconv.Atoi(asize)
|
||||
return i
|
||||
}
|
||||
+761
@@ -0,0 +1,761 @@
|
||||
// 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.
|
||||
|
||||
// Derived from go/internal/gcimporter/ureader.go
|
||||
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/internal/aliases"
|
||||
"golang.org/x/tools/internal/pkgbits"
|
||||
"golang.org/x/tools/internal/typesinternal"
|
||||
)
|
||||
|
||||
// A pkgReader holds the shared state for reading a unified IR package
|
||||
// description.
|
||||
type pkgReader struct {
|
||||
pkgbits.PkgDecoder
|
||||
|
||||
fake fakeFileSet
|
||||
|
||||
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.
|
||||
posBases []string // position bases (i.e., file names)
|
||||
pkgs []*types.Package
|
||||
typs []types.Type
|
||||
|
||||
// laterFns holds functions that need to be invoked at the end of
|
||||
// import reading.
|
||||
laterFns []func()
|
||||
// laterFors is used in case of 'type A B' to ensure that B is processed before A.
|
||||
laterFors map[types.Type]int
|
||||
|
||||
// ifaces holds a list of constructed Interfaces, which need to have
|
||||
// Complete called after importing is done.
|
||||
ifaces []*types.Interface
|
||||
}
|
||||
|
||||
// later adds a function to be invoked at the end of import reading.
|
||||
func (pr *pkgReader) later(fn func()) {
|
||||
pr.laterFns = append(pr.laterFns, fn)
|
||||
}
|
||||
|
||||
// See cmd/compile/internal/noder.derivedInfo.
|
||||
type derivedInfo struct {
|
||||
idx pkgbits.Index
|
||||
}
|
||||
|
||||
// See cmd/compile/internal/noder.typeInfo.
|
||||
type typeInfo struct {
|
||||
idx pkgbits.Index
|
||||
derived bool
|
||||
}
|
||||
|
||||
func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
|
||||
if !debug {
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
err = fmt.Errorf("internal error in importing %q (%v); please report an issue", path, x)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
s := string(data)
|
||||
input := pkgbits.NewPkgDecoder(path, s)
|
||||
pkg = readUnifiedPackage(fset, nil, imports, input)
|
||||
return
|
||||
}
|
||||
|
||||
// laterFor adds a function to be invoked at the end of import reading, and records the type that function is finishing.
|
||||
func (pr *pkgReader) laterFor(t types.Type, fn func()) {
|
||||
if pr.laterFors == nil {
|
||||
pr.laterFors = make(map[types.Type]int)
|
||||
}
|
||||
pr.laterFors[t] = len(pr.laterFns)
|
||||
pr.laterFns = append(pr.laterFns, fn)
|
||||
}
|
||||
|
||||
// readUnifiedPackage reads a package description from the given
|
||||
// unified IR export data decoder.
|
||||
func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[string]*types.Package, input pkgbits.PkgDecoder) *types.Package {
|
||||
pr := pkgReader{
|
||||
PkgDecoder: input,
|
||||
|
||||
fake: fakeFileSet{
|
||||
fset: fset,
|
||||
files: make(map[string]*fileInfo),
|
||||
},
|
||||
|
||||
ctxt: ctxt,
|
||||
imports: imports,
|
||||
aliases: aliases.Enabled(),
|
||||
|
||||
posBases: make([]string, input.NumElems(pkgbits.RelocPosBase)),
|
||||
pkgs: make([]*types.Package, input.NumElems(pkgbits.RelocPkg)),
|
||||
typs: make([]types.Type, input.NumElems(pkgbits.RelocType)),
|
||||
}
|
||||
defer pr.fake.setLines()
|
||||
|
||||
r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
|
||||
pkg := r.pkg()
|
||||
if r.Version().Has(pkgbits.HasInit) {
|
||||
r.Bool()
|
||||
}
|
||||
|
||||
for i, n := 0, r.Len(); i < n; i++ {
|
||||
// As if r.obj(), but avoiding the Scope.Lookup call,
|
||||
// to avoid eager loading of imports.
|
||||
r.Sync(pkgbits.SyncObject)
|
||||
if r.Version().Has(pkgbits.DerivedFuncInstance) {
|
||||
assert(!r.Bool())
|
||||
}
|
||||
r.p.objIdx(r.Reloc(pkgbits.RelocObj))
|
||||
assert(r.Len() == 0)
|
||||
}
|
||||
|
||||
r.Sync(pkgbits.SyncEOF)
|
||||
|
||||
for _, fn := range pr.laterFns {
|
||||
fn()
|
||||
}
|
||||
|
||||
for _, iface := range pr.ifaces {
|
||||
iface.Complete()
|
||||
}
|
||||
|
||||
// Imports() of pkg are all of the transitive packages that were loaded.
|
||||
var imps []*types.Package
|
||||
for _, imp := range pr.pkgs {
|
||||
if imp != nil && imp != pkg {
|
||||
imps = append(imps, imp)
|
||||
}
|
||||
}
|
||||
sort.Sort(byPath(imps))
|
||||
pkg.SetImports(imps)
|
||||
|
||||
pkg.MarkComplete()
|
||||
return pkg
|
||||
}
|
||||
|
||||
// A reader holds the state for reading a single unified IR element
|
||||
// within a package.
|
||||
type reader struct {
|
||||
pkgbits.Decoder
|
||||
|
||||
p *pkgReader
|
||||
|
||||
dict *readerDict
|
||||
}
|
||||
|
||||
// A readerDict holds the state for type parameters that parameterize
|
||||
// the current unified IR element.
|
||||
type readerDict struct {
|
||||
// bounds is a slice of typeInfos corresponding to the underlying
|
||||
// bounds of the element's type parameters.
|
||||
bounds []typeInfo
|
||||
|
||||
// tparams is a slice of the constructed TypeParams for the element.
|
||||
tparams []*types.TypeParam
|
||||
|
||||
// derived is a slice of types derived from tparams, which may be
|
||||
// instantiated while reading the current element.
|
||||
derived []derivedInfo
|
||||
derivedTypes []types.Type // lazily instantiated from derived
|
||||
}
|
||||
|
||||
func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
|
||||
return &reader{
|
||||
Decoder: pr.NewDecoder(k, idx, marker),
|
||||
p: pr,
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *pkgReader) tempReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
|
||||
return &reader{
|
||||
Decoder: pr.TempDecoder(k, idx, marker),
|
||||
p: pr,
|
||||
}
|
||||
}
|
||||
|
||||
func (pr *pkgReader) retireReader(r *reader) {
|
||||
pr.RetireDecoder(&r.Decoder)
|
||||
}
|
||||
|
||||
// @@@ Positions
|
||||
|
||||
func (r *reader) pos() token.Pos {
|
||||
r.Sync(pkgbits.SyncPos)
|
||||
if !r.Bool() {
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
// TODO(mdempsky): Delta encoding.
|
||||
posBase := r.posBase()
|
||||
line := r.Uint()
|
||||
col := r.Uint()
|
||||
return r.p.fake.pos(posBase, int(line), int(col))
|
||||
}
|
||||
|
||||
func (r *reader) posBase() string {
|
||||
return r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase))
|
||||
}
|
||||
|
||||
func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) string {
|
||||
if b := pr.posBases[idx]; b != "" {
|
||||
return b
|
||||
}
|
||||
|
||||
var filename string
|
||||
{
|
||||
r := pr.tempReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase)
|
||||
|
||||
// Within types2, position bases have a lot more details (e.g.,
|
||||
// keeping track of where //line directives appeared exactly).
|
||||
//
|
||||
// For go/types, we just track the file name.
|
||||
|
||||
filename = r.String()
|
||||
|
||||
if r.Bool() { // file base
|
||||
// Was: "b = token.NewTrimmedFileBase(filename, true)"
|
||||
} else { // line base
|
||||
pos := r.pos()
|
||||
line := r.Uint()
|
||||
col := r.Uint()
|
||||
|
||||
// Was: "b = token.NewLineBase(pos, filename, true, line, col)"
|
||||
_, _, _ = pos, line, col
|
||||
}
|
||||
pr.retireReader(r)
|
||||
}
|
||||
b := filename
|
||||
pr.posBases[idx] = b
|
||||
return b
|
||||
}
|
||||
|
||||
// @@@ Packages
|
||||
|
||||
func (r *reader) pkg() *types.Package {
|
||||
r.Sync(pkgbits.SyncPkg)
|
||||
return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg))
|
||||
}
|
||||
|
||||
func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Package {
|
||||
// TODO(mdempsky): Consider using some non-nil pointer to indicate
|
||||
// the universe scope, so we don't need to keep re-reading it.
|
||||
if pkg := pr.pkgs[idx]; pkg != nil {
|
||||
return pkg
|
||||
}
|
||||
|
||||
pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg()
|
||||
pr.pkgs[idx] = pkg
|
||||
return pkg
|
||||
}
|
||||
|
||||
func (r *reader) doPkg() *types.Package {
|
||||
path := r.String()
|
||||
switch path {
|
||||
// cmd/compile emits path="main" for main packages because
|
||||
// that's the linker symbol prefix it used; but we need
|
||||
// the package's path as it would be reported by go list,
|
||||
// hence "main" below.
|
||||
// See test at go/packages.TestMainPackagePathInModeTypes.
|
||||
case "", "main":
|
||||
path = r.p.PkgPath()
|
||||
case "builtin":
|
||||
return nil // universe
|
||||
case "unsafe":
|
||||
return types.Unsafe
|
||||
}
|
||||
|
||||
if pkg := r.p.imports[path]; pkg != nil {
|
||||
return pkg
|
||||
}
|
||||
|
||||
name := r.String()
|
||||
|
||||
pkg := types.NewPackage(path, name)
|
||||
r.p.imports[path] = pkg
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
// @@@ Types
|
||||
|
||||
func (r *reader) typ() types.Type {
|
||||
return r.p.typIdx(r.typInfo(), r.dict)
|
||||
}
|
||||
|
||||
func (r *reader) typInfo() typeInfo {
|
||||
r.Sync(pkgbits.SyncType)
|
||||
if r.Bool() {
|
||||
return typeInfo{idx: pkgbits.Index(r.Len()), derived: true}
|
||||
}
|
||||
return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false}
|
||||
}
|
||||
|
||||
func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) types.Type {
|
||||
idx := info.idx
|
||||
var where *types.Type
|
||||
if info.derived {
|
||||
where = &dict.derivedTypes[idx]
|
||||
idx = dict.derived[idx].idx
|
||||
} else {
|
||||
where = &pr.typs[idx]
|
||||
}
|
||||
|
||||
if typ := *where; typ != nil {
|
||||
return typ
|
||||
}
|
||||
|
||||
var typ types.Type
|
||||
{
|
||||
r := pr.tempReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx)
|
||||
r.dict = dict
|
||||
|
||||
typ = r.doTyp()
|
||||
assert(typ != nil)
|
||||
pr.retireReader(r)
|
||||
}
|
||||
// See comment in pkgReader.typIdx explaining how this happens.
|
||||
if prev := *where; prev != nil {
|
||||
return prev
|
||||
}
|
||||
|
||||
*where = typ
|
||||
return typ
|
||||
}
|
||||
|
||||
func (r *reader) doTyp() (res types.Type) {
|
||||
switch tag := pkgbits.CodeType(r.Code(pkgbits.SyncType)); tag {
|
||||
default:
|
||||
errorf("unhandled type tag: %v", tag)
|
||||
panic("unreachable")
|
||||
|
||||
case pkgbits.TypeBasic:
|
||||
return types.Typ[r.Len()]
|
||||
|
||||
case pkgbits.TypeNamed:
|
||||
obj, targs := r.obj()
|
||||
name := obj.(*types.TypeName)
|
||||
if len(targs) != 0 {
|
||||
t, _ := types.Instantiate(r.p.ctxt, name.Type(), targs, false)
|
||||
return t
|
||||
}
|
||||
return name.Type()
|
||||
|
||||
case pkgbits.TypeTypeParam:
|
||||
return r.dict.tparams[r.Len()]
|
||||
|
||||
case pkgbits.TypeArray:
|
||||
len := int64(r.Uint64())
|
||||
return types.NewArray(r.typ(), len)
|
||||
case pkgbits.TypeChan:
|
||||
dir := types.ChanDir(r.Len())
|
||||
return types.NewChan(dir, r.typ())
|
||||
case pkgbits.TypeMap:
|
||||
return types.NewMap(r.typ(), r.typ())
|
||||
case pkgbits.TypePointer:
|
||||
return types.NewPointer(r.typ())
|
||||
case pkgbits.TypeSignature:
|
||||
return r.signature(nil, nil, nil)
|
||||
case pkgbits.TypeSlice:
|
||||
return types.NewSlice(r.typ())
|
||||
case pkgbits.TypeStruct:
|
||||
return r.structType()
|
||||
case pkgbits.TypeInterface:
|
||||
return r.interfaceType()
|
||||
case pkgbits.TypeUnion:
|
||||
return r.unionType()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reader) structType() *types.Struct {
|
||||
fields := make([]*types.Var, r.Len())
|
||||
var tags []string
|
||||
for i := range fields {
|
||||
pos := r.pos()
|
||||
pkg, name := r.selector()
|
||||
ftyp := r.typ()
|
||||
tag := r.String()
|
||||
embedded := r.Bool()
|
||||
|
||||
fields[i] = types.NewField(pos, pkg, name, ftyp, embedded)
|
||||
if tag != "" {
|
||||
for len(tags) < i {
|
||||
tags = append(tags, "")
|
||||
}
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
}
|
||||
return types.NewStruct(fields, tags)
|
||||
}
|
||||
|
||||
func (r *reader) unionType() *types.Union {
|
||||
terms := make([]*types.Term, r.Len())
|
||||
for i := range terms {
|
||||
terms[i] = types.NewTerm(r.Bool(), r.typ())
|
||||
}
|
||||
return types.NewUnion(terms)
|
||||
}
|
||||
|
||||
func (r *reader) interfaceType() *types.Interface {
|
||||
methods := make([]*types.Func, r.Len())
|
||||
embeddeds := make([]types.Type, r.Len())
|
||||
implicit := len(methods) == 0 && len(embeddeds) == 1 && r.Bool()
|
||||
|
||||
for i := range methods {
|
||||
pos := r.pos()
|
||||
pkg, name := r.selector()
|
||||
mtyp := r.signature(nil, nil, nil)
|
||||
methods[i] = types.NewFunc(pos, pkg, name, mtyp)
|
||||
}
|
||||
|
||||
for i := range embeddeds {
|
||||
embeddeds[i] = r.typ()
|
||||
}
|
||||
|
||||
iface := types.NewInterfaceType(methods, embeddeds)
|
||||
if implicit {
|
||||
iface.MarkImplicit()
|
||||
}
|
||||
|
||||
// We need to call iface.Complete(), but if there are any embedded
|
||||
// defined types, then we may not have set their underlying
|
||||
// interface type yet. So we need to defer calling Complete until
|
||||
// after we've called SetUnderlying everywhere.
|
||||
//
|
||||
// TODO(mdempsky): After CL 424876 lands, it should be safe to call
|
||||
// iface.Complete() immediately.
|
||||
r.p.ifaces = append(r.p.ifaces, iface)
|
||||
|
||||
return iface
|
||||
}
|
||||
|
||||
func (r *reader) signature(recv *types.Var, rtparams, tparams []*types.TypeParam) *types.Signature {
|
||||
r.Sync(pkgbits.SyncSignature)
|
||||
|
||||
params := r.params()
|
||||
results := r.params()
|
||||
variadic := r.Bool()
|
||||
|
||||
return types.NewSignatureType(recv, rtparams, tparams, params, results, variadic)
|
||||
}
|
||||
|
||||
func (r *reader) params() *types.Tuple {
|
||||
r.Sync(pkgbits.SyncParams)
|
||||
|
||||
params := make([]*types.Var, r.Len())
|
||||
for i := range params {
|
||||
params[i] = r.param()
|
||||
}
|
||||
|
||||
return types.NewTuple(params...)
|
||||
}
|
||||
|
||||
func (r *reader) param() *types.Var {
|
||||
r.Sync(pkgbits.SyncParam)
|
||||
|
||||
pos := r.pos()
|
||||
pkg, name := r.localIdent()
|
||||
typ := r.typ()
|
||||
|
||||
return types.NewParam(pos, pkg, name, typ)
|
||||
}
|
||||
|
||||
// @@@ Objects
|
||||
|
||||
func (r *reader) obj() (types.Object, []types.Type) {
|
||||
r.Sync(pkgbits.SyncObject)
|
||||
|
||||
if r.Version().Has(pkgbits.DerivedFuncInstance) {
|
||||
assert(!r.Bool())
|
||||
}
|
||||
|
||||
pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj))
|
||||
obj := pkgScope(pkg).Lookup(name)
|
||||
|
||||
targs := make([]types.Type, r.Len())
|
||||
for i := range targs {
|
||||
targs[i] = r.typ()
|
||||
}
|
||||
|
||||
return obj, targs
|
||||
}
|
||||
|
||||
func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
|
||||
|
||||
var objPkg *types.Package
|
||||
var objName string
|
||||
var tag pkgbits.CodeObj
|
||||
{
|
||||
rname := pr.tempReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
|
||||
|
||||
objPkg, objName = rname.qualifiedIdent()
|
||||
assert(objName != "")
|
||||
|
||||
tag = pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj))
|
||||
pr.retireReader(rname)
|
||||
}
|
||||
|
||||
if tag == pkgbits.ObjStub {
|
||||
assert(objPkg == nil || objPkg == types.Unsafe)
|
||||
return objPkg, objName
|
||||
}
|
||||
|
||||
// Ignore local types promoted to global scope (#55110).
|
||||
if _, suffix := splitVargenSuffix(objName); suffix != "" {
|
||||
return objPkg, objName
|
||||
}
|
||||
|
||||
if objPkg.Scope().Lookup(objName) == nil {
|
||||
dict := pr.objDictIdx(idx)
|
||||
|
||||
r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1)
|
||||
r.dict = dict
|
||||
|
||||
declare := func(obj types.Object) {
|
||||
objPkg.Scope().Insert(obj)
|
||||
}
|
||||
|
||||
switch tag {
|
||||
default:
|
||||
panic("weird")
|
||||
|
||||
case pkgbits.ObjAlias:
|
||||
pos := r.pos()
|
||||
var tparams []*types.TypeParam
|
||||
if r.Version().Has(pkgbits.AliasTypeParamNames) {
|
||||
tparams = r.typeParamNames()
|
||||
}
|
||||
typ := r.typ()
|
||||
declare(aliases.NewAlias(r.p.aliases, pos, objPkg, objName, typ, tparams))
|
||||
|
||||
case pkgbits.ObjConst:
|
||||
pos := r.pos()
|
||||
typ := r.typ()
|
||||
val := r.Value()
|
||||
declare(types.NewConst(pos, objPkg, objName, typ, val))
|
||||
|
||||
case pkgbits.ObjFunc:
|
||||
pos := r.pos()
|
||||
tparams := r.typeParamNames()
|
||||
sig := r.signature(nil, nil, tparams)
|
||||
declare(types.NewFunc(pos, objPkg, objName, sig))
|
||||
|
||||
case pkgbits.ObjType:
|
||||
pos := r.pos()
|
||||
|
||||
obj := types.NewTypeName(pos, objPkg, objName, nil)
|
||||
named := types.NewNamed(obj, nil, nil)
|
||||
declare(obj)
|
||||
|
||||
named.SetTypeParams(r.typeParamNames())
|
||||
|
||||
setUnderlying := func(underlying types.Type) {
|
||||
// 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 := types.Unalias(underlying).(*types.Interface); ok && iface.NumExplicitMethods() != 0 {
|
||||
methods := make([]*types.Func, iface.NumExplicitMethods())
|
||||
for i := range methods {
|
||||
fn := iface.ExplicitMethod(i)
|
||||
sig := fn.Type().(*types.Signature)
|
||||
|
||||
recv := types.NewVar(fn.Pos(), fn.Pkg(), "", named)
|
||||
typesinternal.SetVarKind(recv, typesinternal.RecvVar)
|
||||
methods[i] = types.NewFunc(fn.Pos(), fn.Pkg(), fn.Name(), types.NewSignatureType(recv, nil, nil, sig.Params(), sig.Results(), sig.Variadic()))
|
||||
}
|
||||
|
||||
embeds := make([]types.Type, iface.NumEmbeddeds())
|
||||
for i := range embeds {
|
||||
embeds[i] = iface.EmbeddedType(i)
|
||||
}
|
||||
|
||||
newIface := types.NewInterfaceType(methods, embeds)
|
||||
r.p.ifaces = append(r.p.ifaces, newIface)
|
||||
underlying = newIface
|
||||
}
|
||||
|
||||
named.SetUnderlying(underlying)
|
||||
}
|
||||
|
||||
// Since go.dev/cl/455279, we can assume rhs.Underlying() will
|
||||
// always be non-nil. However, to temporarily support users of
|
||||
// older snapshot releases, we continue to fallback to the old
|
||||
// behavior for now.
|
||||
//
|
||||
// TODO(mdempsky): Remove fallback code and simplify after
|
||||
// allowing time for snapshot users to upgrade.
|
||||
rhs := r.typ()
|
||||
if underlying := rhs.Underlying(); underlying != nil {
|
||||
setUnderlying(underlying)
|
||||
} else {
|
||||
pk := r.p
|
||||
pk.laterFor(named, func() {
|
||||
// First be sure that the rhs is initialized, if it needs to be initialized.
|
||||
delete(pk.laterFors, named) // prevent cycles
|
||||
if i, ok := pk.laterFors[rhs]; ok {
|
||||
f := pk.laterFns[i]
|
||||
pk.laterFns[i] = func() {} // function is running now, so replace it with a no-op
|
||||
f() // initialize RHS
|
||||
}
|
||||
setUnderlying(rhs.Underlying())
|
||||
})
|
||||
}
|
||||
|
||||
for i, n := 0, r.Len(); i < n; i++ {
|
||||
named.AddMethod(r.method())
|
||||
}
|
||||
|
||||
case pkgbits.ObjVar:
|
||||
pos := r.pos()
|
||||
typ := r.typ()
|
||||
v := types.NewVar(pos, objPkg, objName, typ)
|
||||
typesinternal.SetVarKind(v, typesinternal.PackageVar)
|
||||
declare(v)
|
||||
}
|
||||
}
|
||||
|
||||
return objPkg, objName
|
||||
}
|
||||
|
||||
func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict {
|
||||
|
||||
var dict readerDict
|
||||
|
||||
{
|
||||
r := pr.tempReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
|
||||
if implicits := r.Len(); implicits != 0 {
|
||||
errorf("unexpected object with %v implicit type parameter(s)", implicits)
|
||||
}
|
||||
|
||||
dict.bounds = make([]typeInfo, r.Len())
|
||||
for i := range dict.bounds {
|
||||
dict.bounds[i] = r.typInfo()
|
||||
}
|
||||
|
||||
dict.derived = make([]derivedInfo, r.Len())
|
||||
dict.derivedTypes = make([]types.Type, len(dict.derived))
|
||||
for i := range dict.derived {
|
||||
dict.derived[i] = derivedInfo{idx: r.Reloc(pkgbits.RelocType)}
|
||||
if r.Version().Has(pkgbits.DerivedInfoNeeded) {
|
||||
assert(!r.Bool())
|
||||
}
|
||||
}
|
||||
|
||||
pr.retireReader(r)
|
||||
}
|
||||
// function references follow, but reader doesn't need those
|
||||
|
||||
return &dict
|
||||
}
|
||||
|
||||
func (r *reader) typeParamNames() []*types.TypeParam {
|
||||
r.Sync(pkgbits.SyncTypeParamNames)
|
||||
|
||||
// Note: This code assumes it only processes objects without
|
||||
// implement type parameters. This is currently fine, because
|
||||
// reader is only used to read in exported declarations, which are
|
||||
// always package scoped.
|
||||
|
||||
if len(r.dict.bounds) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Careful: Type parameter lists may have cycles. To allow for this,
|
||||
// we construct the type parameter list in two passes: first we
|
||||
// create all the TypeNames and TypeParams, then we construct and
|
||||
// set the bound type.
|
||||
|
||||
r.dict.tparams = make([]*types.TypeParam, len(r.dict.bounds))
|
||||
for i := range r.dict.bounds {
|
||||
pos := r.pos()
|
||||
pkg, name := r.localIdent()
|
||||
|
||||
tname := types.NewTypeName(pos, pkg, name, nil)
|
||||
r.dict.tparams[i] = types.NewTypeParam(tname, nil)
|
||||
}
|
||||
|
||||
typs := make([]types.Type, len(r.dict.bounds))
|
||||
for i, bound := range r.dict.bounds {
|
||||
typs[i] = r.p.typIdx(bound, r.dict)
|
||||
}
|
||||
|
||||
// TODO(mdempsky): This is subtle, elaborate further.
|
||||
//
|
||||
// We have to save tparams outside of the closure, because
|
||||
// typeParamNames() can be called multiple times with the same
|
||||
// dictionary instance.
|
||||
//
|
||||
// Also, this needs to happen later to make sure SetUnderlying has
|
||||
// been called.
|
||||
//
|
||||
// TODO(mdempsky): Is it safe to have a single "later" slice or do
|
||||
// we need to have multiple passes? See comments on CL 386002 and
|
||||
// go.dev/issue/52104.
|
||||
tparams := r.dict.tparams
|
||||
r.p.later(func() {
|
||||
for i, typ := range typs {
|
||||
tparams[i].SetConstraint(typ)
|
||||
}
|
||||
})
|
||||
|
||||
return r.dict.tparams
|
||||
}
|
||||
|
||||
func (r *reader) method() *types.Func {
|
||||
r.Sync(pkgbits.SyncMethod)
|
||||
pos := r.pos()
|
||||
pkg, name := r.selector()
|
||||
|
||||
rparams := r.typeParamNames()
|
||||
sig := r.signature(r.param(), rparams, nil)
|
||||
|
||||
_ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
|
||||
return types.NewFunc(pos, pkg, name, sig)
|
||||
}
|
||||
|
||||
func (r *reader) qualifiedIdent() (*types.Package, string) { return r.ident(pkgbits.SyncSym) }
|
||||
func (r *reader) localIdent() (*types.Package, string) { return r.ident(pkgbits.SyncLocalIdent) }
|
||||
func (r *reader) selector() (*types.Package, string) { return r.ident(pkgbits.SyncSelector) }
|
||||
|
||||
func (r *reader) ident(marker pkgbits.SyncMarker) (*types.Package, string) {
|
||||
r.Sync(marker)
|
||||
return r.pkg(), r.String()
|
||||
}
|
||||
|
||||
// pkgScope returns pkg.Scope().
|
||||
// If pkg is nil, it returns types.Universe instead.
|
||||
//
|
||||
// TODO(mdempsky): Remove after x/tools can depend on Go 1.19.
|
||||
func pkgScope(pkg *types.Package) *types.Scope {
|
||||
if pkg != nil {
|
||||
return pkg.Scope()
|
||||
}
|
||||
return types.Universe
|
||||
}
|
||||
|
||||
// See cmd/compile/internal/types.SplitVargenSuffix.
|
||||
func splitVargenSuffix(name string) (base, suffix string) {
|
||||
i := len(name)
|
||||
for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' {
|
||||
i--
|
||||
}
|
||||
const dot = "·"
|
||||
if i >= len(dot) && name[i-len(dot):i] == dot {
|
||||
i -= len(dot)
|
||||
return name[:i], name[i:]
|
||||
}
|
||||
return name, ""
|
||||
}
|
||||
+567
@@ -0,0 +1,567 @@
|
||||
// Copyright 2020 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 gocommand is a helper for calling the go command.
|
||||
package gocommand
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/tools/internal/event"
|
||||
"golang.org/x/tools/internal/event/keys"
|
||||
"golang.org/x/tools/internal/event/label"
|
||||
)
|
||||
|
||||
// A Runner will run go command invocations and serialize
|
||||
// them if it sees a concurrency error.
|
||||
type Runner struct {
|
||||
// once guards the runner initialization.
|
||||
once sync.Once
|
||||
|
||||
// inFlight tracks available workers.
|
||||
inFlight chan struct{}
|
||||
|
||||
// serialized guards the ability to run a go command serially,
|
||||
// to avoid deadlocks when claiming workers.
|
||||
serialized chan struct{}
|
||||
}
|
||||
|
||||
const maxInFlight = 10
|
||||
|
||||
func (runner *Runner) initialize() {
|
||||
runner.once.Do(func() {
|
||||
runner.inFlight = make(chan struct{}, maxInFlight)
|
||||
runner.serialized = make(chan struct{}, 1)
|
||||
})
|
||||
}
|
||||
|
||||
// 1.13: go: updates to go.mod needed, but contents have changed
|
||||
// 1.14: go: updating go.mod: existing contents have changed since last read
|
||||
var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`)
|
||||
|
||||
// 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), directory.Of(inv.WorkingDir)}
|
||||
}
|
||||
|
||||
// Run is a convenience wrapper around RunRaw.
|
||||
// It returns only stdout and a "friendly" error.
|
||||
func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) {
|
||||
ctx, done := event.Start(ctx, "gocommand.Runner.Run", invLabels(inv)...)
|
||||
defer done()
|
||||
|
||||
stdout, _, friendly, _ := runner.RunRaw(ctx, inv)
|
||||
return stdout, friendly
|
||||
}
|
||||
|
||||
// RunPiped runs the invocation serially, always waiting for any concurrent
|
||||
// invocations to complete first.
|
||||
func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error {
|
||||
ctx, done := event.Start(ctx, "gocommand.Runner.RunPiped", invLabels(inv)...)
|
||||
defer done()
|
||||
|
||||
_, err := runner.runPiped(ctx, inv, stdout, stderr)
|
||||
return err
|
||||
}
|
||||
|
||||
// RunRaw runs the invocation, serializing requests only if they fight over
|
||||
// go.mod changes.
|
||||
// Postcondition: both error results have same nilness.
|
||||
func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
|
||||
ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...)
|
||||
defer done()
|
||||
// Make sure the runner is always initialized.
|
||||
runner.initialize()
|
||||
|
||||
// First, try to run the go command concurrently.
|
||||
stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv)
|
||||
|
||||
// If we encounter a load concurrency error, we need to retry serially.
|
||||
if friendlyErr != nil && modConcurrencyError.MatchString(friendlyErr.Error()) {
|
||||
event.Error(ctx, "Load concurrency error, will retry serially", err)
|
||||
|
||||
// Run serially by calling runPiped.
|
||||
stdout.Reset()
|
||||
stderr.Reset()
|
||||
friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr)
|
||||
}
|
||||
|
||||
return stdout, stderr, friendlyErr, err
|
||||
}
|
||||
|
||||
// Postcondition: both error results have same nilness.
|
||||
func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
|
||||
// Wait for 1 worker to become available.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, nil, ctx.Err(), ctx.Err()
|
||||
case runner.inFlight <- struct{}{}:
|
||||
defer func() { <-runner.inFlight }()
|
||||
}
|
||||
|
||||
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
|
||||
friendlyErr, err := inv.runWithFriendlyError(ctx, stdout, stderr)
|
||||
return stdout, stderr, friendlyErr, err
|
||||
}
|
||||
|
||||
// Postcondition: both error results have same nilness.
|
||||
func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) {
|
||||
// Make sure the runner is always initialized.
|
||||
runner.initialize()
|
||||
|
||||
// Acquire the serialization lock. This avoids deadlocks between two
|
||||
// runPiped commands.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err(), ctx.Err()
|
||||
case runner.serialized <- struct{}{}:
|
||||
defer func() { <-runner.serialized }()
|
||||
}
|
||||
|
||||
// Wait for all in-progress go commands to return before proceeding,
|
||||
// to avoid load concurrency errors.
|
||||
for range maxInFlight {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err(), ctx.Err()
|
||||
case runner.inFlight <- struct{}{}:
|
||||
// Make sure we always "return" any workers we took.
|
||||
defer func() { <-runner.inFlight }()
|
||||
}
|
||||
}
|
||||
|
||||
return inv.runWithFriendlyError(ctx, stdout, stderr)
|
||||
}
|
||||
|
||||
// An Invocation represents a call to the go command.
|
||||
type Invocation struct {
|
||||
Verb string
|
||||
Args []string
|
||||
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
|
||||
|
||||
// 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
|
||||
// in Env, not starting with os.Environ.
|
||||
CleanEnv bool
|
||||
Env []string
|
||||
WorkingDir string
|
||||
Logf func(format string, args ...any)
|
||||
}
|
||||
|
||||
// Postcondition: both error results have same nilness.
|
||||
func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) {
|
||||
rawError = i.run(ctx, stdout, stderr)
|
||||
if rawError != nil {
|
||||
friendlyError = rawError
|
||||
// Check for 'go' executable not being found.
|
||||
if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
|
||||
friendlyError = fmt.Errorf("go command required, not found: %v", ee)
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
friendlyError = ctx.Err()
|
||||
}
|
||||
friendlyError = fmt.Errorf("err: %v: stderr: %s", friendlyError, stderr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// logf logs if i.Logf is non-nil.
|
||||
func (i *Invocation) logf(format string, args ...any) {
|
||||
if i.Logf != nil {
|
||||
i.Logf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
|
||||
goArgs := []string{i.Verb}
|
||||
|
||||
appendModFile := func() {
|
||||
if i.ModFile != "" {
|
||||
goArgs = append(goArgs, "-modfile="+i.ModFile)
|
||||
}
|
||||
}
|
||||
appendModFlag := func() {
|
||||
if i.ModFlag != "" {
|
||||
goArgs = append(goArgs, "-mod="+i.ModFlag)
|
||||
}
|
||||
}
|
||||
appendOverlayFlag := func() {
|
||||
if i.Overlay != "" {
|
||||
goArgs = append(goArgs, "-overlay="+i.Overlay)
|
||||
}
|
||||
}
|
||||
|
||||
switch i.Verb {
|
||||
case "env", "version":
|
||||
goArgs = append(goArgs, i.Args...)
|
||||
case "mod":
|
||||
// mod needs the sub-verb before flags.
|
||||
goArgs = append(goArgs, i.Args[0])
|
||||
appendModFile()
|
||||
goArgs = append(goArgs, i.Args[1:]...)
|
||||
case "get":
|
||||
goArgs = append(goArgs, i.BuildFlags...)
|
||||
appendModFile()
|
||||
goArgs = append(goArgs, i.Args...)
|
||||
|
||||
default: // notably list and build.
|
||||
goArgs = append(goArgs, i.BuildFlags...)
|
||||
appendModFile()
|
||||
appendModFlag()
|
||||
appendOverlayFlag()
|
||||
goArgs = append(goArgs, i.Args...)
|
||||
}
|
||||
cmd := exec.Command("go", goArgs...)
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
|
||||
// https://go.dev/issue/59541: don't wait forever copying stderr
|
||||
// after the command has exited.
|
||||
// After CL 484741 we copy stdout manually, so we we'll stop reading that as
|
||||
// soon as ctx is done. However, we also don't want to wait around forever
|
||||
// for stderr. Give a much-longer-than-reasonable delay and then assume that
|
||||
// something has wedged in the kernel or runtime.
|
||||
cmd.WaitDelay = 30 * time.Second
|
||||
|
||||
// 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 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()
|
||||
}
|
||||
cmd.Env = append(cmd.Env, i.Env...)
|
||||
if i.WorkingDir != "" {
|
||||
cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir)
|
||||
cmd.Dir = i.WorkingDir
|
||||
}
|
||||
|
||||
debugStr := cmdDebugStr(cmd)
|
||||
i.logf("starting %v", debugStr)
|
||||
start := time.Now()
|
||||
defer func() {
|
||||
i.logf("%s for %v", time.Since(start), debugStr)
|
||||
}()
|
||||
|
||||
return runCmdContext(ctx, cmd)
|
||||
}
|
||||
|
||||
// DebugHangingGoCommands may be set by tests to enable additional
|
||||
// instrumentation (including panics) for debugging hanging Go commands.
|
||||
//
|
||||
// See golang/go#54461 for details.
|
||||
var DebugHangingGoCommands = false
|
||||
|
||||
// runCmdContext is like exec.CommandContext except it sends os.Interrupt
|
||||
// before os.Kill.
|
||||
func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
|
||||
// If cmd.Stdout is not an *os.File, the exec package will create a pipe and
|
||||
// copy it to the Writer in a goroutine until the process has finished and
|
||||
// either the pipe reaches EOF or command's WaitDelay expires.
|
||||
//
|
||||
// However, the output from 'go list' can be quite large, and we don't want to
|
||||
// keep reading (and allocating buffers) if we've already decided we don't
|
||||
// care about the output. We don't want to wait for the process to finish, and
|
||||
// we don't wait to wait for the WaitDelay to expire either.
|
||||
//
|
||||
// Instead, if cmd.Stdout requires a copying goroutine we explicitly replace
|
||||
// it with a pipe (which is an *os.File), which we can close in order to stop
|
||||
// copying output as soon as we realize we don't care about it.
|
||||
var stdoutW *os.File
|
||||
if cmd.Stdout != nil {
|
||||
if _, ok := cmd.Stdout.(*os.File); !ok {
|
||||
var stdoutR *os.File
|
||||
stdoutR, stdoutW, err = os.Pipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prevStdout := cmd.Stdout
|
||||
cmd.Stdout = stdoutW
|
||||
|
||||
stdoutErr := make(chan error, 1)
|
||||
go func() {
|
||||
_, err := io.Copy(prevStdout, stdoutR)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("copying stdout: %w", err)
|
||||
}
|
||||
stdoutErr <- err
|
||||
}()
|
||||
defer func() {
|
||||
// We started a goroutine to copy a stdout pipe.
|
||||
// Wait for it to finish, or terminate it if need be.
|
||||
var err2 error
|
||||
select {
|
||||
case err2 = <-stdoutErr:
|
||||
stdoutR.Close()
|
||||
case <-ctx.Done():
|
||||
stdoutR.Close()
|
||||
// Per https://pkg.go.dev/os#File.Close, the call to stdoutR.Close
|
||||
// should cause the Read call in io.Copy to unblock and return
|
||||
// immediately, but we still need to receive from stdoutErr to confirm
|
||||
// that it has happened.
|
||||
<-stdoutErr
|
||||
err2 = ctx.Err()
|
||||
}
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
}()
|
||||
|
||||
// Per https://pkg.go.dev/os/exec#Cmd, “If Stdout and Stderr are the
|
||||
// same writer, and have a type that can be compared with ==, at most
|
||||
// one goroutine at a time will call Write.”
|
||||
//
|
||||
// Since we're starting a goroutine that writes to cmd.Stdout, we must
|
||||
// also update cmd.Stderr so that it still holds.
|
||||
func() {
|
||||
defer func() { recover() }()
|
||||
if cmd.Stderr == prevStdout {
|
||||
cmd.Stderr = cmd.Stdout
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
err = cmd.Start()
|
||||
if stdoutW != nil {
|
||||
// The child process has inherited the pipe file,
|
||||
// so close the copy held in this process.
|
||||
stdoutW.Close()
|
||||
stdoutW = nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resChan := make(chan error, 1)
|
||||
go func() {
|
||||
resChan <- cmd.Wait()
|
||||
}()
|
||||
|
||||
// If we're interested in debugging hanging Go commands, stop waiting after a
|
||||
// minute and panic with interesting information.
|
||||
debug := DebugHangingGoCommands
|
||||
if debug {
|
||||
timer := time.NewTimer(1 * time.Minute)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case err := <-resChan:
|
||||
return err
|
||||
case <-timer.C:
|
||||
// HandleHangingGoCommand terminates this process.
|
||||
// Pass off resChan in case we can collect the command error.
|
||||
handleHangingGoCommand(startTime, cmd, resChan)
|
||||
case <-ctx.Done():
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case err := <-resChan:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
|
||||
// Cancelled. Interrupt and see if it ends voluntarily.
|
||||
if err := cmd.Process.Signal(os.Interrupt); err == nil {
|
||||
// (We used to wait only 1s but this proved
|
||||
// fragile on loaded builder machines.)
|
||||
timer := time.NewTimer(5 * time.Second)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case err := <-resChan:
|
||||
return err
|
||||
case <-timer.C:
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't shut down in response to interrupt. Kill it hard.
|
||||
if err := cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) && debug {
|
||||
log.Printf("error killing the Go command: %v", err)
|
||||
}
|
||||
|
||||
return <-resChan
|
||||
}
|
||||
|
||||
// handleHangingGoCommand outputs debugging information to help diagnose the
|
||||
// cause of a hanging Go command, and then exits with log.Fatalf.
|
||||
func handleHangingGoCommand(start time.Time, cmd *exec.Cmd, resChan chan error) {
|
||||
switch runtime.GOOS {
|
||||
case "linux", "darwin", "freebsd", "netbsd", "openbsd":
|
||||
fmt.Fprintln(os.Stderr, `DETECTED A HANGING GO COMMAND
|
||||
|
||||
The gopls test runner has detected a hanging go command. In order to debug
|
||||
this, the output of ps and lsof/fstat is printed below.
|
||||
|
||||
See golang/go#54461 for more details.`)
|
||||
|
||||
fmt.Fprintln(os.Stderr, "\nps axo ppid,pid,command:")
|
||||
fmt.Fprintln(os.Stderr, "-------------------------")
|
||||
psCmd := exec.Command("ps", "axo", "ppid,pid,command")
|
||||
psCmd.Stdout = os.Stderr
|
||||
psCmd.Stderr = os.Stderr
|
||||
if err := psCmd.Run(); err != nil {
|
||||
log.Printf("Handling hanging Go command: running ps: %v", err)
|
||||
}
|
||||
|
||||
listFiles := "lsof"
|
||||
if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" {
|
||||
listFiles = "fstat"
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "\n"+listFiles+":")
|
||||
fmt.Fprintln(os.Stderr, "-----")
|
||||
listFilesCmd := exec.Command(listFiles)
|
||||
listFilesCmd.Stdout = os.Stderr
|
||||
listFilesCmd.Stderr = os.Stderr
|
||||
if err := listFilesCmd.Run(); err != nil {
|
||||
log.Printf("Handling hanging Go command: running %s: %v", listFiles, err)
|
||||
}
|
||||
// Try to extract information about the slow go process by issuing a SIGQUIT.
|
||||
if err := cmd.Process.Signal(sigStuckProcess); err == nil {
|
||||
select {
|
||||
case err := <-resChan:
|
||||
stderr := "not a bytes.Buffer"
|
||||
if buf, _ := cmd.Stderr.(*bytes.Buffer); buf != nil {
|
||||
stderr = buf.String()
|
||||
}
|
||||
log.Printf("Quit hanging go command:\n\terr:%v\n\tstderr:\n%v\n\n", err, stderr)
|
||||
case <-time.After(5 * time.Second):
|
||||
}
|
||||
} else {
|
||||
log.Printf("Sending signal %d to hanging go command: %v", sigStuckProcess, err)
|
||||
}
|
||||
}
|
||||
log.Fatalf("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 {
|
||||
env := make(map[string]string)
|
||||
for _, kv := range cmd.Env {
|
||||
split := strings.SplitN(kv, "=", 2)
|
||||
if len(split) == 2 {
|
||||
k, v := split[0], split[1]
|
||||
env[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
var args []string
|
||||
for _, arg := range cmd.Args {
|
||||
quoted := strconv.Quote(arg)
|
||||
if quoted[1:len(quoted)-1] != arg || strings.Contains(arg, " ") {
|
||||
args = append(args, quoted)
|
||||
} else {
|
||||
args = append(args, arg)
|
||||
}
|
||||
}
|
||||
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", 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
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// Copyright 2025 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 !unix
|
||||
|
||||
package gocommand
|
||||
|
||||
import "os"
|
||||
|
||||
// sigStuckProcess is the signal to send to kill a hanging subprocess.
|
||||
// On Unix we send SIGQUIT, but on non-Unix we only have os.Kill.
|
||||
var sigStuckProcess = os.Kill
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// Copyright 2025 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 unix
|
||||
|
||||
package gocommand
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Sigstuckprocess is the signal to send to kill a hanging subprocess.
|
||||
// Send SIGQUIT to get a stack trace.
|
||||
var sigStuckProcess = syscall.SIGQUIT
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
// Copyright 2020 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 gocommand
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// ModuleJSON holds information about a module.
|
||||
type ModuleJSON struct {
|
||||
Path string // module path
|
||||
Version string // module version
|
||||
Versions []string // available module versions (with -versions)
|
||||
Replace *ModuleJSON // replaced by this module
|
||||
Time *time.Time // time version was created
|
||||
Update *ModuleJSON // available update, if any (with -u)
|
||||
Main bool // is this the main module?
|
||||
Indirect bool // is this module only an indirect dependency of main module?
|
||||
Dir string // directory holding files for this module, if any
|
||||
GoMod string // path to go.mod file used when loading this module, if any
|
||||
GoVersion string // go version used in module
|
||||
}
|
||||
|
||||
var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
|
||||
|
||||
// VendorEnabled reports whether 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 VendorEnabled(ctx context.Context, inv Invocation, r *Runner) (bool, *ModuleJSON, error) {
|
||||
mainMod, go114, err := getMainModuleAnd114(ctx, inv, r)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
// We check the GOFLAGS to see if there is anything overridden or not.
|
||||
inv.Verb = "env"
|
||||
inv.Args = []string{"GOFLAGS"}
|
||||
stdout, err := r.Run(ctx, inv)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
goflags := string(bytes.TrimSpace(stdout.Bytes()))
|
||||
matches := modFlagRegexp.FindStringSubmatch(goflags)
|
||||
var modFlag string
|
||||
if len(matches) != 0 {
|
||||
modFlag = matches[1]
|
||||
}
|
||||
// Don't override an explicit '-mod=' argument.
|
||||
if modFlag == "vendor" {
|
||||
return true, mainMod, nil
|
||||
} else if modFlag != "" {
|
||||
return false, nil, nil
|
||||
}
|
||||
if mainMod == nil || !go114 {
|
||||
return false, nil, nil
|
||||
}
|
||||
// Check 1.14's automatic vendor mode.
|
||||
if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() {
|
||||
if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 {
|
||||
// The Go version is at least 1.14, and a vendor directory exists.
|
||||
// Set -mod=vendor by default.
|
||||
return true, mainMod, nil
|
||||
}
|
||||
}
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
// getMainModuleAnd114 gets one of the main modules' information and whether the
|
||||
// go command in use is 1.14+. This is the information needed to figure out
|
||||
// if vendoring should be enabled.
|
||||
func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) {
|
||||
const format = `{{.Path}}
|
||||
{{.Dir}}
|
||||
{{.GoMod}}
|
||||
{{.GoVersion}}
|
||||
{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}
|
||||
`
|
||||
inv.Verb = "list"
|
||||
inv.Args = []string{"-m", "-f", format}
|
||||
stdout, err := r.Run(ctx, inv)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
lines := strings.Split(stdout.String(), "\n")
|
||||
if len(lines) < 5 {
|
||||
return nil, false, fmt.Errorf("unexpected stdout: %q", stdout.String())
|
||||
}
|
||||
mod := &ModuleJSON{
|
||||
Path: lines[0],
|
||||
Dir: lines[1],
|
||||
GoMod: lines[2],
|
||||
GoVersion: lines[3],
|
||||
Main: true,
|
||||
}
|
||||
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
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
// Copyright 2020 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 gocommand
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GoVersion reports the minor version number of the highest release
|
||||
// tag built into the go command on the PATH.
|
||||
//
|
||||
// Note that this may be higher than the version of the go tool used
|
||||
// to build this application, and thus the versions of the standard
|
||||
// go/{scanner,parser,ast,types} packages that are linked into it.
|
||||
// In that case, callers should either downgrade to the version of
|
||||
// go used to build the application, or report an error that the
|
||||
// application is too old to use the go command on the PATH.
|
||||
func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
|
||||
inv.Verb = "list"
|
||||
inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`, `--`, `unsafe`}
|
||||
inv.BuildFlags = nil // This is not a build command.
|
||||
inv.ModFlag = ""
|
||||
inv.ModFile = ""
|
||||
inv.Env = append(inv.Env[:len(inv.Env):len(inv.Env)], "GO111MODULE=off")
|
||||
|
||||
stdoutBytes, err := r.Run(ctx, inv)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
stdout := stdoutBytes.String()
|
||||
if len(stdout) < 3 {
|
||||
return 0, fmt.Errorf("bad ReleaseTags output: %q", stdout)
|
||||
}
|
||||
// Split up "[go1.1 go1.15]" and return highest go1.X value.
|
||||
tags := strings.Fields(stdout[1 : len(stdout)-2])
|
||||
for i := len(tags) - 1; i >= 0; i-- {
|
||||
var version int
|
||||
if _, err := fmt.Sscanf(tags[i], "go1.%d", &version); err != nil {
|
||||
continue
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
return 0, fmt.Errorf("no parseable ReleaseTags in %v", tags)
|
||||
}
|
||||
|
||||
// GoVersionOutput returns the complete output of the go version command.
|
||||
func GoVersionOutput(ctx context.Context, inv Invocation, r *Runner) (string, error) {
|
||||
inv.Verb = "version"
|
||||
goVersion, err := r.Run(ctx, inv)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return goVersion.String(), nil
|
||||
}
|
||||
|
||||
// ParseGoVersionOutput extracts the Go version string
|
||||
// from the output of the "go version" command.
|
||||
// Given an unrecognized form, it returns an empty string.
|
||||
func ParseGoVersionOutput(data string) string {
|
||||
re := regexp.MustCompile(`^go version (go\S+|devel \S+)`)
|
||||
m := re.FindStringSubmatch(data)
|
||||
if len(m) != 2 {
|
||||
return "" // unrecognized version
|
||||
}
|
||||
return m[1]
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// Copyright 2020 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 packagesinternal exposes internal-only fields from go/packages.
|
||||
package packagesinternal
|
||||
|
||||
import "fmt"
|
||||
|
||||
var GetDepsErrors = func(p any) []*PackageError { return nil }
|
||||
|
||||
type PackageError struct {
|
||||
ImportStack []string // shortest path from package named on command line to this one
|
||||
Pos string // position of error (if present, file:line:col)
|
||||
Err string // the error itself
|
||||
}
|
||||
|
||||
func (err PackageError) String() string {
|
||||
return fmt.Sprintf("%s: %s (import stack: %s)", err.Pos, err.Err, err.ImportStack)
|
||||
}
|
||||
|
||||
var TypecheckCgo int
|
||||
var DepsErrors int // must be set as a LoadMode to call GetDepsErrors
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// 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 pkgbits
|
||||
|
||||
// A Code is an enum value that can be encoded into bitstreams.
|
||||
//
|
||||
// Code types are preferable for enum types, because they allow
|
||||
// Decoder to detect desyncs.
|
||||
type Code interface {
|
||||
// Marker returns the SyncMarker for the Code's dynamic type.
|
||||
Marker() SyncMarker
|
||||
|
||||
// Value returns the Code's ordinal value.
|
||||
Value() int
|
||||
}
|
||||
|
||||
// A CodeVal distinguishes among go/constant.Value encodings.
|
||||
type CodeVal int
|
||||
|
||||
func (c CodeVal) Marker() SyncMarker { return SyncVal }
|
||||
func (c CodeVal) Value() int { return int(c) }
|
||||
|
||||
// Note: These values are public and cannot be changed without
|
||||
// updating the go/types importers.
|
||||
|
||||
const (
|
||||
ValBool CodeVal = iota
|
||||
ValString
|
||||
ValInt64
|
||||
ValBigInt
|
||||
ValBigRat
|
||||
ValBigFloat
|
||||
)
|
||||
|
||||
// A CodeType distinguishes among go/types.Type encodings.
|
||||
type CodeType int
|
||||
|
||||
func (c CodeType) Marker() SyncMarker { return SyncType }
|
||||
func (c CodeType) Value() int { return int(c) }
|
||||
|
||||
// Note: These values are public and cannot be changed without
|
||||
// updating the go/types importers.
|
||||
|
||||
const (
|
||||
TypeBasic CodeType = iota
|
||||
TypeNamed
|
||||
TypePointer
|
||||
TypeSlice
|
||||
TypeArray
|
||||
TypeChan
|
||||
TypeMap
|
||||
TypeSignature
|
||||
TypeStruct
|
||||
TypeInterface
|
||||
TypeUnion
|
||||
TypeTypeParam
|
||||
)
|
||||
|
||||
// A CodeObj distinguishes among go/types.Object encodings.
|
||||
type CodeObj int
|
||||
|
||||
func (c CodeObj) Marker() SyncMarker { return SyncCodeObj }
|
||||
func (c CodeObj) Value() int { return int(c) }
|
||||
|
||||
// Note: These values are public and cannot be changed without
|
||||
// updating the go/types importers.
|
||||
|
||||
const (
|
||||
ObjAlias CodeObj = iota
|
||||
ObjConst
|
||||
ObjType
|
||||
ObjFunc
|
||||
ObjVar
|
||||
ObjStub
|
||||
)
|
||||
+519
@@ -0,0 +1,519 @@
|
||||
// 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 pkgbits
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A PkgDecoder provides methods for decoding a package's Unified IR
|
||||
// export data.
|
||||
type PkgDecoder struct {
|
||||
// version is the file format version.
|
||||
version Version
|
||||
|
||||
// sync indicates whether the file uses sync markers.
|
||||
sync bool
|
||||
|
||||
// pkgPath is the package path for the package to be decoded.
|
||||
//
|
||||
// TODO(mdempsky): Remove; unneeded since CL 391014.
|
||||
pkgPath string
|
||||
|
||||
// elemData is the full data payload of the encoded package.
|
||||
// Elements are densely and contiguously packed together.
|
||||
//
|
||||
// The last 8 bytes of elemData are the package fingerprint.
|
||||
elemData string
|
||||
|
||||
// elemEnds stores the byte-offset end positions of element
|
||||
// bitstreams within elemData.
|
||||
//
|
||||
// For example, element I's bitstream data starts at elemEnds[I-1]
|
||||
// (or 0, if I==0) and ends at elemEnds[I].
|
||||
//
|
||||
// Note: elemEnds is indexed by absolute indices, not
|
||||
// section-relative indices.
|
||||
elemEnds []uint32
|
||||
|
||||
// elemEndsEnds stores the index-offset end positions of relocation
|
||||
// sections within elemEnds.
|
||||
//
|
||||
// For example, section K's end positions start at elemEndsEnds[K-1]
|
||||
// (or 0, if K==0) and end at elemEndsEnds[K].
|
||||
elemEndsEnds [numRelocs]uint32
|
||||
|
||||
scratchRelocEnt []RelocEnt
|
||||
}
|
||||
|
||||
// PkgPath returns the package path for the package
|
||||
//
|
||||
// TODO(mdempsky): Remove; unneeded since CL 391014.
|
||||
func (pr *PkgDecoder) PkgPath() string { return pr.pkgPath }
|
||||
|
||||
// SyncMarkers reports whether pr uses sync markers.
|
||||
func (pr *PkgDecoder) SyncMarkers() bool { return pr.sync }
|
||||
|
||||
// NewPkgDecoder returns a PkgDecoder initialized to read the Unified
|
||||
// IR export data from input. pkgPath is the package path for the
|
||||
// compilation unit that produced the export data.
|
||||
func NewPkgDecoder(pkgPath, input string) PkgDecoder {
|
||||
pr := PkgDecoder{
|
||||
pkgPath: pkgPath,
|
||||
}
|
||||
|
||||
// TODO(mdempsky): Implement direct indexing of input string to
|
||||
// avoid copying the position information.
|
||||
|
||||
r := strings.NewReader(input)
|
||||
|
||||
var ver uint32
|
||||
assert(binary.Read(r, binary.LittleEndian, &ver) == nil)
|
||||
pr.version = Version(ver)
|
||||
|
||||
if pr.version >= numVersions {
|
||||
panic(fmt.Errorf("cannot decode %q, export data version %d is greater than maximum supported version %d", pkgPath, pr.version, numVersions-1))
|
||||
}
|
||||
|
||||
if pr.version.Has(Flags) {
|
||||
var flags uint32
|
||||
assert(binary.Read(r, binary.LittleEndian, &flags) == nil)
|
||||
pr.sync = flags&flagSyncMarkers != 0
|
||||
}
|
||||
|
||||
assert(binary.Read(r, binary.LittleEndian, pr.elemEndsEnds[:]) == nil)
|
||||
|
||||
pr.elemEnds = make([]uint32, pr.elemEndsEnds[len(pr.elemEndsEnds)-1])
|
||||
assert(binary.Read(r, binary.LittleEndian, pr.elemEnds[:]) == nil)
|
||||
|
||||
pos, err := r.Seek(0, io.SeekCurrent)
|
||||
assert(err == nil)
|
||||
|
||||
pr.elemData = input[pos:]
|
||||
|
||||
const fingerprintSize = 8
|
||||
assert(len(pr.elemData)-fingerprintSize == int(pr.elemEnds[len(pr.elemEnds)-1]))
|
||||
|
||||
return pr
|
||||
}
|
||||
|
||||
// NumElems returns the number of elements in section k.
|
||||
func (pr *PkgDecoder) NumElems(k RelocKind) int {
|
||||
count := int(pr.elemEndsEnds[k])
|
||||
if k > 0 {
|
||||
count -= int(pr.elemEndsEnds[k-1])
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// TotalElems returns the total number of elements across all sections.
|
||||
func (pr *PkgDecoder) TotalElems() int {
|
||||
return len(pr.elemEnds)
|
||||
}
|
||||
|
||||
// Fingerprint returns the package fingerprint.
|
||||
func (pr *PkgDecoder) Fingerprint() [8]byte {
|
||||
var fp [8]byte
|
||||
copy(fp[:], pr.elemData[len(pr.elemData)-8:])
|
||||
return fp
|
||||
}
|
||||
|
||||
// AbsIdx returns the absolute index for the given (section, index)
|
||||
// pair.
|
||||
func (pr *PkgDecoder) AbsIdx(k RelocKind, idx Index) int {
|
||||
absIdx := int(idx)
|
||||
if k > 0 {
|
||||
absIdx += int(pr.elemEndsEnds[k-1])
|
||||
}
|
||||
if absIdx >= int(pr.elemEndsEnds[k]) {
|
||||
panicf("%v:%v is out of bounds; %v", k, idx, pr.elemEndsEnds)
|
||||
}
|
||||
return absIdx
|
||||
}
|
||||
|
||||
// DataIdx returns the raw element bitstream for the given (section,
|
||||
// index) pair.
|
||||
func (pr *PkgDecoder) DataIdx(k RelocKind, idx Index) string {
|
||||
absIdx := pr.AbsIdx(k, idx)
|
||||
|
||||
var start uint32
|
||||
if absIdx > 0 {
|
||||
start = pr.elemEnds[absIdx-1]
|
||||
}
|
||||
end := pr.elemEnds[absIdx]
|
||||
|
||||
return pr.elemData[start:end]
|
||||
}
|
||||
|
||||
// StringIdx returns the string value for the given string index.
|
||||
func (pr *PkgDecoder) StringIdx(idx Index) string {
|
||||
return pr.DataIdx(RelocString, idx)
|
||||
}
|
||||
|
||||
// NewDecoder returns a Decoder for the given (section, index) pair,
|
||||
// and decodes the given SyncMarker from the element bitstream.
|
||||
func (pr *PkgDecoder) NewDecoder(k RelocKind, idx Index, marker SyncMarker) Decoder {
|
||||
r := pr.NewDecoderRaw(k, idx)
|
||||
r.Sync(marker)
|
||||
return r
|
||||
}
|
||||
|
||||
// TempDecoder returns a Decoder for the given (section, index) pair,
|
||||
// and decodes the given SyncMarker from the element bitstream.
|
||||
// If possible the Decoder should be RetireDecoder'd when it is no longer
|
||||
// needed, this will avoid heap allocations.
|
||||
func (pr *PkgDecoder) TempDecoder(k RelocKind, idx Index, marker SyncMarker) Decoder {
|
||||
r := pr.TempDecoderRaw(k, idx)
|
||||
r.Sync(marker)
|
||||
return r
|
||||
}
|
||||
|
||||
func (pr *PkgDecoder) RetireDecoder(d *Decoder) {
|
||||
pr.scratchRelocEnt = d.Relocs
|
||||
d.Relocs = nil
|
||||
}
|
||||
|
||||
// NewDecoderRaw returns a Decoder for the given (section, index) pair.
|
||||
//
|
||||
// Most callers should use NewDecoder instead.
|
||||
func (pr *PkgDecoder) NewDecoderRaw(k RelocKind, idx Index) Decoder {
|
||||
r := Decoder{
|
||||
common: pr,
|
||||
k: k,
|
||||
Idx: idx,
|
||||
}
|
||||
|
||||
r.Data.Reset(pr.DataIdx(k, idx))
|
||||
r.Sync(SyncRelocs)
|
||||
r.Relocs = make([]RelocEnt, r.Len())
|
||||
for i := range r.Relocs {
|
||||
r.Sync(SyncReloc)
|
||||
r.Relocs[i] = RelocEnt{RelocKind(r.Len()), Index(r.Len())}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (pr *PkgDecoder) TempDecoderRaw(k RelocKind, idx Index) Decoder {
|
||||
r := Decoder{
|
||||
common: pr,
|
||||
k: k,
|
||||
Idx: idx,
|
||||
}
|
||||
|
||||
r.Data.Reset(pr.DataIdx(k, idx))
|
||||
r.Sync(SyncRelocs)
|
||||
l := r.Len()
|
||||
if cap(pr.scratchRelocEnt) >= l {
|
||||
r.Relocs = pr.scratchRelocEnt[:l]
|
||||
pr.scratchRelocEnt = nil
|
||||
} else {
|
||||
r.Relocs = make([]RelocEnt, l)
|
||||
}
|
||||
for i := range r.Relocs {
|
||||
r.Sync(SyncReloc)
|
||||
r.Relocs[i] = RelocEnt{RelocKind(r.Len()), Index(r.Len())}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// A Decoder provides methods for decoding an individual element's
|
||||
// bitstream data.
|
||||
type Decoder struct {
|
||||
common *PkgDecoder
|
||||
|
||||
Relocs []RelocEnt
|
||||
Data strings.Reader
|
||||
|
||||
k RelocKind
|
||||
Idx Index
|
||||
}
|
||||
|
||||
func (r *Decoder) checkErr(err error) {
|
||||
if err != nil {
|
||||
panicf("unexpected decoding error: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Decoder) rawUvarint() uint64 {
|
||||
x, err := readUvarint(&r.Data)
|
||||
r.checkErr(err)
|
||||
return x
|
||||
}
|
||||
|
||||
// readUvarint is a type-specialized copy of encoding/binary.ReadUvarint.
|
||||
// This avoids the interface conversion and thus has better escape properties,
|
||||
// which flows up the stack.
|
||||
func readUvarint(r *strings.Reader) (uint64, error) {
|
||||
var x uint64
|
||||
var s uint
|
||||
for i := range binary.MaxVarintLen64 {
|
||||
b, err := r.ReadByte()
|
||||
if err != nil {
|
||||
if i > 0 && err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return x, err
|
||||
}
|
||||
if b < 0x80 {
|
||||
if i == binary.MaxVarintLen64-1 && b > 1 {
|
||||
return x, overflow
|
||||
}
|
||||
return x | uint64(b)<<s, nil
|
||||
}
|
||||
x |= uint64(b&0x7f) << s
|
||||
s += 7
|
||||
}
|
||||
return x, overflow
|
||||
}
|
||||
|
||||
var overflow = errors.New("pkgbits: readUvarint overflows a 64-bit integer")
|
||||
|
||||
func (r *Decoder) rawVarint() int64 {
|
||||
ux := r.rawUvarint()
|
||||
|
||||
// Zig-zag decode.
|
||||
x := int64(ux >> 1)
|
||||
if ux&1 != 0 {
|
||||
x = ^x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func (r *Decoder) rawReloc(k RelocKind, idx int) Index {
|
||||
e := r.Relocs[idx]
|
||||
assert(e.Kind == k)
|
||||
return e.Idx
|
||||
}
|
||||
|
||||
// Sync decodes a sync marker from the element bitstream and asserts
|
||||
// that it matches the expected marker.
|
||||
//
|
||||
// If r.common.sync is false, then Sync is a no-op.
|
||||
func (r *Decoder) Sync(mWant SyncMarker) {
|
||||
if !r.common.sync {
|
||||
return
|
||||
}
|
||||
|
||||
pos, _ := r.Data.Seek(0, io.SeekCurrent)
|
||||
mHave := SyncMarker(r.rawUvarint())
|
||||
writerPCs := make([]int, r.rawUvarint())
|
||||
for i := range writerPCs {
|
||||
writerPCs[i] = int(r.rawUvarint())
|
||||
}
|
||||
|
||||
if mHave == mWant {
|
||||
return
|
||||
}
|
||||
|
||||
// There's some tension here between printing:
|
||||
//
|
||||
// (1) full file paths that tools can recognize (e.g., so emacs
|
||||
// hyperlinks the "file:line" text for easy navigation), or
|
||||
//
|
||||
// (2) short file paths that are easier for humans to read (e.g., by
|
||||
// omitting redundant or irrelevant details, so it's easier to
|
||||
// focus on the useful bits that remain).
|
||||
//
|
||||
// The current formatting favors the former, as it seems more
|
||||
// helpful in practice. But perhaps the formatting could be improved
|
||||
// to better address both concerns. For example, use relative file
|
||||
// paths if they would be shorter, or rewrite file paths to contain
|
||||
// "$GOROOT" (like objabi.AbsFile does) if tools can be taught how
|
||||
// to reliably expand that again.
|
||||
|
||||
fmt.Printf("export data desync: package %q, section %v, index %v, offset %v\n", r.common.pkgPath, r.k, r.Idx, pos)
|
||||
|
||||
fmt.Printf("\nfound %v, written at:\n", mHave)
|
||||
if len(writerPCs) == 0 {
|
||||
fmt.Printf("\t[stack trace unavailable; recompile package %q with -d=syncframes]\n", r.common.pkgPath)
|
||||
}
|
||||
for _, pc := range writerPCs {
|
||||
fmt.Printf("\t%s\n", r.common.StringIdx(r.rawReloc(RelocString, pc)))
|
||||
}
|
||||
|
||||
fmt.Printf("\nexpected %v, reading at:\n", mWant)
|
||||
var readerPCs [32]uintptr // TODO(mdempsky): Dynamically size?
|
||||
n := runtime.Callers(2, readerPCs[:])
|
||||
for _, pc := range fmtFrames(readerPCs[:n]...) {
|
||||
fmt.Printf("\t%s\n", pc)
|
||||
}
|
||||
|
||||
// We already printed a stack trace for the reader, so now we can
|
||||
// simply exit. Printing a second one with panic or base.Fatalf
|
||||
// would just be noise.
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Bool decodes and returns a bool value from the element bitstream.
|
||||
func (r *Decoder) Bool() bool {
|
||||
r.Sync(SyncBool)
|
||||
x, err := r.Data.ReadByte()
|
||||
r.checkErr(err)
|
||||
assert(x < 2)
|
||||
return x != 0
|
||||
}
|
||||
|
||||
// Int64 decodes and returns an int64 value from the element bitstream.
|
||||
func (r *Decoder) Int64() int64 {
|
||||
r.Sync(SyncInt64)
|
||||
return r.rawVarint()
|
||||
}
|
||||
|
||||
// Uint64 decodes and returns a uint64 value from the element bitstream.
|
||||
func (r *Decoder) Uint64() uint64 {
|
||||
r.Sync(SyncUint64)
|
||||
return r.rawUvarint()
|
||||
}
|
||||
|
||||
// Len decodes and returns a non-negative int value from the element bitstream.
|
||||
func (r *Decoder) Len() int { x := r.Uint64(); v := int(x); assert(uint64(v) == x); return v }
|
||||
|
||||
// Int decodes and returns an int value from the element bitstream.
|
||||
func (r *Decoder) Int() int { x := r.Int64(); v := int(x); assert(int64(v) == x); return v }
|
||||
|
||||
// Uint decodes and returns a uint value from the element bitstream.
|
||||
func (r *Decoder) Uint() uint { x := r.Uint64(); v := uint(x); assert(uint64(v) == x); return v }
|
||||
|
||||
// Code decodes a Code value from the element bitstream and returns
|
||||
// its ordinal value. It's the caller's responsibility to convert the
|
||||
// result to an appropriate Code type.
|
||||
//
|
||||
// TODO(mdempsky): Ideally this method would have signature "Code[T
|
||||
// Code] T" instead, but we don't allow generic methods and the
|
||||
// compiler can't depend on generics yet anyway.
|
||||
func (r *Decoder) Code(mark SyncMarker) int {
|
||||
r.Sync(mark)
|
||||
return r.Len()
|
||||
}
|
||||
|
||||
// Reloc decodes a relocation of expected section k from the element
|
||||
// bitstream and returns an index to the referenced element.
|
||||
func (r *Decoder) Reloc(k RelocKind) Index {
|
||||
r.Sync(SyncUseReloc)
|
||||
return r.rawReloc(k, r.Len())
|
||||
}
|
||||
|
||||
// String decodes and returns a string value from the element
|
||||
// bitstream.
|
||||
func (r *Decoder) String() string {
|
||||
r.Sync(SyncString)
|
||||
return r.common.StringIdx(r.Reloc(RelocString))
|
||||
}
|
||||
|
||||
// Strings decodes and returns a variable-length slice of strings from
|
||||
// the element bitstream.
|
||||
func (r *Decoder) Strings() []string {
|
||||
res := make([]string, r.Len())
|
||||
for i := range res {
|
||||
res[i] = r.String()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Value decodes and returns a constant.Value from the element
|
||||
// bitstream.
|
||||
func (r *Decoder) Value() constant.Value {
|
||||
r.Sync(SyncValue)
|
||||
isComplex := r.Bool()
|
||||
val := r.scalar()
|
||||
if isComplex {
|
||||
val = constant.BinaryOp(val, token.ADD, constant.MakeImag(r.scalar()))
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (r *Decoder) scalar() constant.Value {
|
||||
switch tag := CodeVal(r.Code(SyncVal)); tag {
|
||||
default:
|
||||
panic(fmt.Errorf("unexpected scalar tag: %v", tag))
|
||||
|
||||
case ValBool:
|
||||
return constant.MakeBool(r.Bool())
|
||||
case ValString:
|
||||
return constant.MakeString(r.String())
|
||||
case ValInt64:
|
||||
return constant.MakeInt64(r.Int64())
|
||||
case ValBigInt:
|
||||
return constant.Make(r.bigInt())
|
||||
case ValBigRat:
|
||||
num := r.bigInt()
|
||||
denom := r.bigInt()
|
||||
return constant.Make(new(big.Rat).SetFrac(num, denom))
|
||||
case ValBigFloat:
|
||||
return constant.Make(r.bigFloat())
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Decoder) bigInt() *big.Int {
|
||||
v := new(big.Int).SetBytes([]byte(r.String()))
|
||||
if r.Bool() {
|
||||
v.Neg(v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (r *Decoder) bigFloat() *big.Float {
|
||||
v := new(big.Float).SetPrec(512)
|
||||
assert(v.UnmarshalText([]byte(r.String())) == nil)
|
||||
return v
|
||||
}
|
||||
|
||||
// @@@ Helpers
|
||||
|
||||
// TODO(mdempsky): These should probably be removed. I think they're a
|
||||
// smell that the export data format is not yet quite right.
|
||||
|
||||
// PeekPkgPath returns the package path for the specified package
|
||||
// index.
|
||||
func (pr *PkgDecoder) PeekPkgPath(idx Index) string {
|
||||
var path string
|
||||
{
|
||||
r := pr.TempDecoder(RelocPkg, idx, SyncPkgDef)
|
||||
path = r.String()
|
||||
pr.RetireDecoder(&r)
|
||||
}
|
||||
if path == "" {
|
||||
path = pr.pkgPath
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// PeekObj returns the package path, object name, and CodeObj for the
|
||||
// specified object index.
|
||||
func (pr *PkgDecoder) PeekObj(idx Index) (string, string, CodeObj) {
|
||||
var ridx Index
|
||||
var name string
|
||||
var rcode int
|
||||
{
|
||||
r := pr.TempDecoder(RelocName, idx, SyncObject1)
|
||||
r.Sync(SyncSym)
|
||||
r.Sync(SyncPkg)
|
||||
ridx = r.Reloc(RelocPkg)
|
||||
name = r.String()
|
||||
rcode = r.Code(SyncCodeObj)
|
||||
pr.RetireDecoder(&r)
|
||||
}
|
||||
|
||||
path := pr.PeekPkgPath(ridx)
|
||||
assert(name != "")
|
||||
|
||||
tag := CodeObj(rcode)
|
||||
|
||||
return path, name, tag
|
||||
}
|
||||
|
||||
// Version reports the version of the bitstream.
|
||||
func (w *Decoder) Version() Version { return w.common.version }
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// 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 pkgbits implements low-level coding abstractions for
|
||||
// Unified IR's export data format.
|
||||
//
|
||||
// At a low-level, a package is a collection of bitstream elements.
|
||||
// Each element has a "kind" and a dense, non-negative index.
|
||||
// Elements can be randomly accessed given their kind and index.
|
||||
//
|
||||
// Individual elements are sequences of variable-length values (e.g.,
|
||||
// integers, booleans, strings, go/constant values, cross-references
|
||||
// to other elements). Package pkgbits provides APIs for encoding and
|
||||
// decoding these low-level values, but the details of mapping
|
||||
// higher-level Go constructs into elements is left to higher-level
|
||||
// abstractions.
|
||||
//
|
||||
// Elements may cross-reference each other with "relocations." For
|
||||
// example, an element representing a pointer type has a relocation
|
||||
// referring to the element type.
|
||||
//
|
||||
// Go constructs may be composed as a constellation of multiple
|
||||
// elements. For example, a declared function may have one element to
|
||||
// describe the object (e.g., its name, type, position), and a
|
||||
// separate element to describe its function body. This allows readers
|
||||
// some flexibility in efficiently seeking or re-reading data (e.g.,
|
||||
// inlining requires re-reading the function body for each inlined
|
||||
// call, without needing to re-read the object-level details).
|
||||
//
|
||||
// This is a copy of internal/pkgbits in the Go implementation.
|
||||
package pkgbits
|
||||
+392
@@ -0,0 +1,392 @@
|
||||
// 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 pkgbits
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"go/constant"
|
||||
"io"
|
||||
"math/big"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A PkgEncoder provides methods for encoding a package's Unified IR
|
||||
// export data.
|
||||
type PkgEncoder struct {
|
||||
// version of the bitstream.
|
||||
version Version
|
||||
|
||||
// elems holds the bitstream for previously encoded elements.
|
||||
elems [numRelocs][]string
|
||||
|
||||
// stringsIdx maps previously encoded strings to their index within
|
||||
// the RelocString section, to allow deduplication. That is,
|
||||
// elems[RelocString][stringsIdx[s]] == s (if present).
|
||||
stringsIdx map[string]Index
|
||||
|
||||
// syncFrames is the number of frames to write at each sync
|
||||
// marker. A negative value means sync markers are omitted.
|
||||
syncFrames int
|
||||
}
|
||||
|
||||
// SyncMarkers reports whether pw uses sync markers.
|
||||
func (pw *PkgEncoder) SyncMarkers() bool { return pw.syncFrames >= 0 }
|
||||
|
||||
// NewPkgEncoder returns an initialized PkgEncoder.
|
||||
//
|
||||
// syncFrames is the number of caller frames that should be serialized
|
||||
// at Sync points. Serializing additional frames results in larger
|
||||
// export data files, but can help diagnosing desync errors in
|
||||
// higher-level Unified IR reader/writer code. If syncFrames is
|
||||
// negative, then sync markers are omitted entirely.
|
||||
func NewPkgEncoder(version Version, syncFrames int) PkgEncoder {
|
||||
return PkgEncoder{
|
||||
version: version,
|
||||
stringsIdx: make(map[string]Index),
|
||||
syncFrames: syncFrames,
|
||||
}
|
||||
}
|
||||
|
||||
// DumpTo writes the package's encoded data to out0 and returns the
|
||||
// package fingerprint.
|
||||
func (pw *PkgEncoder) DumpTo(out0 io.Writer) (fingerprint [8]byte) {
|
||||
h := md5.New()
|
||||
out := io.MultiWriter(out0, h)
|
||||
|
||||
writeUint32 := func(x uint32) {
|
||||
assert(binary.Write(out, binary.LittleEndian, x) == nil)
|
||||
}
|
||||
|
||||
writeUint32(uint32(pw.version))
|
||||
|
||||
if pw.version.Has(Flags) {
|
||||
var flags uint32
|
||||
if pw.SyncMarkers() {
|
||||
flags |= flagSyncMarkers
|
||||
}
|
||||
writeUint32(flags)
|
||||
}
|
||||
|
||||
// Write elemEndsEnds.
|
||||
var sum uint32
|
||||
for _, elems := range &pw.elems {
|
||||
sum += uint32(len(elems))
|
||||
writeUint32(sum)
|
||||
}
|
||||
|
||||
// Write elemEnds.
|
||||
sum = 0
|
||||
for _, elems := range &pw.elems {
|
||||
for _, elem := range elems {
|
||||
sum += uint32(len(elem))
|
||||
writeUint32(sum)
|
||||
}
|
||||
}
|
||||
|
||||
// Write elemData.
|
||||
for _, elems := range &pw.elems {
|
||||
for _, elem := range elems {
|
||||
_, err := io.WriteString(out, elem)
|
||||
assert(err == nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Write fingerprint.
|
||||
copy(fingerprint[:], h.Sum(nil))
|
||||
_, err := out0.Write(fingerprint[:])
|
||||
assert(err == nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// StringIdx adds a string value to the strings section, if not
|
||||
// already present, and returns its index.
|
||||
func (pw *PkgEncoder) StringIdx(s string) Index {
|
||||
if idx, ok := pw.stringsIdx[s]; ok {
|
||||
assert(pw.elems[RelocString][idx] == s)
|
||||
return idx
|
||||
}
|
||||
|
||||
idx := Index(len(pw.elems[RelocString]))
|
||||
pw.elems[RelocString] = append(pw.elems[RelocString], s)
|
||||
pw.stringsIdx[s] = idx
|
||||
return idx
|
||||
}
|
||||
|
||||
// NewEncoder returns an Encoder for a new element within the given
|
||||
// section, and encodes the given SyncMarker as the start of the
|
||||
// element bitstream.
|
||||
func (pw *PkgEncoder) NewEncoder(k RelocKind, marker SyncMarker) Encoder {
|
||||
e := pw.NewEncoderRaw(k)
|
||||
e.Sync(marker)
|
||||
return e
|
||||
}
|
||||
|
||||
// NewEncoderRaw returns an Encoder for a new element within the given
|
||||
// section.
|
||||
//
|
||||
// Most callers should use NewEncoder instead.
|
||||
func (pw *PkgEncoder) NewEncoderRaw(k RelocKind) Encoder {
|
||||
idx := Index(len(pw.elems[k]))
|
||||
pw.elems[k] = append(pw.elems[k], "") // placeholder
|
||||
|
||||
return Encoder{
|
||||
p: pw,
|
||||
k: k,
|
||||
Idx: idx,
|
||||
}
|
||||
}
|
||||
|
||||
// An Encoder provides methods for encoding an individual element's
|
||||
// bitstream data.
|
||||
type Encoder struct {
|
||||
p *PkgEncoder
|
||||
|
||||
Relocs []RelocEnt
|
||||
RelocMap map[RelocEnt]uint32
|
||||
Data bytes.Buffer // accumulated element bitstream data
|
||||
|
||||
encodingRelocHeader bool
|
||||
|
||||
k RelocKind
|
||||
Idx Index // index within relocation section
|
||||
}
|
||||
|
||||
// Flush finalizes the element's bitstream and returns its Index.
|
||||
func (w *Encoder) Flush() Index {
|
||||
var sb strings.Builder
|
||||
|
||||
// Backup the data so we write the relocations at the front.
|
||||
var tmp bytes.Buffer
|
||||
io.Copy(&tmp, &w.Data)
|
||||
|
||||
// TODO(mdempsky): Consider writing these out separately so they're
|
||||
// easier to strip, along with function bodies, so that we can prune
|
||||
// down to just the data that's relevant to go/types.
|
||||
if w.encodingRelocHeader {
|
||||
panic("encodingRelocHeader already true; recursive flush?")
|
||||
}
|
||||
w.encodingRelocHeader = true
|
||||
w.Sync(SyncRelocs)
|
||||
w.Len(len(w.Relocs))
|
||||
for _, rEnt := range w.Relocs {
|
||||
w.Sync(SyncReloc)
|
||||
w.Len(int(rEnt.Kind))
|
||||
w.Len(int(rEnt.Idx))
|
||||
}
|
||||
|
||||
io.Copy(&sb, &w.Data)
|
||||
io.Copy(&sb, &tmp)
|
||||
w.p.elems[w.k][w.Idx] = sb.String()
|
||||
|
||||
return w.Idx
|
||||
}
|
||||
|
||||
func (w *Encoder) checkErr(err error) {
|
||||
if err != nil {
|
||||
panicf("unexpected encoding error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Encoder) rawUvarint(x uint64) {
|
||||
var buf [binary.MaxVarintLen64]byte
|
||||
n := binary.PutUvarint(buf[:], x)
|
||||
_, err := w.Data.Write(buf[:n])
|
||||
w.checkErr(err)
|
||||
}
|
||||
|
||||
func (w *Encoder) rawVarint(x int64) {
|
||||
// Zig-zag encode.
|
||||
ux := uint64(x) << 1
|
||||
if x < 0 {
|
||||
ux = ^ux
|
||||
}
|
||||
|
||||
w.rawUvarint(ux)
|
||||
}
|
||||
|
||||
func (w *Encoder) rawReloc(r RelocKind, idx Index) int {
|
||||
e := RelocEnt{r, idx}
|
||||
if w.RelocMap != nil {
|
||||
if i, ok := w.RelocMap[e]; ok {
|
||||
return int(i)
|
||||
}
|
||||
} else {
|
||||
w.RelocMap = make(map[RelocEnt]uint32)
|
||||
}
|
||||
|
||||
i := len(w.Relocs)
|
||||
w.RelocMap[e] = uint32(i)
|
||||
w.Relocs = append(w.Relocs, e)
|
||||
return i
|
||||
}
|
||||
|
||||
func (w *Encoder) Sync(m SyncMarker) {
|
||||
if !w.p.SyncMarkers() {
|
||||
return
|
||||
}
|
||||
|
||||
// Writing out stack frame string references requires working
|
||||
// relocations, but writing out the relocations themselves involves
|
||||
// sync markers. To prevent infinite recursion, we simply trim the
|
||||
// stack frame for sync markers within the relocation header.
|
||||
var frames []string
|
||||
if !w.encodingRelocHeader && w.p.syncFrames > 0 {
|
||||
pcs := make([]uintptr, w.p.syncFrames)
|
||||
n := runtime.Callers(2, pcs)
|
||||
frames = fmtFrames(pcs[:n]...)
|
||||
}
|
||||
|
||||
// TODO(mdempsky): Save space by writing out stack frames as a
|
||||
// linked list so we can share common stack frames.
|
||||
w.rawUvarint(uint64(m))
|
||||
w.rawUvarint(uint64(len(frames)))
|
||||
for _, frame := range frames {
|
||||
w.rawUvarint(uint64(w.rawReloc(RelocString, w.p.StringIdx(frame))))
|
||||
}
|
||||
}
|
||||
|
||||
// Bool encodes and writes a bool value into the element bitstream,
|
||||
// and then returns the bool value.
|
||||
//
|
||||
// For simple, 2-alternative encodings, the idiomatic way to call Bool
|
||||
// is something like:
|
||||
//
|
||||
// if w.Bool(x != 0) {
|
||||
// // alternative #1
|
||||
// } else {
|
||||
// // alternative #2
|
||||
// }
|
||||
//
|
||||
// For multi-alternative encodings, use Code instead.
|
||||
func (w *Encoder) Bool(b bool) bool {
|
||||
w.Sync(SyncBool)
|
||||
var x byte
|
||||
if b {
|
||||
x = 1
|
||||
}
|
||||
err := w.Data.WriteByte(x)
|
||||
w.checkErr(err)
|
||||
return b
|
||||
}
|
||||
|
||||
// Int64 encodes and writes an int64 value into the element bitstream.
|
||||
func (w *Encoder) Int64(x int64) {
|
||||
w.Sync(SyncInt64)
|
||||
w.rawVarint(x)
|
||||
}
|
||||
|
||||
// Uint64 encodes and writes a uint64 value into the element bitstream.
|
||||
func (w *Encoder) Uint64(x uint64) {
|
||||
w.Sync(SyncUint64)
|
||||
w.rawUvarint(x)
|
||||
}
|
||||
|
||||
// Len encodes and writes a non-negative int value into the element bitstream.
|
||||
func (w *Encoder) Len(x int) { assert(x >= 0); w.Uint64(uint64(x)) }
|
||||
|
||||
// Int encodes and writes an int value into the element bitstream.
|
||||
func (w *Encoder) Int(x int) { w.Int64(int64(x)) }
|
||||
|
||||
// Uint encodes and writes a uint value into the element bitstream.
|
||||
func (w *Encoder) Uint(x uint) { w.Uint64(uint64(x)) }
|
||||
|
||||
// Reloc encodes and writes a relocation for the given (section,
|
||||
// index) pair into the element bitstream.
|
||||
//
|
||||
// Note: Only the index is formally written into the element
|
||||
// bitstream, so bitstream decoders must know from context which
|
||||
// section an encoded relocation refers to.
|
||||
func (w *Encoder) Reloc(r RelocKind, idx Index) {
|
||||
w.Sync(SyncUseReloc)
|
||||
w.Len(w.rawReloc(r, idx))
|
||||
}
|
||||
|
||||
// Code encodes and writes a Code value into the element bitstream.
|
||||
func (w *Encoder) Code(c Code) {
|
||||
w.Sync(c.Marker())
|
||||
w.Len(c.Value())
|
||||
}
|
||||
|
||||
// String encodes and writes a string value into the element
|
||||
// bitstream.
|
||||
//
|
||||
// Internally, strings are deduplicated by adding them to the strings
|
||||
// section (if not already present), and then writing a relocation
|
||||
// into the element bitstream.
|
||||
func (w *Encoder) String(s string) {
|
||||
w.StringRef(w.p.StringIdx(s))
|
||||
}
|
||||
|
||||
// StringRef writes a reference to the given index, which must be a
|
||||
// previously encoded string value.
|
||||
func (w *Encoder) StringRef(idx Index) {
|
||||
w.Sync(SyncString)
|
||||
w.Reloc(RelocString, idx)
|
||||
}
|
||||
|
||||
// Strings encodes and writes a variable-length slice of strings into
|
||||
// the element bitstream.
|
||||
func (w *Encoder) Strings(ss []string) {
|
||||
w.Len(len(ss))
|
||||
for _, s := range ss {
|
||||
w.String(s)
|
||||
}
|
||||
}
|
||||
|
||||
// Value encodes and writes a constant.Value into the element
|
||||
// bitstream.
|
||||
func (w *Encoder) Value(val constant.Value) {
|
||||
w.Sync(SyncValue)
|
||||
if w.Bool(val.Kind() == constant.Complex) {
|
||||
w.scalar(constant.Real(val))
|
||||
w.scalar(constant.Imag(val))
|
||||
} else {
|
||||
w.scalar(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Encoder) scalar(val constant.Value) {
|
||||
switch v := constant.Val(val).(type) {
|
||||
default:
|
||||
panicf("unhandled %v (%v)", val, val.Kind())
|
||||
case bool:
|
||||
w.Code(ValBool)
|
||||
w.Bool(v)
|
||||
case string:
|
||||
w.Code(ValString)
|
||||
w.String(v)
|
||||
case int64:
|
||||
w.Code(ValInt64)
|
||||
w.Int64(v)
|
||||
case *big.Int:
|
||||
w.Code(ValBigInt)
|
||||
w.bigInt(v)
|
||||
case *big.Rat:
|
||||
w.Code(ValBigRat)
|
||||
w.bigInt(v.Num())
|
||||
w.bigInt(v.Denom())
|
||||
case *big.Float:
|
||||
w.Code(ValBigFloat)
|
||||
w.bigFloat(v)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Encoder) bigInt(v *big.Int) {
|
||||
b := v.Bytes()
|
||||
w.String(string(b)) // TODO: More efficient encoding.
|
||||
w.Bool(v.Sign() < 0)
|
||||
}
|
||||
|
||||
func (w *Encoder) bigFloat(v *big.Float) {
|
||||
b := v.Append(nil, 'p', -1)
|
||||
w.String(string(b)) // TODO: More efficient encoding.
|
||||
}
|
||||
|
||||
// Version reports the version of the bitstream.
|
||||
func (w *Encoder) Version() Version { return w.p.version }
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// 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 pkgbits
|
||||
|
||||
const (
|
||||
flagSyncMarkers = 1 << iota // file format contains sync markers
|
||||
)
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// 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 pkgbits
|
||||
|
||||
// A RelocKind indicates a particular section within a unified IR export.
|
||||
type RelocKind int32
|
||||
|
||||
// An Index represents a bitstream element index within a particular
|
||||
// section.
|
||||
type Index int32
|
||||
|
||||
// A relocEnt (relocation entry) is an entry in an element's local
|
||||
// reference table.
|
||||
//
|
||||
// TODO(mdempsky): Rename this too.
|
||||
type RelocEnt struct {
|
||||
Kind RelocKind
|
||||
Idx Index
|
||||
}
|
||||
|
||||
// Reserved indices within the meta relocation section.
|
||||
const (
|
||||
PublicRootIdx Index = 0
|
||||
PrivateRootIdx Index = 1
|
||||
)
|
||||
|
||||
const (
|
||||
RelocString RelocKind = iota
|
||||
RelocMeta
|
||||
RelocPosBase
|
||||
RelocPkg
|
||||
RelocName
|
||||
RelocType
|
||||
RelocObj
|
||||
RelocObjExt
|
||||
RelocObjDict
|
||||
RelocBody
|
||||
|
||||
numRelocs = iota
|
||||
)
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// 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 pkgbits
|
||||
|
||||
import "fmt"
|
||||
|
||||
func assert(b bool) {
|
||||
if !b {
|
||||
panic("assertion failed")
|
||||
}
|
||||
}
|
||||
|
||||
func panicf(format string, args ...any) {
|
||||
panic(fmt.Errorf(format, args...))
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
// 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 pkgbits
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// fmtFrames formats a backtrace for reporting reader/writer desyncs.
|
||||
func fmtFrames(pcs ...uintptr) []string {
|
||||
res := make([]string, 0, len(pcs))
|
||||
walkFrames(pcs, func(file string, line int, name string, offset uintptr) {
|
||||
// Trim package from function name. It's just redundant noise.
|
||||
name = strings.TrimPrefix(name, "cmd/compile/internal/noder.")
|
||||
|
||||
res = append(res, fmt.Sprintf("%s:%v: %s +0x%v", file, line, name, offset))
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
type frameVisitor func(file string, line int, name string, offset uintptr)
|
||||
|
||||
// walkFrames calls visit for each call frame represented by pcs.
|
||||
//
|
||||
// pcs should be a slice of PCs, as returned by runtime.Callers.
|
||||
func walkFrames(pcs []uintptr, visit frameVisitor) {
|
||||
if len(pcs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
frames := runtime.CallersFrames(pcs)
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
visit(frame.File, frame.Line, frame.Function, frame.PC-frame.Entry)
|
||||
if !more {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SyncMarker is an enum type that represents markers that may be
|
||||
// written to export data to ensure the reader and writer stay
|
||||
// synchronized.
|
||||
type SyncMarker int
|
||||
|
||||
//go:generate stringer -type=SyncMarker -trimprefix=Sync
|
||||
|
||||
const (
|
||||
_ SyncMarker = iota
|
||||
|
||||
// Public markers (known to go/types importers).
|
||||
|
||||
// Low-level coding markers.
|
||||
SyncEOF
|
||||
SyncBool
|
||||
SyncInt64
|
||||
SyncUint64
|
||||
SyncString
|
||||
SyncValue
|
||||
SyncVal
|
||||
SyncRelocs
|
||||
SyncReloc
|
||||
SyncUseReloc
|
||||
|
||||
// Higher-level object and type markers.
|
||||
SyncPublic
|
||||
SyncPos
|
||||
SyncPosBase
|
||||
SyncObject
|
||||
SyncObject1
|
||||
SyncPkg
|
||||
SyncPkgDef
|
||||
SyncMethod
|
||||
SyncType
|
||||
SyncTypeIdx
|
||||
SyncTypeParamNames
|
||||
SyncSignature
|
||||
SyncParams
|
||||
SyncParam
|
||||
SyncCodeObj
|
||||
SyncSym
|
||||
SyncLocalIdent
|
||||
SyncSelector
|
||||
|
||||
// Private markers (only known to cmd/compile).
|
||||
SyncPrivate
|
||||
|
||||
SyncFuncExt
|
||||
SyncVarExt
|
||||
SyncTypeExt
|
||||
SyncPragma
|
||||
|
||||
SyncExprList
|
||||
SyncExprs
|
||||
SyncExpr
|
||||
SyncExprType
|
||||
SyncAssign
|
||||
SyncOp
|
||||
SyncFuncLit
|
||||
SyncCompLit
|
||||
|
||||
SyncDecl
|
||||
SyncFuncBody
|
||||
SyncOpenScope
|
||||
SyncCloseScope
|
||||
SyncCloseAnotherScope
|
||||
SyncDeclNames
|
||||
SyncDeclName
|
||||
|
||||
SyncStmts
|
||||
SyncBlockStmt
|
||||
SyncIfStmt
|
||||
SyncForStmt
|
||||
SyncSwitchStmt
|
||||
SyncRangeStmt
|
||||
SyncCaseClause
|
||||
SyncCommClause
|
||||
SyncSelectStmt
|
||||
SyncDecls
|
||||
SyncLabeledStmt
|
||||
SyncUseObjLocal
|
||||
SyncAddLocal
|
||||
SyncLinkname
|
||||
SyncStmt1
|
||||
SyncStmtsEnd
|
||||
SyncLabel
|
||||
SyncOptLabel
|
||||
|
||||
SyncMultiExpr
|
||||
SyncRType
|
||||
SyncConvRTTI
|
||||
)
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// Code generated by "stringer -type=SyncMarker -trimprefix=Sync"; DO NOT EDIT.
|
||||
|
||||
package pkgbits
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[SyncEOF-1]
|
||||
_ = x[SyncBool-2]
|
||||
_ = x[SyncInt64-3]
|
||||
_ = x[SyncUint64-4]
|
||||
_ = x[SyncString-5]
|
||||
_ = x[SyncValue-6]
|
||||
_ = x[SyncVal-7]
|
||||
_ = x[SyncRelocs-8]
|
||||
_ = x[SyncReloc-9]
|
||||
_ = x[SyncUseReloc-10]
|
||||
_ = x[SyncPublic-11]
|
||||
_ = x[SyncPos-12]
|
||||
_ = x[SyncPosBase-13]
|
||||
_ = x[SyncObject-14]
|
||||
_ = x[SyncObject1-15]
|
||||
_ = x[SyncPkg-16]
|
||||
_ = x[SyncPkgDef-17]
|
||||
_ = x[SyncMethod-18]
|
||||
_ = x[SyncType-19]
|
||||
_ = x[SyncTypeIdx-20]
|
||||
_ = x[SyncTypeParamNames-21]
|
||||
_ = x[SyncSignature-22]
|
||||
_ = x[SyncParams-23]
|
||||
_ = x[SyncParam-24]
|
||||
_ = x[SyncCodeObj-25]
|
||||
_ = x[SyncSym-26]
|
||||
_ = x[SyncLocalIdent-27]
|
||||
_ = x[SyncSelector-28]
|
||||
_ = x[SyncPrivate-29]
|
||||
_ = x[SyncFuncExt-30]
|
||||
_ = x[SyncVarExt-31]
|
||||
_ = x[SyncTypeExt-32]
|
||||
_ = x[SyncPragma-33]
|
||||
_ = x[SyncExprList-34]
|
||||
_ = x[SyncExprs-35]
|
||||
_ = x[SyncExpr-36]
|
||||
_ = x[SyncExprType-37]
|
||||
_ = x[SyncAssign-38]
|
||||
_ = x[SyncOp-39]
|
||||
_ = x[SyncFuncLit-40]
|
||||
_ = x[SyncCompLit-41]
|
||||
_ = x[SyncDecl-42]
|
||||
_ = x[SyncFuncBody-43]
|
||||
_ = x[SyncOpenScope-44]
|
||||
_ = x[SyncCloseScope-45]
|
||||
_ = x[SyncCloseAnotherScope-46]
|
||||
_ = x[SyncDeclNames-47]
|
||||
_ = x[SyncDeclName-48]
|
||||
_ = x[SyncStmts-49]
|
||||
_ = x[SyncBlockStmt-50]
|
||||
_ = x[SyncIfStmt-51]
|
||||
_ = x[SyncForStmt-52]
|
||||
_ = x[SyncSwitchStmt-53]
|
||||
_ = x[SyncRangeStmt-54]
|
||||
_ = x[SyncCaseClause-55]
|
||||
_ = x[SyncCommClause-56]
|
||||
_ = x[SyncSelectStmt-57]
|
||||
_ = x[SyncDecls-58]
|
||||
_ = x[SyncLabeledStmt-59]
|
||||
_ = x[SyncUseObjLocal-60]
|
||||
_ = x[SyncAddLocal-61]
|
||||
_ = x[SyncLinkname-62]
|
||||
_ = x[SyncStmt1-63]
|
||||
_ = x[SyncStmtsEnd-64]
|
||||
_ = x[SyncLabel-65]
|
||||
_ = x[SyncOptLabel-66]
|
||||
_ = x[SyncMultiExpr-67]
|
||||
_ = x[SyncRType-68]
|
||||
_ = x[SyncConvRTTI-69]
|
||||
}
|
||||
|
||||
const _SyncMarker_name = "EOFBoolInt64Uint64StringValueValRelocsRelocUseRelocPublicPosPosBaseObjectObject1PkgPkgDefMethodTypeTypeIdxTypeParamNamesSignatureParamsParamCodeObjSymLocalIdentSelectorPrivateFuncExtVarExtTypeExtPragmaExprListExprsExprExprTypeAssignOpFuncLitCompLitDeclFuncBodyOpenScopeCloseScopeCloseAnotherScopeDeclNamesDeclNameStmtsBlockStmtIfStmtForStmtSwitchStmtRangeStmtCaseClauseCommClauseSelectStmtDeclsLabeledStmtUseObjLocalAddLocalLinknameStmt1StmtsEndLabelOptLabelMultiExprRTypeConvRTTI"
|
||||
|
||||
var _SyncMarker_index = [...]uint16{0, 3, 7, 12, 18, 24, 29, 32, 38, 43, 51, 57, 60, 67, 73, 80, 83, 89, 95, 99, 106, 120, 129, 135, 140, 147, 150, 160, 168, 175, 182, 188, 195, 201, 209, 214, 218, 226, 232, 234, 241, 248, 252, 260, 269, 279, 296, 305, 313, 318, 327, 333, 340, 350, 359, 369, 379, 389, 394, 405, 416, 424, 432, 437, 445, 450, 458, 467, 472, 480}
|
||||
|
||||
func (i SyncMarker) String() string {
|
||||
i -= 1
|
||||
if i < 0 || i >= SyncMarker(len(_SyncMarker_index)-1) {
|
||||
return "SyncMarker(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||
}
|
||||
return _SyncMarker_name[_SyncMarker_index[i]:_SyncMarker_index[i+1]]
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
// 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 pkgbits
|
||||
|
||||
// Version indicates a version of a unified IR bitstream.
|
||||
// Each Version indicates the addition, removal, or change of
|
||||
// new data in the bitstream.
|
||||
//
|
||||
// These are serialized to disk and the interpretation remains fixed.
|
||||
type Version uint32
|
||||
|
||||
const (
|
||||
// V0: initial prototype.
|
||||
//
|
||||
// All data that is not assigned a Field is in version V0
|
||||
// and has not been deprecated.
|
||||
V0 Version = iota
|
||||
|
||||
// V1: adds the Flags uint32 word
|
||||
V1
|
||||
|
||||
// V2: removes unused legacy fields and supports type parameters for aliases.
|
||||
// - remove the legacy "has init" bool from the public root
|
||||
// - remove obj's "derived func instance" bool
|
||||
// - add a TypeParamNames field to ObjAlias
|
||||
// - remove derived info "needed" bool
|
||||
V2
|
||||
|
||||
numVersions = iota
|
||||
)
|
||||
|
||||
// Field denotes a unit of data in the serialized unified IR bitstream.
|
||||
// It is conceptually a like field in a structure.
|
||||
//
|
||||
// We only really need Fields when the data may or may not be present
|
||||
// in a stream based on the Version of the bitstream.
|
||||
//
|
||||
// Unlike much of pkgbits, Fields are not serialized and
|
||||
// can change values as needed.
|
||||
type Field int
|
||||
|
||||
const (
|
||||
// Flags in a uint32 in the header of a bitstream
|
||||
// that is used to indicate whether optional features are enabled.
|
||||
Flags Field = iota
|
||||
|
||||
// Deprecated: HasInit was a bool indicating whether a package
|
||||
// has any init functions.
|
||||
HasInit
|
||||
|
||||
// Deprecated: DerivedFuncInstance was a bool indicating
|
||||
// whether an object was a function instance.
|
||||
DerivedFuncInstance
|
||||
|
||||
// ObjAlias has a list of TypeParamNames.
|
||||
AliasTypeParamNames
|
||||
|
||||
// Deprecated: DerivedInfoNeeded was a bool indicating
|
||||
// whether a type was a derived type.
|
||||
DerivedInfoNeeded
|
||||
|
||||
numFields = iota
|
||||
)
|
||||
|
||||
// introduced is the version a field was added.
|
||||
var introduced = [numFields]Version{
|
||||
Flags: V1,
|
||||
AliasTypeParamNames: V2,
|
||||
}
|
||||
|
||||
// removed is the version a field was removed in or 0 for fields
|
||||
// that have not yet been deprecated.
|
||||
// (So removed[f]-1 is the last version it is included in.)
|
||||
var removed = [numFields]Version{
|
||||
HasInit: V2,
|
||||
DerivedFuncInstance: V2,
|
||||
DerivedInfoNeeded: V2,
|
||||
}
|
||||
|
||||
// Has reports whether field f is present in a bitstream at version v.
|
||||
func (v Version) Has(f Field) bool {
|
||||
return introduced[f] <= v && (v < removed[f] || removed[f] == V0)
|
||||
}
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
// Copyright 2025 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 generate.go. DO NOT EDIT.
|
||||
|
||||
package stdlib
|
||||
|
||||
type pkginfo struct {
|
||||
name string
|
||||
deps string // list of indices of dependencies, as varint-encoded deltas
|
||||
}
|
||||
|
||||
var deps = [...]pkginfo{
|
||||
{"archive/tar", "\x03j\x03E5\x01\v\x01#\x01\x01\x02\x05\n\x02\x01\x02\x02\v"},
|
||||
{"archive/zip", "\x02\x04`\a\x16\x0205\x01+\x05\x01\x11\x03\x02\r\x04"},
|
||||
{"bufio", "\x03j}F\x13"},
|
||||
{"bytes", "m+R\x03\fH\x02\x02"},
|
||||
{"cmp", ""},
|
||||
{"compress/bzip2", "\x02\x02\xe6\x01C"},
|
||||
{"compress/flate", "\x02k\x03z\r\x025\x01\x03"},
|
||||
{"compress/gzip", "\x02\x04`\a\x03\x15eU"},
|
||||
{"compress/lzw", "\x02k\x03z"},
|
||||
{"compress/zlib", "\x02\x04`\a\x03\x13\x01f"},
|
||||
{"container/heap", "\xae\x02"},
|
||||
{"container/list", ""},
|
||||
{"container/ring", ""},
|
||||
{"context", "m\\i\x01\f"},
|
||||
{"crypto", "\x83\x01gE"},
|
||||
{"crypto/aes", "\x10\n\a\x8e\x02"},
|
||||
{"crypto/cipher", "\x03\x1e\x01\x01\x1d\x11\x1c,Q"},
|
||||
{"crypto/des", "\x10\x13\x1d-,\x96\x01\x03"},
|
||||
{"crypto/dsa", "@\x04)}\x0e"},
|
||||
{"crypto/ecdh", "\x03\v\f\x0e\x04\x14\x04\r\x1c}"},
|
||||
{"crypto/ecdsa", "\x0e\x05\x03\x04\x01\x0e\x16\x01\x04\f\x01\x1c}\x0e\x04L\x01"},
|
||||
{"crypto/ed25519", "\x0e\x1c\x16\n\a\x1c}E"},
|
||||
{"crypto/elliptic", "0=}\x0e:"},
|
||||
{"crypto/fips140", " \x05\x90\x01"},
|
||||
{"crypto/hkdf", "-\x12\x01-\x16"},
|
||||
{"crypto/hmac", "\x1a\x14\x11\x01\x112"},
|
||||
{"crypto/internal/boring", "\x0e\x02\rf"},
|
||||
{"crypto/internal/boring/bbig", "\x1a\xde\x01M"},
|
||||
{"crypto/internal/boring/bcache", "\xb3\x02\x12"},
|
||||
{"crypto/internal/boring/sig", ""},
|
||||
{"crypto/internal/cryptotest", "\x03\r\n)\x0e\x19\x06\x13\x12#\a\t\x11\x11\x11\x1b\x01\f\r\x05\n"},
|
||||
{"crypto/internal/entropy", "E"},
|
||||
{"crypto/internal/fips140", ">/}9\r\x15"},
|
||||
{"crypto/internal/fips140/aes", "\x03\x1d\x03\x02\x13\x04\x01\x01\x05*\x8c\x016"},
|
||||
{"crypto/internal/fips140/aes/gcm", " \x01\x02\x02\x02\x11\x04\x01\x06*\x8a\x01"},
|
||||
{"crypto/internal/fips140/alias", "\xc5\x02"},
|
||||
{"crypto/internal/fips140/bigmod", "%\x17\x01\x06*\x8c\x01"},
|
||||
{"crypto/internal/fips140/check", " \x0e\x06\b\x02\xac\x01["},
|
||||
{"crypto/internal/fips140/check/checktest", "%\xfe\x01\""},
|
||||
{"crypto/internal/fips140/drbg", "\x03\x1c\x01\x01\x04\x13\x04\b\x01(}\x0f9"},
|
||||
{"crypto/internal/fips140/ecdh", "\x03\x1d\x05\x02\t\f1}\x0f9"},
|
||||
{"crypto/internal/fips140/ecdsa", "\x03\x1d\x04\x01\x02\a\x02\x067}H"},
|
||||
{"crypto/internal/fips140/ed25519", "\x03\x1d\x05\x02\x04\v7\xc2\x01\x03"},
|
||||
{"crypto/internal/fips140/edwards25519", "%\a\f\x041\x8c\x019"},
|
||||
{"crypto/internal/fips140/edwards25519/field", "%\x13\x041\x8c\x01"},
|
||||
{"crypto/internal/fips140/hkdf", "\x03\x1d\x05\t\x069"},
|
||||
{"crypto/internal/fips140/hmac", "\x03\x1d\x14\x01\x017"},
|
||||
{"crypto/internal/fips140/mlkem", "\x03\x1d\x05\x02\x0e\x03\x041"},
|
||||
{"crypto/internal/fips140/nistec", "%\f\a\x041\x8c\x01*\x0f\x13"},
|
||||
{"crypto/internal/fips140/nistec/fiat", "%\x135\x8c\x01"},
|
||||
{"crypto/internal/fips140/pbkdf2", "\x03\x1d\x05\t\x069"},
|
||||
{"crypto/internal/fips140/rsa", "\x03\x1d\x04\x01\x02\r\x01\x01\x025}H"},
|
||||
{"crypto/internal/fips140/sha256", "\x03\x1d\x1c\x01\x06*\x8c\x01"},
|
||||
{"crypto/internal/fips140/sha3", "\x03\x1d\x18\x04\x010\x8c\x01L"},
|
||||
{"crypto/internal/fips140/sha512", "\x03\x1d\x1c\x01\x06*\x8c\x01"},
|
||||
{"crypto/internal/fips140/ssh", " \x05"},
|
||||
{"crypto/internal/fips140/subtle", "#"},
|
||||
{"crypto/internal/fips140/tls12", "\x03\x1d\x05\t\x06\x027"},
|
||||
{"crypto/internal/fips140/tls13", "\x03\x1d\x05\b\a\b1"},
|
||||
{"crypto/internal/fips140deps", ""},
|
||||
{"crypto/internal/fips140deps/byteorder", "\x99\x01"},
|
||||
{"crypto/internal/fips140deps/cpu", "\xad\x01\a"},
|
||||
{"crypto/internal/fips140deps/godebug", "\xb5\x01"},
|
||||
{"crypto/internal/fips140hash", "5\x1a4\xc2\x01"},
|
||||
{"crypto/internal/fips140only", "'\r\x01\x01M25"},
|
||||
{"crypto/internal/fips140test", ""},
|
||||
{"crypto/internal/hpke", "\x0e\x01\x01\x03\x1a\x1d#,`N"},
|
||||
{"crypto/internal/impl", "\xb0\x02"},
|
||||
{"crypto/internal/randutil", "\xea\x01\x12"},
|
||||
{"crypto/internal/sysrand", "mi!\x1f\r\x0f\x01\x01\v\x06"},
|
||||
{"crypto/internal/sysrand/internal/seccomp", "m"},
|
||||
{"crypto/md5", "\x0e2-\x16\x16`"},
|
||||
{"crypto/mlkem", "/"},
|
||||
{"crypto/pbkdf2", "2\r\x01-\x16"},
|
||||
{"crypto/rand", "\x1a\x06\a\x19\x04\x01(}\x0eM"},
|
||||
{"crypto/rc4", "#\x1d-\xc2\x01"},
|
||||
{"crypto/rsa", "\x0e\f\x01\t\x0f\f\x01\x04\x06\a\x1c\x03\x1325\r\x01"},
|
||||
{"crypto/sha1", "\x0e\f&-\x16\x16\x14L"},
|
||||
{"crypto/sha256", "\x0e\f\x1aO"},
|
||||
{"crypto/sha3", "\x0e'N\xc2\x01"},
|
||||
{"crypto/sha512", "\x0e\f\x1cM"},
|
||||
{"crypto/subtle", "8\x96\x01U"},
|
||||
{"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x03\x01\a\x01\v\x02\n\x01\b\x05\x03\x01\x01\x01\x01\x02\x01\x02\x01\x17\x02\x03\x13\x16\x14\b5\x16\x16\r\n\x01\x01\x01\x02\x01\f\x06\x02\x01"},
|
||||
{"crypto/tls/internal/fips140tls", " \x93\x02"},
|
||||
{"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x011\x03\x02\x01\x01\x02\x05\x0e\x06\x02\x02\x03E\x032\x01\x02\t\x01\x01\x01\a\x10\x05\x01\x06\x02\x05\f\x01\x02\r\x02\x01\x01\x02\x03\x01"},
|
||||
{"crypto/x509/pkix", "c\x06\a\x88\x01G"},
|
||||
{"database/sql", "\x03\nJ\x16\x03z\f\x06\"\x05\n\x02\x03\x01\f\x02\x02\x02"},
|
||||
{"database/sql/driver", "\r`\x03\xae\x01\x11\x10"},
|
||||
{"debug/buildinfo", "\x03W\x02\x01\x01\b\a\x03`\x18\x02\x01+\x0f "},
|
||||
{"debug/dwarf", "\x03c\a\x03z1\x13\x01\x01"},
|
||||
{"debug/elf", "\x03\x06P\r\a\x03`\x19\x01,\x19\x01\x15"},
|
||||
{"debug/gosym", "\x03c\n\xbe\x01\x01\x01\x02"},
|
||||
{"debug/macho", "\x03\x06P\r\n`\x1a,\x19\x01"},
|
||||
{"debug/pe", "\x03\x06P\r\a\x03`\x1a,\x19\x01\x15"},
|
||||
{"debug/plan9obj", "f\a\x03`\x1a,"},
|
||||
{"embed", "m+:\x18\x01T"},
|
||||
{"embed/internal/embedtest", ""},
|
||||
{"encoding", ""},
|
||||
{"encoding/ascii85", "\xea\x01E"},
|
||||
{"encoding/asn1", "\x03j\x03\x87\x01\x01&\x0f\x02\x01\x0f\x03\x01"},
|
||||
{"encoding/base32", "\xea\x01C\x02"},
|
||||
{"encoding/base64", "\x99\x01QC\x02"},
|
||||
{"encoding/binary", "m}\r'\x0f\x05"},
|
||||
{"encoding/csv", "\x02\x01j\x03zF\x11\x02"},
|
||||
{"encoding/gob", "\x02_\x05\a\x03`\x1a\f\x01\x02\x1d\b\x14\x01\x0e\x02"},
|
||||
{"encoding/hex", "m\x03zC\x03"},
|
||||
{"encoding/json", "\x03\x01]\x04\b\x03z\r'\x0f\x02\x01\x02\x0f\x01\x01\x02"},
|
||||
{"encoding/pem", "\x03b\b}C\x03"},
|
||||
{"encoding/xml", "\x02\x01^\f\x03z4\x05\f\x01\x02\x0f\x02"},
|
||||
{"errors", "\xc9\x01|"},
|
||||
{"expvar", "jK9\t\n\x15\r\n\x02\x03\x01\x10"},
|
||||
{"flag", "a\f\x03z,\b\x05\n\x02\x01\x0f"},
|
||||
{"fmt", "mE8\r\x1f\b\x0f\x02\x03\x11"},
|
||||
{"go/ast", "\x03\x01l\x0f\x01j\x03)\b\x0f\x02\x01"},
|
||||
{"go/ast/internal/tests", ""},
|
||||
{"go/build", "\x02\x01j\x03\x01\x03\x02\a\x02\x01\x17\x1e\x04\x02\t\x14\x12\x01+\x01\x04\x01\a\n\x02\x01\x11\x02\x02"},
|
||||
{"go/build/constraint", "m\xc2\x01\x01\x11\x02"},
|
||||
{"go/constant", "p\x10w\x01\x016\x01\x02\x11"},
|
||||
{"go/doc", "\x04l\x01\x06\t=-1\x12\x02\x01\x11\x02"},
|
||||
{"go/doc/comment", "\x03m\xbd\x01\x01\x01\x01\x11\x02"},
|
||||
{"go/format", "\x03m\x01\f\x01\x02jF"},
|
||||
{"go/importer", "s\a\x01\x01\x04\x01i9"},
|
||||
{"go/internal/gccgoimporter", "\x02\x01W\x13\x03\x05\v\x01g\x02,\x01\x05\x13\x01\v\b"},
|
||||
{"go/internal/gcimporter", "\x02n\x10\x01/\x05\x0e',\x17\x03\x02"},
|
||||
{"go/internal/srcimporter", "p\x01\x02\n\x03\x01i,\x01\x05\x14\x02\x13"},
|
||||
{"go/parser", "\x03j\x03\x01\x03\v\x01j\x01+\x06\x14"},
|
||||
{"go/printer", "p\x01\x03\x03\tj\r\x1f\x17\x02\x01\x02\n\x05\x02"},
|
||||
{"go/scanner", "\x03m\x10j2\x12\x01\x12\x02"},
|
||||
{"go/token", "\x04l\xbd\x01\x02\x03\x01\x0e\x02"},
|
||||
{"go/types", "\x03\x01\x06c\x03\x01\x04\b\x03\x02\x15\x1e\x06+\x04\x03\n%\a\n\x01\x01\x01\x02\x01\x0e\x02\x02"},
|
||||
{"go/version", "\xba\x01v"},
|
||||
{"hash", "\xea\x01"},
|
||||
{"hash/adler32", "m\x16\x16"},
|
||||
{"hash/crc32", "m\x16\x16\x14\x85\x01\x01\x12"},
|
||||
{"hash/crc64", "m\x16\x16\x99\x01"},
|
||||
{"hash/fnv", "m\x16\x16`"},
|
||||
{"hash/maphash", "\x94\x01\x05\x1b\x03@N"},
|
||||
{"html", "\xb0\x02\x02\x11"},
|
||||
{"html/template", "\x03g\x06\x19,5\x01\v \x05\x01\x02\x03\x0e\x01\x02\v\x01\x03\x02"},
|
||||
{"image", "\x02k\x1f^\x0f6\x03\x01"},
|
||||
{"image/color", ""},
|
||||
{"image/color/palette", "\x8c\x01"},
|
||||
{"image/draw", "\x8b\x01\x01\x04"},
|
||||
{"image/gif", "\x02\x01\x05e\x03\x1b\x01\x01\x01\vQ"},
|
||||
{"image/internal/imageutil", "\x8b\x01"},
|
||||
{"image/jpeg", "\x02k\x1e\x01\x04Z"},
|
||||
{"image/png", "\x02\a]\n\x13\x02\x06\x01^E"},
|
||||
{"index/suffixarray", "\x03c\a}\r*\f\x01"},
|
||||
{"internal/abi", "\xb4\x01\x91\x01"},
|
||||
{"internal/asan", "\xc5\x02"},
|
||||
{"internal/bisect", "\xa3\x02\x0f\x01"},
|
||||
{"internal/buildcfg", "pG_\x06\x02\x05\f\x01"},
|
||||
{"internal/bytealg", "\xad\x01\x98\x01"},
|
||||
{"internal/byteorder", ""},
|
||||
{"internal/cfg", ""},
|
||||
{"internal/chacha8rand", "\x99\x01\x1b\x91\x01"},
|
||||
{"internal/copyright", ""},
|
||||
{"internal/coverage", ""},
|
||||
{"internal/coverage/calloc", ""},
|
||||
{"internal/coverage/cfile", "j\x06\x17\x16\x01\x02\x01\x01\x01\x01\x01\x01\x01#\x01\x1f,\x06\a\f\x01\x03\f\x06"},
|
||||
{"internal/coverage/cformat", "\x04l-\x04I\f7\x01\x02\f"},
|
||||
{"internal/coverage/cmerge", "p-Z"},
|
||||
{"internal/coverage/decodecounter", "f\n-\v\x02@,\x19\x16"},
|
||||
{"internal/coverage/decodemeta", "\x02d\n\x17\x16\v\x02@,"},
|
||||
{"internal/coverage/encodecounter", "\x02d\n-\f\x01\x02>\f \x17"},
|
||||
{"internal/coverage/encodemeta", "\x02\x01c\n\x13\x04\x16\r\x02>,/"},
|
||||
{"internal/coverage/pods", "\x04l-y\x06\x05\f\x02\x01"},
|
||||
{"internal/coverage/rtcov", "\xc5\x02"},
|
||||
{"internal/coverage/slicereader", "f\nz["},
|
||||
{"internal/coverage/slicewriter", "pz"},
|
||||
{"internal/coverage/stringtab", "p8\x04>"},
|
||||
{"internal/coverage/test", ""},
|
||||
{"internal/coverage/uleb128", ""},
|
||||
{"internal/cpu", "\xc5\x02"},
|
||||
{"internal/dag", "\x04l\xbd\x01\x03"},
|
||||
{"internal/diff", "\x03m\xbe\x01\x02"},
|
||||
{"internal/exportdata", "\x02\x01j\x03\x03]\x1a,\x01\x05\x13\x01\x02"},
|
||||
{"internal/filepathlite", "m+:\x19B"},
|
||||
{"internal/fmtsort", "\x04\x9a\x02\x0f"},
|
||||
{"internal/fuzz", "\x03\nA\x18\x04\x03\x03\x01\f\x0355\r\x02\x1d\x01\x05\x02\x05\f\x01\x02\x01\x01\v\x04\x02"},
|
||||
{"internal/goarch", ""},
|
||||
{"internal/godebug", "\x96\x01 |\x01\x12"},
|
||||
{"internal/godebugs", ""},
|
||||
{"internal/goexperiment", ""},
|
||||
{"internal/goos", ""},
|
||||
{"internal/goroot", "\x96\x02\x01\x05\x14\x02"},
|
||||
{"internal/gover", "\x04"},
|
||||
{"internal/goversion", ""},
|
||||
{"internal/itoa", ""},
|
||||
{"internal/lazyregexp", "\x96\x02\v\x0f\x02"},
|
||||
{"internal/lazytemplate", "\xea\x01,\x1a\x02\v"},
|
||||
{"internal/msan", "\xc5\x02"},
|
||||
{"internal/nettrace", ""},
|
||||
{"internal/obscuretestdata", "e\x85\x01,"},
|
||||
{"internal/oserror", "m"},
|
||||
{"internal/pkgbits", "\x03K\x18\a\x03\x05\vj\x0e\x1e\r\f\x01"},
|
||||
{"internal/platform", ""},
|
||||
{"internal/poll", "mO\x1a\x149\x0f\x01\x01\v\x06"},
|
||||
{"internal/profile", "\x03\x04f\x03z7\r\x01\x01\x0f"},
|
||||
{"internal/profilerecord", ""},
|
||||
{"internal/race", "\x94\x01\xb1\x01"},
|
||||
{"internal/reflectlite", "\x94\x01 3<\""},
|
||||
{"internal/runtime/atomic", "\xc5\x02"},
|
||||
{"internal/runtime/exithook", "\xca\x01{"},
|
||||
{"internal/runtime/maps", "\x94\x01\x01\x1f\v\t\x05\x01w"},
|
||||
{"internal/runtime/math", "\xb4\x01"},
|
||||
{"internal/runtime/sys", "\xb4\x01\x04"},
|
||||
{"internal/runtime/syscall", "\xc5\x02"},
|
||||
{"internal/saferio", "\xea\x01["},
|
||||
{"internal/singleflight", "\xb2\x02"},
|
||||
{"internal/stringslite", "\x98\x01\xad\x01"},
|
||||
{"internal/sync", "\x94\x01 \x14k\x12"},
|
||||
{"internal/synctest", "\xc5\x02"},
|
||||
{"internal/syscall/execenv", "\xb4\x02"},
|
||||
{"internal/syscall/unix", "\xa3\x02\x10\x01\x11"},
|
||||
{"internal/sysinfo", "\x02\x01\xaa\x01=,\x1a\x02"},
|
||||
{"internal/syslist", ""},
|
||||
{"internal/testenv", "\x03\n`\x02\x01*\x1a\x10'+\x01\x05\a\f\x01\x02\x02\x01\n"},
|
||||
{"internal/testlog", "\xb2\x02\x01\x12"},
|
||||
{"internal/testpty", "m\x03\xa6\x01"},
|
||||
{"internal/trace", "\x02\x01\x01\x06\\\a\x03n\x03\x03\x06\x03\n6\x01\x02\x0f\x06"},
|
||||
{"internal/trace/internal/testgen", "\x03c\nl\x03\x02\x03\x011\v\x0f"},
|
||||
{"internal/trace/internal/tracev1", "\x03\x01b\a\x03t\x06\r6\x01"},
|
||||
{"internal/trace/raw", "\x02d\nq\x03\x06E\x01\x11"},
|
||||
{"internal/trace/testtrace", "\x02\x01j\x03l\x03\x06\x057\f\x02\x01"},
|
||||
{"internal/trace/tracev2", ""},
|
||||
{"internal/trace/traceviewer", "\x02]\v\x06\x1a<\x16\a\a\x04\t\n\x15\x01\x05\a\f\x01\x02\r"},
|
||||
{"internal/trace/traceviewer/format", ""},
|
||||
{"internal/trace/version", "pq\t"},
|
||||
{"internal/txtar", "\x03m\xa6\x01\x1a"},
|
||||
{"internal/types/errors", "\xaf\x02"},
|
||||
{"internal/unsafeheader", "\xc5\x02"},
|
||||
{"internal/xcoff", "Y\r\a\x03`\x1a,\x19\x01"},
|
||||
{"internal/zstd", "f\a\x03z\x0f"},
|
||||
{"io", "m\xc5\x01"},
|
||||
{"io/fs", "m+*(1\x12\x12\x04"},
|
||||
{"io/ioutil", "\xea\x01\x01+\x17\x03"},
|
||||
{"iter", "\xc8\x01[\""},
|
||||
{"log", "pz\x05'\r\x0f\x01\f"},
|
||||
{"log/internal", ""},
|
||||
{"log/slog", "\x03\nT\t\x03\x03z\x04\x01\x02\x02\x04'\x05\n\x02\x01\x02\x01\f\x02\x02\x02"},
|
||||
{"log/slog/internal", ""},
|
||||
{"log/slog/internal/benchmarks", "\r`\x03z\x06\x03<\x10"},
|
||||
{"log/slog/internal/buffer", "\xb2\x02"},
|
||||
{"log/slog/internal/slogtest", "\xf0\x01"},
|
||||
{"log/syslog", "m\x03~\x12\x16\x1a\x02\r"},
|
||||
{"maps", "\xed\x01X"},
|
||||
{"math", "\xad\x01LL"},
|
||||
{"math/big", "\x03j\x03)\x14=\r\x02\x024\x01\x02\x13"},
|
||||
{"math/bits", "\xc5\x02"},
|
||||
{"math/cmplx", "\xf7\x01\x02"},
|
||||
{"math/rand", "\xb5\x01B;\x01\x12"},
|
||||
{"math/rand/v2", "m,\x02\\\x02L"},
|
||||
{"mime", "\x02\x01b\b\x03z\f \x17\x03\x02\x0f\x02"},
|
||||
{"mime/multipart", "\x02\x01G#\x03E5\f\x01\x06\x02\x15\x02\x06\x11\x02\x01\x15"},
|
||||
{"mime/quotedprintable", "\x02\x01mz"},
|
||||
{"net", "\x04\t`+\x1d\a\x04\x05\f\x01\x04\x14\x01%\x06\r\n\x05\x01\x01\v\x06\a"},
|
||||
{"net/http", "\x02\x01\x04\x04\x02=\b\x13\x01\a\x03E5\x01\x03\b\x01\x02\x02\x02\x01\x02\x06\x02\x01\x01\n\x01\x01\x05\x01\x02\x05\n\x01\x01\x01\x02\x01\x01\v\x02\x02\x02\b\x01\x01\x01"},
|
||||
{"net/http/cgi", "\x02P\x1b\x03z\x04\b\n\x01\x13\x01\x01\x01\x04\x01\x05\x02\n\x02\x01\x0f\x0e"},
|
||||
{"net/http/cookiejar", "\x04i\x03\x90\x01\x01\b\f\x18\x03\x02\r\x04"},
|
||||
{"net/http/fcgi", "\x02\x01\nY\a\x03z\x16\x01\x01\x14\x1a\x02\r"},
|
||||
{"net/http/httptest", "\x02\x01\nE\x02\x1b\x01z\x04\x12\x01\n\t\x02\x19\x01\x02\r\x0e"},
|
||||
{"net/http/httptrace", "\rEn@\x14\n!"},
|
||||
{"net/http/httputil", "\x02\x01\n`\x03z\x04\x0f\x03\x01\x05\x02\x01\v\x01\x1b\x02\r\x0e"},
|
||||
{"net/http/internal", "\x02\x01j\x03z"},
|
||||
{"net/http/internal/ascii", "\xb0\x02\x11"},
|
||||
{"net/http/internal/httpcommon", "\r`\x03\x96\x01\x0e\x01\x19\x01\x01\x02\x1b\x02"},
|
||||
{"net/http/internal/testcert", "\xb0\x02"},
|
||||
{"net/http/pprof", "\x02\x01\nc\x19,\x11$\x04\x13\x14\x01\r\x06\x03\x01\x02\x01\x0f"},
|
||||
{"net/internal/cgotest", ""},
|
||||
{"net/internal/socktest", "p\xc2\x01\x02"},
|
||||
{"net/mail", "\x02k\x03z\x04\x0f\x03\x14\x1c\x02\r\x04"},
|
||||
{"net/netip", "\x04i+\x01#;\x026\x15"},
|
||||
{"net/rpc", "\x02f\x05\x03\x10\n`\x04\x12\x01\x1d\x0f\x03\x02"},
|
||||
{"net/rpc/jsonrpc", "j\x03\x03z\x16\x11!"},
|
||||
{"net/smtp", "\x19.\v\x13\b\x03z\x16\x14\x1c"},
|
||||
{"net/textproto", "\x02\x01j\x03z\r\t/\x01\x02\x13"},
|
||||
{"net/url", "m\x03\x86\x01%\x12\x02\x01\x15"},
|
||||
{"os", "m+\x01\x18\x03\b\t\r\x03\x01\x04\x10\x018\n\x05\x01\x01\v\x06"},
|
||||
{"os/exec", "\x03\n`H \x01\x14\x01+\x06\a\f\x01\x04\v"},
|
||||
{"os/exec/internal/fdtest", "\xb4\x02"},
|
||||
{"os/signal", "\r\x89\x02\x17\x05\x02"},
|
||||
{"os/user", "\x02\x01j\x03z,\r\f\x01\x02"},
|
||||
{"path", "m+\xab\x01"},
|
||||
{"path/filepath", "m+\x19:+\r\n\x03\x04\x0f"},
|
||||
{"plugin", "m"},
|
||||
{"reflect", "m'\x04\x1c\b\f\x04\x02\x19\x10,\f\x03\x0f\x02\x02"},
|
||||
{"reflect/internal/example1", ""},
|
||||
{"reflect/internal/example2", ""},
|
||||
{"regexp", "\x03\xe7\x018\v\x02\x01\x02\x0f\x02"},
|
||||
{"regexp/syntax", "\xad\x02\x01\x01\x01\x11\x02"},
|
||||
{"runtime", "\x94\x01\x04\x01\x02\f\x06\a\x02\x01\x01\x0f\x03\x01\x01\x01\x01\x01\x03\x0fd"},
|
||||
{"runtime/coverage", "\x9f\x01K"},
|
||||
{"runtime/debug", "pUQ\r\n\x02\x01\x0f\x06"},
|
||||
{"runtime/internal/startlinetest", ""},
|
||||
{"runtime/internal/wasitest", ""},
|
||||
{"runtime/metrics", "\xb6\x01A,\""},
|
||||
{"runtime/pprof", "\x02\x01\x01\x03\x06Y\a\x03$3#\r\x1f\r\n\x01\x01\x01\x02\x02\b\x03\x06"},
|
||||
{"runtime/race", "\xab\x02"},
|
||||
{"runtime/race/internal/amd64v1", ""},
|
||||
{"runtime/trace", "\rcz9\x0f\x01\x12"},
|
||||
{"slices", "\x04\xe9\x01\fL"},
|
||||
{"sort", "\xc9\x0104"},
|
||||
{"strconv", "m+:%\x02J"},
|
||||
{"strings", "m'\x04:\x18\x03\f9\x0f\x02\x02"},
|
||||
{"structs", ""},
|
||||
{"sync", "\xc8\x01\vP\x10\x12"},
|
||||
{"sync/atomic", "\xc5\x02"},
|
||||
{"syscall", "m(\x03\x01\x1b\b\x03\x03\x06\aT\n\x05\x01\x12"},
|
||||
{"testing", "\x03\n`\x02\x01X\x0f\x13\r\x04\x1b\x06\x02\x05\x02\a\x01\x02\x01\x02\x01\f\x02\x02\x02"},
|
||||
{"testing/fstest", "m\x03z\x01\v%\x12\x03\b\a"},
|
||||
{"testing/internal/testdeps", "\x02\v\xa6\x01'\x10,\x03\x05\x03\b\a\x02\r"},
|
||||
{"testing/iotest", "\x03j\x03z\x04"},
|
||||
{"testing/quick", "o\x01\x87\x01\x04#\x12\x0f"},
|
||||
{"testing/slogtest", "\r`\x03\x80\x01.\x05\x12\n"},
|
||||
{"text/scanner", "\x03mz,+\x02"},
|
||||
{"text/tabwriter", "pzY"},
|
||||
{"text/template", "m\x03B8\x01\v\x1f\x01\x05\x01\x02\x05\r\x02\f\x03\x02"},
|
||||
{"text/template/parse", "\x03m\xb3\x01\f\x01\x11\x02"},
|
||||
{"time", "m+\x1d\x1d'*\x0f\x02\x11"},
|
||||
{"time/tzdata", "m\xc7\x01\x11"},
|
||||
{"unicode", ""},
|
||||
{"unicode/utf16", ""},
|
||||
{"unicode/utf8", ""},
|
||||
{"unique", "\x94\x01>\x01P\x0f\x13\x12"},
|
||||
{"unsafe", ""},
|
||||
{"vendor/golang.org/x/crypto/chacha20", "\x10V\a\x8c\x01*'"},
|
||||
{"vendor/golang.org/x/crypto/chacha20poly1305", "\x10V\a\xd9\x01\x04\x01\a"},
|
||||
{"vendor/golang.org/x/crypto/cryptobyte", "c\n\x03\x88\x01&!\n"},
|
||||
{"vendor/golang.org/x/crypto/cryptobyte/asn1", ""},
|
||||
{"vendor/golang.org/x/crypto/internal/alias", "\xc5\x02"},
|
||||
{"vendor/golang.org/x/crypto/internal/poly1305", "Q\x15\x93\x01"},
|
||||
{"vendor/golang.org/x/net/dns/dnsmessage", "m"},
|
||||
{"vendor/golang.org/x/net/http/httpguts", "\x80\x02\x14\x1c\x13\r"},
|
||||
{"vendor/golang.org/x/net/http/httpproxy", "m\x03\x90\x01\x15\x01\x1a\x13\r"},
|
||||
{"vendor/golang.org/x/net/http2/hpack", "\x03j\x03zH"},
|
||||
{"vendor/golang.org/x/net/idna", "p\x87\x019\x13\x10\x02\x01"},
|
||||
{"vendor/golang.org/x/net/nettest", "\x03c\a\x03z\x11\x05\x16\x01\f\f\x01\x02\x02\x01\n"},
|
||||
{"vendor/golang.org/x/sys/cpu", "\x96\x02\r\f\x01\x15"},
|
||||
{"vendor/golang.org/x/text/secure/bidirule", "m\xd6\x01\x11\x01"},
|
||||
{"vendor/golang.org/x/text/transform", "\x03j}Y"},
|
||||
{"vendor/golang.org/x/text/unicode/bidi", "\x03\be~@\x15"},
|
||||
{"vendor/golang.org/x/text/unicode/norm", "f\nzH\x11\x11"},
|
||||
{"weak", "\x94\x01\x8f\x01\""},
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
// Copyright 2025 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 stdlib
|
||||
|
||||
// This file provides the API for the import graph of the standard library.
|
||||
//
|
||||
// Be aware that the compiler-generated code for every package
|
||||
// implicitly depends on package "runtime" and a handful of others
|
||||
// (see runtimePkgs in GOROOT/src/cmd/internal/objabi/pkgspecial.go).
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"iter"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Imports returns the sequence of packages directly imported by the
|
||||
// named standard packages, in name order.
|
||||
// The imports of an unknown package are the empty set.
|
||||
//
|
||||
// The graph is built into the application and may differ from the
|
||||
// graph in the Go source tree being analyzed by the application.
|
||||
func Imports(pkgs ...string) iter.Seq[string] {
|
||||
return func(yield func(string) bool) {
|
||||
for _, pkg := range pkgs {
|
||||
if i, ok := find(pkg); ok {
|
||||
var depIndex uint64
|
||||
for data := []byte(deps[i].deps); len(data) > 0; {
|
||||
delta, n := binary.Uvarint(data)
|
||||
depIndex += delta
|
||||
if !yield(deps[depIndex].name) {
|
||||
return
|
||||
}
|
||||
data = data[n:]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dependencies returns the set of all dependencies of the named
|
||||
// standard packages, including the initial package,
|
||||
// in a deterministic topological order.
|
||||
// The dependencies of an unknown package are the empty set.
|
||||
//
|
||||
// The graph is built into the application and may differ from the
|
||||
// graph in the Go source tree being analyzed by the application.
|
||||
func Dependencies(pkgs ...string) iter.Seq[string] {
|
||||
return func(yield func(string) bool) {
|
||||
for _, pkg := range pkgs {
|
||||
if i, ok := find(pkg); ok {
|
||||
var seen [1 + len(deps)/8]byte // bit set of seen packages
|
||||
var visit func(i int) bool
|
||||
visit = func(i int) bool {
|
||||
bit := byte(1) << (i % 8)
|
||||
if seen[i/8]&bit == 0 {
|
||||
seen[i/8] |= bit
|
||||
var depIndex uint64
|
||||
for data := []byte(deps[i].deps); len(data) > 0; {
|
||||
delta, n := binary.Uvarint(data)
|
||||
depIndex += delta
|
||||
if !visit(int(depIndex)) {
|
||||
return false
|
||||
}
|
||||
data = data[n:]
|
||||
}
|
||||
if !yield(deps[i].name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
if !visit(i) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find returns the index of pkg in the deps table.
|
||||
func find(pkg string) (int, bool) {
|
||||
return slices.BinarySearchFunc(deps[:], pkg, func(p pkginfo, n string) int {
|
||||
return strings.Compare(p.name, n)
|
||||
})
|
||||
}
|
||||
+17676
File diff suppressed because it is too large.
Load diff
+105
@@ -0,0 +1,105 @@
|
||||
// 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. It also provides the import graph of std packages.
|
||||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Symbol struct {
|
||||
Name string
|
||||
Kind Kind
|
||||
Version Version // Go version that first included the symbol
|
||||
// Signature provides the type of a function (defined only for Kind=Func).
|
||||
// Imported types are denoted as pkg.T; pkg is not fully qualified.
|
||||
// TODO(adonovan): use an unambiguous encoding that is parseable.
|
||||
//
|
||||
// Example2:
|
||||
// func[M ~map[K]V, K comparable, V any](m M) M
|
||||
// func(fi fs.FileInfo, link string) (*Header, error)
|
||||
Signature string // if Kind == stdlib.Func
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// 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. It
|
||||
// supplements 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 (
|
||||
"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 (or an alias of one).
|
||||
func IsTypeParam(t types.Type) bool {
|
||||
_, ok := types.Unalias(t).(*types.TypeParam)
|
||||
return ok
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"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(T types.Type) ([]*types.Term, error) {
|
||||
// typeSetOf(T) == typeSetOf(Unalias(T))
|
||||
typ := types.Unalias(T)
|
||||
if named, ok := typ.(*types.Named); ok {
|
||||
typ = named.Underlying()
|
||||
}
|
||||
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, T)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Deref returns the type of the variable pointed to by t,
|
||||
// if t's core type is a pointer; otherwise it returns t.
|
||||
//
|
||||
// Do not assume that Deref(T)==T implies T is not a pointer:
|
||||
// consider "type T *T", for example.
|
||||
//
|
||||
// TODO(adonovan): ideally this would live in typesinternal, but that
|
||||
// creates an import cycle. Move there when we melt this package down.
|
||||
func Deref(t types.Type) types.Type {
|
||||
if ptr, ok := CoreType(t).(*types.Pointer); ok {
|
||||
return ptr.Elem()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// MustDeref returns the type of the variable pointed to by t.
|
||||
// It panics if t's core type is not a pointer.
|
||||
//
|
||||
// TODO(adonovan): ideally this would live in typesinternal, but that
|
||||
// creates an import cycle. Move there when we melt this package down.
|
||||
func MustDeref(t types.Type) types.Type {
|
||||
if ptr, ok := CoreType(t).(*types.Pointer); ok {
|
||||
return ptr.Elem()
|
||||
}
|
||||
panic(fmt.Sprintf("%v is not a pointer", t))
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
// 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 typeparams
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/internal/aliases"
|
||||
)
|
||||
|
||||
// Free is a memoization of the set of free type parameters within a
|
||||
// type. It makes a sequence of calls to [Free.Has] for overlapping
|
||||
// types more efficient. The zero value is ready for use.
|
||||
//
|
||||
// NOTE: Adapted from go/types/infer.go. If it is later exported, factor.
|
||||
type Free struct {
|
||||
seen map[types.Type]bool
|
||||
}
|
||||
|
||||
// Has reports whether the specified type has a free type parameter.
|
||||
func (w *Free) Has(typ types.Type) (res bool) {
|
||||
// detect cycles
|
||||
if x, ok := w.seen[typ]; ok {
|
||||
return x
|
||||
}
|
||||
if w.seen == nil {
|
||||
w.seen = make(map[types.Type]bool)
|
||||
}
|
||||
w.seen[typ] = false
|
||||
defer func() {
|
||||
w.seen[typ] = res
|
||||
}()
|
||||
|
||||
switch t := typ.(type) {
|
||||
case nil, *types.Basic: // TODO(gri) should nil be handled here?
|
||||
break
|
||||
|
||||
case *types.Alias:
|
||||
if aliases.TypeParams(t).Len() > aliases.TypeArgs(t).Len() {
|
||||
return true // This is an uninstantiated Alias.
|
||||
}
|
||||
// The expansion of an alias can have free type parameters,
|
||||
// whether or not the alias itself has type parameters:
|
||||
//
|
||||
// func _[K comparable]() {
|
||||
// type Set = map[K]bool // free(Set) = {K}
|
||||
// type MapTo[V] = map[K]V // free(Map[foo]) = {V}
|
||||
// }
|
||||
//
|
||||
// So, we must Unalias.
|
||||
return w.Has(types.Unalias(t))
|
||||
|
||||
case *types.Array:
|
||||
return w.Has(t.Elem())
|
||||
|
||||
case *types.Slice:
|
||||
return w.Has(t.Elem())
|
||||
|
||||
case *types.Struct:
|
||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||
if w.Has(t.Field(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case *types.Pointer:
|
||||
return w.Has(t.Elem())
|
||||
|
||||
case *types.Tuple:
|
||||
n := t.Len()
|
||||
for i := range n {
|
||||
if w.Has(t.At(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case *types.Signature:
|
||||
// t.tparams may not be nil if we are looking at a signature
|
||||
// of a generic function type (or an interface method) that is
|
||||
// part of the type we're testing. We don't care about these type
|
||||
// parameters.
|
||||
// Similarly, the receiver of a method may declare (rather than
|
||||
// use) type parameters, we don't care about those either.
|
||||
// Thus, we only need to look at the input and result parameters.
|
||||
return w.Has(t.Params()) || w.Has(t.Results())
|
||||
|
||||
case *types.Interface:
|
||||
for i, n := 0, t.NumMethods(); i < n; i++ {
|
||||
if w.Has(t.Method(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
terms, err := InterfaceTermSet(t)
|
||||
if err != nil {
|
||||
return false // ill typed
|
||||
}
|
||||
for _, term := range terms {
|
||||
if w.Has(term.Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case *types.Map:
|
||||
return w.Has(t.Key()) || w.Has(t.Elem())
|
||||
|
||||
case *types.Chan:
|
||||
return w.Has(t.Elem())
|
||||
|
||||
case *types.Named:
|
||||
args := t.TypeArgs()
|
||||
if params := t.TypeParams(); params.Len() > args.Len() {
|
||||
return true // this is an uninstantiated named type.
|
||||
}
|
||||
for i, n := 0, args.Len(); i < n; i++ {
|
||||
if w.Has(args.At(i)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return w.Has(t.Underlying()) // recurse for types local to parameterized functions
|
||||
|
||||
case *types.TypeParam:
|
||||
return true
|
||||
|
||||
default:
|
||||
panic(t) // unreachable
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
// 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 ...any) {
|
||||
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()
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
|
||||
// Source: ../../cmd/compile/internal/types2/termlist.go
|
||||
|
||||
// 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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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)}
|
||||
|
||||
// termSep is the separator used between individual terms.
|
||||
const termSep = " | "
|
||||
|
||||
// String prints the termlist exactly (without normalization).
|
||||
func (xl termlist) String() string {
|
||||
if len(xl) == 0 {
|
||||
return "∅"
|
||||
}
|
||||
var buf strings.Builder
|
||||
for i, x := range xl {
|
||||
if i > 0 {
|
||||
buf.WriteString(termSep)
|
||||
}
|
||||
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
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
|
||||
// Source: ../../cmd/compile/internal/types2/typeterm.go
|
||||
|
||||
// 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)
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
// Copyright 2018 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 (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
// CallKind describes the function position of an [*ast.CallExpr].
|
||||
type CallKind int
|
||||
|
||||
const (
|
||||
CallStatic CallKind = iota // static call to known function
|
||||
CallInterface // dynamic call through an interface method
|
||||
CallDynamic // dynamic call of a func value
|
||||
CallBuiltin // call to a builtin function
|
||||
CallConversion // a conversion (not a call)
|
||||
)
|
||||
|
||||
var callKindNames = []string{
|
||||
"CallStatic",
|
||||
"CallInterface",
|
||||
"CallDynamic",
|
||||
"CallBuiltin",
|
||||
"CallConversion",
|
||||
}
|
||||
|
||||
func (k CallKind) String() string {
|
||||
if i := int(k); i >= 0 && i < len(callKindNames) {
|
||||
return callKindNames[i]
|
||||
}
|
||||
return fmt.Sprintf("typeutil.CallKind(%d)", k)
|
||||
}
|
||||
|
||||
// ClassifyCall classifies the function position of a call expression ([*ast.CallExpr]).
|
||||
// It distinguishes among true function calls, calls to builtins, and type conversions,
|
||||
// and further classifies function calls as static calls (where the function is known),
|
||||
// dynamic interface calls, and other dynamic calls.
|
||||
//
|
||||
// For the declarations:
|
||||
//
|
||||
// func f() {}
|
||||
// func g[T any]() {}
|
||||
// var v func()
|
||||
// var s []func()
|
||||
// type I interface { M() }
|
||||
// var i I
|
||||
//
|
||||
// ClassifyCall returns the following:
|
||||
//
|
||||
// f() CallStatic
|
||||
// g[int]() CallStatic
|
||||
// i.M() CallInterface
|
||||
// min(1, 2) CallBuiltin
|
||||
// v() CallDynamic
|
||||
// s[0]() CallDynamic
|
||||
// int(x) CallConversion
|
||||
// []byte("") CallConversion
|
||||
func ClassifyCall(info *types.Info, call *ast.CallExpr) CallKind {
|
||||
if info.Types == nil {
|
||||
panic("ClassifyCall: info.Types is nil")
|
||||
}
|
||||
tv := info.Types[call.Fun]
|
||||
if tv.IsType() {
|
||||
return CallConversion
|
||||
}
|
||||
if tv.IsBuiltin() {
|
||||
return CallBuiltin
|
||||
}
|
||||
obj := info.Uses[UsedIdent(info, call.Fun)]
|
||||
// Classify the call by the type of the object, if any.
|
||||
switch obj := obj.(type) {
|
||||
case *types.Func:
|
||||
if interfaceMethod(obj) {
|
||||
return CallInterface
|
||||
}
|
||||
return CallStatic
|
||||
default:
|
||||
return CallDynamic
|
||||
}
|
||||
}
|
||||
|
||||
// UsedIdent returns the identifier such that info.Uses[UsedIdent(info, e)]
|
||||
// is the [types.Object] used by e, if any.
|
||||
//
|
||||
// If e is one of various forms of reference:
|
||||
//
|
||||
// f, c, v, T lexical reference
|
||||
// pkg.X qualified identifier
|
||||
// f[T] or pkg.F[K,V] instantiations of the above kinds
|
||||
// expr.f field or method value selector
|
||||
// T.f method expression selector
|
||||
//
|
||||
// UsedIdent returns the identifier whose is associated value in [types.Info.Uses]
|
||||
// is the object to which it refers.
|
||||
//
|
||||
// For the declarations:
|
||||
//
|
||||
// func F[T any] {...}
|
||||
// type I interface { M() }
|
||||
// var (
|
||||
// x int
|
||||
// s struct { f int }
|
||||
// a []int
|
||||
// i I
|
||||
// )
|
||||
//
|
||||
// UsedIdent returns the following:
|
||||
//
|
||||
// Expr UsedIdent
|
||||
// x x
|
||||
// s.f f
|
||||
// F[int] F
|
||||
// i.M M
|
||||
// I.M M
|
||||
// min min
|
||||
// int int
|
||||
// 1 nil
|
||||
// a[0] nil
|
||||
// []byte nil
|
||||
//
|
||||
// Note: if e is an instantiated function or method, UsedIdent returns
|
||||
// the corresponding generic function or method on the generic type.
|
||||
func UsedIdent(info *types.Info, e ast.Expr) *ast.Ident {
|
||||
return usedIdent(info, e)
|
||||
}
|
||||
|
||||
//go:linkname usedIdent golang.org/x/tools/go/types/typeutil.usedIdent
|
||||
func usedIdent(info *types.Info, e ast.Expr) *ast.Ident
|
||||
|
||||
//go:linkname interfaceMethod golang.org/x/tools/go/types/typeutil.interfaceMethod
|
||||
func interfaceMethod(f *types.Func) bool
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
// ForEachElement calls f for type T and each type reachable from its
|
||||
// type through reflection. It does this by recursively stripping off
|
||||
// type constructors; in addition, for each named type N, the type *N
|
||||
// is added to the result as it may have additional methods.
|
||||
//
|
||||
// The caller must provide an initially empty set used to de-duplicate
|
||||
// identical types, potentially across multiple calls to ForEachElement.
|
||||
// (Its final value holds all the elements seen, matching the arguments
|
||||
// passed to f.)
|
||||
//
|
||||
// TODO(adonovan): share/harmonize with go/callgraph/rta.
|
||||
func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T types.Type, f func(types.Type)) {
|
||||
var visit func(T types.Type, skip bool)
|
||||
visit = func(T types.Type, skip bool) {
|
||||
if !skip {
|
||||
if seen, _ := rtypes.Set(T, true).(bool); seen {
|
||||
return // de-dup
|
||||
}
|
||||
|
||||
f(T) // notify caller of new element type
|
||||
}
|
||||
|
||||
// Recursion over signatures of each method.
|
||||
tmset := msets.MethodSet(T)
|
||||
for i := 0; i < tmset.Len(); i++ {
|
||||
sig := tmset.At(i).Type().(*types.Signature)
|
||||
// It is tempting to call visit(sig, false)
|
||||
// but, as noted in golang.org/cl/65450043,
|
||||
// the Signature.Recv field is ignored by
|
||||
// types.Identical and typeutil.Map, which
|
||||
// is confusing at best.
|
||||
//
|
||||
// More importantly, the true signature rtype
|
||||
// reachable from a method using reflection
|
||||
// has no receiver but an extra ordinary parameter.
|
||||
// For the Read method of io.Reader we want:
|
||||
// func(Reader, []byte) (int, error)
|
||||
// but here sig is:
|
||||
// func([]byte) (int, error)
|
||||
// with .Recv = Reader (though it is hard to
|
||||
// notice because it doesn't affect Signature.String
|
||||
// or types.Identical).
|
||||
//
|
||||
// TODO(adonovan): construct and visit the correct
|
||||
// non-method signature with an extra parameter
|
||||
// (though since unnamed func types have no methods
|
||||
// there is essentially no actual demand for this).
|
||||
//
|
||||
// TODO(adonovan): document whether or not it is
|
||||
// safe to skip non-exported methods (as RTA does).
|
||||
visit(sig.Params(), true) // skip the Tuple
|
||||
visit(sig.Results(), true) // skip the Tuple
|
||||
}
|
||||
|
||||
switch T := T.(type) {
|
||||
case *types.Alias:
|
||||
visit(types.Unalias(T), skip) // emulates the pre-Alias behavior
|
||||
|
||||
case *types.Basic:
|
||||
// nop
|
||||
|
||||
case *types.Interface:
|
||||
// nop---handled by recursion over method set.
|
||||
|
||||
case *types.Pointer:
|
||||
visit(T.Elem(), false)
|
||||
|
||||
case *types.Slice:
|
||||
visit(T.Elem(), false)
|
||||
|
||||
case *types.Chan:
|
||||
visit(T.Elem(), false)
|
||||
|
||||
case *types.Map:
|
||||
visit(T.Key(), false)
|
||||
visit(T.Elem(), false)
|
||||
|
||||
case *types.Signature:
|
||||
if T.Recv() != nil {
|
||||
panic(fmt.Sprintf("Signature %s has Recv %s", T, T.Recv()))
|
||||
}
|
||||
visit(T.Params(), true) // skip the Tuple
|
||||
visit(T.Results(), true) // skip the Tuple
|
||||
|
||||
case *types.Named:
|
||||
// A pointer-to-named type can be derived from a named
|
||||
// type via reflection. It may have methods too.
|
||||
visit(types.NewPointer(T), false)
|
||||
|
||||
// Consider 'type T struct{S}' where S has methods.
|
||||
// Reflection provides no way to get from T to struct{S},
|
||||
// only to S, so the method set of struct{S} is unwanted,
|
||||
// so set 'skip' flag during recursion.
|
||||
visit(T.Underlying(), true) // skip the unnamed type
|
||||
|
||||
case *types.Array:
|
||||
visit(T.Elem(), false)
|
||||
|
||||
case *types.Struct:
|
||||
for i, n := 0, T.NumFields(); i < n; i++ {
|
||||
// TODO(adonovan): document whether or not
|
||||
// it is safe to skip non-exported fields.
|
||||
visit(T.Field(i).Type(), false)
|
||||
}
|
||||
|
||||
case *types.Tuple:
|
||||
for i, n := 0, T.Len(); i < n; i++ {
|
||||
visit(T.At(i).Type(), false)
|
||||
}
|
||||
|
||||
case *types.TypeParam, *types.Union:
|
||||
// forEachReachable must not be called on parameterized types.
|
||||
panic(T)
|
||||
|
||||
default:
|
||||
panic(T)
|
||||
}
|
||||
}
|
||||
visit(T, false)
|
||||
}
|
||||
+1560
File diff suppressed because it is too large.
Load diff
+179
@@ -0,0 +1,179 @@
|
||||
// Code generated by "stringer -type=ErrorCode"; DO NOT EDIT.
|
||||
|
||||
package typesinternal
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[InvalidSyntaxTree - -1]
|
||||
_ = x[Test-1]
|
||||
_ = x[BlankPkgName-2]
|
||||
_ = x[MismatchedPkgName-3]
|
||||
_ = x[InvalidPkgUse-4]
|
||||
_ = x[BadImportPath-5]
|
||||
_ = x[BrokenImport-6]
|
||||
_ = x[ImportCRenamed-7]
|
||||
_ = x[UnusedImport-8]
|
||||
_ = x[InvalidInitCycle-9]
|
||||
_ = x[DuplicateDecl-10]
|
||||
_ = x[InvalidDeclCycle-11]
|
||||
_ = x[InvalidTypeCycle-12]
|
||||
_ = x[InvalidConstInit-13]
|
||||
_ = x[InvalidConstVal-14]
|
||||
_ = x[InvalidConstType-15]
|
||||
_ = x[UntypedNilUse-16]
|
||||
_ = x[WrongAssignCount-17]
|
||||
_ = x[UnassignableOperand-18]
|
||||
_ = x[NoNewVar-19]
|
||||
_ = x[MultiValAssignOp-20]
|
||||
_ = x[InvalidIfaceAssign-21]
|
||||
_ = x[InvalidChanAssign-22]
|
||||
_ = x[IncompatibleAssign-23]
|
||||
_ = x[UnaddressableFieldAssign-24]
|
||||
_ = x[NotAType-25]
|
||||
_ = x[InvalidArrayLen-26]
|
||||
_ = x[BlankIfaceMethod-27]
|
||||
_ = x[IncomparableMapKey-28]
|
||||
_ = x[InvalidIfaceEmbed-29]
|
||||
_ = x[InvalidPtrEmbed-30]
|
||||
_ = x[BadRecv-31]
|
||||
_ = x[InvalidRecv-32]
|
||||
_ = x[DuplicateFieldAndMethod-33]
|
||||
_ = x[DuplicateMethod-34]
|
||||
_ = x[InvalidBlank-35]
|
||||
_ = x[InvalidIota-36]
|
||||
_ = x[MissingInitBody-37]
|
||||
_ = x[InvalidInitSig-38]
|
||||
_ = x[InvalidInitDecl-39]
|
||||
_ = x[InvalidMainDecl-40]
|
||||
_ = x[TooManyValues-41]
|
||||
_ = x[NotAnExpr-42]
|
||||
_ = x[TruncatedFloat-43]
|
||||
_ = x[NumericOverflow-44]
|
||||
_ = x[UndefinedOp-45]
|
||||
_ = x[MismatchedTypes-46]
|
||||
_ = x[DivByZero-47]
|
||||
_ = x[NonNumericIncDec-48]
|
||||
_ = x[UnaddressableOperand-49]
|
||||
_ = x[InvalidIndirection-50]
|
||||
_ = x[NonIndexableOperand-51]
|
||||
_ = x[InvalidIndex-52]
|
||||
_ = x[SwappedSliceIndices-53]
|
||||
_ = x[NonSliceableOperand-54]
|
||||
_ = x[InvalidSliceExpr-55]
|
||||
_ = x[InvalidShiftCount-56]
|
||||
_ = x[InvalidShiftOperand-57]
|
||||
_ = x[InvalidReceive-58]
|
||||
_ = x[InvalidSend-59]
|
||||
_ = x[DuplicateLitKey-60]
|
||||
_ = x[MissingLitKey-61]
|
||||
_ = x[InvalidLitIndex-62]
|
||||
_ = x[OversizeArrayLit-63]
|
||||
_ = x[MixedStructLit-64]
|
||||
_ = x[InvalidStructLit-65]
|
||||
_ = x[MissingLitField-66]
|
||||
_ = x[DuplicateLitField-67]
|
||||
_ = x[UnexportedLitField-68]
|
||||
_ = x[InvalidLitField-69]
|
||||
_ = x[UntypedLit-70]
|
||||
_ = x[InvalidLit-71]
|
||||
_ = x[AmbiguousSelector-72]
|
||||
_ = x[UndeclaredImportedName-73]
|
||||
_ = x[UnexportedName-74]
|
||||
_ = x[UndeclaredName-75]
|
||||
_ = x[MissingFieldOrMethod-76]
|
||||
_ = x[BadDotDotDotSyntax-77]
|
||||
_ = x[NonVariadicDotDotDot-78]
|
||||
_ = x[MisplacedDotDotDot-79]
|
||||
_ = x[InvalidDotDotDotOperand-80]
|
||||
_ = x[InvalidDotDotDot-81]
|
||||
_ = x[UncalledBuiltin-82]
|
||||
_ = x[InvalidAppend-83]
|
||||
_ = x[InvalidCap-84]
|
||||
_ = x[InvalidClose-85]
|
||||
_ = x[InvalidCopy-86]
|
||||
_ = x[InvalidComplex-87]
|
||||
_ = x[InvalidDelete-88]
|
||||
_ = x[InvalidImag-89]
|
||||
_ = x[InvalidLen-90]
|
||||
_ = x[SwappedMakeArgs-91]
|
||||
_ = x[InvalidMake-92]
|
||||
_ = x[InvalidReal-93]
|
||||
_ = x[InvalidAssert-94]
|
||||
_ = x[ImpossibleAssert-95]
|
||||
_ = x[InvalidConversion-96]
|
||||
_ = x[InvalidUntypedConversion-97]
|
||||
_ = x[BadOffsetofSyntax-98]
|
||||
_ = x[InvalidOffsetof-99]
|
||||
_ = x[UnusedExpr-100]
|
||||
_ = x[UnusedVar-101]
|
||||
_ = x[MissingReturn-102]
|
||||
_ = x[WrongResultCount-103]
|
||||
_ = x[OutOfScopeResult-104]
|
||||
_ = x[InvalidCond-105]
|
||||
_ = x[InvalidPostDecl-106]
|
||||
_ = x[InvalidChanRange-107]
|
||||
_ = x[InvalidIterVar-108]
|
||||
_ = x[InvalidRangeExpr-109]
|
||||
_ = x[MisplacedBreak-110]
|
||||
_ = x[MisplacedContinue-111]
|
||||
_ = x[MisplacedFallthrough-112]
|
||||
_ = x[DuplicateCase-113]
|
||||
_ = x[DuplicateDefault-114]
|
||||
_ = x[BadTypeKeyword-115]
|
||||
_ = x[InvalidTypeSwitch-116]
|
||||
_ = x[InvalidExprSwitch-117]
|
||||
_ = x[InvalidSelectCase-118]
|
||||
_ = x[UndeclaredLabel-119]
|
||||
_ = x[DuplicateLabel-120]
|
||||
_ = x[MisplacedLabel-121]
|
||||
_ = x[UnusedLabel-122]
|
||||
_ = x[JumpOverDecl-123]
|
||||
_ = x[JumpIntoBlock-124]
|
||||
_ = x[InvalidMethodExpr-125]
|
||||
_ = x[WrongArgCount-126]
|
||||
_ = x[InvalidCall-127]
|
||||
_ = x[UnusedResults-128]
|
||||
_ = x[InvalidDefer-129]
|
||||
_ = x[InvalidGo-130]
|
||||
_ = x[BadDecl-131]
|
||||
_ = x[RepeatedDecl-132]
|
||||
_ = x[InvalidUnsafeAdd-133]
|
||||
_ = x[InvalidUnsafeSlice-134]
|
||||
_ = x[UnsupportedFeature-135]
|
||||
_ = x[NotAGenericType-136]
|
||||
_ = x[WrongTypeArgCount-137]
|
||||
_ = x[CannotInferTypeArgs-138]
|
||||
_ = x[InvalidTypeArg-139]
|
||||
_ = x[InvalidInstanceCycle-140]
|
||||
_ = x[InvalidUnion-141]
|
||||
_ = x[MisplacedConstraintIface-142]
|
||||
_ = x[InvalidMethodTypeParams-143]
|
||||
_ = x[MisplacedTypeParam-144]
|
||||
_ = x[InvalidUnsafeSliceData-145]
|
||||
_ = x[InvalidUnsafeString-146]
|
||||
}
|
||||
|
||||
const (
|
||||
_ErrorCode_name_0 = "InvalidSyntaxTree"
|
||||
_ErrorCode_name_1 = "TestBlankPkgNameMismatchedPkgNameInvalidPkgUseBadImportPathBrokenImportImportCRenamedUnusedImportInvalidInitCycleDuplicateDeclInvalidDeclCycleInvalidTypeCycleInvalidConstInitInvalidConstValInvalidConstTypeUntypedNilUseWrongAssignCountUnassignableOperandNoNewVarMultiValAssignOpInvalidIfaceAssignInvalidChanAssignIncompatibleAssignUnaddressableFieldAssignNotATypeInvalidArrayLenBlankIfaceMethodIncomparableMapKeyInvalidIfaceEmbedInvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDotInvalidDotDotDotOperandInvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDeclInvalidChanRangeInvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString"
|
||||
)
|
||||
|
||||
var (
|
||||
_ErrorCode_index_1 = [...]uint16{0, 4, 16, 33, 46, 59, 71, 85, 97, 113, 126, 142, 158, 174, 189, 205, 218, 234, 253, 261, 277, 295, 312, 330, 354, 362, 377, 393, 411, 428, 443, 450, 461, 484, 499, 511, 522, 537, 551, 566, 581, 594, 603, 617, 632, 643, 658, 667, 683, 703, 721, 740, 752, 771, 790, 806, 823, 842, 856, 867, 882, 895, 910, 926, 940, 956, 971, 988, 1006, 1021, 1031, 1041, 1058, 1080, 1094, 1108, 1128, 1146, 1166, 1184, 1207, 1223, 1238, 1251, 1261, 1273, 1284, 1298, 1311, 1322, 1332, 1347, 1358, 1369, 1382, 1398, 1415, 1439, 1456, 1471, 1481, 1490, 1503, 1519, 1535, 1546, 1561, 1577, 1591, 1607, 1621, 1638, 1658, 1671, 1687, 1701, 1718, 1735, 1752, 1767, 1781, 1795, 1806, 1818, 1831, 1848, 1861, 1872, 1885, 1897, 1906, 1913, 1925, 1941, 1959, 1977, 1992, 2009, 2028, 2042, 2062, 2074, 2098, 2121, 2139, 2161, 2180}
|
||||
)
|
||||
|
||||
func (i ErrorCode) String() string {
|
||||
switch {
|
||||
case i == -1:
|
||||
return _ErrorCode_name_0
|
||||
case 1 <= i && i <= 146:
|
||||
i -= 1
|
||||
return _ErrorCode_name_1[_ErrorCode_index_1[i]:_ErrorCode_index_1[i+1]]
|
||||
default:
|
||||
return "ErrorCode(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
// 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/ast"
|
||||
"go/types"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// FileQualifier returns a [types.Qualifier] function that qualifies
|
||||
// imported symbols appropriately based on the import environment of a given
|
||||
// file.
|
||||
// If the same package is imported multiple times, the last appearance is
|
||||
// recorded.
|
||||
func FileQualifier(f *ast.File, pkg *types.Package) types.Qualifier {
|
||||
// Construct mapping of import paths to their defined names.
|
||||
// It is only necessary to look at renaming imports.
|
||||
imports := make(map[string]string)
|
||||
for _, imp := range f.Imports {
|
||||
if imp.Name != nil && imp.Name.Name != "_" {
|
||||
path, _ := strconv.Unquote(imp.Path.Value)
|
||||
imports[path] = imp.Name.Name
|
||||
}
|
||||
}
|
||||
|
||||
// Define qualifier to replace full package paths with names of the imports.
|
||||
return func(p *types.Package) string {
|
||||
if p == nil || p == pkg {
|
||||
return ""
|
||||
}
|
||||
|
||||
if name, ok := imports[p.Path()]; ok {
|
||||
if name == "." {
|
||||
return ""
|
||||
} else {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no local renaming, fall back to the package name.
|
||||
return p.Name()
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
// The named result may be nil if recv is from a method on an
|
||||
// anonymous interface or struct types or in ill-typed code.
|
||||
func ReceiverNamed(recv *types.Var) (isPtr bool, named *types.Named) {
|
||||
t := recv.Type()
|
||||
if ptr, ok := types.Unalias(t).(*types.Pointer); ok {
|
||||
isPtr = true
|
||||
t = ptr.Elem()
|
||||
}
|
||||
named, _ = types.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 := types.Unalias(t).(*types.Pointer); ok {
|
||||
return ptr.Elem()
|
||||
}
|
||||
return t
|
||||
}
|
||||
+89
@@ -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
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
// Copyright 2020 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 provides access to internal go/types APIs that are not
|
||||
// yet exported.
|
||||
package typesinternal
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/tools/internal/aliases"
|
||||
)
|
||||
|
||||
func SetUsesCgo(conf *types.Config) bool {
|
||||
v := reflect.ValueOf(conf).Elem()
|
||||
|
||||
f := v.FieldByName("go115UsesCgo")
|
||||
if !f.IsValid() {
|
||||
f = v.FieldByName("UsesCgo")
|
||||
if !f.IsValid() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
addr := unsafe.Pointer(f.UnsafeAddr())
|
||||
*(*bool)(addr) = true
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ErrorCodeStartEnd extracts additional information from types.Error values
|
||||
// generated by Go version 1.16 and later: the error code, start position, and
|
||||
// end position. If all positions are valid, start <= err.Pos <= end.
|
||||
//
|
||||
// If the data could not be read, the final result parameter will be false.
|
||||
//
|
||||
// TODO(adonovan): eliminate start/end when proposal #71803 is accepted.
|
||||
func ErrorCodeStartEnd(err types.Error) (code ErrorCode, start, end token.Pos, ok bool) {
|
||||
var data [3]int
|
||||
// By coincidence all of these fields are ints, which simplifies things.
|
||||
v := reflect.ValueOf(err)
|
||||
for i, name := range []string{"go116code", "go116start", "go116end"} {
|
||||
f := v.FieldByName(name)
|
||||
if !f.IsValid() {
|
||||
return 0, 0, 0, false
|
||||
}
|
||||
data[i] = int(f.Int())
|
||||
}
|
||||
return ErrorCode(data[0]), token.Pos(data[1]), token.Pos(data[2]), true
|
||||
}
|
||||
|
||||
// NameRelativeTo returns a types.Qualifier that qualifies members of
|
||||
// all packages other than pkg, using only the package name.
|
||||
// (By contrast, [types.RelativeTo] uses the complete package path,
|
||||
// which is often excessive.)
|
||||
//
|
||||
// If pkg is nil, it is equivalent to [*types.Package.Name].
|
||||
func NameRelativeTo(pkg *types.Package) types.Qualifier {
|
||||
return func(other *types.Package) string {
|
||||
if pkg != nil && pkg == other {
|
||||
return "" // same package; unqualified
|
||||
}
|
||||
return other.Name()
|
||||
}
|
||||
}
|
||||
|
||||
// TypeNameFor returns the type name symbol for the specified type, if
|
||||
// it is a [*types.Alias], [*types.Named], [*types.TypeParam], or a
|
||||
// [*types.Basic] representing a type.
|
||||
//
|
||||
// For all other types, and for Basic types representing a builtin,
|
||||
// constant, or nil, it returns nil. Be careful not to convert the
|
||||
// resulting nil pointer to a [types.Object]!
|
||||
//
|
||||
// If t is the type of a constant, it may be an "untyped" type, which
|
||||
// has no TypeName. To access the name of such types (e.g. "untyped
|
||||
// int"), use [types.Basic.Name].
|
||||
func TypeNameFor(t types.Type) *types.TypeName {
|
||||
switch t := t.(type) {
|
||||
case *types.Alias:
|
||||
return t.Obj()
|
||||
case *types.Named:
|
||||
return t.Obj()
|
||||
case *types.TypeParam:
|
||||
return t.Obj()
|
||||
case *types.Basic:
|
||||
// See issues #71886 and #66890 for some history.
|
||||
if tname, ok := types.Universe.Lookup(t.Name()).(*types.TypeName); ok {
|
||||
return tname
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A NamedOrAlias is a [types.Type] that is named (as
|
||||
// defined by the spec) and capable of bearing type parameters: it
|
||||
// abstracts aliases ([types.Alias]) and defined types
|
||||
// ([types.Named]).
|
||||
//
|
||||
// Every type declared by an explicit "type" declaration is a
|
||||
// NamedOrAlias. (Built-in type symbols may additionally
|
||||
// have type [types.Basic], which is not a NamedOrAlias,
|
||||
// though the spec regards them as "named"; see [TypeNameFor].)
|
||||
//
|
||||
// NamedOrAlias cannot expose the Origin method, because
|
||||
// [types.Alias.Origin] and [types.Named.Origin] have different
|
||||
// (covariant) result types; use [Origin] instead.
|
||||
type NamedOrAlias interface {
|
||||
types.Type
|
||||
Obj() *types.TypeName
|
||||
TypeArgs() *types.TypeList
|
||||
TypeParams() *types.TypeParamList
|
||||
SetTypeParams(tparams []*types.TypeParam)
|
||||
}
|
||||
|
||||
var (
|
||||
_ NamedOrAlias = (*types.Alias)(nil)
|
||||
_ NamedOrAlias = (*types.Named)(nil)
|
||||
)
|
||||
|
||||
// Origin returns the generic type of the Named or Alias type t if it
|
||||
// is instantiated, otherwise it returns t.
|
||||
func Origin(t NamedOrAlias) NamedOrAlias {
|
||||
switch t := t.(type) {
|
||||
case *types.Alias:
|
||||
return aliases.Origin(t)
|
||||
case *types.Named:
|
||||
return t.Origin()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// IsPackageLevel reports whether obj is a package-level symbol.
|
||||
func IsPackageLevel(obj types.Object) bool {
|
||||
return obj.Pkg() != nil && obj.Parent() == obj.Pkg().Scope()
|
||||
}
|
||||
|
||||
// NewTypesInfo returns a *types.Info with all maps populated.
|
||||
func NewTypesInfo() *types.Info {
|
||||
return &types.Info{
|
||||
Types: map[ast.Expr]types.TypeAndValue{},
|
||||
Instances: map[*ast.Ident]types.Instance{},
|
||||
Defs: map[*ast.Ident]types.Object{},
|
||||
Uses: map[*ast.Ident]types.Object{},
|
||||
Implicits: map[ast.Node]types.Object{},
|
||||
Selections: map[*ast.SelectorExpr]*types.Selection{},
|
||||
Scopes: map[ast.Node]*types.Scope{},
|
||||
FileVersions: map[*ast.File]string{},
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// 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
|
||||
|
||||
// TODO(adonovan): when CL 645115 lands, define the go1.25 version of
|
||||
// this API that actually does something.
|
||||
|
||||
import "go/types"
|
||||
|
||||
type VarKind uint8
|
||||
|
||||
const (
|
||||
_ VarKind = iota // (not meaningful)
|
||||
PackageVar // a package-level variable
|
||||
LocalVar // a local variable
|
||||
RecvVar // a method receiver variable
|
||||
ParamVar // a function parameter variable
|
||||
ResultVar // a function result variable
|
||||
FieldVar // a struct field
|
||||
)
|
||||
|
||||
func (kind VarKind) String() string {
|
||||
return [...]string{
|
||||
0: "VarKind(0)",
|
||||
PackageVar: "PackageVar",
|
||||
LocalVar: "LocalVar",
|
||||
RecvVar: "RecvVar",
|
||||
ParamVar: "ParamVar",
|
||||
ResultVar: "ResultVar",
|
||||
FieldVar: "FieldVar",
|
||||
}[kind]
|
||||
}
|
||||
|
||||
// GetVarKind returns an invalid VarKind.
|
||||
func GetVarKind(v *types.Var) VarKind { return 0 }
|
||||
|
||||
// SetVarKind has no effect.
|
||||
func SetVarKind(v *types.Var, kind VarKind) {}
|
||||
+392
@@ -0,0 +1,392 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ZeroString returns the string representation of the zero value for any type t.
|
||||
// The boolean result indicates whether the type is or contains an invalid type
|
||||
// or a non-basic (constraint) interface type.
|
||||
//
|
||||
// Even for invalid input types, ZeroString may return a partially correct
|
||||
// string representation. The caller should use the returned isValid boolean
|
||||
// to determine the validity of the expression.
|
||||
//
|
||||
// When assigning to a wider type (such as 'any'), it's the caller's
|
||||
// responsibility to handle any necessary type conversions.
|
||||
//
|
||||
// This string can be used on the right-hand side of an assignment where the
|
||||
// left-hand side has that explicit type.
|
||||
// References to named types are qualified by an appropriate (optional)
|
||||
// qualifier function.
|
||||
// Exception: This does not apply to tuples. Their string representation is
|
||||
// informational only and cannot be used in an assignment.
|
||||
//
|
||||
// See [ZeroExpr] for a variant that returns an [ast.Expr].
|
||||
func ZeroString(t types.Type, qual types.Qualifier) (_ string, isValid bool) {
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
switch {
|
||||
case t.Info()&types.IsBoolean != 0:
|
||||
return "false", true
|
||||
case t.Info()&types.IsNumeric != 0:
|
||||
return "0", true
|
||||
case t.Info()&types.IsString != 0:
|
||||
return `""`, true
|
||||
case t.Kind() == types.UnsafePointer:
|
||||
fallthrough
|
||||
case t.Kind() == types.UntypedNil:
|
||||
return "nil", true
|
||||
case t.Kind() == types.Invalid:
|
||||
return "invalid", false
|
||||
default:
|
||||
panic(fmt.Sprintf("ZeroString for unexpected type %v", t))
|
||||
}
|
||||
|
||||
case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature:
|
||||
return "nil", true
|
||||
|
||||
case *types.Interface:
|
||||
if !t.IsMethodSet() {
|
||||
return "invalid", false
|
||||
}
|
||||
return "nil", true
|
||||
|
||||
case *types.Named:
|
||||
switch under := t.Underlying().(type) {
|
||||
case *types.Struct, *types.Array:
|
||||
return types.TypeString(t, qual) + "{}", true
|
||||
default:
|
||||
return ZeroString(under, qual)
|
||||
}
|
||||
|
||||
case *types.Alias:
|
||||
switch t.Underlying().(type) {
|
||||
case *types.Struct, *types.Array:
|
||||
return types.TypeString(t, qual) + "{}", true
|
||||
default:
|
||||
// A type parameter can have alias but alias type's underlying type
|
||||
// can never be a type parameter.
|
||||
// Use types.Unalias to preserve the info of type parameter instead
|
||||
// of call Underlying() going right through and get the underlying
|
||||
// type of the type parameter which is always an interface.
|
||||
return ZeroString(types.Unalias(t), qual)
|
||||
}
|
||||
|
||||
case *types.Array, *types.Struct:
|
||||
return types.TypeString(t, qual) + "{}", true
|
||||
|
||||
case *types.TypeParam:
|
||||
// Assumes func new is not shadowed.
|
||||
return "*new(" + types.TypeString(t, qual) + ")", true
|
||||
|
||||
case *types.Tuple:
|
||||
// Tuples are not normal values.
|
||||
// We are currently format as "(t[0], ..., t[n])". Could be something else.
|
||||
isValid := true
|
||||
components := make([]string, t.Len())
|
||||
for i := 0; i < t.Len(); i++ {
|
||||
comp, ok := ZeroString(t.At(i).Type(), qual)
|
||||
|
||||
components[i] = comp
|
||||
isValid = isValid && ok
|
||||
}
|
||||
return "(" + strings.Join(components, ", ") + ")", isValid
|
||||
|
||||
case *types.Union:
|
||||
// Variables of these types cannot be created, so it makes
|
||||
// no sense to ask for their zero value.
|
||||
panic(fmt.Sprintf("invalid type for a variable: %v", t))
|
||||
|
||||
default:
|
||||
panic(t) // unreachable.
|
||||
}
|
||||
}
|
||||
|
||||
// ZeroExpr returns the ast.Expr representation of the zero value for any type t.
|
||||
// The boolean result indicates whether the type is or contains an invalid type
|
||||
// or a non-basic (constraint) interface type.
|
||||
//
|
||||
// Even for invalid input types, ZeroExpr may return a partially correct ast.Expr
|
||||
// representation. The caller should use the returned isValid boolean to determine
|
||||
// the validity of the expression.
|
||||
//
|
||||
// This function is designed for types suitable for variables and should not be
|
||||
// used with Tuple or Union types.References to named types are qualified by an
|
||||
// appropriate (optional) qualifier function.
|
||||
//
|
||||
// See [ZeroString] for a variant that returns a string.
|
||||
func ZeroExpr(t types.Type, qual types.Qualifier) (_ ast.Expr, isValid bool) {
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
switch {
|
||||
case t.Info()&types.IsBoolean != 0:
|
||||
return &ast.Ident{Name: "false"}, true
|
||||
case t.Info()&types.IsNumeric != 0:
|
||||
return &ast.BasicLit{Kind: token.INT, Value: "0"}, true
|
||||
case t.Info()&types.IsString != 0:
|
||||
return &ast.BasicLit{Kind: token.STRING, Value: `""`}, true
|
||||
case t.Kind() == types.UnsafePointer:
|
||||
fallthrough
|
||||
case t.Kind() == types.UntypedNil:
|
||||
return ast.NewIdent("nil"), true
|
||||
case t.Kind() == types.Invalid:
|
||||
return &ast.BasicLit{Kind: token.STRING, Value: `"invalid"`}, false
|
||||
default:
|
||||
panic(fmt.Sprintf("ZeroExpr for unexpected type %v", t))
|
||||
}
|
||||
|
||||
case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature:
|
||||
return ast.NewIdent("nil"), true
|
||||
|
||||
case *types.Interface:
|
||||
if !t.IsMethodSet() {
|
||||
return &ast.BasicLit{Kind: token.STRING, Value: `"invalid"`}, false
|
||||
}
|
||||
return ast.NewIdent("nil"), true
|
||||
|
||||
case *types.Named:
|
||||
switch under := t.Underlying().(type) {
|
||||
case *types.Struct, *types.Array:
|
||||
return &ast.CompositeLit{
|
||||
Type: TypeExpr(t, qual),
|
||||
}, true
|
||||
default:
|
||||
return ZeroExpr(under, qual)
|
||||
}
|
||||
|
||||
case *types.Alias:
|
||||
switch t.Underlying().(type) {
|
||||
case *types.Struct, *types.Array:
|
||||
return &ast.CompositeLit{
|
||||
Type: TypeExpr(t, qual),
|
||||
}, true
|
||||
default:
|
||||
return ZeroExpr(types.Unalias(t), qual)
|
||||
}
|
||||
|
||||
case *types.Array, *types.Struct:
|
||||
return &ast.CompositeLit{
|
||||
Type: TypeExpr(t, qual),
|
||||
}, true
|
||||
|
||||
case *types.TypeParam:
|
||||
return &ast.StarExpr{ // *new(T)
|
||||
X: &ast.CallExpr{
|
||||
// Assumes func new is not shadowed.
|
||||
Fun: ast.NewIdent("new"),
|
||||
Args: []ast.Expr{
|
||||
ast.NewIdent(t.Obj().Name()),
|
||||
},
|
||||
},
|
||||
}, true
|
||||
|
||||
case *types.Tuple:
|
||||
// Unlike ZeroString, there is no ast.Expr can express tuple by
|
||||
// "(t[0], ..., t[n])".
|
||||
panic(fmt.Sprintf("invalid type for a variable: %v", t))
|
||||
|
||||
case *types.Union:
|
||||
// Variables of these types cannot be created, so it makes
|
||||
// no sense to ask for their zero value.
|
||||
panic(fmt.Sprintf("invalid type for a variable: %v", t))
|
||||
|
||||
default:
|
||||
panic(t) // unreachable.
|
||||
}
|
||||
}
|
||||
|
||||
// IsZeroExpr uses simple syntactic heuristics to report whether expr
|
||||
// is a obvious zero value, such as 0, "", nil, or false.
|
||||
// It cannot do better without type information.
|
||||
func IsZeroExpr(expr ast.Expr) bool {
|
||||
switch e := expr.(type) {
|
||||
case *ast.BasicLit:
|
||||
return e.Value == "0" || e.Value == `""`
|
||||
case *ast.Ident:
|
||||
return e.Name == "nil" || e.Name == "false"
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// TypeExpr returns syntax for the specified type. References to named types
|
||||
// are qualified by an appropriate (optional) qualifier function.
|
||||
// It may panic for types such as Tuple or Union.
|
||||
func TypeExpr(t types.Type, qual types.Qualifier) ast.Expr {
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
switch t.Kind() {
|
||||
case types.UnsafePointer:
|
||||
return &ast.SelectorExpr{X: ast.NewIdent(qual(types.NewPackage("unsafe", "unsafe"))), Sel: ast.NewIdent("Pointer")}
|
||||
default:
|
||||
return ast.NewIdent(t.Name())
|
||||
}
|
||||
|
||||
case *types.Pointer:
|
||||
return &ast.UnaryExpr{
|
||||
Op: token.MUL,
|
||||
X: TypeExpr(t.Elem(), qual),
|
||||
}
|
||||
|
||||
case *types.Array:
|
||||
return &ast.ArrayType{
|
||||
Len: &ast.BasicLit{
|
||||
Kind: token.INT,
|
||||
Value: fmt.Sprintf("%d", t.Len()),
|
||||
},
|
||||
Elt: TypeExpr(t.Elem(), qual),
|
||||
}
|
||||
|
||||
case *types.Slice:
|
||||
return &ast.ArrayType{
|
||||
Elt: TypeExpr(t.Elem(), qual),
|
||||
}
|
||||
|
||||
case *types.Map:
|
||||
return &ast.MapType{
|
||||
Key: TypeExpr(t.Key(), qual),
|
||||
Value: TypeExpr(t.Elem(), qual),
|
||||
}
|
||||
|
||||
case *types.Chan:
|
||||
dir := ast.ChanDir(t.Dir())
|
||||
if t.Dir() == types.SendRecv {
|
||||
dir = ast.SEND | ast.RECV
|
||||
}
|
||||
return &ast.ChanType{
|
||||
Dir: dir,
|
||||
Value: TypeExpr(t.Elem(), qual),
|
||||
}
|
||||
|
||||
case *types.Signature:
|
||||
var params []*ast.Field
|
||||
for i := 0; i < t.Params().Len(); i++ {
|
||||
params = append(params, &ast.Field{
|
||||
Type: TypeExpr(t.Params().At(i).Type(), qual),
|
||||
Names: []*ast.Ident{
|
||||
{
|
||||
Name: t.Params().At(i).Name(),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
if t.Variadic() {
|
||||
last := params[len(params)-1]
|
||||
last.Type = &ast.Ellipsis{Elt: last.Type.(*ast.ArrayType).Elt}
|
||||
}
|
||||
var returns []*ast.Field
|
||||
for i := 0; i < t.Results().Len(); i++ {
|
||||
returns = append(returns, &ast.Field{
|
||||
Type: TypeExpr(t.Results().At(i).Type(), qual),
|
||||
})
|
||||
}
|
||||
return &ast.FuncType{
|
||||
Params: &ast.FieldList{
|
||||
List: params,
|
||||
},
|
||||
Results: &ast.FieldList{
|
||||
List: returns,
|
||||
},
|
||||
}
|
||||
|
||||
case *types.TypeParam:
|
||||
pkgName := qual(t.Obj().Pkg())
|
||||
if pkgName == "" || t.Obj().Pkg() == nil {
|
||||
return ast.NewIdent(t.Obj().Name())
|
||||
}
|
||||
return &ast.SelectorExpr{
|
||||
X: ast.NewIdent(pkgName),
|
||||
Sel: ast.NewIdent(t.Obj().Name()),
|
||||
}
|
||||
|
||||
// types.TypeParam also implements interface NamedOrAlias. To differentiate,
|
||||
// case TypeParam need to be present before case NamedOrAlias.
|
||||
// TODO(hxjiang): remove this comment once TypeArgs() is added to interface
|
||||
// NamedOrAlias.
|
||||
case NamedOrAlias:
|
||||
var expr ast.Expr = ast.NewIdent(t.Obj().Name())
|
||||
if pkgName := qual(t.Obj().Pkg()); pkgName != "." && pkgName != "" {
|
||||
expr = &ast.SelectorExpr{
|
||||
X: ast.NewIdent(pkgName),
|
||||
Sel: expr.(*ast.Ident),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(hxjiang): call t.TypeArgs after adding method TypeArgs() to
|
||||
// typesinternal.NamedOrAlias.
|
||||
if hasTypeArgs, ok := t.(interface{ TypeArgs() *types.TypeList }); ok {
|
||||
if typeArgs := hasTypeArgs.TypeArgs(); typeArgs != nil && typeArgs.Len() > 0 {
|
||||
var indices []ast.Expr
|
||||
for i := range typeArgs.Len() {
|
||||
indices = append(indices, TypeExpr(typeArgs.At(i), qual))
|
||||
}
|
||||
expr = &ast.IndexListExpr{
|
||||
X: expr,
|
||||
Indices: indices,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return expr
|
||||
|
||||
case *types.Struct:
|
||||
return ast.NewIdent(t.String())
|
||||
|
||||
case *types.Interface:
|
||||
return ast.NewIdent(t.String())
|
||||
|
||||
case *types.Union:
|
||||
if t.Len() == 0 {
|
||||
panic("Union type should have at least one term")
|
||||
}
|
||||
// Same as go/ast, the return expression will put last term in the
|
||||
// Y field at topmost level of BinaryExpr.
|
||||
// For union of type "float32 | float64 | int64", the structure looks
|
||||
// similar to:
|
||||
// {
|
||||
// X: {
|
||||
// X: float32,
|
||||
// Op: |
|
||||
// Y: float64,
|
||||
// }
|
||||
// Op: |,
|
||||
// Y: int64,
|
||||
// }
|
||||
var union ast.Expr
|
||||
for i := range t.Len() {
|
||||
term := t.Term(i)
|
||||
termExpr := TypeExpr(term.Type(), qual)
|
||||
if term.Tilde() {
|
||||
termExpr = &ast.UnaryExpr{
|
||||
Op: token.TILDE,
|
||||
X: termExpr,
|
||||
}
|
||||
}
|
||||
if i == 0 {
|
||||
union = termExpr
|
||||
} else {
|
||||
union = &ast.BinaryExpr{
|
||||
X: union,
|
||||
Op: token.OR,
|
||||
Y: termExpr,
|
||||
}
|
||||
}
|
||||
}
|
||||
return union
|
||||
|
||||
case *types.Tuple:
|
||||
panic("invalid input type types.Tuple")
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
+43
@@ -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
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
// 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.
|
||||
|
||||
// This is a fork of internal/gover for use by x/tools until
|
||||
// go1.21 and earlier are no longer supported by x/tools.
|
||||
|
||||
package versions
|
||||
|
||||
import "strings"
|
||||
|
||||
// A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]]
|
||||
// The numbers are the original decimal strings to avoid integer overflows
|
||||
// and since there is very little actual math. (Probably overflow doesn't matter in practice,
|
||||
// but at the time this code was written, there was an existing test that used
|
||||
// go1.99999999999, which does not fit in an int on 32-bit platforms.
|
||||
// The "big decimal" representation avoids the problem entirely.)
|
||||
type gover struct {
|
||||
major string // decimal
|
||||
minor string // decimal or ""
|
||||
patch string // decimal or ""
|
||||
kind string // "", "alpha", "beta", "rc"
|
||||
pre string // decimal or ""
|
||||
}
|
||||
|
||||
// compare returns -1, 0, or +1 depending on whether
|
||||
// x < y, x == y, or x > y, interpreted as toolchain versions.
|
||||
// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21".
|
||||
// Malformed versions compare less than well-formed versions and equal to each other.
|
||||
// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0".
|
||||
func compare(x, y string) int {
|
||||
vx := parse(x)
|
||||
vy := parse(y)
|
||||
|
||||
if c := cmpInt(vx.major, vy.major); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := cmpInt(vx.minor, vy.minor); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := cmpInt(vx.patch, vy.patch); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc
|
||||
return c
|
||||
}
|
||||
if c := cmpInt(vx.pre, vy.pre); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// lang returns the Go language version. For example, lang("1.2.3") == "1.2".
|
||||
func lang(x string) string {
|
||||
v := parse(x)
|
||||
if v.minor == "" || v.major == "1" && v.minor == "0" {
|
||||
return v.major
|
||||
}
|
||||
return v.major + "." + v.minor
|
||||
}
|
||||
|
||||
// isValid reports whether the version x is valid.
|
||||
func isValid(x string) bool {
|
||||
return parse(x) != gover{}
|
||||
}
|
||||
|
||||
// parse parses the Go version string x into a version.
|
||||
// It returns the zero version if x is malformed.
|
||||
func parse(x string) gover {
|
||||
var v gover
|
||||
|
||||
// Parse major version.
|
||||
var ok bool
|
||||
v.major, x, ok = cutInt(x)
|
||||
if !ok {
|
||||
return gover{}
|
||||
}
|
||||
if x == "" {
|
||||
// Interpret "1" as "1.0.0".
|
||||
v.minor = "0"
|
||||
v.patch = "0"
|
||||
return v
|
||||
}
|
||||
|
||||
// Parse . before minor version.
|
||||
if x[0] != '.' {
|
||||
return gover{}
|
||||
}
|
||||
|
||||
// Parse minor version.
|
||||
v.minor, x, ok = cutInt(x[1:])
|
||||
if !ok {
|
||||
return gover{}
|
||||
}
|
||||
if x == "" {
|
||||
// Patch missing is same as "0" for older versions.
|
||||
// Starting in Go 1.21, patch missing is different from explicit .0.
|
||||
if cmpInt(v.minor, "21") < 0 {
|
||||
v.patch = "0"
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Parse patch if present.
|
||||
if x[0] == '.' {
|
||||
v.patch, x, ok = cutInt(x[1:])
|
||||
if !ok || x != "" {
|
||||
// Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != "").
|
||||
// Allowing them would be a bit confusing because we already have:
|
||||
// 1.21 < 1.21rc1
|
||||
// But a prerelease of a patch would have the opposite effect:
|
||||
// 1.21.3rc1 < 1.21.3
|
||||
// We've never needed them before, so let's not start now.
|
||||
return gover{}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Parse prerelease.
|
||||
i := 0
|
||||
for i < len(x) && (x[i] < '0' || '9' < x[i]) {
|
||||
if x[i] < 'a' || 'z' < x[i] {
|
||||
return gover{}
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i == 0 {
|
||||
return gover{}
|
||||
}
|
||||
v.kind, x = x[:i], x[i:]
|
||||
if x == "" {
|
||||
return v
|
||||
}
|
||||
v.pre, x, ok = cutInt(x)
|
||||
if !ok || x != "" {
|
||||
return gover{}
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// cutInt scans the leading decimal number at the start of x to an integer
|
||||
// and returns that value and the rest of the string.
|
||||
func cutInt(x string) (n, rest string, ok bool) {
|
||||
i := 0
|
||||
for i < len(x) && '0' <= x[i] && x[i] <= '9' {
|
||||
i++
|
||||
}
|
||||
if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero
|
||||
return "", "", false
|
||||
}
|
||||
return x[:i], x[i:], true
|
||||
}
|
||||
|
||||
// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers.
|
||||
// (Copied from golang.org/x/mod/semver's compareInt.)
|
||||
func cmpInt(x, y string) int {
|
||||
if x == y {
|
||||
return 0
|
||||
}
|
||||
if len(x) < len(y) {
|
||||
return -1
|
||||
}
|
||||
if len(x) > len(y) {
|
||||
return +1
|
||||
}
|
||||
if x < y {
|
||||
return -1
|
||||
} else {
|
||||
return +1
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// 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
|
||||
|
||||
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.
|
||||
// For example, during `go get -u ./...` (go.dev/issue/64490) we do not try to include
|
||||
// For this reason, this library just a clone of go/versions for the moment.
|
||||
|
||||
// Lang returns the Go language version for version x.
|
||||
// If x is not a valid version, Lang returns the empty string.
|
||||
// For example:
|
||||
//
|
||||
// Lang("go1.21rc2") = "go1.21"
|
||||
// Lang("go1.21.2") = "go1.21"
|
||||
// Lang("go1.21") = "go1.21"
|
||||
// Lang("go1") = "go1"
|
||||
// Lang("bad") = ""
|
||||
// Lang("1.21") = ""
|
||||
func Lang(x string) string {
|
||||
v := lang(stripGo(x))
|
||||
if v == "" {
|
||||
return ""
|
||||
}
|
||||
return x[:2+len(v)] // "go"+v without allocation
|
||||
}
|
||||
|
||||
// Compare returns -1, 0, or +1 depending on whether
|
||||
// x < y, x == y, or x > y, interpreted as Go versions.
|
||||
// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21".
|
||||
// Invalid versions, including the empty string, compare less than
|
||||
// valid versions and equal to each other.
|
||||
// The language version "go1.21" compares less than the
|
||||
// release candidate and eventual releases "go1.21rc1" and "go1.21.0".
|
||||
// Custom toolchain suffixes are ignored during comparison:
|
||||
// "go1.21.0" and "go1.21.0-bigcorp" are equal.
|
||||
func Compare(x, y string) int { return compare(stripGo(x), stripGo(y)) }
|
||||
|
||||
// IsValid reports whether the version x is valid.
|
||||
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 ""
|
||||
}
|
||||
return v[2:]
|
||||
}
|
||||
Vendored
+19
-1
@@ -480,7 +480,7 @@ github.com/nxadm/tail/ratelimiter
|
||||
github.com/nxadm/tail/util
|
||||
github.com/nxadm/tail/watch
|
||||
github.com/nxadm/tail/winfile
|
||||
# github.com/onsi/ginkgo/v2 v2.25.3
|
||||
# github.com/onsi/ginkgo/v2 v2.26.0
|
||||
## explicit; go 1.23.0
|
||||
github.com/onsi/ginkgo/v2
|
||||
github.com/onsi/ginkgo/v2/config
|
||||
@@ -499,6 +499,7 @@ github.com/onsi/ginkgo/v2/internal
|
||||
github.com/onsi/ginkgo/v2/internal/global
|
||||
github.com/onsi/ginkgo/v2/internal/interrupt_handler
|
||||
github.com/onsi/ginkgo/v2/internal/parallel_support
|
||||
github.com/onsi/ginkgo/v2/internal/reporters
|
||||
github.com/onsi/ginkgo/v2/internal/testingtproxy
|
||||
github.com/onsi/ginkgo/v2/reporters
|
||||
github.com/onsi/ginkgo/v2/types
|
||||
@@ -1043,6 +1044,23 @@ golang.org/x/time/rate
|
||||
golang.org/x/tools/cover
|
||||
golang.org/x/tools/go/ast/edge
|
||||
golang.org/x/tools/go/ast/inspector
|
||||
golang.org/x/tools/go/gcexportdata
|
||||
golang.org/x/tools/go/packages
|
||||
golang.org/x/tools/go/types/objectpath
|
||||
golang.org/x/tools/go/types/typeutil
|
||||
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/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/typeparams
|
||||
golang.org/x/tools/internal/typesinternal
|
||||
golang.org/x/tools/internal/versions
|
||||
# google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e
|
||||
## explicit; go 1.23.0
|
||||
google.golang.org/genproto/googleapis/api
|
||||
|
||||
Reference in new issue
Block a user