Merge pull request #4758 from thaJeztah/bump_assorted

vendor: update some (test) dependencies
This commit is contained in:
Sebastiaan van Stijn 2024-01-08 12:39:50 +01:00 committed by GitHub
commit 5c6ca07208
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
90 changed files with 2250 additions and 1410 deletions

View File

@ -9,7 +9,7 @@ go 1.19
require ( require (
dario.cat/mergo v1.0.0 dario.cat/mergo v1.0.0
github.com/containerd/containerd v1.7.11 github.com/containerd/containerd v1.7.11
github.com/creack/pty v1.1.18 github.com/creack/pty v1.1.21
github.com/distribution/reference v0.5.0 github.com/distribution/reference v0.5.0
github.com/docker/distribution v2.8.3+incompatible github.com/docker/distribution v2.8.3+incompatible
github.com/docker/docker v25.0.0-rc.1+incompatible github.com/docker/docker v25.0.0-rc.1+incompatible
@ -18,7 +18,7 @@ require (
github.com/docker/go-units v0.5.0 github.com/docker/go-units v0.5.0
github.com/fvbommel/sortorder v1.0.2 github.com/fvbommel/sortorder v1.0.2
github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf v1.3.2
github.com/google/go-cmp v0.5.9 github.com/google/go-cmp v0.6.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/mattn/go-runewidth v0.0.15 github.com/mattn/go-runewidth v0.0.15
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
@ -37,7 +37,7 @@ require (
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a
github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d
github.com/xeipuuv/gojsonschema v1.2.0 github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/sync v0.3.0 golang.org/x/sync v0.6.0
golang.org/x/sys v0.15.0 golang.org/x/sys v0.15.0
golang.org/x/term v0.15.0 golang.org/x/term v0.15.0
golang.org/x/text v0.14.0 golang.org/x/text v0.14.0
@ -80,10 +80,10 @@ require (
go.opentelemetry.io/otel/metric v1.19.0 // indirect go.opentelemetry.io/otel/metric v1.19.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect go.opentelemetry.io/otel/trace v1.19.0 // indirect
golang.org/x/crypto v0.17.0 // indirect golang.org/x/crypto v0.17.0 // indirect
golang.org/x/mod v0.11.0 // indirect golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect golang.org/x/net v0.19.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.10.0 // indirect golang.org/x/tools v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/grpc v1.58.3 // indirect google.golang.org/grpc v1.58.3 // indirect
google.golang.org/protobuf v1.31.0 // indirect google.golang.org/protobuf v1.31.0 // indirect

View File

@ -43,8 +43,8 @@ github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -111,8 +111,8 @@ github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519
github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY= github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
@ -299,8 +299,8 @@ golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -308,16 +308,16 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -347,8 +347,8 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

324
vendor/github.com/creack/pty/.golangci.yml generated vendored Normal file
View File

@ -0,0 +1,324 @@
---
# Reference: https://golangci-lint.run/usage/configuration/
run:
timeout: 5m
# modules-download-mode: vendor
# Include test files.
tests: true
skip-dirs: []
skip-files: []
output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number".
format: colored-line-number
print-issued-lines: true
print-linter-name: true
# Linter specific settings. See below in the `linter.enable` section for details on what each linter is doing.
linters-settings:
dogsled:
# Checks assignments with too many blank identifiers. Default is 2.
max-blank-identifiers: 2
dupl:
# Tokens count to trigger issue.
threshold: 150
errcheck:
# Report about not checking of errors in type assertions: `a := b.(MyStruct)`.
# Enabled as this is often overlooked by developers.
check-type-assertions: true
# Report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`.
# Disabled as we consider that if the developer did type `_`, it was on purpose.
# Note that while this isn't enforced by the linter, each and every case of ignored error should
# be accompanied with a comment explaining why that error is being discarded.
check-blank: false
exhaustive:
# Indicates that switch statements are to be considered exhaustive if a
# 'default' case is present, even if all enum members aren't listed in the
# switch.
default-signifies-exhaustive: false
funlen:
# funlen checks the number of lines/statements in a function.
# While is is always best to keep functions short for readability, maintainability and testing,
# the default are a bit too strict (60 lines / 40 statements), increase it to be more flexible.
lines: 160
statements: 70
# NOTE: We don't set `gci` for import order as it supports only one prefix. Use `goimports.local-prefixes` instead.
gocognit:
# Minimal code complexity to report, defaults to 30 in gocognit, defaults 10 in golangci.
# Use 15 as it allows for some flexibility while preventing too much complexity.
# NOTE: Similar to gocyclo.
min-complexity: 35
nestif:
# Minimal complexity of if statements to report.
min-complexity: 8
goconst:
# Minimal length of string constant.
min-len: 4
# Minimal occurrences count to trigger.
# Increase the default from 3 to 5 as small number of const usage can reduce readability instead of improving it.
min-occurrences: 5
gocritic:
# Which checks should be disabled; can't be combined with 'enabled-checks'.
# See https://go-critic.github.io/overview#checks-overview
# To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
disabled-checks:
- hugeParam # Very strict check on the size of variables being copied. Too strict for most developer.
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
enabled-tags:
- diagnostic
- style
- opinionated
- performance
settings:
rangeValCopy:
sizeThreshold: 1024 # Increase the allowed copied bytes in range.
cyclop:
max-complexity: 35
gocyclo:
# Similar check as gocognit.
# NOTE: We might be able to remove this linter as it is redundant with gocyclo. It is in golangci-lint, so we keep it for now.
min-complexity: 35
godot:
# Check all top-level comments, not only declarations.
check-all: true
gofmt:
# simplify code: gofmt with `-s` option.
simplify: true
# NOTE: the goheader settings are set per-project.
goimports:
# Put imports beginning with prefix after 3rd-party packages.
# It's a comma-separated list of prefixes.
local-prefixes: "github.com/creack/pty"
golint:
# Minimal confidence for issues, default is 0.8.
min-confidence: 0.8
gosimple:
# Select the Go version to target. The default is '1.13'.
go: "1.18"
# https://staticcheck.io/docs/options#checks
checks: ["all"]
gosec:
govet:
# Enable all available checks from go vet.
enable-all: false
# Report about shadowed variables.
check-shadowing: true
# NOTE: depguard is disabled as it is very slow and made redundant by gomodguard.
lll:
# Make sure everyone is on the same level, fix the tab width to go's default.
tab-width: 8
# Increase the default max line length to give more flexibility. Forcing newlines can reduce readability instead of improving it.
line-length: 180
misspell:
locale: US
ignore-words:
nakedret:
# Make an issue if func has more lines of code than this setting and it has naked returns; default is 30.
# NOTE: Consider setting this to 1 to prevent naked returns.
max-func-lines: 30
nolintlint:
# Prevent ununsed directive to avoid stale comments.
allow-unused: false
# Require an explanation of nonzero length after each nolint directive.
require-explanation: true
# Exclude following linters from requiring an explanation.
# NOTE: It is strongly discouraged to put anything in there.
allow-no-explanation: []
# Enable to require nolint directives to mention the specific linter being suppressed. This ensurce the developer understand the reason being the error.
require-specific: true
prealloc:
# NOTE: For most programs usage of prealloc will be a premature optimization.
# Keep thing simple, pre-alloc what is obvious and profile the program for more complex scenarios.
#
simple: true # Checkonly on simple loops that have no returns/breaks/continues/gotos in them.
range-loops: true # Check range loops, true by default
for-loops: false # Check suggestions on for loops, false by default
rowserrcheck:
packages: []
staticcheck:
# Select the Go version to target. The default is '1.13'.
go: "1.18"
# https://staticcheck.io/docs/options#checks
checks: ["all"]
stylecheck:
# Select the Go version to target. The default is '1.13'.
go: "1.18"
# https://staticcheck.io/docs/options#checks
checks: ["all"] # "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"]
tagliatelle:
# Check the struck tag name case.
case:
# Use the struct field name to check the name of the struct tag.
use-field-name: false
rules:
# Any struct tag type can be used.
# support string case: `camel`, `pascal`, `kebab`, `snake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower`
json: snake
firestore: camel
yaml: camel
xml: camel
bson: camel
avro: snake
mapstructure: kebab
envconfig: upper
unparam:
# Don't create an error if an exported code have static params being used. It is often expected in libraries.
# NOTE: It would be nice if this linter would differentiate between a main package and a lib.
check-exported: true
unused: {}
whitespace:
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
# Run `golangci-lint help linters` to get the full list of linter with their description.
linters:
disable-all: true
# NOTE: enable-all is deprecated because too many people don't pin versions...
# We still require explicit documentation on why some linters are disabled.
# disable:
# - depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false]
# - exhaustivestruct # Checks if all struct's fields are initialized [fast: true, auto-fix: false]
# - forbidigo # Forbids identifiers [fast: true, auto-fix: false]
# - gci # Gci control golang package import order and make it always deterministic. [fast: true, auto-fix: true]
# - godox # Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false]
# - goerr113 # Golang linter to check the errors handling expressions [fast: true, auto-fix: false]
# - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: false, auto-fix: false]
# - gomnd # An analyzer to detect magic numbers. [fast: true, auto-fix: false]
# - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false]
# - interfacer # Linter that suggests narrower interface types [fast: false, auto-fix: false]
# - maligned # Tool to detect Go structs that would take less memory if their fields were sorted [fast: false, auto-fix: false]
# - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity [fast: true, auto-fix: false]
# - scopelint # Scopelint checks for unpinned variables in go programs [fast: true, auto-fix: false]
# - wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false]
# - wsl # Whitespace Linter - Forces you to use empty lines! [fast: true, auto-fix: false]
# disable-reasons:
# - depguard # Checks whitelisted/blacklisted import path, but runs way too slow. Not that useful.
# - exhaustivestruct # Good concept, but not mature enough (errors on not assignable fields like locks) and too noisy when using AWS SDK as most fields are unused.
# - forbidigo # Great idea, but too strict out of the box. Probably will re-enable soon.
# - gci # Conflicts with goimports/gofumpt.
# - godox # Don't fail when finding TODO, FIXME, etc.
# - goerr113 # Too many false positives.
# - golint # Deprecated (since v1.41.0) due to: The repository of the linter has been archived by the owner. Replaced by revive.
# - gomnd # Checks for magic numbers. Disabled due to too many false positives not configurable (03/01/2020 v1.23.7).
# - gomoddirectives # Doesn't support //nolint to whitelist.
# - interfacer # Deprecated (since v1.38.0) due to: The repository of the linter has been archived by the owner.
# - maligned # Deprecated (since v1.38.0) due to: The repository of the linter has been archived by the owner. Replaced by govet 'fieldalignment'.
# - nlreturn # Actually reduces readability in most cases.
# - scopelint # Deprecated (since v1.39.0) due to: The repository of the linter has been deprecated by the owner. Replaced by exportloopref.
# - wrapcheck # Good concept, but always warns for http coded errors. Need to re-enable and whitelist our error package.
# - wsl # Forces to add newlines around blocks. Lots of false positives, not that useful.
enable:
- asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers [fast: true, auto-fix: false]
- bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false]
- cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false]
- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
- dupl # Tool for code clone detection [fast: true, auto-fix: false]
- durationcheck # check for two durations multiplied together [fast: false, auto-fix: false]
- errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false]
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. [fast: false, auto-fix: false]
- errorlint # go-errorlint is a source code linter for Go software that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. [fast: false, auto-fix: false]
- exhaustive # check exhaustiveness of enum switch statements [fast: false, auto-fix: false]
- exportloopref # checks for pointers to enclosing loop variables [fast: false, auto-fix: false]
- forcetypeassert # finds forced type assertions [fast: true, auto-fix: false]
- funlen # Tool for detection of long functions [fast: true, auto-fix: false]
- gochecknoglobals # check that no global variables exist [fast: true, auto-fix: false]
- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
- gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
- goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
- gocritic # Provides many diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: false]
- gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
- godot # Check if comments end in a period [fast: true, auto-fix: true]
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
- gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true]
- goheader # Checks is file header matches to pattern [fast: true, auto-fix: false]
- goimports # Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true]
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. [fast: true, auto-fix: false]
- goprintffuncname # Checks that printf-like functions are named with `f` at the end [fast: true, auto-fix: false]
- gosec # (gas): Inspects source code for security problems [fast: false, auto-fix: false]
- gosimple # (megacheck): Linter for Go source code that specializes in simplifying a code [fast: false, auto-fix: false]
- govet # (vet, vetshadow): Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: false, auto-fix: false]
- importas # Enforces consistent import aliases [fast: false, auto-fix: false]
- ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false]
- lll # Reports long lines [fast: true, auto-fix: false]
- makezero # Finds slice declarations with non-zero initial length [fast: false, auto-fix: false]
- misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true]
- nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false]
- nestif # Reports deeply nested if statements [fast: true, auto-fix: false]
- nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false]
- noctx # noctx finds sending http request without context.Context [fast: false, auto-fix: false]
- nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: false]
- paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test [fast: true, auto-fix: false]
- prealloc # Finds slice declarations that could potentially be preallocated [fast: true, auto-fix: false]
- predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false]
- promlinter # Check Prometheus metrics naming via promlint [fast: true, auto-fix: false]
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false]
# Disabled due to generic. Work in progress upstream.
# - rowserrcheck # checks whether Err of rows is checked successfully [fast: false, auto-fix: false]
# Disabled due to generic. Work in progress upstream.
# - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. [fast: false, auto-fix: false]
- staticcheck # (megacheck): Staticcheck is a go vet on steroids, applying a ton of static analysis checks [fast: false, auto-fix: false]
- stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false]
# Disabled due to generic. Work in progress upstream.
# - tagliatelle # Checks the struct tags. [fast: true, auto-fix: false]
# - testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false]
- thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers [fast: false, auto-fix: false]
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes [fast: false, auto-fix: false]
- typecheck # Like the front-end of a Go compiler, parses and type-checks Go code [fast: false, auto-fix: false]
- unconvert # Remove unnecessary type conversions [fast: false, auto-fix: false]
- unparam # Reports unused function parameters [fast: false, auto-fix: false]
# Disabled due to way too many false positive in go1.20.
# - unused # (megacheck): Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false]
# Disabled due to generic. Work in progress upstream.
# - wastedassign # wastedassign finds wasted assignment statements. [fast: false, auto-fix: false]
- whitespace # Tool for detection of leading and trailing whitespace [fast: true, auto-fix: true]
issues:
exclude:
# Allow shadowing of 'err'.
- 'shadow: declaration of "err" shadows declaration'
# Allow shadowing of `ctx`.
- 'shadow: declaration of "ctx" shadows declaration'
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
max-per-linter: 10
# Disable default excludes. Always be explicit on what we exclude.
exclude-use-default: false
# Exclude some linters from running on tests files.
exclude-rules: []

View File

@ -1,4 +1,4 @@
ARG GOVERSION=1.14 ARG GOVERSION=1.18.2
FROM golang:${GOVERSION} FROM golang:${GOVERSION}
# Set base env. # Set base env.

View File

@ -1,23 +0,0 @@
# NOTE: Using 1.13 as a base to build the RISCV compiler, the resulting version is based on go1.6.
FROM golang:1.13
# Clone and complie a riscv compatible version of the go compiler.
RUN git clone https://review.gerrithub.io/riscv/riscv-go /riscv-go
# riscvdev branch HEAD as of 2019-06-29.
RUN cd /riscv-go && git checkout 04885fddd096d09d4450726064d06dd107e374bf
ENV PATH=/riscv-go/misc/riscv:/riscv-go/bin:$PATH
RUN cd /riscv-go/src && GOROOT_BOOTSTRAP=$(go env GOROOT) ./make.bash
ENV GOROOT=/riscv-go
# Set the base env.
ENV GOOS=linux GOARCH=riscv CGO_ENABLED=0 GOFLAGS='-v -ldflags=-s -ldflags=-w'
# Pre compile the stdlib.
RUN go build -a std
# Add the code to the image.
WORKDIR pty
ADD . .
# Build the lib.
RUN go build

View File

@ -1,19 +1,28 @@
//go:build !windows && !solaris && !aix //go:build !windows && go1.12
// +build !windows,!solaris,!aix // +build !windows,go1.12
package pty package pty
import "syscall" import "os"
const ( func ioctl(f *os.File, cmd, ptr uintptr) error {
TIOCGWINSZ = syscall.TIOCGWINSZ return ioctlInner(f.Fd(), cmd, ptr) // Fall back to blocking io.
TIOCSWINSZ = syscall.TIOCSWINSZ }
)
func ioctl(fd, cmd, ptr uintptr) error { // NOTE: Unused. Keeping for reference.
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr) func ioctlNonblock(f *os.File, cmd, ptr uintptr) error {
if e != 0 { sc, e := f.SyscallConn()
if e != nil {
return ioctlInner(f.Fd(), cmd, ptr) // Fall back to blocking io (old behavior).
}
ch := make(chan error, 1)
defer close(ch)
e = sc.Control(func(fd uintptr) { ch <- ioctlInner(fd, cmd, ptr) })
if e != nil {
return e return e
} }
return nil e = <-ch
return e
} }

20
vendor/github.com/creack/pty/ioctl_inner.go generated vendored Normal file
View File

@ -0,0 +1,20 @@
//go:build !windows && !solaris && !aix
// +build !windows,!solaris,!aix
package pty
import "syscall"
// Local syscall const values.
const (
TIOCGWINSZ = syscall.TIOCGWINSZ
TIOCSWINSZ = syscall.TIOCSWINSZ
)
func ioctlInner(fd, cmd, ptr uintptr) error {
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
if e != 0 {
return e
}
return nil
}

10
vendor/github.com/creack/pty/ioctl_legacy.go generated vendored Normal file
View File

@ -0,0 +1,10 @@
//go:build !windows && !go1.12
// +build !windows,!go1.12
package pty
import "os"
func ioctl(f *os.File, cmd, ptr uintptr) error {
return ioctlInner(f.Fd(), cmd, ptr) // fall back to blocking io (old behavior)
}

View File

@ -40,7 +40,7 @@ type strioctl struct {
// Defined in asm_solaris_amd64.s. // Defined in asm_solaris_amd64.s.
func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
func ioctl(fd, cmd, ptr uintptr) error { func ioctlInner(fd, cmd, ptr uintptr) error {
if _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procioctl)), 3, fd, cmd, ptr, 0, 0, 0); errno != 0 { if _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procioctl)), 3, fd, cmd, ptr, 0, 0, 0); errno != 0 {
return errno return errno
} }

View File

@ -8,6 +8,6 @@ const (
TIOCSWINSZ = 0 TIOCSWINSZ = 0
) )
func ioctl(fd, cmd, ptr uintptr) error { func ioctlInner(fd, cmd, ptr uintptr) error {
return ErrUnsupported return ErrUnsupported
} }

View File

@ -46,7 +46,7 @@ func open() (pty, tty *os.File, err error) {
func ptsname(f *os.File) (string, error) { func ptsname(f *os.File) (string, error) {
n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME)) n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0]))) err := ioctl(f, syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
if err != nil { if err != nil {
return "", err return "", err
} }
@ -60,9 +60,9 @@ func ptsname(f *os.File) (string, error) {
} }
func grantpt(f *os.File) error { func grantpt(f *os.File) error {
return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0) return ioctl(f, syscall.TIOCPTYGRANT, 0)
} }
func unlockpt(f *os.File) error { func unlockpt(f *os.File) error {
return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0) return ioctl(f, syscall.TIOCPTYUNLK, 0)
} }

View File

@ -45,17 +45,17 @@ func open() (pty, tty *os.File, err error) {
} }
func grantpt(f *os.File) error { func grantpt(f *os.File) error {
_, err := isptmaster(f.Fd()) _, err := isptmaster(f)
return err return err
} }
func unlockpt(f *os.File) error { func unlockpt(f *os.File) error {
_, err := isptmaster(f.Fd()) _, err := isptmaster(f)
return err return err
} }
func isptmaster(fd uintptr) (bool, error) { func isptmaster(f *os.File) (bool, error) {
err := ioctl(fd, syscall.TIOCISPTMASTER, 0) err := ioctl(f, syscall.TIOCISPTMASTER, 0)
return err == nil, err return err == nil, err
} }
@ -68,7 +68,7 @@ func ptsname(f *os.File) (string, error) {
name := make([]byte, _C_SPECNAMELEN) name := make([]byte, _C_SPECNAMELEN)
fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}} fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}}
err := ioctl(f.Fd(), ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa))) err := ioctl(f, ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa)))
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -44,8 +44,8 @@ func open() (pty, tty *os.File, err error) {
return p, t, nil return p, t, nil
} }
func isptmaster(fd uintptr) (bool, error) { func isptmaster(f *os.File) (bool, error) {
err := ioctl(fd, syscall.TIOCPTMASTER, 0) err := ioctl(f, syscall.TIOCPTMASTER, 0)
return err == nil, err return err == nil, err
} }
@ -55,7 +55,7 @@ var (
) )
func ptsname(f *os.File) (string, error) { func ptsname(f *os.File) (string, error) {
master, err := isptmaster(f.Fd()) master, err := isptmaster(f)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -68,7 +68,7 @@ func ptsname(f *os.File) (string, error) {
buf = make([]byte, n) buf = make([]byte, n)
arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))} arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
) )
if err := ioctl(f.Fd(), ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil { if err := ioctl(f, ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil {
return "", err return "", err
} }

View File

@ -40,7 +40,7 @@ func open() (pty, tty *os.File, err error) {
func ptsname(f *os.File) (string, error) { func ptsname(f *os.File) (string, error) {
var n _C_uint var n _C_uint
err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) //nolint:gosec // Expected unsafe pointer for Syscall call. err := ioctl(f, syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) //nolint:gosec // Expected unsafe pointer for Syscall call.
if err != nil { if err != nil {
return "", err return "", err
} }
@ -49,6 +49,6 @@ func ptsname(f *os.File) (string, error) {
func unlockpt(f *os.File) error { func unlockpt(f *os.File) error {
var u _C_int var u _C_int
// use TIOCSPTLCK with a pointer to zero to clear the lock // use TIOCSPTLCK with a pointer to zero to clear the lock.
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) //nolint:gosec // Expected unsafe pointer for Syscall call. return ioctl(f, syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) //nolint:gosec // Expected unsafe pointer for Syscall call.
} }

View File

@ -47,7 +47,7 @@ func ptsname(f *os.File) (string, error) {
* ioctl(fd, TIOCPTSNAME, &pm) == -1 ? NULL : pm.sn; * ioctl(fd, TIOCPTSNAME, &pm) == -1 ? NULL : pm.sn;
*/ */
var ptm ptmget var ptm ptmget
if err := ioctl(f.Fd(), uintptr(ioctl_TIOCPTSNAME), uintptr(unsafe.Pointer(&ptm))); err != nil { if err := ioctl(f, uintptr(ioctl_TIOCPTSNAME), uintptr(unsafe.Pointer(&ptm))); err != nil {
return "", err return "", err
} }
name := make([]byte, len(ptm.Sn)) name := make([]byte, len(ptm.Sn))
@ -65,5 +65,5 @@ func grantpt(f *os.File) error {
* from grantpt(3): Calling grantpt() is equivalent to: * from grantpt(3): Calling grantpt() is equivalent to:
* ioctl(fd, TIOCGRANTPT, 0); * ioctl(fd, TIOCGRANTPT, 0);
*/ */
return ioctl(f.Fd(), uintptr(ioctl_TIOCGRANTPT), 0) return ioctl(f, uintptr(ioctl_TIOCGRANTPT), 0)
} }

View File

@ -9,6 +9,17 @@ import (
"unsafe" "unsafe"
) )
func cInt8ToString(in []int8) string {
var s []byte
for _, v := range in {
if v == 0 {
break
}
s = append(s, byte(v))
}
return string(s)
}
func open() (pty, tty *os.File, err error) { func open() (pty, tty *os.File, err error) {
/* /*
* from ptm(4): * from ptm(4):
@ -25,12 +36,12 @@ func open() (pty, tty *os.File, err error) {
defer p.Close() defer p.Close()
var ptm ptmget var ptm ptmget
if err := ioctl(p.Fd(), uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil { if err := ioctl(p, uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil {
return nil, nil, err return nil, nil, err
} }
pty = os.NewFile(uintptr(ptm.Cfd), "/dev/ptm") pty = os.NewFile(uintptr(ptm.Cfd), cInt8ToString(ptm.Cn[:]))
tty = os.NewFile(uintptr(ptm.Sfd), "/dev/ptm") tty = os.NewFile(uintptr(ptm.Sfd), cInt8ToString(ptm.Sn[:]))
return pty, tty, nil return pty, tty, nil
} }

View File

@ -65,7 +65,7 @@ func open() (pty, tty *os.File, err error) {
} }
func ptsname(f *os.File) (string, error) { func ptsname(f *os.File) (string, error) {
dev, err := ptsdev(f.Fd()) dev, err := ptsdev(f)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -84,12 +84,12 @@ func unlockpt(f *os.File) error {
icLen: 0, icLen: 0,
icDP: nil, icDP: nil,
} }
return ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr))) return ioctl(f, I_STR, uintptr(unsafe.Pointer(&istr)))
} }
func minor(x uint64) uint64 { return x & 0377 } func minor(x uint64) uint64 { return x & 0377 }
func ptsdev(fd uintptr) (uint64, error) { func ptsdev(f *os.File) (uint64, error) {
istr := strioctl{ istr := strioctl{
icCmd: ISPTM, icCmd: ISPTM,
icTimeout: 0, icTimeout: 0,
@ -97,14 +97,33 @@ func ptsdev(fd uintptr) (uint64, error) {
icDP: nil, icDP: nil,
} }
if err := ioctl(fd, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil { if err := ioctl(f, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
return 0, err return 0, err
} }
var errors = make(chan error, 1)
var results = make(chan uint64, 1)
defer close(errors)
defer close(results)
var err error
var sc syscall.RawConn
sc, err = f.SyscallConn()
if err != nil {
return 0, err
}
err = sc.Control(func(fd uintptr) {
var status syscall.Stat_t var status syscall.Stat_t
if err := syscall.Fstat(int(fd), &status); err != nil { if err := syscall.Fstat(int(fd), &status); err != nil {
results <- 0
errors <- err
}
results <- uint64(minor(status.Rdev))
errors <- nil
})
if err != nil {
return 0, err return 0, err
} }
return uint64(minor(status.Rdev)), nil return <-results, <-errors
} }
type ptOwn struct { type ptOwn struct {
@ -113,7 +132,7 @@ type ptOwn struct {
} }
func grantpt(f *os.File) error { func grantpt(f *os.File) error {
if _, err := ptsdev(f.Fd()); err != nil { if _, err := ptsdev(f); err != nil {
return err return err
} }
pto := ptOwn{ pto := ptOwn{
@ -127,7 +146,7 @@ func grantpt(f *os.File) error {
icLen: int32(unsafe.Sizeof(strioctl{})), icLen: int32(unsafe.Sizeof(strioctl{})),
icDP: unsafe.Pointer(&pto), icDP: unsafe.Pointer(&pto),
} }
if err := ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr))); err != nil { if err := ioctl(f, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
return errors.New("access denied") return errors.New("access denied")
} }
return nil return nil
@ -145,8 +164,8 @@ func streamsPush(f *os.File, mod string) error {
// but since we are not using libc or XPG4.2, we should not be // but since we are not using libc or XPG4.2, we should not be
// double-pushing modules // double-pushing modules
if err := ioctl(f.Fd(), I_FIND, uintptr(unsafe.Pointer(&buf[0]))); err != nil { if err := ioctl(f, I_FIND, uintptr(unsafe.Pointer(&buf[0]))); err != nil {
return nil return nil
} }
return ioctl(f.Fd(), I_PUSH, uintptr(unsafe.Pointer(&buf[0]))) return ioctl(f, I_PUSH, uintptr(unsafe.Pointer(&buf[0])))
} }

View File

@ -25,9 +25,9 @@ cross() {
set -e set -e
cross linux amd64 386 arm arm64 ppc64 ppc64le s390x mips mipsle mips64 mips64le cross linux amd64 386 arm arm64 ppc64 ppc64le s390x mips mipsle mips64 mips64le riscv64
cross darwin amd64 arm64 cross darwin amd64 arm64
cross freebsd amd64 386 arm arm64 cross freebsd amd64 386 arm arm64 riscv64
cross netbsd amd64 386 arm arm64 cross netbsd amd64 386 arm arm64
cross openbsd amd64 386 arm arm64 cross openbsd amd64 386 arm arm64
cross dragonfly amd64 cross dragonfly amd64
@ -45,10 +45,6 @@ if ! hash docker; then
return return
fi fi
echo2 "Build for linux."
echo2 " - linux/riscv"
docker build -t creack-pty-test -f Dockerfile.riscv .
# Golang dropped support for darwin 32bits since go1.15. Make sure the lib still compile with go1.14 on those archs. # Golang dropped support for darwin 32bits since go1.15. Make sure the lib still compile with go1.14 on those archs.
echo2 "Build for darwin (32bits)." echo2 "Build for darwin (32bits)."
echo2 " - darwin/386" echo2 " - darwin/386"

View File

@ -10,10 +10,7 @@ func InheritSize(pty, tty *os.File) error {
if err != nil { if err != nil {
return err return err
} }
if err := Setsize(tty, size); err != nil { return Setsize(tty, size)
return err
}
return nil
} }
// Getsize returns the number of rows (lines) and cols (positions // Getsize returns the number of rows (lines) and cols (positions

View File

@ -11,16 +11,16 @@ import (
// Winsize describes the terminal size. // Winsize describes the terminal size.
type Winsize struct { type Winsize struct {
Rows uint16 // ws_row: Number of rows (in cells) Rows uint16 // ws_row: Number of rows (in cells).
Cols uint16 // ws_col: Number of columns (in cells) Cols uint16 // ws_col: Number of columns (in cells).
X uint16 // ws_xpixel: Width in pixels X uint16 // ws_xpixel: Width in pixels.
Y uint16 // ws_ypixel: Height in pixels Y uint16 // ws_ypixel: Height in pixels.
} }
// Setsize resizes t to s. // Setsize resizes t to s.
func Setsize(t *os.File, ws *Winsize) error { func Setsize(t *os.File, ws *Winsize) error {
//nolint:gosec // Expected unsafe pointer for Syscall call. //nolint:gosec // Expected unsafe pointer for Syscall call.
return ioctl(t.Fd(), syscall.TIOCSWINSZ, uintptr(unsafe.Pointer(ws))) return ioctl(t, syscall.TIOCSWINSZ, uintptr(unsafe.Pointer(ws)))
} }
// GetsizeFull returns the full terminal size description. // GetsizeFull returns the full terminal size description.
@ -28,7 +28,7 @@ func GetsizeFull(t *os.File) (size *Winsize, err error) {
var ws Winsize var ws Winsize
//nolint:gosec // Expected unsafe pointer for Syscall call. //nolint:gosec // Expected unsafe pointer for Syscall call.
if err := ioctl(t.Fd(), syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws))); err != nil { if err := ioctl(t, syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws))); err != nil {
return nil, err return nil, err
} }
return &ws, nil return &ws, nil

13
vendor/github.com/creack/pty/ztypes_freebsd_riscv64.go generated vendored Normal file
View File

@ -0,0 +1,13 @@
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs types_freebsd.go
package pty
const (
_C_SPECNAMELEN = 0x3f
)
type fiodgnameArg struct {
Len int32
Buf *byte
}

9
vendor/github.com/creack/pty/ztypes_ppc.go generated vendored Normal file
View File

@ -0,0 +1,9 @@
// Created by cgo -godefs - DO NOT EDIT
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)

12
vendor/github.com/creack/pty/ztypes_sparcx.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
//go:build sparc || sparc64
// +build sparc sparc64
// Code generated by cmd/cgo -godefs; DO NOT EDIT.
// cgo -godefs types.go
package pty
type (
_C_int int32
_C_uint uint32
)

View File

@ -7,6 +7,7 @@ package cmpopts
import ( import (
"errors" "errors"
"fmt"
"math" "math"
"reflect" "reflect"
"time" "time"
@ -16,10 +17,10 @@ import (
func equateAlways(_, _ interface{}) bool { return true } func equateAlways(_, _ interface{}) bool { return true }
// EquateEmpty returns a Comparer option that determines all maps and slices // EquateEmpty returns a [cmp.Comparer] option that determines all maps and slices
// with a length of zero to be equal, regardless of whether they are nil. // with a length of zero to be equal, regardless of whether they are nil.
// //
// EquateEmpty can be used in conjunction with SortSlices and SortMaps. // EquateEmpty can be used in conjunction with [SortSlices] and [SortMaps].
func EquateEmpty() cmp.Option { func EquateEmpty() cmp.Option {
return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways)) return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways))
} }
@ -31,7 +32,7 @@ func isEmpty(x, y interface{}) bool {
(vx.Len() == 0 && vy.Len() == 0) (vx.Len() == 0 && vy.Len() == 0)
} }
// EquateApprox returns a Comparer option that determines float32 or float64 // EquateApprox returns a [cmp.Comparer] option that determines float32 or float64
// values to be equal if they are within a relative fraction or absolute margin. // values to be equal if they are within a relative fraction or absolute margin.
// This option is not used when either x or y is NaN or infinite. // This option is not used when either x or y is NaN or infinite.
// //
@ -45,7 +46,7 @@ func isEmpty(x, y interface{}) bool {
// //
// |x-y| ≤ max(fraction*min(|x|, |y|), margin) // |x-y| ≤ max(fraction*min(|x|, |y|), margin)
// //
// EquateApprox can be used in conjunction with EquateNaNs. // EquateApprox can be used in conjunction with [EquateNaNs].
func EquateApprox(fraction, margin float64) cmp.Option { func EquateApprox(fraction, margin float64) cmp.Option {
if margin < 0 || fraction < 0 || math.IsNaN(margin) || math.IsNaN(fraction) { if margin < 0 || fraction < 0 || math.IsNaN(margin) || math.IsNaN(fraction) {
panic("margin or fraction must be a non-negative number") panic("margin or fraction must be a non-negative number")
@ -73,10 +74,10 @@ func (a approximator) compareF32(x, y float32) bool {
return a.compareF64(float64(x), float64(y)) return a.compareF64(float64(x), float64(y))
} }
// EquateNaNs returns a Comparer option that determines float32 and float64 // EquateNaNs returns a [cmp.Comparer] option that determines float32 and float64
// NaN values to be equal. // NaN values to be equal.
// //
// EquateNaNs can be used in conjunction with EquateApprox. // EquateNaNs can be used in conjunction with [EquateApprox].
func EquateNaNs() cmp.Option { func EquateNaNs() cmp.Option {
return cmp.Options{ return cmp.Options{
cmp.FilterValues(areNaNsF64s, cmp.Comparer(equateAlways)), cmp.FilterValues(areNaNsF64s, cmp.Comparer(equateAlways)),
@ -91,8 +92,8 @@ func areNaNsF32s(x, y float32) bool {
return areNaNsF64s(float64(x), float64(y)) return areNaNsF64s(float64(x), float64(y))
} }
// EquateApproxTime returns a Comparer option that determines two non-zero // EquateApproxTime returns a [cmp.Comparer] option that determines two non-zero
// time.Time values to be equal if they are within some margin of one another. // [time.Time] values to be equal if they are within some margin of one another.
// If both times have a monotonic clock reading, then the monotonic time // If both times have a monotonic clock reading, then the monotonic time
// difference will be used. The margin must be non-negative. // difference will be used. The margin must be non-negative.
func EquateApproxTime(margin time.Duration) cmp.Option { func EquateApproxTime(margin time.Duration) cmp.Option {
@ -131,8 +132,8 @@ type anyError struct{}
func (anyError) Error() string { return "any error" } func (anyError) Error() string { return "any error" }
func (anyError) Is(err error) bool { return err != nil } func (anyError) Is(err error) bool { return err != nil }
// EquateErrors returns a Comparer option that determines errors to be equal // EquateErrors returns a [cmp.Comparer] option that determines errors to be equal
// if errors.Is reports them to match. The AnyError error can be used to // if [errors.Is] reports them to match. The [AnyError] error can be used to
// match any non-nil error. // match any non-nil error.
func EquateErrors() cmp.Option { func EquateErrors() cmp.Option {
return cmp.FilterValues(areConcreteErrors, cmp.Comparer(compareErrors)) return cmp.FilterValues(areConcreteErrors, cmp.Comparer(compareErrors))
@ -154,3 +155,31 @@ func compareErrors(x, y interface{}) bool {
ye := y.(error) ye := y.(error)
return errors.Is(xe, ye) || errors.Is(ye, xe) return errors.Is(xe, ye) || errors.Is(ye, xe)
} }
// EquateComparable returns a [cmp.Option] that determines equality
// of comparable types by directly comparing them using the == operator in Go.
// The types to compare are specified by passing a value of that type.
// This option should only be used on types that are documented as being
// safe for direct == comparison. For example, [net/netip.Addr] is documented
// as being semantically safe to use with ==, while [time.Time] is documented
// to discourage the use of == on time values.
func EquateComparable(typs ...interface{}) cmp.Option {
types := make(typesFilter)
for _, typ := range typs {
switch t := reflect.TypeOf(typ); {
case !t.Comparable():
panic(fmt.Sprintf("%T is not a comparable Go type", typ))
case types[t]:
panic(fmt.Sprintf("%T is already specified", typ))
default:
types[t] = true
}
}
return cmp.FilterPath(types.filter, cmp.Comparer(equateAny))
}
type typesFilter map[reflect.Type]bool
func (tf typesFilter) filter(p cmp.Path) bool { return tf[p.Last().Type()] }
func equateAny(x, y interface{}) bool { return x == y }

View File

@ -14,7 +14,7 @@ import (
"github.com/google/go-cmp/cmp/internal/function" "github.com/google/go-cmp/cmp/internal/function"
) )
// IgnoreFields returns an Option that ignores fields of the // IgnoreFields returns an [cmp.Option] that ignores fields of the
// given names on a single struct type. It respects the names of exported fields // given names on a single struct type. It respects the names of exported fields
// that are forwarded due to struct embedding. // that are forwarded due to struct embedding.
// The struct type is specified by passing in a value of that type. // The struct type is specified by passing in a value of that type.
@ -26,7 +26,7 @@ func IgnoreFields(typ interface{}, names ...string) cmp.Option {
return cmp.FilterPath(sf.filter, cmp.Ignore()) return cmp.FilterPath(sf.filter, cmp.Ignore())
} }
// IgnoreTypes returns an Option that ignores all values assignable to // IgnoreTypes returns an [cmp.Option] that ignores all values assignable to
// certain types, which are specified by passing in a value of each type. // certain types, which are specified by passing in a value of each type.
func IgnoreTypes(typs ...interface{}) cmp.Option { func IgnoreTypes(typs ...interface{}) cmp.Option {
tf := newTypeFilter(typs...) tf := newTypeFilter(typs...)
@ -59,10 +59,10 @@ func (tf typeFilter) filter(p cmp.Path) bool {
return false return false
} }
// IgnoreInterfaces returns an Option that ignores all values or references of // IgnoreInterfaces returns an [cmp.Option] that ignores all values or references of
// values assignable to certain interface types. These interfaces are specified // values assignable to certain interface types. These interfaces are specified
// by passing in an anonymous struct with the interface types embedded in it. // by passing in an anonymous struct with the interface types embedded in it.
// For example, to ignore sync.Locker, pass in struct{sync.Locker}{}. // For example, to ignore [sync.Locker], pass in struct{sync.Locker}{}.
func IgnoreInterfaces(ifaces interface{}) cmp.Option { func IgnoreInterfaces(ifaces interface{}) cmp.Option {
tf := newIfaceFilter(ifaces) tf := newIfaceFilter(ifaces)
return cmp.FilterPath(tf.filter, cmp.Ignore()) return cmp.FilterPath(tf.filter, cmp.Ignore())
@ -107,7 +107,7 @@ func (tf ifaceFilter) filter(p cmp.Path) bool {
return false return false
} }
// IgnoreUnexported returns an Option that only ignores the immediate unexported // IgnoreUnexported returns an [cmp.Option] that only ignores the immediate unexported
// fields of a struct, including anonymous fields of unexported types. // fields of a struct, including anonymous fields of unexported types.
// In particular, unexported fields within the struct's exported fields // In particular, unexported fields within the struct's exported fields
// of struct types, including anonymous fields, will not be ignored unless the // of struct types, including anonymous fields, will not be ignored unless the
@ -115,7 +115,7 @@ func (tf ifaceFilter) filter(p cmp.Path) bool {
// //
// Avoid ignoring unexported fields of a type which you do not control (i.e. a // Avoid ignoring unexported fields of a type which you do not control (i.e. a
// type from another repository), as changes to the implementation of such types // type from another repository), as changes to the implementation of such types
// may change how the comparison behaves. Prefer a custom Comparer instead. // may change how the comparison behaves. Prefer a custom [cmp.Comparer] instead.
func IgnoreUnexported(typs ...interface{}) cmp.Option { func IgnoreUnexported(typs ...interface{}) cmp.Option {
ux := newUnexportedFilter(typs...) ux := newUnexportedFilter(typs...)
return cmp.FilterPath(ux.filter, cmp.Ignore()) return cmp.FilterPath(ux.filter, cmp.Ignore())
@ -148,7 +148,7 @@ func isExported(id string) bool {
return unicode.IsUpper(r) return unicode.IsUpper(r)
} }
// IgnoreSliceElements returns an Option that ignores elements of []V. // IgnoreSliceElements returns an [cmp.Option] that ignores elements of []V.
// The discard function must be of the form "func(T) bool" which is used to // The discard function must be of the form "func(T) bool" which is used to
// ignore slice elements of type V, where V is assignable to T. // ignore slice elements of type V, where V is assignable to T.
// Elements are ignored if the function reports true. // Elements are ignored if the function reports true.
@ -176,7 +176,7 @@ func IgnoreSliceElements(discardFunc interface{}) cmp.Option {
}, cmp.Ignore()) }, cmp.Ignore())
} }
// IgnoreMapEntries returns an Option that ignores entries of map[K]V. // IgnoreMapEntries returns an [cmp.Option] that ignores entries of map[K]V.
// The discard function must be of the form "func(T, R) bool" which is used to // The discard function must be of the form "func(T, R) bool" which is used to
// ignore map entries of type K and V, where K and V are assignable to T and R. // ignore map entries of type K and V, where K and V are assignable to T and R.
// Entries are ignored if the function reports true. // Entries are ignored if the function reports true.

View File

@ -13,7 +13,7 @@ import (
"github.com/google/go-cmp/cmp/internal/function" "github.com/google/go-cmp/cmp/internal/function"
) )
// SortSlices returns a Transformer option that sorts all []V. // SortSlices returns a [cmp.Transformer] option that sorts all []V.
// The less function must be of the form "func(T, T) bool" which is used to // The less function must be of the form "func(T, T) bool" which is used to
// sort any slice with element type V that is assignable to T. // sort any slice with element type V that is assignable to T.
// //
@ -25,7 +25,7 @@ import (
// The less function does not have to be "total". That is, if !less(x, y) and // The less function does not have to be "total". That is, if !less(x, y) and
// !less(y, x) for two elements x and y, their relative order is maintained. // !less(y, x) for two elements x and y, their relative order is maintained.
// //
// SortSlices can be used in conjunction with EquateEmpty. // SortSlices can be used in conjunction with [EquateEmpty].
func SortSlices(lessFunc interface{}) cmp.Option { func SortSlices(lessFunc interface{}) cmp.Option {
vf := reflect.ValueOf(lessFunc) vf := reflect.ValueOf(lessFunc)
if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {
@ -82,13 +82,13 @@ func (ss sliceSorter) less(v reflect.Value, i, j int) bool {
return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool() return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool()
} }
// SortMaps returns a Transformer option that flattens map[K]V types to be a // SortMaps returns a [cmp.Transformer] option that flattens map[K]V types to be a
// sorted []struct{K, V}. The less function must be of the form // sorted []struct{K, V}. The less function must be of the form
// "func(T, T) bool" which is used to sort any map with key K that is // "func(T, T) bool" which is used to sort any map with key K that is
// assignable to T. // assignable to T.
// //
// Flattening the map into a slice has the property that cmp.Equal is able to // Flattening the map into a slice has the property that [cmp.Equal] is able to
// use Comparers on K or the K.Equal method if it exists. // use [cmp.Comparer] options on K or the K.Equal method if it exists.
// //
// The less function must be: // The less function must be:
// - Deterministic: less(x, y) == less(x, y) // - Deterministic: less(x, y) == less(x, y)
@ -96,7 +96,7 @@ func (ss sliceSorter) less(v reflect.Value, i, j int) bool {
// - Transitive: if !less(x, y) and !less(y, z), then !less(x, z) // - Transitive: if !less(x, y) and !less(y, z), then !less(x, z)
// - Total: if x != y, then either less(x, y) or less(y, x) // - Total: if x != y, then either less(x, y) or less(y, x)
// //
// SortMaps can be used in conjunction with EquateEmpty. // SortMaps can be used in conjunction with [EquateEmpty].
func SortMaps(lessFunc interface{}) cmp.Option { func SortMaps(lessFunc interface{}) cmp.Option {
vf := reflect.ValueOf(lessFunc) vf := reflect.ValueOf(lessFunc)
if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { if !function.IsType(vf.Type(), function.Less) || vf.IsNil() {

View File

@ -19,7 +19,7 @@ func (xf xformFilter) filter(p cmp.Path) bool {
return true return true
} }
// AcyclicTransformer returns a Transformer with a filter applied that ensures // AcyclicTransformer returns a [cmp.Transformer] with a filter applied that ensures
// that the transformer cannot be recursively applied upon its own output. // that the transformer cannot be recursively applied upon its own output.
// //
// An example use case is a transformer that splits a string by lines: // An example use case is a transformer that splits a string by lines:
@ -28,7 +28,7 @@ func (xf xformFilter) filter(p cmp.Path) bool {
// return strings.Split(s, "\n") // return strings.Split(s, "\n")
// }) // })
// //
// Had this been an unfiltered Transformer instead, this would result in an // Had this been an unfiltered [cmp.Transformer] instead, this would result in an
// infinite cycle converting a string to []string to [][]string and so on. // infinite cycle converting a string to []string to [][]string and so on.
func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option { func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option {
xf := xformFilter{cmp.Transformer(name, xformFunc)} xf := xformFilter{cmp.Transformer(name, xformFunc)}

View File

@ -5,7 +5,7 @@
// Package cmp determines equality of values. // Package cmp determines equality of values.
// //
// This package is intended to be a more powerful and safer alternative to // This package is intended to be a more powerful and safer alternative to
// reflect.DeepEqual for comparing whether two values are semantically equal. // [reflect.DeepEqual] for comparing whether two values are semantically equal.
// It is intended to only be used in tests, as performance is not a goal and // It is intended to only be used in tests, as performance is not a goal and
// it may panic if it cannot compare the values. Its propensity towards // it may panic if it cannot compare the values. Its propensity towards
// panicking means that its unsuitable for production environments where a // panicking means that its unsuitable for production environments where a
@ -18,16 +18,17 @@
// For example, an equality function may report floats as equal so long as // For example, an equality function may report floats as equal so long as
// they are within some tolerance of each other. // they are within some tolerance of each other.
// //
// - Types with an Equal method may use that method to determine equality. // - Types with an Equal method (e.g., [time.Time.Equal]) may use that method
// This allows package authors to determine the equality operation // to determine equality. This allows package authors to determine
// for the types that they define. // the equality operation for the types that they define.
// //
// - If no custom equality functions are used and no Equal method is defined, // - If no custom equality functions are used and no Equal method is defined,
// equality is determined by recursively comparing the primitive kinds on // equality is determined by recursively comparing the primitive kinds on
// both values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, // both values, much like [reflect.DeepEqual]. Unlike [reflect.DeepEqual],
// unexported fields are not compared by default; they result in panics // unexported fields are not compared by default; they result in panics
// unless suppressed by using an Ignore option (see cmpopts.IgnoreUnexported) // unless suppressed by using an [Ignore] option
// or explicitly compared using the Exporter option. // (see [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported])
// or explicitly compared using the [Exporter] option.
package cmp package cmp
import ( import (
@ -45,14 +46,14 @@ import (
// Equal reports whether x and y are equal by recursively applying the // Equal reports whether x and y are equal by recursively applying the
// following rules in the given order to x and y and all of their sub-values: // following rules in the given order to x and y and all of their sub-values:
// //
// - Let S be the set of all Ignore, Transformer, and Comparer options that // - Let S be the set of all [Ignore], [Transformer], and [Comparer] options that
// remain after applying all path filters, value filters, and type filters. // remain after applying all path filters, value filters, and type filters.
// If at least one Ignore exists in S, then the comparison is ignored. // If at least one [Ignore] exists in S, then the comparison is ignored.
// If the number of Transformer and Comparer options in S is non-zero, // If the number of [Transformer] and [Comparer] options in S is non-zero,
// then Equal panics because it is ambiguous which option to use. // then Equal panics because it is ambiguous which option to use.
// If S contains a single Transformer, then use that to transform // If S contains a single [Transformer], then use that to transform
// the current values and recursively call Equal on the output values. // the current values and recursively call Equal on the output values.
// If S contains a single Comparer, then use that to compare the current values. // If S contains a single [Comparer], then use that to compare the current values.
// Otherwise, evaluation proceeds to the next rule. // Otherwise, evaluation proceeds to the next rule.
// //
// - If the values have an Equal method of the form "(T) Equal(T) bool" or // - If the values have an Equal method of the form "(T) Equal(T) bool" or
@ -66,21 +67,22 @@ import (
// Functions are only equal if they are both nil, otherwise they are unequal. // Functions are only equal if they are both nil, otherwise they are unequal.
// //
// Structs are equal if recursively calling Equal on all fields report equal. // Structs are equal if recursively calling Equal on all fields report equal.
// If a struct contains unexported fields, Equal panics unless an Ignore option // If a struct contains unexported fields, Equal panics unless an [Ignore] option
// (e.g., cmpopts.IgnoreUnexported) ignores that field or the Exporter option // (e.g., [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]) ignores that field
// explicitly permits comparing the unexported field. // or the [Exporter] option explicitly permits comparing the unexported field.
// //
// Slices are equal if they are both nil or both non-nil, where recursively // Slices are equal if they are both nil or both non-nil, where recursively
// calling Equal on all non-ignored slice or array elements report equal. // calling Equal on all non-ignored slice or array elements report equal.
// Empty non-nil slices and nil slices are not equal; to equate empty slices, // Empty non-nil slices and nil slices are not equal; to equate empty slices,
// consider using cmpopts.EquateEmpty. // consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty].
// //
// Maps are equal if they are both nil or both non-nil, where recursively // Maps are equal if they are both nil or both non-nil, where recursively
// calling Equal on all non-ignored map entries report equal. // calling Equal on all non-ignored map entries report equal.
// Map keys are equal according to the == operator. // Map keys are equal according to the == operator.
// To use custom comparisons for map keys, consider using cmpopts.SortMaps. // To use custom comparisons for map keys, consider using
// [github.com/google/go-cmp/cmp/cmpopts.SortMaps].
// Empty non-nil maps and nil maps are not equal; to equate empty maps, // Empty non-nil maps and nil maps are not equal; to equate empty maps,
// consider using cmpopts.EquateEmpty. // consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty].
// //
// Pointers and interfaces are equal if they are both nil or both non-nil, // Pointers and interfaces are equal if they are both nil or both non-nil,
// where they have the same underlying concrete type and recursively // where they have the same underlying concrete type and recursively

View File

@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !purego
// +build !purego
package cmp package cmp
import ( import (
@ -12,8 +9,6 @@ import (
"unsafe" "unsafe"
) )
const supportExporters = true
// retrieveUnexportedField uses unsafe to forcibly retrieve any field from // retrieveUnexportedField uses unsafe to forcibly retrieve any field from
// a struct such that the value has read-write permissions. // a struct such that the value has read-write permissions.
// //

View File

@ -1,16 +0,0 @@
// Copyright 2017, 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 purego
// +build purego
package cmp
import "reflect"
const supportExporters = false
func retrieveUnexportedField(reflect.Value, reflect.StructField, bool) reflect.Value {
panic("no support for forcibly accessing unexported fields")
}

View File

@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !purego
// +build !purego
package value package value
import ( import (

View File

@ -1,34 +0,0 @@
// 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.
//go:build purego
// +build purego
package value
import "reflect"
// Pointer is an opaque typed pointer and is guaranteed to be comparable.
type Pointer struct {
p uintptr
t reflect.Type
}
// PointerOf returns a Pointer from v, which must be a
// reflect.Ptr, reflect.Slice, or reflect.Map.
func PointerOf(v reflect.Value) Pointer {
// NOTE: Storing a pointer as an uintptr is technically incorrect as it
// assumes that the GC implementation does not use a moving collector.
return Pointer{v.Pointer(), v.Type()}
}
// IsNil reports whether the pointer is nil.
func (p Pointer) IsNil() bool {
return p.p == 0
}
// Uintptr returns the pointer as a uintptr.
func (p Pointer) Uintptr() uintptr {
return p.p
}

View File

@ -13,15 +13,15 @@ import (
"github.com/google/go-cmp/cmp/internal/function" "github.com/google/go-cmp/cmp/internal/function"
) )
// Option configures for specific behavior of Equal and Diff. In particular, // Option configures for specific behavior of [Equal] and [Diff]. In particular,
// the fundamental Option functions (Ignore, Transformer, and Comparer), // the fundamental Option functions ([Ignore], [Transformer], and [Comparer]),
// configure how equality is determined. // configure how equality is determined.
// //
// The fundamental options may be composed with filters (FilterPath and // The fundamental options may be composed with filters ([FilterPath] and
// FilterValues) to control the scope over which they are applied. // [FilterValues]) to control the scope over which they are applied.
// //
// The cmp/cmpopts package provides helper functions for creating options that // The [github.com/google/go-cmp/cmp/cmpopts] package provides helper functions
// may be used with Equal and Diff. // for creating options that may be used with [Equal] and [Diff].
type Option interface { type Option interface {
// filter applies all filters and returns the option that remains. // filter applies all filters and returns the option that remains.
// Each option may only read s.curPath and call s.callTTBFunc. // Each option may only read s.curPath and call s.callTTBFunc.
@ -56,9 +56,9 @@ type core struct{}
func (core) isCore() {} func (core) isCore() {}
// Options is a list of Option values that also satisfies the Option interface. // Options is a list of [Option] values that also satisfies the [Option] interface.
// Helper comparison packages may return an Options value when packing multiple // Helper comparison packages may return an Options value when packing multiple
// Option values into a single Option. When this package processes an Options, // [Option] values into a single [Option]. When this package processes an Options,
// it will be implicitly expanded into a flat list. // it will be implicitly expanded into a flat list.
// //
// Applying a filter on an Options is equivalent to applying that same filter // Applying a filter on an Options is equivalent to applying that same filter
@ -105,16 +105,16 @@ func (opts Options) String() string {
return fmt.Sprintf("Options{%s}", strings.Join(ss, ", ")) return fmt.Sprintf("Options{%s}", strings.Join(ss, ", "))
} }
// FilterPath returns a new Option where opt is only evaluated if filter f // FilterPath returns a new [Option] where opt is only evaluated if filter f
// returns true for the current Path in the value tree. // returns true for the current [Path] in the value tree.
// //
// This filter is called even if a slice element or map entry is missing and // This filter is called even if a slice element or map entry is missing and
// provides an opportunity to ignore such cases. The filter function must be // provides an opportunity to ignore such cases. The filter function must be
// symmetric such that the filter result is identical regardless of whether the // symmetric such that the filter result is identical regardless of whether the
// missing value is from x or y. // missing value is from x or y.
// //
// The option passed in may be an Ignore, Transformer, Comparer, Options, or // The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or
// a previously filtered Option. // a previously filtered [Option].
func FilterPath(f func(Path) bool, opt Option) Option { func FilterPath(f func(Path) bool, opt Option) Option {
if f == nil { if f == nil {
panic("invalid path filter function") panic("invalid path filter function")
@ -142,7 +142,7 @@ func (f pathFilter) String() string {
return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt) return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt)
} }
// FilterValues returns a new Option where opt is only evaluated if filter f, // FilterValues returns a new [Option] where opt is only evaluated if filter f,
// which is a function of the form "func(T, T) bool", returns true for the // which is a function of the form "func(T, T) bool", returns true for the
// current pair of values being compared. If either value is invalid or // current pair of values being compared. If either value is invalid or
// the type of the values is not assignable to T, then this filter implicitly // the type of the values is not assignable to T, then this filter implicitly
@ -154,8 +154,8 @@ func (f pathFilter) String() string {
// If T is an interface, it is possible that f is called with two values with // If T is an interface, it is possible that f is called with two values with
// different concrete types that both implement T. // different concrete types that both implement T.
// //
// The option passed in may be an Ignore, Transformer, Comparer, Options, or // The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or
// a previously filtered Option. // a previously filtered [Option].
func FilterValues(f interface{}, opt Option) Option { func FilterValues(f interface{}, opt Option) Option {
v := reflect.ValueOf(f) v := reflect.ValueOf(f)
if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() { if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() {
@ -192,9 +192,9 @@ func (f valuesFilter) String() string {
return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt) return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt)
} }
// Ignore is an Option that causes all comparisons to be ignored. // Ignore is an [Option] that causes all comparisons to be ignored.
// This value is intended to be combined with FilterPath or FilterValues. // This value is intended to be combined with [FilterPath] or [FilterValues].
// It is an error to pass an unfiltered Ignore option to Equal. // It is an error to pass an unfiltered Ignore option to [Equal].
func Ignore() Option { return ignore{} } func Ignore() Option { return ignore{} }
type ignore struct{ core } type ignore struct{ core }
@ -234,6 +234,8 @@ func (validator) apply(s *state, vx, vy reflect.Value) {
name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType
if _, ok := reflect.New(t).Interface().(error); ok { if _, ok := reflect.New(t).Interface().(error); ok {
help = "consider using cmpopts.EquateErrors to compare error values" help = "consider using cmpopts.EquateErrors to compare error values"
} else if t.Comparable() {
help = "consider using cmpopts.EquateComparable to compare comparable Go types"
} }
} else { } else {
// Unnamed type with unexported fields. Derive PkgPath from field. // Unnamed type with unexported fields. Derive PkgPath from field.
@ -254,7 +256,7 @@ const identRx = `[_\p{L}][_\p{L}\p{N}]*`
var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`) var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`)
// Transformer returns an Option that applies a transformation function that // Transformer returns an [Option] that applies a transformation function that
// converts values of a certain type into that of another. // converts values of a certain type into that of another.
// //
// The transformer f must be a function "func(T) R" that converts values of // The transformer f must be a function "func(T) R" that converts values of
@ -265,13 +267,14 @@ var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`)
// same transform to the output of itself (e.g., in the case where the // same transform to the output of itself (e.g., in the case where the
// input and output types are the same), an implicit filter is added such that // input and output types are the same), an implicit filter is added such that
// a transformer is applicable only if that exact transformer is not already // a transformer is applicable only if that exact transformer is not already
// in the tail of the Path since the last non-Transform step. // in the tail of the [Path] since the last non-[Transform] step.
// For situations where the implicit filter is still insufficient, // For situations where the implicit filter is still insufficient,
// consider using cmpopts.AcyclicTransformer, which adds a filter // consider using [github.com/google/go-cmp/cmp/cmpopts.AcyclicTransformer],
// to prevent the transformer from being recursively applied upon itself. // which adds a filter to prevent the transformer from
// being recursively applied upon itself.
// //
// The name is a user provided label that is used as the Transform.Name in the // The name is a user provided label that is used as the [Transform.Name] in the
// transformation PathStep (and eventually shown in the Diff output). // transformation [PathStep] (and eventually shown in the [Diff] output).
// The name must be a valid identifier or qualified identifier in Go syntax. // The name must be a valid identifier or qualified identifier in Go syntax.
// If empty, an arbitrary name is used. // If empty, an arbitrary name is used.
func Transformer(name string, f interface{}) Option { func Transformer(name string, f interface{}) Option {
@ -329,7 +332,7 @@ func (tr transformer) String() string {
return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc)) return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc))
} }
// Comparer returns an Option that determines whether two values are equal // Comparer returns an [Option] that determines whether two values are equal
// to each other. // to each other.
// //
// The comparer f must be a function "func(T, T) bool" and is implicitly // The comparer f must be a function "func(T, T) bool" and is implicitly
@ -377,35 +380,32 @@ func (cm comparer) String() string {
return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc)) return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc))
} }
// Exporter returns an Option that specifies whether Equal is allowed to // Exporter returns an [Option] that specifies whether [Equal] is allowed to
// introspect into the unexported fields of certain struct types. // introspect into the unexported fields of certain struct types.
// //
// Users of this option must understand that comparing on unexported fields // Users of this option must understand that comparing on unexported fields
// from external packages is not safe since changes in the internal // from external packages is not safe since changes in the internal
// implementation of some external package may cause the result of Equal // implementation of some external package may cause the result of [Equal]
// to unexpectedly change. However, it may be valid to use this option on types // to unexpectedly change. However, it may be valid to use this option on types
// defined in an internal package where the semantic meaning of an unexported // defined in an internal package where the semantic meaning of an unexported
// field is in the control of the user. // field is in the control of the user.
// //
// In many cases, a custom Comparer should be used instead that defines // In many cases, a custom [Comparer] should be used instead that defines
// equality as a function of the public API of a type rather than the underlying // equality as a function of the public API of a type rather than the underlying
// unexported implementation. // unexported implementation.
// //
// For example, the reflect.Type documentation defines equality to be determined // For example, the [reflect.Type] documentation defines equality to be determined
// by the == operator on the interface (essentially performing a shallow pointer // by the == operator on the interface (essentially performing a shallow pointer
// comparison) and most attempts to compare *regexp.Regexp types are interested // comparison) and most attempts to compare *[regexp.Regexp] types are interested
// in only checking that the regular expression strings are equal. // in only checking that the regular expression strings are equal.
// Both of these are accomplished using Comparers: // Both of these are accomplished using [Comparer] options:
// //
// Comparer(func(x, y reflect.Type) bool { return x == y }) // Comparer(func(x, y reflect.Type) bool { return x == y })
// Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() }) // Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() })
// //
// In other cases, the cmpopts.IgnoreUnexported option can be used to ignore // In other cases, the [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]
// all unexported fields on specified struct types. // option can be used to ignore all unexported fields on specified struct types.
func Exporter(f func(reflect.Type) bool) Option { func Exporter(f func(reflect.Type) bool) Option {
if !supportExporters {
panic("Exporter is not supported on purego builds")
}
return exporter(f) return exporter(f)
} }
@ -415,10 +415,10 @@ func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableO
panic("not implemented") panic("not implemented")
} }
// AllowUnexported returns an Options that allows Equal to forcibly introspect // AllowUnexported returns an [Option] that allows [Equal] to forcibly introspect
// unexported fields of the specified struct types. // unexported fields of the specified struct types.
// //
// See Exporter for the proper use of this option. // See [Exporter] for the proper use of this option.
func AllowUnexported(types ...interface{}) Option { func AllowUnexported(types ...interface{}) Option {
m := make(map[reflect.Type]bool) m := make(map[reflect.Type]bool)
for _, typ := range types { for _, typ := range types {
@ -432,7 +432,7 @@ func AllowUnexported(types ...interface{}) Option {
} }
// Result represents the comparison result for a single node and // Result represents the comparison result for a single node and
// is provided by cmp when calling Report (see Reporter). // is provided by cmp when calling Report (see [Reporter]).
type Result struct { type Result struct {
_ [0]func() // Make Result incomparable _ [0]func() // Make Result incomparable
flags resultFlags flags resultFlags
@ -445,7 +445,7 @@ func (r Result) Equal() bool {
} }
// ByIgnore reports whether the node is equal because it was ignored. // ByIgnore reports whether the node is equal because it was ignored.
// This never reports true if Equal reports false. // This never reports true if [Result.Equal] reports false.
func (r Result) ByIgnore() bool { func (r Result) ByIgnore() bool {
return r.flags&reportByIgnore != 0 return r.flags&reportByIgnore != 0
} }
@ -455,7 +455,7 @@ func (r Result) ByMethod() bool {
return r.flags&reportByMethod != 0 return r.flags&reportByMethod != 0
} }
// ByFunc reports whether a Comparer function determined equality. // ByFunc reports whether a [Comparer] function determined equality.
func (r Result) ByFunc() bool { func (r Result) ByFunc() bool {
return r.flags&reportByFunc != 0 return r.flags&reportByFunc != 0
} }
@ -478,7 +478,7 @@ const (
reportByCycle reportByCycle
) )
// Reporter is an Option that can be passed to Equal. When Equal traverses // Reporter is an [Option] that can be passed to [Equal]. When [Equal] traverses
// the value trees, it calls PushStep as it descends into each node in the // the value trees, it calls PushStep as it descends into each node in the
// tree and PopStep as it ascend out of the node. The leaves of the tree are // tree and PopStep as it ascend out of the node. The leaves of the tree are
// either compared (determined to be equal or not equal) or ignored and reported // either compared (determined to be equal or not equal) or ignored and reported

View File

@ -14,9 +14,9 @@ import (
"github.com/google/go-cmp/cmp/internal/value" "github.com/google/go-cmp/cmp/internal/value"
) )
// Path is a list of PathSteps describing the sequence of operations to get // Path is a list of [PathStep] describing the sequence of operations to get
// from some root type to the current position in the value tree. // from some root type to the current position in the value tree.
// The first Path element is always an operation-less PathStep that exists // The first Path element is always an operation-less [PathStep] that exists
// simply to identify the initial type. // simply to identify the initial type.
// //
// When traversing structs with embedded structs, the embedded struct will // When traversing structs with embedded structs, the embedded struct will
@ -29,8 +29,13 @@ type Path []PathStep
// a value's tree structure. Users of this package never need to implement // a value's tree structure. Users of this package never need to implement
// these types as values of this type will be returned by this package. // these types as values of this type will be returned by this package.
// //
// Implementations of this interface are // Implementations of this interface:
// StructField, SliceIndex, MapIndex, Indirect, TypeAssertion, and Transform. // - [StructField]
// - [SliceIndex]
// - [MapIndex]
// - [Indirect]
// - [TypeAssertion]
// - [Transform]
type PathStep interface { type PathStep interface {
String() string String() string
@ -70,8 +75,9 @@ func (pa *Path) pop() {
*pa = (*pa)[:len(*pa)-1] *pa = (*pa)[:len(*pa)-1]
} }
// Last returns the last PathStep in the Path. // Last returns the last [PathStep] in the Path.
// If the path is empty, this returns a non-nil PathStep that reports a nil Type. // If the path is empty, this returns a non-nil [PathStep]
// that reports a nil [PathStep.Type].
func (pa Path) Last() PathStep { func (pa Path) Last() PathStep {
return pa.Index(-1) return pa.Index(-1)
} }
@ -79,7 +85,8 @@ func (pa Path) Last() PathStep {
// Index returns the ith step in the Path and supports negative indexing. // Index returns the ith step in the Path and supports negative indexing.
// A negative index starts counting from the tail of the Path such that -1 // A negative index starts counting from the tail of the Path such that -1
// refers to the last step, -2 refers to the second-to-last step, and so on. // refers to the last step, -2 refers to the second-to-last step, and so on.
// If index is invalid, this returns a non-nil PathStep that reports a nil Type. // If index is invalid, this returns a non-nil [PathStep]
// that reports a nil [PathStep.Type].
func (pa Path) Index(i int) PathStep { func (pa Path) Index(i int) PathStep {
if i < 0 { if i < 0 {
i = len(pa) + i i = len(pa) + i
@ -168,7 +175,8 @@ func (ps pathStep) String() string {
return fmt.Sprintf("{%s}", s) return fmt.Sprintf("{%s}", s)
} }
// StructField represents a struct field access on a field called Name. // StructField is a [PathStep] that represents a struct field access
// on a field called [StructField.Name].
type StructField struct{ *structField } type StructField struct{ *structField }
type structField struct { type structField struct {
pathStep pathStep
@ -204,10 +212,11 @@ func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) }
func (sf StructField) Name() string { return sf.name } func (sf StructField) Name() string { return sf.name }
// Index is the index of the field in the parent struct type. // Index is the index of the field in the parent struct type.
// See reflect.Type.Field. // See [reflect.Type.Field].
func (sf StructField) Index() int { return sf.idx } func (sf StructField) Index() int { return sf.idx }
// SliceIndex is an index operation on a slice or array at some index Key. // SliceIndex is a [PathStep] that represents an index operation on
// a slice or array at some index [SliceIndex.Key].
type SliceIndex struct{ *sliceIndex } type SliceIndex struct{ *sliceIndex }
type sliceIndex struct { type sliceIndex struct {
pathStep pathStep
@ -247,12 +256,12 @@ func (si SliceIndex) Key() int {
// all of the indexes to be shifted. If an index is -1, then that // all of the indexes to be shifted. If an index is -1, then that
// indicates that the element does not exist in the associated slice. // indicates that the element does not exist in the associated slice.
// //
// Key is guaranteed to return -1 if and only if the indexes returned // [SliceIndex.Key] is guaranteed to return -1 if and only if the indexes
// by SplitKeys are not the same. SplitKeys will never return -1 for // returned by SplitKeys are not the same. SplitKeys will never return -1 for
// both indexes. // both indexes.
func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey } func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey }
// MapIndex is an index operation on a map at some index Key. // MapIndex is a [PathStep] that represents an index operation on a map at some index Key.
type MapIndex struct{ *mapIndex } type MapIndex struct{ *mapIndex }
type mapIndex struct { type mapIndex struct {
pathStep pathStep
@ -266,7 +275,7 @@ func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]",
// Key is the value of the map key. // Key is the value of the map key.
func (mi MapIndex) Key() reflect.Value { return mi.key } func (mi MapIndex) Key() reflect.Value { return mi.key }
// Indirect represents pointer indirection on the parent type. // Indirect is a [PathStep] that represents pointer indirection on the parent type.
type Indirect struct{ *indirect } type Indirect struct{ *indirect }
type indirect struct { type indirect struct {
pathStep pathStep
@ -276,7 +285,7 @@ func (in Indirect) Type() reflect.Type { return in.typ }
func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy } func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy }
func (in Indirect) String() string { return "*" } func (in Indirect) String() string { return "*" }
// TypeAssertion represents a type assertion on an interface. // TypeAssertion is a [PathStep] that represents a type assertion on an interface.
type TypeAssertion struct{ *typeAssertion } type TypeAssertion struct{ *typeAssertion }
type typeAssertion struct { type typeAssertion struct {
pathStep pathStep
@ -286,7 +295,8 @@ func (ta TypeAssertion) Type() reflect.Type { return ta.typ }
func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy } func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy }
func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", value.TypeString(ta.typ, false)) } func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", value.TypeString(ta.typ, false)) }
// Transform is a transformation from the parent type to the current type. // Transform is a [PathStep] that represents a transformation
// from the parent type to the current type.
type Transform struct{ *transform } type Transform struct{ *transform }
type transform struct { type transform struct {
pathStep pathStep
@ -297,13 +307,13 @@ func (tf Transform) Type() reflect.Type { return tf.typ }
func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy } func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy }
func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) }
// Name is the name of the Transformer. // Name is the name of the [Transformer].
func (tf Transform) Name() string { return tf.trans.name } func (tf Transform) Name() string { return tf.trans.name }
// Func is the function pointer to the transformer function. // Func is the function pointer to the transformer function.
func (tf Transform) Func() reflect.Value { return tf.trans.fnc } func (tf Transform) Func() reflect.Value { return tf.trans.fnc }
// Option returns the originally constructed Transformer option. // Option returns the originally constructed [Transformer] option.
// The == operator can be used to detect the exact option used. // The == operator can be used to detect the exact option used.
func (tf Transform) Option() Option { return tf.trans } func (tf Transform) Option() Option { return tf.trans }

View File

@ -199,7 +199,7 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind,
break break
} }
sf := t.Field(i) sf := t.Field(i)
if supportExporters && !isExported(sf.Name) { if !isExported(sf.Name) {
vv = retrieveUnexportedField(v, sf, true) vv = retrieveUnexportedField(v, sf, true)
} }
s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs) s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs)

View File

@ -140,7 +140,7 @@ func Compare(v, w string) int {
// Max canonicalizes its arguments and then returns the version string // Max canonicalizes its arguments and then returns the version string
// that compares greater. // that compares greater.
// //
// Deprecated: use Compare instead. In most cases, returning a canonicalized // Deprecated: use [Compare] instead. In most cases, returning a canonicalized
// version is not expected or desired. // version is not expected or desired.
func Max(v, w string) string { func Max(v, w string) string {
v = Canonical(v) v = Canonical(v)
@ -151,7 +151,7 @@ func Max(v, w string) string {
return w return w
} }
// ByVersion implements sort.Interface for sorting semantic version strings. // ByVersion implements [sort.Interface] for sorting semantic version strings.
type ByVersion []string type ByVersion []string
func (vs ByVersion) Len() int { return len(vs) } func (vs ByVersion) Len() int { return len(vs) }
@ -164,7 +164,7 @@ func (vs ByVersion) Less(i, j int) bool {
return vs[i] < vs[j] return vs[i] < vs[j]
} }
// Sort sorts a list of semantic version strings using ByVersion. // Sort sorts a list of semantic version strings using [ByVersion].
func Sort(list []string) { func Sort(list []string) {
sort.Sort(ByVersion(list)) sort.Sort(ByVersion(list))
} }

View File

@ -20,42 +20,45 @@ import (
// TODO: Benchmark to determine if the pools are necessary. The GC may have // TODO: Benchmark to determine if the pools are necessary. The GC may have
// improved enough that we can instead allocate chunks like this: // improved enough that we can instead allocate chunks like this:
// make([]byte, max(16<<10, expectedBytesRemaining)) // make([]byte, max(16<<10, expectedBytesRemaining))
var ( var dataChunkPools = [...]sync.Pool{
dataChunkSizeClasses = []int{ {New: func() interface{} { return new([1 << 10]byte) }},
1 << 10, {New: func() interface{} { return new([2 << 10]byte) }},
2 << 10, {New: func() interface{} { return new([4 << 10]byte) }},
4 << 10, {New: func() interface{} { return new([8 << 10]byte) }},
8 << 10, {New: func() interface{} { return new([16 << 10]byte) }},
16 << 10,
} }
dataChunkPools = [...]sync.Pool{
{New: func() interface{} { return make([]byte, 1<<10) }},
{New: func() interface{} { return make([]byte, 2<<10) }},
{New: func() interface{} { return make([]byte, 4<<10) }},
{New: func() interface{} { return make([]byte, 8<<10) }},
{New: func() interface{} { return make([]byte, 16<<10) }},
}
)
func getDataBufferChunk(size int64) []byte { func getDataBufferChunk(size int64) []byte {
i := 0 switch {
for ; i < len(dataChunkSizeClasses)-1; i++ { case size <= 1<<10:
if size <= int64(dataChunkSizeClasses[i]) { return dataChunkPools[0].Get().(*[1 << 10]byte)[:]
break case size <= 2<<10:
return dataChunkPools[1].Get().(*[2 << 10]byte)[:]
case size <= 4<<10:
return dataChunkPools[2].Get().(*[4 << 10]byte)[:]
case size <= 8<<10:
return dataChunkPools[3].Get().(*[8 << 10]byte)[:]
default:
return dataChunkPools[4].Get().(*[16 << 10]byte)[:]
} }
} }
return dataChunkPools[i].Get().([]byte)
}
func putDataBufferChunk(p []byte) { func putDataBufferChunk(p []byte) {
for i, n := range dataChunkSizeClasses { switch len(p) {
if len(p) == n { case 1 << 10:
dataChunkPools[i].Put(p) dataChunkPools[0].Put((*[1 << 10]byte)(p))
return case 2 << 10:
} dataChunkPools[1].Put((*[2 << 10]byte)(p))
} case 4 << 10:
dataChunkPools[2].Put((*[4 << 10]byte)(p))
case 8 << 10:
dataChunkPools[3].Put((*[8 << 10]byte)(p))
case 16 << 10:
dataChunkPools[4].Put((*[16 << 10]byte)(p))
default:
panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) panic(fmt.Sprintf("unexpected buffer len=%v", len(p)))
} }
}
// dataBuffer is an io.ReadWriter backed by a list of data chunks. // dataBuffer is an io.ReadWriter backed by a list of data chunks.
// Each dataBuffer is used to read DATA frames on a single stream. // Each dataBuffer is used to read DATA frames on a single stream.

View File

@ -1,30 +0,0 @@
// 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.
//go:build go1.11
// +build go1.11
package http2
import (
"net/http/httptrace"
"net/textproto"
)
func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool {
return trace != nil && trace.WroteHeaderField != nil
}
func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {
if trace != nil && trace.WroteHeaderField != nil {
trace.WroteHeaderField(k, []string{v})
}
}
func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
if trace != nil {
return trace.Got1xxResponse
}
return nil
}

View File

@ -1,27 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.15
// +build go1.15
package http2
import (
"context"
"crypto/tls"
)
// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS
// connection.
func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) {
dialer := &tls.Dialer{
Config: cfg,
}
cn, err := dialer.DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed
return tlsCn, nil
}

View File

@ -1,17 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
package http2
import (
"crypto/tls"
"net"
)
func tlsUnderlyingConn(tc *tls.Conn) net.Conn {
return tc.NetConn()
}

View File

@ -1,21 +0,0 @@
// 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.
//go:build !go1.11
// +build !go1.11
package http2
import (
"net/http/httptrace"
"net/textproto"
)
func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { return false }
func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {}
func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
return nil
}

View File

@ -1,31 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.15
// +build !go1.15
package http2
import (
"context"
"crypto/tls"
)
// dialTLSWithContext opens a TLS connection.
func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) {
cn, err := tls.Dial(network, addr, cfg)
if err != nil {
return nil, err
}
if err := cn.Handshake(); err != nil {
return nil, err
}
if cfg.InsecureSkipVerify {
return cn, nil
}
if err := cn.VerifyHostname(cfg.ServerName); err != nil {
return nil, err
}
return cn, nil
}

View File

@ -1,17 +0,0 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
package http2
import (
"crypto/tls"
"net"
)
func tlsUnderlyingConn(tc *tls.Conn) net.Conn {
return nil
}

View File

@ -2549,7 +2549,6 @@ type responseWriterState struct {
wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet. wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet.
sentHeader bool // have we sent the header frame? sentHeader bool // have we sent the header frame?
handlerDone bool // handler has finished handlerDone bool // handler has finished
dirty bool // a Write failed; don't reuse this responseWriterState
sentContentLen int64 // non-zero if handler set a Content-Length header sentContentLen int64 // non-zero if handler set a Content-Length header
wroteBytes int64 wroteBytes int64
@ -2669,7 +2668,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
date: date, date: date,
}) })
if err != nil { if err != nil {
rws.dirty = true
return 0, err return 0, err
} }
if endStream { if endStream {
@ -2690,7 +2688,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
if len(p) > 0 || endStream { if len(p) > 0 || endStream {
// only send a 0 byte DATA frame if we're ending the stream. // only send a 0 byte DATA frame if we're ending the stream.
if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil { if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil {
rws.dirty = true
return 0, err return 0, err
} }
} }
@ -2702,9 +2699,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
trailers: rws.trailers, trailers: rws.trailers,
endStream: true, endStream: true,
}) })
if err != nil {
rws.dirty = true
}
return len(p), err return len(p), err
} }
return len(p), nil return len(p), nil
@ -2920,14 +2914,12 @@ func (rws *responseWriterState) writeHeader(code int) {
h.Del("Transfer-Encoding") h.Del("Transfer-Encoding")
} }
if rws.conn.writeHeaders(rws.stream, &writeResHeaders{ rws.conn.writeHeaders(rws.stream, &writeResHeaders{
streamID: rws.stream.id, streamID: rws.stream.id,
httpResCode: code, httpResCode: code,
h: h, h: h,
endStream: rws.handlerDone && !rws.hasTrailers(), endStream: rws.handlerDone && !rws.hasTrailers(),
}) != nil { })
rws.dirty = true
}
return return
} }
@ -2992,20 +2984,11 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int,
func (w *responseWriter) handlerDone() { func (w *responseWriter) handlerDone() {
rws := w.rws rws := w.rws
dirty := rws.dirty
rws.handlerDone = true rws.handlerDone = true
w.Flush() w.Flush()
w.rws = nil w.rws = nil
if !dirty {
// Only recycle the pool if all prior Write calls to
// the serverConn goroutine completed successfully. If
// they returned earlier due to resets from the peer
// there might still be write goroutines outstanding
// from the serverConn referencing the rws memory. See
// issue 20704.
responseWriterStatePool.Put(rws) responseWriterStatePool.Put(rws)
} }
}
// Push errors. // Push errors.
var ( var (
@ -3187,6 +3170,7 @@ func (sc *serverConn) startPush(msg *startPushRequest) {
panic(fmt.Sprintf("newWriterAndRequestNoBody(%+v): %v", msg.url, err)) panic(fmt.Sprintf("newWriterAndRequestNoBody(%+v): %v", msg.url, err))
} }
sc.curHandlers++
go sc.runHandler(rw, req, sc.handler.ServeHTTP) go sc.runHandler(rw, req, sc.handler.ServeHTTP)
return promisedID, nil return promisedID, nil
} }

View File

@ -1018,7 +1018,7 @@ func (cc *ClientConn) forceCloseConn() {
if !ok { if !ok {
return return
} }
if nc := tlsUnderlyingConn(tc); nc != nil { if nc := tc.NetConn(); nc != nil {
nc.Close() nc.Close()
} }
} }
@ -3201,3 +3201,34 @@ func traceFirstResponseByte(trace *httptrace.ClientTrace) {
trace.GotFirstResponseByte() trace.GotFirstResponseByte()
} }
} }
func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool {
return trace != nil && trace.WroteHeaderField != nil
}
func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {
if trace != nil && trace.WroteHeaderField != nil {
trace.WroteHeaderField(k, []string{v})
}
}
func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error {
if trace != nil {
return trace.Got1xxResponse
}
return nil
}
// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS
// connection.
func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) {
dialer := &tls.Dialer{
Config: cfg,
}
cn, err := dialer.DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed
return tlsCn, nil
}

View File

@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.18 //go:build go1.18
// +build go1.18
package idna package idna

View File

@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.10 //go:build go1.10
// +build go1.10
// Package idna implements IDNA2008 using the compatibility processing // Package idna implements IDNA2008 using the compatibility processing
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to // defined by UTS (Unicode Technical Standard) #46, which defines a standard to

View File

@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.10 //go:build !go1.10
// +build !go1.10
// Package idna implements IDNA2008 using the compatibility processing // Package idna implements IDNA2008 using the compatibility processing
// defined by UTS (Unicode Technical Standard) #46, which defines a standard to // defined by UTS (Unicode Technical Standard) #46, which defines a standard to

View File

@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.18 //go:build !go1.18
// +build !go1.18
package idna package idna

View File

@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build go1.10 && !go1.13 //go:build go1.10 && !go1.13
// +build go1.10,!go1.13
package idna package idna

View File

@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build go1.13 && !go1.14 //go:build go1.13 && !go1.14
// +build go1.13,!go1.14
package idna package idna

View File

@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build go1.14 && !go1.16 //go:build go1.14 && !go1.16
// +build go1.14,!go1.16
package idna package idna

View File

@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build go1.16 && !go1.21 //go:build go1.16 && !go1.21
// +build go1.16,!go1.21
package idna package idna

View File

@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build go1.21 //go:build go1.21
// +build go1.21
package idna package idna

View File

@ -1,7 +1,6 @@
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
//go:build !go1.10 //go:build !go1.10
// +build !go1.10
package idna package idna

View File

@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.16 //go:build !go1.16
// +build !go1.16
package idna package idna

View File

@ -5,7 +5,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.16 //go:build go1.16
// +build go1.16
package idna package idna

View File

@ -4,6 +4,9 @@
// Package errgroup provides synchronization, error propagation, and Context // Package errgroup provides synchronization, error propagation, and Context
// cancelation for groups of goroutines working on subtasks of a common task. // cancelation for groups of goroutines working on subtasks of a common task.
//
// [errgroup.Group] is related to [sync.WaitGroup] but adds handling of tasks
// returning errors.
package errgroup package errgroup
import ( import (

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build go1.20 //go:build go1.20
// +build go1.20
package errgroup package errgroup

View File

@ -3,7 +3,6 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !go1.20 //go:build !go1.20
// +build !go1.20
package errgroup package errgroup

View File

@ -1,102 +0,0 @@
// 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 execabs is a drop-in replacement for os/exec
// that requires PATH lookups to find absolute paths.
// That is, execabs.Command("cmd") runs the same PATH lookup
// as exec.Command("cmd"), but if the result is a path
// which is relative, the Run and Start methods will report
// an error instead of running the executable.
//
// See https://blog.golang.org/path-security for more information
// about when it may be necessary or appropriate to use this package.
package execabs
import (
"context"
"fmt"
"os/exec"
"path/filepath"
"reflect"
"unsafe"
)
// ErrNotFound is the error resulting if a path search failed to find an executable file.
// It is an alias for exec.ErrNotFound.
var ErrNotFound = exec.ErrNotFound
// Cmd represents an external command being prepared or run.
// It is an alias for exec.Cmd.
type Cmd = exec.Cmd
// Error is returned by LookPath when it fails to classify a file as an executable.
// It is an alias for exec.Error.
type Error = exec.Error
// An ExitError reports an unsuccessful exit by a command.
// It is an alias for exec.ExitError.
type ExitError = exec.ExitError
func relError(file, path string) error {
return fmt.Errorf("%s resolves to executable in current directory (.%c%s)", file, filepath.Separator, path)
}
// LookPath searches for an executable named file in the directories
// named by the PATH environment variable. If file contains a slash,
// it is tried directly and the PATH is not consulted. The result will be
// an absolute path.
//
// LookPath differs from exec.LookPath in its handling of PATH lookups,
// which are used for file names without slashes. If exec.LookPath's
// PATH lookup would have returned an executable from the current directory,
// LookPath instead returns an error.
func LookPath(file string) (string, error) {
path, err := exec.LookPath(file)
if err != nil && !isGo119ErrDot(err) {
return "", err
}
if filepath.Base(file) == file && !filepath.IsAbs(path) {
return "", relError(file, path)
}
return path, nil
}
func fixCmd(name string, cmd *exec.Cmd) {
if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) && !isGo119ErrFieldSet(cmd) {
// exec.Command was called with a bare binary name and
// exec.LookPath returned a path which is not absolute.
// Set cmd.lookPathErr and clear cmd.Path so that it
// cannot be run.
lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer()))
if *lookPathErr == nil {
*lookPathErr = relError(name, cmd.Path)
}
cmd.Path = ""
}
}
// CommandContext is like Command but includes a context.
//
// The provided context is used to kill the process (by calling os.Process.Kill)
// if the context becomes done before the command completes on its own.
func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd {
cmd := exec.CommandContext(ctx, name, arg...)
fixCmd(name, cmd)
return cmd
}
// Command returns the Cmd struct to execute the named program with the given arguments.
// See exec.Command for most details.
//
// Command differs from exec.Command in its handling of PATH lookups,
// which are used when the program name contains no slashes.
// If exec.Command would have returned an exec.Cmd configured to run an
// executable from the current directory, Command instead
// returns an exec.Cmd that will return an error from Start or Run.
func Command(name string, arg ...string) *exec.Cmd {
cmd := exec.Command(name, arg...)
fixCmd(name, cmd)
return cmd
}

View File

@ -1,17 +0,0 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.19
package execabs
import "os/exec"
func isGo119ErrDot(err error) bool {
return false
}
func isGo119ErrFieldSet(cmd *exec.Cmd) bool {
return false
}

View File

@ -1,20 +0,0 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.19
package execabs
import (
"errors"
"os/exec"
)
func isGo119ErrDot(err error) bool {
return errors.Is(err, exec.ErrDot)
}
func isGo119ErrFieldSet(cmd *exec.Cmd) bool {
return cmd.Err != nil
}

View File

@ -188,6 +188,8 @@ type Generator struct {
trimPrefix string trimPrefix string
lineComment bool lineComment bool
logf func(format string, args ...interface{}) // test logging hook; nil when not testing
} }
func (g *Generator) Printf(format string, args ...interface{}) { func (g *Generator) Printf(format string, args ...interface{}) {
@ -221,13 +223,14 @@ func (g *Generator) parsePackage(patterns []string, tags []string) {
// in a separate pass? For later. // in a separate pass? For later.
Tests: false, Tests: false,
BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))}, BuildFlags: []string{fmt.Sprintf("-tags=%s", strings.Join(tags, " "))},
Logf: g.logf,
} }
pkgs, err := packages.Load(cfg, patterns...) pkgs, err := packages.Load(cfg, patterns...)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
if len(pkgs) != 1 { if len(pkgs) != 1 {
log.Fatalf("error: %d packages found", len(pkgs)) log.Fatalf("error: %d packages matching %v", len(pkgs), strings.Join(patterns, " "))
} }
g.addPackage(pkgs[0]) g.addPackage(pkgs[0])
} }

View File

@ -8,42 +8,46 @@ package packagesdriver
import ( import (
"context" "context"
"fmt" "fmt"
"go/types"
"strings" "strings"
"golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gocommand"
) )
var debug = false func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) {
func GetSizesGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (types.Sizes, error) {
inv.Verb = "list" inv.Verb = "list"
inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"} inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"}
stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv) stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv)
var goarch, compiler string var goarch, compiler string
if rawErr != nil { if rawErr != nil {
if rawErrMsg := rawErr.Error(); strings.Contains(rawErrMsg, "cannot find main module") || strings.Contains(rawErrMsg, "go.mod file not found") { rawErrMsg := rawErr.Error()
// User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc. if strings.Contains(rawErrMsg, "cannot find main module") ||
strings.Contains(rawErrMsg, "go.mod file not found") {
// User's running outside of a module.
// All bets are off. Get GOARCH and guess compiler is gc.
// TODO(matloob): Is this a problem in practice? // TODO(matloob): Is this a problem in practice?
inv.Verb = "env" inv.Verb = "env"
inv.Args = []string{"GOARCH"} inv.Args = []string{"GOARCH"}
envout, enverr := gocmdRunner.Run(ctx, inv) envout, enverr := gocmdRunner.Run(ctx, inv)
if enverr != nil { if enverr != nil {
return nil, enverr return "", "", enverr
} }
goarch = strings.TrimSpace(envout.String()) goarch = strings.TrimSpace(envout.String())
compiler = "gc" compiler = "gc"
} else if friendlyErr != nil {
return "", "", friendlyErr
} else { } else {
return nil, friendlyErr // This should be unreachable, but be defensive
// in case RunRaw's error results are inconsistent.
return "", "", rawErr
} }
} else { } else {
fields := strings.Fields(stdout.String()) fields := strings.Fields(stdout.String())
if len(fields) < 2 { if len(fields) < 2 {
return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>", return "", "", fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>",
stdout.String(), stderr.String()) stdout.String(), stderr.String())
} }
goarch = fields[0] goarch = fields[0]
compiler = fields[1] compiler = fields[1]
} }
return types.SizesFor(compiler, goarch), nil return compiler, goarch, nil
} }

View File

@ -35,7 +35,7 @@ The Package struct provides basic information about the package, including
- Imports, a map from source import strings to the Packages they name; - Imports, a map from source import strings to the Packages they name;
- Types, the type information for the package's exported symbols; - Types, the type information for the package's exported symbols;
- Syntax, the parsed syntax trees for the package's source code; and - Syntax, the parsed syntax trees for the package's source code; and
- TypeInfo, the result of a complete type-check of the package syntax trees. - 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 (See the documentation for type Package for the complete list of fields
and more detailed descriptions.) and more detailed descriptions.)

View File

@ -12,8 +12,8 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
exec "golang.org/x/sys/execabs"
"os" "os"
"os/exec"
"strings" "strings"
) )

View File

@ -9,10 +9,9 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"go/types"
"io/ioutil"
"log" "log"
"os" "os"
"os/exec"
"path" "path"
"path/filepath" "path/filepath"
"reflect" "reflect"
@ -22,7 +21,6 @@ import (
"sync" "sync"
"unicode" "unicode"
exec "golang.org/x/sys/execabs"
"golang.org/x/tools/go/internal/packagesdriver" "golang.org/x/tools/go/internal/packagesdriver"
"golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/packagesinternal" "golang.org/x/tools/internal/packagesinternal"
@ -153,10 +151,10 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 { if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
sizeswg.Add(1) sizeswg.Add(1)
go func() { go func() {
var sizes types.Sizes compiler, arch, err := packagesdriver.GetSizesForArgsGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner)
sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner) sizeserr = err
// types.SizesFor always returns nil or a *types.StdSizes. response.dr.Compiler = compiler
response.dr.Sizes, _ = sizes.(*types.StdSizes) response.dr.Arch = arch
sizeswg.Done() sizeswg.Done()
}() }()
} }
@ -210,62 +208,6 @@ extractQueries:
} }
} }
// Only use go/packages' overlay processing if we're using a Go version
// below 1.16. Otherwise, go list handles it.
if goVersion, err := state.getGoVersion(); err == nil && goVersion < 16 {
modifiedPkgs, needPkgs, err := state.processGolistOverlay(response)
if err != nil {
return nil, err
}
var containsCandidates []string
if len(containFiles) > 0 {
containsCandidates = append(containsCandidates, modifiedPkgs...)
containsCandidates = append(containsCandidates, needPkgs...)
}
if err := state.addNeededOverlayPackages(response, needPkgs); err != nil {
return nil, err
}
// Check candidate packages for containFiles.
if len(containFiles) > 0 {
for _, id := range containsCandidates {
pkg, ok := response.seenPackages[id]
if !ok {
response.addPackage(&Package{
ID: id,
Errors: []Error{{
Kind: ListError,
Msg: fmt.Sprintf("package %s expected but not seen", id),
}},
})
continue
}
for _, f := range containFiles {
for _, g := range pkg.GoFiles {
if sameFile(f, g) {
response.addRoot(id)
}
}
}
}
}
// Add root for any package that matches a pattern. This applies only to
// packages that are modified by overlays, since they are not added as
// roots automatically.
for _, pattern := range restPatterns {
match := matchPattern(pattern)
for _, pkgID := range modifiedPkgs {
pkg, ok := response.seenPackages[pkgID]
if !ok {
continue
}
if match(pkg.PkgPath) {
response.addRoot(pkg.ID)
}
}
}
}
sizeswg.Wait() sizeswg.Wait()
if sizeserr != nil { if sizeserr != nil {
return nil, sizeserr return nil, sizeserr
@ -273,24 +215,6 @@ extractQueries:
return response.dr, nil return response.dr, nil
} }
func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error {
if len(pkgs) == 0 {
return nil
}
dr, err := state.createDriverResponse(pkgs...)
if err != nil {
return err
}
for _, pkg := range dr.Packages {
response.addPackage(pkg)
}
_, needPkgs, err := state.processGolistOverlay(response)
if err != nil {
return err
}
return state.addNeededOverlayPackages(response, needPkgs)
}
func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error { func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error {
for _, query := range queries { for _, query := range queries {
// TODO(matloob): Do only one query per directory. // TODO(matloob): Do only one query per directory.
@ -671,6 +595,9 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
// Temporary work-around for golang/go#39986. Parse filenames out of // Temporary work-around for golang/go#39986. Parse filenames out of
// error messages. This happens if there are unrecoverable syntax // error messages. This happens if there are unrecoverable syntax
// errors in the source, so we can't match on a specific error message. // errors in the source, so we can't match on a specific error message.
//
// TODO(rfindley): remove this heuristic, in favor of considering
// InvalidGoFiles from the list driver.
if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) { if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
addFilenameFromPos := func(pos string) bool { addFilenameFromPos := func(pos string) bool {
split := strings.Split(pos, ":") split := strings.Split(pos, ":")
@ -1107,7 +1034,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err
if len(state.cfg.Overlay) == 0 { if len(state.cfg.Overlay) == 0 {
return "", func() {}, nil return "", func() {}, nil
} }
dir, err := ioutil.TempDir("", "gopackages-*") dir, err := os.MkdirTemp("", "gopackages-*")
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -1126,7 +1053,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err
// Create a unique filename for the overlaid files, to avoid // Create a unique filename for the overlaid files, to avoid
// creating nested directories. // creating nested directories.
noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "") noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "")
f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator)) f, err := os.CreateTemp(dir, fmt.Sprintf("*-%s", noSeparator))
if err != nil { if err != nil {
return "", func() {}, err return "", func() {}, err
} }
@ -1144,7 +1071,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err
} }
// Write out the overlay file that contains the filepath mappings. // Write out the overlay file that contains the filepath mappings.
filename = filepath.Join(dir, "overlay.json") filename = filepath.Join(dir, "overlay.json")
if err := ioutil.WriteFile(filename, b, 0665); err != nil { if err := os.WriteFile(filename, b, 0665); err != nil {
return "", func() {}, err return "", func() {}, err
} }
return filename, cleanup, nil return filename, cleanup, nil

View File

@ -6,314 +6,11 @@ package packages
import ( import (
"encoding/json" "encoding/json"
"fmt"
"go/parser"
"go/token"
"os"
"path/filepath" "path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gocommand"
) )
// processGolistOverlay provides rudimentary support for adding
// files that don't exist on disk to an overlay. The results can be
// sometimes incorrect.
// TODO(matloob): Handle unsupported cases, including the following:
// - determining the correct package to add given a new import path
func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) {
havePkgs := make(map[string]string) // importPath -> non-test package ID
needPkgsSet := make(map[string]bool)
modifiedPkgsSet := make(map[string]bool)
pkgOfDir := make(map[string][]*Package)
for _, pkg := range response.dr.Packages {
// This is an approximation of import path to id. This can be
// wrong for tests, vendored packages, and a number of other cases.
havePkgs[pkg.PkgPath] = pkg.ID
dir, err := commonDir(pkg.GoFiles)
if err != nil {
return nil, nil, err
}
if dir != "" {
pkgOfDir[dir] = append(pkgOfDir[dir], pkg)
}
}
// If no new imports are added, it is safe to avoid loading any needPkgs.
// Otherwise, it's hard to tell which package is actually being loaded
// (due to vendoring) and whether any modified package will show up
// in the transitive set of dependencies (because new imports are added,
// potentially modifying the transitive set of dependencies).
var overlayAddsImports bool
// If both a package and its test package are created by the overlay, we
// need the real package first. Process all non-test files before test
// files, and make the whole process deterministic while we're at it.
var overlayFiles []string
for opath := range state.cfg.Overlay {
overlayFiles = append(overlayFiles, opath)
}
sort.Slice(overlayFiles, func(i, j int) bool {
iTest := strings.HasSuffix(overlayFiles[i], "_test.go")
jTest := strings.HasSuffix(overlayFiles[j], "_test.go")
if iTest != jTest {
return !iTest // non-tests are before tests.
}
return overlayFiles[i] < overlayFiles[j]
})
for _, opath := range overlayFiles {
contents := state.cfg.Overlay[opath]
base := filepath.Base(opath)
dir := filepath.Dir(opath)
var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant
var testVariantOf *Package // if opath is a test file, this is the package it is testing
var fileExists bool
isTestFile := strings.HasSuffix(opath, "_test.go")
pkgName, ok := extractPackageName(opath, contents)
if !ok {
// Don't bother adding a file that doesn't even have a parsable package statement
// to the overlay.
continue
}
// If all the overlay files belong to a different package, change the
// package name to that package.
maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir])
nextPackage:
for _, p := range response.dr.Packages {
if pkgName != p.Name && p.ID != "command-line-arguments" {
continue
}
for _, f := range p.GoFiles {
if !sameFile(filepath.Dir(f), dir) {
continue
}
// Make sure to capture information on the package's test variant, if needed.
if isTestFile && !hasTestFiles(p) {
// TODO(matloob): Are there packages other than the 'production' variant
// of a package that this can match? This shouldn't match the test main package
// because the file is generated in another directory.
testVariantOf = p
continue nextPackage
} else if !isTestFile && hasTestFiles(p) {
// We're examining a test variant, but the overlaid file is
// a non-test file. Because the overlay implementation
// (currently) only adds a file to one package, skip this
// package, so that we can add the file to the production
// variant of the package. (https://golang.org/issue/36857
// tracks handling overlays on both the production and test
// variant of a package).
continue nextPackage
}
if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath {
// We have already seen the production version of the
// for which p is a test variant.
if hasTestFiles(p) {
testVariantOf = pkg
}
}
pkg = p
if filepath.Base(f) == base {
fileExists = true
}
}
}
// The overlay could have included an entirely new package or an
// ad-hoc package. An ad-hoc package is one that we have manually
// constructed from inadequate `go list` results for a file= query.
// It will have the ID command-line-arguments.
if pkg == nil || pkg.ID == "command-line-arguments" {
// Try to find the module or gopath dir the file is contained in.
// Then for modules, add the module opath to the beginning.
pkgPath, ok, err := state.getPkgPath(dir)
if err != nil {
return nil, nil, err
}
if !ok {
break
}
var forTest string // only set for x tests
isXTest := strings.HasSuffix(pkgName, "_test")
if isXTest {
forTest = pkgPath
pkgPath += "_test"
}
id := pkgPath
if isTestFile {
if isXTest {
id = fmt.Sprintf("%s [%s.test]", pkgPath, forTest)
} else {
id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath)
}
}
if pkg != nil {
// TODO(rstambler): We should change the package's path and ID
// here. The only issue is that this messes with the roots.
} else {
// Try to reclaim a package with the same ID, if it exists in the response.
for _, p := range response.dr.Packages {
if reclaimPackage(p, id, opath, contents) {
pkg = p
break
}
}
// Otherwise, create a new package.
if pkg == nil {
pkg = &Package{
PkgPath: pkgPath,
ID: id,
Name: pkgName,
Imports: make(map[string]*Package),
}
response.addPackage(pkg)
havePkgs[pkg.PkgPath] = id
// Add the production package's sources for a test variant.
if isTestFile && !isXTest && testVariantOf != nil {
pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...)
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...)
// Add the package under test and its imports to the test variant.
pkg.forTest = testVariantOf.PkgPath
for k, v := range testVariantOf.Imports {
pkg.Imports[k] = &Package{ID: v.ID}
}
}
if isXTest {
pkg.forTest = forTest
}
}
}
}
if !fileExists {
pkg.GoFiles = append(pkg.GoFiles, opath)
// TODO(matloob): Adding the file to CompiledGoFiles can exhibit the wrong behavior
// if the file will be ignored due to its build tags.
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, opath)
modifiedPkgsSet[pkg.ID] = true
}
imports, err := extractImports(opath, contents)
if err != nil {
// Let the parser or type checker report errors later.
continue
}
for _, imp := range imports {
// TODO(rstambler): If the package is an x test and the import has
// a test variant, make sure to replace it.
if _, found := pkg.Imports[imp]; found {
continue
}
overlayAddsImports = true
id, ok := havePkgs[imp]
if !ok {
var err error
id, err = state.resolveImport(dir, imp)
if err != nil {
return nil, nil, err
}
}
pkg.Imports[imp] = &Package{ID: id}
// Add dependencies to the non-test variant version of this package as well.
if testVariantOf != nil {
testVariantOf.Imports[imp] = &Package{ID: id}
}
}
}
// toPkgPath guesses the package path given the id.
toPkgPath := func(sourceDir, id string) (string, error) {
if i := strings.IndexByte(id, ' '); i >= 0 {
return state.resolveImport(sourceDir, id[:i])
}
return state.resolveImport(sourceDir, id)
}
// Now that new packages have been created, do another pass to determine
// the new set of missing packages.
for _, pkg := range response.dr.Packages {
for _, imp := range pkg.Imports {
if len(pkg.GoFiles) == 0 {
return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath)
}
pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID)
if err != nil {
return nil, nil, err
}
if _, ok := havePkgs[pkgPath]; !ok {
needPkgsSet[pkgPath] = true
}
}
}
if overlayAddsImports {
needPkgs = make([]string, 0, len(needPkgsSet))
for pkg := range needPkgsSet {
needPkgs = append(needPkgs, pkg)
}
}
modifiedPkgs = make([]string, 0, len(modifiedPkgsSet))
for pkg := range modifiedPkgsSet {
modifiedPkgs = append(modifiedPkgs, pkg)
}
return modifiedPkgs, needPkgs, err
}
// resolveImport finds the ID of a package given its import path.
// In particular, it will find the right vendored copy when in GOPATH mode.
func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) {
env, err := state.getEnv()
if err != nil {
return "", err
}
if env["GOMOD"] != "" {
return importPath, nil
}
searchDir := sourceDir
for {
vendorDir := filepath.Join(searchDir, "vendor")
exists, ok := state.vendorDirs[vendorDir]
if !ok {
info, err := os.Stat(vendorDir)
exists = err == nil && info.IsDir()
state.vendorDirs[vendorDir] = exists
}
if exists {
vendoredPath := filepath.Join(vendorDir, importPath)
if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() {
// We should probably check for .go files here, but shame on anyone who fools us.
path, ok, err := state.getPkgPath(vendoredPath)
if err != nil {
return "", err
}
if ok {
return path, nil
}
}
}
// We know we've hit the top of the filesystem when we Dir / and get /,
// or C:\ and get C:\, etc.
next := filepath.Dir(searchDir)
if next == searchDir {
break
}
searchDir = next
}
return importPath, nil
}
func hasTestFiles(p *Package) bool {
for _, f := range p.GoFiles {
if strings.HasSuffix(f, "_test.go") {
return true
}
}
return false
}
// determineRootDirs returns a mapping from absolute directories that could // determineRootDirs returns a mapping from absolute directories that could
// contain code to their corresponding import path prefixes. // contain code to their corresponding import path prefixes.
func (state *golistState) determineRootDirs() (map[string]string, error) { func (state *golistState) determineRootDirs() (map[string]string, error) {
@ -384,192 +81,3 @@ func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) {
} }
return m, nil return m, nil
} }
func extractImports(filename string, contents []byte) ([]string, error) {
f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset?
if err != nil {
return nil, err
}
var res []string
for _, imp := range f.Imports {
quotedPath := imp.Path.Value
path, err := strconv.Unquote(quotedPath)
if err != nil {
return nil, err
}
res = append(res, path)
}
return res, nil
}
// reclaimPackage attempts to reuse a package that failed to load in an overlay.
//
// If the package has errors and has no Name, GoFiles, or Imports,
// then it's possible that it doesn't yet exist on disk.
func reclaimPackage(pkg *Package, id string, filename string, contents []byte) bool {
// TODO(rstambler): Check the message of the actual error?
// It differs between $GOPATH and module mode.
if pkg.ID != id {
return false
}
if len(pkg.Errors) != 1 {
return false
}
if pkg.Name != "" || pkg.ExportFile != "" {
return false
}
if len(pkg.GoFiles) > 0 || len(pkg.CompiledGoFiles) > 0 || len(pkg.OtherFiles) > 0 {
return false
}
if len(pkg.Imports) > 0 {
return false
}
pkgName, ok := extractPackageName(filename, contents)
if !ok {
return false
}
pkg.Name = pkgName
pkg.Errors = nil
return true
}
func extractPackageName(filename string, contents []byte) (string, bool) {
// TODO(rstambler): Check the message of the actual error?
// It differs between $GOPATH and module mode.
f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.PackageClauseOnly) // TODO(matloob): reuse fileset?
if err != nil {
return "", false
}
return f.Name.Name, true
}
// commonDir returns the directory that all files are in, "" if files is empty,
// or an error if they aren't in the same directory.
func commonDir(files []string) (string, error) {
seen := make(map[string]bool)
for _, f := range files {
seen[filepath.Dir(f)] = true
}
if len(seen) > 1 {
return "", fmt.Errorf("files (%v) are in more than one directory: %v", files, seen)
}
for k := range seen {
// seen has only one element; return it.
return k, nil
}
return "", nil // no files
}
// It is possible that the files in the disk directory dir have a different package
// name from newName, which is deduced from the overlays. If they all have a different
// package name, and they all have the same package name, then that name becomes
// the package name.
// It returns true if it changes the package name, false otherwise.
func maybeFixPackageName(newName string, isTestFile bool, pkgsOfDir []*Package) {
names := make(map[string]int)
for _, p := range pkgsOfDir {
names[p.Name]++
}
if len(names) != 1 {
// some files are in different packages
return
}
var oldName string
for k := range names {
oldName = k
}
if newName == oldName {
return
}
// We might have a case where all of the package names in the directory are
// the same, but the overlay file is for an x test, which belongs to its
// own package. If the x test does not yet exist on disk, we may not yet
// have its package name on disk, but we should not rename the packages.
//
// We use a heuristic to determine if this file belongs to an x test:
// The test file should have a package name whose package name has a _test
// suffix or looks like "newName_test".
maybeXTest := strings.HasPrefix(oldName+"_test", newName) || strings.HasSuffix(newName, "_test")
if isTestFile && maybeXTest {
return
}
for _, p := range pkgsOfDir {
p.Name = newName
}
}
// This function is copy-pasted from
// https://github.com/golang/go/blob/9706f510a5e2754595d716bd64be8375997311fb/src/cmd/go/internal/search/search.go#L360.
// It should be deleted when we remove support for overlays from go/packages.
//
// NOTE: This does not handle any ./... or ./ style queries, as this function
// doesn't know the working directory.
//
// matchPattern(pattern)(name) reports whether
// name matches pattern. Pattern is a limited glob
// pattern in which '...' means 'any string' and there
// is no other special syntax.
// Unfortunately, there are two special cases. Quoting "go help packages":
//
// First, /... at the end of the pattern can match an empty string,
// so that net/... matches both net and packages in its subdirectories, like net/http.
// Second, any slash-separated pattern element containing a wildcard never
// participates in a match of the "vendor" element in the path of a vendored
// package, so that ./... does not match packages in subdirectories of
// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
// Note, however, that a directory named vendor that itself contains code
// is not a vendored package: cmd/vendor would be a command named vendor,
// and the pattern cmd/... matches it.
func matchPattern(pattern string) func(name string) bool {
// Convert pattern to regular expression.
// The strategy for the trailing /... is to nest it in an explicit ? expression.
// The strategy for the vendor exclusion is to change the unmatchable
// vendor strings to a disallowed code point (vendorChar) and to use
// "(anything but that codepoint)*" as the implementation of the ... wildcard.
// This is a bit complicated but the obvious alternative,
// namely a hand-written search like in most shell glob matchers,
// is too easy to make accidentally exponential.
// Using package regexp guarantees linear-time matching.
const vendorChar = "\x00"
if strings.Contains(pattern, vendorChar) {
return func(name string) bool { return false }
}
re := regexp.QuoteMeta(pattern)
re = replaceVendor(re, vendorChar)
switch {
case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
case re == vendorChar+`/\.\.\.`:
re = `(/vendor|/` + vendorChar + `/\.\.\.)`
case strings.HasSuffix(re, `/\.\.\.`):
re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
}
re = strings.ReplaceAll(re, `\.\.\.`, `[^`+vendorChar+`]*`)
reg := regexp.MustCompile(`^` + re + `$`)
return func(name string) bool {
if strings.Contains(name, vendorChar) {
return false
}
return reg.MatchString(replaceVendor(name, vendorChar))
}
}
// replaceVendor returns the result of replacing
// non-trailing vendor path elements in x with repl.
func replaceVendor(x, repl string) string {
if !strings.Contains(x, "vendor") {
return x
}
elem := strings.Split(x, "/")
for i := 0; i < len(elem)-1; i++ {
if elem[i] == "vendor" {
elem[i] = repl
}
}
return strings.Join(elem, "/")
}

View File

@ -16,7 +16,6 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"io" "io"
"io/ioutil"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
@ -30,6 +29,7 @@ import (
"golang.org/x/tools/internal/packagesinternal" "golang.org/x/tools/internal/packagesinternal"
"golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typeparams"
"golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/typesinternal"
"golang.org/x/tools/internal/versions"
) )
// A LoadMode controls the amount of detail to return when loading. // A LoadMode controls the amount of detail to return when loading.
@ -220,8 +220,10 @@ type driverResponse struct {
// lists of multiple drivers, go/packages will fall back to the next driver. // lists of multiple drivers, go/packages will fall back to the next driver.
NotHandled bool NotHandled bool
// Sizes, if not nil, is the types.Sizes to use when type checking. // Compiler and Arch are the arguments pass of types.SizesFor
Sizes *types.StdSizes // 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. // 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 have to encode this separately because when we encode a single package
@ -257,31 +259,52 @@ type driverResponse struct {
// proceeding with further analysis. The PrintErrors function is // proceeding with further analysis. The PrintErrors function is
// provided for convenient display of all errors. // provided for convenient display of all errors.
func Load(cfg *Config, patterns ...string) ([]*Package, error) { func Load(cfg *Config, patterns ...string) ([]*Package, error) {
l := newLoader(cfg) ld := newLoader(cfg)
response, err := defaultDriver(&l.Config, patterns...) response, external, err := defaultDriver(&ld.Config, patterns...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
l.sizes = response.Sizes
return l.refine(response) ld.sizes = types.SizesFor(response.Compiler, response.Arch)
if ld.sizes == nil && ld.Config.Mode&(NeedTypes|NeedTypesSizes|NeedTypesInfo) != 0 {
// Type size information is needed but unavailable.
if external {
// An external driver may fail to populate the Compiler/GOARCH fields,
// especially since they are relatively new (see #63700).
// Provide a sensible fallback in this case.
ld.sizes = types.SizesFor("gc", runtime.GOARCH)
if ld.sizes == nil { // gccgo-only arch
ld.sizes = types.SizesFor("gc", "amd64")
}
} else {
// Go list should never fail to deliver accurate size information.
// Reject the whole Load since the error is the same for every package.
return nil, fmt.Errorf("can't determine type sizes for compiler %q on GOARCH %q",
response.Compiler, response.Arch)
}
}
return ld.refine(response)
} }
// defaultDriver is a driver that implements go/packages' fallback behavior. // defaultDriver is a driver that implements go/packages' fallback behavior.
// It will try to request to an external driver, if one exists. If there's // It will try to request to an external driver, if one exists. If there's
// no external driver, or the driver returns a response with NotHandled set, // no external driver, or the driver returns a response with NotHandled set,
// defaultDriver will fall back to the go list driver. // defaultDriver will fall back to the go list driver.
func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) { // The boolean result indicates that an external driver handled the request.
driver := findExternalDriver(cfg) func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, bool, error) {
if driver == nil { if driver := findExternalDriver(cfg); driver != nil {
driver = goListDriver
}
response, err := driver(cfg, patterns...) response, err := driver(cfg, patterns...)
if err != nil { if err != nil {
return response, err return nil, false, err
} else if response.NotHandled { } else if !response.NotHandled {
return goListDriver(cfg, patterns...) return response, true, nil
} }
return response, nil // (fall through)
}
response, err := goListDriver(cfg, patterns...)
return response, false, err
} }
// A Package describes a loaded Go package. // A Package describes a loaded Go package.
@ -410,12 +433,6 @@ func init() {
packagesinternal.GetDepsErrors = func(p interface{}) []*packagesinternal.PackageError { packagesinternal.GetDepsErrors = func(p interface{}) []*packagesinternal.PackageError {
return p.(*Package).depsErrors return p.(*Package).depsErrors
} }
packagesinternal.GetGoCmdRunner = func(config interface{}) *gocommand.Runner {
return config.(*Config).gocmdRunner
}
packagesinternal.SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {
config.(*Config).gocmdRunner = runner
}
packagesinternal.SetModFile = func(config interface{}, value string) { packagesinternal.SetModFile = func(config interface{}, value string) {
config.(*Config).modFile = value config.(*Config).modFile = value
} }
@ -552,7 +569,7 @@ type loaderPackage struct {
type loader struct { type loader struct {
pkgs map[string]*loaderPackage pkgs map[string]*loaderPackage
Config Config
sizes types.Sizes sizes types.Sizes // non-nil if needed by mode
parseCache map[string]*parseValue parseCache map[string]*parseValue
parseCacheMu sync.Mutex parseCacheMu sync.Mutex
exportMu sync.Mutex // enforces mutual exclusion of exportdata operations exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
@ -630,7 +647,7 @@ func newLoader(cfg *Config) *loader {
return ld return ld
} }
// refine connects the supplied packages into a graph and then adds type and // refine connects the supplied packages into a graph and then adds type
// and syntax information as requested by the LoadMode. // and syntax information as requested by the LoadMode.
func (ld *loader) refine(response *driverResponse) ([]*Package, error) { func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
roots := response.Roots roots := response.Roots
@ -677,6 +694,7 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
} }
} }
if ld.Mode&NeedImports != 0 {
// Materialize the import graph. // Materialize the import graph.
const ( const (
@ -690,14 +708,14 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
// //
// Valid imports are saved in the Packages.Import map. // Valid imports are saved in the Packages.Import map.
// Invalid imports (cycles and missing nodes) are saved in the importErrors map. // Invalid imports (cycles and missing nodes) are saved in the importErrors map.
// Thus, even in the presence of both kinds of errors, the Import graph remains a DAG. // Thus, even in the presence of both kinds of errors,
// the Import graph remains a DAG.
// //
// visit returns whether the package needs src or has a transitive // visit returns whether the package needs src or has a transitive
// dependency on a package that does. These are the only packages // dependency on a package that does. These are the only packages
// for which we load source code. // for which we load source code.
var stack []*loaderPackage var stack []*loaderPackage
var visit func(lpkg *loaderPackage) bool var visit func(lpkg *loaderPackage) bool
var srcPkgs []*loaderPackage
visit = func(lpkg *loaderPackage) bool { visit = func(lpkg *loaderPackage) bool {
switch lpkg.color { switch lpkg.color {
case black: case black:
@ -708,8 +726,6 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
lpkg.color = grey lpkg.color = grey
stack = append(stack, lpkg) // push stack = append(stack, lpkg) // push
stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports
// If NeedImports isn't set, the imports fields will all be zeroed out.
if ld.Mode&NeedImports != 0 {
lpkg.Imports = make(map[string]*Package, len(stubs)) lpkg.Imports = make(map[string]*Package, len(stubs))
for importPath, ipkg := range stubs { for importPath, ipkg := range stubs {
var importErr error var importErr error
@ -733,10 +749,17 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
} }
lpkg.Imports[importPath] = imp.Package lpkg.Imports[importPath] = imp.Package
} }
// Complete type information is required for the
// immediate dependencies of each source package.
if lpkg.needsrc && ld.Mode&NeedTypes != 0 {
for _, ipkg := range lpkg.Imports {
ld.pkgs[ipkg.ID].needtypes = true
} }
if lpkg.needsrc {
srcPkgs = append(srcPkgs, lpkg)
} }
// NeedTypeSizes causes TypeSizes to be set even
// on packages for which types aren't needed.
if ld.Mode&NeedTypesSizes != 0 { if ld.Mode&NeedTypesSizes != 0 {
lpkg.TypesSizes = ld.sizes lpkg.TypesSizes = ld.sizes
} }
@ -746,27 +769,19 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) {
return lpkg.needsrc return lpkg.needsrc
} }
if ld.Mode&NeedImports == 0 {
// We do this to drop the stub import packages that we are not even going to try to resolve.
for _, lpkg := range initial {
lpkg.Imports = nil
}
} else {
// For each initial package, create its import DAG. // For each initial package, create its import DAG.
for _, lpkg := range initial { for _, lpkg := range initial {
visit(lpkg) visit(lpkg)
} }
}
if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 { } else {
for _, lpkg := range srcPkgs { // !NeedImports: drop the stub (ID-only) import packages
// Complete type information is required for the // that we are not even going to try to resolve.
// immediate dependencies of each source package. for _, lpkg := range initial {
for _, ipkg := range lpkg.Imports { lpkg.Imports = nil
imp := ld.pkgs[ipkg.ID]
imp.needtypes = true
}
} }
} }
// Load type data and syntax if needed, starting at // Load type data and syntax if needed, starting at
// the initial packages (roots of the import DAG). // the initial packages (roots of the import DAG).
if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 { if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 {
@ -1004,6 +1019,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
Selections: make(map[*ast.SelectorExpr]*types.Selection), Selections: make(map[*ast.SelectorExpr]*types.Selection),
} }
typeparams.InitInstanceInfo(lpkg.TypesInfo) typeparams.InitInstanceInfo(lpkg.TypesInfo)
versions.InitFileVersions(lpkg.TypesInfo)
lpkg.TypesSizes = ld.sizes lpkg.TypesSizes = ld.sizes
importer := importerFunc(func(path string) (*types.Package, error) { importer := importerFunc(func(path string) (*types.Package, error) {
@ -1041,7 +1057,10 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial, IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial,
Error: appendError, Error: appendError,
Sizes: ld.sizes, Sizes: ld.sizes, // may be nil
}
if lpkg.Module != nil && lpkg.Module.GoVersion != "" {
typesinternal.SetGoVersion(tc, "go"+lpkg.Module.GoVersion)
} }
if (ld.Mode & typecheckCgo) != 0 { if (ld.Mode & typecheckCgo) != 0 {
if !typesinternal.SetUsesCgo(tc) { if !typesinternal.SetUsesCgo(tc) {
@ -1122,7 +1141,7 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) {
var err error var err error
if src == nil { if src == nil {
ioLimit <- true // wait ioLimit <- true // wait
src, err = ioutil.ReadFile(filename) src, err = os.ReadFile(filename)
<-ioLimit // signal <-ioLimit // signal
} }
if err != nil { if err != nil {

View File

@ -0,0 +1,752 @@
// 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/typeparams"
)
// 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,Params,Results,Underlying} [EKPRU]
// 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 [EKPRUTC];
// one of these (TypeParam) requires 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)
opConstraint = 'C' // .Constraint() (TypeParam)
// 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 := obj.Type().(*typeparams.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 tname.IsAlias() {
// type alias
if r := find(obj, T, path, nil); r != nil {
return Path(r), nil
}
} else {
if named, _ := T.(*types.Named); named != nil {
if r := findTypeParam(obj, typeparams.ForNamed(named), path, nil); r != nil {
// generic named type
return Path(r), nil
}
}
// defined (named) type
if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); 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), nil); r != nil {
return Path(r), nil
}
}
continue
}
// Inspect declared methods of defined types.
if T, ok := 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), nil); 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 typeparams.OriginMethod(meth) != meth {
return "", false
}
recvT := meth.Type().(*types.Signature).Recv().Type()
if ptr, ok := recvT.(*types.Pointer); ok {
recvT = ptr.Elem()
}
named, ok := recvT.(*types.Named)
if !ok {
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.
func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte {
switch T := T.(type) {
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 find(obj, T.Elem(), append(path, opElem), seen)
case *types.Slice:
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Array:
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Chan:
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Map:
if r := find(obj, T.Key(), append(path, opKey), seen); r != nil {
return r
}
return find(obj, T.Elem(), append(path, opElem), seen)
case *types.Signature:
if r := findTypeParam(obj, typeparams.ForSignature(T), path, seen); r != nil {
return r
}
if r := find(obj, T.Params(), append(path, opParams), seen); r != nil {
return r
}
return find(obj, T.Results(), append(path, opResults), seen)
case *types.Struct:
for i := 0; i < T.NumFields(); i++ {
fld := T.Field(i)
path2 := appendOpArg(path, opField, i)
if fld == obj {
return path2 // found field var
}
if r := find(obj, fld.Type(), append(path2, opType), seen); 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 == obj {
return path2 // found param/result var
}
if r := find(obj, v.Type(), append(path2, opType), seen); r != nil {
return r
}
}
return nil
case *types.Interface:
for i := 0; i < T.NumMethods(); i++ {
m := T.Method(i)
path2 := appendOpArg(path, opMethod, i)
if m == obj {
return path2 // found interface method
}
if r := find(obj, m.Type(), append(path2, opType), seen); r != nil {
return r
}
}
return nil
case *typeparams.TypeParam:
name := T.Obj()
if name == obj {
return append(path, opObj)
}
if seen[name] {
return nil
}
if seen == nil {
seen = make(map[*types.TypeName]bool)
}
seen[name] = true
if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil {
return r
}
return nil
}
panic(T)
}
func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte {
for i := 0; i < list.Len(); i++ {
tparam := list.At(i)
path2 := appendOpArg(path, opTypeParam, i)
if r := find(obj, tparam, path2, seen); 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() *typeparams.TypeParamList
}
// abstraction of *types.{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 [AFM] have an integer operand.
var index int
switch code {
case opAt, opField, opMethod, opTypeParam:
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
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 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 opConstraint:
tparam, ok := t.(*typeparams.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.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
}

View File

@ -19,7 +19,7 @@ var (
File = keys.NewString("file", "") File = keys.NewString("file", "")
Directory = keys.New("directory", "") Directory = keys.New("directory", "")
URI = keys.New("URI", "") URI = keys.New("URI", "")
Package = keys.NewString("package", "") // Package ID Package = keys.NewString("package", "") // sorted comma-separated list of Package IDs
PackagePath = keys.NewString("package_path", "") PackagePath = keys.NewString("package_path", "")
Query = keys.New("query", "") Query = keys.New("query", "")
Snapshot = keys.NewUInt64("snapshot", "") Snapshot = keys.NewUInt64("snapshot", "")

View File

@ -29,7 +29,6 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"io" "io"
"io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -221,7 +220,7 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
switch hdr { switch hdr {
case "$$B\n": case "$$B\n":
var data []byte var data []byte
data, err = ioutil.ReadAll(buf) data, err = io.ReadAll(buf)
if err != nil { if err != nil {
break break
} }

View File

@ -22,17 +22,23 @@ import (
"strconv" "strconv"
"strings" "strings"
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/internal/tokeninternal" "golang.org/x/tools/internal/tokeninternal"
"golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typeparams"
) )
// IExportShallow encodes "shallow" export data for the specified package. // IExportShallow encodes "shallow" export data for the specified package.
// //
// No promises are made about the encoding other than that it can be // No promises are made about the encoding other than that it can be decoded by
// decoded by the same version of IIExportShallow. If you plan to save // the same version of IIExportShallow. If you plan to save export data in the
// export data in the file system, be sure to include a cryptographic // file system, be sure to include a cryptographic digest of the executable in
// digest of the executable in the key to avoid version skew. // the key to avoid version skew.
func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) { //
// If the provided reportf func is non-nil, it will be used for reporting bugs
// encountered during export.
// TODO(rfindley): remove reportf when we are confident enough in the new
// objectpath encoding.
func IExportShallow(fset *token.FileSet, pkg *types.Package, reportf ReportFunc) ([]byte, error) {
// In principle this operation can only fail if out.Write fails, // In principle this operation can only fail if out.Write fails,
// but that's impossible for bytes.Buffer---and as a matter of // but that's impossible for bytes.Buffer---and as a matter of
// fact iexportCommon doesn't even check for I/O errors. // fact iexportCommon doesn't even check for I/O errors.
@ -47,19 +53,27 @@ func IExportShallow(fset *token.FileSet, pkg *types.Package) ([]byte, error) {
// IImportShallow decodes "shallow" types.Package data encoded by // IImportShallow decodes "shallow" types.Package data encoded by
// IExportShallow in the same executable. This function cannot import data from // IExportShallow in the same executable. This function cannot import data from
// cmd/compile or gcexportdata.Write. // cmd/compile or gcexportdata.Write.
func IImportShallow(fset *token.FileSet, getPackage GetPackageFunc, data []byte, path string, insert InsertType) (*types.Package, error) { //
// The importer calls getPackages to obtain package symbols for all
// packages mentioned in the export data, including the one being
// decoded.
//
// If the provided reportf func is non-nil, it will be used for reporting bugs
// encountered during import.
// TODO(rfindley): remove reportf when we are confident enough in the new
// objectpath encoding.
func IImportShallow(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, path string, reportf ReportFunc) (*types.Package, error) {
const bundle = false const bundle = false
pkgs, err := iimportCommon(fset, getPackage, data, bundle, path, insert) const shallow = true
pkgs, err := iimportCommon(fset, getPackages, data, bundle, path, shallow, reportf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return pkgs[0], nil return pkgs[0], nil
} }
// InsertType is the type of a function that creates a types.TypeName // ReportFunc is the type of a function used to report formatted bugs.
// object for a named type and inserts it into the scope of the type ReportFunc = func(string, ...interface{})
// specified Package.
type InsertType = func(pkg *types.Package, name string)
// Current bundled export format version. Increase with each format change. // Current bundled export format version. Increase with each format change.
// 0: initial implementation // 0: initial implementation
@ -314,6 +328,7 @@ type iexporter struct {
version int version int
shallow bool // don't put types from other packages in the index shallow bool // don't put types from other packages in the index
objEncoder *objectpath.Encoder // encodes objects from other packages in shallow mode; lazily allocated
localpkg *types.Package // (nil in bundle mode) localpkg *types.Package // (nil in bundle mode)
// allPkgs tracks all packages that have been referenced by // allPkgs tracks all packages that have been referenced by
@ -354,6 +369,17 @@ func (p *iexporter) trace(format string, args ...interface{}) {
fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...) fmt.Printf(strings.Repeat("..", p.indent)+format+"\n", args...)
} }
// objectpathEncoder returns the lazily allocated objectpath.Encoder to use
// when encoding objects in other packages during shallow export.
//
// Using a shared Encoder amortizes some of cost of objectpath search.
func (p *iexporter) objectpathEncoder() *objectpath.Encoder {
if p.objEncoder == nil {
p.objEncoder = new(objectpath.Encoder)
}
return p.objEncoder
}
// stringOff returns the offset of s within the string section. // stringOff returns the offset of s within the string section.
// If not already present, it's added to the end. // If not already present, it's added to the end.
func (p *iexporter) stringOff(s string) uint64 { func (p *iexporter) stringOff(s string) uint64 {
@ -413,7 +439,6 @@ type exportWriter struct {
p *iexporter p *iexporter
data intWriter data intWriter
currPkg *types.Package
prevFile string prevFile string
prevLine int64 prevLine int64
prevColumn int64 prevColumn int64
@ -436,7 +461,6 @@ func (p *iexporter) doDecl(obj types.Object) {
}() }()
} }
w := p.newWriter() w := p.newWriter()
w.setPkg(obj.Pkg(), false)
switch obj := obj.(type) { switch obj := obj.(type) {
case *types.Var: case *types.Var:
@ -673,6 +697,9 @@ func (w *exportWriter) qualifiedType(obj *types.TypeName) {
w.pkg(obj.Pkg()) w.pkg(obj.Pkg())
} }
// TODO(rfindley): what does 'pkg' even mean here? It would be better to pass
// it in explicitly into signatures and structs that may use it for
// constructing fields.
func (w *exportWriter) typ(t types.Type, pkg *types.Package) { func (w *exportWriter) typ(t types.Type, pkg *types.Package) {
w.data.uint64(w.p.typOff(t, pkg)) w.data.uint64(w.p.typOff(t, pkg))
} }
@ -764,30 +791,53 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
case *types.Signature: case *types.Signature:
w.startType(signatureType) w.startType(signatureType)
w.setPkg(pkg, true) w.pkg(pkg)
w.signature(t) w.signature(t)
case *types.Struct: case *types.Struct:
w.startType(structType) w.startType(structType)
n := t.NumFields() n := t.NumFields()
// Even for struct{} we must emit some qualifying package, because that's
// what the compiler does, and thus that's what the importer expects.
fieldPkg := pkg
if n > 0 { if n > 0 {
w.setPkg(t.Field(0).Pkg(), true) // qualifying package for field objects fieldPkg = t.Field(0).Pkg()
} else {
w.setPkg(pkg, true)
} }
if fieldPkg == nil {
// TODO(rfindley): improve this very hacky logic.
//
// The importer expects a package to be set for all struct types, even
// those with no fields. A better encoding might be to set NumFields
// before pkg. setPkg panics with a nil package, which may be possible
// to reach with invalid packages (and perhaps valid packages, too?), so
// (arbitrarily) set the localpkg if available.
//
// Alternatively, we may be able to simply guarantee that pkg != nil, by
// reconsidering the encoding of constant values.
if w.p.shallow {
fieldPkg = w.p.localpkg
} else {
panic(internalErrorf("no package to set for empty struct"))
}
}
w.pkg(fieldPkg)
w.uint64(uint64(n)) w.uint64(uint64(n))
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
f := t.Field(i) f := t.Field(i)
if w.p.shallow {
w.objectPath(f)
}
w.pos(f.Pos()) w.pos(f.Pos())
w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg w.string(f.Name()) // unexported fields implicitly qualified by prior setPkg
w.typ(f.Type(), pkg) w.typ(f.Type(), fieldPkg)
w.bool(f.Anonymous()) w.bool(f.Anonymous())
w.string(t.Tag(i)) // note (or tag) w.string(t.Tag(i)) // note (or tag)
} }
case *types.Interface: case *types.Interface:
w.startType(interfaceType) w.startType(interfaceType)
w.setPkg(pkg, true) w.pkg(pkg)
n := t.NumEmbeddeds() n := t.NumEmbeddeds()
w.uint64(uint64(n)) w.uint64(uint64(n))
@ -802,10 +852,16 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
w.typ(ft, tPkg) w.typ(ft, tPkg)
} }
// See comment for struct fields. In shallow mode we change the encoding
// for interface methods that are promoted from other packages.
n = t.NumExplicitMethods() n = t.NumExplicitMethods()
w.uint64(uint64(n)) w.uint64(uint64(n))
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
m := t.ExplicitMethod(i) m := t.ExplicitMethod(i)
if w.p.shallow {
w.objectPath(m)
}
w.pos(m.Pos()) w.pos(m.Pos())
w.string(m.Name()) w.string(m.Name())
sig, _ := m.Type().(*types.Signature) sig, _ := m.Type().(*types.Signature)
@ -827,12 +883,61 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
} }
} }
func (w *exportWriter) setPkg(pkg *types.Package, write bool) { // objectPath writes the package and objectPath to use to look up obj in a
if write { // different package, when encoding in "shallow" mode.
w.pkg(pkg) //
// When doing a shallow import, the importer creates only the local package,
// and requests package symbols for dependencies from the client.
// However, certain types defined in the local package may hold objects defined
// (perhaps deeply) within another package.
//
// For example, consider the following:
//
// package a
// func F() chan * map[string] struct { X int }
//
// package b
// import "a"
// var B = a.F()
//
// In this example, the type of b.B holds fields defined in package a.
// In order to have the correct canonical objects for the field defined in the
// type of B, they are encoded as objectPaths and later looked up in the
// importer. The same problem applies to interface methods.
func (w *exportWriter) objectPath(obj types.Object) {
if obj.Pkg() == nil || obj.Pkg() == w.p.localpkg {
// obj.Pkg() may be nil for the builtin error.Error.
// In this case, or if obj is declared in the local package, no need to
// encode.
w.string("")
return
} }
objectPath, err := w.p.objectpathEncoder().For(obj)
w.currPkg = pkg if err != nil {
// Fall back to the empty string, which will cause the importer to create a
// new object, which matches earlier behavior. Creating a new object is
// sufficient for many purposes (such as type checking), but causes certain
// references algorithms to fail (golang/go#60819). However, we didn't
// notice this problem during months of gopls@v0.12.0 testing.
//
// TODO(golang/go#61674): this workaround is insufficient, as in the case
// where the field forwarded from an instantiated type that may not appear
// in the export data of the original package:
//
// // package a
// type A[P any] struct{ F P }
//
// // package b
// type B a.A[int]
//
// We need to update references algorithms not to depend on this
// de-duplication, at which point we may want to simply remove the
// workaround here.
w.string("")
return
}
w.string(string(objectPath))
w.pkg(obj.Pkg())
} }
func (w *exportWriter) signature(sig *types.Signature) { func (w *exportWriter) signature(sig *types.Signature) {
@ -1205,6 +1310,13 @@ type internalError string
func (e internalError) Error() string { return "gcimporter: " + string(e) } func (e internalError) Error() string { return "gcimporter: " + string(e) }
// TODO(adonovan): make this call panic, so that it's symmetric with errorf.
// Otherwise it's easy to forget to do anything with the error.
//
// TODO(adonovan): also, consider switching the names "errorf" and
// "internalErrorf" as the former is used for bugs, whose cause is
// internal inconsistency, whereas the latter is used for ordinary
// situations like bad input, whose cause is external.
func internalErrorf(format string, args ...interface{}) error { func internalErrorf(format string, args ...interface{}) error {
return internalError(fmt.Sprintf(format, args...)) return internalError(fmt.Sprintf(format, args...))
} }

View File

@ -21,6 +21,7 @@ import (
"sort" "sort"
"strings" "strings"
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typeparams"
) )
@ -85,7 +86,7 @@ const (
// If the export data version is not recognized or the format is otherwise // If the export data version is not recognized or the format is otherwise
// compromised, an error is returned. // compromised, an error is returned.
func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) { func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (int, *types.Package, error) {
pkgs, err := iimportCommon(fset, GetPackageFromMap(imports), data, false, path, nil) pkgs, err := iimportCommon(fset, GetPackagesFromMap(imports), data, false, path, false, nil)
if err != nil { if err != nil {
return 0, nil, err return 0, nil, err
} }
@ -94,33 +95,49 @@ func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []
// IImportBundle imports a set of packages from the serialized package bundle. // IImportBundle imports a set of packages from the serialized package bundle.
func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) { func IImportBundle(fset *token.FileSet, imports map[string]*types.Package, data []byte) ([]*types.Package, error) {
return iimportCommon(fset, GetPackageFromMap(imports), data, true, "", nil) return iimportCommon(fset, GetPackagesFromMap(imports), data, true, "", false, nil)
} }
// A GetPackageFunc is a function that gets the package with the given path // A GetPackagesFunc function obtains the non-nil symbols for a set of
// from the importer state, creating it (with the specified name) if necessary. // packages, creating and recursively importing them as needed. An
// It is an abstraction of the map historically used to memoize package creation. // implementation should store each package symbol is in the Pkg
// field of the items array.
// //
// Two calls with the same path must return the same package. // Any error causes importing to fail. This can be used to quickly read
// // the import manifest of an export data file without fully decoding it.
// If the given getPackage func returns nil, the import will fail. type GetPackagesFunc = func(items []GetPackagesItem) error
type GetPackageFunc = func(path, name string) *types.Package
// GetPackageFromMap returns a GetPackageFunc that retrieves packages from the // A GetPackagesItem is a request from the importer for the package
// given map of package path -> package. // symbol of the specified name and path.
// type GetPackagesItem struct {
// The resulting func may mutate m: if a requested package is not found, a new Name, Path string
// package will be inserted into m. Pkg *types.Package // to be filled in by GetPackagesFunc call
func GetPackageFromMap(m map[string]*types.Package) GetPackageFunc {
return func(path, name string) *types.Package { // private importer state
if _, ok := m[path]; !ok { pathOffset uint64
m[path] = types.NewPackage(path, name) nameIndex map[string]uint64
} }
return m[path]
// GetPackagesFromMap returns a GetPackagesFunc that retrieves
// packages from the given map of package path to package.
//
// The returned function may mutate m: each requested package that is not
// found is created with types.NewPackage and inserted into m.
func GetPackagesFromMap(m map[string]*types.Package) GetPackagesFunc {
return func(items []GetPackagesItem) error {
for i, item := range items {
pkg, ok := m[item.Path]
if !ok {
pkg = types.NewPackage(item.Path, item.Name)
m[item.Path] = pkg
}
items[i].Pkg = pkg
}
return nil
} }
} }
func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte, bundle bool, path string, insert InsertType) (pkgs []*types.Package, err error) { func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte, bundle bool, path string, shallow bool, reportf ReportFunc) (pkgs []*types.Package, err error) {
const currentVersion = iexportVersionCurrent const currentVersion = iexportVersionCurrent
version := int64(-1) version := int64(-1)
if !debug { if !debug {
@ -159,7 +176,7 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte,
sLen := int64(r.uint64()) sLen := int64(r.uint64())
var fLen int64 var fLen int64
var fileOffset []uint64 var fileOffset []uint64
if insert != nil { if shallow {
// Shallow mode uses a different position encoding. // Shallow mode uses a different position encoding.
fLen = int64(r.uint64()) fLen = int64(r.uint64())
fileOffset = make([]uint64, r.uint64()) fileOffset = make([]uint64, r.uint64())
@ -178,7 +195,8 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte,
p := iimporter{ p := iimporter{
version: int(version), version: int(version),
ipath: path, ipath: path,
insert: insert, shallow: shallow,
reportf: reportf,
stringData: stringData, stringData: stringData,
stringCache: make(map[uint64]string), stringCache: make(map[uint64]string),
@ -205,8 +223,9 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte,
p.typCache[uint64(i)] = pt p.typCache[uint64(i)] = pt
} }
pkgList := make([]*types.Package, r.uint64()) // Gather the relevant packages from the manifest.
for i := range pkgList { items := make([]GetPackagesItem, r.uint64())
for i := range items {
pkgPathOff := r.uint64() pkgPathOff := r.uint64()
pkgPath := p.stringAt(pkgPathOff) pkgPath := p.stringAt(pkgPathOff)
pkgName := p.stringAt(r.uint64()) pkgName := p.stringAt(r.uint64())
@ -215,29 +234,42 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte,
if pkgPath == "" { if pkgPath == "" {
pkgPath = path pkgPath = path
} }
pkg := getPackage(pkgPath, pkgName) items[i].Name = pkgName
if pkg == nil { items[i].Path = pkgPath
errorf("internal error: getPackage returned nil package for %s", pkgPath) items[i].pathOffset = pkgPathOff
} else if pkg.Name() != pkgName {
errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
}
if i == 0 && !bundle {
p.localpkg = pkg
}
p.pkgCache[pkgPathOff] = pkg
// Read index for package. // Read index for package.
nameIndex := make(map[string]uint64) nameIndex := make(map[string]uint64)
nSyms := r.uint64() nSyms := r.uint64()
// In shallow mode we don't expect an index for other packages. // In shallow mode, only the current package (i=0) has an index.
assert(nSyms == 0 || p.localpkg == pkg || p.insert == nil) assert(!(shallow && i > 0 && nSyms != 0))
for ; nSyms > 0; nSyms-- { for ; nSyms > 0; nSyms-- {
name := p.stringAt(r.uint64()) name := p.stringAt(r.uint64())
nameIndex[name] = r.uint64() nameIndex[name] = r.uint64()
} }
p.pkgIndex[pkg] = nameIndex items[i].nameIndex = nameIndex
}
// Request packages all at once from the client,
// enabling a parallel implementation.
if err := getPackages(items); err != nil {
return nil, err // don't wrap this error
}
// Check the results and complete the index.
pkgList := make([]*types.Package, len(items))
for i, item := range items {
pkg := item.Pkg
if pkg == nil {
errorf("internal error: getPackages returned nil package for %q", item.Path)
} else if pkg.Path() != item.Path {
errorf("internal error: getPackages returned wrong path %q, want %q", pkg.Path(), item.Path)
} else if pkg.Name() != item.Name {
errorf("internal error: getPackages returned wrong name %s for package %q, want %s", pkg.Name(), item.Path, item.Name)
}
p.pkgCache[item.pathOffset] = pkg
p.pkgIndex[pkg] = item.nameIndex
pkgList[i] = pkg pkgList[i] = pkg
} }
@ -296,6 +328,13 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte,
typ.Complete() typ.Complete()
} }
// Workaround for golang/go#61561. See the doc for instanceList for details.
for _, typ := range p.instanceList {
if iface, _ := typ.Underlying().(*types.Interface); iface != nil {
iface.Complete()
}
}
return pkgs, nil return pkgs, nil
} }
@ -308,8 +347,8 @@ type iimporter struct {
version int version int
ipath string ipath string
localpkg *types.Package shallow bool
insert func(pkg *types.Package, name string) // "shallow" mode only reportf ReportFunc // if non-nil, used to report bugs
stringData []byte stringData []byte
stringCache map[uint64]string stringCache map[uint64]string
@ -326,6 +365,12 @@ type iimporter struct {
fake fakeFileSet fake fakeFileSet
interfaceList []*types.Interface interfaceList []*types.Interface
// Workaround for the go/types bug golang/go#61561: instances produced during
// instantiation may contain incomplete interfaces. Here we only complete the
// underlying type of the instance, which is the most common case but doesn't
// handle parameterized interface literals defined deeper in the type.
instanceList []types.Type // instances for later completion (see golang/go#61561)
// Arguments for calls to SetConstraint that are deferred due to recursive types // Arguments for calls to SetConstraint that are deferred due to recursive types
later []setConstraintArgs later []setConstraintArgs
@ -357,13 +402,9 @@ func (p *iimporter) doDecl(pkg *types.Package, name string) {
off, ok := p.pkgIndex[pkg][name] off, ok := p.pkgIndex[pkg][name]
if !ok { if !ok {
// In "shallow" mode, call back to the application to // In deep mode, the index should be complete. In shallow
// find the object and insert it into the package scope. // mode, we should have already recursively loaded necessary
if p.insert != nil { // dependencies so the above Lookup succeeds.
assert(pkg != p.localpkg)
p.insert(pkg, name) // "can't fail"
return
}
errorf("%v.%v not in index", pkg, name) errorf("%v.%v not in index", pkg, name)
} }
@ -730,7 +771,8 @@ func (r *importReader) qualifiedIdent() (*types.Package, string) {
} }
func (r *importReader) pos() token.Pos { func (r *importReader) pos() token.Pos {
if r.p.insert != nil { // shallow mode if r.p.shallow {
// precise offsets are encoded only in shallow mode
return r.posv2() return r.posv2()
} }
if r.p.version >= iexportVersionPosCol { if r.p.version >= iexportVersionPosCol {
@ -831,13 +873,28 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
fields := make([]*types.Var, r.uint64()) fields := make([]*types.Var, r.uint64())
tags := make([]string, len(fields)) tags := make([]string, len(fields))
for i := range fields { for i := range fields {
var field *types.Var
if r.p.shallow {
field, _ = r.objectPathObject().(*types.Var)
}
fpos := r.pos() fpos := r.pos()
fname := r.ident() fname := r.ident()
ftyp := r.typ() ftyp := r.typ()
emb := r.bool() emb := r.bool()
tag := r.string() tag := r.string()
fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb) // Either this is not a shallow import, the field is local, or the
// encoded objectPath failed to produce an object (a bug).
//
// Even in this last, buggy case, fall back on creating a new field. As
// discussed in iexport.go, this is not correct, but mostly works and is
// preferable to failing (for now at least).
if field == nil {
field = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
}
fields[i] = field
tags[i] = tag tags[i] = tag
} }
return types.NewStruct(fields, tags) return types.NewStruct(fields, tags)
@ -853,6 +910,11 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
methods := make([]*types.Func, r.uint64()) methods := make([]*types.Func, r.uint64())
for i := range methods { for i := range methods {
var method *types.Func
if r.p.shallow {
method, _ = r.objectPathObject().(*types.Func)
}
mpos := r.pos() mpos := r.pos()
mname := r.ident() mname := r.ident()
@ -862,9 +924,12 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
if base != nil { if base != nil {
recv = types.NewVar(token.NoPos, r.currPkg, "", base) recv = types.NewVar(token.NoPos, r.currPkg, "", base)
} }
msig := r.signature(recv, nil, nil) msig := r.signature(recv, nil, nil)
methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
if method == nil {
method = types.NewFunc(mpos, r.currPkg, mname, msig)
}
methods[i] = method
} }
typ := newInterface(methods, embeddeds) typ := newInterface(methods, embeddeds)
@ -902,6 +967,9 @@ func (r *importReader) doType(base *types.Named) (res types.Type) {
// we must always use the methods of the base (orig) type. // we must always use the methods of the base (orig) type.
// TODO provide a non-nil *Environment // TODO provide a non-nil *Environment
t, _ := typeparams.Instantiate(nil, baseType, targs, false) t, _ := typeparams.Instantiate(nil, baseType, targs, false)
// Workaround for golang/go#61561. See the doc for instanceList for details.
r.p.instanceList = append(r.p.instanceList, t)
return t return t
case unionType: case unionType:
@ -920,6 +988,26 @@ func (r *importReader) kind() itag {
return itag(r.uint64()) return itag(r.uint64())
} }
// objectPathObject is the inverse of exportWriter.objectPath.
//
// In shallow mode, certain fields and methods may need to be looked up in an
// imported package. See the doc for exportWriter.objectPath for a full
// explanation.
func (r *importReader) objectPathObject() types.Object {
objPath := objectpath.Path(r.string())
if objPath == "" {
return nil
}
pkg := r.pkg()
obj, err := objectpath.Object(pkg, objPath)
if err != nil {
if r.p.reportf != nil {
r.p.reportf("failed to find object for objectPath %q: %v", objPath, err)
}
}
return obj
}
func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature { func (r *importReader) signature(recv *types.Var, rparams []*typeparams.TypeParam, tparams []*typeparams.TypeParam) *types.Signature {
params := r.paramList() params := r.paramList()
results := r.paramList() results := r.paramList()

View File

@ -13,6 +13,7 @@ import (
"io" "io"
"log" "log"
"os" "os"
"os/exec"
"reflect" "reflect"
"regexp" "regexp"
"runtime" "runtime"
@ -21,8 +22,6 @@ import (
"sync" "sync"
"time" "time"
exec "golang.org/x/sys/execabs"
"golang.org/x/tools/internal/event" "golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/keys" "golang.org/x/tools/internal/event/keys"
"golang.org/x/tools/internal/event/label" "golang.org/x/tools/internal/event/label"
@ -85,6 +84,7 @@ func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stde
// RunRaw runs the invocation, serializing requests only if they fight over // RunRaw runs the invocation, serializing requests only if they fight over
// go.mod changes. // 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) { 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)...) ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...)
defer done() defer done()
@ -95,23 +95,24 @@ func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer
stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv) stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv)
// If we encounter a load concurrency error, we need to retry serially. // If we encounter a load concurrency error, we need to retry serially.
if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) { if friendlyErr != nil && modConcurrencyError.MatchString(friendlyErr.Error()) {
return stdout, stderr, friendlyErr, err
}
event.Error(ctx, "Load concurrency error, will retry serially", err) event.Error(ctx, "Load concurrency error, will retry serially", err)
// Run serially by calling runPiped. // Run serially by calling runPiped.
stdout.Reset() stdout.Reset()
stderr.Reset() stderr.Reset()
friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr)
}
return stdout, stderr, friendlyErr, err 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) { func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
// Wait for 1 worker to become available. // Wait for 1 worker to become available.
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, nil, nil, ctx.Err() return nil, nil, ctx.Err(), ctx.Err()
case runner.inFlight <- struct{}{}: case runner.inFlight <- struct{}{}:
defer func() { <-runner.inFlight }() defer func() { <-runner.inFlight }()
} }
@ -121,6 +122,7 @@ func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes
return stdout, stderr, friendlyErr, err 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) { func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) {
// Make sure the runner is always initialized. // Make sure the runner is always initialized.
runner.initialize() runner.initialize()
@ -129,7 +131,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde
// runPiped commands. // runPiped commands.
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, ctx.Err() return ctx.Err(), ctx.Err()
case runner.serialized <- struct{}{}: case runner.serialized <- struct{}{}:
defer func() { <-runner.serialized }() defer func() { <-runner.serialized }()
} }
@ -139,7 +141,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde
for i := 0; i < maxInFlight; i++ { for i := 0; i < maxInFlight; i++ {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, ctx.Err() return ctx.Err(), ctx.Err()
case runner.inFlight <- struct{}{}: case runner.inFlight <- struct{}{}:
// Make sure we always "return" any workers we took. // Make sure we always "return" any workers we took.
defer func() { <-runner.inFlight }() defer func() { <-runner.inFlight }()
@ -172,6 +174,7 @@ type Invocation struct {
Logf func(format string, args ...interface{}) Logf func(format string, args ...interface{})
} }
// Postcondition: both error results have same nilness.
func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) { func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) {
rawError = i.run(ctx, stdout, stderr) rawError = i.run(ctx, stdout, stderr)
if rawError != nil { if rawError != nil {
@ -319,7 +322,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
// Per https://pkg.go.dev/os#File.Close, the call to 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 // should cause the Read call in io.Copy to unblock and return
// immediately, but we still need to receive from stdoutErr to confirm // immediately, but we still need to receive from stdoutErr to confirm
// that that has happened. // that it has happened.
<-stdoutErr <-stdoutErr
err2 = ctx.Err() err2 = ctx.Err()
} }
@ -333,7 +336,7 @@ func runCmdContext(ctx context.Context, cmd *exec.Cmd) (err error) {
// one goroutine at a time will call Write.” // one goroutine at a time will call Write.”
// //
// Since we're starting a goroutine that writes to cmd.Stdout, we must // Since we're starting a goroutine that writes to cmd.Stdout, we must
// also update cmd.Stderr so that that still holds. // also update cmd.Stderr so that it still holds.
func() { func() {
defer func() { recover() }() defer func() { recover() }()
if cmd.Stderr == prevStdout { if cmd.Stderr == prevStdout {

View File

@ -5,10 +5,6 @@
// Package packagesinternal exposes internal-only fields from go/packages. // Package packagesinternal exposes internal-only fields from go/packages.
package packagesinternal package packagesinternal
import (
"golang.org/x/tools/internal/gocommand"
)
var GetForTest = func(p interface{}) string { return "" } var GetForTest = func(p interface{}) string { return "" }
var GetDepsErrors = func(p interface{}) []*PackageError { return nil } var GetDepsErrors = func(p interface{}) []*PackageError { return nil }
@ -18,10 +14,6 @@ type PackageError struct {
Err string // the error itself Err string // the error itself
} }
var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil }
var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {}
var TypecheckCgo int var TypecheckCgo int
var DepsErrors int // must be set as a LoadMode to call GetDepsErrors var DepsErrors int // must be set as a LoadMode to call GetDepsErrors
var ForTest int // must be set as a LoadMode to call GetForTest var ForTest int // must be set as a LoadMode to call GetForTest

View File

@ -23,6 +23,7 @@
package typeparams package typeparams
import ( import (
"fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types" "go/types"
@ -125,6 +126,11 @@ func OriginMethod(fn *types.Func) *types.Func {
} }
} }
// In golang/go#61196, we observe another crash, this time inexplicable.
if gfn == nil {
panic(fmt.Sprintf("missing origin method for %s.%s; named == origin: %t, named.NumMethods(): %d, origin.NumMethods(): %d", named, fn, named == orig, named.NumMethods(), orig.NumMethods()))
}
return gfn.(*types.Func) return gfn.(*types.Func)
} }

View File

@ -30,7 +30,7 @@ func (xl termlist) String() string {
var buf bytes.Buffer var buf bytes.Buffer
for i, x := range xl { for i, x := range xl {
if i > 0 { if i > 0 {
buf.WriteString(" ") buf.WriteString(" | ")
} }
buf.WriteString(x.String()) buf.WriteString(x.String())
} }

View File

@ -129,7 +129,7 @@ func NamedTypeArgs(*types.Named) *TypeList {
} }
// NamedTypeOrigin is the identity method at this Go version. // NamedTypeOrigin is the identity method at this Go version.
func NamedTypeOrigin(named *types.Named) types.Type { func NamedTypeOrigin(named *types.Named) *types.Named {
return named return named
} }

View File

@ -103,7 +103,7 @@ func NamedTypeArgs(named *types.Named) *TypeList {
} }
// NamedTypeOrigin returns named.Orig(). // NamedTypeOrigin returns named.Orig().
func NamedTypeOrigin(named *types.Named) types.Type { func NamedTypeOrigin(named *types.Named) *types.Named {
return named.Origin() return named.Origin()
} }

View File

@ -14,7 +14,6 @@ import "go/types"
// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse) // 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
// T: &term{false, T} == {T} // set of type T // T: &term{false, T} == {T} // set of type T
// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t // ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
//
type term struct { type term struct {
tilde bool // valid if typ != nil tilde bool // valid if typ != nil
typ types.Type typ types.Type

172
vendor/golang.org/x/tools/internal/versions/gover.go generated vendored Normal file
View File

@ -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
}
}

19
vendor/golang.org/x/tools/internal/versions/types.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// 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/types"
)
// GoVersion returns the Go version of the type package.
// It returns zero if no version can be determined.
func GoVersion(pkg *types.Package) string {
// TODO(taking): x/tools can call GoVersion() [from 1.21] after 1.25.
if pkg, ok := any(pkg).(interface{ GoVersion() string }); ok {
return pkg.GoVersion()
}
return ""
}

View File

@ -0,0 +1,20 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.22
// +build !go1.22
package versions
import (
"go/ast"
"go/types"
)
// FileVersions always reports the a file's Go version as the
// zero version at this Go version.
func FileVersions(info *types.Info, file *ast.File) string { return "" }
// InitFileVersions is a noop at this Go version.
func InitFileVersions(*types.Info) {}

View File

@ -0,0 +1,24 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.22
// +build go1.22
package versions
import (
"go/ast"
"go/types"
)
// FileVersions maps a file to the file's semantic Go version.
// The reported version is the zero version if a version cannot be determined.
func FileVersions(info *types.Info, file *ast.File) string {
return info.FileVersions[file]
}
// InitFileVersions initializes info to record Go versions for Go files.
func InitFileVersions(info *types.Info) {
info.FileVersions = make(map[*ast.File]string)
}

View File

@ -0,0 +1,49 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.22
// +build !go1.22
package versions
// 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 {
if len(v) < 2 || v[:2] != "go" {
return ""
}
return v[2:]
}

View File

@ -0,0 +1,38 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.22
// +build go1.22
package versions
import (
"go/version"
)
// 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 { return version.Lang(x) }
// 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 version.Compare(x, y) }
// IsValid reports whether the version x is valid.
func IsValid(x string) bool { return version.IsValid(x) }

21
vendor/modules.txt vendored
View File

@ -30,7 +30,7 @@ github.com/containerd/containerd/platforms
# github.com/containerd/log v0.1.0 # github.com/containerd/log v0.1.0
## explicit; go 1.20 ## explicit; go 1.20
github.com/containerd/log github.com/containerd/log
# github.com/creack/pty v1.1.18 # github.com/creack/pty v1.1.21
## explicit; go 1.13 ## explicit; go 1.13
github.com/creack/pty github.com/creack/pty
# github.com/distribution/reference v0.5.0 # github.com/distribution/reference v0.5.0
@ -142,7 +142,7 @@ github.com/golang/protobuf/ptypes
github.com/golang/protobuf/ptypes/any github.com/golang/protobuf/ptypes/any
github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/duration
github.com/golang/protobuf/ptypes/timestamp github.com/golang/protobuf/ptypes/timestamp
# github.com/google/go-cmp v0.5.9 # github.com/google/go-cmp v0.6.0
## explicit; go 1.13 ## explicit; go 1.13
github.com/google/go-cmp/cmp github.com/google/go-cmp/cmp
github.com/google/go-cmp/cmp/cmpopts github.com/google/go-cmp/cmp/cmpopts
@ -309,23 +309,22 @@ go.opentelemetry.io/otel/trace
## explicit; go 1.18 ## explicit; go 1.18
golang.org/x/crypto/ed25519 golang.org/x/crypto/ed25519
golang.org/x/crypto/pbkdf2 golang.org/x/crypto/pbkdf2
# golang.org/x/mod v0.11.0 # golang.org/x/mod v0.14.0
## explicit; go 1.17 ## explicit; go 1.18
golang.org/x/mod/semver golang.org/x/mod/semver
# golang.org/x/net v0.17.0 # golang.org/x/net v0.19.0
## explicit; go 1.17 ## explicit; go 1.18
golang.org/x/net/http/httpguts golang.org/x/net/http/httpguts
golang.org/x/net/http2 golang.org/x/net/http2
golang.org/x/net/http2/hpack golang.org/x/net/http2/hpack
golang.org/x/net/idna golang.org/x/net/idna
golang.org/x/net/internal/timeseries golang.org/x/net/internal/timeseries
golang.org/x/net/trace golang.org/x/net/trace
# golang.org/x/sync v0.3.0 # golang.org/x/sync v0.6.0
## explicit; go 1.17 ## explicit; go 1.18
golang.org/x/sync/errgroup golang.org/x/sync/errgroup
# golang.org/x/sys v0.15.0 # golang.org/x/sys v0.15.0
## explicit; go 1.18 ## explicit; go 1.18
golang.org/x/sys/execabs
golang.org/x/sys/plan9 golang.org/x/sys/plan9
golang.org/x/sys/unix golang.org/x/sys/unix
golang.org/x/sys/windows golang.org/x/sys/windows
@ -342,12 +341,13 @@ golang.org/x/text/width
# golang.org/x/time v0.3.0 # golang.org/x/time v0.3.0
## explicit ## explicit
golang.org/x/time/rate golang.org/x/time/rate
# golang.org/x/tools v0.10.0 # golang.org/x/tools v0.16.0
## explicit; go 1.18 ## explicit; go 1.18
golang.org/x/tools/cmd/stringer golang.org/x/tools/cmd/stringer
golang.org/x/tools/go/gcexportdata golang.org/x/tools/go/gcexportdata
golang.org/x/tools/go/internal/packagesdriver golang.org/x/tools/go/internal/packagesdriver
golang.org/x/tools/go/packages golang.org/x/tools/go/packages
golang.org/x/tools/go/types/objectpath
golang.org/x/tools/internal/event golang.org/x/tools/internal/event
golang.org/x/tools/internal/event/core golang.org/x/tools/internal/event/core
golang.org/x/tools/internal/event/keys golang.org/x/tools/internal/event/keys
@ -360,6 +360,7 @@ golang.org/x/tools/internal/pkgbits
golang.org/x/tools/internal/tokeninternal golang.org/x/tools/internal/tokeninternal
golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typeparams
golang.org/x/tools/internal/typesinternal golang.org/x/tools/internal/typesinternal
golang.org/x/tools/internal/versions
# google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 # google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98
## explicit; go 1.19 ## explicit; go 1.19
google.golang.org/genproto/googleapis/rpc/status google.golang.org/genproto/googleapis/rpc/status