Update gitea-vet to v0.2.1 (#12282)
* change to new code location * vendor * tagged version v0.2.0 * gitea-vet v0.2.1 Co-authored-by: techknowlogick <techknowlogick@gitea.io>release/v1.15
parent
eb60a5d054
commit
ee97e6a66a
2
Makefile
2
Makefile
|
@ -222,7 +222,7 @@ vet:
|
||||||
# Default vet
|
# Default vet
|
||||||
$(GO) vet $(GO_PACKAGES)
|
$(GO) vet $(GO_PACKAGES)
|
||||||
# Custom vet
|
# Custom vet
|
||||||
$(GO) build -mod=vendor gitea.com/jolheiser/gitea-vet
|
$(GO) build -mod=vendor code.gitea.io/gitea-vet
|
||||||
$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
||||||
|
|
||||||
.PHONY: $(TAGS_EVIDENCE)
|
.PHONY: $(TAGS_EVIDENCE)
|
||||||
|
|
2
build.go
2
build.go
|
@ -25,7 +25,7 @@ import (
|
||||||
_ "golang.org/x/tools/cover"
|
_ "golang.org/x/tools/cover"
|
||||||
|
|
||||||
// for vet
|
// for vet
|
||||||
_ "gitea.com/jolheiser/gitea-vet"
|
_ "code.gitea.io/gitea-vet"
|
||||||
|
|
||||||
// for swagger
|
// for swagger
|
||||||
_ "github.com/go-swagger/go-swagger/cmd/swagger"
|
_ "github.com/go-swagger/go-swagger/cmd/swagger"
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -4,7 +4,7 @@ go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.45.0 // indirect
|
cloud.google.com/go v0.45.0 // indirect
|
||||||
gitea.com/jolheiser/gitea-vet v0.1.0
|
code.gitea.io/gitea-vet v0.2.1
|
||||||
gitea.com/lunny/levelqueue v0.3.0
|
gitea.com/lunny/levelqueue v0.3.0
|
||||||
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b
|
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b
|
||||||
gitea.com/macaron/cache v0.0.0-20190822004001-a6e7fee4ee76
|
gitea.com/macaron/cache v0.0.0-20190822004001-a6e7fee4ee76
|
||||||
|
@ -104,12 +104,12 @@ require (
|
||||||
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691
|
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691
|
||||||
github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60
|
github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
||||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1
|
||||||
golang.org/x/text v0.3.2
|
golang.org/x/text v0.3.2
|
||||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
|
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
|
||||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224
|
golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d
|
||||||
google.golang.org/appengine v1.6.5 // indirect
|
google.golang.org/appengine v1.6.5 // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
|
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
|
||||||
|
|
16
go.sum
16
go.sum
|
@ -9,8 +9,8 @@ cloud.google.com/go v0.45.0 h1:bALuGBSgE+BD4rxsopAYlqjcwqcQtye6pWG4bC3N/k0=
|
||||||
cloud.google.com/go v0.45.0/go.mod h1:452BcPOeI9AZfbvDw0Tbo7D32wA+WX9WME8AZwMEDZU=
|
cloud.google.com/go v0.45.0/go.mod h1:452BcPOeI9AZfbvDw0Tbo7D32wA+WX9WME8AZwMEDZU=
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
gitea.com/jolheiser/gitea-vet v0.1.0 h1:gJEms9YWbIcrPOEmDOJ+5JZXCYFxNpwxlI73uRulAi4=
|
code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s=
|
||||||
gitea.com/jolheiser/gitea-vet v0.1.0/go.mod h1:2Oa6TAdEp1N/38oBNh3ZeiSEER60D/CeDaBFv2sdH58=
|
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
|
||||||
gitea.com/lunny/levelqueue v0.3.0 h1:MHn1GuSZkxvVEDMyAPqlc7A3cOW+q8RcGhRgH/xtm6I=
|
gitea.com/lunny/levelqueue v0.3.0 h1:MHn1GuSZkxvVEDMyAPqlc7A3cOW+q8RcGhRgH/xtm6I=
|
||||||
gitea.com/lunny/levelqueue v0.3.0/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
|
gitea.com/lunny/levelqueue v0.3.0/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
|
||||||
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b h1:vXt85uYV17KURaUlhU7v4GbCShkqRZDSfo0TkC0YCjQ=
|
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b h1:vXt85uYV17KURaUlhU7v4GbCShkqRZDSfo0TkC0YCjQ=
|
||||||
|
@ -775,6 +775,7 @@ github.com/yuin/goldmark v1.1.7/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||||
github.com/yuin/goldmark v1.1.22/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.22/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.25 h1:isv+Q6HQAmmL2Ofcmg8QauBmDPlUUnSoNhEcC940Rds=
|
github.com/yuin/goldmark v1.1.25 h1:isv+Q6HQAmmL2Ofcmg8QauBmDPlUUnSoNhEcC940Rds=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
|
github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 h1:VWSxtAiQNh3zgHJpdpkpVYjTPqRE3P6UZCOPa1nRDio=
|
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 h1:VWSxtAiQNh3zgHJpdpkpVYjTPqRE3P6UZCOPa1nRDio=
|
||||||
|
@ -817,6 +818,8 @@ golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8U
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -832,6 +835,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zH
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||||
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 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -860,8 +865,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -876,6 +881,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-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=
|
||||||
|
@ -949,6 +955,8 @@ golang.org/x/tools v0.0.0-20200225230052-807dcd883420 h1:4RJNOV+2rLxMEfr6QIpC7GE
|
||||||
golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 h1:azwY/v0y0K4mFHVsg5+UrTgchqALYWpqVo6vL5OmkmI=
|
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 h1:azwY/v0y0K4mFHVsg5+UrTgchqALYWpqVo6vL5OmkmI=
|
||||||
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
|
golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d h1:XZxUC4/ZNKTjrT4/Oc9gCgIYnzPW3/CefdPjsndrVWM=
|
||||||
|
golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# The full repository name
|
||||||
|
repo: gitea/gitea-vet
|
||||||
|
|
||||||
|
# Service type (gitea or github)
|
||||||
|
service: gitea
|
||||||
|
|
||||||
|
# Base URL for Gitea instance if using gitea service type (optional)
|
||||||
|
base-url: https://gitea.com
|
||||||
|
|
||||||
|
# Changelog groups and which labeled PRs to add to each group
|
||||||
|
groups:
|
||||||
|
-
|
||||||
|
name: BREAKING
|
||||||
|
labels:
|
||||||
|
- breaking
|
||||||
|
-
|
||||||
|
name: FEATURES
|
||||||
|
labels:
|
||||||
|
- feature
|
||||||
|
-
|
||||||
|
name: BUGFIXES
|
||||||
|
labels:
|
||||||
|
- bug
|
||||||
|
-
|
||||||
|
name: ENHANCEMENTS
|
||||||
|
labels:
|
||||||
|
- enhancement
|
||||||
|
|
||||||
|
# regex indicating which labels to skip for the changelog
|
||||||
|
skip-labels: skip-changelog|backport\/.+
|
|
@ -0,0 +1,45 @@
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: compliance
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: arm64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: check
|
||||||
|
pull: always
|
||||||
|
image: golang:1.14
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.cn
|
||||||
|
commands:
|
||||||
|
- make build
|
||||||
|
- make lint
|
||||||
|
- make vet
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: build-master
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build
|
||||||
|
pull: always
|
||||||
|
image: techknowlogick/xgo:latest
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.cn
|
||||||
|
commands:
|
||||||
|
- make build
|
|
@ -0,0 +1,23 @@
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- deadcode
|
||||||
|
- dogsled
|
||||||
|
- dupl
|
||||||
|
- errcheck
|
||||||
|
- gocognit
|
||||||
|
- goconst
|
||||||
|
- gocritic
|
||||||
|
- gocyclo
|
||||||
|
- gofmt
|
||||||
|
- golint
|
||||||
|
- gosimple
|
||||||
|
- govet
|
||||||
|
- maligned
|
||||||
|
- misspell
|
||||||
|
- prealloc
|
||||||
|
- staticcheck
|
||||||
|
- structcheck
|
||||||
|
- typecheck
|
||||||
|
- unparam
|
||||||
|
- unused
|
||||||
|
- varcheck
|
|
@ -0,0 +1,11 @@
|
||||||
|
## [v0.2.1](https://gitea.com/gitea/gitea-vet/releases/tag/v0.2.1) - 2020-08-15
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Split migration check to Deps and Imports (#9)
|
||||||
|
|
||||||
|
## [0.2.0](https://gitea.com/gitea/gitea-vet/pulls?q=&type=all&state=closed&milestone=1272) - 2020-07-20
|
||||||
|
|
||||||
|
* FEATURES
|
||||||
|
* Add migrations check (#5)
|
||||||
|
* BUGFIXES
|
||||||
|
* Correct Import Paths (#6)
|
|
@ -0,0 +1,22 @@
|
||||||
|
GO ?= go
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
$(GO) build
|
||||||
|
|
||||||
|
.PHONY: fmt
|
||||||
|
fmt:
|
||||||
|
$(GO) fmt ./...
|
||||||
|
|
||||||
|
.PHONY: vet
|
||||||
|
vet: build
|
||||||
|
$(GO) vet ./...
|
||||||
|
$(GO) vet -vettool=gitea-vet ./...
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint:
|
||||||
|
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
export BINARY="golangci-lint"; \
|
||||||
|
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(shell $(GO) env GOPATH)/bin v1.24.0; \
|
||||||
|
fi
|
||||||
|
golangci-lint run --timeout 5m
|
|
@ -0,0 +1,11 @@
|
||||||
|
# gitea-vet
|
||||||
|
|
||||||
|
[![Build Status](https://drone.gitea.com/api/badges/gitea/gitea-vet/status.svg)](https://drone.gitea.com/gitea/gitea-vet)
|
||||||
|
|
||||||
|
`go vet` tool for Gitea
|
||||||
|
|
||||||
|
| Analyzer | Description |
|
||||||
|
|------------|-----------------------------------------------------------------------------|
|
||||||
|
| Imports | Checks for import sorting. stdlib->code.gitea.io->other |
|
||||||
|
| License | Checks file headers for some form of `Copyright...YYYY...Gitea/Gogs` |
|
||||||
|
| Migrations | Checks for black-listed packages in `code.gitea.io/gitea/models/migrations` |
|
|
@ -12,7 +12,7 @@ import (
|
||||||
|
|
||||||
var Imports = &analysis.Analyzer{
|
var Imports = &analysis.Analyzer{
|
||||||
Name: "imports",
|
Name: "imports",
|
||||||
Doc: "check for import order.",
|
Doc: "check for import order",
|
||||||
Run: runImports,
|
Run: runImports,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,11 +22,12 @@ func runImports(pass *analysis.Pass) (interface{}, error) {
|
||||||
for _, im := range file.Imports {
|
for _, im := range file.Imports {
|
||||||
var lvl int
|
var lvl int
|
||||||
val := im.Path.Value
|
val := im.Path.Value
|
||||||
if importHasPrefix(val, "code.gitea.io") {
|
switch {
|
||||||
|
case importHasPrefix(val, "code.gitea.io"):
|
||||||
lvl = 2
|
lvl = 2
|
||||||
} else if strings.Contains(val, ".") {
|
case strings.Contains(val, "."):
|
||||||
lvl = 3
|
lvl = 3
|
||||||
} else {
|
default:
|
||||||
lvl = 1
|
lvl = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,12 +44,3 @@ func runImports(pass *analysis.Pass) (interface{}, error) {
|
||||||
func importHasPrefix(s, p string) bool {
|
func importHasPrefix(s, p string) bool {
|
||||||
return strings.HasPrefix(s, "\""+p)
|
return strings.HasPrefix(s, "\""+p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sliceHasPrefix(s string, prefixes ...string) bool {
|
|
||||||
for _, p := range prefixes {
|
|
||||||
if importHasPrefix(s, p) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
|
@ -19,7 +19,7 @@ var (
|
||||||
|
|
||||||
var License = &analysis.Analyzer{
|
var License = &analysis.Analyzer{
|
||||||
Name: "license",
|
Name: "license",
|
||||||
Doc: "check for a copyright header.",
|
Doc: "check for a copyright header",
|
||||||
Run: runLicense,
|
Run: runLicense,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package checks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/analysis"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Migrations = &analysis.Analyzer{
|
||||||
|
Name: "migrations",
|
||||||
|
Doc: "check migrations for black-listed packages.",
|
||||||
|
Run: checkMigrations,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
migrationDepBlockList = []string{
|
||||||
|
"code.gitea.io/gitea/models",
|
||||||
|
}
|
||||||
|
migrationImpBlockList = []string{
|
||||||
|
"code.gitea.io/gitea/modules/structs",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkMigrations(pass *analysis.Pass) (interface{}, error) {
|
||||||
|
if !strings.EqualFold(pass.Pkg.Path(), "code.gitea.io/gitea/models/migrations") {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := exec.LookPath("go"); err != nil {
|
||||||
|
return nil, errors.New("go was not found in the PATH")
|
||||||
|
}
|
||||||
|
|
||||||
|
depsCmd := exec.Command("go", "list", "-f", `{{join .Deps "\n"}}`, "code.gitea.io/gitea/models/migrations")
|
||||||
|
depsOut, err := depsCmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
deps := strings.Split(string(depsOut), "\n")
|
||||||
|
for _, dep := range deps {
|
||||||
|
if stringInSlice(dep, migrationDepBlockList) {
|
||||||
|
pass.Reportf(0, "code.gitea.io/gitea/models/migrations cannot depend on the following packages: %s", migrationDepBlockList)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impsCmd := exec.Command("go", "list", "-f", `{{join .Imports "\n"}}`, "code.gitea.io/gitea/models/migrations")
|
||||||
|
impsOut, err := impsCmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
imps := strings.Split(string(impsOut), "\n")
|
||||||
|
for _, imp := range imps {
|
||||||
|
if stringInSlice(imp, migrationImpBlockList) {
|
||||||
|
pass.Reportf(0, "code.gitea.io/gitea/models/migrations cannot import the following packages: %s", migrationImpBlockList)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringInSlice(needle string, haystack []string) bool {
|
||||||
|
for _, h := range haystack {
|
||||||
|
if strings.EqualFold(needle, h) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
2
vendor/gitea.com/jolheiser/gitea-vet/go.mod → vendor/code.gitea.io/gitea-vet/go.mod
generated
vendored
2
vendor/gitea.com/jolheiser/gitea-vet/go.mod → vendor/code.gitea.io/gitea-vet/go.mod
generated
vendored
|
@ -1,4 +1,4 @@
|
||||||
module gitea.com/jolheiser/gitea-vet
|
module code.gitea.io/gitea-vet
|
||||||
|
|
||||||
go 1.14
|
go 1.14
|
||||||
|
|
0
vendor/gitea.com/jolheiser/gitea-vet/go.sum → vendor/code.gitea.io/gitea-vet/go.sum
generated
vendored
0
vendor/gitea.com/jolheiser/gitea-vet/go.sum → vendor/code.gitea.io/gitea-vet/go.sum
generated
vendored
|
@ -5,7 +5,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.com/jolheiser/gitea-vet/checks"
|
"code.gitea.io/gitea-vet/checks"
|
||||||
|
|
||||||
"golang.org/x/tools/go/analysis/unitchecker"
|
"golang.org/x/tools/go/analysis/unitchecker"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,5 +14,6 @@ func main() {
|
||||||
unitchecker.Main(
|
unitchecker.Main(
|
||||||
checks.Imports,
|
checks.Imports,
|
||||||
checks.License,
|
checks.License,
|
||||||
|
checks.Migrations,
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
.PHONY: build
|
|
||||||
build:
|
|
||||||
go build
|
|
||||||
|
|
||||||
.PHONY: fmt
|
|
||||||
fmt:
|
|
||||||
go fmt ./...
|
|
|
@ -1,7 +0,0 @@
|
||||||
# gitea-vet
|
|
||||||
`go vet` tool for Gitea
|
|
||||||
|
|
||||||
| Analyzer | Description |
|
|
||||||
|----------|---------------------------------------------------------------------|
|
|
||||||
| Imports | Checks for import sorting. stdlib->code.gitea.io->other |
|
|
||||||
| License | Checks file headers for some form of `Copyright...YYYY...Gitea/Gogs`|
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"golang.org/x/tools/internal/analysisinternal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An Analyzer describes an analysis function and its options.
|
// An Analyzer describes an analysis function and its options.
|
||||||
|
@ -69,6 +71,17 @@ type Analyzer struct {
|
||||||
|
|
||||||
func (a *Analyzer) String() string { return a.Name }
|
func (a *Analyzer) String() string { return a.Name }
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Set the analysisinternal functions to be able to pass type errors
|
||||||
|
// to the Pass type without modifying the go/analysis API.
|
||||||
|
analysisinternal.SetTypeErrors = func(p interface{}, errors []types.Error) {
|
||||||
|
p.(*Pass).typeErrors = errors
|
||||||
|
}
|
||||||
|
analysisinternal.GetTypeErrors = func(p interface{}) []types.Error {
|
||||||
|
return p.(*Pass).typeErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A Pass provides information to the Run function that
|
// A Pass provides information to the Run function that
|
||||||
// applies a specific analyzer to a single Go package.
|
// applies a specific analyzer to a single Go package.
|
||||||
//
|
//
|
||||||
|
@ -138,6 +151,9 @@ type Pass struct {
|
||||||
// WARNING: This is an experimental API and may change in the future.
|
// WARNING: This is an experimental API and may change in the future.
|
||||||
AllObjectFacts func() []ObjectFact
|
AllObjectFacts func() []ObjectFact
|
||||||
|
|
||||||
|
// typeErrors contains types.Errors that are associated with the pkg.
|
||||||
|
typeErrors []types.Error
|
||||||
|
|
||||||
/* Further fields may be added in future. */
|
/* Further fields may be added in future. */
|
||||||
// For example, suggested or applied refactorings.
|
// For example, suggested or applied refactorings.
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,6 +170,15 @@ Diagnostic is defined as:
|
||||||
The optional Category field is a short identifier that classifies the
|
The optional Category field is a short identifier that classifies the
|
||||||
kind of message when an analysis produces several kinds of diagnostic.
|
kind of message when an analysis produces several kinds of diagnostic.
|
||||||
|
|
||||||
|
Many analyses want to associate diagnostics with a severity level.
|
||||||
|
Because Diagnostic does not have a severity level field, an Analyzer's
|
||||||
|
diagnostics effectively all have the same severity level. To separate which
|
||||||
|
diagnostics are high severity and which are low severity, expose multiple
|
||||||
|
Analyzers instead. Analyzers should also be separated when their
|
||||||
|
diagnostics belong in different groups, or could be tagged differently
|
||||||
|
before being shown to the end user. Analyzers should document their severity
|
||||||
|
level to help downstream tools surface diagnostics properly.
|
||||||
|
|
||||||
Most Analyzers inspect typed Go syntax trees, but a few, such as asmdecl
|
Most Analyzers inspect typed Go syntax trees, but a few, such as asmdecl
|
||||||
and buildtag, inspect the raw text of Go source files or even non-Go
|
and buildtag, inspect the raw text of Go source files or even non-Go
|
||||||
files such as assembly. To report a diagnostic against a line of a
|
files such as assembly. To report a diagnostic against a line of a
|
||||||
|
|
|
@ -382,7 +382,7 @@ func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis.
|
||||||
func (tree JSONTree) Print() {
|
func (tree JSONTree) Print() {
|
||||||
data, err := json.MarshalIndent(tree, "", "\t")
|
data, err := json.MarshalIndent(tree, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("internal error: JSON marshalling failed: %v", err)
|
log.Panicf("internal error: JSON marshaling failed: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("%s\n", data)
|
fmt.Printf("%s\n", data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,7 @@ import (
|
||||||
|
|
||||||
var debug = false
|
var debug = false
|
||||||
|
|
||||||
// GetSizes returns the sizes used by the underlying driver with the given parameters.
|
func GetSizes(ctx context.Context, buildFlags, env []string, gocmdRunner *gocommand.Runner, dir string) (types.Sizes, error) {
|
||||||
func GetSizes(ctx context.Context, buildFlags, env []string, dir string, usesExportData bool) (types.Sizes, error) {
|
|
||||||
// TODO(matloob): Clean this up. This code is mostly a copy of packages.findExternalDriver.
|
// TODO(matloob): Clean this up. This code is mostly a copy of packages.findExternalDriver.
|
||||||
const toolPrefix = "GOPACKAGESDRIVER="
|
const toolPrefix = "GOPACKAGESDRIVER="
|
||||||
tool := ""
|
tool := ""
|
||||||
|
@ -40,7 +39,7 @@ func GetSizes(ctx context.Context, buildFlags, env []string, dir string, usesExp
|
||||||
}
|
}
|
||||||
|
|
||||||
if tool == "off" {
|
if tool == "off" {
|
||||||
return GetSizesGolist(ctx, buildFlags, env, dir, usesExportData)
|
return GetSizesGolist(ctx, buildFlags, env, gocmdRunner, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := json.Marshal(struct {
|
req, err := json.Marshal(struct {
|
||||||
|
@ -76,7 +75,7 @@ func GetSizes(ctx context.Context, buildFlags, env []string, dir string, usesExp
|
||||||
return response.Sizes, nil
|
return response.Sizes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSizesGolist(ctx context.Context, buildFlags, env []string, dir string, usesExportData bool) (types.Sizes, error) {
|
func GetSizesGolist(ctx context.Context, buildFlags, env []string, gocmdRunner *gocommand.Runner, dir string) (types.Sizes, error) {
|
||||||
inv := gocommand.Invocation{
|
inv := gocommand.Invocation{
|
||||||
Verb: "list",
|
Verb: "list",
|
||||||
Args: []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"},
|
Args: []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"},
|
||||||
|
@ -84,7 +83,7 @@ func GetSizesGolist(ctx context.Context, buildFlags, env []string, dir string, u
|
||||||
BuildFlags: buildFlags,
|
BuildFlags: buildFlags,
|
||||||
WorkingDir: dir,
|
WorkingDir: dir,
|
||||||
}
|
}
|
||||||
stdout, stderr, friendlyErr, rawErr := inv.RunRaw(ctx)
|
stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv)
|
||||||
var goarch, compiler string
|
var goarch, compiler string
|
||||||
if rawErr != nil {
|
if rawErr != nil {
|
||||||
if strings.Contains(rawErr.Error(), "cannot find main module") {
|
if strings.Contains(rawErr.Error(), "cannot find main module") {
|
||||||
|
@ -96,7 +95,7 @@ func GetSizesGolist(ctx context.Context, buildFlags, env []string, dir string, u
|
||||||
Env: env,
|
Env: env,
|
||||||
WorkingDir: dir,
|
WorkingDir: dir,
|
||||||
}
|
}
|
||||||
envout, enverr := inv.Run(ctx)
|
envout, enverr := gocmdRunner.Run(ctx, inv)
|
||||||
if enverr != nil {
|
if enverr != nil {
|
||||||
return nil, enverr
|
return nil, enverr
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
|
|
||||||
"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/xerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// debug controls verbose logging.
|
// debug controls verbose logging.
|
||||||
|
@ -89,6 +89,10 @@ type golistState struct {
|
||||||
rootDirsError error
|
rootDirsError error
|
||||||
rootDirs map[string]string
|
rootDirs map[string]string
|
||||||
|
|
||||||
|
goVersionOnce sync.Once
|
||||||
|
goVersionError error
|
||||||
|
goVersion string // third field of 'go version'
|
||||||
|
|
||||||
// vendorDirs caches the (non)existence of vendor directories.
|
// vendorDirs caches the (non)existence of vendor directories.
|
||||||
vendorDirs map[string]bool
|
vendorDirs map[string]bool
|
||||||
}
|
}
|
||||||
|
@ -142,7 +146,7 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
|
||||||
sizeswg.Add(1)
|
sizeswg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
var sizes types.Sizes
|
var sizes types.Sizes
|
||||||
sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg))
|
sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, cfg.BuildFlags, cfg.Env, cfg.gocmdRunner, cfg.Dir)
|
||||||
// types.SizesFor always returns nil or a *types.StdSizes.
|
// types.SizesFor always returns nil or a *types.StdSizes.
|
||||||
response.dr.Sizes, _ = sizes.(*types.StdSizes)
|
response.dr.Sizes, _ = sizes.(*types.StdSizes)
|
||||||
sizeswg.Done()
|
sizeswg.Done()
|
||||||
|
@ -381,7 +385,7 @@ type jsonPackage struct {
|
||||||
Imports []string
|
Imports []string
|
||||||
ImportMap map[string]string
|
ImportMap map[string]string
|
||||||
Deps []string
|
Deps []string
|
||||||
Module *packagesinternal.Module
|
Module *Module
|
||||||
TestGoFiles []string
|
TestGoFiles []string
|
||||||
TestImports []string
|
TestImports []string
|
||||||
XTestGoFiles []string
|
XTestGoFiles []string
|
||||||
|
@ -502,10 +506,19 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
||||||
errkind = "use of internal package not allowed"
|
errkind = "use of internal package not allowed"
|
||||||
}
|
}
|
||||||
if errkind != "" {
|
if errkind != "" {
|
||||||
if len(old.Error.ImportStack) < 2 {
|
if len(old.Error.ImportStack) < 1 {
|
||||||
return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack with fewer than two elements`, errkind)
|
return nil, fmt.Errorf(`internal error: go list gave a %q error with empty import stack`, errkind)
|
||||||
|
}
|
||||||
|
importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-1]
|
||||||
|
if importingPkg == old.ImportPath {
|
||||||
|
// Using an older version of Go which put this package itself on top of import
|
||||||
|
// stack, instead of the importer. Look for importer in second from top
|
||||||
|
// position.
|
||||||
|
if len(old.Error.ImportStack) < 2 {
|
||||||
|
return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack without importing package`, errkind)
|
||||||
|
}
|
||||||
|
importingPkg = old.Error.ImportStack[len(old.Error.ImportStack)-2]
|
||||||
}
|
}
|
||||||
importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-2]
|
|
||||||
additionalErrors[importingPkg] = append(additionalErrors[importingPkg], Error{
|
additionalErrors[importingPkg] = append(additionalErrors[importingPkg], Error{
|
||||||
Pos: old.Error.Pos,
|
Pos: old.Error.Pos,
|
||||||
Msg: old.Error.Err,
|
Msg: old.Error.Err,
|
||||||
|
@ -531,7 +544,26 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
||||||
CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
|
CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
|
||||||
OtherFiles: absJoin(p.Dir, otherFiles(p)...),
|
OtherFiles: absJoin(p.Dir, otherFiles(p)...),
|
||||||
forTest: p.ForTest,
|
forTest: p.ForTest,
|
||||||
module: p.Module,
|
Module: p.Module,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.cfg.Mode&typecheckCgo) != 0 && len(p.CgoFiles) != 0 {
|
||||||
|
if len(p.CompiledGoFiles) > len(p.GoFiles) {
|
||||||
|
// We need the cgo definitions, which are in the first
|
||||||
|
// CompiledGoFile after the non-cgo ones. This is a hack but there
|
||||||
|
// isn't currently a better way to find it. We also need the pure
|
||||||
|
// Go files and unprocessed cgo files, all of which are already
|
||||||
|
// in pkg.GoFiles.
|
||||||
|
cgoTypes := p.CompiledGoFiles[len(p.GoFiles)]
|
||||||
|
pkg.CompiledGoFiles = append([]string{cgoTypes}, pkg.GoFiles...)
|
||||||
|
} else {
|
||||||
|
// golang/go#38990: go list silently fails to do cgo processing
|
||||||
|
pkg.CompiledGoFiles = nil
|
||||||
|
pkg.Errors = append(pkg.Errors, Error{
|
||||||
|
Msg: "go list failed to return CompiledGoFiles; https://golang.org/issue/38990?",
|
||||||
|
Kind: ListError,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Work around https://golang.org/issue/28749:
|
// Work around https://golang.org/issue/28749:
|
||||||
|
@ -607,6 +639,39 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
||||||
pkg.CompiledGoFiles = pkg.GoFiles
|
pkg.CompiledGoFiles = pkg.GoFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporary work-around for golang/go#39986. Parse filenames out of
|
||||||
|
// error messages. This happens if there are unrecoverable syntax
|
||||||
|
// errors in the source, so we can't match on a specific error message.
|
||||||
|
if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) {
|
||||||
|
addFilenameFromPos := func(pos string) bool {
|
||||||
|
split := strings.Split(pos, ":")
|
||||||
|
if len(split) < 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
filename := strings.TrimSpace(split[0])
|
||||||
|
if filename == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(filename) {
|
||||||
|
filename = filepath.Join(state.cfg.Dir, filename)
|
||||||
|
}
|
||||||
|
info, _ := os.Stat(filename)
|
||||||
|
if info == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename)
|
||||||
|
pkg.GoFiles = append(pkg.GoFiles, filename)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
found := addFilenameFromPos(err.Pos)
|
||||||
|
// In some cases, go list only reports the error position in the
|
||||||
|
// error text, not the error position. One such case is when the
|
||||||
|
// file's package name is a keyword (see golang.org/issue/39763).
|
||||||
|
if !found {
|
||||||
|
addFilenameFromPos(err.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if p.Error != nil {
|
if p.Error != nil {
|
||||||
msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363.
|
msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363.
|
||||||
// Address golang.org/issue/35964 by appending import stack to error message.
|
// Address golang.org/issue/35964 by appending import stack to error message.
|
||||||
|
@ -636,6 +701,58 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
||||||
return &response, nil
|
return &response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool {
|
||||||
|
if len(p.GoFiles) > 0 || len(p.CompiledGoFiles) > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
goV, err := state.getGoVersion()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// On Go 1.14 and earlier, only add filenames from errors if the import stack is empty.
|
||||||
|
// The import stack behaves differently for these versions than newer Go versions.
|
||||||
|
if strings.HasPrefix(goV, "go1.13") || strings.HasPrefix(goV, "go1.14") {
|
||||||
|
return len(p.Error.ImportStack) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// On Go 1.15 and later, only parse filenames out of error if there's no import stack,
|
||||||
|
// or the current package is at the top of the import stack. This is not guaranteed
|
||||||
|
// to work perfectly, but should avoid some cases where files in errors don't belong to this
|
||||||
|
// package.
|
||||||
|
return len(p.Error.ImportStack) == 0 || p.Error.ImportStack[len(p.Error.ImportStack)-1] == p.ImportPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func (state *golistState) getGoVersion() (string, error) {
|
||||||
|
state.goVersionOnce.Do(func() {
|
||||||
|
var b *bytes.Buffer
|
||||||
|
// Invoke go version. Don't use invokeGo because it will supply build flags, and
|
||||||
|
// go version doesn't expect build flags.
|
||||||
|
inv := gocommand.Invocation{
|
||||||
|
Verb: "version",
|
||||||
|
Env: state.cfg.Env,
|
||||||
|
Logf: state.cfg.Logf,
|
||||||
|
}
|
||||||
|
gocmdRunner := state.cfg.gocmdRunner
|
||||||
|
if gocmdRunner == nil {
|
||||||
|
gocmdRunner = &gocommand.Runner{}
|
||||||
|
}
|
||||||
|
b, _, _, state.goVersionError = gocmdRunner.RunRaw(state.cfg.Context, inv)
|
||||||
|
if state.goVersionError != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sp := strings.Split(b.String(), " ")
|
||||||
|
if len(sp) < 3 {
|
||||||
|
state.goVersionError = fmt.Errorf("go version output: expected 'go version <version>', got '%s'", b.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state.goVersion = sp[2]
|
||||||
|
})
|
||||||
|
return state.goVersion, state.goVersionError
|
||||||
|
}
|
||||||
|
|
||||||
// getPkgPath finds the package path of a directory if it's relative to a root directory.
|
// getPkgPath finds the package path of a directory if it's relative to a root directory.
|
||||||
func (state *golistState) getPkgPath(dir string) (string, bool, error) {
|
func (state *golistState) getPkgPath(dir string) (string, bool, error) {
|
||||||
absDir, err := filepath.Abs(dir)
|
absDir, err := filepath.Abs(dir)
|
||||||
|
@ -707,7 +824,7 @@ func golistargs(cfg *Config, words []string) []string {
|
||||||
func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) {
|
func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) {
|
||||||
cfg := state.cfg
|
cfg := state.cfg
|
||||||
|
|
||||||
inv := &gocommand.Invocation{
|
inv := gocommand.Invocation{
|
||||||
Verb: verb,
|
Verb: verb,
|
||||||
Args: args,
|
Args: args,
|
||||||
BuildFlags: cfg.BuildFlags,
|
BuildFlags: cfg.BuildFlags,
|
||||||
|
@ -715,8 +832,11 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
|
||||||
Logf: cfg.Logf,
|
Logf: cfg.Logf,
|
||||||
WorkingDir: cfg.Dir,
|
WorkingDir: cfg.Dir,
|
||||||
}
|
}
|
||||||
|
gocmdRunner := cfg.gocmdRunner
|
||||||
stdout, stderr, _, err := inv.RunRaw(cfg.Context)
|
if gocmdRunner == nil {
|
||||||
|
gocmdRunner = &gocommand.Runner{}
|
||||||
|
}
|
||||||
|
stdout, stderr, _, err := gocmdRunner.RunRaw(cfg.Context, inv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Check for 'go' executable not being found.
|
// Check for 'go' executable not being found.
|
||||||
if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
|
if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
|
||||||
|
@ -727,7 +847,7 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
|
||||||
if !ok {
|
if !ok {
|
||||||
// Catastrophic error:
|
// Catastrophic error:
|
||||||
// - context cancellation
|
// - context cancellation
|
||||||
return nil, fmt.Errorf("couldn't run 'go': %v", err)
|
return nil, xerrors.Errorf("couldn't run 'go': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Old go version?
|
// Old go version?
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -22,10 +23,15 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
|
||||||
needPkgsSet := make(map[string]bool)
|
needPkgsSet := make(map[string]bool)
|
||||||
modifiedPkgsSet := make(map[string]bool)
|
modifiedPkgsSet := make(map[string]bool)
|
||||||
|
|
||||||
|
pkgOfDir := make(map[string][]*Package)
|
||||||
for _, pkg := range response.dr.Packages {
|
for _, pkg := range response.dr.Packages {
|
||||||
// This is an approximation of import path to id. This can be
|
// This is an approximation of import path to id. This can be
|
||||||
// wrong for tests, vendored packages, and a number of other cases.
|
// wrong for tests, vendored packages, and a number of other cases.
|
||||||
havePkgs[pkg.PkgPath] = pkg.ID
|
havePkgs[pkg.PkgPath] = pkg.ID
|
||||||
|
x := commonDir(pkg.GoFiles)
|
||||||
|
if x != "" {
|
||||||
|
pkgOfDir[x] = append(pkgOfDir[x], pkg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no new imports are added, it is safe to avoid loading any needPkgs.
|
// If no new imports are added, it is safe to avoid loading any needPkgs.
|
||||||
|
@ -64,6 +70,9 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
|
||||||
// to the overlay.
|
// to the overlay.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// If all the overlay files belong to a different package, change the
|
||||||
|
// package name to that package.
|
||||||
|
maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir])
|
||||||
nextPackage:
|
nextPackage:
|
||||||
for _, p := range response.dr.Packages {
|
for _, p := range response.dr.Packages {
|
||||||
if pkgName != p.Name && p.ID != "command-line-arguments" {
|
if pkgName != p.Name && p.ID != "command-line-arguments" {
|
||||||
|
@ -93,8 +102,11 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The overlay could have included an entirely new package.
|
// The overlay could have included an entirely new package or an
|
||||||
if pkg == nil {
|
// 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.
|
// Try to find the module or gopath dir the file is contained in.
|
||||||
// Then for modules, add the module opath to the beginning.
|
// Then for modules, add the module opath to the beginning.
|
||||||
pkgPath, ok, err := state.getPkgPath(dir)
|
pkgPath, ok, err := state.getPkgPath(dir)
|
||||||
|
@ -104,24 +116,39 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
|
||||||
if !ok {
|
if !ok {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
var forTest string // only set for x tests
|
||||||
isXTest := strings.HasSuffix(pkgName, "_test")
|
isXTest := strings.HasSuffix(pkgName, "_test")
|
||||||
if isXTest {
|
if isXTest {
|
||||||
|
forTest = pkgPath
|
||||||
pkgPath += "_test"
|
pkgPath += "_test"
|
||||||
}
|
}
|
||||||
id := pkgPath
|
id := pkgPath
|
||||||
if isTestFile && !isXTest {
|
if isTestFile {
|
||||||
|
if isXTest {
|
||||||
|
id = fmt.Sprintf("%s [%s.test]", pkgPath, forTest)
|
||||||
|
} else {
|
||||||
id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath)
|
id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath)
|
||||||
}
|
}
|
||||||
// Try to reclaim a package with the same id if it exists in the response.
|
}
|
||||||
|
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 {
|
for _, p := range response.dr.Packages {
|
||||||
if reclaimPackage(p, id, opath, contents) {
|
if reclaimPackage(p, id, opath, contents) {
|
||||||
pkg = p
|
pkg = p
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise, create a new package
|
// Otherwise, create a new package.
|
||||||
if pkg == nil {
|
if pkg == nil {
|
||||||
pkg = &Package{PkgPath: pkgPath, ID: id, Name: pkgName, Imports: make(map[string]*Package)}
|
pkg = &Package{
|
||||||
|
PkgPath: pkgPath,
|
||||||
|
ID: id,
|
||||||
|
Name: pkgName,
|
||||||
|
Imports: make(map[string]*Package),
|
||||||
|
}
|
||||||
response.addPackage(pkg)
|
response.addPackage(pkg)
|
||||||
havePkgs[pkg.PkgPath] = id
|
havePkgs[pkg.PkgPath] = id
|
||||||
// Add the production package's sources for a test variant.
|
// Add the production package's sources for a test variant.
|
||||||
|
@ -134,6 +161,10 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
|
||||||
pkg.Imports[k] = &Package{ID: v.ID}
|
pkg.Imports[k] = &Package{ID: v.ID}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if isXTest {
|
||||||
|
pkg.forTest = forTest
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !fileExists {
|
if !fileExists {
|
||||||
|
@ -149,6 +180,8 @@ func (state *golistState) processGolistOverlay(response *responseDeduper) (modif
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, imp := range imports {
|
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 {
|
if _, found := pkg.Imports[imp]; found {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -282,7 +315,17 @@ func (state *golistState) determineRootDirs() (map[string]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (state *golistState) determineRootDirsModules() (map[string]string, error) {
|
func (state *golistState) determineRootDirsModules() (map[string]string, error) {
|
||||||
out, err := state.invokeGo("list", "-m", "-json", "all")
|
// This will only return the root directory for the main module.
|
||||||
|
// For now we only support overlays in main modules.
|
||||||
|
// Editing files in the module cache isn't a great idea, so we don't
|
||||||
|
// plan to ever support that, but editing files in replaced modules
|
||||||
|
// is something we may want to support. To do that, we'll want to
|
||||||
|
// do a go list -m to determine the replaced module's module path and
|
||||||
|
// directory, and then a go list -m {{with .Replace}}{{.Dir}}{{end}} <replaced module's path>
|
||||||
|
// from the main module to determine if that module is actually a replacement.
|
||||||
|
// See bcmills's comment here: https://github.com/golang/go/issues/37629#issuecomment-594179751
|
||||||
|
// for more information.
|
||||||
|
out, err := state.invokeGo("list", "-m", "-json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -374,3 +417,57 @@ func extractPackageName(filename string, contents []byte) (string, bool) {
|
||||||
}
|
}
|
||||||
return f.Name.Name, true
|
return f.Name.Name, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func commonDir(a []string) string {
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
x := append([]string{}, a...)
|
||||||
|
for _, f := range x {
|
||||||
|
seen[filepath.Dir(f)] = true
|
||||||
|
}
|
||||||
|
if len(seen) > 1 {
|
||||||
|
log.Fatalf("commonDir saw %v for %v", seen, x)
|
||||||
|
}
|
||||||
|
for k := range seen {
|
||||||
|
// len(seen) == 1
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
return "" // 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ var modeStrings = []string{
|
||||||
func (mod LoadMode) String() string {
|
func (mod LoadMode) String() string {
|
||||||
m := mod
|
m := mod
|
||||||
if m == 0 {
|
if m == 0 {
|
||||||
return fmt.Sprintf("LoadMode(0)")
|
return "LoadMode(0)"
|
||||||
}
|
}
|
||||||
var out []string
|
var out []string
|
||||||
for i, x := range allModes {
|
for i, x := range allModes {
|
||||||
|
|
|
@ -21,9 +21,12 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/tools/go/gcexportdata"
|
"golang.org/x/tools/go/gcexportdata"
|
||||||
|
"golang.org/x/tools/internal/gocommand"
|
||||||
"golang.org/x/tools/internal/packagesinternal"
|
"golang.org/x/tools/internal/packagesinternal"
|
||||||
|
"golang.org/x/tools/internal/typesinternal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A LoadMode controls the amount of detail to return when loading.
|
// A LoadMode controls the amount of detail to return when loading.
|
||||||
|
@ -69,6 +72,13 @@ const (
|
||||||
|
|
||||||
// NeedTypesSizes adds TypesSizes.
|
// NeedTypesSizes adds TypesSizes.
|
||||||
NeedTypesSizes
|
NeedTypesSizes
|
||||||
|
|
||||||
|
// typecheckCgo enables full support for type checking cgo. Requires Go 1.15+.
|
||||||
|
// Modifies CompiledGoFiles and Types, and has no effect on its own.
|
||||||
|
typecheckCgo
|
||||||
|
|
||||||
|
// NeedModule adds Module.
|
||||||
|
NeedModule
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -127,6 +137,9 @@ type Config struct {
|
||||||
//
|
//
|
||||||
Env []string
|
Env []string
|
||||||
|
|
||||||
|
// gocmdRunner guards go command calls from concurrency errors.
|
||||||
|
gocmdRunner *gocommand.Runner
|
||||||
|
|
||||||
// BuildFlags is a list of command-line flags to be passed through to
|
// BuildFlags is a list of command-line flags to be passed through to
|
||||||
// the build system's query tool.
|
// the build system's query tool.
|
||||||
BuildFlags []string
|
BuildFlags []string
|
||||||
|
@ -178,6 +191,13 @@ type driver func(cfg *Config, patterns ...string) (*driverResponse, error)
|
||||||
|
|
||||||
// driverResponse contains the results for a driver query.
|
// driverResponse contains the results for a driver query.
|
||||||
type driverResponse struct {
|
type driverResponse struct {
|
||||||
|
// NotHandled is returned if the request can't be handled by the current
|
||||||
|
// driver. If an external driver returns a response with NotHandled, the
|
||||||
|
// rest of the driverResponse is ignored, and go/packages will fallback
|
||||||
|
// to the next driver. If go/packages is extended in the future to support
|
||||||
|
// lists of multiple drivers, go/packages will fall back to the next driver.
|
||||||
|
NotHandled bool
|
||||||
|
|
||||||
// Sizes, if not nil, is the types.Sizes to use when type checking.
|
// Sizes, if not nil, is the types.Sizes to use when type checking.
|
||||||
Sizes *types.StdSizes
|
Sizes *types.StdSizes
|
||||||
|
|
||||||
|
@ -219,14 +239,22 @@ func Load(cfg *Config, patterns ...string) ([]*Package, error) {
|
||||||
return l.refine(response.Roots, response.Packages...)
|
return l.refine(response.Roots, response.Packages...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultDriver is a driver that looks for an external driver binary, and if
|
// defaultDriver is a driver that implements go/packages' fallback behavior.
|
||||||
// it does not find it falls back to the built in go list driver.
|
// 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,
|
||||||
|
// defaultDriver will fall back to the go list driver.
|
||||||
func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
|
func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
|
||||||
driver := findExternalDriver(cfg)
|
driver := findExternalDriver(cfg)
|
||||||
if driver == nil {
|
if driver == nil {
|
||||||
driver = goListDriver
|
driver = goListDriver
|
||||||
}
|
}
|
||||||
return driver(cfg, patterns...)
|
response, err := driver(cfg, patterns...)
|
||||||
|
if err != nil {
|
||||||
|
return response, err
|
||||||
|
} else if response.NotHandled {
|
||||||
|
return goListDriver(cfg, patterns...)
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Package describes a loaded Go package.
|
// A Package describes a loaded Go package.
|
||||||
|
@ -253,7 +281,7 @@ type Package struct {
|
||||||
GoFiles []string
|
GoFiles []string
|
||||||
|
|
||||||
// CompiledGoFiles lists the absolute file paths of the package's source
|
// CompiledGoFiles lists the absolute file paths of the package's source
|
||||||
// files that were presented to the compiler.
|
// files that are suitable for type checking.
|
||||||
// This may differ from GoFiles if files are processed before compilation.
|
// This may differ from GoFiles if files are processed before compilation.
|
||||||
CompiledGoFiles []string
|
CompiledGoFiles []string
|
||||||
|
|
||||||
|
@ -301,16 +329,39 @@ type Package struct {
|
||||||
forTest string
|
forTest string
|
||||||
|
|
||||||
// module is the module information for the package if it exists.
|
// module is the module information for the package if it exists.
|
||||||
module *packagesinternal.Module
|
Module *Module
|
||||||
|
}
|
||||||
|
|
||||||
|
// Module provides module information for a package.
|
||||||
|
type Module struct {
|
||||||
|
Path string // module path
|
||||||
|
Version string // module version
|
||||||
|
Replace *Module // replaced by this module
|
||||||
|
Time *time.Time // time version was created
|
||||||
|
Main bool // is this the main module?
|
||||||
|
Indirect bool // is this module only an indirect dependency of main module?
|
||||||
|
Dir string // directory holding files for this module, if any
|
||||||
|
GoMod string // path to go.mod file used when loading this module, if any
|
||||||
|
GoVersion string // go version used in module
|
||||||
|
Error *ModuleError // error loading module
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModuleError holds errors loading a module.
|
||||||
|
type ModuleError struct {
|
||||||
|
Err string // the error itself
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
packagesinternal.GetForTest = func(p interface{}) string {
|
packagesinternal.GetForTest = func(p interface{}) string {
|
||||||
return p.(*Package).forTest
|
return p.(*Package).forTest
|
||||||
}
|
}
|
||||||
packagesinternal.GetModule = func(p interface{}) *packagesinternal.Module {
|
packagesinternal.GetGoCmdRunner = func(config interface{}) *gocommand.Runner {
|
||||||
return p.(*Package).module
|
return config.(*Config).gocmdRunner
|
||||||
}
|
}
|
||||||
|
packagesinternal.SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {
|
||||||
|
config.(*Config).gocmdRunner = runner
|
||||||
|
}
|
||||||
|
packagesinternal.TypecheckCgo = int(typecheckCgo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An Error describes a problem with a package's metadata, syntax, or types.
|
// An Error describes a problem with a package's metadata, syntax, or types.
|
||||||
|
@ -473,6 +524,9 @@ func newLoader(cfg *Config) *loader {
|
||||||
if ld.Config.Env == nil {
|
if ld.Config.Env == nil {
|
||||||
ld.Config.Env = os.Environ()
|
ld.Config.Env = os.Environ()
|
||||||
}
|
}
|
||||||
|
if ld.Config.gocmdRunner == nil {
|
||||||
|
ld.Config.gocmdRunner = &gocommand.Runner{}
|
||||||
|
}
|
||||||
if ld.Context == nil {
|
if ld.Context == nil {
|
||||||
ld.Context = context.Background()
|
ld.Context = context.Background()
|
||||||
}
|
}
|
||||||
|
@ -690,6 +744,9 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
|
||||||
if ld.requestedMode&NeedTypesSizes == 0 {
|
if ld.requestedMode&NeedTypesSizes == 0 {
|
||||||
ld.pkgs[i].TypesSizes = nil
|
ld.pkgs[i].TypesSizes = nil
|
||||||
}
|
}
|
||||||
|
if ld.requestedMode&NeedModule == 0 {
|
||||||
|
ld.pkgs[i].Module = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
@ -865,6 +922,15 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
||||||
Error: appendError,
|
Error: appendError,
|
||||||
Sizes: ld.sizes,
|
Sizes: ld.sizes,
|
||||||
}
|
}
|
||||||
|
if (ld.Mode & typecheckCgo) != 0 {
|
||||||
|
if !typesinternal.SetUsesCgo(tc) {
|
||||||
|
appendError(Error{
|
||||||
|
Msg: "typecheckCgo requires Go 1.15+",
|
||||||
|
Kind: ListError,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
|
types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
|
||||||
|
|
||||||
lpkg.importErrors = nil // no longer needed
|
lpkg.importErrors = nil // no longer needed
|
||||||
|
|
|
@ -226,7 +226,8 @@ func For(obj types.Object) (Path, error) {
|
||||||
// the best paths because non-types may
|
// the best paths because non-types may
|
||||||
// refer to types, but not the reverse.
|
// refer to types, but not the reverse.
|
||||||
empty := make([]byte, 0, 48) // initial space
|
empty := make([]byte, 0, 48) // initial space
|
||||||
for _, name := range scope.Names() {
|
names := scope.Names()
|
||||||
|
for _, name := range names {
|
||||||
o := scope.Lookup(name)
|
o := scope.Lookup(name)
|
||||||
tname, ok := o.(*types.TypeName)
|
tname, ok := o.(*types.TypeName)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -253,7 +254,7 @@ func For(obj types.Object) (Path, error) {
|
||||||
|
|
||||||
// Then inspect everything else:
|
// Then inspect everything else:
|
||||||
// non-types, and declared methods of defined types.
|
// non-types, and declared methods of defined types.
|
||||||
for _, name := range scope.Names() {
|
for _, name := range names {
|
||||||
o := scope.Lookup(name)
|
o := scope.Lookup(name)
|
||||||
path := append(empty, name...)
|
path := append(empty, name...)
|
||||||
if _, ok := o.(*types.TypeName); !ok {
|
if _, ok := o.(*types.TypeName); !ok {
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
package imports // import "golang.org/x/tools/imports"
|
package imports // import "golang.org/x/tools/imports"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/build"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
|
|
||||||
|
"golang.org/x/tools/internal/gocommand"
|
||||||
intimp "golang.org/x/tools/internal/imports"
|
intimp "golang.org/x/tools/internal/imports"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,25 +31,28 @@ var Debug = false
|
||||||
var LocalPrefix string
|
var LocalPrefix string
|
||||||
|
|
||||||
// Process formats and adjusts imports for the provided file.
|
// Process formats and adjusts imports for the provided file.
|
||||||
// If opt is nil the defaults are used.
|
// If opt is nil the defaults are used, and if src is nil the source
|
||||||
|
// is read from the filesystem.
|
||||||
//
|
//
|
||||||
// Note that filename's directory influences which imports can be chosen,
|
// Note that filename's directory influences which imports can be chosen,
|
||||||
// so it is important that filename be accurate.
|
// so it is important that filename be accurate.
|
||||||
// To process data ``as if'' it were in filename, pass the data as a non-nil src.
|
// To process data ``as if'' it were in filename, pass the data as a non-nil src.
|
||||||
func Process(filename string, src []byte, opt *Options) ([]byte, error) {
|
func Process(filename string, src []byte, opt *Options) ([]byte, error) {
|
||||||
|
var err error
|
||||||
|
if src == nil {
|
||||||
|
src, err = ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
if opt == nil {
|
if opt == nil {
|
||||||
opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
|
opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
|
||||||
}
|
}
|
||||||
intopt := &intimp.Options{
|
intopt := &intimp.Options{
|
||||||
Env: &intimp.ProcessEnv{
|
Env: &intimp.ProcessEnv{
|
||||||
GOPATH: build.Default.GOPATH,
|
GocmdRunner: &gocommand.Runner{},
|
||||||
GOROOT: build.Default.GOROOT,
|
|
||||||
GOFLAGS: os.Getenv("GOFLAGS"),
|
|
||||||
GO111MODULE: os.Getenv("GO111MODULE"),
|
|
||||||
GOPROXY: os.Getenv("GOPROXY"),
|
|
||||||
GOSUMDB: os.Getenv("GOSUMDB"),
|
|
||||||
LocalPrefix: LocalPrefix,
|
|
||||||
},
|
},
|
||||||
|
LocalPrefix: LocalPrefix,
|
||||||
AllErrors: opt.AllErrors,
|
AllErrors: opt.AllErrors,
|
||||||
Comments: opt.Comments,
|
Comments: opt.Comments,
|
||||||
FormatOnly: opt.FormatOnly,
|
FormatOnly: opt.FormatOnly,
|
||||||
|
|
|
@ -0,0 +1,421 @@
|
||||||
|
// 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 analysisinternal exposes internal-only fields from go/analysis.
|
||||||
|
package analysisinternal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"go/types"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
|
"golang.org/x/tools/internal/lsp/fuzzy"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
GetTypeErrors func(p interface{}) []types.Error
|
||||||
|
SetTypeErrors func(p interface{}, errors []types.Error)
|
||||||
|
)
|
||||||
|
|
||||||
|
func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos {
|
||||||
|
// Get the end position for the type error.
|
||||||
|
offset, end := fset.PositionFor(start, false).Offset, start
|
||||||
|
if offset >= len(src) {
|
||||||
|
return end
|
||||||
|
}
|
||||||
|
if width := bytes.IndexAny(src[offset:], " \n,():;[]+-*"); width > 0 {
|
||||||
|
end = start + token.Pos(width)
|
||||||
|
}
|
||||||
|
return end
|
||||||
|
}
|
||||||
|
|
||||||
|
func ZeroValue(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
|
||||||
|
under := typ
|
||||||
|
if n, ok := typ.(*types.Named); ok {
|
||||||
|
under = n.Underlying()
|
||||||
|
}
|
||||||
|
switch u := under.(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
switch {
|
||||||
|
case u.Info()&types.IsNumeric != 0:
|
||||||
|
return &ast.BasicLit{Kind: token.INT, Value: "0"}
|
||||||
|
case u.Info()&types.IsBoolean != 0:
|
||||||
|
return &ast.Ident{Name: "false"}
|
||||||
|
case u.Info()&types.IsString != 0:
|
||||||
|
return &ast.BasicLit{Kind: token.STRING, Value: `""`}
|
||||||
|
default:
|
||||||
|
panic("unknown basic type")
|
||||||
|
}
|
||||||
|
case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice, *types.Array:
|
||||||
|
return ast.NewIdent("nil")
|
||||||
|
case *types.Struct:
|
||||||
|
texpr := TypeExpr(fset, f, pkg, typ) // typ because we want the name here.
|
||||||
|
if texpr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &ast.CompositeLit{
|
||||||
|
Type: texpr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsZeroValue checks whether the given expression is a 'zero value' (as determined by output of
|
||||||
|
// analysisinternal.ZeroValue)
|
||||||
|
func IsZeroValue(expr ast.Expr) bool {
|
||||||
|
switch e := expr.(type) {
|
||||||
|
case *ast.BasicLit:
|
||||||
|
return e.Value == "0" || e.Value == `""`
|
||||||
|
case *ast.Ident:
|
||||||
|
return e.Name == "nil" || e.Name == "false"
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
|
||||||
|
switch t := typ.(type) {
|
||||||
|
case *types.Basic:
|
||||||
|
switch t.Kind() {
|
||||||
|
case types.UnsafePointer:
|
||||||
|
return &ast.SelectorExpr{X: ast.NewIdent("unsafe"), Sel: ast.NewIdent("Pointer")}
|
||||||
|
default:
|
||||||
|
return ast.NewIdent(t.Name())
|
||||||
|
}
|
||||||
|
case *types.Pointer:
|
||||||
|
x := TypeExpr(fset, f, pkg, t.Elem())
|
||||||
|
if x == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &ast.UnaryExpr{
|
||||||
|
Op: token.MUL,
|
||||||
|
X: x,
|
||||||
|
}
|
||||||
|
case *types.Array:
|
||||||
|
elt := TypeExpr(fset, f, pkg, t.Elem())
|
||||||
|
if elt == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &ast.ArrayType{
|
||||||
|
Len: &ast.BasicLit{
|
||||||
|
Kind: token.INT,
|
||||||
|
Value: fmt.Sprintf("%d", t.Len()),
|
||||||
|
},
|
||||||
|
Elt: elt,
|
||||||
|
}
|
||||||
|
case *types.Slice:
|
||||||
|
elt := TypeExpr(fset, f, pkg, t.Elem())
|
||||||
|
if elt == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &ast.ArrayType{
|
||||||
|
Elt: elt,
|
||||||
|
}
|
||||||
|
case *types.Map:
|
||||||
|
key := TypeExpr(fset, f, pkg, t.Key())
|
||||||
|
value := TypeExpr(fset, f, pkg, t.Elem())
|
||||||
|
if key == nil || value == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &ast.MapType{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
case *types.Chan:
|
||||||
|
dir := ast.ChanDir(t.Dir())
|
||||||
|
if t.Dir() == types.SendRecv {
|
||||||
|
dir = ast.SEND | ast.RECV
|
||||||
|
}
|
||||||
|
value := TypeExpr(fset, f, pkg, t.Elem())
|
||||||
|
if value == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &ast.ChanType{
|
||||||
|
Dir: dir,
|
||||||
|
Value: value,
|
||||||
|
}
|
||||||
|
case *types.Signature:
|
||||||
|
var params []*ast.Field
|
||||||
|
for i := 0; i < t.Params().Len(); i++ {
|
||||||
|
p := TypeExpr(fset, f, pkg, t.Params().At(i).Type())
|
||||||
|
if p == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
params = append(params, &ast.Field{
|
||||||
|
Type: p,
|
||||||
|
Names: []*ast.Ident{
|
||||||
|
{
|
||||||
|
Name: t.Params().At(i).Name(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
var returns []*ast.Field
|
||||||
|
for i := 0; i < t.Results().Len(); i++ {
|
||||||
|
r := TypeExpr(fset, f, pkg, t.Results().At(i).Type())
|
||||||
|
if r == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
returns = append(returns, &ast.Field{
|
||||||
|
Type: r,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return &ast.FuncType{
|
||||||
|
Params: &ast.FieldList{
|
||||||
|
List: params,
|
||||||
|
},
|
||||||
|
Results: &ast.FieldList{
|
||||||
|
List: returns,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case *types.Named:
|
||||||
|
if t.Obj().Pkg() == nil {
|
||||||
|
return ast.NewIdent(t.Obj().Name())
|
||||||
|
}
|
||||||
|
if t.Obj().Pkg() == pkg {
|
||||||
|
return ast.NewIdent(t.Obj().Name())
|
||||||
|
}
|
||||||
|
pkgName := t.Obj().Pkg().Name()
|
||||||
|
// If the file already imports the package under another name, use that.
|
||||||
|
for _, group := range astutil.Imports(fset, f) {
|
||||||
|
for _, cand := range group {
|
||||||
|
if strings.Trim(cand.Path.Value, `"`) == t.Obj().Pkg().Path() {
|
||||||
|
if cand.Name != nil && cand.Name.Name != "" {
|
||||||
|
pkgName = cand.Name.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pkgName == "." {
|
||||||
|
return ast.NewIdent(t.Obj().Name())
|
||||||
|
}
|
||||||
|
return &ast.SelectorExpr{
|
||||||
|
X: ast.NewIdent(pkgName),
|
||||||
|
Sel: ast.NewIdent(t.Obj().Name()),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil // TODO: anonymous structs, but who does that
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypeErrorPass string
|
||||||
|
|
||||||
|
const (
|
||||||
|
NoNewVars TypeErrorPass = "nonewvars"
|
||||||
|
NoResultValues TypeErrorPass = "noresultvalues"
|
||||||
|
UndeclaredName TypeErrorPass = "undeclaredname"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StmtToInsertVarBefore returns the ast.Stmt before which we can safely insert a new variable.
|
||||||
|
// Some examples:
|
||||||
|
//
|
||||||
|
// Basic Example:
|
||||||
|
// z := 1
|
||||||
|
// y := z + x
|
||||||
|
// If x is undeclared, then this function would return `y := z + x`, so that we
|
||||||
|
// can insert `x := ` on the line before `y := z + x`.
|
||||||
|
//
|
||||||
|
// If stmt example:
|
||||||
|
// if z == 1 {
|
||||||
|
// } else if z == y {}
|
||||||
|
// If y is undeclared, then this function would return `if z == 1 {`, because we cannot
|
||||||
|
// insert a statement between an if and an else if statement. As a result, we need to find
|
||||||
|
// the top of the if chain to insert `y := ` before.
|
||||||
|
func StmtToInsertVarBefore(path []ast.Node) ast.Stmt {
|
||||||
|
enclosingIndex := -1
|
||||||
|
for i, p := range path {
|
||||||
|
if _, ok := p.(ast.Stmt); ok {
|
||||||
|
enclosingIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if enclosingIndex == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
enclosingStmt := path[enclosingIndex]
|
||||||
|
switch enclosingStmt.(type) {
|
||||||
|
case *ast.IfStmt:
|
||||||
|
// The enclosingStmt is inside of the if declaration,
|
||||||
|
// We need to check if we are in an else-if stmt and
|
||||||
|
// get the base if statement.
|
||||||
|
return baseIfStmt(path, enclosingIndex)
|
||||||
|
case *ast.CaseClause:
|
||||||
|
// Get the enclosing switch stmt if the enclosingStmt is
|
||||||
|
// inside of the case statement.
|
||||||
|
for i := enclosingIndex + 1; i < len(path); i++ {
|
||||||
|
if node, ok := path[i].(*ast.SwitchStmt); ok {
|
||||||
|
return node
|
||||||
|
} else if node, ok := path[i].(*ast.TypeSwitchStmt); ok {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(path) <= enclosingIndex+1 {
|
||||||
|
return enclosingStmt.(ast.Stmt)
|
||||||
|
}
|
||||||
|
// Check if the enclosing statement is inside another node.
|
||||||
|
switch expr := path[enclosingIndex+1].(type) {
|
||||||
|
case *ast.IfStmt:
|
||||||
|
// Get the base if statement.
|
||||||
|
return baseIfStmt(path, enclosingIndex+1)
|
||||||
|
case *ast.ForStmt:
|
||||||
|
if expr.Init == enclosingStmt || expr.Post == enclosingStmt {
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return enclosingStmt.(ast.Stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// baseIfStmt walks up the if/else-if chain until we get to
|
||||||
|
// the top of the current if chain.
|
||||||
|
func baseIfStmt(path []ast.Node, index int) ast.Stmt {
|
||||||
|
stmt := path[index]
|
||||||
|
for i := index + 1; i < len(path); i++ {
|
||||||
|
if node, ok := path[i].(*ast.IfStmt); ok && node.Else == stmt {
|
||||||
|
stmt = node
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return stmt.(ast.Stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WalkASTWithParent walks the AST rooted at n. The semantics are
|
||||||
|
// similar to ast.Inspect except it does not call f(nil).
|
||||||
|
func WalkASTWithParent(n ast.Node, f func(n ast.Node, parent ast.Node) bool) {
|
||||||
|
var ancestors []ast.Node
|
||||||
|
ast.Inspect(n, func(n ast.Node) (recurse bool) {
|
||||||
|
if n == nil {
|
||||||
|
ancestors = ancestors[:len(ancestors)-1]
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var parent ast.Node
|
||||||
|
if len(ancestors) > 0 {
|
||||||
|
parent = ancestors[len(ancestors)-1]
|
||||||
|
}
|
||||||
|
ancestors = append(ancestors, n)
|
||||||
|
return f(n, parent)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMatchingIdents finds all identifiers in 'node' that match any of the given types.
|
||||||
|
// 'pos' represents the position at which the identifiers may be inserted. 'pos' must be within
|
||||||
|
// the scope of each of identifier we select. Otherwise, we will insert a variable at 'pos' that
|
||||||
|
// is unrecognized.
|
||||||
|
func FindMatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *types.Info, pkg *types.Package) map[types.Type][]*ast.Ident {
|
||||||
|
matches := map[types.Type][]*ast.Ident{}
|
||||||
|
// Initialize matches to contain the variable types we are searching for.
|
||||||
|
for _, typ := range typs {
|
||||||
|
if typ == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matches[typ] = []*ast.Ident{}
|
||||||
|
}
|
||||||
|
seen := map[types.Object]struct{}{}
|
||||||
|
ast.Inspect(node, func(n ast.Node) bool {
|
||||||
|
if n == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Prevent circular definitions. If 'pos' is within an assignment statement, do not
|
||||||
|
// allow any identifiers in that assignment statement to be selected. Otherwise,
|
||||||
|
// we could do the following, where 'x' satisfies the type of 'f0':
|
||||||
|
//
|
||||||
|
// x := fakeStruct{f0: x}
|
||||||
|
//
|
||||||
|
assignment, ok := n.(*ast.AssignStmt)
|
||||||
|
if ok && pos > assignment.Pos() && pos <= assignment.End() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n.End() > pos {
|
||||||
|
return n.Pos() <= pos
|
||||||
|
}
|
||||||
|
ident, ok := n.(*ast.Ident)
|
||||||
|
if !ok || ident.Name == "_" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
obj := info.Defs[ident]
|
||||||
|
if obj == nil || obj.Type() == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := obj.(*types.TypeName); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Prevent duplicates in matches' values.
|
||||||
|
if _, ok = seen[obj]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
seen[obj] = struct{}{}
|
||||||
|
// Find the scope for the given position. Then, check whether the object
|
||||||
|
// exists within the scope.
|
||||||
|
innerScope := pkg.Scope().Innermost(pos)
|
||||||
|
if innerScope == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_, foundObj := innerScope.LookupParent(ident.Name, pos)
|
||||||
|
if foundObj != obj {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// The object must match one of the types that we are searching for.
|
||||||
|
if idents, ok := matches[obj.Type()]; ok {
|
||||||
|
matches[obj.Type()] = append(idents, ast.NewIdent(ident.Name))
|
||||||
|
}
|
||||||
|
// If the object type does not exactly match any of the target types, greedily
|
||||||
|
// find the first target type that the object type can satisfy.
|
||||||
|
for typ := range matches {
|
||||||
|
if obj.Type() == typ {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if equivalentTypes(obj.Type(), typ) {
|
||||||
|
matches[typ] = append(matches[typ], ast.NewIdent(ident.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return matches
|
||||||
|
}
|
||||||
|
|
||||||
|
func equivalentTypes(want, got types.Type) bool {
|
||||||
|
if want == got || types.Identical(want, got) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Code segment to help check for untyped equality from (golang/go#32146).
|
||||||
|
if rhs, ok := want.(*types.Basic); ok && rhs.Info()&types.IsUntyped > 0 {
|
||||||
|
if lhs, ok := got.Underlying().(*types.Basic); ok {
|
||||||
|
return rhs.Info()&types.IsConstType == lhs.Info()&types.IsConstType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.AssignableTo(want, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindBestMatch employs fuzzy matching to evaluate the similarity of each given identifier to the
|
||||||
|
// given pattern. We return the identifier whose name is most similar to the pattern.
|
||||||
|
func FindBestMatch(pattern string, idents []*ast.Ident) ast.Expr {
|
||||||
|
fuzz := fuzzy.NewMatcher(pattern)
|
||||||
|
var bestFuzz ast.Expr
|
||||||
|
highScore := float32(-1) // minimum score is -1 (no match)
|
||||||
|
for _, ident := range idents {
|
||||||
|
// TODO: Improve scoring algorithm.
|
||||||
|
score := fuzz.Score(ident.Name)
|
||||||
|
if score > highScore {
|
||||||
|
highScore = score
|
||||||
|
bestFuzz = ident
|
||||||
|
} else if score == -1 {
|
||||||
|
// Order matters in the fuzzy matching algorithm. If we find no match
|
||||||
|
// when matching the target to the identifier, try matching the identifier
|
||||||
|
// to the target.
|
||||||
|
revFuzz := fuzzy.NewMatcher(ident.Name)
|
||||||
|
revScore := revFuzz.Score(pattern)
|
||||||
|
if revScore > highScore {
|
||||||
|
highScore = revScore
|
||||||
|
bestFuzz = ident
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestFuzz
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package core provides support for event based telemetry.
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/tools/internal/event/label"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Event holds the information about an event of note that ocurred.
|
||||||
|
type Event struct {
|
||||||
|
at time.Time
|
||||||
|
|
||||||
|
// As events are often on the stack, storing the first few labels directly
|
||||||
|
// in the event can avoid an allocation at all for the very common cases of
|
||||||
|
// simple events.
|
||||||
|
// The length needs to be large enough to cope with the majority of events
|
||||||
|
// but no so large as to cause undue stack pressure.
|
||||||
|
// A log message with two values will use 3 labels (one for each value and
|
||||||
|
// one for the message itself).
|
||||||
|
|
||||||
|
static [3]label.Label // inline storage for the first few labels
|
||||||
|
dynamic []label.Label // dynamically sized storage for remaining labels
|
||||||
|
}
|
||||||
|
|
||||||
|
// eventLabelMap implements label.Map for a the labels of an Event.
|
||||||
|
type eventLabelMap struct {
|
||||||
|
event Event
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ev Event) At() time.Time { return ev.at }
|
||||||
|
|
||||||
|
func (ev Event) Format(f fmt.State, r rune) {
|
||||||
|
if !ev.at.IsZero() {
|
||||||
|
fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 "))
|
||||||
|
}
|
||||||
|
for index := 0; ev.Valid(index); index++ {
|
||||||
|
if l := ev.Label(index); l.Valid() {
|
||||||
|
fmt.Fprintf(f, "\n\t%v", l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ev Event) Valid(index int) bool {
|
||||||
|
return index >= 0 && index < len(ev.static)+len(ev.dynamic)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ev Event) Label(index int) label.Label {
|
||||||
|
if index < len(ev.static) {
|
||||||
|
return ev.static[index]
|
||||||
|
}
|
||||||
|
return ev.dynamic[index-len(ev.static)]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ev Event) Find(key label.Key) label.Label {
|
||||||
|
for _, l := range ev.static {
|
||||||
|
if l.Key() == key {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, l := range ev.dynamic {
|
||||||
|
if l.Key() == key {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return label.Label{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeEvent(static [3]label.Label, labels []label.Label) Event {
|
||||||
|
return Event{
|
||||||
|
static: static,
|
||||||
|
dynamic: labels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneEvent event returns a copy of the event with the time adjusted to at.
|
||||||
|
func CloneEvent(ev Event, at time.Time) Event {
|
||||||
|
ev.at = at
|
||||||
|
return ev
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/tools/internal/event/label"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exporter is a function that handles events.
|
||||||
|
// It may return a modified context and event.
|
||||||
|
type Exporter func(context.Context, Event, label.Map) context.Context
|
||||||
|
|
||||||
|
var (
|
||||||
|
exporter unsafe.Pointer
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetExporter sets the global exporter function that handles all events.
|
||||||
|
// The exporter is called synchronously from the event call site, so it should
|
||||||
|
// return quickly so as not to hold up user code.
|
||||||
|
func SetExporter(e Exporter) {
|
||||||
|
p := unsafe.Pointer(&e)
|
||||||
|
if e == nil {
|
||||||
|
// &e is always valid, and so p is always valid, but for the early abort
|
||||||
|
// of ProcessEvent to be efficient it needs to make the nil check on the
|
||||||
|
// pointer without having to dereference it, so we make the nil function
|
||||||
|
// also a nil pointer
|
||||||
|
p = nil
|
||||||
|
}
|
||||||
|
atomic.StorePointer(&exporter, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deliver is called to deliver an event to the supplied exporter.
|
||||||
|
// it will fill in the time.
|
||||||
|
func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context {
|
||||||
|
// add the current time to the event
|
||||||
|
ev.at = time.Now()
|
||||||
|
// hand the event off to the current exporter
|
||||||
|
return exporter(ctx, ev, ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export is called to deliver an event to the global exporter if set.
|
||||||
|
func Export(ctx context.Context, ev Event) context.Context {
|
||||||
|
// get the global exporter and abort early if there is not one
|
||||||
|
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
|
||||||
|
if exporterPtr == nil {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
return deliver(ctx, *exporterPtr, ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportPair is called to deliver a start event to the supplied exporter.
|
||||||
|
// It also returns a function that will deliver the end event to the same
|
||||||
|
// exporter.
|
||||||
|
// It will fill in the time.
|
||||||
|
func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) {
|
||||||
|
// get the global exporter and abort early if there is not one
|
||||||
|
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
|
||||||
|
if exporterPtr == nil {
|
||||||
|
return ctx, func() {}
|
||||||
|
}
|
||||||
|
ctx = deliver(ctx, *exporterPtr, begin)
|
||||||
|
return ctx, func() { deliver(ctx, *exporterPtr, end) }
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"golang.org/x/tools/internal/event/keys"
|
||||||
|
"golang.org/x/tools/internal/event/label"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Log1 takes a message and one label delivers a log event to the exporter.
|
||||||
|
// It is a customized version of Print that is faster and does no allocation.
|
||||||
|
func Log1(ctx context.Context, message string, t1 label.Label) {
|
||||||
|
Export(ctx, MakeEvent([3]label.Label{
|
||||||
|
keys.Msg.Of(message),
|
||||||
|
t1,
|
||||||
|
}, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log2 takes a message and two labels and delivers a log event to the exporter.
|
||||||
|
// It is a customized version of Print that is faster and does no allocation.
|
||||||
|
func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) {
|
||||||
|
Export(ctx, MakeEvent([3]label.Label{
|
||||||
|
keys.Msg.Of(message),
|
||||||
|
t1,
|
||||||
|
t2,
|
||||||
|
}, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metric1 sends a label event to the exporter with the supplied labels.
|
||||||
|
func Metric1(ctx context.Context, t1 label.Label) context.Context {
|
||||||
|
return Export(ctx, MakeEvent([3]label.Label{
|
||||||
|
keys.Metric.New(),
|
||||||
|
t1,
|
||||||
|
}, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metric2 sends a label event to the exporter with the supplied labels.
|
||||||
|
func Metric2(ctx context.Context, t1, t2 label.Label) context.Context {
|
||||||
|
return Export(ctx, MakeEvent([3]label.Label{
|
||||||
|
keys.Metric.New(),
|
||||||
|
t1,
|
||||||
|
t2,
|
||||||
|
}, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start1 sends a span start event with the supplied label list to the exporter.
|
||||||
|
// It also returns a function that will end the span, which should normally be
|
||||||
|
// deferred.
|
||||||
|
func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) {
|
||||||
|
return ExportPair(ctx,
|
||||||
|
MakeEvent([3]label.Label{
|
||||||
|
keys.Start.Of(name),
|
||||||
|
t1,
|
||||||
|
}, nil),
|
||||||
|
MakeEvent([3]label.Label{
|
||||||
|
keys.End.New(),
|
||||||
|
}, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start2 sends a span start event with the supplied label list to the exporter.
|
||||||
|
// It also returns a function that will end the span, which should normally be
|
||||||
|
// deferred.
|
||||||
|
func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) {
|
||||||
|
return ExportPair(ctx,
|
||||||
|
MakeEvent([3]label.Label{
|
||||||
|
keys.Start.Of(name),
|
||||||
|
t1,
|
||||||
|
t2,
|
||||||
|
}, nil),
|
||||||
|
MakeEvent([3]label.Label{
|
||||||
|
keys.End.New(),
|
||||||
|
}, nil))
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package event provides a set of packages that cover the main
|
||||||
|
// concepts of telemetry in an implementation agnostic way.
|
||||||
|
package event
|
|
@ -0,0 +1,127 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package event
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"golang.org/x/tools/internal/event/core"
|
||||||
|
"golang.org/x/tools/internal/event/keys"
|
||||||
|
"golang.org/x/tools/internal/event/label"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Exporter is a function that handles events.
|
||||||
|
// It may return a modified context and event.
|
||||||
|
type Exporter func(context.Context, core.Event, label.Map) context.Context
|
||||||
|
|
||||||
|
// SetExporter sets the global exporter function that handles all events.
|
||||||
|
// The exporter is called synchronously from the event call site, so it should
|
||||||
|
// return quickly so as not to hold up user code.
|
||||||
|
func SetExporter(e Exporter) {
|
||||||
|
core.SetExporter(core.Exporter(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log takes a message and a label list and combines them into a single event
|
||||||
|
// before delivering them to the exporter.
|
||||||
|
func Log(ctx context.Context, message string, labels ...label.Label) {
|
||||||
|
core.Export(ctx, core.MakeEvent([3]label.Label{
|
||||||
|
keys.Msg.Of(message),
|
||||||
|
}, labels))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLog returns true if the event was built by the Log function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsLog(ev core.Event) bool {
|
||||||
|
return ev.Label(0).Key() == keys.Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error takes a message and a label list and combines them into a single event
|
||||||
|
// before delivering them to the exporter. It captures the error in the
|
||||||
|
// delivered event.
|
||||||
|
func Error(ctx context.Context, message string, err error, labels ...label.Label) {
|
||||||
|
core.Export(ctx, core.MakeEvent([3]label.Label{
|
||||||
|
keys.Msg.Of(message),
|
||||||
|
keys.Err.Of(err),
|
||||||
|
}, labels))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsError returns true if the event was built by the Error function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsError(ev core.Event) bool {
|
||||||
|
return ev.Label(0).Key() == keys.Msg &&
|
||||||
|
ev.Label(1).Key() == keys.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metric sends a label event to the exporter with the supplied labels.
|
||||||
|
func Metric(ctx context.Context, labels ...label.Label) {
|
||||||
|
core.Export(ctx, core.MakeEvent([3]label.Label{
|
||||||
|
keys.Metric.New(),
|
||||||
|
}, labels))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMetric returns true if the event was built by the Metric function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsMetric(ev core.Event) bool {
|
||||||
|
return ev.Label(0).Key() == keys.Metric
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label sends a label event to the exporter with the supplied labels.
|
||||||
|
func Label(ctx context.Context, labels ...label.Label) context.Context {
|
||||||
|
return core.Export(ctx, core.MakeEvent([3]label.Label{
|
||||||
|
keys.Label.New(),
|
||||||
|
}, labels))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLabel returns true if the event was built by the Label function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsLabel(ev core.Event) bool {
|
||||||
|
return ev.Label(0).Key() == keys.Label
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start sends a span start event with the supplied label list to the exporter.
|
||||||
|
// It also returns a function that will end the span, which should normally be
|
||||||
|
// deferred.
|
||||||
|
func Start(ctx context.Context, name string, labels ...label.Label) (context.Context, func()) {
|
||||||
|
return core.ExportPair(ctx,
|
||||||
|
core.MakeEvent([3]label.Label{
|
||||||
|
keys.Start.Of(name),
|
||||||
|
}, labels),
|
||||||
|
core.MakeEvent([3]label.Label{
|
||||||
|
keys.End.New(),
|
||||||
|
}, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsStart returns true if the event was built by the Start function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsStart(ev core.Event) bool {
|
||||||
|
return ev.Label(0).Key() == keys.Start
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnd returns true if the event was built by the End function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsEnd(ev core.Event) bool {
|
||||||
|
return ev.Label(0).Key() == keys.End
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach returns a context without an associated span.
|
||||||
|
// This allows the creation of spans that are not children of the current span.
|
||||||
|
func Detach(ctx context.Context) context.Context {
|
||||||
|
return core.Export(ctx, core.MakeEvent([3]label.Label{
|
||||||
|
keys.Detach.New(),
|
||||||
|
}, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDetach returns true if the event was built by the Detach function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsDetach(ev core.Event) bool {
|
||||||
|
return ev.Label(0).Key() == keys.Detach
|
||||||
|
}
|
|
@ -0,0 +1,564 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"golang.org/x/tools/internal/event/label"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Value represents a key for untyped values.
|
||||||
|
type Value struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Key for untyped values.
|
||||||
|
func New(name, description string) *Value {
|
||||||
|
return &Value{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Value) Name() string { return k.name }
|
||||||
|
func (k *Value) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *Value) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
fmt.Fprint(w, k.From(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Value) Get(lm label.Map) interface{} {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Value) From(t label.Label) interface{} { return t.UnpackValue() }
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Value) Of(value interface{}) label.Label { return label.OfValue(k, value) }
|
||||||
|
|
||||||
|
// Tag represents a key for tagging labels that have no value.
|
||||||
|
// These are used when the existence of the label is the entire information it
|
||||||
|
// carries, such as marking events to be of a specific kind, or from a specific
|
||||||
|
// package.
|
||||||
|
type Tag struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTag creates a new Key for tagging labels.
|
||||||
|
func NewTag(name, description string) *Tag {
|
||||||
|
return &Tag{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Tag) Name() string { return k.name }
|
||||||
|
func (k *Tag) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *Tag) Format(w io.Writer, buf []byte, l label.Label) {}
|
||||||
|
|
||||||
|
// New creates a new Label with this key.
|
||||||
|
func (k *Tag) New() label.Label { return label.OfValue(k, nil) }
|
||||||
|
|
||||||
|
// Int represents a key
|
||||||
|
type Int struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt creates a new Key for int values.
|
||||||
|
func NewInt(name, description string) *Int {
|
||||||
|
return &Int{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Int) Name() string { return k.name }
|
||||||
|
func (k *Int) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *Int) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Int) Of(v int) label.Label { return label.Of64(k, uint64(v)) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Int) Get(lm label.Map) int {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Int) From(t label.Label) int { return int(t.Unpack64()) }
|
||||||
|
|
||||||
|
// Int8 represents a key
|
||||||
|
type Int8 struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt8 creates a new Key for int8 values.
|
||||||
|
func NewInt8(name, description string) *Int8 {
|
||||||
|
return &Int8{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Int8) Name() string { return k.name }
|
||||||
|
func (k *Int8) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *Int8) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Int8) Of(v int8) label.Label { return label.Of64(k, uint64(v)) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Int8) Get(lm label.Map) int8 {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Int8) From(t label.Label) int8 { return int8(t.Unpack64()) }
|
||||||
|
|
||||||
|
// Int16 represents a key
|
||||||
|
type Int16 struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt16 creates a new Key for int16 values.
|
||||||
|
func NewInt16(name, description string) *Int16 {
|
||||||
|
return &Int16{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Int16) Name() string { return k.name }
|
||||||
|
func (k *Int16) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *Int16) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Int16) Of(v int16) label.Label { return label.Of64(k, uint64(v)) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Int16) Get(lm label.Map) int16 {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Int16) From(t label.Label) int16 { return int16(t.Unpack64()) }
|
||||||
|
|
||||||
|
// Int32 represents a key
|
||||||
|
type Int32 struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt32 creates a new Key for int32 values.
|
||||||
|
func NewInt32(name, description string) *Int32 {
|
||||||
|
return &Int32{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Int32) Name() string { return k.name }
|
||||||
|
func (k *Int32) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *Int32) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Int32) Of(v int32) label.Label { return label.Of64(k, uint64(v)) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Int32) Get(lm label.Map) int32 {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Int32) From(t label.Label) int32 { return int32(t.Unpack64()) }
|
||||||
|
|
||||||
|
// Int64 represents a key
|
||||||
|
type Int64 struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInt64 creates a new Key for int64 values.
|
||||||
|
func NewInt64(name, description string) *Int64 {
|
||||||
|
return &Int64{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Int64) Name() string { return k.name }
|
||||||
|
func (k *Int64) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *Int64) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendInt(buf, k.From(l), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Int64) Of(v int64) label.Label { return label.Of64(k, uint64(v)) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Int64) Get(lm label.Map) int64 {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Int64) From(t label.Label) int64 { return int64(t.Unpack64()) }
|
||||||
|
|
||||||
|
// UInt represents a key
|
||||||
|
type UInt struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUInt creates a new Key for uint values.
|
||||||
|
func NewUInt(name, description string) *UInt {
|
||||||
|
return &UInt{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *UInt) Name() string { return k.name }
|
||||||
|
func (k *UInt) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *UInt) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *UInt) Of(v uint) label.Label { return label.Of64(k, uint64(v)) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *UInt) Get(lm label.Map) uint {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *UInt) From(t label.Label) uint { return uint(t.Unpack64()) }
|
||||||
|
|
||||||
|
// UInt8 represents a key
|
||||||
|
type UInt8 struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUInt8 creates a new Key for uint8 values.
|
||||||
|
func NewUInt8(name, description string) *UInt8 {
|
||||||
|
return &UInt8{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *UInt8) Name() string { return k.name }
|
||||||
|
func (k *UInt8) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *UInt8) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *UInt8) Of(v uint8) label.Label { return label.Of64(k, uint64(v)) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *UInt8) Get(lm label.Map) uint8 {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *UInt8) From(t label.Label) uint8 { return uint8(t.Unpack64()) }
|
||||||
|
|
||||||
|
// UInt16 represents a key
|
||||||
|
type UInt16 struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUInt16 creates a new Key for uint16 values.
|
||||||
|
func NewUInt16(name, description string) *UInt16 {
|
||||||
|
return &UInt16{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *UInt16) Name() string { return k.name }
|
||||||
|
func (k *UInt16) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *UInt16) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *UInt16) Of(v uint16) label.Label { return label.Of64(k, uint64(v)) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *UInt16) Get(lm label.Map) uint16 {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *UInt16) From(t label.Label) uint16 { return uint16(t.Unpack64()) }
|
||||||
|
|
||||||
|
// UInt32 represents a key
|
||||||
|
type UInt32 struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUInt32 creates a new Key for uint32 values.
|
||||||
|
func NewUInt32(name, description string) *UInt32 {
|
||||||
|
return &UInt32{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *UInt32) Name() string { return k.name }
|
||||||
|
func (k *UInt32) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *UInt32) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *UInt32) Of(v uint32) label.Label { return label.Of64(k, uint64(v)) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *UInt32) Get(lm label.Map) uint32 {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *UInt32) From(t label.Label) uint32 { return uint32(t.Unpack64()) }
|
||||||
|
|
||||||
|
// UInt64 represents a key
|
||||||
|
type UInt64 struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUInt64 creates a new Key for uint64 values.
|
||||||
|
func NewUInt64(name, description string) *UInt64 {
|
||||||
|
return &UInt64{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *UInt64) Name() string { return k.name }
|
||||||
|
func (k *UInt64) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *UInt64) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendUint(buf, k.From(l), 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *UInt64) Of(v uint64) label.Label { return label.Of64(k, v) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *UInt64) Get(lm label.Map) uint64 {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *UInt64) From(t label.Label) uint64 { return t.Unpack64() }
|
||||||
|
|
||||||
|
// Float32 represents a key
|
||||||
|
type Float32 struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat32 creates a new Key for float32 values.
|
||||||
|
func NewFloat32(name, description string) *Float32 {
|
||||||
|
return &Float32{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Float32) Name() string { return k.name }
|
||||||
|
func (k *Float32) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *Float32) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendFloat(buf, float64(k.From(l)), 'E', -1, 32))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Float32) Of(v float32) label.Label {
|
||||||
|
return label.Of64(k, uint64(math.Float32bits(v)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Float32) Get(lm label.Map) float32 {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Float32) From(t label.Label) float32 {
|
||||||
|
return math.Float32frombits(uint32(t.Unpack64()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 represents a key
|
||||||
|
type Float64 struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFloat64 creates a new Key for int64 values.
|
||||||
|
func NewFloat64(name, description string) *Float64 {
|
||||||
|
return &Float64{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Float64) Name() string { return k.name }
|
||||||
|
func (k *Float64) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *Float64) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendFloat(buf, k.From(l), 'E', -1, 64))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Float64) Of(v float64) label.Label {
|
||||||
|
return label.Of64(k, math.Float64bits(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Float64) Get(lm label.Map) float64 {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Float64) From(t label.Label) float64 {
|
||||||
|
return math.Float64frombits(t.Unpack64())
|
||||||
|
}
|
||||||
|
|
||||||
|
// String represents a key
|
||||||
|
type String struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewString creates a new Key for int64 values.
|
||||||
|
func NewString(name, description string) *String {
|
||||||
|
return &String{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *String) Name() string { return k.name }
|
||||||
|
func (k *String) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *String) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendQuote(buf, k.From(l)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *String) Of(v string) label.Label { return label.OfString(k, v) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *String) Get(lm label.Map) string {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *String) From(t label.Label) string { return t.UnpackString() }
|
||||||
|
|
||||||
|
// Boolean represents a key
|
||||||
|
type Boolean struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBoolean creates a new Key for bool values.
|
||||||
|
func NewBoolean(name, description string) *Boolean {
|
||||||
|
return &Boolean{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Boolean) Name() string { return k.name }
|
||||||
|
func (k *Boolean) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *Boolean) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
w.Write(strconv.AppendBool(buf, k.From(l)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Boolean) Of(v bool) label.Label {
|
||||||
|
if v {
|
||||||
|
return label.Of64(k, 1)
|
||||||
|
}
|
||||||
|
return label.Of64(k, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Boolean) Get(lm label.Map) bool {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Boolean) From(t label.Label) bool { return t.Unpack64() > 0 }
|
||||||
|
|
||||||
|
// Error represents a key
|
||||||
|
type Error struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewError creates a new Key for int64 values.
|
||||||
|
func NewError(name, description string) *Error {
|
||||||
|
return &Error{name: name, description: description}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Error) Name() string { return k.name }
|
||||||
|
func (k *Error) Description() string { return k.description }
|
||||||
|
|
||||||
|
func (k *Error) Format(w io.Writer, buf []byte, l label.Label) {
|
||||||
|
io.WriteString(w, k.From(l).Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Error) Of(v error) label.Label { return label.OfValue(k, v) }
|
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Error) Get(lm label.Map) error {
|
||||||
|
if t := lm.Find(k); t.Valid() {
|
||||||
|
return k.From(t)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Error) From(t label.Label) error {
|
||||||
|
err, _ := t.UnpackValue().(error)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package keys
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Msg is a key used to add message strings to label lists.
|
||||||
|
Msg = NewString("message", "a readable message")
|
||||||
|
// Label is a key used to indicate an event adds labels to the context.
|
||||||
|
Label = NewTag("label", "a label context marker")
|
||||||
|
// Start is used for things like traces that have a name.
|
||||||
|
Start = NewString("start", "span start")
|
||||||
|
// Metric is a key used to indicate an event records metrics.
|
||||||
|
End = NewTag("end", "a span end marker")
|
||||||
|
// Metric is a key used to indicate an event records metrics.
|
||||||
|
Detach = NewTag("detach", "a span detach marker")
|
||||||
|
// Err is a key used to add error values to label lists.
|
||||||
|
Err = NewError("error", "an error that occurred")
|
||||||
|
// Metric is a key used to indicate an event records metrics.
|
||||||
|
Metric = NewTag("metric", "a metric event marker")
|
||||||
|
)
|
|
@ -0,0 +1,213 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package label
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Key is used as the identity of a Label.
|
||||||
|
// Keys are intended to be compared by pointer only, the name should be unique
|
||||||
|
// for communicating with external systems, but it is not required or enforced.
|
||||||
|
type Key interface {
|
||||||
|
// Name returns the key name.
|
||||||
|
Name() string
|
||||||
|
// Description returns a string that can be used to describe the value.
|
||||||
|
Description() string
|
||||||
|
|
||||||
|
// Format is used in formatting to append the value of the label to the
|
||||||
|
// supplied buffer.
|
||||||
|
// The formatter may use the supplied buf as a scratch area to avoid
|
||||||
|
// allocations.
|
||||||
|
Format(w io.Writer, buf []byte, l Label)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label holds a key and value pair.
|
||||||
|
// It is normally used when passing around lists of labels.
|
||||||
|
type Label struct {
|
||||||
|
key Key
|
||||||
|
packed uint64
|
||||||
|
untyped interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map is the interface to a collection of Labels indexed by key.
|
||||||
|
type Map interface {
|
||||||
|
// Find returns the label that matches the supplied key.
|
||||||
|
Find(key Key) Label
|
||||||
|
}
|
||||||
|
|
||||||
|
// List is the interface to something that provides an iterable
|
||||||
|
// list of labels.
|
||||||
|
// Iteration should start from 0 and continue until Valid returns false.
|
||||||
|
type List interface {
|
||||||
|
// Valid returns true if the index is within range for the list.
|
||||||
|
// It does not imply the label at that index will itself be valid.
|
||||||
|
Valid(index int) bool
|
||||||
|
// Label returns the label at the given index.
|
||||||
|
Label(index int) Label
|
||||||
|
}
|
||||||
|
|
||||||
|
// list implements LabelList for a list of Labels.
|
||||||
|
type list struct {
|
||||||
|
labels []Label
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter wraps a LabelList filtering out specific labels.
|
||||||
|
type filter struct {
|
||||||
|
keys []Key
|
||||||
|
underlying List
|
||||||
|
}
|
||||||
|
|
||||||
|
// listMap implements LabelMap for a simple list of labels.
|
||||||
|
type listMap struct {
|
||||||
|
labels []Label
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapChain implements LabelMap for a list of underlying LabelMap.
|
||||||
|
type mapChain struct {
|
||||||
|
maps []Map
|
||||||
|
}
|
||||||
|
|
||||||
|
// OfValue creates a new label from the key and value.
|
||||||
|
// This method is for implementing new key types, label creation should
|
||||||
|
// normally be done with the Of method of the key.
|
||||||
|
func OfValue(k Key, value interface{}) Label { return Label{key: k, untyped: value} }
|
||||||
|
|
||||||
|
// UnpackValue assumes the label was built using LabelOfValue and returns the value
|
||||||
|
// that was passed to that constructor.
|
||||||
|
// This method is for implementing new key types, for type safety normal
|
||||||
|
// access should be done with the From method of the key.
|
||||||
|
func (t Label) UnpackValue() interface{} { return t.untyped }
|
||||||
|
|
||||||
|
// Of64 creates a new label from a key and a uint64. This is often
|
||||||
|
// used for non uint64 values that can be packed into a uint64.
|
||||||
|
// This method is for implementing new key types, label creation should
|
||||||
|
// normally be done with the Of method of the key.
|
||||||
|
func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} }
|
||||||
|
|
||||||
|
// Unpack64 assumes the label was built using LabelOf64 and returns the value that
|
||||||
|
// was passed to that constructor.
|
||||||
|
// This method is for implementing new key types, for type safety normal
|
||||||
|
// access should be done with the From method of the key.
|
||||||
|
func (t Label) Unpack64() uint64 { return t.packed }
|
||||||
|
|
||||||
|
// OfString creates a new label from a key and a string.
|
||||||
|
// This method is for implementing new key types, label creation should
|
||||||
|
// normally be done with the Of method of the key.
|
||||||
|
func OfString(k Key, v string) Label {
|
||||||
|
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
|
||||||
|
return Label{
|
||||||
|
key: k,
|
||||||
|
packed: uint64(hdr.Len),
|
||||||
|
untyped: unsafe.Pointer(hdr.Data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnpackString assumes the label was built using LabelOfString and returns the
|
||||||
|
// value that was passed to that constructor.
|
||||||
|
// This method is for implementing new key types, for type safety normal
|
||||||
|
// access should be done with the From method of the key.
|
||||||
|
func (t Label) UnpackString() string {
|
||||||
|
var v string
|
||||||
|
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
|
||||||
|
hdr.Data = uintptr(t.untyped.(unsafe.Pointer))
|
||||||
|
hdr.Len = int(t.packed)
|
||||||
|
return *(*string)(unsafe.Pointer(hdr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid returns true if the Label is a valid one (it has a key).
|
||||||
|
func (t Label) Valid() bool { return t.key != nil }
|
||||||
|
|
||||||
|
// Key returns the key of this Label.
|
||||||
|
func (t Label) Key() Key { return t.key }
|
||||||
|
|
||||||
|
// Format is used for debug printing of labels.
|
||||||
|
func (t Label) Format(f fmt.State, r rune) {
|
||||||
|
if !t.Valid() {
|
||||||
|
io.WriteString(f, `nil`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
io.WriteString(f, t.Key().Name())
|
||||||
|
io.WriteString(f, "=")
|
||||||
|
var buf [128]byte
|
||||||
|
t.Key().Format(f, buf[:0], t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *list) Valid(index int) bool {
|
||||||
|
return index >= 0 && index < len(l.labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *list) Label(index int) Label {
|
||||||
|
return l.labels[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *filter) Valid(index int) bool {
|
||||||
|
return f.underlying.Valid(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *filter) Label(index int) Label {
|
||||||
|
l := f.underlying.Label(index)
|
||||||
|
for _, f := range f.keys {
|
||||||
|
if l.Key() == f {
|
||||||
|
return Label{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lm listMap) Find(key Key) Label {
|
||||||
|
for _, l := range lm.labels {
|
||||||
|
if l.Key() == key {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Label{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c mapChain) Find(key Key) Label {
|
||||||
|
for _, src := range c.maps {
|
||||||
|
l := src.Find(key)
|
||||||
|
if l.Valid() {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Label{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptyList = &list{}
|
||||||
|
|
||||||
|
func NewList(labels ...Label) List {
|
||||||
|
if len(labels) == 0 {
|
||||||
|
return emptyList
|
||||||
|
}
|
||||||
|
return &list{labels: labels}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Filter(l List, keys ...Key) List {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
return &filter{keys: keys, underlying: l}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMap(labels ...Label) Map {
|
||||||
|
return listMap{labels: labels}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MergeMaps(srcs ...Map) Map {
|
||||||
|
var nonNil []Map
|
||||||
|
for _, src := range srcs {
|
||||||
|
if src != nil {
|
||||||
|
nonNil = append(nonNil, src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(nonNil) == 1 {
|
||||||
|
return nonNil[0]
|
||||||
|
}
|
||||||
|
return mapChain{maps: nonNil}
|
||||||
|
}
|
|
@ -1,3 +1,7 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package gocommand is a helper for calling the go command.
|
// Package gocommand is a helper for calling the go command.
|
||||||
package gocommand
|
package gocommand
|
||||||
|
|
||||||
|
@ -8,10 +12,119 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/tools/internal/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// An Runner will run go command invocations and serialize
|
||||||
|
// them if it sees a concurrency error.
|
||||||
|
type Runner struct {
|
||||||
|
// once guards the runner initialization.
|
||||||
|
once sync.Once
|
||||||
|
|
||||||
|
// inFlight tracks available workers.
|
||||||
|
inFlight chan struct{}
|
||||||
|
|
||||||
|
// serialized guards the ability to run a go command serially,
|
||||||
|
// to avoid deadlocks when claiming workers.
|
||||||
|
serialized chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxInFlight = 10
|
||||||
|
|
||||||
|
func (runner *Runner) initialize() {
|
||||||
|
runner.once.Do(func() {
|
||||||
|
runner.inFlight = make(chan struct{}, maxInFlight)
|
||||||
|
runner.serialized = make(chan struct{}, 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.13: go: updates to go.mod needed, but contents have changed
|
||||||
|
// 1.14: go: updating go.mod: existing contents have changed since last read
|
||||||
|
var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`)
|
||||||
|
|
||||||
|
// Run is a convenience wrapper around RunRaw.
|
||||||
|
// It returns only stdout and a "friendly" error.
|
||||||
|
func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) {
|
||||||
|
stdout, _, friendly, _ := runner.RunRaw(ctx, inv)
|
||||||
|
return stdout, friendly
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunPiped runs the invocation serially, always waiting for any concurrent
|
||||||
|
// invocations to complete first.
|
||||||
|
func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error {
|
||||||
|
_, err := runner.runPiped(ctx, inv, stdout, stderr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunRaw runs the invocation, serializing requests only if they fight over
|
||||||
|
// go.mod changes.
|
||||||
|
func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
|
||||||
|
// Make sure the runner is always initialized.
|
||||||
|
runner.initialize()
|
||||||
|
|
||||||
|
// First, try to run the go command concurrently.
|
||||||
|
stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv)
|
||||||
|
|
||||||
|
// If we encounter a load concurrency error, we need to retry serially.
|
||||||
|
if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) {
|
||||||
|
return stdout, stderr, friendlyErr, err
|
||||||
|
}
|
||||||
|
event.Error(ctx, "Load concurrency error, will retry serially", err)
|
||||||
|
|
||||||
|
// Run serially by calling runPiped.
|
||||||
|
stdout.Reset()
|
||||||
|
stderr.Reset()
|
||||||
|
friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr)
|
||||||
|
return stdout, stderr, friendlyErr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
|
||||||
|
// Wait for 1 worker to become available.
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, nil, nil, ctx.Err()
|
||||||
|
case runner.inFlight <- struct{}{}:
|
||||||
|
defer func() { <-runner.inFlight }()
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
|
||||||
|
friendlyErr, err := inv.runWithFriendlyError(ctx, stdout, stderr)
|
||||||
|
return stdout, stderr, friendlyErr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) {
|
||||||
|
// Make sure the runner is always initialized.
|
||||||
|
runner.initialize()
|
||||||
|
|
||||||
|
// Acquire the serialization lock. This avoids deadlocks between two
|
||||||
|
// runPiped commands.
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case runner.serialized <- struct{}{}:
|
||||||
|
defer func() { <-runner.serialized }()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all in-progress go commands to return before proceeding,
|
||||||
|
// to avoid load concurrency errors.
|
||||||
|
for i := 0; i < maxInFlight; i++ {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case runner.inFlight <- struct{}{}:
|
||||||
|
// Make sure we always "return" any workers we took.
|
||||||
|
defer func() { <-runner.inFlight }()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inv.runWithFriendlyError(ctx, stdout, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
// An Invocation represents a call to the go command.
|
// An Invocation represents a call to the go command.
|
||||||
type Invocation struct {
|
type Invocation struct {
|
||||||
Verb string
|
Verb string
|
||||||
|
@ -22,20 +135,10 @@ type Invocation struct {
|
||||||
Logf func(format string, args ...interface{})
|
Logf func(format string, args ...interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs the invocation, returning its stdout and an error suitable for
|
func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) {
|
||||||
// human consumption, including stderr.
|
rawError = i.run(ctx, stdout, stderr)
|
||||||
func (i *Invocation) Run(ctx context.Context) (*bytes.Buffer, error) {
|
|
||||||
stdout, _, friendly, _ := i.RunRaw(ctx)
|
|
||||||
return stdout, friendly
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunRaw is like RunPiped, but also returns the raw stderr and error for callers
|
|
||||||
// that want to do low-level error handling/recovery.
|
|
||||||
func (i *Invocation) RunRaw(ctx context.Context) (stdout *bytes.Buffer, stderr *bytes.Buffer, friendlyError error, rawError error) {
|
|
||||||
stdout = &bytes.Buffer{}
|
|
||||||
stderr = &bytes.Buffer{}
|
|
||||||
rawError = i.RunPiped(ctx, stdout, stderr)
|
|
||||||
if rawError != nil {
|
if rawError != nil {
|
||||||
|
friendlyError = rawError
|
||||||
// Check for 'go' executable not being found.
|
// Check for 'go' executable not being found.
|
||||||
if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
|
if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
|
||||||
friendlyError = fmt.Errorf("go command required, not found: %v", ee)
|
friendlyError = fmt.Errorf("go command required, not found: %v", ee)
|
||||||
|
@ -43,13 +146,12 @@ func (i *Invocation) RunRaw(ctx context.Context) (stdout *bytes.Buffer, stderr *
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
friendlyError = ctx.Err()
|
friendlyError = ctx.Err()
|
||||||
}
|
}
|
||||||
friendlyError = fmt.Errorf("err: %v: stderr: %s", rawError, stderr)
|
friendlyError = fmt.Errorf("err: %v: stderr: %s", friendlyError, stderr)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunPiped is like Run, but relies on the given stdout/stderr
|
func (i *Invocation) run(ctx context.Context, stdout, stderr io.Writer) error {
|
||||||
func (i *Invocation) RunPiped(ctx context.Context, stdout, stderr io.Writer) error {
|
|
||||||
log := i.Logf
|
log := i.Logf
|
||||||
if log == nil {
|
if log == nil {
|
||||||
log = func(string, ...interface{}) {}
|
log = func(string, ...interface{}) {}
|
||||||
|
@ -78,9 +180,11 @@ func (i *Invocation) RunPiped(ctx context.Context, stdout, stderr io.Writer) err
|
||||||
// The Go stdlib has a special feature where if the cwd and the PWD are the
|
// The Go stdlib has a special feature where if the cwd and the PWD are the
|
||||||
// same node then it trusts the PWD, so by setting it in the env for the child
|
// same node then it trusts the PWD, so by setting it in the env for the child
|
||||||
// process we fix up all the paths returned by the go command.
|
// process we fix up all the paths returned by the go command.
|
||||||
cmd.Env = append(append([]string{}, i.Env...), "PWD="+i.WorkingDir)
|
cmd.Env = append(os.Environ(), i.Env...)
|
||||||
|
if i.WorkingDir != "" {
|
||||||
|
cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir)
|
||||||
cmd.Dir = i.WorkingDir
|
cmd.Dir = i.WorkingDir
|
||||||
|
}
|
||||||
defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
|
defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
|
||||||
|
|
||||||
return runCmdContext(ctx, cmd)
|
return runCmdContext(ctx, cmd)
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gocommand
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/mod/semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ModuleJSON holds information about a module.
|
||||||
|
type ModuleJSON struct {
|
||||||
|
Path string // module path
|
||||||
|
Replace *ModuleJSON // replaced by this module
|
||||||
|
Main bool // is this the main module?
|
||||||
|
Indirect bool // is this module only an indirect dependency of main module?
|
||||||
|
Dir string // directory holding files for this module, if any
|
||||||
|
GoMod string // path to go.mod file for this module, if any
|
||||||
|
GoVersion string // go version used in module
|
||||||
|
}
|
||||||
|
|
||||||
|
var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
|
||||||
|
|
||||||
|
// VendorEnabled reports whether vendoring is enabled. It takes a *Runner to execute Go commands
|
||||||
|
// with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields,
|
||||||
|
// of which only Verb and Args are modified to run the appropriate Go command.
|
||||||
|
// Inspired by setDefaultBuildMod in modload/init.go
|
||||||
|
func VendorEnabled(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) {
|
||||||
|
mainMod, go114, err := getMainModuleAnd114(ctx, inv, r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We check the GOFLAGS to see if there is anything overridden or not.
|
||||||
|
inv.Verb = "env"
|
||||||
|
inv.Args = []string{"GOFLAGS"}
|
||||||
|
stdout, err := r.Run(ctx, inv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
goflags := string(bytes.TrimSpace(stdout.Bytes()))
|
||||||
|
matches := modFlagRegexp.FindStringSubmatch(goflags)
|
||||||
|
var modFlag string
|
||||||
|
if len(matches) != 0 {
|
||||||
|
modFlag = matches[1]
|
||||||
|
}
|
||||||
|
if modFlag != "" {
|
||||||
|
// Don't override an explicit '-mod=' argument.
|
||||||
|
return mainMod, modFlag == "vendor", nil
|
||||||
|
}
|
||||||
|
if mainMod == nil || !go114 {
|
||||||
|
return mainMod, false, nil
|
||||||
|
}
|
||||||
|
// Check 1.14's automatic vendor mode.
|
||||||
|
if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() {
|
||||||
|
if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 {
|
||||||
|
// The Go version is at least 1.14, and a vendor directory exists.
|
||||||
|
// Set -mod=vendor by default.
|
||||||
|
return mainMod, true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mainMod, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMainModuleAnd114 gets the main module's information and whether the
|
||||||
|
// go command in use is 1.14+. This is the information needed to figure out
|
||||||
|
// if vendoring should be enabled.
|
||||||
|
func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) {
|
||||||
|
const format = `{{.Path}}
|
||||||
|
{{.Dir}}
|
||||||
|
{{.GoMod}}
|
||||||
|
{{.GoVersion}}
|
||||||
|
{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}
|
||||||
|
`
|
||||||
|
inv.Verb = "list"
|
||||||
|
inv.Args = []string{"-m", "-f", format}
|
||||||
|
stdout, err := r.Run(ctx, inv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(stdout.String(), "\n")
|
||||||
|
if len(lines) < 5 {
|
||||||
|
return nil, false, fmt.Errorf("unexpected stdout: %q", stdout.String())
|
||||||
|
}
|
||||||
|
mod := &ModuleJSON{
|
||||||
|
Path: lines[0],
|
||||||
|
Dir: lines[1],
|
||||||
|
GoMod: lines[2],
|
||||||
|
GoVersion: lines[3],
|
||||||
|
Main: true,
|
||||||
|
}
|
||||||
|
return mod, lines[4] == "go1.14", nil
|
||||||
|
}
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -47,16 +46,6 @@ type Root struct {
|
||||||
Type RootType
|
Type RootType
|
||||||
}
|
}
|
||||||
|
|
||||||
// SrcDirsRoots returns the roots from build.Default.SrcDirs(). Not modules-compatible.
|
|
||||||
func SrcDirsRoots(ctx *build.Context) []Root {
|
|
||||||
var roots []Root
|
|
||||||
roots = append(roots, Root{filepath.Join(ctx.GOROOT, "src"), RootGOROOT})
|
|
||||||
for _, p := range filepath.SplitList(ctx.GOPATH) {
|
|
||||||
roots = append(roots, Root{filepath.Join(p, "src"), RootGOPATH})
|
|
||||||
}
|
|
||||||
return roots
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk walks Go source directories ($GOROOT, $GOPATH, etc) to find packages.
|
// Walk walks Go source directories ($GOROOT, $GOPATH, etc) to find packages.
|
||||||
// For each package found, add will be called (concurrently) with the absolute
|
// For each package found, add will be called (concurrently) with the absolute
|
||||||
// paths of the containing source directory and the package directory.
|
// paths of the containing source directory and the package directory.
|
||||||
|
|
|
@ -7,6 +7,7 @@ package imports
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
"go/build"
|
||||||
|
@ -31,35 +32,36 @@ import (
|
||||||
|
|
||||||
// importToGroup is a list of functions which map from an import path to
|
// importToGroup is a list of functions which map from an import path to
|
||||||
// a group number.
|
// a group number.
|
||||||
var importToGroup = []func(env *ProcessEnv, importPath string) (num int, ok bool){
|
var importToGroup = []func(localPrefix, importPath string) (num int, ok bool){
|
||||||
func(env *ProcessEnv, importPath string) (num int, ok bool) {
|
func(localPrefix, importPath string) (num int, ok bool) {
|
||||||
if env.LocalPrefix == "" {
|
if localPrefix == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, p := range strings.Split(env.LocalPrefix, ",") {
|
for _, p := range strings.Split(localPrefix, ",") {
|
||||||
if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath {
|
if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath {
|
||||||
return 3, true
|
return 3, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
func(_ *ProcessEnv, importPath string) (num int, ok bool) {
|
func(_, importPath string) (num int, ok bool) {
|
||||||
if strings.HasPrefix(importPath, "appengine") {
|
if strings.HasPrefix(importPath, "appengine") {
|
||||||
return 2, true
|
return 2, true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
func(_ *ProcessEnv, importPath string) (num int, ok bool) {
|
func(_, importPath string) (num int, ok bool) {
|
||||||
if strings.Contains(importPath, ".") {
|
firstComponent := strings.Split(importPath, "/")[0]
|
||||||
|
if strings.Contains(firstComponent, ".") {
|
||||||
return 1, true
|
return 1, true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func importGroup(env *ProcessEnv, importPath string) int {
|
func importGroup(localPrefix, importPath string) int {
|
||||||
for _, fn := range importToGroup {
|
for _, fn := range importToGroup {
|
||||||
if n, ok := fn(env, importPath); ok {
|
if n, ok := fn(localPrefix, importPath); ok {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,7 +278,12 @@ func (p *pass) loadPackageNames(imports []*ImportInfo) error {
|
||||||
unknown = append(unknown, imp.ImportPath)
|
unknown = append(unknown, imp.ImportPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
names, err := p.env.GetResolver().loadPackageNames(unknown, p.srcDir)
|
resolver, err := p.env.GetResolver()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
names, err := resolver.loadPackageNames(unknown, p.srcDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -566,7 +573,9 @@ func getFixes(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv
|
||||||
return fixes, nil
|
return fixes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
addStdlibCandidates(p, p.missingRefs)
|
if err := addStdlibCandidates(p, p.missingRefs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
p.assumeSiblingImportsValid()
|
p.assumeSiblingImportsValid()
|
||||||
if fixes, done := p.fix(); done {
|
if fixes, done := p.fix(); done {
|
||||||
return fixes, nil
|
return fixes, nil
|
||||||
|
@ -594,10 +603,14 @@ func getCandidatePkgs(ctx context.Context, wrappedCallback *scanCallback, filena
|
||||||
notSelf := func(p *pkg) bool {
|
notSelf := func(p *pkg) bool {
|
||||||
return p.packageName != filePkg || p.dir != filepath.Dir(filename)
|
return p.packageName != filePkg || p.dir != filepath.Dir(filename)
|
||||||
}
|
}
|
||||||
|
goenv, err := env.goEnv()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// Start off with the standard library.
|
// Start off with the standard library.
|
||||||
for importPath, exports := range stdlib {
|
for importPath, exports := range stdlib {
|
||||||
p := &pkg{
|
p := &pkg{
|
||||||
dir: filepath.Join(env.GOROOT, "src", importPath),
|
dir: filepath.Join(goenv["GOROOT"], "src", importPath),
|
||||||
importPathShort: importPath,
|
importPathShort: importPath,
|
||||||
packageName: path.Base(importPath),
|
packageName: path.Base(importPath),
|
||||||
relevance: MaxRelevance,
|
relevance: MaxRelevance,
|
||||||
|
@ -638,15 +651,23 @@ func getCandidatePkgs(ctx context.Context, wrappedCallback *scanCallback, filena
|
||||||
wrappedCallback.exportsLoaded(pkg, exports)
|
wrappedCallback.exportsLoaded(pkg, exports)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return env.GetResolver().scan(ctx, scanFilter)
|
resolver, err := env.GetResolver()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return resolver.scan(ctx, scanFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) map[string]int {
|
func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) (map[string]int, error) {
|
||||||
result := make(map[string]int)
|
result := make(map[string]int)
|
||||||
for _, path := range paths {
|
resolver, err := env.GetResolver()
|
||||||
result[path] = env.GetResolver().scoreImportPath(ctx, path)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return result
|
for _, path := range paths {
|
||||||
|
result[path] = resolver.scoreImportPath(ctx, path)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrimeCache(ctx context.Context, env *ProcessEnv) error {
|
func PrimeCache(ctx context.Context, env *ProcessEnv) error {
|
||||||
|
@ -672,8 +693,9 @@ func candidateImportName(pkg *pkg) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAllCandidates gets all of the candidates to be imported, regardless of if they are needed.
|
// GetAllCandidates gets all of the packages starting with prefix that can be
|
||||||
func getAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
|
// imported by filename, sorted by import path.
|
||||||
|
func GetAllCandidates(ctx context.Context, wrapped func(ImportFix), searchPrefix, filename, filePkg string, env *ProcessEnv) error {
|
||||||
callback := &scanCallback{
|
callback := &scanCallback{
|
||||||
rootFound: func(gopathwalk.Root) bool {
|
rootFound: func(gopathwalk.Root) bool {
|
||||||
return true
|
return true
|
||||||
|
@ -712,7 +734,8 @@ type PackageExport struct {
|
||||||
Exports []string
|
Exports []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPackageExports(ctx context.Context, wrapped func(PackageExport), searchPkg, filename, filePkg string, env *ProcessEnv) error {
|
// GetPackageExports returns all known packages with name pkg and their exports.
|
||||||
|
func GetPackageExports(ctx context.Context, wrapped func(PackageExport), searchPkg, filename, filePkg string, env *ProcessEnv) error {
|
||||||
callback := &scanCallback{
|
callback := &scanCallback{
|
||||||
rootFound: func(gopathwalk.Root) bool {
|
rootFound: func(gopathwalk.Root) bool {
|
||||||
return true
|
return true
|
||||||
|
@ -742,67 +765,126 @@ func getPackageExports(ctx context.Context, wrapped func(PackageExport), searchP
|
||||||
return getCandidatePkgs(ctx, callback, filename, filePkg, env)
|
return getCandidatePkgs(ctx, callback, filename, filePkg, env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var RequiredGoEnvVars = []string{"GO111MODULE", "GOFLAGS", "GOINSECURE", "GOMOD", "GOMODCACHE", "GONOPROXY", "GONOSUMDB", "GOPATH", "GOPROXY", "GOROOT", "GOSUMDB"}
|
||||||
|
|
||||||
// ProcessEnv contains environment variables and settings that affect the use of
|
// ProcessEnv contains environment variables and settings that affect the use of
|
||||||
// the go command, the go/build package, etc.
|
// the go command, the go/build package, etc.
|
||||||
type ProcessEnv struct {
|
type ProcessEnv struct {
|
||||||
LocalPrefix string
|
GocmdRunner *gocommand.Runner
|
||||||
|
|
||||||
BuildFlags []string
|
BuildFlags []string
|
||||||
|
|
||||||
// If non-empty, these will be used instead of the
|
// Env overrides the OS environment, and can be used to specify
|
||||||
// process-wide values.
|
// GOPROXY, GO111MODULE, etc. PATH cannot be set here, because
|
||||||
GOPATH, GOROOT, GO111MODULE, GOPROXY, GOFLAGS, GOSUMDB string
|
// exec.Command will not honor it.
|
||||||
|
// Specifying all of RequiredGoEnvVars avoids a call to `go env`.
|
||||||
|
Env map[string]string
|
||||||
|
|
||||||
WorkingDir string
|
WorkingDir string
|
||||||
|
|
||||||
// If Logf is non-nil, debug logging is enabled through this function.
|
// If Logf is non-nil, debug logging is enabled through this function.
|
||||||
Logf func(format string, args ...interface{})
|
Logf func(format string, args ...interface{})
|
||||||
|
|
||||||
|
initialized bool
|
||||||
|
|
||||||
resolver Resolver
|
resolver Resolver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *ProcessEnv) goEnv() (map[string]string, error) {
|
||||||
|
if err := e.init(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return e.Env, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ProcessEnv) matchFile(dir, name string) (bool, error) {
|
||||||
|
return build.Default.MatchFile(dir, name)
|
||||||
|
}
|
||||||
|
|
||||||
// CopyConfig copies the env's configuration into a new env.
|
// CopyConfig copies the env's configuration into a new env.
|
||||||
func (e *ProcessEnv) CopyConfig() *ProcessEnv {
|
func (e *ProcessEnv) CopyConfig() *ProcessEnv {
|
||||||
copy := *e
|
copy := &ProcessEnv{
|
||||||
copy.resolver = nil
|
GocmdRunner: e.GocmdRunner,
|
||||||
return ©
|
initialized: e.initialized,
|
||||||
|
BuildFlags: e.BuildFlags,
|
||||||
|
Logf: e.Logf,
|
||||||
|
WorkingDir: e.WorkingDir,
|
||||||
|
resolver: nil,
|
||||||
|
Env: map[string]string{},
|
||||||
|
}
|
||||||
|
for k, v := range e.Env {
|
||||||
|
copy.Env[k] = v
|
||||||
|
}
|
||||||
|
return copy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ProcessEnv) init() error {
|
||||||
|
if e.initialized {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
foundAllRequired := true
|
||||||
|
for _, k := range RequiredGoEnvVars {
|
||||||
|
if _, ok := e.Env[k]; !ok {
|
||||||
|
foundAllRequired = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundAllRequired {
|
||||||
|
e.initialized = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Env == nil {
|
||||||
|
e.Env = map[string]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
goEnv := map[string]string{}
|
||||||
|
stdout, err := e.invokeGo(context.TODO(), "env", append([]string{"-json"}, RequiredGoEnvVars...)...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(stdout.Bytes(), &goEnv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for k, v := range goEnv {
|
||||||
|
e.Env[k] = v
|
||||||
|
}
|
||||||
|
e.initialized = true
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ProcessEnv) env() []string {
|
func (e *ProcessEnv) env() []string {
|
||||||
env := os.Environ()
|
var env []string // the gocommand package will prepend os.Environ.
|
||||||
add := func(k, v string) {
|
for k, v := range e.Env {
|
||||||
if v != "" {
|
|
||||||
env = append(env, k+"="+v)
|
env = append(env, k+"="+v)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
add("GOPATH", e.GOPATH)
|
|
||||||
add("GOROOT", e.GOROOT)
|
|
||||||
add("GO111MODULE", e.GO111MODULE)
|
|
||||||
add("GOPROXY", e.GOPROXY)
|
|
||||||
add("GOFLAGS", e.GOFLAGS)
|
|
||||||
add("GOSUMDB", e.GOSUMDB)
|
|
||||||
if e.WorkingDir != "" {
|
|
||||||
add("PWD", e.WorkingDir)
|
|
||||||
}
|
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ProcessEnv) GetResolver() Resolver {
|
func (e *ProcessEnv) GetResolver() (Resolver, error) {
|
||||||
if e.resolver != nil {
|
if e.resolver != nil {
|
||||||
return e.resolver
|
return e.resolver, nil
|
||||||
}
|
}
|
||||||
out, err := e.invokeGo(context.TODO(), "env", "GOMOD")
|
if err := e.init(); err != nil {
|
||||||
if err != nil || len(bytes.TrimSpace(out.Bytes())) == 0 {
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(e.Env["GOMOD"]) == 0 {
|
||||||
e.resolver = newGopathResolver(e)
|
e.resolver = newGopathResolver(e)
|
||||||
return e.resolver
|
return e.resolver, nil
|
||||||
}
|
}
|
||||||
e.resolver = newModuleResolver(e)
|
e.resolver = newModuleResolver(e)
|
||||||
return e.resolver
|
return e.resolver, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ProcessEnv) buildContext() *build.Context {
|
func (e *ProcessEnv) buildContext() (*build.Context, error) {
|
||||||
ctx := build.Default
|
ctx := build.Default
|
||||||
ctx.GOROOT = e.GOROOT
|
goenv, err := e.goEnv()
|
||||||
ctx.GOPATH = e.GOPATH
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.GOROOT = goenv["GOROOT"]
|
||||||
|
ctx.GOPATH = goenv["GOPATH"]
|
||||||
|
|
||||||
// As of Go 1.14, build.Context has a Dir field
|
// As of Go 1.14, build.Context has a Dir field
|
||||||
// (see golang.org/issue/34860).
|
// (see golang.org/issue/34860).
|
||||||
|
@ -818,7 +900,7 @@ func (e *ProcessEnv) buildContext() *build.Context {
|
||||||
dir.SetString(e.WorkingDir)
|
dir.SetString(e.WorkingDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ctx
|
return &ctx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ProcessEnv) invokeGo(ctx context.Context, verb string, args ...string) (*bytes.Buffer, error) {
|
func (e *ProcessEnv) invokeGo(ctx context.Context, verb string, args ...string) (*bytes.Buffer, error) {
|
||||||
|
@ -830,13 +912,17 @@ func (e *ProcessEnv) invokeGo(ctx context.Context, verb string, args ...string)
|
||||||
Logf: e.Logf,
|
Logf: e.Logf,
|
||||||
WorkingDir: e.WorkingDir,
|
WorkingDir: e.WorkingDir,
|
||||||
}
|
}
|
||||||
return inv.Run(ctx)
|
return e.GocmdRunner.Run(ctx, inv)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addStdlibCandidates(pass *pass, refs references) {
|
func addStdlibCandidates(pass *pass, refs references) error {
|
||||||
|
goenv, err := pass.env.goEnv()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
add := func(pkg string) {
|
add := func(pkg string) {
|
||||||
// Prevent self-imports.
|
// Prevent self-imports.
|
||||||
if path.Base(pkg) == pass.f.Name.Name && filepath.Join(pass.env.GOROOT, "src", pkg) == pass.srcDir {
|
if path.Base(pkg) == pass.f.Name.Name && filepath.Join(goenv["GOROOT"], "src", pkg) == pass.srcDir {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
exports := copyExports(stdlib[pkg])
|
exports := copyExports(stdlib[pkg])
|
||||||
|
@ -857,6 +943,7 @@ func addStdlibCandidates(pass *pass, refs references) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Resolver does the build-system-specific parts of goimports.
|
// A Resolver does the build-system-specific parts of goimports.
|
||||||
|
@ -921,10 +1008,13 @@ func addExternalCandidates(pass *pass, refs references, filename string) error {
|
||||||
return false // We'll do our own loading after we sort.
|
return false // We'll do our own loading after we sort.
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := pass.env.GetResolver().scan(context.Background(), callback)
|
resolver, err := pass.env.GetResolver()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err = resolver.scan(context.Background(), callback); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Search for imports matching potential package references.
|
// Search for imports matching potential package references.
|
||||||
type result struct {
|
type result struct {
|
||||||
|
@ -1050,21 +1140,24 @@ func (r *gopathResolver) ClearForNewScan() {
|
||||||
|
|
||||||
func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
|
func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
|
||||||
names := map[string]string{}
|
names := map[string]string{}
|
||||||
|
bctx, err := r.env.buildContext()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
for _, path := range importPaths {
|
for _, path := range importPaths {
|
||||||
names[path] = importPathToName(r.env, path, srcDir)
|
names[path] = importPathToName(bctx, path, srcDir)
|
||||||
}
|
}
|
||||||
return names, nil
|
return names, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// importPathToName finds out the actual package name, as declared in its .go files.
|
// importPathToName finds out the actual package name, as declared in its .go files.
|
||||||
// If there's a problem, it returns "".
|
func importPathToName(bctx *build.Context, importPath, srcDir string) string {
|
||||||
func importPathToName(env *ProcessEnv, importPath, srcDir string) (packageName string) {
|
|
||||||
// Fast path for standard library without going to disk.
|
// Fast path for standard library without going to disk.
|
||||||
if _, ok := stdlib[importPath]; ok {
|
if _, ok := stdlib[importPath]; ok {
|
||||||
return path.Base(importPath) // stdlib packages always match their paths.
|
return path.Base(importPath) // stdlib packages always match their paths.
|
||||||
}
|
}
|
||||||
|
|
||||||
buildPkg, err := env.buildContext().Import(importPath, srcDir, build.FindOnly)
|
buildPkg, err := bctx.Import(importPath, srcDir, build.FindOnly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -1225,8 +1318,18 @@ func (r *gopathResolver) scan(ctx context.Context, callback *scanCallback) error
|
||||||
}
|
}
|
||||||
stop := r.cache.ScanAndListen(ctx, processDir)
|
stop := r.cache.ScanAndListen(ctx, processDir)
|
||||||
defer stop()
|
defer stop()
|
||||||
|
|
||||||
|
goenv, err := r.env.goEnv()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var roots []gopathwalk.Root
|
||||||
|
roots = append(roots, gopathwalk.Root{filepath.Join(goenv["GOROOT"], "src"), gopathwalk.RootGOROOT})
|
||||||
|
for _, p := range filepath.SplitList(goenv["GOPATH"]) {
|
||||||
|
roots = append(roots, gopathwalk.Root{filepath.Join(p, "src"), gopathwalk.RootGOPATH})
|
||||||
|
}
|
||||||
// The callback is not necessarily safe to use in the goroutine below. Process roots eagerly.
|
// The callback is not necessarily safe to use in the goroutine below. Process roots eagerly.
|
||||||
roots := filterRoots(gopathwalk.SrcDirsRoots(r.env.buildContext()), callback.rootFound)
|
roots = filterRoots(roots, callback.rootFound)
|
||||||
// We can't cancel walks, because we need them to finish to have a usable
|
// We can't cancel walks, because we need them to finish to have a usable
|
||||||
// cache. Instead, run them in a separate goroutine and detach.
|
// cache. Instead, run them in a separate goroutine and detach.
|
||||||
scanDone := make(chan struct{})
|
scanDone := make(chan struct{})
|
||||||
|
@ -1286,8 +1389,6 @@ func VendorlessPath(ipath string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, includeTest bool) (string, []string, error) {
|
func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, includeTest bool) (string, []string, error) {
|
||||||
var exports []string
|
|
||||||
|
|
||||||
// Look for non-test, buildable .go files which could provide exports.
|
// Look for non-test, buildable .go files which could provide exports.
|
||||||
all, err := ioutil.ReadDir(dir)
|
all, err := ioutil.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1299,7 +1400,7 @@ func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, incl
|
||||||
if !strings.HasSuffix(name, ".go") || (!includeTest && strings.HasSuffix(name, "_test.go")) {
|
if !strings.HasSuffix(name, ".go") || (!includeTest && strings.HasSuffix(name, "_test.go")) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
match, err := env.buildContext().MatchFile(dir, fi.Name())
|
match, err := env.matchFile(dir, fi.Name())
|
||||||
if err != nil || !match {
|
if err != nil || !match {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1311,6 +1412,7 @@ func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, incl
|
||||||
}
|
}
|
||||||
|
|
||||||
var pkgName string
|
var pkgName string
|
||||||
|
var exports []string
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
for _, fi := range files {
|
for _, fi := range files {
|
||||||
select {
|
select {
|
||||||
|
@ -1322,7 +1424,10 @@ func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, incl
|
||||||
fullFile := filepath.Join(dir, fi.Name())
|
fullFile := filepath.Join(dir, fi.Name())
|
||||||
f, err := parser.ParseFile(fset, fullFile, nil, 0)
|
f, err := parser.ParseFile(fset, fullFile, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, fmt.Errorf("parsing %s: %v", fullFile, err)
|
if env.Logf != nil {
|
||||||
|
env.Logf("error parsing %v: %v", fullFile, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if f.Name.Name == "documentation" {
|
if f.Name.Name == "documentation" {
|
||||||
// Special case from go/build.ImportDir, not
|
// Special case from go/build.ImportDir, not
|
||||||
|
@ -1362,6 +1467,10 @@ func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgNa
|
||||||
pass.env.Logf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), c.pkg.importPathShort, c.pkg.dir)
|
pass.env.Logf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), c.pkg.importPathShort, c.pkg.dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
resolver, err := pass.env.GetResolver()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Collect exports for packages with matching names.
|
// Collect exports for packages with matching names.
|
||||||
rescv := make([]chan *pkg, len(candidates))
|
rescv := make([]chan *pkg, len(candidates))
|
||||||
|
@ -1400,7 +1509,7 @@ func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgNa
|
||||||
}
|
}
|
||||||
// If we're an x_test, load the package under test's test variant.
|
// If we're an x_test, load the package under test's test variant.
|
||||||
includeTest := strings.HasSuffix(pass.f.Name.Name, "_test") && c.pkg.dir == pass.srcDir
|
includeTest := strings.HasSuffix(pass.f.Name.Name, "_test") && c.pkg.dir == pass.srcDir
|
||||||
_, exports, err := pass.env.GetResolver().loadExports(ctx, c.pkg, includeTest)
|
_, exports, err := resolver.loadExports(ctx, c.pkg, includeTest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if pass.env.Logf != nil {
|
if pass.env.Logf != nil {
|
||||||
pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err)
|
pass.env.Logf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err)
|
||||||
|
|
|
@ -11,17 +11,13 @@ package imports
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/build"
|
|
||||||
"go/format"
|
"go/format"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/printer"
|
"go/printer"
|
||||||
"go/token"
|
"go/token"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -33,6 +29,11 @@ import (
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Env *ProcessEnv // The environment to use. Note: this contains the cached module and filesystem state.
|
Env *ProcessEnv // The environment to use. Note: this contains the cached module and filesystem state.
|
||||||
|
|
||||||
|
// LocalPrefix is a comma-separated string of import path prefixes, which, if
|
||||||
|
// set, instructs Process to sort the import paths with the given prefixes
|
||||||
|
// into another group after 3rd-party packages.
|
||||||
|
LocalPrefix string
|
||||||
|
|
||||||
Fragment bool // Accept fragment of a source file (no package statement)
|
Fragment bool // Accept fragment of a source file (no package statement)
|
||||||
AllErrors bool // Report all errors (not just the first 10 on different lines)
|
AllErrors bool // Report all errors (not just the first 10 on different lines)
|
||||||
|
|
||||||
|
@ -43,13 +44,8 @@ type Options struct {
|
||||||
FormatOnly bool // Disable the insertion and deletion of imports
|
FormatOnly bool // Disable the insertion and deletion of imports
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process implements golang.org/x/tools/imports.Process with explicit context in env.
|
// Process implements golang.org/x/tools/imports.Process with explicit context in opt.Env.
|
||||||
func Process(filename string, src []byte, opt *Options) (formatted []byte, err error) {
|
func Process(filename string, src []byte, opt *Options) (formatted []byte, err error) {
|
||||||
src, opt, err = initialize(filename, src, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fileSet := token.NewFileSet()
|
fileSet := token.NewFileSet()
|
||||||
file, adjust, err := parse(fileSet, filename, src, opt)
|
file, adjust, err := parse(fileSet, filename, src, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -65,16 +61,12 @@ func Process(filename string, src []byte, opt *Options) (formatted []byte, err e
|
||||||
}
|
}
|
||||||
|
|
||||||
// FixImports returns a list of fixes to the imports that, when applied,
|
// FixImports returns a list of fixes to the imports that, when applied,
|
||||||
// will leave the imports in the same state as Process.
|
// will leave the imports in the same state as Process. src and opt must
|
||||||
|
// be specified.
|
||||||
//
|
//
|
||||||
// Note that filename's directory influences which imports can be chosen,
|
// Note that filename's directory influences which imports can be chosen,
|
||||||
// so it is important that filename be accurate.
|
// so it is important that filename be accurate.
|
||||||
func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) {
|
func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) {
|
||||||
src, opt, err = initialize(filename, src, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fileSet := token.NewFileSet()
|
fileSet := token.NewFileSet()
|
||||||
file, _, err := parse(fileSet, filename, src, opt)
|
file, _, err := parse(fileSet, filename, src, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -85,13 +77,9 @@ func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyFixes applies all of the fixes to the file and formats it. extraMode
|
// ApplyFixes applies all of the fixes to the file and formats it. extraMode
|
||||||
// is added in when parsing the file.
|
// is added in when parsing the file. src and opts must be specified, but no
|
||||||
|
// env is needed.
|
||||||
func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, extraMode parser.Mode) (formatted []byte, err error) {
|
func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, extraMode parser.Mode) (formatted []byte, err error) {
|
||||||
src, opt, err = initialize(filename, src, opt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't use parse() -- we don't care about fragments or statement lists
|
// Don't use parse() -- we don't care about fragments or statement lists
|
||||||
// here, and we need to work with unparseable files.
|
// here, and we need to work with unparseable files.
|
||||||
fileSet := token.NewFileSet()
|
fileSet := token.NewFileSet()
|
||||||
|
@ -115,59 +103,9 @@ func ApplyFixes(fixes []*ImportFix, filename string, src []byte, opt *Options, e
|
||||||
return formatFile(fileSet, file, src, nil, opt)
|
return formatFile(fileSet, file, src, nil, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllCandidates gets all of the packages starting with prefix that can be
|
|
||||||
// imported by filename, sorted by import path.
|
|
||||||
func GetAllCandidates(ctx context.Context, callback func(ImportFix), searchPrefix, filename, filePkg string, opt *Options) error {
|
|
||||||
_, opt, err := initialize(filename, []byte{}, opt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return getAllCandidates(ctx, callback, searchPrefix, filename, filePkg, opt.Env)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPackageExports returns all known packages with name pkg and their exports.
|
|
||||||
func GetPackageExports(ctx context.Context, callback func(PackageExport), searchPkg, filename, filePkg string, opt *Options) error {
|
|
||||||
_, opt, err := initialize(filename, []byte{}, opt)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return getPackageExports(ctx, callback, searchPkg, filename, filePkg, opt.Env)
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize sets the values for opt and src.
|
|
||||||
// If they are provided, they are not changed. Otherwise opt is set to the
|
|
||||||
// default values and src is read from the file system.
|
|
||||||
func initialize(filename string, src []byte, opt *Options) ([]byte, *Options, error) {
|
|
||||||
// Use defaults if opt is nil.
|
|
||||||
if opt == nil {
|
|
||||||
opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the env if the user has not provided it.
|
|
||||||
if opt.Env == nil {
|
|
||||||
opt.Env = &ProcessEnv{
|
|
||||||
GOPATH: build.Default.GOPATH,
|
|
||||||
GOROOT: build.Default.GOROOT,
|
|
||||||
GOFLAGS: os.Getenv("GOFLAGS"),
|
|
||||||
GO111MODULE: os.Getenv("GO111MODULE"),
|
|
||||||
GOPROXY: os.Getenv("GOPROXY"),
|
|
||||||
GOSUMDB: os.Getenv("GOSUMDB"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if src == nil {
|
|
||||||
b, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
src = b
|
|
||||||
}
|
|
||||||
|
|
||||||
return src, opt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(orig []byte, src []byte) []byte, opt *Options) ([]byte, error) {
|
func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(orig []byte, src []byte) []byte, opt *Options) ([]byte, error) {
|
||||||
mergeImports(opt.Env, fileSet, file)
|
mergeImports(fileSet, file)
|
||||||
sortImports(opt.Env, fileSet, file)
|
sortImports(opt.LocalPrefix, fileSet, file)
|
||||||
imps := astutil.Imports(fileSet, file)
|
imps := astutil.Imports(fileSet, file)
|
||||||
var spacesBefore []string // import paths we need spaces before
|
var spacesBefore []string // import paths we need spaces before
|
||||||
for _, impSection := range imps {
|
for _, impSection := range imps {
|
||||||
|
@ -178,7 +116,7 @@ func formatFile(fileSet *token.FileSet, file *ast.File, src []byte, adjust func(
|
||||||
lastGroup := -1
|
lastGroup := -1
|
||||||
for _, importSpec := range impSection {
|
for _, importSpec := range impSection {
|
||||||
importPath, _ := strconv.Unquote(importSpec.Path.Value)
|
importPath, _ := strconv.Unquote(importSpec.Path.Value)
|
||||||
groupNum := importGroup(opt.Env, importPath)
|
groupNum := importGroup(opt.LocalPrefix, importPath)
|
||||||
if groupNum != lastGroup && lastGroup != -1 {
|
if groupNum != lastGroup && lastGroup != -1 {
|
||||||
spacesBefore = append(spacesBefore, importPath)
|
spacesBefore = append(spacesBefore, importPath)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/mod/module"
|
"golang.org/x/mod/module"
|
||||||
"golang.org/x/mod/semver"
|
"golang.org/x/tools/internal/gocommand"
|
||||||
"golang.org/x/tools/internal/gopathwalk"
|
"golang.org/x/tools/internal/gopathwalk"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,31 +24,21 @@ import (
|
||||||
type ModuleResolver struct {
|
type ModuleResolver struct {
|
||||||
env *ProcessEnv
|
env *ProcessEnv
|
||||||
moduleCacheDir string
|
moduleCacheDir string
|
||||||
dummyVendorMod *ModuleJSON // If vendoring is enabled, the pseudo-module that represents the /vendor directory.
|
dummyVendorMod *gocommand.ModuleJSON // If vendoring is enabled, the pseudo-module that represents the /vendor directory.
|
||||||
roots []gopathwalk.Root
|
roots []gopathwalk.Root
|
||||||
scanSema chan struct{} // scanSema prevents concurrent scans and guards scannedRoots.
|
scanSema chan struct{} // scanSema prevents concurrent scans and guards scannedRoots.
|
||||||
scannedRoots map[gopathwalk.Root]bool
|
scannedRoots map[gopathwalk.Root]bool
|
||||||
|
|
||||||
initialized bool
|
initialized bool
|
||||||
main *ModuleJSON
|
main *gocommand.ModuleJSON
|
||||||
modsByModPath []*ModuleJSON // All modules, ordered by # of path components in module Path...
|
modsByModPath []*gocommand.ModuleJSON // All modules, ordered by # of path components in module Path...
|
||||||
modsByDir []*ModuleJSON // ...or Dir.
|
modsByDir []*gocommand.ModuleJSON // ...or Dir.
|
||||||
|
|
||||||
// moduleCacheCache stores information about the module cache.
|
// moduleCacheCache stores information about the module cache.
|
||||||
moduleCacheCache *dirInfoCache
|
moduleCacheCache *dirInfoCache
|
||||||
otherCache *dirInfoCache
|
otherCache *dirInfoCache
|
||||||
}
|
}
|
||||||
|
|
||||||
type ModuleJSON struct {
|
|
||||||
Path string // module path
|
|
||||||
Replace *ModuleJSON // replaced by this module
|
|
||||||
Main bool // is this the main module?
|
|
||||||
Indirect bool // is this module only an indirect dependency of main module?
|
|
||||||
Dir string // directory holding files for this module, if any
|
|
||||||
GoMod string // path to go.mod file for this module, if any
|
|
||||||
GoVersion string // go version used in module
|
|
||||||
}
|
|
||||||
|
|
||||||
func newModuleResolver(e *ProcessEnv) *ModuleResolver {
|
func newModuleResolver(e *ProcessEnv) *ModuleResolver {
|
||||||
r := &ModuleResolver{
|
r := &ModuleResolver{
|
||||||
env: e,
|
env: e,
|
||||||
|
@ -62,7 +52,18 @@ func (r *ModuleResolver) init() error {
|
||||||
if r.initialized {
|
if r.initialized {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
mainMod, vendorEnabled, err := vendorEnabled(r.env)
|
|
||||||
|
goenv, err := r.env.goEnv()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
inv := gocommand.Invocation{
|
||||||
|
BuildFlags: r.env.BuildFlags,
|
||||||
|
Env: r.env.env(),
|
||||||
|
Logf: r.env.Logf,
|
||||||
|
WorkingDir: r.env.WorkingDir,
|
||||||
|
}
|
||||||
|
mainMod, vendorEnabled, err := gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -71,18 +72,22 @@ func (r *ModuleResolver) init() error {
|
||||||
// Vendor mode is on, so all the non-Main modules are irrelevant,
|
// Vendor mode is on, so all the non-Main modules are irrelevant,
|
||||||
// and we need to search /vendor for everything.
|
// and we need to search /vendor for everything.
|
||||||
r.main = mainMod
|
r.main = mainMod
|
||||||
r.dummyVendorMod = &ModuleJSON{
|
r.dummyVendorMod = &gocommand.ModuleJSON{
|
||||||
Path: "",
|
Path: "",
|
||||||
Dir: filepath.Join(mainMod.Dir, "vendor"),
|
Dir: filepath.Join(mainMod.Dir, "vendor"),
|
||||||
}
|
}
|
||||||
r.modsByModPath = []*ModuleJSON{mainMod, r.dummyVendorMod}
|
r.modsByModPath = []*gocommand.ModuleJSON{mainMod, r.dummyVendorMod}
|
||||||
r.modsByDir = []*ModuleJSON{mainMod, r.dummyVendorMod}
|
r.modsByDir = []*gocommand.ModuleJSON{mainMod, r.dummyVendorMod}
|
||||||
} else {
|
} else {
|
||||||
// Vendor mode is off, so run go list -m ... to find everything.
|
// Vendor mode is off, so run go list -m ... to find everything.
|
||||||
r.initAllMods()
|
r.initAllMods()
|
||||||
}
|
}
|
||||||
|
|
||||||
r.moduleCacheDir = filepath.Join(filepath.SplitList(r.env.GOPATH)[0], "/pkg/mod")
|
if gmc := r.env.Env["GOMODCACHE"]; gmc != "" {
|
||||||
|
r.moduleCacheDir = gmc
|
||||||
|
} else {
|
||||||
|
r.moduleCacheDir = filepath.Join(filepath.SplitList(goenv["GOPATH"])[0], "/pkg/mod")
|
||||||
|
}
|
||||||
|
|
||||||
sort.Slice(r.modsByModPath, func(i, j int) bool {
|
sort.Slice(r.modsByModPath, func(i, j int) bool {
|
||||||
count := func(x int) int {
|
count := func(x int) int {
|
||||||
|
@ -98,7 +103,7 @@ func (r *ModuleResolver) init() error {
|
||||||
})
|
})
|
||||||
|
|
||||||
r.roots = []gopathwalk.Root{
|
r.roots = []gopathwalk.Root{
|
||||||
{filepath.Join(r.env.GOROOT, "/src"), gopathwalk.RootGOROOT},
|
{filepath.Join(goenv["GOROOT"], "/src"), gopathwalk.RootGOROOT},
|
||||||
}
|
}
|
||||||
if r.main != nil {
|
if r.main != nil {
|
||||||
r.roots = append(r.roots, gopathwalk.Root{r.main.Dir, gopathwalk.RootCurrentModule})
|
r.roots = append(r.roots, gopathwalk.Root{r.main.Dir, gopathwalk.RootCurrentModule})
|
||||||
|
@ -106,7 +111,7 @@ func (r *ModuleResolver) init() error {
|
||||||
if vendorEnabled {
|
if vendorEnabled {
|
||||||
r.roots = append(r.roots, gopathwalk.Root{r.dummyVendorMod.Dir, gopathwalk.RootOther})
|
r.roots = append(r.roots, gopathwalk.Root{r.dummyVendorMod.Dir, gopathwalk.RootOther})
|
||||||
} else {
|
} else {
|
||||||
addDep := func(mod *ModuleJSON) {
|
addDep := func(mod *gocommand.ModuleJSON) {
|
||||||
if mod.Replace == nil {
|
if mod.Replace == nil {
|
||||||
// This is redundant with the cache, but we'll skip it cheaply enough.
|
// This is redundant with the cache, but we'll skip it cheaply enough.
|
||||||
r.roots = append(r.roots, gopathwalk.Root{mod.Dir, gopathwalk.RootModuleCache})
|
r.roots = append(r.roots, gopathwalk.Root{mod.Dir, gopathwalk.RootModuleCache})
|
||||||
|
@ -151,7 +156,7 @@ func (r *ModuleResolver) initAllMods() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for dec := json.NewDecoder(stdout); dec.More(); {
|
for dec := json.NewDecoder(stdout); dec.More(); {
|
||||||
mod := &ModuleJSON{}
|
mod := &gocommand.ModuleJSON{}
|
||||||
if err := dec.Decode(mod); err != nil {
|
if err := dec.Decode(mod); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -197,7 +202,7 @@ func (r *ModuleResolver) ClearForNewMod() {
|
||||||
|
|
||||||
// findPackage returns the module and directory that contains the package at
|
// findPackage returns the module and directory that contains the package at
|
||||||
// the given import path, or returns nil, "" if no module is in scope.
|
// the given import path, or returns nil, "" if no module is in scope.
|
||||||
func (r *ModuleResolver) findPackage(importPath string) (*ModuleJSON, string) {
|
func (r *ModuleResolver) findPackage(importPath string) (*gocommand.ModuleJSON, string) {
|
||||||
// This can't find packages in the stdlib, but that's harmless for all
|
// This can't find packages in the stdlib, but that's harmless for all
|
||||||
// the existing code paths.
|
// the existing code paths.
|
||||||
for _, m := range r.modsByModPath {
|
for _, m := range r.modsByModPath {
|
||||||
|
@ -239,7 +244,7 @@ func (r *ModuleResolver) findPackage(importPath string) (*ModuleJSON, string) {
|
||||||
// files in that directory. If not, it could be provided by an
|
// files in that directory. If not, it could be provided by an
|
||||||
// outer module. See #29736.
|
// outer module. See #29736.
|
||||||
for _, fi := range pkgFiles {
|
for _, fi := range pkgFiles {
|
||||||
if ok, _ := r.env.buildContext().MatchFile(pkgDir, fi.Name()); ok {
|
if ok, _ := r.env.matchFile(pkgDir, fi.Name()); ok {
|
||||||
return m, pkgDir
|
return m, pkgDir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,7 +288,7 @@ func (r *ModuleResolver) cacheExports(ctx context.Context, env *ProcessEnv, info
|
||||||
|
|
||||||
// findModuleByDir returns the module that contains dir, or nil if no such
|
// findModuleByDir returns the module that contains dir, or nil if no such
|
||||||
// module is in scope.
|
// module is in scope.
|
||||||
func (r *ModuleResolver) findModuleByDir(dir string) *ModuleJSON {
|
func (r *ModuleResolver) findModuleByDir(dir string) *gocommand.ModuleJSON {
|
||||||
// This is quite tricky and may not be correct. dir could be:
|
// This is quite tricky and may not be correct. dir could be:
|
||||||
// - a package in the main module.
|
// - a package in the main module.
|
||||||
// - a replace target underneath the main module's directory.
|
// - a replace target underneath the main module's directory.
|
||||||
|
@ -310,7 +315,7 @@ func (r *ModuleResolver) findModuleByDir(dir string) *ModuleJSON {
|
||||||
|
|
||||||
// dirIsNestedModule reports if dir is contained in a nested module underneath
|
// dirIsNestedModule reports if dir is contained in a nested module underneath
|
||||||
// mod, not actually in mod.
|
// mod, not actually in mod.
|
||||||
func (r *ModuleResolver) dirIsNestedModule(dir string, mod *ModuleJSON) bool {
|
func (r *ModuleResolver) dirIsNestedModule(dir string, mod *gocommand.ModuleJSON) bool {
|
||||||
if !strings.HasPrefix(dir, mod.Dir) {
|
if !strings.HasPrefix(dir, mod.Dir) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -490,7 +495,7 @@ func (r *ModuleResolver) scoreImportPath(ctx context.Context, path string) int {
|
||||||
return modRelevance(mod)
|
return modRelevance(mod)
|
||||||
}
|
}
|
||||||
|
|
||||||
func modRelevance(mod *ModuleJSON) int {
|
func modRelevance(mod *gocommand.ModuleJSON) int {
|
||||||
switch {
|
switch {
|
||||||
case mod == nil: // out of scope
|
case mod == nil: // out of scope
|
||||||
return MaxRelevance - 4
|
return MaxRelevance - 4
|
||||||
|
@ -656,63 +661,3 @@ func modulePath(mod []byte) string {
|
||||||
}
|
}
|
||||||
return "" // missing module path
|
return "" // missing module path
|
||||||
}
|
}
|
||||||
|
|
||||||
var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
|
|
||||||
|
|
||||||
// vendorEnabled indicates if vendoring is enabled.
|
|
||||||
// Inspired by setDefaultBuildMod in modload/init.go
|
|
||||||
func vendorEnabled(env *ProcessEnv) (*ModuleJSON, bool, error) {
|
|
||||||
mainMod, go114, err := getMainModuleAnd114(env)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
matches := modFlagRegexp.FindStringSubmatch(env.GOFLAGS)
|
|
||||||
var modFlag string
|
|
||||||
if len(matches) != 0 {
|
|
||||||
modFlag = matches[1]
|
|
||||||
}
|
|
||||||
if modFlag != "" {
|
|
||||||
// Don't override an explicit '-mod=' argument.
|
|
||||||
return mainMod, modFlag == "vendor", nil
|
|
||||||
}
|
|
||||||
if mainMod == nil || !go114 {
|
|
||||||
return mainMod, false, nil
|
|
||||||
}
|
|
||||||
// Check 1.14's automatic vendor mode.
|
|
||||||
if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() {
|
|
||||||
if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 {
|
|
||||||
// The Go version is at least 1.14, and a vendor directory exists.
|
|
||||||
// Set -mod=vendor by default.
|
|
||||||
return mainMod, true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mainMod, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getMainModuleAnd114 gets the main module's information and whether the
|
|
||||||
// go command in use is 1.14+. This is the information needed to figure out
|
|
||||||
// if vendoring should be enabled.
|
|
||||||
func getMainModuleAnd114(env *ProcessEnv) (*ModuleJSON, bool, error) {
|
|
||||||
const format = `{{.Path}}
|
|
||||||
{{.Dir}}
|
|
||||||
{{.GoMod}}
|
|
||||||
{{.GoVersion}}
|
|
||||||
{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}
|
|
||||||
`
|
|
||||||
stdout, err := env.invokeGo(context.TODO(), "list", "-m", "-f", format)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, nil
|
|
||||||
}
|
|
||||||
lines := strings.Split(stdout.String(), "\n")
|
|
||||||
if len(lines) < 5 {
|
|
||||||
return nil, false, fmt.Errorf("unexpected stdout: %q", stdout)
|
|
||||||
}
|
|
||||||
mod := &ModuleJSON{
|
|
||||||
Path: lines[0],
|
|
||||||
Dir: lines[1],
|
|
||||||
GoMod: lines[2],
|
|
||||||
GoVersion: lines[3],
|
|
||||||
Main: true,
|
|
||||||
}
|
|
||||||
return mod, lines[4] == "go1.14", nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
|
|
||||||
// sortImports sorts runs of consecutive import lines in import blocks in f.
|
// sortImports sorts runs of consecutive import lines in import blocks in f.
|
||||||
// It also removes duplicate imports when it is possible to do so without data loss.
|
// It also removes duplicate imports when it is possible to do so without data loss.
|
||||||
func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
|
func sortImports(localPrefix string, fset *token.FileSet, f *ast.File) {
|
||||||
for i, d := range f.Decls {
|
for i, d := range f.Decls {
|
||||||
d, ok := d.(*ast.GenDecl)
|
d, ok := d.(*ast.GenDecl)
|
||||||
if !ok || d.Tok != token.IMPORT {
|
if !ok || d.Tok != token.IMPORT {
|
||||||
|
@ -40,11 +40,11 @@ func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
|
||||||
for j, s := range d.Specs {
|
for j, s := range d.Specs {
|
||||||
if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
|
if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
|
||||||
// j begins a new run. End this one.
|
// j begins a new run. End this one.
|
||||||
specs = append(specs, sortSpecs(env, fset, f, d.Specs[i:j])...)
|
specs = append(specs, sortSpecs(localPrefix, fset, f, d.Specs[i:j])...)
|
||||||
i = j
|
i = j
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
specs = append(specs, sortSpecs(env, fset, f, d.Specs[i:])...)
|
specs = append(specs, sortSpecs(localPrefix, fset, f, d.Specs[i:])...)
|
||||||
d.Specs = specs
|
d.Specs = specs
|
||||||
|
|
||||||
// Deduping can leave a blank line before the rparen; clean that up.
|
// Deduping can leave a blank line before the rparen; clean that up.
|
||||||
|
@ -60,7 +60,7 @@ func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
|
||||||
|
|
||||||
// mergeImports merges all the import declarations into the first one.
|
// mergeImports merges all the import declarations into the first one.
|
||||||
// Taken from golang.org/x/tools/ast/astutil.
|
// Taken from golang.org/x/tools/ast/astutil.
|
||||||
func mergeImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
|
func mergeImports(fset *token.FileSet, f *ast.File) {
|
||||||
if len(f.Decls) <= 1 {
|
if len(f.Decls) <= 1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ type posSpan struct {
|
||||||
End token.Pos
|
End token.Pos
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
|
func sortSpecs(localPrefix string, fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
|
||||||
// Can't short-circuit here even if specs are already sorted,
|
// Can't short-circuit here even if specs are already sorted,
|
||||||
// since they might yet need deduplication.
|
// since they might yet need deduplication.
|
||||||
// A lone import, however, may be safely ignored.
|
// A lone import, however, may be safely ignored.
|
||||||
|
@ -191,7 +191,7 @@ func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Sp
|
||||||
// Reassign the import paths to have the same position sequence.
|
// Reassign the import paths to have the same position sequence.
|
||||||
// Reassign each comment to abut the end of its spec.
|
// Reassign each comment to abut the end of its spec.
|
||||||
// Sort the comments by new position.
|
// Sort the comments by new position.
|
||||||
sort.Sort(byImportSpec{env, specs})
|
sort.Sort(byImportSpec{localPrefix, specs})
|
||||||
|
|
||||||
// Dedup. Thanks to our sorting, we can just consider
|
// Dedup. Thanks to our sorting, we can just consider
|
||||||
// adjacent pairs of imports.
|
// adjacent pairs of imports.
|
||||||
|
@ -245,7 +245,7 @@ func sortSpecs(env *ProcessEnv, fset *token.FileSet, f *ast.File, specs []ast.Sp
|
||||||
}
|
}
|
||||||
|
|
||||||
type byImportSpec struct {
|
type byImportSpec struct {
|
||||||
env *ProcessEnv
|
localPrefix string
|
||||||
specs []ast.Spec // slice of *ast.ImportSpec
|
specs []ast.Spec // slice of *ast.ImportSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,8 +255,8 @@ func (x byImportSpec) Less(i, j int) bool {
|
||||||
ipath := importPath(x.specs[i])
|
ipath := importPath(x.specs[i])
|
||||||
jpath := importPath(x.specs[j])
|
jpath := importPath(x.specs[j])
|
||||||
|
|
||||||
igroup := importGroup(x.env, ipath)
|
igroup := importGroup(x.localPrefix, ipath)
|
||||||
jgroup := importGroup(x.env, jpath)
|
jgroup := importGroup(x.localPrefix, jpath)
|
||||||
if igroup != jgroup {
|
if igroup != jgroup {
|
||||||
return igroup < jgroup
|
return igroup < jgroup
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ var stdlib = map[string][]string{
|
||||||
},
|
},
|
||||||
"bufio": []string{
|
"bufio": []string{
|
||||||
"ErrAdvanceTooFar",
|
"ErrAdvanceTooFar",
|
||||||
|
"ErrBadReadCount",
|
||||||
"ErrBufferFull",
|
"ErrBufferFull",
|
||||||
"ErrFinalToken",
|
"ErrFinalToken",
|
||||||
"ErrInvalidUnreadByte",
|
"ErrInvalidUnreadByte",
|
||||||
|
@ -303,7 +304,9 @@ var stdlib = map[string][]string{
|
||||||
"PrivateKey",
|
"PrivateKey",
|
||||||
"PublicKey",
|
"PublicKey",
|
||||||
"Sign",
|
"Sign",
|
||||||
|
"SignASN1",
|
||||||
"Verify",
|
"Verify",
|
||||||
|
"VerifyASN1",
|
||||||
},
|
},
|
||||||
"crypto/ed25519": []string{
|
"crypto/ed25519": []string{
|
||||||
"GenerateKey",
|
"GenerateKey",
|
||||||
|
@ -322,11 +325,13 @@ var stdlib = map[string][]string{
|
||||||
"CurveParams",
|
"CurveParams",
|
||||||
"GenerateKey",
|
"GenerateKey",
|
||||||
"Marshal",
|
"Marshal",
|
||||||
|
"MarshalCompressed",
|
||||||
"P224",
|
"P224",
|
||||||
"P256",
|
"P256",
|
||||||
"P384",
|
"P384",
|
||||||
"P521",
|
"P521",
|
||||||
"Unmarshal",
|
"Unmarshal",
|
||||||
|
"UnmarshalCompressed",
|
||||||
},
|
},
|
||||||
"crypto/hmac": []string{
|
"crypto/hmac": []string{
|
||||||
"Equal",
|
"Equal",
|
||||||
|
@ -415,6 +420,9 @@ var stdlib = map[string][]string{
|
||||||
"crypto/tls": []string{
|
"crypto/tls": []string{
|
||||||
"Certificate",
|
"Certificate",
|
||||||
"CertificateRequestInfo",
|
"CertificateRequestInfo",
|
||||||
|
"CipherSuite",
|
||||||
|
"CipherSuiteName",
|
||||||
|
"CipherSuites",
|
||||||
"Client",
|
"Client",
|
||||||
"ClientAuthType",
|
"ClientAuthType",
|
||||||
"ClientHelloInfo",
|
"ClientHelloInfo",
|
||||||
|
@ -429,11 +437,13 @@ var stdlib = map[string][]string{
|
||||||
"CurveP521",
|
"CurveP521",
|
||||||
"Dial",
|
"Dial",
|
||||||
"DialWithDialer",
|
"DialWithDialer",
|
||||||
|
"Dialer",
|
||||||
"ECDSAWithP256AndSHA256",
|
"ECDSAWithP256AndSHA256",
|
||||||
"ECDSAWithP384AndSHA384",
|
"ECDSAWithP384AndSHA384",
|
||||||
"ECDSAWithP521AndSHA512",
|
"ECDSAWithP521AndSHA512",
|
||||||
"ECDSAWithSHA1",
|
"ECDSAWithSHA1",
|
||||||
"Ed25519",
|
"Ed25519",
|
||||||
|
"InsecureCipherSuites",
|
||||||
"Listen",
|
"Listen",
|
||||||
"LoadX509KeyPair",
|
"LoadX509KeyPair",
|
||||||
"NewLRUClientSessionCache",
|
"NewLRUClientSessionCache",
|
||||||
|
@ -465,6 +475,7 @@ var stdlib = map[string][]string{
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
||||||
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
@ -473,6 +484,7 @@ var stdlib = map[string][]string{
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||||
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
||||||
"TLS_FALLBACK_SCSV",
|
"TLS_FALLBACK_SCSV",
|
||||||
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
@ -501,6 +513,7 @@ var stdlib = map[string][]string{
|
||||||
"ConstraintViolationError",
|
"ConstraintViolationError",
|
||||||
"CreateCertificate",
|
"CreateCertificate",
|
||||||
"CreateCertificateRequest",
|
"CreateCertificateRequest",
|
||||||
|
"CreateRevocationList",
|
||||||
"DSA",
|
"DSA",
|
||||||
"DSAWithSHA1",
|
"DSAWithSHA1",
|
||||||
"DSAWithSHA256",
|
"DSAWithSHA256",
|
||||||
|
@ -575,6 +588,7 @@ var stdlib = map[string][]string{
|
||||||
"PublicKeyAlgorithm",
|
"PublicKeyAlgorithm",
|
||||||
"PureEd25519",
|
"PureEd25519",
|
||||||
"RSA",
|
"RSA",
|
||||||
|
"RevocationList",
|
||||||
"SHA1WithRSA",
|
"SHA1WithRSA",
|
||||||
"SHA256WithRSA",
|
"SHA256WithRSA",
|
||||||
"SHA256WithRSAPSS",
|
"SHA256WithRSAPSS",
|
||||||
|
@ -688,6 +702,7 @@ var stdlib = map[string][]string{
|
||||||
"String",
|
"String",
|
||||||
"Tx",
|
"Tx",
|
||||||
"TxOptions",
|
"TxOptions",
|
||||||
|
"Validator",
|
||||||
"Value",
|
"Value",
|
||||||
"ValueConverter",
|
"ValueConverter",
|
||||||
"Valuer",
|
"Valuer",
|
||||||
|
@ -698,36 +713,65 @@ var stdlib = map[string][]string{
|
||||||
"Attr",
|
"Attr",
|
||||||
"AttrAbstractOrigin",
|
"AttrAbstractOrigin",
|
||||||
"AttrAccessibility",
|
"AttrAccessibility",
|
||||||
|
"AttrAddrBase",
|
||||||
"AttrAddrClass",
|
"AttrAddrClass",
|
||||||
|
"AttrAlignment",
|
||||||
"AttrAllocated",
|
"AttrAllocated",
|
||||||
"AttrArtificial",
|
"AttrArtificial",
|
||||||
"AttrAssociated",
|
"AttrAssociated",
|
||||||
"AttrBaseTypes",
|
"AttrBaseTypes",
|
||||||
|
"AttrBinaryScale",
|
||||||
"AttrBitOffset",
|
"AttrBitOffset",
|
||||||
"AttrBitSize",
|
"AttrBitSize",
|
||||||
"AttrByteSize",
|
"AttrByteSize",
|
||||||
|
"AttrCallAllCalls",
|
||||||
|
"AttrCallAllSourceCalls",
|
||||||
|
"AttrCallAllTailCalls",
|
||||||
"AttrCallColumn",
|
"AttrCallColumn",
|
||||||
|
"AttrCallDataLocation",
|
||||||
|
"AttrCallDataValue",
|
||||||
"AttrCallFile",
|
"AttrCallFile",
|
||||||
"AttrCallLine",
|
"AttrCallLine",
|
||||||
|
"AttrCallOrigin",
|
||||||
|
"AttrCallPC",
|
||||||
|
"AttrCallParameter",
|
||||||
|
"AttrCallReturnPC",
|
||||||
|
"AttrCallTailCall",
|
||||||
|
"AttrCallTarget",
|
||||||
|
"AttrCallTargetClobbered",
|
||||||
|
"AttrCallValue",
|
||||||
"AttrCalling",
|
"AttrCalling",
|
||||||
"AttrCommonRef",
|
"AttrCommonRef",
|
||||||
"AttrCompDir",
|
"AttrCompDir",
|
||||||
|
"AttrConstExpr",
|
||||||
"AttrConstValue",
|
"AttrConstValue",
|
||||||
"AttrContainingType",
|
"AttrContainingType",
|
||||||
"AttrCount",
|
"AttrCount",
|
||||||
|
"AttrDataBitOffset",
|
||||||
"AttrDataLocation",
|
"AttrDataLocation",
|
||||||
"AttrDataMemberLoc",
|
"AttrDataMemberLoc",
|
||||||
|
"AttrDecimalScale",
|
||||||
|
"AttrDecimalSign",
|
||||||
"AttrDeclColumn",
|
"AttrDeclColumn",
|
||||||
"AttrDeclFile",
|
"AttrDeclFile",
|
||||||
"AttrDeclLine",
|
"AttrDeclLine",
|
||||||
"AttrDeclaration",
|
"AttrDeclaration",
|
||||||
"AttrDefaultValue",
|
"AttrDefaultValue",
|
||||||
|
"AttrDefaulted",
|
||||||
|
"AttrDeleted",
|
||||||
"AttrDescription",
|
"AttrDescription",
|
||||||
|
"AttrDigitCount",
|
||||||
"AttrDiscr",
|
"AttrDiscr",
|
||||||
"AttrDiscrList",
|
"AttrDiscrList",
|
||||||
"AttrDiscrValue",
|
"AttrDiscrValue",
|
||||||
|
"AttrDwoName",
|
||||||
|
"AttrElemental",
|
||||||
"AttrEncoding",
|
"AttrEncoding",
|
||||||
|
"AttrEndianity",
|
||||||
"AttrEntrypc",
|
"AttrEntrypc",
|
||||||
|
"AttrEnumClass",
|
||||||
|
"AttrExplicit",
|
||||||
|
"AttrExportSymbols",
|
||||||
"AttrExtension",
|
"AttrExtension",
|
||||||
"AttrExternal",
|
"AttrExternal",
|
||||||
"AttrFrameBase",
|
"AttrFrameBase",
|
||||||
|
@ -738,27 +782,47 @@ var stdlib = map[string][]string{
|
||||||
"AttrInline",
|
"AttrInline",
|
||||||
"AttrIsOptional",
|
"AttrIsOptional",
|
||||||
"AttrLanguage",
|
"AttrLanguage",
|
||||||
|
"AttrLinkageName",
|
||||||
"AttrLocation",
|
"AttrLocation",
|
||||||
|
"AttrLoclistsBase",
|
||||||
"AttrLowerBound",
|
"AttrLowerBound",
|
||||||
"AttrLowpc",
|
"AttrLowpc",
|
||||||
"AttrMacroInfo",
|
"AttrMacroInfo",
|
||||||
|
"AttrMacros",
|
||||||
|
"AttrMainSubprogram",
|
||||||
|
"AttrMutable",
|
||||||
"AttrName",
|
"AttrName",
|
||||||
"AttrNamelistItem",
|
"AttrNamelistItem",
|
||||||
|
"AttrNoreturn",
|
||||||
|
"AttrObjectPointer",
|
||||||
"AttrOrdering",
|
"AttrOrdering",
|
||||||
|
"AttrPictureString",
|
||||||
"AttrPriority",
|
"AttrPriority",
|
||||||
"AttrProducer",
|
"AttrProducer",
|
||||||
"AttrPrototyped",
|
"AttrPrototyped",
|
||||||
|
"AttrPure",
|
||||||
"AttrRanges",
|
"AttrRanges",
|
||||||
|
"AttrRank",
|
||||||
|
"AttrRecursive",
|
||||||
|
"AttrReference",
|
||||||
"AttrReturnAddr",
|
"AttrReturnAddr",
|
||||||
|
"AttrRnglistsBase",
|
||||||
|
"AttrRvalueReference",
|
||||||
"AttrSegment",
|
"AttrSegment",
|
||||||
"AttrSibling",
|
"AttrSibling",
|
||||||
|
"AttrSignature",
|
||||||
|
"AttrSmall",
|
||||||
"AttrSpecification",
|
"AttrSpecification",
|
||||||
"AttrStartScope",
|
"AttrStartScope",
|
||||||
"AttrStaticLink",
|
"AttrStaticLink",
|
||||||
"AttrStmtList",
|
"AttrStmtList",
|
||||||
|
"AttrStrOffsetsBase",
|
||||||
"AttrStride",
|
"AttrStride",
|
||||||
"AttrStrideSize",
|
"AttrStrideSize",
|
||||||
"AttrStringLength",
|
"AttrStringLength",
|
||||||
|
"AttrStringLengthBitSize",
|
||||||
|
"AttrStringLengthByteSize",
|
||||||
|
"AttrThreadsScaled",
|
||||||
"AttrTrampoline",
|
"AttrTrampoline",
|
||||||
"AttrType",
|
"AttrType",
|
||||||
"AttrUpperBound",
|
"AttrUpperBound",
|
||||||
|
@ -772,18 +836,23 @@ var stdlib = map[string][]string{
|
||||||
"BoolType",
|
"BoolType",
|
||||||
"CharType",
|
"CharType",
|
||||||
"Class",
|
"Class",
|
||||||
|
"ClassAddrPtr",
|
||||||
"ClassAddress",
|
"ClassAddress",
|
||||||
"ClassBlock",
|
"ClassBlock",
|
||||||
"ClassConstant",
|
"ClassConstant",
|
||||||
"ClassExprLoc",
|
"ClassExprLoc",
|
||||||
"ClassFlag",
|
"ClassFlag",
|
||||||
"ClassLinePtr",
|
"ClassLinePtr",
|
||||||
|
"ClassLocList",
|
||||||
"ClassLocListPtr",
|
"ClassLocListPtr",
|
||||||
"ClassMacPtr",
|
"ClassMacPtr",
|
||||||
"ClassRangeListPtr",
|
"ClassRangeListPtr",
|
||||||
"ClassReference",
|
"ClassReference",
|
||||||
"ClassReferenceAlt",
|
"ClassReferenceAlt",
|
||||||
"ClassReferenceSig",
|
"ClassReferenceSig",
|
||||||
|
"ClassRngList",
|
||||||
|
"ClassRngListsPtr",
|
||||||
|
"ClassStrOffsetsPtr",
|
||||||
"ClassString",
|
"ClassString",
|
||||||
"ClassStringAlt",
|
"ClassStringAlt",
|
||||||
"ClassUnknown",
|
"ClassUnknown",
|
||||||
|
@ -814,9 +883,13 @@ var stdlib = map[string][]string{
|
||||||
"Tag",
|
"Tag",
|
||||||
"TagAccessDeclaration",
|
"TagAccessDeclaration",
|
||||||
"TagArrayType",
|
"TagArrayType",
|
||||||
|
"TagAtomicType",
|
||||||
"TagBaseType",
|
"TagBaseType",
|
||||||
|
"TagCallSite",
|
||||||
|
"TagCallSiteParameter",
|
||||||
"TagCatchDwarfBlock",
|
"TagCatchDwarfBlock",
|
||||||
"TagClassType",
|
"TagClassType",
|
||||||
|
"TagCoarrayType",
|
||||||
"TagCommonDwarfBlock",
|
"TagCommonDwarfBlock",
|
||||||
"TagCommonInclusion",
|
"TagCommonInclusion",
|
||||||
"TagCompileUnit",
|
"TagCompileUnit",
|
||||||
|
@ -824,12 +897,15 @@ var stdlib = map[string][]string{
|
||||||
"TagConstType",
|
"TagConstType",
|
||||||
"TagConstant",
|
"TagConstant",
|
||||||
"TagDwarfProcedure",
|
"TagDwarfProcedure",
|
||||||
|
"TagDynamicType",
|
||||||
"TagEntryPoint",
|
"TagEntryPoint",
|
||||||
"TagEnumerationType",
|
"TagEnumerationType",
|
||||||
"TagEnumerator",
|
"TagEnumerator",
|
||||||
"TagFileType",
|
"TagFileType",
|
||||||
"TagFormalParameter",
|
"TagFormalParameter",
|
||||||
"TagFriend",
|
"TagFriend",
|
||||||
|
"TagGenericSubrange",
|
||||||
|
"TagImmutableType",
|
||||||
"TagImportedDeclaration",
|
"TagImportedDeclaration",
|
||||||
"TagImportedModule",
|
"TagImportedModule",
|
||||||
"TagImportedUnit",
|
"TagImportedUnit",
|
||||||
|
@ -853,6 +929,7 @@ var stdlib = map[string][]string{
|
||||||
"TagRvalueReferenceType",
|
"TagRvalueReferenceType",
|
||||||
"TagSetType",
|
"TagSetType",
|
||||||
"TagSharedType",
|
"TagSharedType",
|
||||||
|
"TagSkeletonUnit",
|
||||||
"TagStringType",
|
"TagStringType",
|
||||||
"TagStructType",
|
"TagStructType",
|
||||||
"TagSubprogram",
|
"TagSubprogram",
|
||||||
|
@ -2281,6 +2358,27 @@ var stdlib = map[string][]string{
|
||||||
"IMAGE_DIRECTORY_ENTRY_RESOURCE",
|
"IMAGE_DIRECTORY_ENTRY_RESOURCE",
|
||||||
"IMAGE_DIRECTORY_ENTRY_SECURITY",
|
"IMAGE_DIRECTORY_ENTRY_SECURITY",
|
||||||
"IMAGE_DIRECTORY_ENTRY_TLS",
|
"IMAGE_DIRECTORY_ENTRY_TLS",
|
||||||
|
"IMAGE_DLLCHARACTERISTICS_APPCONTAINER",
|
||||||
|
"IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE",
|
||||||
|
"IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY",
|
||||||
|
"IMAGE_DLLCHARACTERISTICS_GUARD_CF",
|
||||||
|
"IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA",
|
||||||
|
"IMAGE_DLLCHARACTERISTICS_NO_BIND",
|
||||||
|
"IMAGE_DLLCHARACTERISTICS_NO_ISOLATION",
|
||||||
|
"IMAGE_DLLCHARACTERISTICS_NO_SEH",
|
||||||
|
"IMAGE_DLLCHARACTERISTICS_NX_COMPAT",
|
||||||
|
"IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE",
|
||||||
|
"IMAGE_DLLCHARACTERISTICS_WDM_DRIVER",
|
||||||
|
"IMAGE_FILE_32BIT_MACHINE",
|
||||||
|
"IMAGE_FILE_AGGRESIVE_WS_TRIM",
|
||||||
|
"IMAGE_FILE_BYTES_REVERSED_HI",
|
||||||
|
"IMAGE_FILE_BYTES_REVERSED_LO",
|
||||||
|
"IMAGE_FILE_DEBUG_STRIPPED",
|
||||||
|
"IMAGE_FILE_DLL",
|
||||||
|
"IMAGE_FILE_EXECUTABLE_IMAGE",
|
||||||
|
"IMAGE_FILE_LARGE_ADDRESS_AWARE",
|
||||||
|
"IMAGE_FILE_LINE_NUMS_STRIPPED",
|
||||||
|
"IMAGE_FILE_LOCAL_SYMS_STRIPPED",
|
||||||
"IMAGE_FILE_MACHINE_AM33",
|
"IMAGE_FILE_MACHINE_AM33",
|
||||||
"IMAGE_FILE_MACHINE_AMD64",
|
"IMAGE_FILE_MACHINE_AMD64",
|
||||||
"IMAGE_FILE_MACHINE_ARM",
|
"IMAGE_FILE_MACHINE_ARM",
|
||||||
|
@ -2303,6 +2401,25 @@ var stdlib = map[string][]string{
|
||||||
"IMAGE_FILE_MACHINE_THUMB",
|
"IMAGE_FILE_MACHINE_THUMB",
|
||||||
"IMAGE_FILE_MACHINE_UNKNOWN",
|
"IMAGE_FILE_MACHINE_UNKNOWN",
|
||||||
"IMAGE_FILE_MACHINE_WCEMIPSV2",
|
"IMAGE_FILE_MACHINE_WCEMIPSV2",
|
||||||
|
"IMAGE_FILE_NET_RUN_FROM_SWAP",
|
||||||
|
"IMAGE_FILE_RELOCS_STRIPPED",
|
||||||
|
"IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP",
|
||||||
|
"IMAGE_FILE_SYSTEM",
|
||||||
|
"IMAGE_FILE_UP_SYSTEM_ONLY",
|
||||||
|
"IMAGE_SUBSYSTEM_EFI_APPLICATION",
|
||||||
|
"IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER",
|
||||||
|
"IMAGE_SUBSYSTEM_EFI_ROM",
|
||||||
|
"IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER",
|
||||||
|
"IMAGE_SUBSYSTEM_NATIVE",
|
||||||
|
"IMAGE_SUBSYSTEM_NATIVE_WINDOWS",
|
||||||
|
"IMAGE_SUBSYSTEM_OS2_CUI",
|
||||||
|
"IMAGE_SUBSYSTEM_POSIX_CUI",
|
||||||
|
"IMAGE_SUBSYSTEM_UNKNOWN",
|
||||||
|
"IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION",
|
||||||
|
"IMAGE_SUBSYSTEM_WINDOWS_CE_GUI",
|
||||||
|
"IMAGE_SUBSYSTEM_WINDOWS_CUI",
|
||||||
|
"IMAGE_SUBSYSTEM_WINDOWS_GUI",
|
||||||
|
"IMAGE_SUBSYSTEM_XBOX",
|
||||||
"ImportDirectory",
|
"ImportDirectory",
|
||||||
"NewFile",
|
"NewFile",
|
||||||
"Open",
|
"Open",
|
||||||
|
@ -2359,6 +2476,7 @@ var stdlib = map[string][]string{
|
||||||
"RawValue",
|
"RawValue",
|
||||||
"StructuralError",
|
"StructuralError",
|
||||||
"SyntaxError",
|
"SyntaxError",
|
||||||
|
"TagBMPString",
|
||||||
"TagBitString",
|
"TagBitString",
|
||||||
"TagBoolean",
|
"TagBoolean",
|
||||||
"TagEnum",
|
"TagEnum",
|
||||||
|
@ -2787,6 +2905,7 @@ var stdlib = map[string][]string{
|
||||||
"IsPredeclared",
|
"IsPredeclared",
|
||||||
"Mode",
|
"Mode",
|
||||||
"New",
|
"New",
|
||||||
|
"NewFromFiles",
|
||||||
"Note",
|
"Note",
|
||||||
"Package",
|
"Package",
|
||||||
"PreserveAST",
|
"PreserveAST",
|
||||||
|
@ -3115,6 +3234,11 @@ var stdlib = map[string][]string{
|
||||||
"New64",
|
"New64",
|
||||||
"New64a",
|
"New64a",
|
||||||
},
|
},
|
||||||
|
"hash/maphash": []string{
|
||||||
|
"Hash",
|
||||||
|
"MakeSeed",
|
||||||
|
"Seed",
|
||||||
|
},
|
||||||
"html": []string{
|
"html": []string{
|
||||||
"EscapeString",
|
"EscapeString",
|
||||||
"UnescapeString",
|
"UnescapeString",
|
||||||
|
@ -3367,6 +3491,7 @@ var stdlib = map[string][]string{
|
||||||
"Ldate",
|
"Ldate",
|
||||||
"Llongfile",
|
"Llongfile",
|
||||||
"Lmicroseconds",
|
"Lmicroseconds",
|
||||||
|
"Lmsgprefix",
|
||||||
"Logger",
|
"Logger",
|
||||||
"Lshortfile",
|
"Lshortfile",
|
||||||
"LstdFlags",
|
"LstdFlags",
|
||||||
|
@ -3443,6 +3568,7 @@ var stdlib = map[string][]string{
|
||||||
"Exp",
|
"Exp",
|
||||||
"Exp2",
|
"Exp2",
|
||||||
"Expm1",
|
"Expm1",
|
||||||
|
"FMA",
|
||||||
"Float32bits",
|
"Float32bits",
|
||||||
"Float32frombits",
|
"Float32frombits",
|
||||||
"Float64bits",
|
"Float64bits",
|
||||||
|
@ -3567,6 +3693,9 @@ var stdlib = map[string][]string{
|
||||||
"OnesCount32",
|
"OnesCount32",
|
||||||
"OnesCount64",
|
"OnesCount64",
|
||||||
"OnesCount8",
|
"OnesCount8",
|
||||||
|
"Rem",
|
||||||
|
"Rem32",
|
||||||
|
"Rem64",
|
||||||
"Reverse",
|
"Reverse",
|
||||||
"Reverse16",
|
"Reverse16",
|
||||||
"Reverse32",
|
"Reverse32",
|
||||||
|
@ -4108,6 +4237,7 @@ var stdlib = map[string][]string{
|
||||||
"DevNull",
|
"DevNull",
|
||||||
"Environ",
|
"Environ",
|
||||||
"ErrClosed",
|
"ErrClosed",
|
||||||
|
"ErrDeadlineExceeded",
|
||||||
"ErrExist",
|
"ErrExist",
|
||||||
"ErrInvalid",
|
"ErrInvalid",
|
||||||
"ErrNoDeadline",
|
"ErrNoDeadline",
|
||||||
|
@ -4566,6 +4696,7 @@ var stdlib = map[string][]string{
|
||||||
"ErrRange",
|
"ErrRange",
|
||||||
"ErrSyntax",
|
"ErrSyntax",
|
||||||
"FormatBool",
|
"FormatBool",
|
||||||
|
"FormatComplex",
|
||||||
"FormatFloat",
|
"FormatFloat",
|
||||||
"FormatInt",
|
"FormatInt",
|
||||||
"FormatUint",
|
"FormatUint",
|
||||||
|
@ -4575,6 +4706,7 @@ var stdlib = map[string][]string{
|
||||||
"Itoa",
|
"Itoa",
|
||||||
"NumError",
|
"NumError",
|
||||||
"ParseBool",
|
"ParseBool",
|
||||||
|
"ParseComplex",
|
||||||
"ParseFloat",
|
"ParseFloat",
|
||||||
"ParseInt",
|
"ParseInt",
|
||||||
"ParseUint",
|
"ParseUint",
|
||||||
|
@ -5140,7 +5272,10 @@ var stdlib = map[string][]string{
|
||||||
"CTL_NET",
|
"CTL_NET",
|
||||||
"CTL_QUERY",
|
"CTL_QUERY",
|
||||||
"CTRL_BREAK_EVENT",
|
"CTRL_BREAK_EVENT",
|
||||||
|
"CTRL_CLOSE_EVENT",
|
||||||
"CTRL_C_EVENT",
|
"CTRL_C_EVENT",
|
||||||
|
"CTRL_LOGOFF_EVENT",
|
||||||
|
"CTRL_SHUTDOWN_EVENT",
|
||||||
"CancelIo",
|
"CancelIo",
|
||||||
"CancelIoEx",
|
"CancelIoEx",
|
||||||
"CertAddCertificateContextToStore",
|
"CertAddCertificateContextToStore",
|
||||||
|
@ -10112,6 +10247,7 @@ var stdlib = map[string][]string{
|
||||||
"Duployan",
|
"Duployan",
|
||||||
"Egyptian_Hieroglyphs",
|
"Egyptian_Hieroglyphs",
|
||||||
"Elbasan",
|
"Elbasan",
|
||||||
|
"Elymaic",
|
||||||
"Ethiopic",
|
"Ethiopic",
|
||||||
"Extender",
|
"Extender",
|
||||||
"FoldCategory",
|
"FoldCategory",
|
||||||
|
@ -10215,6 +10351,7 @@ var stdlib = map[string][]string{
|
||||||
"Myanmar",
|
"Myanmar",
|
||||||
"N",
|
"N",
|
||||||
"Nabataean",
|
"Nabataean",
|
||||||
|
"Nandinagari",
|
||||||
"Nd",
|
"Nd",
|
||||||
"New_Tai_Lue",
|
"New_Tai_Lue",
|
||||||
"Newa",
|
"Newa",
|
||||||
|
@ -10224,6 +10361,7 @@ var stdlib = map[string][]string{
|
||||||
"Noncharacter_Code_Point",
|
"Noncharacter_Code_Point",
|
||||||
"Number",
|
"Number",
|
||||||
"Nushu",
|
"Nushu",
|
||||||
|
"Nyiakeng_Puachue_Hmong",
|
||||||
"Ogham",
|
"Ogham",
|
||||||
"Ol_Chiki",
|
"Ol_Chiki",
|
||||||
"Old_Hungarian",
|
"Old_Hungarian",
|
||||||
|
@ -10331,6 +10469,7 @@ var stdlib = map[string][]string{
|
||||||
"Vai",
|
"Vai",
|
||||||
"Variation_Selector",
|
"Variation_Selector",
|
||||||
"Version",
|
"Version",
|
||||||
|
"Wancho",
|
||||||
"Warang_Citi",
|
"Warang_Citi",
|
||||||
"White_Space",
|
"White_Space",
|
||||||
"Yi",
|
"Yi",
|
||||||
|
|
|
@ -0,0 +1,168 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package fuzzy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RuneRole specifies the role of a rune in the context of an input.
|
||||||
|
type RuneRole byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RNone specifies a rune without any role in the input (i.e., whitespace/non-ASCII).
|
||||||
|
RNone RuneRole = iota
|
||||||
|
// RSep specifies a rune with the role of segment separator.
|
||||||
|
RSep
|
||||||
|
// RTail specifies a rune which is a lower-case tail in a word in the input.
|
||||||
|
RTail
|
||||||
|
// RUCTail specifies a rune which is an upper-case tail in a word in the input.
|
||||||
|
RUCTail
|
||||||
|
// RHead specifies a rune which is the first character in a word in the input.
|
||||||
|
RHead
|
||||||
|
)
|
||||||
|
|
||||||
|
// RuneRoles detects the roles of each byte rune in an input string and stores it in the output
|
||||||
|
// slice. The rune role depends on the input type. Stops when it parsed all the runes in the string
|
||||||
|
// or when it filled the output. If output is nil, then it gets created.
|
||||||
|
func RuneRoles(str string, reuse []RuneRole) []RuneRole {
|
||||||
|
var output []RuneRole
|
||||||
|
if cap(reuse) < len(str) {
|
||||||
|
output = make([]RuneRole, 0, len(str))
|
||||||
|
} else {
|
||||||
|
output = reuse[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
prev, prev2 := rtNone, rtNone
|
||||||
|
for i := 0; i < len(str); i++ {
|
||||||
|
r := rune(str[i])
|
||||||
|
|
||||||
|
role := RNone
|
||||||
|
|
||||||
|
curr := rtLower
|
||||||
|
if str[i] <= unicode.MaxASCII {
|
||||||
|
curr = runeType(rt[str[i]] - '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
if curr == rtLower {
|
||||||
|
if prev == rtNone || prev == rtPunct {
|
||||||
|
role = RHead
|
||||||
|
} else {
|
||||||
|
role = RTail
|
||||||
|
}
|
||||||
|
} else if curr == rtUpper {
|
||||||
|
role = RHead
|
||||||
|
|
||||||
|
if prev == rtUpper {
|
||||||
|
// This and previous characters are both upper case.
|
||||||
|
|
||||||
|
if i+1 == len(str) {
|
||||||
|
// This is last character, previous was also uppercase -> this is UCTail
|
||||||
|
// i.e., (current char is C): aBC / BC / ABC
|
||||||
|
role = RUCTail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if curr == rtPunct {
|
||||||
|
switch r {
|
||||||
|
case '.', ':':
|
||||||
|
role = RSep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if curr != rtLower {
|
||||||
|
if i > 1 && output[i-1] == RHead && prev2 == rtUpper && (output[i-2] == RHead || output[i-2] == RUCTail) {
|
||||||
|
// The previous two characters were uppercase. The current one is not a lower case, so the
|
||||||
|
// previous one can't be a HEAD. Make it a UCTail.
|
||||||
|
// i.e., (last char is current char - B must be a UCTail): ABC / ZABC / AB.
|
||||||
|
output[i-1] = RUCTail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output = append(output, role)
|
||||||
|
prev2 = prev
|
||||||
|
prev = curr
|
||||||
|
}
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
type runeType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
rtNone runeType = iota
|
||||||
|
rtPunct
|
||||||
|
rtLower
|
||||||
|
rtUpper
|
||||||
|
)
|
||||||
|
|
||||||
|
const rt = "00000000000000000000000000000000000000000000001122222222221000000333333333333333333333333330000002222222222222222222222222200000"
|
||||||
|
|
||||||
|
// LastSegment returns the substring representing the last segment from the input, where each
|
||||||
|
// byte has an associated RuneRole in the roles slice. This makes sense only for inputs of Symbol
|
||||||
|
// or Filename type.
|
||||||
|
func LastSegment(input string, roles []RuneRole) string {
|
||||||
|
// Exclude ending separators.
|
||||||
|
end := len(input) - 1
|
||||||
|
for end >= 0 && roles[end] == RSep {
|
||||||
|
end--
|
||||||
|
}
|
||||||
|
if end < 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
start := end - 1
|
||||||
|
for start >= 0 && roles[start] != RSep {
|
||||||
|
start--
|
||||||
|
}
|
||||||
|
|
||||||
|
return input[start+1 : end+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToLower transforms the input string to lower case, which is stored in the output byte slice.
|
||||||
|
// The lower casing considers only ASCII values - non ASCII values are left unmodified.
|
||||||
|
// Stops when parsed all input or when it filled the output slice. If output is nil, then it gets
|
||||||
|
// created.
|
||||||
|
func ToLower(input string, reuse []byte) []byte {
|
||||||
|
output := reuse
|
||||||
|
if cap(reuse) < len(input) {
|
||||||
|
output = make([]byte, len(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(input); i++ {
|
||||||
|
r := rune(input[i])
|
||||||
|
if r <= unicode.MaxASCII {
|
||||||
|
if 'A' <= r && r <= 'Z' {
|
||||||
|
r += 'a' - 'A'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output[i] = byte(r)
|
||||||
|
}
|
||||||
|
return output[:len(input)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// WordConsumer defines a consumer for a word delimited by the [start,end) byte offsets in an input
|
||||||
|
// (start is inclusive, end is exclusive).
|
||||||
|
type WordConsumer func(start, end int)
|
||||||
|
|
||||||
|
// Words find word delimiters in an input based on its bytes' mappings to rune roles. The offset
|
||||||
|
// delimiters for each word are fed to the provided consumer function.
|
||||||
|
func Words(roles []RuneRole, consume WordConsumer) {
|
||||||
|
var wordStart int
|
||||||
|
for i, r := range roles {
|
||||||
|
switch r {
|
||||||
|
case RUCTail, RTail:
|
||||||
|
case RHead, RNone, RSep:
|
||||||
|
if i != wordStart {
|
||||||
|
consume(wordStart, i)
|
||||||
|
}
|
||||||
|
wordStart = i
|
||||||
|
if r != RHead {
|
||||||
|
// Skip this character.
|
||||||
|
wordStart = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if wordStart != len(roles) {
|
||||||
|
consume(wordStart, len(roles))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,398 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package fuzzy implements a fuzzy matching algorithm.
|
||||||
|
package fuzzy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxInputSize is the maximum size of the input scored against the fuzzy matcher. Longer inputs
|
||||||
|
// will be truncated to this size.
|
||||||
|
MaxInputSize = 127
|
||||||
|
// MaxPatternSize is the maximum size of the pattern used to construct the fuzzy matcher. Longer
|
||||||
|
// inputs are truncated to this size.
|
||||||
|
MaxPatternSize = 63
|
||||||
|
)
|
||||||
|
|
||||||
|
type scoreVal int
|
||||||
|
|
||||||
|
func (s scoreVal) val() int {
|
||||||
|
return int(s) >> 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s scoreVal) prevK() int {
|
||||||
|
return int(s) & 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func score(val int, prevK int /*0 or 1*/) scoreVal {
|
||||||
|
return scoreVal(val<<1 + prevK)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matcher implements a fuzzy matching algorithm for scoring candidates against a pattern.
|
||||||
|
// The matcher does not support parallel usage.
|
||||||
|
type Matcher struct {
|
||||||
|
pattern string
|
||||||
|
patternLower []byte // lower-case version of the pattern
|
||||||
|
patternShort []byte // first characters of the pattern
|
||||||
|
caseSensitive bool // set if the pattern is mix-cased
|
||||||
|
|
||||||
|
patternRoles []RuneRole // the role of each character in the pattern
|
||||||
|
roles []RuneRole // the role of each character in the tested string
|
||||||
|
|
||||||
|
scores [MaxInputSize + 1][MaxPatternSize + 1][2]scoreVal
|
||||||
|
|
||||||
|
scoreScale float32
|
||||||
|
|
||||||
|
lastCandidateLen int // in bytes
|
||||||
|
lastCandidateMatched bool
|
||||||
|
|
||||||
|
// Here we save the last candidate in lower-case. This is basically a byte slice we reuse for
|
||||||
|
// performance reasons, so the slice is not reallocated for every candidate.
|
||||||
|
lowerBuf [MaxInputSize]byte
|
||||||
|
rolesBuf [MaxInputSize]RuneRole
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) bestK(i, j int) int {
|
||||||
|
if m.scores[i][j][0].val() < m.scores[i][j][1].val() {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMatcher returns a new fuzzy matcher for scoring candidates against the provided pattern.
|
||||||
|
func NewMatcher(pattern string) *Matcher {
|
||||||
|
if len(pattern) > MaxPatternSize {
|
||||||
|
pattern = pattern[:MaxPatternSize]
|
||||||
|
}
|
||||||
|
|
||||||
|
m := &Matcher{
|
||||||
|
pattern: pattern,
|
||||||
|
patternLower: ToLower(pattern, nil),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, c := range m.patternLower {
|
||||||
|
if pattern[i] != c {
|
||||||
|
m.caseSensitive = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pattern) > 3 {
|
||||||
|
m.patternShort = m.patternLower[:3]
|
||||||
|
} else {
|
||||||
|
m.patternShort = m.patternLower
|
||||||
|
}
|
||||||
|
|
||||||
|
m.patternRoles = RuneRoles(pattern, nil)
|
||||||
|
|
||||||
|
if len(pattern) > 0 {
|
||||||
|
maxCharScore := 4
|
||||||
|
m.scoreScale = 1 / float32(maxCharScore*len(pattern))
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Score returns the score returned by matching the candidate to the pattern.
|
||||||
|
// This is not designed for parallel use. Multiple candidates must be scored sequentially.
|
||||||
|
// Returns a score between 0 and 1 (0 - no match, 1 - perfect match).
|
||||||
|
func (m *Matcher) Score(candidate string) float32 {
|
||||||
|
if len(candidate) > MaxInputSize {
|
||||||
|
candidate = candidate[:MaxInputSize]
|
||||||
|
}
|
||||||
|
lower := ToLower(candidate, m.lowerBuf[:])
|
||||||
|
m.lastCandidateLen = len(candidate)
|
||||||
|
|
||||||
|
if len(m.pattern) == 0 {
|
||||||
|
// Empty patterns perfectly match candidates.
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.match(candidate, lower) {
|
||||||
|
sc := m.computeScore(candidate, lower)
|
||||||
|
if sc > minScore/2 && !m.poorMatch() {
|
||||||
|
m.lastCandidateMatched = true
|
||||||
|
if len(m.pattern) == len(candidate) {
|
||||||
|
// Perfect match.
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if sc < 0 {
|
||||||
|
sc = 0
|
||||||
|
}
|
||||||
|
normalizedScore := float32(sc) * m.scoreScale
|
||||||
|
if normalizedScore > 1 {
|
||||||
|
normalizedScore = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedScore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.lastCandidateMatched = false
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
const minScore = -10000
|
||||||
|
|
||||||
|
// MatchedRanges returns matches ranges for the last scored string as a flattened array of
|
||||||
|
// [begin, end) byte offset pairs.
|
||||||
|
func (m *Matcher) MatchedRanges() []int {
|
||||||
|
if len(m.pattern) == 0 || !m.lastCandidateMatched {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
i, j := m.lastCandidateLen, len(m.pattern)
|
||||||
|
if m.scores[i][j][0].val() < minScore/2 && m.scores[i][j][1].val() < minScore/2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret []int
|
||||||
|
k := m.bestK(i, j)
|
||||||
|
for i > 0 {
|
||||||
|
take := (k == 1)
|
||||||
|
k = m.scores[i][j][k].prevK()
|
||||||
|
if take {
|
||||||
|
if len(ret) == 0 || ret[len(ret)-1] != i {
|
||||||
|
ret = append(ret, i)
|
||||||
|
ret = append(ret, i-1)
|
||||||
|
} else {
|
||||||
|
ret[len(ret)-1] = i - 1
|
||||||
|
}
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
// Reverse slice.
|
||||||
|
for i := 0; i < len(ret)/2; i++ {
|
||||||
|
ret[i], ret[len(ret)-1-i] = ret[len(ret)-1-i], ret[i]
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) match(candidate string, candidateLower []byte) bool {
|
||||||
|
i, j := 0, 0
|
||||||
|
for ; i < len(candidateLower) && j < len(m.patternLower); i++ {
|
||||||
|
if candidateLower[i] == m.patternLower[j] {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if j != len(m.patternLower) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// The input passes the simple test against pattern, so it is time to classify its characters.
|
||||||
|
// Character roles are used below to find the last segment.
|
||||||
|
m.roles = RuneRoles(candidate, m.rolesBuf[:])
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) computeScore(candidate string, candidateLower []byte) int {
|
||||||
|
pattLen, candLen := len(m.pattern), len(candidate)
|
||||||
|
|
||||||
|
for j := 0; j <= len(m.pattern); j++ {
|
||||||
|
m.scores[0][j][0] = minScore << 1
|
||||||
|
m.scores[0][j][1] = minScore << 1
|
||||||
|
}
|
||||||
|
m.scores[0][0][0] = score(0, 0) // Start with 0.
|
||||||
|
|
||||||
|
segmentsLeft, lastSegStart := 1, 0
|
||||||
|
for i := 0; i < candLen; i++ {
|
||||||
|
if m.roles[i] == RSep {
|
||||||
|
segmentsLeft++
|
||||||
|
lastSegStart = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A per-character bonus for a consecutive match.
|
||||||
|
consecutiveBonus := 2
|
||||||
|
wordIdx := 0 // Word count within segment.
|
||||||
|
for i := 1; i <= candLen; i++ {
|
||||||
|
|
||||||
|
role := m.roles[i-1]
|
||||||
|
isHead := role == RHead
|
||||||
|
|
||||||
|
if isHead {
|
||||||
|
wordIdx++
|
||||||
|
} else if role == RSep && segmentsLeft > 1 {
|
||||||
|
wordIdx = 0
|
||||||
|
segmentsLeft--
|
||||||
|
}
|
||||||
|
|
||||||
|
var skipPenalty int
|
||||||
|
if i == 1 || (i-1) == lastSegStart {
|
||||||
|
// Skipping the start of first or last segment.
|
||||||
|
skipPenalty++
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j <= pattLen; j++ {
|
||||||
|
// By default, we don't have a match. Fill in the skip data.
|
||||||
|
m.scores[i][j][1] = minScore << 1
|
||||||
|
|
||||||
|
// Compute the skip score.
|
||||||
|
k := 0
|
||||||
|
if m.scores[i-1][j][0].val() < m.scores[i-1][j][1].val() {
|
||||||
|
k = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
skipScore := m.scores[i-1][j][k].val()
|
||||||
|
// Do not penalize missing characters after the last matched segment.
|
||||||
|
if j != pattLen {
|
||||||
|
skipScore -= skipPenalty
|
||||||
|
}
|
||||||
|
m.scores[i][j][0] = score(skipScore, k)
|
||||||
|
|
||||||
|
if j == 0 || candidateLower[i-1] != m.patternLower[j-1] {
|
||||||
|
// Not a match.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pRole := m.patternRoles[j-1]
|
||||||
|
|
||||||
|
if role == RTail && pRole == RHead {
|
||||||
|
if j > 1 {
|
||||||
|
// Not a match: a head in the pattern matches a tail character in the candidate.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Special treatment for the first character of the pattern. We allow
|
||||||
|
// matches in the middle of a word if they are long enough, at least
|
||||||
|
// min(3, pattern.length) characters.
|
||||||
|
if !bytes.HasPrefix(candidateLower[i-1:], m.patternShort) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the char score.
|
||||||
|
var charScore int
|
||||||
|
// Bonus 1: the char is in the candidate's last segment.
|
||||||
|
if segmentsLeft <= 1 {
|
||||||
|
charScore++
|
||||||
|
}
|
||||||
|
// Bonus 2: Case match or a Head in the pattern aligns with one in the word.
|
||||||
|
// Single-case patterns lack segmentation signals and we assume any character
|
||||||
|
// can be a head of a segment.
|
||||||
|
if candidate[i-1] == m.pattern[j-1] || role == RHead && (!m.caseSensitive || pRole == RHead) {
|
||||||
|
charScore++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Penalty 1: pattern char is Head, candidate char is Tail.
|
||||||
|
if role == RTail && pRole == RHead {
|
||||||
|
charScore--
|
||||||
|
}
|
||||||
|
// Penalty 2: first pattern character matched in the middle of a word.
|
||||||
|
if j == 1 && role == RTail {
|
||||||
|
charScore -= 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// Third dimension encodes whether there is a gap between the previous match and the current
|
||||||
|
// one.
|
||||||
|
for k := 0; k < 2; k++ {
|
||||||
|
sc := m.scores[i-1][j-1][k].val() + charScore
|
||||||
|
|
||||||
|
isConsecutive := k == 1 || i-1 == 0 || i-1 == lastSegStart
|
||||||
|
if isConsecutive {
|
||||||
|
// Bonus 3: a consecutive match. First character match also gets a bonus to
|
||||||
|
// ensure prefix final match score normalizes to 1.0.
|
||||||
|
// Logically, this is a part of charScore, but we have to compute it here because it
|
||||||
|
// only applies for consecutive matches (k == 1).
|
||||||
|
sc += consecutiveBonus
|
||||||
|
}
|
||||||
|
if k == 0 {
|
||||||
|
// Penalty 3: Matching inside a segment (and previous char wasn't matched). Penalize for the lack
|
||||||
|
// of alignment.
|
||||||
|
if role == RTail || role == RUCTail {
|
||||||
|
sc -= 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sc > m.scores[i][j][1].val() {
|
||||||
|
m.scores[i][j][1] = score(sc, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := m.scores[len(candidate)][len(m.pattern)][m.bestK(len(candidate), len(m.pattern))].val()
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScoreTable returns the score table computed for the provided candidate. Used only for debugging.
|
||||||
|
func (m *Matcher) ScoreTable(candidate string) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
var line1, line2, separator bytes.Buffer
|
||||||
|
line1.WriteString("\t")
|
||||||
|
line2.WriteString("\t")
|
||||||
|
for j := 0; j < len(m.pattern); j++ {
|
||||||
|
line1.WriteString(fmt.Sprintf("%c\t\t", m.pattern[j]))
|
||||||
|
separator.WriteString("----------------")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.WriteString(line1.String())
|
||||||
|
buf.WriteString("\n")
|
||||||
|
buf.WriteString(separator.String())
|
||||||
|
buf.WriteString("\n")
|
||||||
|
|
||||||
|
for i := 1; i <= len(candidate); i++ {
|
||||||
|
line1.Reset()
|
||||||
|
line2.Reset()
|
||||||
|
|
||||||
|
line1.WriteString(fmt.Sprintf("%c\t", candidate[i-1]))
|
||||||
|
line2.WriteString("\t")
|
||||||
|
|
||||||
|
for j := 1; j <= len(m.pattern); j++ {
|
||||||
|
line1.WriteString(fmt.Sprintf("M%6d(%c)\t", m.scores[i][j][0].val(), dir(m.scores[i][j][0].prevK())))
|
||||||
|
line2.WriteString(fmt.Sprintf("H%6d(%c)\t", m.scores[i][j][1].val(), dir(m.scores[i][j][1].prevK())))
|
||||||
|
}
|
||||||
|
buf.WriteString(line1.String())
|
||||||
|
buf.WriteString("\n")
|
||||||
|
buf.WriteString(line2.String())
|
||||||
|
buf.WriteString("\n")
|
||||||
|
buf.WriteString(separator.String())
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func dir(prevK int) rune {
|
||||||
|
if prevK == 0 {
|
||||||
|
return 'M'
|
||||||
|
}
|
||||||
|
return 'H'
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) poorMatch() bool {
|
||||||
|
if len(m.pattern) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
i, j := m.lastCandidateLen, len(m.pattern)
|
||||||
|
k := m.bestK(i, j)
|
||||||
|
|
||||||
|
var counter, len int
|
||||||
|
for i > 0 {
|
||||||
|
take := (k == 1)
|
||||||
|
k = m.scores[i][j][k].prevK()
|
||||||
|
if take {
|
||||||
|
len++
|
||||||
|
if k == 0 && len < 3 && m.roles[i-1] == RTail {
|
||||||
|
// Short match in the middle of a word
|
||||||
|
counter++
|
||||||
|
if counter > 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j--
|
||||||
|
} else {
|
||||||
|
len = 0
|
||||||
|
}
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -1,27 +1,14 @@
|
||||||
// Package packagesinternal exposes internal-only fields from go/packages.
|
// Package packagesinternal exposes internal-only fields from go/packages.
|
||||||
package packagesinternal
|
package packagesinternal
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"golang.org/x/tools/internal/gocommand"
|
||||||
// Fields must match go list;
|
)
|
||||||
type Module struct {
|
|
||||||
Path string // module path
|
|
||||||
Version string // module version
|
|
||||||
Versions []string // available module versions (with -versions)
|
|
||||||
Replace *Module // replaced by this module
|
|
||||||
Time *time.Time // time version was created
|
|
||||||
Update *Module // available update, if any (with -u)
|
|
||||||
Main bool // is this the main module?
|
|
||||||
Indirect bool // is this module only an indirect dependency of main module?
|
|
||||||
Dir string // directory holding files for this module, if any
|
|
||||||
GoMod string // path to go.mod file used when loading this module, if any
|
|
||||||
GoVersion string // go version used in module
|
|
||||||
Error *ModuleError // error loading module
|
|
||||||
}
|
|
||||||
type ModuleError struct {
|
|
||||||
Err string // the error itself
|
|
||||||
}
|
|
||||||
|
|
||||||
var GetForTest = func(p interface{}) string { return "" }
|
var GetForTest = func(p interface{}) string { return "" }
|
||||||
|
|
||||||
var GetModule = func(p interface{}) *Module { return nil }
|
var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil }
|
||||||
|
|
||||||
|
var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {}
|
||||||
|
|
||||||
|
var TypecheckCgo int
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package typesinternal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/types"
|
||||||
|
"reflect"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetUsesCgo(conf *types.Config) bool {
|
||||||
|
v := reflect.ValueOf(conf).Elem()
|
||||||
|
|
||||||
|
f := v.FieldByName("go115UsesCgo")
|
||||||
|
if !f.IsValid() {
|
||||||
|
f = v.FieldByName("UsesCgo")
|
||||||
|
if !f.IsValid() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := unsafe.Pointer(f.UnsafeAddr())
|
||||||
|
*(*bool)(addr) = true
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
# cloud.google.com/go v0.45.0
|
# cloud.google.com/go v0.45.0
|
||||||
## explicit
|
## explicit
|
||||||
cloud.google.com/go/compute/metadata
|
cloud.google.com/go/compute/metadata
|
||||||
# gitea.com/jolheiser/gitea-vet v0.1.0
|
# code.gitea.io/gitea-vet v0.2.1
|
||||||
## explicit
|
## explicit
|
||||||
gitea.com/jolheiser/gitea-vet
|
code.gitea.io/gitea-vet
|
||||||
gitea.com/jolheiser/gitea-vet/checks
|
code.gitea.io/gitea-vet/checks
|
||||||
# gitea.com/lunny/levelqueue v0.3.0
|
# gitea.com/lunny/levelqueue v0.3.0
|
||||||
## explicit
|
## explicit
|
||||||
gitea.com/lunny/levelqueue
|
gitea.com/lunny/levelqueue
|
||||||
|
@ -769,10 +769,10 @@ golang.org/x/crypto/ssh
|
||||||
golang.org/x/crypto/ssh/agent
|
golang.org/x/crypto/ssh/agent
|
||||||
golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
|
golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
|
||||||
golang.org/x/crypto/ssh/knownhosts
|
golang.org/x/crypto/ssh/knownhosts
|
||||||
# golang.org/x/mod v0.2.0
|
# golang.org/x/mod v0.3.0
|
||||||
golang.org/x/mod/module
|
golang.org/x/mod/module
|
||||||
golang.org/x/mod/semver
|
golang.org/x/mod/semver
|
||||||
# golang.org/x/net v0.0.0-20200602114024-627f9648deb9
|
# golang.org/x/net v0.0.0-20200625001655-4c5254603344
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/net/context
|
golang.org/x/net/context
|
||||||
golang.org/x/net/context/ctxhttp
|
golang.org/x/net/context/ctxhttp
|
||||||
|
@ -823,7 +823,7 @@ golang.org/x/text/width
|
||||||
# golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
|
# golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/time/rate
|
golang.org/x/time/rate
|
||||||
# golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224
|
# golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d
|
||||||
## explicit
|
## explicit
|
||||||
golang.org/x/tools/cover
|
golang.org/x/tools/cover
|
||||||
golang.org/x/tools/go/analysis
|
golang.org/x/tools/go/analysis
|
||||||
|
@ -840,11 +840,18 @@ golang.org/x/tools/go/loader
|
||||||
golang.org/x/tools/go/packages
|
golang.org/x/tools/go/packages
|
||||||
golang.org/x/tools/go/types/objectpath
|
golang.org/x/tools/go/types/objectpath
|
||||||
golang.org/x/tools/imports
|
golang.org/x/tools/imports
|
||||||
|
golang.org/x/tools/internal/analysisinternal
|
||||||
|
golang.org/x/tools/internal/event
|
||||||
|
golang.org/x/tools/internal/event/core
|
||||||
|
golang.org/x/tools/internal/event/keys
|
||||||
|
golang.org/x/tools/internal/event/label
|
||||||
golang.org/x/tools/internal/fastwalk
|
golang.org/x/tools/internal/fastwalk
|
||||||
golang.org/x/tools/internal/gocommand
|
golang.org/x/tools/internal/gocommand
|
||||||
golang.org/x/tools/internal/gopathwalk
|
golang.org/x/tools/internal/gopathwalk
|
||||||
golang.org/x/tools/internal/imports
|
golang.org/x/tools/internal/imports
|
||||||
|
golang.org/x/tools/internal/lsp/fuzzy
|
||||||
golang.org/x/tools/internal/packagesinternal
|
golang.org/x/tools/internal/packagesinternal
|
||||||
|
golang.org/x/tools/internal/typesinternal
|
||||||
# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||||
golang.org/x/xerrors
|
golang.org/x/xerrors
|
||||||
golang.org/x/xerrors/internal
|
golang.org/x/xerrors/internal
|
||||||
|
|
Loading…
Reference in New Issue