Compare commits

...

7 Commits

Author SHA1 Message Date
dependabot[bot] 91983ccac9
Merge 75a46a3c5e into f8ce22d9b9 2024-04-25 10:46:07 +00:00
Frank Denis f8ce22d9b9 Update deps 2024-04-25 12:45:52 +02:00
Frank Denis 249dba391d Support gzip compression to fetch source files 2024-04-25 12:43:29 +02:00
Frank Denis 987ae216e3 Add fritz.box to the set of undelegated zones 2024-04-21 20:14:15 +02:00
Frank Denis 7fba32651b Make it more visible that DNS64 has been enabled 2024-04-19 18:27:39 +02:00
Frank Denis 6ae388e646 DNS64 plugin: don't return SYNTH data, alter the response directly
Fixes #2619

However, cached responses now appear with the "PASS" status rather
than "CLOAK".
2024-04-19 18:19:16 +02:00
dependabot[bot] 75a46a3c5e
Bump google.golang.org/protobuf from 1.30.0 to 1.33.0
Bumps google.golang.org/protobuf from 1.30.0 to 1.33.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-13 22:26:47 +00:00
107 changed files with 5347 additions and 1641 deletions

View File

@ -119,6 +119,7 @@ var undelegatedSet = []string{
"envoy", "envoy",
"example", "example",
"f.f.ip6.arpa", "f.f.ip6.arpa",
"fritz.box",
"grp", "grp",
"gw==", "gw==",
"home", "home",

View File

@ -49,7 +49,7 @@ func (plugin *PluginDNS64) Init(proxy *Proxy) error {
if err != nil { if err != nil {
return err return err
} }
dlog.Infof("Registered DNS64 prefix [%s]", pref.String()) dlog.Noticef("Registered DNS64 prefix [%s]", pref.String())
plugin.pref64 = append(plugin.pref64, pref) plugin.pref64 = append(plugin.pref64, pref)
} }
} else if len(proxy.dns64Resolvers) != 0 { } else if len(proxy.dns64Resolvers) != 0 {
@ -57,7 +57,10 @@ func (plugin *PluginDNS64) Init(proxy *Proxy) error {
if err := plugin.refreshPref64(); err != nil { if err := plugin.refreshPref64(); err != nil {
return err return err
} }
} else {
return nil
} }
dlog.Notice("DNS64 map enabled")
return nil return nil
} }
@ -152,11 +155,10 @@ func (plugin *PluginDNS64) Eval(pluginsState *PluginsState, msg *dns.Msg) error
} }
} }
synth := EmptyResponseFromMessage(msg) msg.Answer = synth64
synth.Answer = append(synth.Answer, synth64...) msg.AuthenticatedData = false
msg.SetEdns0(uint16(MaxDNSUDPSafePacketSize), false)
pluginsState.synthResponse = synth
pluginsState.action = PluginsActionSynth
pluginsState.returnCode = PluginsReturnCodeCloak pluginsState.returnCode = PluginsReturnCodeCloak
return nil return nil

View File

@ -131,7 +131,7 @@ func (source *Source) parseURLs(urls []string) {
} }
func fetchFromURL(xTransport *XTransport, u *url.URL) ([]byte, error) { func fetchFromURL(xTransport *XTransport, u *url.URL) ([]byte, error) {
bin, _, _, _, err := xTransport.Get(u, "", DefaultTimeout) bin, _, _, _, err := xTransport.GetWithCompression(u, "", DefaultTimeout)
return bin, err return bin, err
} }

View File

@ -2,6 +2,7 @@ package main
import ( import (
"bytes" "bytes"
"compress/gzip"
"context" "context"
"crypto/sha512" "crypto/sha512"
"crypto/tls" "crypto/tls"
@ -482,6 +483,7 @@ func (xTransport *XTransport) Fetch(
contentType string, contentType string,
body *[]byte, body *[]byte,
timeout time.Duration, timeout time.Duration,
compress bool,
) ([]byte, int, *tls.ConnectionState, time.Duration, error) { ) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
if timeout <= 0 { if timeout <= 0 {
timeout = xTransport.timeout timeout = xTransport.timeout
@ -530,6 +532,9 @@ func (xTransport *XTransport) Fetch(
) )
return nil, 0, nil, 0, err return nil, 0, nil, 0, err
} }
if compress && body == nil {
header["Accept-Encoding"] = []string{"gzip"}
}
req := &http.Request{ req := &http.Request{
Method: method, Method: method,
URL: url, URL: url,
@ -596,7 +601,17 @@ func (xTransport *XTransport) Fetch(
} }
} }
tls := resp.TLS tls := resp.TLS
bin, err := io.ReadAll(io.LimitReader(resp.Body, MaxHTTPBodyLength))
var bodyReader io.ReadCloser = resp.Body
if compress && resp.Header.Get("Content-Encoding") == "gzip" {
bodyReader, err = gzip.NewReader(io.LimitReader(resp.Body, MaxHTTPBodyLength))
if err != nil {
return nil, statusCode, tls, rtt, err
}
defer bodyReader.Close()
}
bin, err := io.ReadAll(io.LimitReader(bodyReader, MaxHTTPBodyLength))
if err != nil { if err != nil {
return nil, statusCode, tls, rtt, err return nil, statusCode, tls, rtt, err
} }
@ -604,12 +619,20 @@ func (xTransport *XTransport) Fetch(
return bin, statusCode, tls, rtt, err return bin, statusCode, tls, rtt, err
} }
func (xTransport *XTransport) GetWithCompression(
url *url.URL,
accept string,
timeout time.Duration,
) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
return xTransport.Fetch("GET", url, accept, "", nil, timeout, true)
}
func (xTransport *XTransport) Get( func (xTransport *XTransport) Get(
url *url.URL, url *url.URL,
accept string, accept string,
timeout time.Duration, timeout time.Duration,
) ([]byte, int, *tls.ConnectionState, time.Duration, error) { ) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
return xTransport.Fetch("GET", url, accept, "", nil, timeout) return xTransport.Fetch("GET", url, accept, "", nil, timeout, false)
} }
func (xTransport *XTransport) Post( func (xTransport *XTransport) Post(
@ -619,7 +642,7 @@ func (xTransport *XTransport) Post(
body *[]byte, body *[]byte,
timeout time.Duration, timeout time.Duration,
) ([]byte, int, *tls.ConnectionState, time.Duration, error) { ) ([]byte, int, *tls.ConnectionState, time.Duration, error) {
return xTransport.Fetch("POST", url, accept, contentType, body, timeout) return xTransport.Fetch("POST", url, accept, contentType, body, timeout, false)
} }
func (xTransport *XTransport) dohLikeQuery( func (xTransport *XTransport) dohLikeQuery(

10
go.mod
View File

@ -11,13 +11,13 @@ require (
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb
github.com/jedisct1/dlog v0.0.0-20230811132706-443b333ff1b3 github.com/jedisct1/dlog v0.0.0-20230811132706-443b333ff1b3
github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e
github.com/jedisct1/go-dnsstamps v0.0.0-20230211133001-124a632de565 github.com/jedisct1/go-dnsstamps v0.0.0-20240423203910-07a0735c7774
github.com/jedisct1/go-hpke-compact v0.0.0-20230811132953-4ee502b61f80 github.com/jedisct1/go-hpke-compact v0.0.0-20230811132953-4ee502b61f80
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267
github.com/jedisct1/xsecretbox v0.0.0-20230811132812-b950633f9f1f github.com/jedisct1/xsecretbox v0.0.0-20230811132812-b950633f9f1f
github.com/k-sone/critbitgo v1.4.0 github.com/k-sone/critbitgo v1.4.0
github.com/kardianos/service v1.2.2 github.com/kardianos/service v1.2.2
github.com/miekg/dns v1.1.58 github.com/miekg/dns v1.1.59
github.com/opencoff/go-sieve v0.2.1 github.com/opencoff/go-sieve v0.2.1
github.com/powerman/check v1.7.0 github.com/powerman/check v1.7.0
github.com/quic-go/quic-go v0.42.0 github.com/quic-go/quic-go v0.42.0
@ -42,10 +42,10 @@ require (
github.com/smartystreets/goconvey v1.7.2 // indirect github.com/smartystreets/goconvey v1.7.2 // indirect
go.uber.org/mock v0.4.0 // indirect go.uber.org/mock v0.4.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/mod v0.14.0 // indirect golang.org/x/mod v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.17.0 // indirect golang.org/x/tools v0.19.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/grpc v1.53.0 // indirect google.golang.org/grpc v1.53.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.33.0 // indirect
) )

20
go.sum
View File

@ -41,8 +41,8 @@ github.com/jedisct1/dlog v0.0.0-20230811132706-443b333ff1b3 h1:3wOfILZqpvhb7bNBG
github.com/jedisct1/dlog v0.0.0-20230811132706-443b333ff1b3/go.mod h1:m25Jh2eJ0FXLWjMXFZItQ24W0HAIjosAe4Z0a+SZitU= github.com/jedisct1/dlog v0.0.0-20230811132706-443b333ff1b3/go.mod h1:m25Jh2eJ0FXLWjMXFZItQ24W0HAIjosAe4Z0a+SZitU=
github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e h1:tzG4EjKgHIqKVkLIAC4pXTIapuM2BR05uXokEEysAXA= github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e h1:tzG4EjKgHIqKVkLIAC4pXTIapuM2BR05uXokEEysAXA=
github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw= github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e/go.mod h1:SAINchklztk2jcLWJ4bpNF4KnwDUSUTX+cJbspWC2Rw=
github.com/jedisct1/go-dnsstamps v0.0.0-20230211133001-124a632de565 h1:BPBMaUCgtmiHvqgugbSuegXjADJfERsPbmRqgdq8Pjo= github.com/jedisct1/go-dnsstamps v0.0.0-20240423203910-07a0735c7774 h1:DobL5d8UxrYzlD0PbU/EVBAGHuDiFyH46gr6povMw50=
github.com/jedisct1/go-dnsstamps v0.0.0-20230211133001-124a632de565/go.mod h1:mEGEFZsGe4sG5Mb3Xi89pmsy+TZ0946ArbYMGKAM5uA= github.com/jedisct1/go-dnsstamps v0.0.0-20240423203910-07a0735c7774/go.mod h1:mEGEFZsGe4sG5Mb3Xi89pmsy+TZ0946ArbYMGKAM5uA=
github.com/jedisct1/go-hpke-compact v0.0.0-20230811132953-4ee502b61f80 h1:o6d/E+fxKQrSEOyR7H+Lb3iFMW3daWdq1nsTnQPWaF0= github.com/jedisct1/go-hpke-compact v0.0.0-20230811132953-4ee502b61f80 h1:o6d/E+fxKQrSEOyR7H+Lb3iFMW3daWdq1nsTnQPWaF0=
github.com/jedisct1/go-hpke-compact v0.0.0-20230811132953-4ee502b61f80/go.mod h1:dwjlOg9tYzZlxyi9HAGJYKUlVe3RZIfXvcni4VUfqRA= github.com/jedisct1/go-hpke-compact v0.0.0-20230811132953-4ee502b61f80/go.mod h1:dwjlOg9tYzZlxyi9HAGJYKUlVe3RZIfXvcni4VUfqRA=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY= github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY=
@ -55,8 +55,8 @@ github.com/k-sone/critbitgo v1.4.0 h1:l71cTyBGeh6X5ATh6Fibgw3+rtNT80BA0uNNWgkPrb
github.com/k-sone/critbitgo v1.4.0/go.mod h1:7E6pyoyADnFxlUBEKcnfS49b7SUAQGMK+OAp/UQvo0s= github.com/k-sone/critbitgo v1.4.0/go.mod h1:7E6pyoyADnFxlUBEKcnfS49b7SUAQGMK+OAp/UQvo0s=
github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60= github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60=
github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
@ -89,8 +89,8 @@ golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
@ -108,8 +108,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
@ -117,8 +117,8 @@ google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=

View File

@ -11,7 +11,11 @@ import (
"strings" "strings"
) )
const DefaultPort = 443 const (
DefaultPort = 443
DefaultDNSPort = 53
StampScheme = "sdns://"
)
type ServerInformalProperties uint64 type ServerInformalProperties uint64
@ -97,7 +101,10 @@ func NewServerStampFromString(stampStr string) (ServerStamp, error) {
if len(bin) < 1 { if len(bin) < 1 {
return ServerStamp{}, errors.New("Stamp is too short") return ServerStamp{}, errors.New("Stamp is too short")
} }
if bin[0] == uint8(StampProtoTypeDNSCrypt) {
if bin[0] == uint8(StampProtoTypePlain) {
return newPlainDNSServerStamp(bin)
} else if bin[0] == uint8(StampProtoTypeDNSCrypt) {
return newDNSCryptServerStamp(bin) return newDNSCryptServerStamp(bin)
} else if bin[0] == uint8(StampProtoTypeDoH) { } else if bin[0] == uint8(StampProtoTypeDoH) {
return newDoHServerStamp(bin) return newDoHServerStamp(bin)
@ -112,7 +119,7 @@ func NewServerStampFromString(stampStr string) (ServerStamp, error) {
} }
func NewRelayAndServerStampFromString(stampStr string) (ServerStamp, ServerStamp, error) { func NewRelayAndServerStampFromString(stampStr string) (ServerStamp, ServerStamp, error) {
if !strings.HasPrefix(stampStr, "sdns://") { if !strings.HasPrefix(stampStr, StampScheme) {
return ServerStamp{}, ServerStamp{}, errors.New("Stamps are expected to start with \"sdns://\"") return ServerStamp{}, ServerStamp{}, errors.New("Stamps are expected to start with \"sdns://\"")
} }
stampStr = stampStr[7:] stampStr = stampStr[7:]
@ -120,11 +127,11 @@ func NewRelayAndServerStampFromString(stampStr string) (ServerStamp, ServerStamp
if len(parts) != 2 { if len(parts) != 2 {
return ServerStamp{}, ServerStamp{}, errors.New("This is not a relay+server stamp") return ServerStamp{}, ServerStamp{}, errors.New("This is not a relay+server stamp")
} }
relayStamp, err := NewServerStampFromString("sdns://" + parts[0]) relayStamp, err := NewServerStampFromString(StampScheme + parts[0])
if err != nil { if err != nil {
return ServerStamp{}, ServerStamp{}, err return ServerStamp{}, ServerStamp{}, err
} }
serverStamp, err := NewServerStampFromString("sdns://" + parts[1]) serverStamp, err := NewServerStampFromString(StampScheme + parts[1])
if err != nil { if err != nil {
return ServerStamp{}, ServerStamp{}, err return ServerStamp{}, ServerStamp{}, err
} }
@ -137,6 +144,49 @@ func NewRelayAndServerStampFromString(stampStr string) (ServerStamp, ServerStamp
return relayStamp, serverStamp, nil return relayStamp, serverStamp, nil
} }
// id(u8)=0x00 props 0x00 addrLen(1) serverAddr
func newPlainDNSServerStamp(bin []byte) (ServerStamp, error) {
stamp := ServerStamp{Proto: StampProtoTypePlain}
if len(bin) < 1+8+1+1 {
return stamp, errors.New("Stamp is too short")
}
stamp.Props = ServerInformalProperties(binary.LittleEndian.Uint64(bin[1:9]))
binLen := len(bin)
pos := 9
length := int(bin[pos])
if 1+length > binLen-pos {
return stamp, errors.New("Invalid stamp")
}
pos++
stamp.ServerAddrStr = string(bin[pos : pos+length])
pos += length
colIndex := strings.LastIndex(stamp.ServerAddrStr, ":")
bracketIndex := strings.LastIndex(stamp.ServerAddrStr, "]")
if colIndex < bracketIndex {
colIndex = -1
}
if colIndex < 0 {
colIndex = len(stamp.ServerAddrStr) // DefaultDNSPort
stamp.ServerAddrStr = fmt.Sprintf("%s:%d", stamp.ServerAddrStr, DefaultDNSPort)
}
if colIndex >= len(stamp.ServerAddrStr)-1 {
return stamp, errors.New("Invalid stamp (empty port)")
}
ipOnly := stamp.ServerAddrStr[:colIndex]
if err := validatePort(stamp.ServerAddrStr[colIndex+1:]); err != nil {
return stamp, errors.New("Invalid stamp (port range)")
}
if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil {
return stamp, errors.New("Invalid stamp (IP address)")
}
if pos != binLen {
return stamp, errors.New("Invalid stamp (garbage after end)")
}
return stamp, nil
}
// id(u8)=0x01 props addrLen(1) serverAddr pkStrlen(1) pkStr providerNameLen(1) providerName // id(u8)=0x01 props addrLen(1) serverAddr pkStrlen(1) pkStr providerNameLen(1) providerName
func newDNSCryptServerStamp(bin []byte) (ServerStamp, error) { func newDNSCryptServerStamp(bin []byte) (ServerStamp, error) {
@ -169,8 +219,7 @@ func newDNSCryptServerStamp(bin []byte) (ServerStamp, error) {
return stamp, errors.New("Invalid stamp (empty port)") return stamp, errors.New("Invalid stamp (empty port)")
} }
ipOnly := stamp.ServerAddrStr[:colIndex] ipOnly := stamp.ServerAddrStr[:colIndex]
portOnly := stamp.ServerAddrStr[colIndex+1:] if err := validatePort(stamp.ServerAddrStr[colIndex+1:]); err != nil {
if _, err := strconv.ParseUint(portOnly, 10, 16); err != nil {
return stamp, errors.New("Invalid stamp (port range)") return stamp, errors.New("Invalid stamp (port range)")
} }
if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil { if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil {
@ -268,8 +317,7 @@ func newDoHServerStamp(bin []byte) (ServerStamp, error) {
return stamp, errors.New("Invalid stamp (empty port)") return stamp, errors.New("Invalid stamp (empty port)")
} }
ipOnly := stamp.ServerAddrStr[:colIndex] ipOnly := stamp.ServerAddrStr[:colIndex]
portOnly := stamp.ServerAddrStr[colIndex+1:] if err := validatePort(stamp.ServerAddrStr[colIndex+1:]); err != nil {
if _, err := strconv.ParseUint(portOnly, 10, 16); err != nil {
return stamp, errors.New("Invalid stamp (port range)") return stamp, errors.New("Invalid stamp (port range)")
} }
if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil { if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil {
@ -344,8 +392,7 @@ func newDNSCryptRelayStamp(bin []byte) (ServerStamp, error) {
return stamp, errors.New("Invalid stamp (empty port)") return stamp, errors.New("Invalid stamp (empty port)")
} }
ipOnly := stamp.ServerAddrStr[:colIndex] ipOnly := stamp.ServerAddrStr[:colIndex]
portOnly := stamp.ServerAddrStr[colIndex+1:] if err := validatePort(stamp.ServerAddrStr[colIndex+1:]); err != nil {
if _, err := strconv.ParseUint(portOnly, 10, 16); err != nil {
return stamp, errors.New("Invalid stamp (port range)") return stamp, errors.New("Invalid stamp (port range)")
} }
if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil { if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil {
@ -426,8 +473,7 @@ func newODoHRelayStamp(bin []byte) (ServerStamp, error) {
return stamp, errors.New("Invalid stamp (empty port)") return stamp, errors.New("Invalid stamp (empty port)")
} }
ipOnly := stamp.ServerAddrStr[:colIndex] ipOnly := stamp.ServerAddrStr[:colIndex]
portOnly := stamp.ServerAddrStr[colIndex+1:] if err := validatePort(stamp.ServerAddrStr[colIndex+1:]); err != nil {
if _, err := strconv.ParseUint(portOnly, 10, 16); err != nil {
return stamp, errors.New("Invalid stamp (port range)") return stamp, errors.New("Invalid stamp (port range)")
} }
if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil { if net.ParseIP(strings.TrimRight(strings.TrimLeft(ipOnly, "["), "]")) == nil {
@ -438,8 +484,17 @@ func newODoHRelayStamp(bin []byte) (ServerStamp, error) {
return stamp, nil return stamp, nil
} }
func validatePort(port string) error {
if _, err := strconv.ParseUint(port, 10, 16); err != nil {
return errors.New("Invalid port")
}
return nil
}
func (stamp *ServerStamp) String() string { func (stamp *ServerStamp) String() string {
if stamp.Proto == StampProtoTypeDNSCrypt { if stamp.Proto == StampProtoTypePlain {
return stamp.plainStrng()
} else if stamp.Proto == StampProtoTypeDNSCrypt {
return stamp.dnsCryptString() return stamp.dnsCryptString()
} else if stamp.Proto == StampProtoTypeDoH { } else if stamp.Proto == StampProtoTypeDoH {
return stamp.dohString() return stamp.dohString()
@ -453,6 +508,22 @@ func (stamp *ServerStamp) String() string {
panic("Unsupported protocol") panic("Unsupported protocol")
} }
func (stamp *ServerStamp) plainStrng() string {
bin := make([]uint8, 9)
bin[0] = uint8(StampProtoTypePlain)
binary.LittleEndian.PutUint64(bin[1:9], uint64(stamp.Props))
serverAddrStr := stamp.ServerAddrStr
if strings.HasSuffix(serverAddrStr, ":"+strconv.Itoa(DefaultDNSPort)) {
serverAddrStr = serverAddrStr[:len(serverAddrStr)-1-len(strconv.Itoa(DefaultDNSPort))]
}
bin = append(bin, uint8(len(serverAddrStr)))
bin = append(bin, []uint8(serverAddrStr)...)
str := base64.RawURLEncoding.EncodeToString(bin)
return StampScheme + str
}
func (stamp *ServerStamp) dnsCryptString() string { func (stamp *ServerStamp) dnsCryptString() string {
bin := make([]uint8, 9) bin := make([]uint8, 9)
bin[0] = uint8(StampProtoTypeDNSCrypt) bin[0] = uint8(StampProtoTypeDNSCrypt)
@ -473,7 +544,7 @@ func (stamp *ServerStamp) dnsCryptString() string {
str := base64.RawURLEncoding.EncodeToString(bin) str := base64.RawURLEncoding.EncodeToString(bin)
return "sdns://" + str return StampScheme + str
} }
func (stamp *ServerStamp) dohString() string { func (stamp *ServerStamp) dohString() string {
@ -510,7 +581,7 @@ func (stamp *ServerStamp) dohString() string {
str := base64.RawURLEncoding.EncodeToString(bin) str := base64.RawURLEncoding.EncodeToString(bin)
return "sdns://" + str return StampScheme + str
} }
func (stamp *ServerStamp) oDohTargetString() string { func (stamp *ServerStamp) oDohTargetString() string {
@ -526,7 +597,7 @@ func (stamp *ServerStamp) oDohTargetString() string {
str := base64.RawURLEncoding.EncodeToString(bin) str := base64.RawURLEncoding.EncodeToString(bin)
return "sdns://" + str return StampScheme + str
} }
func (stamp *ServerStamp) dnsCryptRelayString() string { func (stamp *ServerStamp) dnsCryptRelayString() string {
@ -542,7 +613,7 @@ func (stamp *ServerStamp) dnsCryptRelayString() string {
str := base64.RawURLEncoding.EncodeToString(bin) str := base64.RawURLEncoding.EncodeToString(bin)
return "sdns://" + str return StampScheme + str
} }
func (stamp *ServerStamp) oDohRelayString() string { func (stamp *ServerStamp) oDohRelayString() string {
@ -579,5 +650,5 @@ func (stamp *ServerStamp) oDohRelayString() string {
str := base64.RawURLEncoding.EncodeToString(bin) str := base64.RawURLEncoding.EncodeToString(bin)
return "sdns://" + str return StampScheme + str
} }

View File

@ -83,6 +83,8 @@ A not-so-up-to-date-list-that-may-be-actually-current:
* https://github.com/egbakou/domainverifier * https://github.com/egbakou/domainverifier
* https://github.com/semihalev/sdns * https://github.com/semihalev/sdns
* https://github.com/wintbiit/NineDNS * https://github.com/wintbiit/NineDNS
* https://linuxcontainers.org/incus/
* https://ifconfig.es
Send pull request if you want to be listed here. Send pull request if you want to be listed here.

View File

@ -198,10 +198,12 @@ func IsDomainName(s string) (labels int, ok bool) {
off int off int
begin int begin int
wasDot bool wasDot bool
escape bool
) )
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {
switch s[i] { switch s[i] {
case '\\': case '\\':
escape = !escape
if off+1 > lenmsg { if off+1 > lenmsg {
return labels, false return labels, false
} }
@ -217,6 +219,7 @@ func IsDomainName(s string) (labels int, ok bool) {
wasDot = false wasDot = false
case '.': case '.':
escape = false
if i == 0 && len(s) > 1 { if i == 0 && len(s) > 1 {
// leading dots are not legal except for the root zone // leading dots are not legal except for the root zone
return labels, false return labels, false
@ -243,10 +246,13 @@ func IsDomainName(s string) (labels int, ok bool) {
labels++ labels++
begin = i + 1 begin = i + 1
default: default:
escape = false
wasDot = false wasDot = false
} }
} }
if escape {
return labels, false
}
return labels, true return labels, true
} }

2
vendor/github.com/miekg/dns/msg.go generated vendored
View File

@ -714,7 +714,7 @@ func (h *MsgHdr) String() string {
return s return s
} }
// Pack packs a Msg: it is converted to to wire format. // Pack packs a Msg: it is converted to wire format.
// If the dns.Compress is true the message will be in compressed wire format. // If the dns.Compress is true the message will be in compressed wire format.
func (dns *Msg) Pack() (msg []byte, err error) { func (dns *Msg) Pack() (msg []byte, err error) {
return dns.PackBuffer(nil) return dns.PackBuffer(nil)

11
vendor/github.com/miekg/dns/scan.go generated vendored
View File

@ -101,12 +101,13 @@ type ttlState struct {
isByDirective bool // isByDirective indicates whether ttl was set by a $TTL directive isByDirective bool // isByDirective indicates whether ttl was set by a $TTL directive
} }
// NewRR reads the RR contained in the string s. Only the first RR is returned. // NewRR reads a string s and returns the first RR.
// If s contains no records, NewRR will return nil with no error. // If s contains no records, NewRR will return nil with no error.
// //
// The class defaults to IN and TTL defaults to 3600. The full zone file syntax // The class defaults to IN, TTL defaults to 3600, and
// like $TTL, $ORIGIN, etc. is supported. All fields of the returned RR are // origin for resolving relative domain names defaults to the DNS root (.).
// set, except RR.Header().Rdlength which is set to 0. // Full zone file syntax is supported, including directives like $TTL and $ORIGIN.
// All fields of the returned RR are set from the read data, except RR.Header().Rdlength which is set to 0.
func NewRR(s string) (RR, error) { func NewRR(s string) (RR, error) {
if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline if len(s) > 0 && s[len(s)-1] != '\n' { // We need a closing newline
return ReadRR(strings.NewReader(s+"\n"), "") return ReadRR(strings.NewReader(s+"\n"), "")
@ -1282,7 +1283,7 @@ func stringToCm(token string) (e, m uint8, ok bool) {
cmeters *= 10 cmeters *= 10
} }
} }
// This slighly ugly condition will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm). // This slightly ugly condition will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm).
if !hasCM || mStr != "" { if !hasCM || mStr != "" {
meters, err = strconv.Atoi(mStr) meters, err = strconv.Atoi(mStr)
// RFC1876 states the max value is 90000000.00. The latter two conditions enforce it. // RFC1876 states the max value is 90000000.00. The latter two conditions enforce it.

View File

@ -51,25 +51,21 @@ func endingToTxtSlice(c *zlexer, errstr string) ([]string, *ParseError) {
switch l.value { switch l.value {
case zString: case zString:
empty = false empty = false
if len(l.token) > 255 { // split up tokens that are larger than 255 into 255-chunks
// split up tokens that are larger than 255 into 255-chunks sx := []string{}
sx := []string{} p := 0
p, i := 0, 255 for {
for { i := escapedStringOffset(l.token[p:], 255)
if i <= len(l.token) { if i != -1 && p+i != len(l.token) {
sx = append(sx, l.token[p:i]) sx = append(sx, l.token[p:p+i])
} else { } else {
sx = append(sx, l.token[p:]) sx = append(sx, l.token[p:])
break break
}
p, i = p+255, i+255
} }
s = append(s, sx...) p += i
break
} }
s = append(s, sx...)
s = append(s, l.token)
case zBlank: case zBlank:
if quote { if quote {
// zBlank can only be seen in between txt parts. // zBlank can only be seen in between txt parts.
@ -1920,3 +1916,32 @@ func (rr *APL) parse(c *zlexer, o string) *ParseError {
rr.Prefixes = prefixes rr.Prefixes = prefixes
return nil return nil
} }
// escapedStringOffset finds the offset within a string (which may contain escape
// sequences) that corresponds to a certain byte offset. If the input offset is
// out of bounds, -1 is returned.
func escapedStringOffset(s string, byteOffset int) int {
if byteOffset == 0 {
return 0
}
offset := 0
for i := 0; i < len(s); i++ {
offset += 1
// Skip escape sequences
if s[i] != '\\' {
// Not an escape sequence; nothing to do.
} else if isDDD(s[i+1:]) {
i += 3
} else {
i++
}
if offset >= byteOffset {
return i + 1
}
}
return -1
}

10
vendor/github.com/miekg/dns/xfr.go generated vendored
View File

@ -1,6 +1,7 @@
package dns package dns
import ( import (
"crypto/tls"
"fmt" "fmt"
"time" "time"
) )
@ -20,6 +21,7 @@ type Transfer struct {
TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations. TsigProvider TsigProvider // An implementation of the TsigProvider interface. If defined it replaces TsigSecret and is used for all TSIG operations.
TsigSecret map[string]string // Secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2) TsigSecret map[string]string // Secret(s) for Tsig map[<zonename>]<base64 secret>, zonename must be in canonical form (lowercase, fqdn, see RFC 4034 Section 6.2)
tsigTimersOnly bool tsigTimersOnly bool
TLS *tls.Config // TLS config. If Xfr over TLS will be attempted
} }
func (t *Transfer) tsigProvider() TsigProvider { func (t *Transfer) tsigProvider() TsigProvider {
@ -57,7 +59,11 @@ func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) {
} }
if t.Conn == nil { if t.Conn == nil {
t.Conn, err = DialTimeout("tcp", a, timeout) if t.TLS != nil {
t.Conn, err = DialTimeoutWithTLS("tcp-tls", a, t.TLS, timeout)
} else {
t.Conn, err = DialTimeout("tcp", a, timeout)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -182,7 +188,7 @@ func (t *Transfer) inIxfr(q *Msg, c chan *Envelope) {
if v, ok := rr.(*SOA); ok { if v, ok := rr.(*SOA); ok {
if v.Serial == serial { if v.Serial == serial {
n++ n++
// quit if it's a full axfr or the the servers' SOA is repeated the third time // quit if it's a full axfr or the servers' SOA is repeated the third time
if axfr && n == 2 || n == 3 { if axfr && n == 2 || n == 3 {
c <- &Envelope{in.Answer, nil} c <- &Envelope{in.Answer, nil}
return return

View File

@ -308,6 +308,7 @@ var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].
// Toolchains must be named beginning with `go1`, // Toolchains must be named beginning with `go1`,
// like "go1.20.3" or "go1.20.3-gccgo". As a special case, "default" is also permitted. // like "go1.20.3" or "go1.20.3-gccgo". As a special case, "default" is also permitted.
// TODO(samthanawalla): Replace regex with https://pkg.go.dev/go/version#IsValid in 1.23+
var ToolchainRE = lazyregexp.New(`^default$|^go1($|\.)`) var ToolchainRE = lazyregexp.New(`^default$|^go1($|\.)`)
func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) { func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
@ -384,7 +385,7 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a
errorf("toolchain directive expects exactly one argument") errorf("toolchain directive expects exactly one argument")
return return
} else if strict && !ToolchainRE.MatchString(args[0]) { } else if strict && !ToolchainRE.MatchString(args[0]) {
errorf("invalid toolchain version '%s': must match format go1.23.0 or local", args[0]) errorf("invalid toolchain version '%s': must match format go1.23.0 or default", args[0])
return return
} }
f.Toolchain = &Toolchain{Syntax: line} f.Toolchain = &Toolchain{Syntax: line}
@ -630,7 +631,7 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string,
errorf("go directive expects exactly one argument") errorf("go directive expects exactly one argument")
return return
} else if !GoVersionRE.MatchString(args[0]) { } else if !GoVersionRE.MatchString(args[0]) {
errorf("invalid go version '%s': must match format 1.23", args[0]) errorf("invalid go version '%s': must match format 1.23.0", args[0])
return return
} }
@ -646,7 +647,7 @@ func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string,
errorf("toolchain directive expects exactly one argument") errorf("toolchain directive expects exactly one argument")
return return
} else if !ToolchainRE.MatchString(args[0]) { } else if !ToolchainRE.MatchString(args[0]) {
errorf("invalid toolchain version '%s': must match format go1.23 or local", args[0]) errorf("invalid toolchain version '%s': must match format go1.23.0 or default", args[0])
return return
} }

View File

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

View File

@ -15,22 +15,10 @@ Load passes most patterns directly to the underlying build tool.
The default build tool is the go command. The default build tool is the go command.
Its supported patterns are described at Its supported patterns are described at
https://pkg.go.dev/cmd/go#hdr-Package_lists_and_patterns. https://pkg.go.dev/cmd/go#hdr-Package_lists_and_patterns.
Other build systems may be supported by providing a "driver";
see [The driver protocol].
Load may be used in Go projects that use alternative build systems, by All patterns with the prefix "query=", where query is a
installing an appropriate "driver" program for the build system and
specifying its location in the GOPACKAGESDRIVER environment variable.
For example,
https://github.com/bazelbuild/rules_go/wiki/Editor-and-tool-integration
explains how to use the driver for Bazel.
The driver program is responsible for interpreting patterns in its
preferred notation and reporting information about the packages that
they identify.
(See driverRequest and driverResponse types for the JSON
schema used by the protocol.
Though the protocol is supported, these types are currently unexported;
see #64608 for a proposal to publish them.)
Regardless of driver, all patterns with the prefix "query=", where query is a
non-empty string of letters from [a-z], are reserved and may be non-empty string of letters from [a-z], are reserved and may be
interpreted as query operators. interpreted as query operators.
@ -86,7 +74,29 @@ for details.
Most tools should pass their command-line arguments (after any flags) Most tools should pass their command-line arguments (after any flags)
uninterpreted to [Load], so that it can interpret them uninterpreted to [Load], so that it can interpret them
according to the conventions of the underlying build system. according to the conventions of the underlying build system.
See the Example function for typical usage. See the Example function for typical usage.
# The driver protocol
[Load] may be used to load Go packages even in Go projects that use
alternative build systems, by installing an appropriate "driver"
program for the build system and specifying its location in the
GOPACKAGESDRIVER environment variable.
For example,
https://github.com/bazelbuild/rules_go/wiki/Editor-and-tool-integration
explains how to use the driver for Bazel.
The driver program is responsible for interpreting patterns in its
preferred notation and reporting information about the packages that
those patterns identify. Drivers must also support the special "file="
and "pattern=" patterns described above.
The patterns are provided as positional command-line arguments. A
JSON-encoded [DriverRequest] message providing additional information
is written to the driver's standard input. The driver must write a
JSON-encoded [DriverResponse] message to its standard output. (This
message differs from the JSON schema produced by 'go list'.)
*/ */
package packages // import "golang.org/x/tools/go/packages" package packages // import "golang.org/x/tools/go/packages"

View File

@ -2,12 +2,11 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// This file enables an external tool to intercept package requests.
// If the tool is present then its results are used in preference to
// the go list command.
package packages package packages
// This file defines the protocol that enables an external "driver"
// tool to supply package metadata in place of 'go list'.
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
@ -17,31 +16,71 @@ import (
"strings" "strings"
) )
// The Driver Protocol // DriverRequest defines the schema of a request for package metadata
// from an external driver program. The JSON-encoded DriverRequest
// message is provided to the driver program's standard input. The
// query patterns are provided as command-line arguments.
// //
// The driver, given the inputs to a call to Load, returns metadata about the packages specified. // See the package documentation for an overview.
// This allows for different build systems to support go/packages by telling go/packages how the type DriverRequest struct {
// packages' source is organized.
// The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in
// the path as gopackagesdriver. It's given the inputs to load in its argv. See the package
// documentation in doc.go for the full description of the patterns that need to be supported.
// A driver receives as a JSON-serialized driverRequest struct in standard input and will
// produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output.
// driverRequest is used to provide the portion of Load's Config that is needed by a driver.
type driverRequest struct {
Mode LoadMode `json:"mode"` Mode LoadMode `json:"mode"`
// Env specifies the environment the underlying build system should be run in. // Env specifies the environment the underlying build system should be run in.
Env []string `json:"env"` Env []string `json:"env"`
// BuildFlags are flags that should be passed to the underlying build system. // BuildFlags are flags that should be passed to the underlying build system.
BuildFlags []string `json:"build_flags"` BuildFlags []string `json:"build_flags"`
// Tests specifies whether the patterns should also return test packages. // Tests specifies whether the patterns should also return test packages.
Tests bool `json:"tests"` Tests bool `json:"tests"`
// Overlay maps file paths (relative to the driver's working directory) to the byte contents // Overlay maps file paths (relative to the driver's working directory) to the byte contents
// of overlay files. // of overlay files.
Overlay map[string][]byte `json:"overlay"` Overlay map[string][]byte `json:"overlay"`
} }
// DriverResponse defines the schema of a response from an external
// driver program, providing the results of a query for package
// metadata. The driver program must write a JSON-encoded
// DriverResponse message to its standard output.
//
// See the package documentation for an overview.
type DriverResponse struct {
// NotHandled is returned if the request can't be handled by the current
// driver. If an external driver returns a response with NotHandled, the
// rest of the DriverResponse is ignored, and go/packages will fallback
// to the next driver. If go/packages is extended in the future to support
// lists of multiple drivers, go/packages will fall back to the next driver.
NotHandled bool
// Compiler and Arch are the arguments pass of types.SizesFor
// to get a types.Sizes to use when type checking.
Compiler string
Arch string
// Roots is the set of package IDs that make up the root packages.
// We have to encode this separately because when we encode a single package
// we cannot know if it is one of the roots as that requires knowledge of the
// graph it is part of.
Roots []string `json:",omitempty"`
// Packages is the full set of packages in the graph.
// The packages are not connected into a graph.
// The Imports if populated will be stubs that only have their ID set.
// Imports will be connected and then type and syntax information added in a
// later pass (see refine).
Packages []*Package
// GoVersion is the minor version number used by the driver
// (e.g. the go command on the PATH) when selecting .go files.
// Zero means unknown.
GoVersion int
}
// driver is the type for functions that query the build system for the
// packages named by the patterns.
type driver func(cfg *Config, patterns ...string) (*DriverResponse, error)
// findExternalDriver returns the file path of a tool that supplies // findExternalDriver returns the file path of a tool that supplies
// the build system package structure, or "" if not found." // the build system package structure, or "" if not found."
// If GOPACKAGESDRIVER is set in the environment findExternalTool returns its // If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
@ -64,8 +103,8 @@ func findExternalDriver(cfg *Config) driver {
return nil return nil
} }
} }
return func(cfg *Config, words ...string) (*driverResponse, error) { return func(cfg *Config, words ...string) (*DriverResponse, error) {
req, err := json.Marshal(driverRequest{ req, err := json.Marshal(DriverRequest{
Mode: cfg.Mode, Mode: cfg.Mode,
Env: cfg.Env, Env: cfg.Env,
BuildFlags: cfg.BuildFlags, BuildFlags: cfg.BuildFlags,
@ -92,7 +131,7 @@ func findExternalDriver(cfg *Config) driver {
fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr) fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd), stderr)
} }
var response driverResponse var response DriverResponse
if err := json.Unmarshal(buf.Bytes(), &response); err != nil { if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
return nil, err return nil, err
} }

View File

@ -35,23 +35,23 @@ type goTooOldError struct {
error error
} }
// responseDeduper wraps a driverResponse, deduplicating its contents. // responseDeduper wraps a DriverResponse, deduplicating its contents.
type responseDeduper struct { type responseDeduper struct {
seenRoots map[string]bool seenRoots map[string]bool
seenPackages map[string]*Package seenPackages map[string]*Package
dr *driverResponse dr *DriverResponse
} }
func newDeduper() *responseDeduper { func newDeduper() *responseDeduper {
return &responseDeduper{ return &responseDeduper{
dr: &driverResponse{}, dr: &DriverResponse{},
seenRoots: map[string]bool{}, seenRoots: map[string]bool{},
seenPackages: map[string]*Package{}, seenPackages: map[string]*Package{},
} }
} }
// addAll fills in r with a driverResponse. // addAll fills in r with a DriverResponse.
func (r *responseDeduper) addAll(dr *driverResponse) { func (r *responseDeduper) addAll(dr *DriverResponse) {
for _, pkg := range dr.Packages { for _, pkg := range dr.Packages {
r.addPackage(pkg) r.addPackage(pkg)
} }
@ -128,7 +128,7 @@ func (state *golistState) mustGetEnv() map[string]string {
// goListDriver uses the go list command to interpret the patterns and produce // goListDriver uses the go list command to interpret the patterns and produce
// the build system package structure. // the build system package structure.
// See driver for more details. // See driver for more details.
func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) { func goListDriver(cfg *Config, patterns ...string) (_ *DriverResponse, err error) {
// Make sure that any asynchronous go commands are killed when we return. // Make sure that any asynchronous go commands are killed when we return.
parentCtx := cfg.Context parentCtx := cfg.Context
if parentCtx == nil { if parentCtx == nil {
@ -146,16 +146,18 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
} }
// Fill in response.Sizes asynchronously if necessary. // Fill in response.Sizes asynchronously if necessary.
var sizeserr error
var sizeswg sync.WaitGroup
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 { if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
sizeswg.Add(1) errCh := make(chan error)
go func() { go func() {
compiler, arch, err := packagesdriver.GetSizesForArgsGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner) compiler, arch, err := packagesdriver.GetSizesForArgsGolist(ctx, state.cfgInvocation(), cfg.gocmdRunner)
sizeserr = err
response.dr.Compiler = compiler response.dr.Compiler = compiler
response.dr.Arch = arch response.dr.Arch = arch
sizeswg.Done() errCh <- err
}()
defer func() {
if sizesErr := <-errCh; sizesErr != nil {
err = sizesErr
}
}() }()
} }
@ -208,10 +210,7 @@ extractQueries:
} }
} }
sizeswg.Wait() // (We may yet return an error due to defer.)
if sizeserr != nil {
return nil, sizeserr
}
return response.dr, nil return response.dr, nil
} }
@ -266,7 +265,7 @@ func (state *golistState) runContainsQueries(response *responseDeduper, queries
// adhocPackage attempts to load or construct an ad-hoc package for a given // adhocPackage attempts to load or construct an ad-hoc package for a given
// query, if the original call to the driver produced inadequate results. // query, if the original call to the driver produced inadequate results.
func (state *golistState) adhocPackage(pattern, query string) (*driverResponse, error) { func (state *golistState) adhocPackage(pattern, query string) (*DriverResponse, error) {
response, err := state.createDriverResponse(query) response, err := state.createDriverResponse(query)
if err != nil { if err != nil {
return nil, err return nil, err
@ -357,7 +356,7 @@ func otherFiles(p *jsonPackage) [][]string {
// createDriverResponse uses the "go list" command to expand the pattern // createDriverResponse uses the "go list" command to expand the pattern
// words and return a response for the specified packages. // words and return a response for the specified packages.
func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) { func (state *golistState) createDriverResponse(words ...string) (*DriverResponse, error) {
// go list uses the following identifiers in ImportPath and Imports: // go list uses the following identifiers in ImportPath and Imports:
// //
// "p" -- importable package or main (command) // "p" -- importable package or main (command)
@ -384,7 +383,7 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
pkgs := make(map[string]*Package) pkgs := make(map[string]*Package)
additionalErrors := make(map[string][]Error) additionalErrors := make(map[string][]Error)
// Decode the JSON and convert it to Package form. // Decode the JSON and convert it to Package form.
response := &driverResponse{ response := &DriverResponse{
GoVersion: goVersion, GoVersion: goVersion,
} }
for dec := json.NewDecoder(buf); dec.More(); { for dec := json.NewDecoder(buf); dec.More(); {

View File

@ -206,43 +206,6 @@ type Config struct {
Overlay map[string][]byte Overlay map[string][]byte
} }
// driver is the type for functions that query the build system for the
// packages named by the patterns.
type driver func(cfg *Config, patterns ...string) (*driverResponse, error)
// driverResponse contains the results for a driver query.
type driverResponse struct {
// NotHandled is returned if the request can't be handled by the current
// driver. If an external driver returns a response with NotHandled, the
// rest of the driverResponse is ignored, and go/packages will fallback
// to the next driver. If go/packages is extended in the future to support
// lists of multiple drivers, go/packages will fall back to the next driver.
NotHandled bool
// Compiler and Arch are the arguments pass of types.SizesFor
// to get a types.Sizes to use when type checking.
Compiler string
Arch string
// Roots is the set of package IDs that make up the root packages.
// We have to encode this separately because when we encode a single package
// we cannot know if it is one of the roots as that requires knowledge of the
// graph it is part of.
Roots []string `json:",omitempty"`
// Packages is the full set of packages in the graph.
// The packages are not connected into a graph.
// The Imports if populated will be stubs that only have their ID set.
// Imports will be connected and then type and syntax information added in a
// later pass (see refine).
Packages []*Package
// GoVersion is the minor version number used by the driver
// (e.g. the go command on the PATH) when selecting .go files.
// Zero means unknown.
GoVersion int
}
// Load loads and returns the Go packages named by the given patterns. // Load loads and returns the Go packages named by the given patterns.
// //
// Config specifies loading options; // Config specifies loading options;
@ -291,7 +254,7 @@ func Load(cfg *Config, patterns ...string) ([]*Package, error) {
// no external driver, or the driver returns a response with NotHandled set, // no external driver, or the driver returns a response with NotHandled set,
// defaultDriver will fall back to the go list driver. // defaultDriver will fall back to the go list driver.
// The boolean result indicates that an external driver handled the request. // The boolean result indicates that an external driver handled the request.
func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, bool, error) { func defaultDriver(cfg *Config, patterns ...string) (*DriverResponse, bool, error) {
if driver := findExternalDriver(cfg); driver != nil { if driver := findExternalDriver(cfg); driver != nil {
response, err := driver(cfg, patterns...) response, err := driver(cfg, patterns...)
if err != nil { if err != nil {
@ -303,7 +266,10 @@ func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, bool, erro
} }
response, err := goListDriver(cfg, patterns...) response, err := goListDriver(cfg, patterns...)
return response, false, err if err != nil {
return nil, false, err
}
return response, false, nil
} }
// A Package describes a loaded Go package. // A Package describes a loaded Go package.
@ -648,7 +614,7 @@ func newLoader(cfg *Config) *loader {
// refine connects the supplied packages into a graph and then adds type // refine connects the supplied packages into a graph and then adds type
// and syntax information as requested by the LoadMode. // and syntax information as requested by the LoadMode.
func (ld *loader) refine(response *driverResponse) ([]*Package, error) { func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
roots := response.Roots roots := response.Roots
rootMap := make(map[string]int, len(roots)) rootMap := make(map[string]int, len(roots))
for i, root := range roots { for i, root := range roots {

View File

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

28
vendor/golang.org/x/tools/internal/aliases/aliases.go generated vendored Normal file
View File

@ -0,0 +1,28 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package aliases
import (
"go/token"
"go/types"
)
// Package aliases defines backward compatible shims
// for the types.Alias type representation added in 1.22.
// This defines placeholders for x/tools until 1.26.
// NewAlias creates a new TypeName in Package pkg that
// is an alias for the type rhs.
//
// When GoVersion>=1.22 and GODEBUG=gotypesalias=1,
// the Type() of the return value is a *types.Alias.
func NewAlias(pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName {
if enabled() {
tname := types.NewTypeName(pos, pkg, name, nil)
newAlias(tname, rhs)
return tname
}
return types.NewTypeName(pos, pkg, name, rhs)
}

View File

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

View File

@ -0,0 +1,72 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.22
// +build go1.22
package aliases
import (
"go/ast"
"go/parser"
"go/token"
"go/types"
"os"
"strings"
"sync"
)
// Alias is an alias of types.Alias.
type Alias = types.Alias
// Unalias is a wrapper of types.Unalias.
func Unalias(t types.Type) types.Type { return types.Unalias(t) }
// newAlias is an internal alias around types.NewAlias.
// Direct usage is discouraged as the moment.
// Try to use NewAlias instead.
func newAlias(tname *types.TypeName, rhs types.Type) *Alias {
a := types.NewAlias(tname, rhs)
// TODO(go.dev/issue/65455): Remove kludgy workaround to set a.actual as a side-effect.
Unalias(a)
return a
}
// enabled returns true when types.Aliases are enabled.
func enabled() bool {
// Use the gotypesalias value in GODEBUG if set.
godebug := os.Getenv("GODEBUG")
value := -1 // last set value.
for _, f := range strings.Split(godebug, ",") {
switch f {
case "gotypesalias=1":
value = 1
case "gotypesalias=0":
value = 0
}
}
switch value {
case 0:
return false
case 1:
return true
default:
return aliasesDefault()
}
}
// aliasesDefault reports if aliases are enabled by default.
func aliasesDefault() bool {
// Dynamically check if Aliases will be produced from go/types.
aliasesDefaultOnce.Do(func() {
fset := token.NewFileSet()
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0)
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
_, gotypesaliasDefault = pkg.Scope().Lookup("A").Type().(*types.Alias)
})
return gotypesaliasDefault
}
var gotypesaliasDefault bool
var aliasesDefaultOnce sync.Once

View File

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

View File

@ -23,6 +23,7 @@ import (
"strings" "strings"
"golang.org/x/tools/go/types/objectpath" "golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/tokeninternal" "golang.org/x/tools/internal/tokeninternal"
) )
@ -506,13 +507,13 @@ func (p *iexporter) doDecl(obj types.Object) {
case *types.TypeName: case *types.TypeName:
t := obj.Type() t := obj.Type()
if tparam, ok := t.(*types.TypeParam); ok { if tparam, ok := aliases.Unalias(t).(*types.TypeParam); ok {
w.tag('P') w.tag('P')
w.pos(obj.Pos()) w.pos(obj.Pos())
constraint := tparam.Constraint() constraint := tparam.Constraint()
if p.version >= iexportVersionGo1_18 { if p.version >= iexportVersionGo1_18 {
implicit := false implicit := false
if iface, _ := constraint.(*types.Interface); iface != nil { if iface, _ := aliases.Unalias(constraint).(*types.Interface); iface != nil {
implicit = iface.IsImplicit() implicit = iface.IsImplicit()
} }
w.bool(implicit) w.bool(implicit)
@ -738,6 +739,8 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
}() }()
} }
switch t := t.(type) { switch t := t.(type) {
// TODO(adonovan): support types.Alias.
case *types.Named: case *types.Named:
if targs := t.TypeArgs(); targs.Len() > 0 { if targs := t.TypeArgs(); targs.Len() > 0 {
w.startType(instanceType) w.startType(instanceType)
@ -843,7 +846,7 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
ft := t.EmbeddedType(i) ft := t.EmbeddedType(i)
tPkg := pkg tPkg := pkg
if named, _ := ft.(*types.Named); named != nil { if named, _ := aliases.Unalias(ft).(*types.Named); named != nil {
w.pos(named.Obj().Pos()) w.pos(named.Obj().Pos())
} else { } else {
w.pos(token.NoPos) w.pos(token.NoPos)

View File

@ -22,6 +22,8 @@ import (
"strings" "strings"
"golang.org/x/tools/go/types/objectpath" "golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typesinternal"
) )
type intReader struct { type intReader struct {
@ -224,6 +226,7 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte
// Gather the relevant packages from the manifest. // Gather the relevant packages from the manifest.
items := make([]GetPackagesItem, r.uint64()) items := make([]GetPackagesItem, r.uint64())
uniquePkgPaths := make(map[string]bool)
for i := range items { for i := range items {
pkgPathOff := r.uint64() pkgPathOff := r.uint64()
pkgPath := p.stringAt(pkgPathOff) pkgPath := p.stringAt(pkgPathOff)
@ -248,6 +251,12 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte
} }
items[i].nameIndex = nameIndex items[i].nameIndex = nameIndex
uniquePkgPaths[pkgPath] = true
}
// Debugging #63822; hypothesis: there are duplicate PkgPaths.
if len(uniquePkgPaths) != len(items) {
reportf("found duplicate PkgPaths while reading export data manifest: %v", items)
} }
// Request packages all at once from the client, // Request packages all at once from the client,
@ -515,7 +524,7 @@ func canReuse(def *types.Named, rhs types.Type) bool {
if def == nil { if def == nil {
return true return true
} }
iface, _ := rhs.(*types.Interface) iface, _ := aliases.Unalias(rhs).(*types.Interface)
if iface == nil { if iface == nil {
return true return true
} }
@ -580,14 +589,13 @@ func (r *importReader) obj(name string) {
// If the receiver has any targs, set those as the // If the receiver has any targs, set those as the
// rparams of the method (since those are the // rparams of the method (since those are the
// typeparams being used in the method sig/body). // typeparams being used in the method sig/body).
base := baseType(recv.Type()) _, recvNamed := typesinternal.ReceiverNamed(recv)
assert(base != nil) targs := recvNamed.TypeArgs()
targs := base.TypeArgs()
var rparams []*types.TypeParam var rparams []*types.TypeParam
if targs.Len() > 0 { if targs.Len() > 0 {
rparams = make([]*types.TypeParam, targs.Len()) rparams = make([]*types.TypeParam, targs.Len())
for i := range rparams { for i := range rparams {
rparams[i] = targs.At(i).(*types.TypeParam) rparams[i] = aliases.Unalias(targs.At(i)).(*types.TypeParam)
} }
} }
msig := r.signature(recv, rparams, nil) msig := r.signature(recv, rparams, nil)
@ -617,7 +625,7 @@ func (r *importReader) obj(name string) {
} }
constraint := r.typ() constraint := r.typ()
if implicit { if implicit {
iface, _ := constraint.(*types.Interface) iface, _ := aliases.Unalias(constraint).(*types.Interface)
if iface == nil { if iface == nil {
errorf("non-interface constraint marked implicit") errorf("non-interface constraint marked implicit")
} }
@ -824,7 +832,7 @@ func (r *importReader) typ() types.Type {
} }
func isInterface(t types.Type) bool { func isInterface(t types.Type) bool {
_, ok := t.(*types.Interface) _, ok := aliases.Unalias(t).(*types.Interface)
return ok return ok
} }
@ -1023,7 +1031,7 @@ func (r *importReader) tparamList() []*types.TypeParam {
for i := range xs { for i := range xs {
// Note: the standard library importer is tolerant of nil types here, // Note: the standard library importer is tolerant of nil types here,
// though would panic in SetTypeParams. // though would panic in SetTypeParams.
xs[i] = r.typ().(*types.TypeParam) xs[i] = aliases.Unalias(r.typ()).(*types.TypeParam)
} }
return xs return xs
} }
@ -1070,13 +1078,3 @@ func (r *importReader) byte() byte {
} }
return x return x
} }
func baseType(typ types.Type) *types.Named {
// pointer receivers are never types.Named types
if p, _ := typ.(*types.Pointer); p != nil {
typ = p.Elem()
}
// receiver base types are always (possibly generic) types.Named types
n, _ := typ.(*types.Named)
return n
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,9 +4,6 @@
// Derived from go/internal/gcimporter/ureader.go // Derived from go/internal/gcimporter/ureader.go
//go:build go1.18
// +build go1.18
package gcimporter package gcimporter
import ( import (
@ -16,6 +13,7 @@ import (
"sort" "sort"
"strings" "strings"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/pkgbits" "golang.org/x/tools/internal/pkgbits"
) )
@ -553,7 +551,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
// If the underlying type is an interface, we need to // If the underlying type is an interface, we need to
// duplicate its methods so we can replace the receiver // duplicate its methods so we can replace the receiver
// parameter's type (#49906). // parameter's type (#49906).
if iface, ok := underlying.(*types.Interface); ok && iface.NumExplicitMethods() != 0 { if iface, ok := aliases.Unalias(underlying).(*types.Interface); ok && iface.NumExplicitMethods() != 0 {
methods := make([]*types.Func, iface.NumExplicitMethods()) methods := make([]*types.Func, iface.NumExplicitMethods())
for i := range methods { for i := range methods {
fn := iface.ExplicitMethod(i) fn := iface.ExplicitMethod(i)

View File

@ -9,11 +9,13 @@ package gopathwalk
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"io"
"io/fs" "io/fs"
"log"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"sync"
"time" "time"
) )
@ -21,8 +23,13 @@ import (
type Options struct { type Options struct {
// 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{})
// Search module caches. Also disables legacy goimports ignore rules. // Search module caches. Also disables legacy goimports ignore rules.
ModulesEnabled bool ModulesEnabled bool
// Maximum number of concurrent calls to user-provided callbacks,
// or 0 for GOMAXPROCS.
Concurrency int
} }
// RootType indicates the type of a Root. // RootType indicates the type of a Root.
@ -43,19 +50,28 @@ type Root struct {
Type RootType Type RootType
} }
// Walk walks Go source directories ($GOROOT, $GOPATH, etc) to find packages. // Walk concurrently walks Go source directories ($GOROOT, $GOPATH, etc) to find packages.
//
// For each package found, add will be called with the absolute // For each package found, add will be called with the absolute
// paths of the containing source directory and the package directory. // paths of the containing source directory and the package directory.
//
// Unlike filepath.WalkDir, Walk follows symbolic links
// (while guarding against cycles).
func Walk(roots []Root, add func(root Root, dir string), opts Options) { func Walk(roots []Root, add func(root Root, dir string), opts Options) {
WalkSkip(roots, add, func(Root, string) bool { return false }, opts) WalkSkip(roots, add, func(Root, string) bool { return false }, opts)
} }
// WalkSkip walks Go source directories ($GOROOT, $GOPATH, etc) to find packages. // WalkSkip concurrently walks Go source directories ($GOROOT, $GOPATH, etc) to
// find packages.
//
// For each package found, add will be called with the absolute // For each package found, add will be called with the absolute
// paths of the containing source directory and the package directory. // paths of the containing source directory and the package directory.
// For each directory that will be scanned, skip will be called // For each directory that will be scanned, skip will be called
// with the absolute paths of the containing source directory and the directory. // with the absolute paths of the containing source directory and the directory.
// If skip returns false on a directory it will be processed. // If skip returns false on a directory it will be processed.
//
// Unlike filepath.WalkDir, WalkSkip follows symbolic links
// (while guarding against cycles).
func WalkSkip(roots []Root, add func(root Root, dir string), skip func(root Root, dir string) bool, opts Options) { func WalkSkip(roots []Root, add func(root Root, dir string), skip func(root Root, dir string) bool, opts Options) {
for _, root := range roots { for _, root := range roots {
walkDir(root, add, skip, opts) walkDir(root, add, skip, opts)
@ -64,45 +80,51 @@ func WalkSkip(roots []Root, add func(root Root, dir string), skip func(root Root
// walkDir creates a walker and starts fastwalk with this walker. // walkDir creates a walker and starts fastwalk with this walker.
func walkDir(root Root, add func(Root, string), skip func(root Root, dir string) bool, opts Options) { func walkDir(root Root, add func(Root, string), skip func(root Root, dir string) bool, opts Options) {
if opts.Logf == nil {
opts.Logf = func(format string, args ...interface{}) {}
}
if _, err := os.Stat(root.Path); os.IsNotExist(err) { if _, err := os.Stat(root.Path); os.IsNotExist(err) {
if opts.Logf != nil { opts.Logf("skipping nonexistent directory: %v", root.Path)
opts.Logf("skipping nonexistent directory: %v", root.Path)
}
return return
} }
start := time.Now() start := time.Now()
if opts.Logf != nil { opts.Logf("scanning %s", root.Path)
opts.Logf("scanning %s", root.Path)
}
concurrency := opts.Concurrency
if concurrency == 0 {
// The walk be either CPU-bound or I/O-bound, depending on what the
// caller-supplied add function does and the details of the user's platform
// and machine. Rather than trying to fine-tune the concurrency level for a
// specific environment, we default to GOMAXPROCS: it is likely to be a good
// choice for a CPU-bound add function, and if it is instead I/O-bound, then
// dealing with I/O saturation is arguably the job of the kernel and/or
// runtime. (Oversaturating I/O seems unlikely to harm performance as badly
// as failing to saturate would.)
concurrency = runtime.GOMAXPROCS(0)
}
w := &walker{ w := &walker{
root: root, root: root,
add: add, add: add,
skip: skip, skip: skip,
opts: opts, opts: opts,
added: make(map[string]bool), sem: make(chan struct{}, concurrency),
} }
w.init() w.init()
// Add a trailing path separator to cause filepath.WalkDir to traverse symlinks. w.sem <- struct{}{}
path := root.Path path := root.Path
if len(path) == 0 { if path == "" {
path = "." + string(filepath.Separator) path = "."
} else if !os.IsPathSeparator(path[len(path)-1]) {
path = path + string(filepath.Separator)
} }
if fi, err := os.Lstat(path); err == nil {
w.walk(path, nil, fs.FileInfoToDirEntry(fi))
} else {
w.opts.Logf("scanning directory %v: %v", root.Path, err)
}
<-w.sem
w.walking.Wait()
if err := filepath.WalkDir(path, w.walk); err != nil { opts.Logf("scanned %s in %v", root.Path, time.Since(start))
logf := opts.Logf
if logf == nil {
logf = log.Printf
}
logf("scanning directory %v: %v", root.Path, err)
}
if opts.Logf != nil {
opts.Logf("scanned %s in %v", root.Path, time.Since(start))
}
} }
// walker is the callback for fastwalk.Walk. // walker is the callback for fastwalk.Walk.
@ -112,10 +134,18 @@ type walker struct {
skip func(Root, string) bool // The callback that will be invoked for every dir. dir is skipped if it returns true. skip func(Root, string) bool // The callback that will be invoked for every dir. dir is skipped if it returns true.
opts Options // Options passed to Walk by the user. opts Options // Options passed to Walk by the user.
pathSymlinks []os.FileInfo walking sync.WaitGroup
ignoredDirs []string sem chan struct{} // Channel of semaphore tokens; send to acquire, receive to release.
ignoredDirs []string
added map[string]bool added sync.Map // map[string]bool
}
// A symlinkList is a linked list of os.FileInfos for parent directories
// reached via symlinks.
type symlinkList struct {
info os.FileInfo
prev *symlinkList
} }
// init initializes the walker based on its Options // init initializes the walker based on its Options
@ -132,9 +162,7 @@ func (w *walker) init() {
for _, p := range ignoredPaths { for _, p := range ignoredPaths {
full := filepath.Join(w.root.Path, p) full := filepath.Join(w.root.Path, p)
w.ignoredDirs = append(w.ignoredDirs, full) w.ignoredDirs = append(w.ignoredDirs, full)
if w.opts.Logf != nil { w.opts.Logf("Directory added to ignore list: %s", full)
w.opts.Logf("Directory added to ignore list: %s", full)
}
} }
} }
@ -144,12 +172,10 @@ func (w *walker) init() {
func (w *walker) getIgnoredDirs(path string) []string { func (w *walker) getIgnoredDirs(path string) []string {
file := filepath.Join(path, ".goimportsignore") file := filepath.Join(path, ".goimportsignore")
slurp, err := os.ReadFile(file) slurp, err := os.ReadFile(file)
if w.opts.Logf != nil { if err != nil {
if err != nil { w.opts.Logf("%v", err)
w.opts.Logf("%v", err) } else {
} else { w.opts.Logf("Read %s", file)
w.opts.Logf("Read %s", file)
}
} }
if err != nil { if err != nil {
return nil return nil
@ -183,63 +209,22 @@ func (w *walker) shouldSkipDir(dir string) bool {
// walk walks through the given path. // walk walks through the given path.
// //
// Errors are logged if w.opts.Logf is non-nil, but otherwise ignored: // Errors are logged if w.opts.Logf is non-nil, but otherwise ignored.
// walk returns only nil or fs.SkipDir. func (w *walker) walk(path string, pathSymlinks *symlinkList, d fs.DirEntry) {
func (w *walker) walk(path string, d fs.DirEntry, err error) error {
if err != nil {
// We have no way to report errors back through Walk or WalkSkip,
// so just log and ignore them.
if w.opts.Logf != nil {
w.opts.Logf("%v", err)
}
if d == nil {
// Nothing more to do: the error prevents us from knowing
// what path even represents.
return nil
}
}
if d.Type().IsRegular() {
if !strings.HasSuffix(path, ".go") {
return nil
}
dir := filepath.Dir(path)
if dir == w.root.Path && (w.root.Type == RootGOROOT || w.root.Type == RootGOPATH) {
// Doesn't make sense to have regular files
// directly in your $GOPATH/src or $GOROOT/src.
return nil
}
if !w.added[dir] {
w.add(w.root, dir)
w.added[dir] = true
}
return nil
}
if d.IsDir() {
base := filepath.Base(path)
if base == "" || base[0] == '.' || base[0] == '_' ||
base == "testdata" ||
(w.root.Type == RootGOROOT && w.opts.ModulesEnabled && base == "vendor") ||
(!w.opts.ModulesEnabled && base == "node_modules") {
return fs.SkipDir
}
if w.shouldSkipDir(path) {
return fs.SkipDir
}
return nil
}
if d.Type()&os.ModeSymlink != 0 { if d.Type()&os.ModeSymlink != 0 {
// Walk the symlink's target rather than the symlink itself.
//
// (Note that os.Stat, unlike the lower-lever os.Readlink,
// follows arbitrarily many layers of symlinks, so it will eventually
// reach either a non-symlink or a nonexistent target.)
//
// TODO(bcmills): 'go list all' itself ignores symlinks within GOROOT/src // TODO(bcmills): 'go list all' itself ignores symlinks within GOROOT/src
// and GOPATH/src. Do we really need to traverse them here? If so, why? // and GOPATH/src. Do we really need to traverse them here? If so, why?
fi, err := os.Stat(path) fi, err := os.Stat(path)
if err != nil || !fi.IsDir() { if err != nil {
// Not a directory. Just walk the file (or broken link) and be done. w.opts.Logf("%v", err)
return w.walk(path, fs.FileInfoToDirEntry(fi), err) return
} }
// Avoid walking symlink cycles: if we have already followed a symlink to // Avoid walking symlink cycles: if we have already followed a symlink to
@ -249,83 +234,104 @@ func (w *walker) walk(path string, d fs.DirEntry, err error) error {
// the number of extra stat calls we make if we *don't* encounter a cycle. // the number of extra stat calls we make if we *don't* encounter a cycle.
// Since we don't actually expect to encounter symlink cycles in practice, // Since we don't actually expect to encounter symlink cycles in practice,
// this seems like the right tradeoff. // this seems like the right tradeoff.
for _, parent := range w.pathSymlinks { for parent := pathSymlinks; parent != nil; parent = parent.prev {
if os.SameFile(fi, parent) { if os.SameFile(fi, parent.info) {
return nil return
} }
} }
w.pathSymlinks = append(w.pathSymlinks, fi) pathSymlinks = &symlinkList{
defer func() { info: fi,
w.pathSymlinks = w.pathSymlinks[:len(w.pathSymlinks)-1] prev: pathSymlinks,
}() }
d = fs.FileInfoToDirEntry(fi)
}
// On some platforms the OS (or the Go os package) sometimes fails to if d.Type().IsRegular() {
// resolve directory symlinks before a trailing slash if !strings.HasSuffix(path, ".go") {
// (even though POSIX requires it to do so). return
//
// On macOS that failure may be caused by a known libc/kernel bug;
// see https://go.dev/issue/59586.
//
// On Windows before Go 1.21, it may be caused by a bug in
// os.Lstat (fixed in https://go.dev/cl/463177).
//
// Since we need to handle this explicitly on broken platforms anyway,
// it is simplest to just always do that and not rely on POSIX pathname
// resolution to walk the directory (such as by calling WalkDir with
// a trailing slash appended to the path).
//
// Instead, we make a sequence of walk calls — directly and through
// recursive calls to filepath.WalkDir — simulating what WalkDir would do
// if the symlink were a regular directory.
// First we call walk on the path as a directory
// (instead of a symlink).
err = w.walk(path, fs.FileInfoToDirEntry(fi), nil)
if err == fs.SkipDir {
return nil
} else if err != nil {
// This should be impossible, but handle it anyway in case
// walk is changed to return other errors.
return err
} }
// Now read the directory and walk its entries. dir := filepath.Dir(path)
ents, err := os.ReadDir(path) if dir == w.root.Path && (w.root.Type == RootGOROOT || w.root.Type == RootGOPATH) {
// Doesn't make sense to have regular files
// directly in your $GOPATH/src or $GOROOT/src.
//
// TODO(bcmills): there are many levels of directory within
// RootModuleCache where this also wouldn't make sense,
// Can we generalize this to any directory without a corresponding
// import path?
return
}
if _, dup := w.added.LoadOrStore(dir, true); !dup {
w.add(w.root, dir)
}
}
if !d.IsDir() {
return
}
base := filepath.Base(path)
if base == "" || base[0] == '.' || base[0] == '_' ||
base == "testdata" ||
(w.root.Type == RootGOROOT && w.opts.ModulesEnabled && base == "vendor") ||
(!w.opts.ModulesEnabled && base == "node_modules") ||
w.shouldSkipDir(path) {
return
}
// Read the directory and walk its entries.
f, err := os.Open(path)
if err != nil {
w.opts.Logf("%v", err)
return
}
defer f.Close()
for {
// We impose an arbitrary limit on the number of ReadDir results per
// directory to limit the amount of memory consumed for stale or upcoming
// directory entries. The limit trades off CPU (number of syscalls to read
// the whole directory) against RAM (reachable directory entries other than
// the one currently being processed).
//
// Since we process the directories recursively, we will end up maintaining
// a slice of entries for each level of the directory tree.
// (Compare https://go.dev/issue/36197.)
ents, err := f.ReadDir(1024)
if err != nil { if err != nil {
// Report the ReadDir error, as filepath.WalkDir would do. if err != io.EOF {
err = w.walk(path, fs.FileInfoToDirEntry(fi), err) w.opts.Logf("%v", err)
if err == fs.SkipDir {
return nil
} else if err != nil {
return err // Again, should be impossible.
} }
// Fall through and iterate over whatever entries we did manage to get. break
} }
for _, d := range ents { for _, d := range ents {
nextPath := filepath.Join(path, d.Name()) nextPath := filepath.Join(path, d.Name())
if d.IsDir() { if d.IsDir() {
// We want to walk the whole directory tree rooted at nextPath, select {
// not just the single entry for the directory. case w.sem <- struct{}{}:
err := filepath.WalkDir(nextPath, w.walk) // Got a new semaphore token, so we can traverse the directory concurrently.
if err != nil && w.opts.Logf != nil { d := d
w.opts.Logf("%v", err) w.walking.Add(1)
} go func() {
} else { defer func() {
err := w.walk(nextPath, d, nil) <-w.sem
if err == fs.SkipDir { w.walking.Done()
// Skip the rest of the entries in the parent directory of nextPath }()
// (that is, path itself). w.walk(nextPath, pathSymlinks, d)
break }()
} else if err != nil { continue
return err // Again, should be impossible.
default:
// No tokens available, so traverse serially.
} }
} }
}
return nil
}
// Not a file, regular directory, or symlink; skip. w.walk(nextPath, pathSymlinks, d)
return nil }
}
} }

View File

@ -13,6 +13,7 @@ import (
"go/build" "go/build"
"go/parser" "go/parser"
"go/token" "go/token"
"go/types"
"io/fs" "io/fs"
"io/ioutil" "io/ioutil"
"os" "os"
@ -700,20 +701,21 @@ func ScoreImportPaths(ctx context.Context, env *ProcessEnv, paths []string) (map
return result, nil return result, nil
} }
func PrimeCache(ctx context.Context, env *ProcessEnv) error { func PrimeCache(ctx context.Context, resolver Resolver) error {
// Fully scan the disk for directories, but don't actually read any Go files. // Fully scan the disk for directories, but don't actually read any Go files.
callback := &scanCallback{ callback := &scanCallback{
rootFound: func(gopathwalk.Root) bool { rootFound: func(root gopathwalk.Root) bool {
return true // See getCandidatePkgs: walking GOROOT is apparently expensive and
// unnecessary.
return root.Type != gopathwalk.RootGOROOT
}, },
dirFound: func(pkg *pkg) bool { dirFound: func(pkg *pkg) bool {
return false return false
}, },
packageNameLoaded: func(pkg *pkg) bool { // packageNameLoaded and exportsLoaded must never be called.
return false
},
} }
return getCandidatePkgs(ctx, callback, "", "", env)
return resolver.scan(ctx, callback)
} }
func candidateImportName(pkg *pkg) string { func candidateImportName(pkg *pkg) string {
@ -827,16 +829,45 @@ 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", "GOWORK"} // TODO(rfindley): we should depend on GOOS and GOARCH, to provide accurate
// imports when doing cross-platform development.
var requiredGoEnvVars = []string{
"GO111MODULE",
"GOFLAGS",
"GOINSECURE",
"GOMOD",
"GOMODCACHE",
"GONOPROXY",
"GONOSUMDB",
"GOPATH",
"GOPROXY",
"GOROOT",
"GOSUMDB",
"GOWORK",
}
// 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.
//
// ...a ProcessEnv *also* overwrites its Env along with derived state in the
// form of the resolver. And because it is lazily initialized, an env may just
// be broken and unusable, but there is no way for the caller to detect that:
// all queries will just fail.
//
// TODO(rfindley): refactor this package so that this type (perhaps renamed to
// just Env or Config) is an immutable configuration struct, to be exchanged
// for an initialized object via a constructor that returns an error. Perhaps
// the signature should be `func NewResolver(*Env) (*Resolver, error)`, where
// resolver is a concrete type used for resolving imports. Via this
// refactoring, we can avoid the need to call ProcessEnv.init and
// ProcessEnv.GoEnv everywhere, and implicitly fix all the places where this
// these are misused. Also, we'd delegate the caller the decision of how to
// handle a broken environment.
type ProcessEnv struct { type ProcessEnv struct {
GocmdRunner *gocommand.Runner GocmdRunner *gocommand.Runner
BuildFlags []string BuildFlags []string
ModFlag string ModFlag string
ModFile string
// SkipPathInScan returns true if the path should be skipped from scans of // SkipPathInScan returns true if the path should be skipped from scans of
// the RootCurrentModule root type. The function argument is a clean, // the RootCurrentModule root type. The function argument is a clean,
@ -846,7 +877,7 @@ type ProcessEnv struct {
// Env overrides the OS environment, and can be used to specify // Env overrides the OS environment, and can be used to specify
// GOPROXY, GO111MODULE, etc. PATH cannot be set here, because // GOPROXY, GO111MODULE, etc. PATH cannot be set here, because
// exec.Command will not honor it. // exec.Command will not honor it.
// Specifying all of RequiredGoEnvVars avoids a call to `go env`. // Specifying all of requiredGoEnvVars avoids a call to `go env`.
Env map[string]string Env map[string]string
WorkingDir string WorkingDir string
@ -854,9 +885,17 @@ type ProcessEnv struct {
// 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 // If set, ModCache holds a shared cache of directory info to use across
// multiple ProcessEnvs.
ModCache *DirInfoCache
resolver Resolver initialized bool // see TODO above
// resolver and resolverErr are lazily evaluated (see GetResolver).
// This is unclean, but see the big TODO in the docstring for ProcessEnv
// above: for now, we can't be sure that the ProcessEnv is fully initialized.
resolver Resolver
resolverErr error
} }
func (e *ProcessEnv) goEnv() (map[string]string, error) { func (e *ProcessEnv) goEnv() (map[string]string, error) {
@ -936,20 +975,31 @@ func (e *ProcessEnv) env() []string {
} }
func (e *ProcessEnv) GetResolver() (Resolver, error) { func (e *ProcessEnv) GetResolver() (Resolver, error) {
if e.resolver != nil {
return e.resolver, nil
}
if err := e.init(); err != nil { if err := e.init(); err != nil {
return nil, err return nil, err
} }
if len(e.Env["GOMOD"]) == 0 && len(e.Env["GOWORK"]) == 0 {
e.resolver = newGopathResolver(e) if e.resolver == nil && e.resolverErr == nil {
return e.resolver, nil // TODO(rfindley): we should only use a gopathResolver here if the working
// directory is actually *in* GOPATH. (I seem to recall an open gopls issue
// for this behavior, but I can't find it).
//
// For gopls, we can optionally explicitly choose a resolver type, since we
// already know the view type.
if len(e.Env["GOMOD"]) == 0 && len(e.Env["GOWORK"]) == 0 {
e.resolver = newGopathResolver(e)
} else {
e.resolver, e.resolverErr = newModuleResolver(e, e.ModCache)
}
} }
e.resolver = newModuleResolver(e)
return e.resolver, nil return e.resolver, e.resolverErr
} }
// buildContext returns the build.Context to use for matching files.
//
// TODO(rfindley): support dynamic GOOS, GOARCH here, when doing cross-platform
// development.
func (e *ProcessEnv) buildContext() (*build.Context, error) { func (e *ProcessEnv) buildContext() (*build.Context, error) {
ctx := build.Default ctx := build.Default
goenv, err := e.goEnv() goenv, err := e.goEnv()
@ -1029,15 +1079,23 @@ func addStdlibCandidates(pass *pass, refs references) error {
type Resolver interface { type Resolver interface {
// loadPackageNames loads the package names in importPaths. // loadPackageNames loads the package names in importPaths.
loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error)
// scan works with callback to search for packages. See scanCallback for details. // scan works with callback to search for packages. See scanCallback for details.
scan(ctx context.Context, callback *scanCallback) error scan(ctx context.Context, callback *scanCallback) error
// loadExports returns the set of exported symbols in the package at dir. // loadExports returns the set of exported symbols in the package at dir.
// loadExports may be called concurrently. // loadExports may be called concurrently.
loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error)
// scoreImportPath returns the relevance for an import path. // scoreImportPath returns the relevance for an import path.
scoreImportPath(ctx context.Context, path string) float64 scoreImportPath(ctx context.Context, path string) float64
ClearForNewScan() // ClearForNewScan returns a new Resolver based on the receiver that has
// cleared its internal caches of directory contents.
//
// The new resolver should be primed and then set via
// [ProcessEnv.UpdateResolver].
ClearForNewScan() Resolver
} }
// A scanCallback controls a call to scan and receives its results. // A scanCallback controls a call to scan and receives its results.
@ -1120,7 +1178,7 @@ func addExternalCandidates(ctx context.Context, pass *pass, refs references, fil
go func(pkgName string, symbols map[string]bool) { go func(pkgName string, symbols map[string]bool) {
defer wg.Done() defer wg.Done()
found, err := findImport(ctx, pass, found[pkgName], pkgName, symbols, filename) found, err := findImport(ctx, pass, found[pkgName], pkgName, symbols)
if err != nil { if err != nil {
firstErrOnce.Do(func() { firstErrOnce.Do(func() {
@ -1151,6 +1209,17 @@ func addExternalCandidates(ctx context.Context, pass *pass, refs references, fil
}() }()
for result := range results { for result := range results {
// Don't offer completions that would shadow predeclared
// names, such as github.com/coreos/etcd/error.
if types.Universe.Lookup(result.pkg.name) != nil { // predeclared
// Ideally we would skip this candidate only
// if the predeclared name is actually
// referenced by the file, but that's a lot
// trickier to compute and would still create
// an import that is likely to surprise the
// user before long.
continue
}
pass.addCandidate(result.imp, result.pkg) pass.addCandidate(result.imp, result.pkg)
} }
return firstErr return firstErr
@ -1193,31 +1262,22 @@ func ImportPathToAssumedName(importPath string) string {
type gopathResolver struct { type gopathResolver struct {
env *ProcessEnv env *ProcessEnv
walked bool walked bool
cache *dirInfoCache cache *DirInfoCache
scanSema chan struct{} // scanSema prevents concurrent scans. scanSema chan struct{} // scanSema prevents concurrent scans.
} }
func newGopathResolver(env *ProcessEnv) *gopathResolver { func newGopathResolver(env *ProcessEnv) *gopathResolver {
r := &gopathResolver{ r := &gopathResolver{
env: env, env: env,
cache: &dirInfoCache{ cache: NewDirInfoCache(),
dirs: map[string]*directoryPackageInfo{},
listeners: map[*int]cacheListener{},
},
scanSema: make(chan struct{}, 1), scanSema: make(chan struct{}, 1),
} }
r.scanSema <- struct{}{} r.scanSema <- struct{}{}
return r return r
} }
func (r *gopathResolver) ClearForNewScan() { func (r *gopathResolver) ClearForNewScan() Resolver {
<-r.scanSema return newGopathResolver(r.env)
r.cache = &dirInfoCache{
dirs: map[string]*directoryPackageInfo{},
listeners: map[*int]cacheListener{},
}
r.walked = false
r.scanSema <- struct{}{}
} }
func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
@ -1538,7 +1598,7 @@ func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, incl
// findImport searches for a package with the given symbols. // findImport searches for a package with the given symbols.
// If no package is found, findImport returns ("", false, nil) // If no package is found, findImport returns ("", false, nil)
func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgName string, symbols map[string]bool, filename string) (*pkg, error) { func findImport(ctx context.Context, pass *pass, candidates []pkgDistance, pkgName string, symbols map[string]bool) (*pkg, error) {
// Sort the candidates by their import package length, // Sort the candidates by their import package length,
// assuming that shorter package names are better than long // assuming that shorter package names are better than long
// ones. Note that this sorts by the de-vendored name, so // ones. Note that this sorts by the de-vendored name, so

View File

@ -236,7 +236,7 @@ func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast
src = src[:len(src)-len("}\n")] src = src[:len(src)-len("}\n")]
// Gofmt has also indented the function body one level. // Gofmt has also indented the function body one level.
// Remove that indent. // Remove that indent.
src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1) src = bytes.ReplaceAll(src, []byte("\n\t"), []byte("\n"))
return matchSpace(orig, src) return matchSpace(orig, src)
} }
return file, adjust, nil return file, adjust, nil

View File

@ -23,49 +23,88 @@ import (
"golang.org/x/tools/internal/gopathwalk" "golang.org/x/tools/internal/gopathwalk"
) )
// ModuleResolver implements resolver for modules using the go command as little // Notes(rfindley): ModuleResolver appears to be heavily optimized for scanning
// as feasible. // as fast as possible, which is desirable for a call to goimports from the
// command line, but it doesn't work as well for gopls, where it suffers from
// slow startup (golang/go#44863) and intermittent hanging (golang/go#59216),
// both caused by populating the cache, albeit in slightly different ways.
//
// A high level list of TODOs:
// - Optimize the scan itself, as there is some redundancy statting and
// reading go.mod files.
// - Invert the relationship between ProcessEnv and Resolver (see the
// docstring of ProcessEnv).
// - Make it easier to use an external resolver implementation.
//
// Smaller TODOs are annotated in the code below.
// ModuleResolver implements the Resolver interface for a workspace using
// modules.
//
// A goal of the ModuleResolver is to invoke the Go command as little as
// possible. To this end, it runs the Go command only for listing module
// information (i.e. `go list -m -e -json ...`). Package scanning, the process
// of loading package information for the modules, is implemented internally
// via the scan method.
//
// It has two types of state: the state derived from the go command, which
// is populated by init, and the state derived from scans, which is populated
// via scan. A root is considered scanned if it has been walked to discover
// directories. However, if the scan did not require additional information
// from the directory (such as package name or exports), the directory
// information itself may be partially populated. It will be lazily filled in
// as needed by scans, using the scanCallback.
type ModuleResolver struct { type ModuleResolver struct {
env *ProcessEnv env *ProcessEnv
moduleCacheDir string
dummyVendorMod *gocommand.ModuleJSON // If vendoring is enabled, the pseudo-module that represents the /vendor directory.
roots []gopathwalk.Root
scanSema chan struct{} // scanSema prevents concurrent scans and guards scannedRoots.
scannedRoots map[gopathwalk.Root]bool
initialized bool // Module state, populated during construction
mains []*gocommand.ModuleJSON dummyVendorMod *gocommand.ModuleJSON // if vendoring is enabled, a pseudo-module to represent the /vendor directory
mainByDir map[string]*gocommand.ModuleJSON moduleCacheDir string // GOMODCACHE, inferred from GOPATH if unset
modsByModPath []*gocommand.ModuleJSON // All modules, ordered by # of path components in module Path... roots []gopathwalk.Root // roots to scan, in approximate order of importance
modsByDir []*gocommand.ModuleJSON // ...or number of path components in their Dir. mains []*gocommand.ModuleJSON // main modules
mainByDir map[string]*gocommand.ModuleJSON // module information by dir, to join with roots
modsByModPath []*gocommand.ModuleJSON // all modules, ordered by # of path components in their module path
modsByDir []*gocommand.ModuleJSON // ...or by the number of path components in their Dir.
// moduleCacheCache stores information about the module cache. // Scanning state, populated by scan
moduleCacheCache *dirInfoCache
otherCache *dirInfoCache // scanSema prevents concurrent scans, and guards scannedRoots and the cache
// fields below (though the caches themselves are concurrency safe).
// Receive to acquire, send to release.
scanSema chan struct{}
scannedRoots map[gopathwalk.Root]bool // if true, root has been walked
// Caches of directory info, populated by scans and scan callbacks
//
// moduleCacheCache stores cached information about roots in the module
// cache, which are immutable and therefore do not need to be invalidated.
//
// otherCache stores information about all other roots (even GOROOT), which
// may change.
moduleCacheCache *DirInfoCache
otherCache *DirInfoCache
} }
func newModuleResolver(e *ProcessEnv) *ModuleResolver { // newModuleResolver returns a new module-aware goimports resolver.
//
// Note: use caution when modifying this constructor: changes must also be
// reflected in ModuleResolver.ClearForNewScan.
func newModuleResolver(e *ProcessEnv, moduleCacheCache *DirInfoCache) (*ModuleResolver, error) {
r := &ModuleResolver{ r := &ModuleResolver{
env: e, env: e,
scanSema: make(chan struct{}, 1), scanSema: make(chan struct{}, 1),
} }
r.scanSema <- struct{}{} r.scanSema <- struct{}{} // release
return r
}
func (r *ModuleResolver) init() error {
if r.initialized {
return nil
}
goenv, err := r.env.goEnv() goenv, err := r.env.goEnv()
if err != nil { if err != nil {
return err return nil, err
} }
// TODO(rfindley): can we refactor to share logic with r.env.invokeGo?
inv := gocommand.Invocation{ inv := gocommand.Invocation{
BuildFlags: r.env.BuildFlags, BuildFlags: r.env.BuildFlags,
ModFlag: r.env.ModFlag, ModFlag: r.env.ModFlag,
ModFile: r.env.ModFile,
Env: r.env.env(), Env: r.env.env(),
Logf: r.env.Logf, Logf: r.env.Logf,
WorkingDir: r.env.WorkingDir, WorkingDir: r.env.WorkingDir,
@ -77,9 +116,12 @@ func (r *ModuleResolver) init() error {
// Module vendor directories are ignored in workspace mode: // Module vendor directories are ignored in workspace mode:
// https://go.googlesource.com/proposal/+/master/design/45713-workspace.md // https://go.googlesource.com/proposal/+/master/design/45713-workspace.md
if len(r.env.Env["GOWORK"]) == 0 { if len(r.env.Env["GOWORK"]) == 0 {
// TODO(rfindley): VendorEnabled runs the go command to get GOFLAGS, but
// they should be available from the ProcessEnv. Can we avoid the redundant
// invocation?
vendorEnabled, mainModVendor, err = gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner) vendorEnabled, mainModVendor, err = gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner)
if err != nil { if err != nil {
return err return nil, err
} }
} }
@ -100,19 +142,14 @@ func (r *ModuleResolver) init() error {
// GO111MODULE=on. Other errors are fatal. // GO111MODULE=on. Other errors are fatal.
if err != nil { if err != nil {
if errMsg := err.Error(); !strings.Contains(errMsg, "working directory is not part of a module") && !strings.Contains(errMsg, "go.mod file not found") { if errMsg := err.Error(); !strings.Contains(errMsg, "working directory is not part of a module") && !strings.Contains(errMsg, "go.mod file not found") {
return err return nil, err
} }
} }
} }
if gmc := r.env.Env["GOMODCACHE"]; gmc != "" { r.moduleCacheDir = gomodcacheForEnv(goenv)
r.moduleCacheDir = gmc if r.moduleCacheDir == "" {
} else { return nil, fmt.Errorf("cannot resolve GOMODCACHE")
gopaths := filepath.SplitList(goenv["GOPATH"])
if len(gopaths) == 0 {
return fmt.Errorf("empty GOPATH")
}
r.moduleCacheDir = filepath.Join(gopaths[0], "/pkg/mod")
} }
sort.Slice(r.modsByModPath, func(i, j int) bool { sort.Slice(r.modsByModPath, func(i, j int) bool {
@ -141,7 +178,11 @@ func (r *ModuleResolver) init() error {
} else { } else {
addDep := func(mod *gocommand.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
// when we encounter it in the module cache scan.
//
// Including it at a lower index in r.roots than the module cache dir
// helps prioritize matches from within existing dependencies.
r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootModuleCache}) r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootModuleCache})
} else { } else {
r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootOther}) r.roots = append(r.roots, gopathwalk.Root{Path: mod.Dir, Type: gopathwalk.RootOther})
@ -158,24 +199,40 @@ func (r *ModuleResolver) init() error {
addDep(mod) addDep(mod)
} }
} }
// If provided, share the moduleCacheCache.
//
// TODO(rfindley): The module cache is immutable. However, the loaded
// exports do depend on GOOS and GOARCH. Fortunately, the
// ProcessEnv.buildContext does not adjust these from build.DefaultContext
// (even though it should). So for now, this is OK to share, but we need to
// add logic for handling GOOS/GOARCH.
r.moduleCacheCache = moduleCacheCache
r.roots = append(r.roots, gopathwalk.Root{Path: r.moduleCacheDir, Type: gopathwalk.RootModuleCache}) r.roots = append(r.roots, gopathwalk.Root{Path: r.moduleCacheDir, Type: gopathwalk.RootModuleCache})
} }
r.scannedRoots = map[gopathwalk.Root]bool{} r.scannedRoots = map[gopathwalk.Root]bool{}
if r.moduleCacheCache == nil { if r.moduleCacheCache == nil {
r.moduleCacheCache = &dirInfoCache{ r.moduleCacheCache = NewDirInfoCache()
dirs: map[string]*directoryPackageInfo{},
listeners: map[*int]cacheListener{},
}
} }
if r.otherCache == nil { r.otherCache = NewDirInfoCache()
r.otherCache = &dirInfoCache{ return r, nil
dirs: map[string]*directoryPackageInfo{}, }
listeners: map[*int]cacheListener{},
} // gomodcacheForEnv returns the GOMODCACHE value to use based on the given env
// map, which must have GOMODCACHE and GOPATH populated.
//
// TODO(rfindley): this is defensive refactoring.
// 1. Is this even relevant anymore? Can't we just read GOMODCACHE.
// 2. Use this to separate module cache scanning from other scanning.
func gomodcacheForEnv(goenv map[string]string) string {
if gmc := goenv["GOMODCACHE"]; gmc != "" {
return gmc
} }
r.initialized = true gopaths := filepath.SplitList(goenv["GOPATH"])
return nil if len(gopaths) == 0 {
return ""
}
return filepath.Join(gopaths[0], "/pkg/mod")
} }
func (r *ModuleResolver) initAllMods() error { func (r *ModuleResolver) initAllMods() error {
@ -206,30 +263,82 @@ func (r *ModuleResolver) initAllMods() error {
return nil return nil
} }
func (r *ModuleResolver) ClearForNewScan() { // ClearForNewScan invalidates the last scan.
<-r.scanSema //
r.scannedRoots = map[gopathwalk.Root]bool{} // It preserves the set of roots, but forgets about the set of directories.
r.otherCache = &dirInfoCache{ // Though it forgets the set of module cache directories, it remembers their
dirs: map[string]*directoryPackageInfo{}, // contents, since they are assumed to be immutable.
listeners: map[*int]cacheListener{}, func (r *ModuleResolver) ClearForNewScan() Resolver {
} <-r.scanSema // acquire r, to guard scannedRoots
r.scanSema <- struct{}{} r2 := &ModuleResolver{
} env: r.env,
dummyVendorMod: r.dummyVendorMod,
moduleCacheDir: r.moduleCacheDir,
roots: r.roots,
mains: r.mains,
mainByDir: r.mainByDir,
modsByModPath: r.modsByModPath,
func (r *ModuleResolver) ClearForNewMod() { scanSema: make(chan struct{}, 1),
<-r.scanSema scannedRoots: make(map[gopathwalk.Root]bool),
*r = ModuleResolver{ otherCache: NewDirInfoCache(),
env: r.env,
moduleCacheCache: r.moduleCacheCache, moduleCacheCache: r.moduleCacheCache,
otherCache: r.otherCache,
scanSema: r.scanSema,
} }
r.init() r2.scanSema <- struct{}{} // r2 must start released
r.scanSema <- struct{}{} // Invalidate root scans. We don't need to invalidate module cache roots,
// because they are immutable.
// (We don't support a use case where GOMODCACHE is cleaned in the middle of
// e.g. a gopls session: the user must restart gopls to get accurate
// imports.)
//
// Scanning for new directories in GOMODCACHE should be handled elsewhere,
// via a call to ScanModuleCache.
for _, root := range r.roots {
if root.Type == gopathwalk.RootModuleCache && r.scannedRoots[root] {
r2.scannedRoots[root] = true
}
}
r.scanSema <- struct{}{} // release r
return r2
} }
// findPackage returns the module and directory that contains the package at // ClearModuleInfo invalidates resolver state that depends on go.mod file
// the given import path, or returns nil, "" if no module is in scope. // contents (essentially, the output of go list -m -json ...).
//
// Notably, it does not forget directory contents, which are reset
// asynchronously via ClearForNewScan.
//
// If the ProcessEnv is a GOPATH environment, ClearModuleInfo is a no op.
//
// TODO(rfindley): move this to a new env.go, consolidating ProcessEnv methods.
func (e *ProcessEnv) ClearModuleInfo() {
if r, ok := e.resolver.(*ModuleResolver); ok {
resolver, resolverErr := newModuleResolver(e, e.ModCache)
if resolverErr == nil {
<-r.scanSema // acquire (guards caches)
resolver.moduleCacheCache = r.moduleCacheCache
resolver.otherCache = r.otherCache
r.scanSema <- struct{}{} // release
}
e.resolver = resolver
e.resolverErr = resolverErr
}
}
// UpdateResolver sets the resolver for the ProcessEnv to use in imports
// operations. Only for use with the result of [Resolver.ClearForNewScan].
//
// TODO(rfindley): this awkward API is a result of the (arguably) inverted
// relationship between configuration and state described in the doc comment
// for [ProcessEnv].
func (e *ProcessEnv) UpdateResolver(r Resolver) {
e.resolver = r
e.resolverErr = nil
}
// findPackage returns the module and directory from within the main modules
// and their dependencies that contains the package at the given import path,
// or returns nil, "" if no module is in scope.
func (r *ModuleResolver) findPackage(importPath string) (*gocommand.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.
@ -295,10 +404,6 @@ func (r *ModuleResolver) cacheStore(info directoryPackageInfo) {
} }
} }
func (r *ModuleResolver) cacheKeys() []string {
return append(r.moduleCacheCache.Keys(), r.otherCache.Keys()...)
}
// cachePackageName caches the package name for a dir already in the cache. // cachePackageName caches the package name for a dir already in the cache.
func (r *ModuleResolver) cachePackageName(info directoryPackageInfo) (string, error) { func (r *ModuleResolver) cachePackageName(info directoryPackageInfo) (string, error) {
if info.rootType == gopathwalk.RootModuleCache { if info.rootType == gopathwalk.RootModuleCache {
@ -367,15 +472,15 @@ func (r *ModuleResolver) dirIsNestedModule(dir string, mod *gocommand.ModuleJSON
return modDir != mod.Dir return modDir != mod.Dir
} }
func (r *ModuleResolver) modInfo(dir string) (modDir string, modName string) { func readModName(modFile string) string {
readModName := func(modFile string) string { modBytes, err := os.ReadFile(modFile)
modBytes, err := os.ReadFile(modFile) if err != nil {
if err != nil { return ""
return ""
}
return modulePath(modBytes)
} }
return modulePath(modBytes)
}
func (r *ModuleResolver) modInfo(dir string) (modDir, modName string) {
if r.dirInModuleCache(dir) { if r.dirInModuleCache(dir) {
if matches := modCacheRegexp.FindStringSubmatch(dir); len(matches) == 3 { if matches := modCacheRegexp.FindStringSubmatch(dir); len(matches) == 3 {
index := strings.Index(dir, matches[1]+"@"+matches[2]) index := strings.Index(dir, matches[1]+"@"+matches[2])
@ -409,11 +514,9 @@ func (r *ModuleResolver) dirInModuleCache(dir string) bool {
} }
func (r *ModuleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) { func (r *ModuleResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
if err := r.init(); err != nil {
return nil, err
}
names := map[string]string{} names := map[string]string{}
for _, path := range importPaths { for _, path := range importPaths {
// TODO(rfindley): shouldn't this use the dirInfoCache?
_, packageDir := r.findPackage(path) _, packageDir := r.findPackage(path)
if packageDir == "" { if packageDir == "" {
continue continue
@ -431,10 +534,6 @@ func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error
ctx, done := event.Start(ctx, "imports.ModuleResolver.scan") ctx, done := event.Start(ctx, "imports.ModuleResolver.scan")
defer done() defer done()
if err := r.init(); err != nil {
return err
}
processDir := func(info directoryPackageInfo) { processDir := func(info directoryPackageInfo) {
// Skip this directory if we were not able to get the package information successfully. // Skip this directory if we were not able to get the package information successfully.
if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil { if scanned, err := info.reachedStatus(directoryScanned); !scanned || err != nil {
@ -444,18 +543,18 @@ func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error
if err != nil { if err != nil {
return return
} }
if !callback.dirFound(pkg) { if !callback.dirFound(pkg) {
return return
} }
pkg.packageName, err = r.cachePackageName(info) pkg.packageName, err = r.cachePackageName(info)
if err != nil { if err != nil {
return return
} }
if !callback.packageNameLoaded(pkg) { if !callback.packageNameLoaded(pkg) {
return return
} }
_, exports, err := r.loadExports(ctx, pkg, false) _, exports, err := r.loadExports(ctx, pkg, false)
if err != nil { if err != nil {
return return
@ -494,7 +593,6 @@ func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error
return packageScanned return packageScanned
} }
// Add anything new to the cache, and process it if we're still listening.
add := func(root gopathwalk.Root, dir string) { add := func(root gopathwalk.Root, dir string) {
r.cacheStore(r.scanDirForPackage(root, dir)) r.cacheStore(r.scanDirForPackage(root, dir))
} }
@ -509,9 +607,9 @@ func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error
select { select {
case <-ctx.Done(): case <-ctx.Done():
return return
case <-r.scanSema: case <-r.scanSema: // acquire
} }
defer func() { r.scanSema <- struct{}{} }() defer func() { r.scanSema <- struct{}{} }() // release
// We have the lock on r.scannedRoots, and no other scans can run. // We have the lock on r.scannedRoots, and no other scans can run.
for _, root := range roots { for _, root := range roots {
if ctx.Err() != nil { if ctx.Err() != nil {
@ -613,9 +711,6 @@ func (r *ModuleResolver) canonicalize(info directoryPackageInfo) (*pkg, error) {
} }
func (r *ModuleResolver) loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) { func (r *ModuleResolver) loadExports(ctx context.Context, pkg *pkg, includeTest bool) (string, []string, error) {
if err := r.init(); err != nil {
return "", nil, err
}
if info, ok := r.cacheLoad(pkg.dir); ok && !includeTest { if info, ok := r.cacheLoad(pkg.dir); ok && !includeTest {
return r.cacheExports(ctx, r.env, info) return r.cacheExports(ctx, r.env, info)
} }

View File

@ -7,8 +7,12 @@ package imports
import ( import (
"context" "context"
"fmt" "fmt"
"path"
"path/filepath"
"strings"
"sync" "sync"
"golang.org/x/mod/module"
"golang.org/x/tools/internal/gopathwalk" "golang.org/x/tools/internal/gopathwalk"
) )
@ -39,6 +43,8 @@ const (
exportsLoaded exportsLoaded
) )
// directoryPackageInfo holds (possibly incomplete) information about packages
// contained in a given directory.
type directoryPackageInfo struct { type directoryPackageInfo struct {
// status indicates the extent to which this struct has been filled in. // status indicates the extent to which this struct has been filled in.
status directoryPackageStatus status directoryPackageStatus
@ -63,7 +69,10 @@ type directoryPackageInfo struct {
packageName string // the package name, as declared in the source. packageName string // the package name, as declared in the source.
// Set when status >= exportsLoaded. // Set when status >= exportsLoaded.
// TODO(rfindley): it's hard to see this, but exports depend implicitly on
// the default build context GOOS and GOARCH.
//
// We can make this explicit, and key exports by GOOS, GOARCH.
exports []string exports []string
} }
@ -79,7 +88,7 @@ func (info *directoryPackageInfo) reachedStatus(target directoryPackageStatus) (
return true, nil return true, nil
} }
// dirInfoCache is a concurrency safe map for storing information about // DirInfoCache is a concurrency-safe map for storing information about
// directories that may contain packages. // directories that may contain packages.
// //
// The information in this cache is built incrementally. Entries are initialized in scan. // The information in this cache is built incrementally. Entries are initialized in scan.
@ -92,21 +101,26 @@ func (info *directoryPackageInfo) reachedStatus(target directoryPackageStatus) (
// The information in the cache is not expected to change for the cache's // The information in the cache is not expected to change for the cache's
// lifetime, so there is no protection against competing writes. Users should // lifetime, so there is no protection against competing writes. Users should
// take care not to hold the cache across changes to the underlying files. // take care not to hold the cache across changes to the underlying files.
// type DirInfoCache struct {
// TODO(suzmue): consider other concurrency strategies and data structures (RWLocks, sync.Map, etc)
type dirInfoCache struct {
mu sync.Mutex mu sync.Mutex
// dirs stores information about packages in directories, keyed by absolute path. // dirs stores information about packages in directories, keyed by absolute path.
dirs map[string]*directoryPackageInfo dirs map[string]*directoryPackageInfo
listeners map[*int]cacheListener listeners map[*int]cacheListener
} }
func NewDirInfoCache() *DirInfoCache {
return &DirInfoCache{
dirs: make(map[string]*directoryPackageInfo),
listeners: make(map[*int]cacheListener),
}
}
type cacheListener func(directoryPackageInfo) type cacheListener func(directoryPackageInfo)
// ScanAndListen calls listener on all the items in the cache, and on anything // ScanAndListen calls listener on all the items in the cache, and on anything
// newly added. The returned stop function waits for all in-flight callbacks to // newly added. The returned stop function waits for all in-flight callbacks to
// finish and blocks new ones. // finish and blocks new ones.
func (d *dirInfoCache) ScanAndListen(ctx context.Context, listener cacheListener) func() { func (d *DirInfoCache) ScanAndListen(ctx context.Context, listener cacheListener) func() {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
// Flushing out all the callbacks is tricky without knowing how many there // Flushing out all the callbacks is tricky without knowing how many there
@ -162,8 +176,10 @@ func (d *dirInfoCache) ScanAndListen(ctx context.Context, listener cacheListener
} }
// Store stores the package info for dir. // Store stores the package info for dir.
func (d *dirInfoCache) Store(dir string, info directoryPackageInfo) { func (d *DirInfoCache) Store(dir string, info directoryPackageInfo) {
d.mu.Lock() d.mu.Lock()
// TODO(rfindley, golang/go#59216): should we overwrite an existing entry?
// That seems incorrect as the cache should be idempotent.
_, old := d.dirs[dir] _, old := d.dirs[dir]
d.dirs[dir] = &info d.dirs[dir] = &info
var listeners []cacheListener var listeners []cacheListener
@ -180,7 +196,7 @@ func (d *dirInfoCache) Store(dir string, info directoryPackageInfo) {
} }
// Load returns a copy of the directoryPackageInfo for absolute directory dir. // Load returns a copy of the directoryPackageInfo for absolute directory dir.
func (d *dirInfoCache) Load(dir string) (directoryPackageInfo, bool) { func (d *DirInfoCache) Load(dir string) (directoryPackageInfo, bool) {
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock() defer d.mu.Unlock()
info, ok := d.dirs[dir] info, ok := d.dirs[dir]
@ -191,7 +207,7 @@ func (d *dirInfoCache) Load(dir string) (directoryPackageInfo, bool) {
} }
// Keys returns the keys currently present in d. // Keys returns the keys currently present in d.
func (d *dirInfoCache) Keys() (keys []string) { func (d *DirInfoCache) Keys() (keys []string) {
d.mu.Lock() d.mu.Lock()
defer d.mu.Unlock() defer d.mu.Unlock()
for key := range d.dirs { for key := range d.dirs {
@ -200,7 +216,7 @@ func (d *dirInfoCache) Keys() (keys []string) {
return keys return keys
} }
func (d *dirInfoCache) CachePackageName(info directoryPackageInfo) (string, error) { func (d *DirInfoCache) CachePackageName(info directoryPackageInfo) (string, error) {
if loaded, err := info.reachedStatus(nameLoaded); loaded { if loaded, err := info.reachedStatus(nameLoaded); loaded {
return info.packageName, err return info.packageName, err
} }
@ -213,7 +229,7 @@ func (d *dirInfoCache) CachePackageName(info directoryPackageInfo) (string, erro
return info.packageName, info.err return info.packageName, info.err
} }
func (d *dirInfoCache) CacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) { func (d *DirInfoCache) CacheExports(ctx context.Context, env *ProcessEnv, info directoryPackageInfo) (string, []string, error) {
if reached, _ := info.reachedStatus(exportsLoaded); reached { if reached, _ := info.reachedStatus(exportsLoaded); reached {
return info.packageName, info.exports, info.err return info.packageName, info.exports, info.err
} }
@ -234,3 +250,81 @@ func (d *dirInfoCache) CacheExports(ctx context.Context, env *ProcessEnv, info d
d.Store(info.dir, info) d.Store(info.dir, info)
return info.packageName, info.exports, info.err return info.packageName, info.exports, info.err
} }
// ScanModuleCache walks the given directory, which must be a GOMODCACHE value,
// for directory package information, storing the results in cache.
func ScanModuleCache(dir string, cache *DirInfoCache, logf func(string, ...any)) {
// Note(rfindley): it's hard to see, but this function attempts to implement
// just the side effects on cache of calling PrimeCache with a ProcessEnv
// that has the given dir as its GOMODCACHE.
//
// Teasing out the control flow, we see that we can avoid any handling of
// vendor/ and can infer module info entirely from the path, simplifying the
// logic here.
root := gopathwalk.Root{
Path: filepath.Clean(dir),
Type: gopathwalk.RootModuleCache,
}
directoryInfo := func(root gopathwalk.Root, dir string) directoryPackageInfo {
// This is a copy of ModuleResolver.scanDirForPackage, trimmed down to
// logic that applies to a module cache directory.
subdir := ""
if dir != root.Path {
subdir = dir[len(root.Path)+len("/"):]
}
matches := modCacheRegexp.FindStringSubmatch(subdir)
if len(matches) == 0 {
return directoryPackageInfo{
status: directoryScanned,
err: fmt.Errorf("invalid module cache path: %v", subdir),
}
}
modPath, err := module.UnescapePath(filepath.ToSlash(matches[1]))
if err != nil {
if logf != nil {
logf("decoding module cache path %q: %v", subdir, err)
}
return directoryPackageInfo{
status: directoryScanned,
err: fmt.Errorf("decoding module cache path %q: %v", subdir, err),
}
}
importPath := path.Join(modPath, filepath.ToSlash(matches[3]))
index := strings.Index(dir, matches[1]+"@"+matches[2])
modDir := filepath.Join(dir[:index], matches[1]+"@"+matches[2])
modName := readModName(filepath.Join(modDir, "go.mod"))
return directoryPackageInfo{
status: directoryScanned,
dir: dir,
rootType: root.Type,
nonCanonicalImportPath: importPath,
moduleDir: modDir,
moduleName: modName,
}
}
add := func(root gopathwalk.Root, dir string) {
info := directoryInfo(root, dir)
cache.Store(info.dir, info)
}
skip := func(_ gopathwalk.Root, dir string) bool {
// Skip directories that have already been scanned.
//
// Note that gopathwalk only adds "package" directories, which must contain
// a .go file, and all such package directories in the module cache are
// immutable. So if we can load a dir, it can be skipped.
info, ok := cache.Load(dir)
if !ok {
return false
}
packageScanned, _ := info.reachedStatus(directoryScanned)
return packageScanned
}
gopathwalk.WalkSkip([]gopathwalk.Root{root}, add, skip, gopathwalk.Options{Logf: logf, ModulesEnabled: true})
}

View File

@ -151,6 +151,7 @@ var stdlib = map[string][]string{
"cmp": { "cmp": {
"Compare", "Compare",
"Less", "Less",
"Or",
"Ordered", "Ordered",
}, },
"compress/bzip2": { "compress/bzip2": {
@ -632,6 +633,8 @@ var stdlib = map[string][]string{
"NameMismatch", "NameMismatch",
"NewCertPool", "NewCertPool",
"NotAuthorizedToSign", "NotAuthorizedToSign",
"OID",
"OIDFromInts",
"PEMCipher", "PEMCipher",
"PEMCipher3DES", "PEMCipher3DES",
"PEMCipherAES128", "PEMCipherAES128",
@ -706,6 +709,7 @@ var stdlib = map[string][]string{
"LevelWriteCommitted", "LevelWriteCommitted",
"Named", "Named",
"NamedArg", "NamedArg",
"Null",
"NullBool", "NullBool",
"NullByte", "NullByte",
"NullFloat64", "NullFloat64",
@ -1921,6 +1925,7 @@ var stdlib = map[string][]string{
"R_LARCH_32", "R_LARCH_32",
"R_LARCH_32_PCREL", "R_LARCH_32_PCREL",
"R_LARCH_64", "R_LARCH_64",
"R_LARCH_64_PCREL",
"R_LARCH_ABS64_HI12", "R_LARCH_ABS64_HI12",
"R_LARCH_ABS64_LO20", "R_LARCH_ABS64_LO20",
"R_LARCH_ABS_HI20", "R_LARCH_ABS_HI20",
@ -1928,12 +1933,17 @@ var stdlib = map[string][]string{
"R_LARCH_ADD16", "R_LARCH_ADD16",
"R_LARCH_ADD24", "R_LARCH_ADD24",
"R_LARCH_ADD32", "R_LARCH_ADD32",
"R_LARCH_ADD6",
"R_LARCH_ADD64", "R_LARCH_ADD64",
"R_LARCH_ADD8", "R_LARCH_ADD8",
"R_LARCH_ADD_ULEB128",
"R_LARCH_ALIGN",
"R_LARCH_B16", "R_LARCH_B16",
"R_LARCH_B21", "R_LARCH_B21",
"R_LARCH_B26", "R_LARCH_B26",
"R_LARCH_CFA",
"R_LARCH_COPY", "R_LARCH_COPY",
"R_LARCH_DELETE",
"R_LARCH_GNU_VTENTRY", "R_LARCH_GNU_VTENTRY",
"R_LARCH_GNU_VTINHERIT", "R_LARCH_GNU_VTINHERIT",
"R_LARCH_GOT64_HI12", "R_LARCH_GOT64_HI12",
@ -1953,6 +1963,7 @@ var stdlib = map[string][]string{
"R_LARCH_PCALA64_LO20", "R_LARCH_PCALA64_LO20",
"R_LARCH_PCALA_HI20", "R_LARCH_PCALA_HI20",
"R_LARCH_PCALA_LO12", "R_LARCH_PCALA_LO12",
"R_LARCH_PCREL20_S2",
"R_LARCH_RELATIVE", "R_LARCH_RELATIVE",
"R_LARCH_RELAX", "R_LARCH_RELAX",
"R_LARCH_SOP_ADD", "R_LARCH_SOP_ADD",
@ -1983,8 +1994,10 @@ var stdlib = map[string][]string{
"R_LARCH_SUB16", "R_LARCH_SUB16",
"R_LARCH_SUB24", "R_LARCH_SUB24",
"R_LARCH_SUB32", "R_LARCH_SUB32",
"R_LARCH_SUB6",
"R_LARCH_SUB64", "R_LARCH_SUB64",
"R_LARCH_SUB8", "R_LARCH_SUB8",
"R_LARCH_SUB_ULEB128",
"R_LARCH_TLS_DTPMOD32", "R_LARCH_TLS_DTPMOD32",
"R_LARCH_TLS_DTPMOD64", "R_LARCH_TLS_DTPMOD64",
"R_LARCH_TLS_DTPREL32", "R_LARCH_TLS_DTPREL32",
@ -2035,6 +2048,7 @@ var stdlib = map[string][]string{
"R_MIPS_LO16", "R_MIPS_LO16",
"R_MIPS_NONE", "R_MIPS_NONE",
"R_MIPS_PC16", "R_MIPS_PC16",
"R_MIPS_PC32",
"R_MIPS_PJUMP", "R_MIPS_PJUMP",
"R_MIPS_REL16", "R_MIPS_REL16",
"R_MIPS_REL32", "R_MIPS_REL32",
@ -2952,6 +2966,8 @@ var stdlib = map[string][]string{
"RegisterName", "RegisterName",
}, },
"encoding/hex": { "encoding/hex": {
"AppendDecode",
"AppendEncode",
"Decode", "Decode",
"DecodeString", "DecodeString",
"DecodedLen", "DecodedLen",
@ -3233,6 +3249,7 @@ var stdlib = map[string][]string{
"TypeSpec", "TypeSpec",
"TypeSwitchStmt", "TypeSwitchStmt",
"UnaryExpr", "UnaryExpr",
"Unparen",
"ValueSpec", "ValueSpec",
"Var", "Var",
"Visitor", "Visitor",
@ -3492,6 +3509,7 @@ var stdlib = map[string][]string{
"XOR_ASSIGN", "XOR_ASSIGN",
}, },
"go/types": { "go/types": {
"Alias",
"ArgumentError", "ArgumentError",
"Array", "Array",
"AssertableTo", "AssertableTo",
@ -3559,6 +3577,7 @@ var stdlib = map[string][]string{
"MethodVal", "MethodVal",
"MissingMethod", "MissingMethod",
"Named", "Named",
"NewAlias",
"NewArray", "NewArray",
"NewChan", "NewChan",
"NewChecker", "NewChecker",
@ -3627,6 +3646,7 @@ var stdlib = map[string][]string{
"Uint64", "Uint64",
"Uint8", "Uint8",
"Uintptr", "Uintptr",
"Unalias",
"Union", "Union",
"Universe", "Universe",
"Unsafe", "Unsafe",
@ -3643,6 +3663,11 @@ var stdlib = map[string][]string{
"WriteSignature", "WriteSignature",
"WriteType", "WriteType",
}, },
"go/version": {
"Compare",
"IsValid",
"Lang",
},
"hash": { "hash": {
"Hash", "Hash",
"Hash32", "Hash32",
@ -4078,6 +4103,7 @@ var stdlib = map[string][]string{
"NewTextHandler", "NewTextHandler",
"Record", "Record",
"SetDefault", "SetDefault",
"SetLogLoggerLevel",
"Source", "Source",
"SourceKey", "SourceKey",
"String", "String",
@ -4367,6 +4393,35 @@ var stdlib = map[string][]string{
"Uint64", "Uint64",
"Zipf", "Zipf",
}, },
"math/rand/v2": {
"ChaCha8",
"ExpFloat64",
"Float32",
"Float64",
"Int",
"Int32",
"Int32N",
"Int64",
"Int64N",
"IntN",
"N",
"New",
"NewChaCha8",
"NewPCG",
"NewZipf",
"NormFloat64",
"PCG",
"Perm",
"Rand",
"Shuffle",
"Source",
"Uint32",
"Uint32N",
"Uint64",
"Uint64N",
"UintN",
"Zipf",
},
"mime": { "mime": {
"AddExtensionType", "AddExtensionType",
"BEncoding", "BEncoding",
@ -4540,6 +4595,7 @@ var stdlib = map[string][]string{
"FS", "FS",
"File", "File",
"FileServer", "FileServer",
"FileServerFS",
"FileSystem", "FileSystem",
"Flusher", "Flusher",
"Get", "Get",
@ -4566,6 +4622,7 @@ var stdlib = map[string][]string{
"MethodPut", "MethodPut",
"MethodTrace", "MethodTrace",
"NewFileTransport", "NewFileTransport",
"NewFileTransportFS",
"NewRequest", "NewRequest",
"NewRequestWithContext", "NewRequestWithContext",
"NewResponseController", "NewResponseController",
@ -4599,6 +4656,7 @@ var stdlib = map[string][]string{
"Serve", "Serve",
"ServeContent", "ServeContent",
"ServeFile", "ServeFile",
"ServeFileFS",
"ServeMux", "ServeMux",
"ServeTLS", "ServeTLS",
"Server", "Server",
@ -5106,6 +5164,7 @@ var stdlib = map[string][]string{
"StructTag", "StructTag",
"Swapper", "Swapper",
"Type", "Type",
"TypeFor",
"TypeOf", "TypeOf",
"Uint", "Uint",
"Uint16", "Uint16",
@ -5342,6 +5401,7 @@ var stdlib = map[string][]string{
"CompactFunc", "CompactFunc",
"Compare", "Compare",
"CompareFunc", "CompareFunc",
"Concat",
"Contains", "Contains",
"ContainsFunc", "ContainsFunc",
"Delete", "Delete",
@ -10824,6 +10884,7 @@ var stdlib = map[string][]string{
"Value", "Value",
}, },
"testing/slogtest": { "testing/slogtest": {
"Run",
"TestHandler", "TestHandler",
}, },
"text/scanner": { "text/scanner": {

View File

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

View File

@ -2,20 +2,10 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package typeparams contains common utilities for writing tools that interact // Package typeparams contains common utilities for writing tools that
// with generic Go code, as introduced with Go 1.18. // interact with generic Go code, as introduced with Go 1.18. It
// // supplements the standard library APIs. Notably, the StructuralTerms
// Many of the types and functions in this package are proxies for the new APIs // API computes a minimal representation of the structural
// introduced in the standard library with Go 1.18. For example, the
// typeparams.Union type is an alias for go/types.Union, and the ForTypeSpec
// function returns the value of the go/ast.TypeSpec.TypeParams field. At Go
// versions older than 1.18 these helpers are implemented as stubs, allowing
// users of this package to write code that handles generic constructs inline,
// even if the Go version being used to compile does not support generics.
//
// Additionally, this package contains common utilities for working with the
// new generic constructs, to supplement the standard library APIs. Notably,
// the StructuralTerms API computes a minimal representation of the structural
// restrictions on a type parameter. // restrictions on a type parameter.
// //
// An external version of these APIs is available in the // An external version of these APIs is available in the
@ -27,6 +17,9 @@ import (
"go/ast" "go/ast"
"go/token" "go/token"
"go/types" "go/types"
"golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typesinternal"
) )
// UnpackIndexExpr extracts data from AST nodes that represent index // UnpackIndexExpr extracts data from AST nodes that represent index
@ -72,9 +65,9 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke
} }
} }
// IsTypeParam reports whether t is a type parameter. // IsTypeParam reports whether t is a type parameter (or an alias of one).
func IsTypeParam(t types.Type) bool { func IsTypeParam(t types.Type) bool {
_, ok := t.(*types.TypeParam) _, ok := aliases.Unalias(t).(*types.TypeParam)
return ok return ok
} }
@ -90,13 +83,8 @@ func OriginMethod(fn *types.Func) *types.Func {
if recv == nil { if recv == nil {
return fn return fn
} }
base := recv.Type() _, named := typesinternal.ReceiverNamed(recv)
p, isPtr := base.(*types.Pointer) if named == nil {
if isPtr {
base = p.Elem()
}
named, isNamed := base.(*types.Named)
if !isNamed {
// Receiver is a *types.Interface. // Receiver is a *types.Interface.
return fn return fn
} }
@ -158,6 +146,9 @@ func OriginMethod(fn *types.Func) *types.Func {
// In this case, GenericAssignableTo reports that instantiations of Container // In this case, GenericAssignableTo reports that instantiations of Container
// are assignable to the corresponding instantiation of Interface. // are assignable to the corresponding instantiation of Interface.
func GenericAssignableTo(ctxt *types.Context, V, T types.Type) bool { func GenericAssignableTo(ctxt *types.Context, V, T types.Type) bool {
V = aliases.Unalias(V)
T = aliases.Unalias(T)
// If V and T are not both named, or do not have matching non-empty type // If V and T are not both named, or do not have matching non-empty type
// parameter lists, fall back on types.AssignableTo. // parameter lists, fall back on types.AssignableTo.

View File

@ -5,7 +5,10 @@
package typeparams package typeparams
import ( import (
"fmt"
"go/types" "go/types"
"golang.org/x/tools/internal/aliases"
) )
// CoreType returns the core type of T or nil if T does not have a core type. // CoreType returns the core type of T or nil if T does not have a core type.
@ -109,7 +112,7 @@ func CoreType(T types.Type) types.Type {
// _NormalTerms makes no guarantees about the order of terms, except that it // _NormalTerms makes no guarantees about the order of terms, except that it
// is deterministic. // is deterministic.
func _NormalTerms(typ types.Type) ([]*types.Term, error) { func _NormalTerms(typ types.Type) ([]*types.Term, error) {
switch typ := typ.(type) { switch typ := aliases.Unalias(typ).(type) {
case *types.TypeParam: case *types.TypeParam:
return StructuralTerms(typ) return StructuralTerms(typ)
case *types.Union: case *types.Union:
@ -120,3 +123,15 @@ func _NormalTerms(typ types.Type) ([]*types.Term, error) {
return []*types.Term{types.NewTerm(false, typ)}, nil return []*types.Term{types.NewTerm(false, typ)}, nil
} }
} }
// MustDeref returns the type of the variable pointed to by t.
// It panics if t's core type is not a pointer.
//
// TODO(adonovan): ideally this would live in typesinternal, but that
// creates an import cycle. Move there when we melt this package down.
func MustDeref(t types.Type) types.Type {
if ptr, ok := CoreType(t).(*types.Pointer); ok {
return ptr.Elem()
}
panic(fmt.Sprintf("%v is not a pointer", t))
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -21,7 +21,7 @@ import (
"google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/reflect/protoregistry"
) )
// Unmarshal reads the given []byte into the given proto.Message. // Unmarshal reads the given []byte into the given [proto.Message].
// The provided message must be mutable (e.g., a non-nil pointer to a message). // The provided message must be mutable (e.g., a non-nil pointer to a message).
func Unmarshal(b []byte, m proto.Message) error { func Unmarshal(b []byte, m proto.Message) error {
return UnmarshalOptions{}.Unmarshal(b, m) return UnmarshalOptions{}.Unmarshal(b, m)
@ -51,7 +51,7 @@ type UnmarshalOptions struct {
} }
} }
// Unmarshal reads the given []byte and populates the given proto.Message // Unmarshal reads the given []byte and populates the given [proto.Message]
// using options in the UnmarshalOptions object. // using options in the UnmarshalOptions object.
// The provided message must be mutable (e.g., a non-nil pointer to a message). // The provided message must be mutable (e.g., a non-nil pointer to a message).
func (o UnmarshalOptions) Unmarshal(b []byte, m proto.Message) error { func (o UnmarshalOptions) Unmarshal(b []byte, m proto.Message) error {
@ -739,7 +739,9 @@ func (d decoder) skipValue() error {
case text.ListClose: case text.ListClose:
return nil return nil
case text.MessageOpen: case text.MessageOpen:
return d.skipMessageValue() if err := d.skipMessageValue(); err != nil {
return err
}
default: default:
// Skip items. This will not validate whether skipped values are // Skip items. This will not validate whether skipped values are
// of the same type or not, same behavior as C++ // of the same type or not, same behavior as C++

View File

@ -33,7 +33,7 @@ func Format(m proto.Message) string {
return MarshalOptions{Multiline: true}.Format(m) return MarshalOptions{Multiline: true}.Format(m)
} }
// Marshal writes the given proto.Message in textproto format using default // Marshal writes the given [proto.Message] in textproto format using default
// options. Do not depend on the output being stable. It may change over time // options. Do not depend on the output being stable. It may change over time
// across different versions of the program. // across different versions of the program.
func Marshal(m proto.Message) ([]byte, error) { func Marshal(m proto.Message) ([]byte, error) {
@ -97,17 +97,23 @@ func (o MarshalOptions) Format(m proto.Message) string {
return string(b) return string(b)
} }
// Marshal writes the given proto.Message in textproto format using options in // Marshal writes the given [proto.Message] in textproto format using options in
// MarshalOptions object. Do not depend on the output being stable. It may // MarshalOptions object. Do not depend on the output being stable. It may
// change over time across different versions of the program. // change over time across different versions of the program.
func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) { func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
return o.marshal(m) return o.marshal(nil, m)
}
// MarshalAppend appends the textproto format encoding of m to b,
// returning the result.
func (o MarshalOptions) MarshalAppend(b []byte, m proto.Message) ([]byte, error) {
return o.marshal(b, m)
} }
// marshal is a centralized function that all marshal operations go through. // marshal is a centralized function that all marshal operations go through.
// For profiling purposes, avoid changing the name of this function or // For profiling purposes, avoid changing the name of this function or
// introducing other code paths for marshal that do not go through this. // introducing other code paths for marshal that do not go through this.
func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) { func (o MarshalOptions) marshal(b []byte, m proto.Message) ([]byte, error) {
var delims = [2]byte{'{', '}'} var delims = [2]byte{'{', '}'}
if o.Multiline && o.Indent == "" { if o.Multiline && o.Indent == "" {
@ -117,7 +123,7 @@ func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) {
o.Resolver = protoregistry.GlobalTypes o.Resolver = protoregistry.GlobalTypes
} }
internalEnc, err := text.NewEncoder(o.Indent, delims, o.EmitASCII) internalEnc, err := text.NewEncoder(b, o.Indent, delims, o.EmitASCII)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -125,7 +131,7 @@ func (o MarshalOptions) marshal(m proto.Message) ([]byte, error) {
// Treat nil message interface as an empty message, // Treat nil message interface as an empty message,
// in which case there is nothing to output. // in which case there is nothing to output.
if m == nil { if m == nil {
return []byte{}, nil return b, nil
} }
enc := encoder{internalEnc, o} enc := encoder{internalEnc, o}

View File

@ -6,7 +6,7 @@
// See https://protobuf.dev/programming-guides/encoding. // See https://protobuf.dev/programming-guides/encoding.
// //
// For marshaling and unmarshaling entire protobuf messages, // For marshaling and unmarshaling entire protobuf messages,
// use the "google.golang.org/protobuf/proto" package instead. // use the [google.golang.org/protobuf/proto] package instead.
package protowire package protowire
import ( import (
@ -87,7 +87,7 @@ func ParseError(n int) error {
// ConsumeField parses an entire field record (both tag and value) and returns // ConsumeField parses an entire field record (both tag and value) and returns
// the field number, the wire type, and the total length. // the field number, the wire type, and the total length.
// This returns a negative length upon an error (see ParseError). // This returns a negative length upon an error (see [ParseError]).
// //
// The total length includes the tag header and the end group marker (if the // The total length includes the tag header and the end group marker (if the
// field is a group). // field is a group).
@ -104,8 +104,8 @@ func ConsumeField(b []byte) (Number, Type, int) {
} }
// ConsumeFieldValue parses a field value and returns its length. // ConsumeFieldValue parses a field value and returns its length.
// This assumes that the field Number and wire Type have already been parsed. // This assumes that the field [Number] and wire [Type] have already been parsed.
// This returns a negative length upon an error (see ParseError). // This returns a negative length upon an error (see [ParseError]).
// //
// When parsing a group, the length includes the end group marker and // When parsing a group, the length includes the end group marker and
// the end group is verified to match the starting field number. // the end group is verified to match the starting field number.
@ -164,7 +164,7 @@ func AppendTag(b []byte, num Number, typ Type) []byte {
} }
// ConsumeTag parses b as a varint-encoded tag, reporting its length. // ConsumeTag parses b as a varint-encoded tag, reporting its length.
// This returns a negative length upon an error (see ParseError). // This returns a negative length upon an error (see [ParseError]).
func ConsumeTag(b []byte) (Number, Type, int) { func ConsumeTag(b []byte) (Number, Type, int) {
v, n := ConsumeVarint(b) v, n := ConsumeVarint(b)
if n < 0 { if n < 0 {
@ -263,7 +263,7 @@ func AppendVarint(b []byte, v uint64) []byte {
} }
// ConsumeVarint parses b as a varint-encoded uint64, reporting its length. // ConsumeVarint parses b as a varint-encoded uint64, reporting its length.
// This returns a negative length upon an error (see ParseError). // This returns a negative length upon an error (see [ParseError]).
func ConsumeVarint(b []byte) (v uint64, n int) { func ConsumeVarint(b []byte) (v uint64, n int) {
var y uint64 var y uint64
if len(b) <= 0 { if len(b) <= 0 {
@ -384,7 +384,7 @@ func AppendFixed32(b []byte, v uint32) []byte {
} }
// ConsumeFixed32 parses b as a little-endian uint32, reporting its length. // ConsumeFixed32 parses b as a little-endian uint32, reporting its length.
// This returns a negative length upon an error (see ParseError). // This returns a negative length upon an error (see [ParseError]).
func ConsumeFixed32(b []byte) (v uint32, n int) { func ConsumeFixed32(b []byte) (v uint32, n int) {
if len(b) < 4 { if len(b) < 4 {
return 0, errCodeTruncated return 0, errCodeTruncated
@ -412,7 +412,7 @@ func AppendFixed64(b []byte, v uint64) []byte {
} }
// ConsumeFixed64 parses b as a little-endian uint64, reporting its length. // ConsumeFixed64 parses b as a little-endian uint64, reporting its length.
// This returns a negative length upon an error (see ParseError). // This returns a negative length upon an error (see [ParseError]).
func ConsumeFixed64(b []byte) (v uint64, n int) { func ConsumeFixed64(b []byte) (v uint64, n int) {
if len(b) < 8 { if len(b) < 8 {
return 0, errCodeTruncated return 0, errCodeTruncated
@ -432,7 +432,7 @@ func AppendBytes(b []byte, v []byte) []byte {
} }
// ConsumeBytes parses b as a length-prefixed bytes value, reporting its length. // ConsumeBytes parses b as a length-prefixed bytes value, reporting its length.
// This returns a negative length upon an error (see ParseError). // This returns a negative length upon an error (see [ParseError]).
func ConsumeBytes(b []byte) (v []byte, n int) { func ConsumeBytes(b []byte) (v []byte, n int) {
m, n := ConsumeVarint(b) m, n := ConsumeVarint(b)
if n < 0 { if n < 0 {
@ -456,7 +456,7 @@ func AppendString(b []byte, v string) []byte {
} }
// ConsumeString parses b as a length-prefixed bytes value, reporting its length. // ConsumeString parses b as a length-prefixed bytes value, reporting its length.
// This returns a negative length upon an error (see ParseError). // This returns a negative length upon an error (see [ParseError]).
func ConsumeString(b []byte) (v string, n int) { func ConsumeString(b []byte) (v string, n int) {
bb, n := ConsumeBytes(b) bb, n := ConsumeBytes(b)
return string(bb), n return string(bb), n
@ -471,7 +471,7 @@ func AppendGroup(b []byte, num Number, v []byte) []byte {
// ConsumeGroup parses b as a group value until the trailing end group marker, // ConsumeGroup parses b as a group value until the trailing end group marker,
// and verifies that the end marker matches the provided num. The value v // and verifies that the end marker matches the provided num. The value v
// does not contain the end marker, while the length does contain the end marker. // does not contain the end marker, while the length does contain the end marker.
// This returns a negative length upon an error (see ParseError). // This returns a negative length upon an error (see [ParseError]).
func ConsumeGroup(num Number, b []byte) (v []byte, n int) { func ConsumeGroup(num Number, b []byte) (v []byte, n int) {
n = ConsumeFieldValue(num, StartGroupType, b) n = ConsumeFieldValue(num, StartGroupType, b)
if n < 0 { if n < 0 {
@ -495,8 +495,8 @@ func SizeGroup(num Number, n int) int {
return n + SizeTag(num) return n + SizeTag(num)
} }
// DecodeTag decodes the field Number and wire Type from its unified form. // DecodeTag decodes the field [Number] and wire [Type] from its unified form.
// The Number is -1 if the decoded field number overflows int32. // The [Number] is -1 if the decoded field number overflows int32.
// Other than overflow, this does not check for field number validity. // Other than overflow, this does not check for field number validity.
func DecodeTag(x uint64) (Number, Type) { func DecodeTag(x uint64) (Number, Type) {
// NOTE: MessageSet allows for larger field numbers than normal. // NOTE: MessageSet allows for larger field numbers than normal.
@ -506,7 +506,7 @@ func DecodeTag(x uint64) (Number, Type) {
return Number(x >> 3), Type(x & 7) return Number(x >> 3), Type(x & 7)
} }
// EncodeTag encodes the field Number and wire Type into its unified form. // EncodeTag encodes the field [Number] and wire [Type] into its unified form.
func EncodeTag(num Number, typ Type) uint64 { func EncodeTag(num Number, typ Type) uint64 {
return uint64(num)<<3 | uint64(typ&7) return uint64(num)<<3 | uint64(typ&7)
} }

View File

@ -83,7 +83,13 @@ func formatListOpt(vs list, isRoot, allowMulti bool) string {
case protoreflect.FileImports: case protoreflect.FileImports:
for i := 0; i < vs.Len(); i++ { for i := 0; i < vs.Len(); i++ {
var rs records var rs records
rs.Append(reflect.ValueOf(vs.Get(i)), "Path", "Package", "IsPublic", "IsWeak") rv := reflect.ValueOf(vs.Get(i))
rs.Append(rv, []methodAndName{
{rv.MethodByName("Path"), "Path"},
{rv.MethodByName("Package"), "Package"},
{rv.MethodByName("IsPublic"), "IsPublic"},
{rv.MethodByName("IsWeak"), "IsWeak"},
}...)
ss = append(ss, "{"+rs.Join()+"}") ss = append(ss, "{"+rs.Join()+"}")
} }
return start + joinStrings(ss, allowMulti) + end return start + joinStrings(ss, allowMulti) + end
@ -92,34 +98,26 @@ func formatListOpt(vs list, isRoot, allowMulti bool) string {
for i := 0; i < vs.Len(); i++ { for i := 0; i < vs.Len(); i++ {
m := reflect.ValueOf(vs).MethodByName("Get") m := reflect.ValueOf(vs).MethodByName("Get")
v := m.Call([]reflect.Value{reflect.ValueOf(i)})[0].Interface() v := m.Call([]reflect.Value{reflect.ValueOf(i)})[0].Interface()
ss = append(ss, formatDescOpt(v.(protoreflect.Descriptor), false, allowMulti && !isEnumValue)) ss = append(ss, formatDescOpt(v.(protoreflect.Descriptor), false, allowMulti && !isEnumValue, nil))
} }
return start + joinStrings(ss, allowMulti && isEnumValue) + end return start + joinStrings(ss, allowMulti && isEnumValue) + end
} }
} }
// descriptorAccessors is a list of accessors to print for each descriptor. type methodAndName struct {
// method reflect.Value
// Do not print all accessors since some contain redundant information, name string
// while others are pointers that we do not want to follow since the descriptor
// is actually a cyclic graph.
//
// Using a list allows us to print the accessors in a sensible order.
var descriptorAccessors = map[reflect.Type][]string{
reflect.TypeOf((*protoreflect.FileDescriptor)(nil)).Elem(): {"Path", "Package", "Imports", "Messages", "Enums", "Extensions", "Services"},
reflect.TypeOf((*protoreflect.MessageDescriptor)(nil)).Elem(): {"IsMapEntry", "Fields", "Oneofs", "ReservedNames", "ReservedRanges", "RequiredNumbers", "ExtensionRanges", "Messages", "Enums", "Extensions"},
reflect.TypeOf((*protoreflect.FieldDescriptor)(nil)).Elem(): {"Number", "Cardinality", "Kind", "HasJSONName", "JSONName", "HasPresence", "IsExtension", "IsPacked", "IsWeak", "IsList", "IsMap", "MapKey", "MapValue", "HasDefault", "Default", "ContainingOneof", "ContainingMessage", "Message", "Enum"},
reflect.TypeOf((*protoreflect.OneofDescriptor)(nil)).Elem(): {"Fields"}, // not directly used; must keep in sync with formatDescOpt
reflect.TypeOf((*protoreflect.EnumDescriptor)(nil)).Elem(): {"Values", "ReservedNames", "ReservedRanges"},
reflect.TypeOf((*protoreflect.EnumValueDescriptor)(nil)).Elem(): {"Number"},
reflect.TypeOf((*protoreflect.ServiceDescriptor)(nil)).Elem(): {"Methods"},
reflect.TypeOf((*protoreflect.MethodDescriptor)(nil)).Elem(): {"Input", "Output", "IsStreamingClient", "IsStreamingServer"},
} }
func FormatDesc(s fmt.State, r rune, t protoreflect.Descriptor) { func FormatDesc(s fmt.State, r rune, t protoreflect.Descriptor) {
io.WriteString(s, formatDescOpt(t, true, r == 'v' && (s.Flag('+') || s.Flag('#')))) io.WriteString(s, formatDescOpt(t, true, r == 'v' && (s.Flag('+') || s.Flag('#')), nil))
} }
func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool) string {
func InternalFormatDescOptForTesting(t protoreflect.Descriptor, isRoot, allowMulti bool, record func(string)) string {
return formatDescOpt(t, isRoot, allowMulti, record)
}
func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool, record func(string)) string {
rv := reflect.ValueOf(t) rv := reflect.ValueOf(t)
rt := rv.MethodByName("ProtoType").Type().In(0) rt := rv.MethodByName("ProtoType").Type().In(0)
@ -129,26 +127,60 @@ func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool) string {
} }
_, isFile := t.(protoreflect.FileDescriptor) _, isFile := t.(protoreflect.FileDescriptor)
rs := records{allowMulti: allowMulti} rs := records{
allowMulti: allowMulti,
record: record,
}
if t.IsPlaceholder() { if t.IsPlaceholder() {
if isFile { if isFile {
rs.Append(rv, "Path", "Package", "IsPlaceholder") rs.Append(rv, []methodAndName{
{rv.MethodByName("Path"), "Path"},
{rv.MethodByName("Package"), "Package"},
{rv.MethodByName("IsPlaceholder"), "IsPlaceholder"},
}...)
} else { } else {
rs.Append(rv, "FullName", "IsPlaceholder") rs.Append(rv, []methodAndName{
{rv.MethodByName("FullName"), "FullName"},
{rv.MethodByName("IsPlaceholder"), "IsPlaceholder"},
}...)
} }
} else { } else {
switch { switch {
case isFile: case isFile:
rs.Append(rv, "Syntax") rs.Append(rv, methodAndName{rv.MethodByName("Syntax"), "Syntax"})
case isRoot: case isRoot:
rs.Append(rv, "Syntax", "FullName") rs.Append(rv, []methodAndName{
{rv.MethodByName("Syntax"), "Syntax"},
{rv.MethodByName("FullName"), "FullName"},
}...)
default: default:
rs.Append(rv, "Name") rs.Append(rv, methodAndName{rv.MethodByName("Name"), "Name"})
} }
switch t := t.(type) { switch t := t.(type) {
case protoreflect.FieldDescriptor: case protoreflect.FieldDescriptor:
for _, s := range descriptorAccessors[rt] { accessors := []methodAndName{
switch s { {rv.MethodByName("Number"), "Number"},
{rv.MethodByName("Cardinality"), "Cardinality"},
{rv.MethodByName("Kind"), "Kind"},
{rv.MethodByName("HasJSONName"), "HasJSONName"},
{rv.MethodByName("JSONName"), "JSONName"},
{rv.MethodByName("HasPresence"), "HasPresence"},
{rv.MethodByName("IsExtension"), "IsExtension"},
{rv.MethodByName("IsPacked"), "IsPacked"},
{rv.MethodByName("IsWeak"), "IsWeak"},
{rv.MethodByName("IsList"), "IsList"},
{rv.MethodByName("IsMap"), "IsMap"},
{rv.MethodByName("MapKey"), "MapKey"},
{rv.MethodByName("MapValue"), "MapValue"},
{rv.MethodByName("HasDefault"), "HasDefault"},
{rv.MethodByName("Default"), "Default"},
{rv.MethodByName("ContainingOneof"), "ContainingOneof"},
{rv.MethodByName("ContainingMessage"), "ContainingMessage"},
{rv.MethodByName("Message"), "Message"},
{rv.MethodByName("Enum"), "Enum"},
}
for _, s := range accessors {
switch s.name {
case "MapKey": case "MapKey":
if k := t.MapKey(); k != nil { if k := t.MapKey(); k != nil {
rs.recs = append(rs.recs, [2]string{"MapKey", k.Kind().String()}) rs.recs = append(rs.recs, [2]string{"MapKey", k.Kind().String()})
@ -157,20 +189,20 @@ func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool) string {
if v := t.MapValue(); v != nil { if v := t.MapValue(); v != nil {
switch v.Kind() { switch v.Kind() {
case protoreflect.EnumKind: case protoreflect.EnumKind:
rs.recs = append(rs.recs, [2]string{"MapValue", string(v.Enum().FullName())}) rs.AppendRecs("MapValue", [2]string{"MapValue", string(v.Enum().FullName())})
case protoreflect.MessageKind, protoreflect.GroupKind: case protoreflect.MessageKind, protoreflect.GroupKind:
rs.recs = append(rs.recs, [2]string{"MapValue", string(v.Message().FullName())}) rs.AppendRecs("MapValue", [2]string{"MapValue", string(v.Message().FullName())})
default: default:
rs.recs = append(rs.recs, [2]string{"MapValue", v.Kind().String()}) rs.AppendRecs("MapValue", [2]string{"MapValue", v.Kind().String()})
} }
} }
case "ContainingOneof": case "ContainingOneof":
if od := t.ContainingOneof(); od != nil { if od := t.ContainingOneof(); od != nil {
rs.recs = append(rs.recs, [2]string{"Oneof", string(od.Name())}) rs.AppendRecs("ContainingOneof", [2]string{"Oneof", string(od.Name())})
} }
case "ContainingMessage": case "ContainingMessage":
if t.IsExtension() { if t.IsExtension() {
rs.recs = append(rs.recs, [2]string{"Extendee", string(t.ContainingMessage().FullName())}) rs.AppendRecs("ContainingMessage", [2]string{"Extendee", string(t.ContainingMessage().FullName())})
} }
case "Message": case "Message":
if !t.IsMap() { if !t.IsMap() {
@ -187,13 +219,61 @@ func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool) string {
ss = append(ss, string(fs.Get(i).Name())) ss = append(ss, string(fs.Get(i).Name()))
} }
if len(ss) > 0 { if len(ss) > 0 {
rs.recs = append(rs.recs, [2]string{"Fields", "[" + joinStrings(ss, false) + "]"}) rs.AppendRecs("Fields", [2]string{"Fields", "[" + joinStrings(ss, false) + "]"})
} }
default:
rs.Append(rv, descriptorAccessors[rt]...) case protoreflect.FileDescriptor:
rs.Append(rv, []methodAndName{
{rv.MethodByName("Path"), "Path"},
{rv.MethodByName("Package"), "Package"},
{rv.MethodByName("Imports"), "Imports"},
{rv.MethodByName("Messages"), "Messages"},
{rv.MethodByName("Enums"), "Enums"},
{rv.MethodByName("Extensions"), "Extensions"},
{rv.MethodByName("Services"), "Services"},
}...)
case protoreflect.MessageDescriptor:
rs.Append(rv, []methodAndName{
{rv.MethodByName("IsMapEntry"), "IsMapEntry"},
{rv.MethodByName("Fields"), "Fields"},
{rv.MethodByName("Oneofs"), "Oneofs"},
{rv.MethodByName("ReservedNames"), "ReservedNames"},
{rv.MethodByName("ReservedRanges"), "ReservedRanges"},
{rv.MethodByName("RequiredNumbers"), "RequiredNumbers"},
{rv.MethodByName("ExtensionRanges"), "ExtensionRanges"},
{rv.MethodByName("Messages"), "Messages"},
{rv.MethodByName("Enums"), "Enums"},
{rv.MethodByName("Extensions"), "Extensions"},
}...)
case protoreflect.EnumDescriptor:
rs.Append(rv, []methodAndName{
{rv.MethodByName("Values"), "Values"},
{rv.MethodByName("ReservedNames"), "ReservedNames"},
{rv.MethodByName("ReservedRanges"), "ReservedRanges"},
}...)
case protoreflect.EnumValueDescriptor:
rs.Append(rv, []methodAndName{
{rv.MethodByName("Number"), "Number"},
}...)
case protoreflect.ServiceDescriptor:
rs.Append(rv, []methodAndName{
{rv.MethodByName("Methods"), "Methods"},
}...)
case protoreflect.MethodDescriptor:
rs.Append(rv, []methodAndName{
{rv.MethodByName("Input"), "Input"},
{rv.MethodByName("Output"), "Output"},
{rv.MethodByName("IsStreamingClient"), "IsStreamingClient"},
{rv.MethodByName("IsStreamingServer"), "IsStreamingServer"},
}...)
} }
if rv.MethodByName("GoType").IsValid() { if m := rv.MethodByName("GoType"); m.IsValid() {
rs.Append(rv, "GoType") rs.Append(rv, methodAndName{m, "GoType"})
} }
} }
return start + rs.Join() + end return start + rs.Join() + end
@ -202,19 +282,34 @@ func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool) string {
type records struct { type records struct {
recs [][2]string recs [][2]string
allowMulti bool allowMulti bool
// record is a function that will be called for every Append() or
// AppendRecs() call, to be used for testing with the
// InternalFormatDescOptForTesting function.
record func(string)
} }
func (rs *records) Append(v reflect.Value, accessors ...string) { func (rs *records) AppendRecs(fieldName string, newRecs [2]string) {
if rs.record != nil {
rs.record(fieldName)
}
rs.recs = append(rs.recs, newRecs)
}
func (rs *records) Append(v reflect.Value, accessors ...methodAndName) {
for _, a := range accessors { for _, a := range accessors {
if rs.record != nil {
rs.record(a.name)
}
var rv reflect.Value var rv reflect.Value
if m := v.MethodByName(a); m.IsValid() { if a.method.IsValid() {
rv = m.Call(nil)[0] rv = a.method.Call(nil)[0]
} }
if v.Kind() == reflect.Struct && !rv.IsValid() { if v.Kind() == reflect.Struct && !rv.IsValid() {
rv = v.FieldByName(a) rv = v.FieldByName(a.name)
} }
if !rv.IsValid() { if !rv.IsValid() {
panic(fmt.Sprintf("unknown accessor: %v.%s", v.Type(), a)) panic(fmt.Sprintf("unknown accessor: %v.%s", v.Type(), a.name))
} }
if _, ok := rv.Interface().(protoreflect.Value); ok { if _, ok := rv.Interface().(protoreflect.Value); ok {
rv = rv.MethodByName("Interface").Call(nil)[0] rv = rv.MethodByName("Interface").Call(nil)[0]
@ -261,7 +356,7 @@ func (rs *records) Append(v reflect.Value, accessors ...string) {
default: default:
s = fmt.Sprint(v) s = fmt.Sprint(v)
} }
rs.recs = append(rs.recs, [2]string{a, s}) rs.recs = append(rs.recs, [2]string{a.name, s})
} }
} }

View File

@ -0,0 +1,12 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package editiondefaults contains the binary representation of the editions
// defaults.
package editiondefaults
import _ "embed"
//go:embed editions_defaults.binpb
var Defaults []byte

View File

@ -0,0 +1,4 @@
  (0ж
  (0з
  (0и ж

View File

@ -53,8 +53,10 @@ type encoderState struct {
// If outputASCII is true, strings will be serialized in such a way that // If outputASCII is true, strings will be serialized in such a way that
// multi-byte UTF-8 sequences are escaped. This property ensures that the // multi-byte UTF-8 sequences are escaped. This property ensures that the
// overall output is ASCII (as opposed to UTF-8). // overall output is ASCII (as opposed to UTF-8).
func NewEncoder(indent string, delims [2]byte, outputASCII bool) (*Encoder, error) { func NewEncoder(buf []byte, indent string, delims [2]byte, outputASCII bool) (*Encoder, error) {
e := &Encoder{} e := &Encoder{
encoderState: encoderState{out: buf},
}
if len(indent) > 0 { if len(indent) > 0 {
if strings.Trim(indent, " \t") != "" { if strings.Trim(indent, " \t") != "" {
return nil, errors.New("indent may only be composed of space and tab characters") return nil, errors.New("indent may only be composed of space and tab characters")
@ -195,13 +197,13 @@ func appendFloat(out []byte, n float64, bitSize int) []byte {
// WriteInt writes out the given signed integer value. // WriteInt writes out the given signed integer value.
func (e *Encoder) WriteInt(n int64) { func (e *Encoder) WriteInt(n int64) {
e.prepareNext(scalar) e.prepareNext(scalar)
e.out = append(e.out, strconv.FormatInt(n, 10)...) e.out = strconv.AppendInt(e.out, n, 10)
} }
// WriteUint writes out the given unsigned integer value. // WriteUint writes out the given unsigned integer value.
func (e *Encoder) WriteUint(n uint64) { func (e *Encoder) WriteUint(n uint64) {
e.prepareNext(scalar) e.prepareNext(scalar)
e.out = append(e.out, strconv.FormatUint(n, 10)...) e.out = strconv.AppendUint(e.out, n, 10)
} }
// WriteLiteral writes out the given string as a literal value without quotes. // WriteLiteral writes out the given string as a literal value without quotes.

View File

@ -21,11 +21,26 @@ import (
"google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/reflect/protoregistry"
) )
// Edition is an Enum for proto2.Edition
type Edition int32
// These values align with the value of Enum in descriptor.proto which allows
// direct conversion between the proto enum and this enum.
const (
EditionUnknown Edition = 0
EditionProto2 Edition = 998
EditionProto3 Edition = 999
Edition2023 Edition = 1000
EditionUnsupported Edition = 100000
)
// The types in this file may have a suffix: // The types in this file may have a suffix:
// • L0: Contains fields common to all descriptors (except File) and // • L0: Contains fields common to all descriptors (except File) and
// must be initialized up front. // must be initialized up front.
// • L1: Contains fields specific to a descriptor and // • L1: Contains fields specific to a descriptor and
// must be initialized up front. // must be initialized up front. If the associated proto uses Editions, the
// Editions features must always be resolved. If not explicitly set, the
// appropriate default must be resolved and set.
// • L2: Contains fields that are lazily initialized when constructing // • L2: Contains fields that are lazily initialized when constructing
// from the raw file descriptor. When constructing as a literal, the L2 // from the raw file descriptor. When constructing as a literal, the L2
// fields must be initialized up front. // fields must be initialized up front.
@ -44,6 +59,7 @@ type (
} }
FileL1 struct { FileL1 struct {
Syntax protoreflect.Syntax Syntax protoreflect.Syntax
Edition Edition // Only used if Syntax == Editions
Path string Path string
Package protoreflect.FullName Package protoreflect.FullName
@ -51,12 +67,41 @@ type (
Messages Messages Messages Messages
Extensions Extensions Extensions Extensions
Services Services Services Services
EditionFeatures EditionFeatures
} }
FileL2 struct { FileL2 struct {
Options func() protoreflect.ProtoMessage Options func() protoreflect.ProtoMessage
Imports FileImports Imports FileImports
Locations SourceLocations Locations SourceLocations
} }
EditionFeatures struct {
// IsFieldPresence is true if field_presence is EXPLICIT
// https://protobuf.dev/editions/features/#field_presence
IsFieldPresence bool
// IsFieldPresence is true if field_presence is LEGACY_REQUIRED
// https://protobuf.dev/editions/features/#field_presence
IsLegacyRequired bool
// IsOpenEnum is true if enum_type is OPEN
// https://protobuf.dev/editions/features/#enum_type
IsOpenEnum bool
// IsPacked is true if repeated_field_encoding is PACKED
// https://protobuf.dev/editions/features/#repeated_field_encoding
IsPacked bool
// IsUTF8Validated is true if utf_validation is VERIFY
// https://protobuf.dev/editions/features/#utf8_validation
IsUTF8Validated bool
// IsDelimitedEncoded is true if message_encoding is DELIMITED
// https://protobuf.dev/editions/features/#message_encoding
IsDelimitedEncoded bool
// IsJSONCompliant is true if json_format is ALLOW
// https://protobuf.dev/editions/features/#json_format
IsJSONCompliant bool
// GenerateLegacyUnmarshalJSON determines if the plugin generates the
// UnmarshalJSON([]byte) error method for enums.
GenerateLegacyUnmarshalJSON bool
}
) )
func (fd *File) ParentFile() protoreflect.FileDescriptor { return fd } func (fd *File) ParentFile() protoreflect.FileDescriptor { return fd }
@ -117,6 +162,8 @@ type (
} }
EnumL1 struct { EnumL1 struct {
eagerValues bool // controls whether EnumL2.Values is already populated eagerValues bool // controls whether EnumL2.Values is already populated
EditionFeatures EditionFeatures
} }
EnumL2 struct { EnumL2 struct {
Options func() protoreflect.ProtoMessage Options func() protoreflect.ProtoMessage
@ -178,6 +225,8 @@ type (
Extensions Extensions Extensions Extensions
IsMapEntry bool // promoted from google.protobuf.MessageOptions IsMapEntry bool // promoted from google.protobuf.MessageOptions
IsMessageSet bool // promoted from google.protobuf.MessageOptions IsMessageSet bool // promoted from google.protobuf.MessageOptions
EditionFeatures EditionFeatures
} }
MessageL2 struct { MessageL2 struct {
Options func() protoreflect.ProtoMessage Options func() protoreflect.ProtoMessage
@ -210,6 +259,8 @@ type (
ContainingOneof protoreflect.OneofDescriptor // must be consistent with Message.Oneofs.Fields ContainingOneof protoreflect.OneofDescriptor // must be consistent with Message.Oneofs.Fields
Enum protoreflect.EnumDescriptor Enum protoreflect.EnumDescriptor
Message protoreflect.MessageDescriptor Message protoreflect.MessageDescriptor
EditionFeatures EditionFeatures
} }
Oneof struct { Oneof struct {
@ -219,6 +270,8 @@ type (
OneofL1 struct { OneofL1 struct {
Options func() protoreflect.ProtoMessage Options func() protoreflect.ProtoMessage
Fields OneofFields // must be consistent with Message.Fields.ContainingOneof Fields OneofFields // must be consistent with Message.Fields.ContainingOneof
EditionFeatures EditionFeatures
} }
) )
@ -268,23 +321,36 @@ func (fd *Field) Options() protoreflect.ProtoMessage {
} }
func (fd *Field) Number() protoreflect.FieldNumber { return fd.L1.Number } func (fd *Field) Number() protoreflect.FieldNumber { return fd.L1.Number }
func (fd *Field) Cardinality() protoreflect.Cardinality { return fd.L1.Cardinality } func (fd *Field) Cardinality() protoreflect.Cardinality { return fd.L1.Cardinality }
func (fd *Field) Kind() protoreflect.Kind { return fd.L1.Kind } func (fd *Field) Kind() protoreflect.Kind {
func (fd *Field) HasJSONName() bool { return fd.L1.StringName.hasJSON } return fd.L1.Kind
func (fd *Field) JSONName() string { return fd.L1.StringName.getJSON(fd) } }
func (fd *Field) TextName() string { return fd.L1.StringName.getText(fd) } func (fd *Field) HasJSONName() bool { return fd.L1.StringName.hasJSON }
func (fd *Field) JSONName() string { return fd.L1.StringName.getJSON(fd) }
func (fd *Field) TextName() string { return fd.L1.StringName.getText(fd) }
func (fd *Field) HasPresence() bool { func (fd *Field) HasPresence() bool {
return fd.L1.Cardinality != protoreflect.Repeated && (fd.L0.ParentFile.L1.Syntax == protoreflect.Proto2 || fd.L1.Message != nil || fd.L1.ContainingOneof != nil) if fd.L1.Cardinality == protoreflect.Repeated {
return false
}
explicitFieldPresence := fd.Syntax() == protoreflect.Editions && fd.L1.EditionFeatures.IsFieldPresence
return fd.Syntax() == protoreflect.Proto2 || explicitFieldPresence || fd.L1.Message != nil || fd.L1.ContainingOneof != nil
} }
func (fd *Field) HasOptionalKeyword() bool { func (fd *Field) HasOptionalKeyword() bool {
return (fd.L0.ParentFile.L1.Syntax == protoreflect.Proto2 && fd.L1.Cardinality == protoreflect.Optional && fd.L1.ContainingOneof == nil) || fd.L1.IsProto3Optional return (fd.L0.ParentFile.L1.Syntax == protoreflect.Proto2 && fd.L1.Cardinality == protoreflect.Optional && fd.L1.ContainingOneof == nil) || fd.L1.IsProto3Optional
} }
func (fd *Field) IsPacked() bool { func (fd *Field) IsPacked() bool {
if !fd.L1.HasPacked && fd.L0.ParentFile.L1.Syntax != protoreflect.Proto2 && fd.L1.Cardinality == protoreflect.Repeated { if fd.L1.Cardinality != protoreflect.Repeated {
switch fd.L1.Kind { return false
case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind: }
default: switch fd.L1.Kind {
return true case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind:
} return false
}
if fd.L0.ParentFile.L1.Syntax == protoreflect.Editions {
return fd.L1.EditionFeatures.IsPacked
}
if fd.L0.ParentFile.L1.Syntax == protoreflect.Proto3 {
// proto3 repeated fields are packed by default.
return !fd.L1.HasPacked || fd.L1.IsPacked
} }
return fd.L1.IsPacked return fd.L1.IsPacked
} }
@ -333,6 +399,9 @@ func (fd *Field) ProtoType(protoreflect.FieldDescriptor) {}
// WARNING: This method is exempt from the compatibility promise and may be // WARNING: This method is exempt from the compatibility promise and may be
// removed in the future without warning. // removed in the future without warning.
func (fd *Field) EnforceUTF8() bool { func (fd *Field) EnforceUTF8() bool {
if fd.L0.ParentFile.L1.Syntax == protoreflect.Editions {
return fd.L1.EditionFeatures.IsUTF8Validated
}
if fd.L1.HasEnforceUTF8 { if fd.L1.HasEnforceUTF8 {
return fd.L1.EnforceUTF8 return fd.L1.EnforceUTF8
} }
@ -359,10 +428,11 @@ type (
L2 *ExtensionL2 // protected by fileDesc.once L2 *ExtensionL2 // protected by fileDesc.once
} }
ExtensionL1 struct { ExtensionL1 struct {
Number protoreflect.FieldNumber Number protoreflect.FieldNumber
Extendee protoreflect.MessageDescriptor Extendee protoreflect.MessageDescriptor
Cardinality protoreflect.Cardinality Cardinality protoreflect.Cardinality
Kind protoreflect.Kind Kind protoreflect.Kind
EditionFeatures EditionFeatures
} }
ExtensionL2 struct { ExtensionL2 struct {
Options func() protoreflect.ProtoMessage Options func() protoreflect.ProtoMessage

View File

@ -5,6 +5,7 @@
package filedesc package filedesc
import ( import (
"fmt"
"sync" "sync"
"google.golang.org/protobuf/encoding/protowire" "google.golang.org/protobuf/encoding/protowire"
@ -98,6 +99,7 @@ func (fd *File) unmarshalSeed(b []byte) {
var prevField protoreflect.FieldNumber var prevField protoreflect.FieldNumber
var numEnums, numMessages, numExtensions, numServices int var numEnums, numMessages, numExtensions, numServices int
var posEnums, posMessages, posExtensions, posServices int var posEnums, posMessages, posExtensions, posServices int
var options []byte
b0 := b b0 := b
for len(b) > 0 { for len(b) > 0 {
num, typ, n := protowire.ConsumeTag(b) num, typ, n := protowire.ConsumeTag(b)
@ -113,6 +115,8 @@ func (fd *File) unmarshalSeed(b []byte) {
fd.L1.Syntax = protoreflect.Proto2 fd.L1.Syntax = protoreflect.Proto2
case "proto3": case "proto3":
fd.L1.Syntax = protoreflect.Proto3 fd.L1.Syntax = protoreflect.Proto3
case "editions":
fd.L1.Syntax = protoreflect.Editions
default: default:
panic("invalid syntax") panic("invalid syntax")
} }
@ -120,6 +124,8 @@ func (fd *File) unmarshalSeed(b []byte) {
fd.L1.Path = sb.MakeString(v) fd.L1.Path = sb.MakeString(v)
case genid.FileDescriptorProto_Package_field_number: case genid.FileDescriptorProto_Package_field_number:
fd.L1.Package = protoreflect.FullName(sb.MakeString(v)) fd.L1.Package = protoreflect.FullName(sb.MakeString(v))
case genid.FileDescriptorProto_Options_field_number:
options = v
case genid.FileDescriptorProto_EnumType_field_number: case genid.FileDescriptorProto_EnumType_field_number:
if prevField != genid.FileDescriptorProto_EnumType_field_number { if prevField != genid.FileDescriptorProto_EnumType_field_number {
if numEnums > 0 { if numEnums > 0 {
@ -154,6 +160,13 @@ func (fd *File) unmarshalSeed(b []byte) {
numServices++ numServices++
} }
prevField = num prevField = num
case protowire.VarintType:
v, m := protowire.ConsumeVarint(b)
b = b[m:]
switch num {
case genid.FileDescriptorProto_Edition_field_number:
fd.L1.Edition = Edition(v)
}
default: default:
m := protowire.ConsumeFieldValue(num, typ, b) m := protowire.ConsumeFieldValue(num, typ, b)
b = b[m:] b = b[m:]
@ -166,6 +179,15 @@ func (fd *File) unmarshalSeed(b []byte) {
fd.L1.Syntax = protoreflect.Proto2 fd.L1.Syntax = protoreflect.Proto2
} }
if fd.L1.Syntax == protoreflect.Editions {
fd.L1.EditionFeatures = getFeaturesFor(fd.L1.Edition)
}
// Parse editions features from options if any
if options != nil {
fd.unmarshalSeedOptions(options)
}
// Must allocate all declarations before parsing each descriptor type // Must allocate all declarations before parsing each descriptor type
// to ensure we handled all descriptors in "flattened ordering". // to ensure we handled all descriptors in "flattened ordering".
if numEnums > 0 { if numEnums > 0 {
@ -219,6 +241,28 @@ func (fd *File) unmarshalSeed(b []byte) {
} }
} }
func (fd *File) unmarshalSeedOptions(b []byte) {
for b := b; len(b) > 0; {
num, typ, n := protowire.ConsumeTag(b)
b = b[n:]
switch typ {
case protowire.BytesType:
v, m := protowire.ConsumeBytes(b)
b = b[m:]
switch num {
case genid.FileOptions_Features_field_number:
if fd.Syntax() != protoreflect.Editions {
panic(fmt.Sprintf("invalid descriptor: using edition features in a proto with syntax %s", fd.Syntax()))
}
fd.L1.EditionFeatures = unmarshalFeatureSet(v, fd.L1.EditionFeatures)
}
default:
m := protowire.ConsumeFieldValue(num, typ, b)
b = b[m:]
}
}
}
func (ed *Enum) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd protoreflect.Descriptor, i int) { func (ed *Enum) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd protoreflect.Descriptor, i int) {
ed.L0.ParentFile = pf ed.L0.ParentFile = pf
ed.L0.Parent = pd ed.L0.Parent = pd
@ -275,6 +319,7 @@ func (md *Message) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd protor
md.L0.ParentFile = pf md.L0.ParentFile = pf
md.L0.Parent = pd md.L0.Parent = pd
md.L0.Index = i md.L0.Index = i
md.L1.EditionFeatures = featuresFromParentDesc(md.Parent())
var prevField protoreflect.FieldNumber var prevField protoreflect.FieldNumber
var numEnums, numMessages, numExtensions int var numEnums, numMessages, numExtensions int
@ -380,6 +425,13 @@ func (md *Message) unmarshalSeedOptions(b []byte) {
case genid.MessageOptions_MessageSetWireFormat_field_number: case genid.MessageOptions_MessageSetWireFormat_field_number:
md.L1.IsMessageSet = protowire.DecodeBool(v) md.L1.IsMessageSet = protowire.DecodeBool(v)
} }
case protowire.BytesType:
v, m := protowire.ConsumeBytes(b)
b = b[m:]
switch num {
case genid.MessageOptions_Features_field_number:
md.L1.EditionFeatures = unmarshalFeatureSet(v, md.L1.EditionFeatures)
}
default: default:
m := protowire.ConsumeFieldValue(num, typ, b) m := protowire.ConsumeFieldValue(num, typ, b)
b = b[m:] b = b[m:]

View File

@ -414,6 +414,7 @@ func (fd *Field) unmarshalFull(b []byte, sb *strs.Builder, pf *File, pd protoref
fd.L0.ParentFile = pf fd.L0.ParentFile = pf
fd.L0.Parent = pd fd.L0.Parent = pd
fd.L0.Index = i fd.L0.Index = i
fd.L1.EditionFeatures = featuresFromParentDesc(fd.Parent())
var rawTypeName []byte var rawTypeName []byte
var rawOptions []byte var rawOptions []byte
@ -465,6 +466,12 @@ func (fd *Field) unmarshalFull(b []byte, sb *strs.Builder, pf *File, pd protoref
b = b[m:] b = b[m:]
} }
} }
if fd.Syntax() == protoreflect.Editions && fd.L1.Kind == protoreflect.MessageKind && fd.L1.EditionFeatures.IsDelimitedEncoded {
fd.L1.Kind = protoreflect.GroupKind
}
if fd.Syntax() == protoreflect.Editions && fd.L1.EditionFeatures.IsLegacyRequired {
fd.L1.Cardinality = protoreflect.Required
}
if rawTypeName != nil { if rawTypeName != nil {
name := makeFullName(sb, rawTypeName) name := makeFullName(sb, rawTypeName)
switch fd.L1.Kind { switch fd.L1.Kind {
@ -497,6 +504,13 @@ func (fd *Field) unmarshalOptions(b []byte) {
fd.L1.HasEnforceUTF8 = true fd.L1.HasEnforceUTF8 = true
fd.L1.EnforceUTF8 = protowire.DecodeBool(v) fd.L1.EnforceUTF8 = protowire.DecodeBool(v)
} }
case protowire.BytesType:
v, m := protowire.ConsumeBytes(b)
b = b[m:]
switch num {
case genid.FieldOptions_Features_field_number:
fd.L1.EditionFeatures = unmarshalFeatureSet(v, fd.L1.EditionFeatures)
}
default: default:
m := protowire.ConsumeFieldValue(num, typ, b) m := protowire.ConsumeFieldValue(num, typ, b)
b = b[m:] b = b[m:]
@ -534,6 +548,7 @@ func (od *Oneof) unmarshalFull(b []byte, sb *strs.Builder, pf *File, pd protoref
func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) { func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) {
var rawTypeName []byte var rawTypeName []byte
var rawOptions []byte var rawOptions []byte
xd.L1.EditionFeatures = featuresFromParentDesc(xd.L1.Extendee)
xd.L2 = new(ExtensionL2) xd.L2 = new(ExtensionL2)
for len(b) > 0 { for len(b) > 0 {
num, typ, n := protowire.ConsumeTag(b) num, typ, n := protowire.ConsumeTag(b)
@ -565,6 +580,12 @@ func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) {
b = b[m:] b = b[m:]
} }
} }
if xd.Syntax() == protoreflect.Editions && xd.L1.Kind == protoreflect.MessageKind && xd.L1.EditionFeatures.IsDelimitedEncoded {
xd.L1.Kind = protoreflect.GroupKind
}
if xd.Syntax() == protoreflect.Editions && xd.L1.EditionFeatures.IsLegacyRequired {
xd.L1.Cardinality = protoreflect.Required
}
if rawTypeName != nil { if rawTypeName != nil {
name := makeFullName(sb, rawTypeName) name := makeFullName(sb, rawTypeName)
switch xd.L1.Kind { switch xd.L1.Kind {
@ -589,6 +610,13 @@ func (xd *Extension) unmarshalOptions(b []byte) {
case genid.FieldOptions_Packed_field_number: case genid.FieldOptions_Packed_field_number:
xd.L2.IsPacked = protowire.DecodeBool(v) xd.L2.IsPacked = protowire.DecodeBool(v)
} }
case protowire.BytesType:
v, m := protowire.ConsumeBytes(b)
b = b[m:]
switch num {
case genid.FieldOptions_Features_field_number:
xd.L1.EditionFeatures = unmarshalFeatureSet(v, xd.L1.EditionFeatures)
}
default: default:
m := protowire.ConsumeFieldValue(num, typ, b) m := protowire.ConsumeFieldValue(num, typ, b)
b = b[m:] b = b[m:]

View File

@ -0,0 +1,142 @@
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package filedesc
import (
"fmt"
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/internal/editiondefaults"
"google.golang.org/protobuf/internal/genid"
"google.golang.org/protobuf/reflect/protoreflect"
)
var defaultsCache = make(map[Edition]EditionFeatures)
func init() {
unmarshalEditionDefaults(editiondefaults.Defaults)
}
func unmarshalGoFeature(b []byte, parent EditionFeatures) EditionFeatures {
for len(b) > 0 {
num, _, n := protowire.ConsumeTag(b)
b = b[n:]
switch num {
case genid.GoFeatures_LegacyUnmarshalJsonEnum_field_number:
v, m := protowire.ConsumeVarint(b)
b = b[m:]
parent.GenerateLegacyUnmarshalJSON = protowire.DecodeBool(v)
default:
panic(fmt.Sprintf("unkown field number %d while unmarshalling GoFeatures", num))
}
}
return parent
}
func unmarshalFeatureSet(b []byte, parent EditionFeatures) EditionFeatures {
for len(b) > 0 {
num, typ, n := protowire.ConsumeTag(b)
b = b[n:]
switch typ {
case protowire.VarintType:
v, m := protowire.ConsumeVarint(b)
b = b[m:]
switch num {
case genid.FeatureSet_FieldPresence_field_number:
parent.IsFieldPresence = v == genid.FeatureSet_EXPLICIT_enum_value || v == genid.FeatureSet_LEGACY_REQUIRED_enum_value
parent.IsLegacyRequired = v == genid.FeatureSet_LEGACY_REQUIRED_enum_value
case genid.FeatureSet_EnumType_field_number:
parent.IsOpenEnum = v == genid.FeatureSet_OPEN_enum_value
case genid.FeatureSet_RepeatedFieldEncoding_field_number:
parent.IsPacked = v == genid.FeatureSet_PACKED_enum_value
case genid.FeatureSet_Utf8Validation_field_number:
parent.IsUTF8Validated = v == genid.FeatureSet_VERIFY_enum_value
case genid.FeatureSet_MessageEncoding_field_number:
parent.IsDelimitedEncoded = v == genid.FeatureSet_DELIMITED_enum_value
case genid.FeatureSet_JsonFormat_field_number:
parent.IsJSONCompliant = v == genid.FeatureSet_ALLOW_enum_value
default:
panic(fmt.Sprintf("unkown field number %d while unmarshalling FeatureSet", num))
}
case protowire.BytesType:
v, m := protowire.ConsumeBytes(b)
b = b[m:]
switch num {
case genid.GoFeatures_LegacyUnmarshalJsonEnum_field_number:
parent = unmarshalGoFeature(v, parent)
}
}
}
return parent
}
func featuresFromParentDesc(parentDesc protoreflect.Descriptor) EditionFeatures {
var parentFS EditionFeatures
switch p := parentDesc.(type) {
case *File:
parentFS = p.L1.EditionFeatures
case *Message:
parentFS = p.L1.EditionFeatures
default:
panic(fmt.Sprintf("unknown parent type %T", parentDesc))
}
return parentFS
}
func unmarshalEditionDefault(b []byte) {
var ed Edition
var fs EditionFeatures
for len(b) > 0 {
num, typ, n := protowire.ConsumeTag(b)
b = b[n:]
switch typ {
case protowire.VarintType:
v, m := protowire.ConsumeVarint(b)
b = b[m:]
switch num {
case genid.FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_number:
ed = Edition(v)
}
case protowire.BytesType:
v, m := protowire.ConsumeBytes(b)
b = b[m:]
switch num {
case genid.FeatureSetDefaults_FeatureSetEditionDefault_Features_field_number:
fs = unmarshalFeatureSet(v, fs)
}
}
}
defaultsCache[ed] = fs
}
func unmarshalEditionDefaults(b []byte) {
for len(b) > 0 {
num, _, n := protowire.ConsumeTag(b)
b = b[n:]
switch num {
case genid.FeatureSetDefaults_Defaults_field_number:
def, m := protowire.ConsumeBytes(b)
b = b[m:]
unmarshalEditionDefault(def)
case genid.FeatureSetDefaults_MinimumEdition_field_number,
genid.FeatureSetDefaults_MaximumEdition_field_number:
// We don't care about the minimum and maximum editions. If the
// edition we are looking for later on is not in the cache we know
// it is outside of the range between minimum and maximum edition.
_, m := protowire.ConsumeVarint(b)
b = b[m:]
default:
panic(fmt.Sprintf("unkown field number %d while unmarshalling EditionDefault", num))
}
}
}
func getFeaturesFor(ed Edition) EditionFeatures {
if def, ok := defaultsCache[ed]; ok {
return def
}
panic(fmt.Sprintf("unsupported edition: %v", ed))
}

View File

@ -12,6 +12,27 @@ import (
const File_google_protobuf_descriptor_proto = "google/protobuf/descriptor.proto" const File_google_protobuf_descriptor_proto = "google/protobuf/descriptor.proto"
// Full and short names for google.protobuf.Edition.
const (
Edition_enum_fullname = "google.protobuf.Edition"
Edition_enum_name = "Edition"
)
// Enum values for google.protobuf.Edition.
const (
Edition_EDITION_UNKNOWN_enum_value = 0
Edition_EDITION_PROTO2_enum_value = 998
Edition_EDITION_PROTO3_enum_value = 999
Edition_EDITION_2023_enum_value = 1000
Edition_EDITION_2024_enum_value = 1001
Edition_EDITION_1_TEST_ONLY_enum_value = 1
Edition_EDITION_2_TEST_ONLY_enum_value = 2
Edition_EDITION_99997_TEST_ONLY_enum_value = 99997
Edition_EDITION_99998_TEST_ONLY_enum_value = 99998
Edition_EDITION_99999_TEST_ONLY_enum_value = 99999
Edition_EDITION_MAX_enum_value = 2147483647
)
// Names for google.protobuf.FileDescriptorSet. // Names for google.protobuf.FileDescriptorSet.
const ( const (
FileDescriptorSet_message_name protoreflect.Name = "FileDescriptorSet" FileDescriptorSet_message_name protoreflect.Name = "FileDescriptorSet"
@ -81,7 +102,7 @@ const (
FileDescriptorProto_Options_field_number protoreflect.FieldNumber = 8 FileDescriptorProto_Options_field_number protoreflect.FieldNumber = 8
FileDescriptorProto_SourceCodeInfo_field_number protoreflect.FieldNumber = 9 FileDescriptorProto_SourceCodeInfo_field_number protoreflect.FieldNumber = 9
FileDescriptorProto_Syntax_field_number protoreflect.FieldNumber = 12 FileDescriptorProto_Syntax_field_number protoreflect.FieldNumber = 12
FileDescriptorProto_Edition_field_number protoreflect.FieldNumber = 13 FileDescriptorProto_Edition_field_number protoreflect.FieldNumber = 14
) )
// Names for google.protobuf.DescriptorProto. // Names for google.protobuf.DescriptorProto.
@ -183,13 +204,64 @@ const (
// Field names for google.protobuf.ExtensionRangeOptions. // Field names for google.protobuf.ExtensionRangeOptions.
const ( const (
ExtensionRangeOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" ExtensionRangeOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option"
ExtensionRangeOptions_Declaration_field_name protoreflect.Name = "declaration"
ExtensionRangeOptions_Features_field_name protoreflect.Name = "features"
ExtensionRangeOptions_Verification_field_name protoreflect.Name = "verification"
ExtensionRangeOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.uninterpreted_option" ExtensionRangeOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.uninterpreted_option"
ExtensionRangeOptions_Declaration_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.declaration"
ExtensionRangeOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.features"
ExtensionRangeOptions_Verification_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.verification"
) )
// Field numbers for google.protobuf.ExtensionRangeOptions. // Field numbers for google.protobuf.ExtensionRangeOptions.
const ( const (
ExtensionRangeOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ExtensionRangeOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999
ExtensionRangeOptions_Declaration_field_number protoreflect.FieldNumber = 2
ExtensionRangeOptions_Features_field_number protoreflect.FieldNumber = 50
ExtensionRangeOptions_Verification_field_number protoreflect.FieldNumber = 3
)
// Full and short names for google.protobuf.ExtensionRangeOptions.VerificationState.
const (
ExtensionRangeOptions_VerificationState_enum_fullname = "google.protobuf.ExtensionRangeOptions.VerificationState"
ExtensionRangeOptions_VerificationState_enum_name = "VerificationState"
)
// Enum values for google.protobuf.ExtensionRangeOptions.VerificationState.
const (
ExtensionRangeOptions_DECLARATION_enum_value = 0
ExtensionRangeOptions_UNVERIFIED_enum_value = 1
)
// Names for google.protobuf.ExtensionRangeOptions.Declaration.
const (
ExtensionRangeOptions_Declaration_message_name protoreflect.Name = "Declaration"
ExtensionRangeOptions_Declaration_message_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration"
)
// Field names for google.protobuf.ExtensionRangeOptions.Declaration.
const (
ExtensionRangeOptions_Declaration_Number_field_name protoreflect.Name = "number"
ExtensionRangeOptions_Declaration_FullName_field_name protoreflect.Name = "full_name"
ExtensionRangeOptions_Declaration_Type_field_name protoreflect.Name = "type"
ExtensionRangeOptions_Declaration_Reserved_field_name protoreflect.Name = "reserved"
ExtensionRangeOptions_Declaration_Repeated_field_name protoreflect.Name = "repeated"
ExtensionRangeOptions_Declaration_Number_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration.number"
ExtensionRangeOptions_Declaration_FullName_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration.full_name"
ExtensionRangeOptions_Declaration_Type_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration.type"
ExtensionRangeOptions_Declaration_Reserved_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration.reserved"
ExtensionRangeOptions_Declaration_Repeated_field_fullname protoreflect.FullName = "google.protobuf.ExtensionRangeOptions.Declaration.repeated"
)
// Field numbers for google.protobuf.ExtensionRangeOptions.Declaration.
const (
ExtensionRangeOptions_Declaration_Number_field_number protoreflect.FieldNumber = 1
ExtensionRangeOptions_Declaration_FullName_field_number protoreflect.FieldNumber = 2
ExtensionRangeOptions_Declaration_Type_field_number protoreflect.FieldNumber = 3
ExtensionRangeOptions_Declaration_Reserved_field_number protoreflect.FieldNumber = 5
ExtensionRangeOptions_Declaration_Repeated_field_number protoreflect.FieldNumber = 6
) )
// Names for google.protobuf.FieldDescriptorProto. // Names for google.protobuf.FieldDescriptorProto.
@ -246,12 +318,41 @@ const (
FieldDescriptorProto_Type_enum_name = "Type" FieldDescriptorProto_Type_enum_name = "Type"
) )
// Enum values for google.protobuf.FieldDescriptorProto.Type.
const (
FieldDescriptorProto_TYPE_DOUBLE_enum_value = 1
FieldDescriptorProto_TYPE_FLOAT_enum_value = 2
FieldDescriptorProto_TYPE_INT64_enum_value = 3
FieldDescriptorProto_TYPE_UINT64_enum_value = 4
FieldDescriptorProto_TYPE_INT32_enum_value = 5
FieldDescriptorProto_TYPE_FIXED64_enum_value = 6
FieldDescriptorProto_TYPE_FIXED32_enum_value = 7
FieldDescriptorProto_TYPE_BOOL_enum_value = 8
FieldDescriptorProto_TYPE_STRING_enum_value = 9
FieldDescriptorProto_TYPE_GROUP_enum_value = 10
FieldDescriptorProto_TYPE_MESSAGE_enum_value = 11
FieldDescriptorProto_TYPE_BYTES_enum_value = 12
FieldDescriptorProto_TYPE_UINT32_enum_value = 13
FieldDescriptorProto_TYPE_ENUM_enum_value = 14
FieldDescriptorProto_TYPE_SFIXED32_enum_value = 15
FieldDescriptorProto_TYPE_SFIXED64_enum_value = 16
FieldDescriptorProto_TYPE_SINT32_enum_value = 17
FieldDescriptorProto_TYPE_SINT64_enum_value = 18
)
// Full and short names for google.protobuf.FieldDescriptorProto.Label. // Full and short names for google.protobuf.FieldDescriptorProto.Label.
const ( const (
FieldDescriptorProto_Label_enum_fullname = "google.protobuf.FieldDescriptorProto.Label" FieldDescriptorProto_Label_enum_fullname = "google.protobuf.FieldDescriptorProto.Label"
FieldDescriptorProto_Label_enum_name = "Label" FieldDescriptorProto_Label_enum_name = "Label"
) )
// Enum values for google.protobuf.FieldDescriptorProto.Label.
const (
FieldDescriptorProto_LABEL_OPTIONAL_enum_value = 1
FieldDescriptorProto_LABEL_REPEATED_enum_value = 3
FieldDescriptorProto_LABEL_REQUIRED_enum_value = 2
)
// Names for google.protobuf.OneofDescriptorProto. // Names for google.protobuf.OneofDescriptorProto.
const ( const (
OneofDescriptorProto_message_name protoreflect.Name = "OneofDescriptorProto" OneofDescriptorProto_message_name protoreflect.Name = "OneofDescriptorProto"
@ -423,7 +524,6 @@ const (
FileOptions_CcGenericServices_field_name protoreflect.Name = "cc_generic_services" FileOptions_CcGenericServices_field_name protoreflect.Name = "cc_generic_services"
FileOptions_JavaGenericServices_field_name protoreflect.Name = "java_generic_services" FileOptions_JavaGenericServices_field_name protoreflect.Name = "java_generic_services"
FileOptions_PyGenericServices_field_name protoreflect.Name = "py_generic_services" FileOptions_PyGenericServices_field_name protoreflect.Name = "py_generic_services"
FileOptions_PhpGenericServices_field_name protoreflect.Name = "php_generic_services"
FileOptions_Deprecated_field_name protoreflect.Name = "deprecated" FileOptions_Deprecated_field_name protoreflect.Name = "deprecated"
FileOptions_CcEnableArenas_field_name protoreflect.Name = "cc_enable_arenas" FileOptions_CcEnableArenas_field_name protoreflect.Name = "cc_enable_arenas"
FileOptions_ObjcClassPrefix_field_name protoreflect.Name = "objc_class_prefix" FileOptions_ObjcClassPrefix_field_name protoreflect.Name = "objc_class_prefix"
@ -433,6 +533,7 @@ const (
FileOptions_PhpNamespace_field_name protoreflect.Name = "php_namespace" FileOptions_PhpNamespace_field_name protoreflect.Name = "php_namespace"
FileOptions_PhpMetadataNamespace_field_name protoreflect.Name = "php_metadata_namespace" FileOptions_PhpMetadataNamespace_field_name protoreflect.Name = "php_metadata_namespace"
FileOptions_RubyPackage_field_name protoreflect.Name = "ruby_package" FileOptions_RubyPackage_field_name protoreflect.Name = "ruby_package"
FileOptions_Features_field_name protoreflect.Name = "features"
FileOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" FileOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option"
FileOptions_JavaPackage_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.java_package" FileOptions_JavaPackage_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.java_package"
@ -445,7 +546,6 @@ const (
FileOptions_CcGenericServices_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.cc_generic_services" FileOptions_CcGenericServices_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.cc_generic_services"
FileOptions_JavaGenericServices_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.java_generic_services" FileOptions_JavaGenericServices_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.java_generic_services"
FileOptions_PyGenericServices_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.py_generic_services" FileOptions_PyGenericServices_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.py_generic_services"
FileOptions_PhpGenericServices_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.php_generic_services"
FileOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.deprecated" FileOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.deprecated"
FileOptions_CcEnableArenas_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.cc_enable_arenas" FileOptions_CcEnableArenas_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.cc_enable_arenas"
FileOptions_ObjcClassPrefix_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.objc_class_prefix" FileOptions_ObjcClassPrefix_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.objc_class_prefix"
@ -455,6 +555,7 @@ const (
FileOptions_PhpNamespace_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.php_namespace" FileOptions_PhpNamespace_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.php_namespace"
FileOptions_PhpMetadataNamespace_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.php_metadata_namespace" FileOptions_PhpMetadataNamespace_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.php_metadata_namespace"
FileOptions_RubyPackage_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.ruby_package" FileOptions_RubyPackage_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.ruby_package"
FileOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.features"
FileOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.uninterpreted_option" FileOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.FileOptions.uninterpreted_option"
) )
@ -470,7 +571,6 @@ const (
FileOptions_CcGenericServices_field_number protoreflect.FieldNumber = 16 FileOptions_CcGenericServices_field_number protoreflect.FieldNumber = 16
FileOptions_JavaGenericServices_field_number protoreflect.FieldNumber = 17 FileOptions_JavaGenericServices_field_number protoreflect.FieldNumber = 17
FileOptions_PyGenericServices_field_number protoreflect.FieldNumber = 18 FileOptions_PyGenericServices_field_number protoreflect.FieldNumber = 18
FileOptions_PhpGenericServices_field_number protoreflect.FieldNumber = 42
FileOptions_Deprecated_field_number protoreflect.FieldNumber = 23 FileOptions_Deprecated_field_number protoreflect.FieldNumber = 23
FileOptions_CcEnableArenas_field_number protoreflect.FieldNumber = 31 FileOptions_CcEnableArenas_field_number protoreflect.FieldNumber = 31
FileOptions_ObjcClassPrefix_field_number protoreflect.FieldNumber = 36 FileOptions_ObjcClassPrefix_field_number protoreflect.FieldNumber = 36
@ -480,6 +580,7 @@ const (
FileOptions_PhpNamespace_field_number protoreflect.FieldNumber = 41 FileOptions_PhpNamespace_field_number protoreflect.FieldNumber = 41
FileOptions_PhpMetadataNamespace_field_number protoreflect.FieldNumber = 44 FileOptions_PhpMetadataNamespace_field_number protoreflect.FieldNumber = 44
FileOptions_RubyPackage_field_number protoreflect.FieldNumber = 45 FileOptions_RubyPackage_field_number protoreflect.FieldNumber = 45
FileOptions_Features_field_number protoreflect.FieldNumber = 50
FileOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 FileOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999
) )
@ -489,6 +590,13 @@ const (
FileOptions_OptimizeMode_enum_name = "OptimizeMode" FileOptions_OptimizeMode_enum_name = "OptimizeMode"
) )
// Enum values for google.protobuf.FileOptions.OptimizeMode.
const (
FileOptions_SPEED_enum_value = 1
FileOptions_CODE_SIZE_enum_value = 2
FileOptions_LITE_RUNTIME_enum_value = 3
)
// Names for google.protobuf.MessageOptions. // Names for google.protobuf.MessageOptions.
const ( const (
MessageOptions_message_name protoreflect.Name = "MessageOptions" MessageOptions_message_name protoreflect.Name = "MessageOptions"
@ -502,6 +610,7 @@ const (
MessageOptions_Deprecated_field_name protoreflect.Name = "deprecated" MessageOptions_Deprecated_field_name protoreflect.Name = "deprecated"
MessageOptions_MapEntry_field_name protoreflect.Name = "map_entry" MessageOptions_MapEntry_field_name protoreflect.Name = "map_entry"
MessageOptions_DeprecatedLegacyJsonFieldConflicts_field_name protoreflect.Name = "deprecated_legacy_json_field_conflicts" MessageOptions_DeprecatedLegacyJsonFieldConflicts_field_name protoreflect.Name = "deprecated_legacy_json_field_conflicts"
MessageOptions_Features_field_name protoreflect.Name = "features"
MessageOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" MessageOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option"
MessageOptions_MessageSetWireFormat_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.message_set_wire_format" MessageOptions_MessageSetWireFormat_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.message_set_wire_format"
@ -509,6 +618,7 @@ const (
MessageOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.deprecated" MessageOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.deprecated"
MessageOptions_MapEntry_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.map_entry" MessageOptions_MapEntry_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.map_entry"
MessageOptions_DeprecatedLegacyJsonFieldConflicts_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.deprecated_legacy_json_field_conflicts" MessageOptions_DeprecatedLegacyJsonFieldConflicts_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.deprecated_legacy_json_field_conflicts"
MessageOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.features"
MessageOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.uninterpreted_option" MessageOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.MessageOptions.uninterpreted_option"
) )
@ -519,6 +629,7 @@ const (
MessageOptions_Deprecated_field_number protoreflect.FieldNumber = 3 MessageOptions_Deprecated_field_number protoreflect.FieldNumber = 3
MessageOptions_MapEntry_field_number protoreflect.FieldNumber = 7 MessageOptions_MapEntry_field_number protoreflect.FieldNumber = 7
MessageOptions_DeprecatedLegacyJsonFieldConflicts_field_number protoreflect.FieldNumber = 11 MessageOptions_DeprecatedLegacyJsonFieldConflicts_field_number protoreflect.FieldNumber = 11
MessageOptions_Features_field_number protoreflect.FieldNumber = 12
MessageOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 MessageOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999
) )
@ -539,7 +650,9 @@ const (
FieldOptions_Weak_field_name protoreflect.Name = "weak" FieldOptions_Weak_field_name protoreflect.Name = "weak"
FieldOptions_DebugRedact_field_name protoreflect.Name = "debug_redact" FieldOptions_DebugRedact_field_name protoreflect.Name = "debug_redact"
FieldOptions_Retention_field_name protoreflect.Name = "retention" FieldOptions_Retention_field_name protoreflect.Name = "retention"
FieldOptions_Target_field_name protoreflect.Name = "target" FieldOptions_Targets_field_name protoreflect.Name = "targets"
FieldOptions_EditionDefaults_field_name protoreflect.Name = "edition_defaults"
FieldOptions_Features_field_name protoreflect.Name = "features"
FieldOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" FieldOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option"
FieldOptions_Ctype_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.ctype" FieldOptions_Ctype_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.ctype"
@ -551,7 +664,9 @@ const (
FieldOptions_Weak_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.weak" FieldOptions_Weak_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.weak"
FieldOptions_DebugRedact_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.debug_redact" FieldOptions_DebugRedact_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.debug_redact"
FieldOptions_Retention_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.retention" FieldOptions_Retention_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.retention"
FieldOptions_Target_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.target" FieldOptions_Targets_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.targets"
FieldOptions_EditionDefaults_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.edition_defaults"
FieldOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.features"
FieldOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.uninterpreted_option" FieldOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.uninterpreted_option"
) )
@ -566,7 +681,9 @@ const (
FieldOptions_Weak_field_number protoreflect.FieldNumber = 10 FieldOptions_Weak_field_number protoreflect.FieldNumber = 10
FieldOptions_DebugRedact_field_number protoreflect.FieldNumber = 16 FieldOptions_DebugRedact_field_number protoreflect.FieldNumber = 16
FieldOptions_Retention_field_number protoreflect.FieldNumber = 17 FieldOptions_Retention_field_number protoreflect.FieldNumber = 17
FieldOptions_Target_field_number protoreflect.FieldNumber = 18 FieldOptions_Targets_field_number protoreflect.FieldNumber = 19
FieldOptions_EditionDefaults_field_number protoreflect.FieldNumber = 20
FieldOptions_Features_field_number protoreflect.FieldNumber = 21
FieldOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 FieldOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999
) )
@ -576,24 +693,80 @@ const (
FieldOptions_CType_enum_name = "CType" FieldOptions_CType_enum_name = "CType"
) )
// Enum values for google.protobuf.FieldOptions.CType.
const (
FieldOptions_STRING_enum_value = 0
FieldOptions_CORD_enum_value = 1
FieldOptions_STRING_PIECE_enum_value = 2
)
// Full and short names for google.protobuf.FieldOptions.JSType. // Full and short names for google.protobuf.FieldOptions.JSType.
const ( const (
FieldOptions_JSType_enum_fullname = "google.protobuf.FieldOptions.JSType" FieldOptions_JSType_enum_fullname = "google.protobuf.FieldOptions.JSType"
FieldOptions_JSType_enum_name = "JSType" FieldOptions_JSType_enum_name = "JSType"
) )
// Enum values for google.protobuf.FieldOptions.JSType.
const (
FieldOptions_JS_NORMAL_enum_value = 0
FieldOptions_JS_STRING_enum_value = 1
FieldOptions_JS_NUMBER_enum_value = 2
)
// Full and short names for google.protobuf.FieldOptions.OptionRetention. // Full and short names for google.protobuf.FieldOptions.OptionRetention.
const ( const (
FieldOptions_OptionRetention_enum_fullname = "google.protobuf.FieldOptions.OptionRetention" FieldOptions_OptionRetention_enum_fullname = "google.protobuf.FieldOptions.OptionRetention"
FieldOptions_OptionRetention_enum_name = "OptionRetention" FieldOptions_OptionRetention_enum_name = "OptionRetention"
) )
// Enum values for google.protobuf.FieldOptions.OptionRetention.
const (
FieldOptions_RETENTION_UNKNOWN_enum_value = 0
FieldOptions_RETENTION_RUNTIME_enum_value = 1
FieldOptions_RETENTION_SOURCE_enum_value = 2
)
// Full and short names for google.protobuf.FieldOptions.OptionTargetType. // Full and short names for google.protobuf.FieldOptions.OptionTargetType.
const ( const (
FieldOptions_OptionTargetType_enum_fullname = "google.protobuf.FieldOptions.OptionTargetType" FieldOptions_OptionTargetType_enum_fullname = "google.protobuf.FieldOptions.OptionTargetType"
FieldOptions_OptionTargetType_enum_name = "OptionTargetType" FieldOptions_OptionTargetType_enum_name = "OptionTargetType"
) )
// Enum values for google.protobuf.FieldOptions.OptionTargetType.
const (
FieldOptions_TARGET_TYPE_UNKNOWN_enum_value = 0
FieldOptions_TARGET_TYPE_FILE_enum_value = 1
FieldOptions_TARGET_TYPE_EXTENSION_RANGE_enum_value = 2
FieldOptions_TARGET_TYPE_MESSAGE_enum_value = 3
FieldOptions_TARGET_TYPE_FIELD_enum_value = 4
FieldOptions_TARGET_TYPE_ONEOF_enum_value = 5
FieldOptions_TARGET_TYPE_ENUM_enum_value = 6
FieldOptions_TARGET_TYPE_ENUM_ENTRY_enum_value = 7
FieldOptions_TARGET_TYPE_SERVICE_enum_value = 8
FieldOptions_TARGET_TYPE_METHOD_enum_value = 9
)
// Names for google.protobuf.FieldOptions.EditionDefault.
const (
FieldOptions_EditionDefault_message_name protoreflect.Name = "EditionDefault"
FieldOptions_EditionDefault_message_fullname protoreflect.FullName = "google.protobuf.FieldOptions.EditionDefault"
)
// Field names for google.protobuf.FieldOptions.EditionDefault.
const (
FieldOptions_EditionDefault_Edition_field_name protoreflect.Name = "edition"
FieldOptions_EditionDefault_Value_field_name protoreflect.Name = "value"
FieldOptions_EditionDefault_Edition_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.EditionDefault.edition"
FieldOptions_EditionDefault_Value_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.EditionDefault.value"
)
// Field numbers for google.protobuf.FieldOptions.EditionDefault.
const (
FieldOptions_EditionDefault_Edition_field_number protoreflect.FieldNumber = 3
FieldOptions_EditionDefault_Value_field_number protoreflect.FieldNumber = 2
)
// Names for google.protobuf.OneofOptions. // Names for google.protobuf.OneofOptions.
const ( const (
OneofOptions_message_name protoreflect.Name = "OneofOptions" OneofOptions_message_name protoreflect.Name = "OneofOptions"
@ -602,13 +775,16 @@ const (
// Field names for google.protobuf.OneofOptions. // Field names for google.protobuf.OneofOptions.
const ( const (
OneofOptions_Features_field_name protoreflect.Name = "features"
OneofOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" OneofOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option"
OneofOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.OneofOptions.features"
OneofOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.OneofOptions.uninterpreted_option" OneofOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.OneofOptions.uninterpreted_option"
) )
// Field numbers for google.protobuf.OneofOptions. // Field numbers for google.protobuf.OneofOptions.
const ( const (
OneofOptions_Features_field_number protoreflect.FieldNumber = 1
OneofOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 OneofOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999
) )
@ -623,11 +799,13 @@ const (
EnumOptions_AllowAlias_field_name protoreflect.Name = "allow_alias" EnumOptions_AllowAlias_field_name protoreflect.Name = "allow_alias"
EnumOptions_Deprecated_field_name protoreflect.Name = "deprecated" EnumOptions_Deprecated_field_name protoreflect.Name = "deprecated"
EnumOptions_DeprecatedLegacyJsonFieldConflicts_field_name protoreflect.Name = "deprecated_legacy_json_field_conflicts" EnumOptions_DeprecatedLegacyJsonFieldConflicts_field_name protoreflect.Name = "deprecated_legacy_json_field_conflicts"
EnumOptions_Features_field_name protoreflect.Name = "features"
EnumOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" EnumOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option"
EnumOptions_AllowAlias_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.allow_alias" EnumOptions_AllowAlias_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.allow_alias"
EnumOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.deprecated" EnumOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.deprecated"
EnumOptions_DeprecatedLegacyJsonFieldConflicts_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.deprecated_legacy_json_field_conflicts" EnumOptions_DeprecatedLegacyJsonFieldConflicts_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.deprecated_legacy_json_field_conflicts"
EnumOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.features"
EnumOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.uninterpreted_option" EnumOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.EnumOptions.uninterpreted_option"
) )
@ -636,6 +814,7 @@ const (
EnumOptions_AllowAlias_field_number protoreflect.FieldNumber = 2 EnumOptions_AllowAlias_field_number protoreflect.FieldNumber = 2
EnumOptions_Deprecated_field_number protoreflect.FieldNumber = 3 EnumOptions_Deprecated_field_number protoreflect.FieldNumber = 3
EnumOptions_DeprecatedLegacyJsonFieldConflicts_field_number protoreflect.FieldNumber = 6 EnumOptions_DeprecatedLegacyJsonFieldConflicts_field_number protoreflect.FieldNumber = 6
EnumOptions_Features_field_number protoreflect.FieldNumber = 7
EnumOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 EnumOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999
) )
@ -648,15 +827,21 @@ const (
// Field names for google.protobuf.EnumValueOptions. // Field names for google.protobuf.EnumValueOptions.
const ( const (
EnumValueOptions_Deprecated_field_name protoreflect.Name = "deprecated" EnumValueOptions_Deprecated_field_name protoreflect.Name = "deprecated"
EnumValueOptions_Features_field_name protoreflect.Name = "features"
EnumValueOptions_DebugRedact_field_name protoreflect.Name = "debug_redact"
EnumValueOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" EnumValueOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option"
EnumValueOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.deprecated" EnumValueOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.deprecated"
EnumValueOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.features"
EnumValueOptions_DebugRedact_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.debug_redact"
EnumValueOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.uninterpreted_option" EnumValueOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.uninterpreted_option"
) )
// Field numbers for google.protobuf.EnumValueOptions. // Field numbers for google.protobuf.EnumValueOptions.
const ( const (
EnumValueOptions_Deprecated_field_number protoreflect.FieldNumber = 1 EnumValueOptions_Deprecated_field_number protoreflect.FieldNumber = 1
EnumValueOptions_Features_field_number protoreflect.FieldNumber = 2
EnumValueOptions_DebugRedact_field_number protoreflect.FieldNumber = 3
EnumValueOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 EnumValueOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999
) )
@ -668,15 +853,18 @@ const (
// Field names for google.protobuf.ServiceOptions. // Field names for google.protobuf.ServiceOptions.
const ( const (
ServiceOptions_Features_field_name protoreflect.Name = "features"
ServiceOptions_Deprecated_field_name protoreflect.Name = "deprecated" ServiceOptions_Deprecated_field_name protoreflect.Name = "deprecated"
ServiceOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" ServiceOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option"
ServiceOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.ServiceOptions.features"
ServiceOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.ServiceOptions.deprecated" ServiceOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.ServiceOptions.deprecated"
ServiceOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.ServiceOptions.uninterpreted_option" ServiceOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.ServiceOptions.uninterpreted_option"
) )
// Field numbers for google.protobuf.ServiceOptions. // Field numbers for google.protobuf.ServiceOptions.
const ( const (
ServiceOptions_Features_field_number protoreflect.FieldNumber = 34
ServiceOptions_Deprecated_field_number protoreflect.FieldNumber = 33 ServiceOptions_Deprecated_field_number protoreflect.FieldNumber = 33
ServiceOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ServiceOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999
) )
@ -691,10 +879,12 @@ const (
const ( const (
MethodOptions_Deprecated_field_name protoreflect.Name = "deprecated" MethodOptions_Deprecated_field_name protoreflect.Name = "deprecated"
MethodOptions_IdempotencyLevel_field_name protoreflect.Name = "idempotency_level" MethodOptions_IdempotencyLevel_field_name protoreflect.Name = "idempotency_level"
MethodOptions_Features_field_name protoreflect.Name = "features"
MethodOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" MethodOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option"
MethodOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.MethodOptions.deprecated" MethodOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.MethodOptions.deprecated"
MethodOptions_IdempotencyLevel_field_fullname protoreflect.FullName = "google.protobuf.MethodOptions.idempotency_level" MethodOptions_IdempotencyLevel_field_fullname protoreflect.FullName = "google.protobuf.MethodOptions.idempotency_level"
MethodOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.MethodOptions.features"
MethodOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.MethodOptions.uninterpreted_option" MethodOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.MethodOptions.uninterpreted_option"
) )
@ -702,6 +892,7 @@ const (
const ( const (
MethodOptions_Deprecated_field_number protoreflect.FieldNumber = 33 MethodOptions_Deprecated_field_number protoreflect.FieldNumber = 33
MethodOptions_IdempotencyLevel_field_number protoreflect.FieldNumber = 34 MethodOptions_IdempotencyLevel_field_number protoreflect.FieldNumber = 34
MethodOptions_Features_field_number protoreflect.FieldNumber = 35
MethodOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 MethodOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999
) )
@ -711,6 +902,13 @@ const (
MethodOptions_IdempotencyLevel_enum_name = "IdempotencyLevel" MethodOptions_IdempotencyLevel_enum_name = "IdempotencyLevel"
) )
// Enum values for google.protobuf.MethodOptions.IdempotencyLevel.
const (
MethodOptions_IDEMPOTENCY_UNKNOWN_enum_value = 0
MethodOptions_NO_SIDE_EFFECTS_enum_value = 1
MethodOptions_IDEMPOTENT_enum_value = 2
)
// Names for google.protobuf.UninterpretedOption. // Names for google.protobuf.UninterpretedOption.
const ( const (
UninterpretedOption_message_name protoreflect.Name = "UninterpretedOption" UninterpretedOption_message_name protoreflect.Name = "UninterpretedOption"
@ -768,6 +966,163 @@ const (
UninterpretedOption_NamePart_IsExtension_field_number protoreflect.FieldNumber = 2 UninterpretedOption_NamePart_IsExtension_field_number protoreflect.FieldNumber = 2
) )
// Names for google.protobuf.FeatureSet.
const (
FeatureSet_message_name protoreflect.Name = "FeatureSet"
FeatureSet_message_fullname protoreflect.FullName = "google.protobuf.FeatureSet"
)
// Field names for google.protobuf.FeatureSet.
const (
FeatureSet_FieldPresence_field_name protoreflect.Name = "field_presence"
FeatureSet_EnumType_field_name protoreflect.Name = "enum_type"
FeatureSet_RepeatedFieldEncoding_field_name protoreflect.Name = "repeated_field_encoding"
FeatureSet_Utf8Validation_field_name protoreflect.Name = "utf8_validation"
FeatureSet_MessageEncoding_field_name protoreflect.Name = "message_encoding"
FeatureSet_JsonFormat_field_name protoreflect.Name = "json_format"
FeatureSet_FieldPresence_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.field_presence"
FeatureSet_EnumType_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.enum_type"
FeatureSet_RepeatedFieldEncoding_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.repeated_field_encoding"
FeatureSet_Utf8Validation_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.utf8_validation"
FeatureSet_MessageEncoding_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.message_encoding"
FeatureSet_JsonFormat_field_fullname protoreflect.FullName = "google.protobuf.FeatureSet.json_format"
)
// Field numbers for google.protobuf.FeatureSet.
const (
FeatureSet_FieldPresence_field_number protoreflect.FieldNumber = 1
FeatureSet_EnumType_field_number protoreflect.FieldNumber = 2
FeatureSet_RepeatedFieldEncoding_field_number protoreflect.FieldNumber = 3
FeatureSet_Utf8Validation_field_number protoreflect.FieldNumber = 4
FeatureSet_MessageEncoding_field_number protoreflect.FieldNumber = 5
FeatureSet_JsonFormat_field_number protoreflect.FieldNumber = 6
)
// Full and short names for google.protobuf.FeatureSet.FieldPresence.
const (
FeatureSet_FieldPresence_enum_fullname = "google.protobuf.FeatureSet.FieldPresence"
FeatureSet_FieldPresence_enum_name = "FieldPresence"
)
// Enum values for google.protobuf.FeatureSet.FieldPresence.
const (
FeatureSet_FIELD_PRESENCE_UNKNOWN_enum_value = 0
FeatureSet_EXPLICIT_enum_value = 1
FeatureSet_IMPLICIT_enum_value = 2
FeatureSet_LEGACY_REQUIRED_enum_value = 3
)
// Full and short names for google.protobuf.FeatureSet.EnumType.
const (
FeatureSet_EnumType_enum_fullname = "google.protobuf.FeatureSet.EnumType"
FeatureSet_EnumType_enum_name = "EnumType"
)
// Enum values for google.protobuf.FeatureSet.EnumType.
const (
FeatureSet_ENUM_TYPE_UNKNOWN_enum_value = 0
FeatureSet_OPEN_enum_value = 1
FeatureSet_CLOSED_enum_value = 2
)
// Full and short names for google.protobuf.FeatureSet.RepeatedFieldEncoding.
const (
FeatureSet_RepeatedFieldEncoding_enum_fullname = "google.protobuf.FeatureSet.RepeatedFieldEncoding"
FeatureSet_RepeatedFieldEncoding_enum_name = "RepeatedFieldEncoding"
)
// Enum values for google.protobuf.FeatureSet.RepeatedFieldEncoding.
const (
FeatureSet_REPEATED_FIELD_ENCODING_UNKNOWN_enum_value = 0
FeatureSet_PACKED_enum_value = 1
FeatureSet_EXPANDED_enum_value = 2
)
// Full and short names for google.protobuf.FeatureSet.Utf8Validation.
const (
FeatureSet_Utf8Validation_enum_fullname = "google.protobuf.FeatureSet.Utf8Validation"
FeatureSet_Utf8Validation_enum_name = "Utf8Validation"
)
// Enum values for google.protobuf.FeatureSet.Utf8Validation.
const (
FeatureSet_UTF8_VALIDATION_UNKNOWN_enum_value = 0
FeatureSet_VERIFY_enum_value = 2
FeatureSet_NONE_enum_value = 3
)
// Full and short names for google.protobuf.FeatureSet.MessageEncoding.
const (
FeatureSet_MessageEncoding_enum_fullname = "google.protobuf.FeatureSet.MessageEncoding"
FeatureSet_MessageEncoding_enum_name = "MessageEncoding"
)
// Enum values for google.protobuf.FeatureSet.MessageEncoding.
const (
FeatureSet_MESSAGE_ENCODING_UNKNOWN_enum_value = 0
FeatureSet_LENGTH_PREFIXED_enum_value = 1
FeatureSet_DELIMITED_enum_value = 2
)
// Full and short names for google.protobuf.FeatureSet.JsonFormat.
const (
FeatureSet_JsonFormat_enum_fullname = "google.protobuf.FeatureSet.JsonFormat"
FeatureSet_JsonFormat_enum_name = "JsonFormat"
)
// Enum values for google.protobuf.FeatureSet.JsonFormat.
const (
FeatureSet_JSON_FORMAT_UNKNOWN_enum_value = 0
FeatureSet_ALLOW_enum_value = 1
FeatureSet_LEGACY_BEST_EFFORT_enum_value = 2
)
// Names for google.protobuf.FeatureSetDefaults.
const (
FeatureSetDefaults_message_name protoreflect.Name = "FeatureSetDefaults"
FeatureSetDefaults_message_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults"
)
// Field names for google.protobuf.FeatureSetDefaults.
const (
FeatureSetDefaults_Defaults_field_name protoreflect.Name = "defaults"
FeatureSetDefaults_MinimumEdition_field_name protoreflect.Name = "minimum_edition"
FeatureSetDefaults_MaximumEdition_field_name protoreflect.Name = "maximum_edition"
FeatureSetDefaults_Defaults_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.defaults"
FeatureSetDefaults_MinimumEdition_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.minimum_edition"
FeatureSetDefaults_MaximumEdition_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.maximum_edition"
)
// Field numbers for google.protobuf.FeatureSetDefaults.
const (
FeatureSetDefaults_Defaults_field_number protoreflect.FieldNumber = 1
FeatureSetDefaults_MinimumEdition_field_number protoreflect.FieldNumber = 4
FeatureSetDefaults_MaximumEdition_field_number protoreflect.FieldNumber = 5
)
// Names for google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.
const (
FeatureSetDefaults_FeatureSetEditionDefault_message_name protoreflect.Name = "FeatureSetEditionDefault"
FeatureSetDefaults_FeatureSetEditionDefault_message_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault"
)
// Field names for google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.
const (
FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_name protoreflect.Name = "edition"
FeatureSetDefaults_FeatureSetEditionDefault_Features_field_name protoreflect.Name = "features"
FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.edition"
FeatureSetDefaults_FeatureSetEditionDefault_Features_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.features"
)
// Field numbers for google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.
const (
FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_number protoreflect.FieldNumber = 3
FeatureSetDefaults_FeatureSetEditionDefault_Features_field_number protoreflect.FieldNumber = 2
)
// Names for google.protobuf.SourceCodeInfo. // Names for google.protobuf.SourceCodeInfo.
const ( const (
SourceCodeInfo_message_name protoreflect.Name = "SourceCodeInfo" SourceCodeInfo_message_name protoreflect.Name = "SourceCodeInfo"
@ -869,3 +1224,10 @@ const (
GeneratedCodeInfo_Annotation_Semantic_enum_fullname = "google.protobuf.GeneratedCodeInfo.Annotation.Semantic" GeneratedCodeInfo_Annotation_Semantic_enum_fullname = "google.protobuf.GeneratedCodeInfo.Annotation.Semantic"
GeneratedCodeInfo_Annotation_Semantic_enum_name = "Semantic" GeneratedCodeInfo_Annotation_Semantic_enum_name = "Semantic"
) )
// Enum values for google.protobuf.GeneratedCodeInfo.Annotation.Semantic.
const (
GeneratedCodeInfo_Annotation_NONE_enum_value = 0
GeneratedCodeInfo_Annotation_SET_enum_value = 1
GeneratedCodeInfo_Annotation_ALIAS_enum_value = 2
)

View File

@ -0,0 +1,31 @@
// 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.
// Code generated by generate-protos. DO NOT EDIT.
package genid
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
)
const File_reflect_protodesc_proto_go_features_proto = "reflect/protodesc/proto/go_features.proto"
// Names for google.protobuf.GoFeatures.
const (
GoFeatures_message_name protoreflect.Name = "GoFeatures"
GoFeatures_message_fullname protoreflect.FullName = "google.protobuf.GoFeatures"
)
// Field names for google.protobuf.GoFeatures.
const (
GoFeatures_LegacyUnmarshalJsonEnum_field_name protoreflect.Name = "legacy_unmarshal_json_enum"
GoFeatures_LegacyUnmarshalJsonEnum_field_fullname protoreflect.FullName = "google.protobuf.GoFeatures.legacy_unmarshal_json_enum"
)
// Field numbers for google.protobuf.GoFeatures.
const (
GoFeatures_LegacyUnmarshalJsonEnum_field_number protoreflect.FieldNumber = 1
)

View File

@ -18,6 +18,11 @@ const (
NullValue_enum_name = "NullValue" NullValue_enum_name = "NullValue"
) )
// Enum values for google.protobuf.NullValue.
const (
NullValue_NULL_VALUE_enum_value = 0
)
// Names for google.protobuf.Struct. // Names for google.protobuf.Struct.
const ( const (
Struct_message_name protoreflect.Name = "Struct" Struct_message_name protoreflect.Name = "Struct"

View File

@ -18,6 +18,13 @@ const (
Syntax_enum_name = "Syntax" Syntax_enum_name = "Syntax"
) )
// Enum values for google.protobuf.Syntax.
const (
Syntax_SYNTAX_PROTO2_enum_value = 0
Syntax_SYNTAX_PROTO3_enum_value = 1
Syntax_SYNTAX_EDITIONS_enum_value = 2
)
// Names for google.protobuf.Type. // Names for google.protobuf.Type.
const ( const (
Type_message_name protoreflect.Name = "Type" Type_message_name protoreflect.Name = "Type"
@ -32,6 +39,7 @@ const (
Type_Options_field_name protoreflect.Name = "options" Type_Options_field_name protoreflect.Name = "options"
Type_SourceContext_field_name protoreflect.Name = "source_context" Type_SourceContext_field_name protoreflect.Name = "source_context"
Type_Syntax_field_name protoreflect.Name = "syntax" Type_Syntax_field_name protoreflect.Name = "syntax"
Type_Edition_field_name protoreflect.Name = "edition"
Type_Name_field_fullname protoreflect.FullName = "google.protobuf.Type.name" Type_Name_field_fullname protoreflect.FullName = "google.protobuf.Type.name"
Type_Fields_field_fullname protoreflect.FullName = "google.protobuf.Type.fields" Type_Fields_field_fullname protoreflect.FullName = "google.protobuf.Type.fields"
@ -39,6 +47,7 @@ const (
Type_Options_field_fullname protoreflect.FullName = "google.protobuf.Type.options" Type_Options_field_fullname protoreflect.FullName = "google.protobuf.Type.options"
Type_SourceContext_field_fullname protoreflect.FullName = "google.protobuf.Type.source_context" Type_SourceContext_field_fullname protoreflect.FullName = "google.protobuf.Type.source_context"
Type_Syntax_field_fullname protoreflect.FullName = "google.protobuf.Type.syntax" Type_Syntax_field_fullname protoreflect.FullName = "google.protobuf.Type.syntax"
Type_Edition_field_fullname protoreflect.FullName = "google.protobuf.Type.edition"
) )
// Field numbers for google.protobuf.Type. // Field numbers for google.protobuf.Type.
@ -49,6 +58,7 @@ const (
Type_Options_field_number protoreflect.FieldNumber = 4 Type_Options_field_number protoreflect.FieldNumber = 4
Type_SourceContext_field_number protoreflect.FieldNumber = 5 Type_SourceContext_field_number protoreflect.FieldNumber = 5
Type_Syntax_field_number protoreflect.FieldNumber = 6 Type_Syntax_field_number protoreflect.FieldNumber = 6
Type_Edition_field_number protoreflect.FieldNumber = 7
) )
// Names for google.protobuf.Field. // Names for google.protobuf.Field.
@ -102,12 +112,43 @@ const (
Field_Kind_enum_name = "Kind" Field_Kind_enum_name = "Kind"
) )
// Enum values for google.protobuf.Field.Kind.
const (
Field_TYPE_UNKNOWN_enum_value = 0
Field_TYPE_DOUBLE_enum_value = 1
Field_TYPE_FLOAT_enum_value = 2
Field_TYPE_INT64_enum_value = 3
Field_TYPE_UINT64_enum_value = 4
Field_TYPE_INT32_enum_value = 5
Field_TYPE_FIXED64_enum_value = 6
Field_TYPE_FIXED32_enum_value = 7
Field_TYPE_BOOL_enum_value = 8
Field_TYPE_STRING_enum_value = 9
Field_TYPE_GROUP_enum_value = 10
Field_TYPE_MESSAGE_enum_value = 11
Field_TYPE_BYTES_enum_value = 12
Field_TYPE_UINT32_enum_value = 13
Field_TYPE_ENUM_enum_value = 14
Field_TYPE_SFIXED32_enum_value = 15
Field_TYPE_SFIXED64_enum_value = 16
Field_TYPE_SINT32_enum_value = 17
Field_TYPE_SINT64_enum_value = 18
)
// Full and short names for google.protobuf.Field.Cardinality. // Full and short names for google.protobuf.Field.Cardinality.
const ( const (
Field_Cardinality_enum_fullname = "google.protobuf.Field.Cardinality" Field_Cardinality_enum_fullname = "google.protobuf.Field.Cardinality"
Field_Cardinality_enum_name = "Cardinality" Field_Cardinality_enum_name = "Cardinality"
) )
// Enum values for google.protobuf.Field.Cardinality.
const (
Field_CARDINALITY_UNKNOWN_enum_value = 0
Field_CARDINALITY_OPTIONAL_enum_value = 1
Field_CARDINALITY_REQUIRED_enum_value = 2
Field_CARDINALITY_REPEATED_enum_value = 3
)
// Names for google.protobuf.Enum. // Names for google.protobuf.Enum.
const ( const (
Enum_message_name protoreflect.Name = "Enum" Enum_message_name protoreflect.Name = "Enum"
@ -121,12 +162,14 @@ const (
Enum_Options_field_name protoreflect.Name = "options" Enum_Options_field_name protoreflect.Name = "options"
Enum_SourceContext_field_name protoreflect.Name = "source_context" Enum_SourceContext_field_name protoreflect.Name = "source_context"
Enum_Syntax_field_name protoreflect.Name = "syntax" Enum_Syntax_field_name protoreflect.Name = "syntax"
Enum_Edition_field_name protoreflect.Name = "edition"
Enum_Name_field_fullname protoreflect.FullName = "google.protobuf.Enum.name" Enum_Name_field_fullname protoreflect.FullName = "google.protobuf.Enum.name"
Enum_Enumvalue_field_fullname protoreflect.FullName = "google.protobuf.Enum.enumvalue" Enum_Enumvalue_field_fullname protoreflect.FullName = "google.protobuf.Enum.enumvalue"
Enum_Options_field_fullname protoreflect.FullName = "google.protobuf.Enum.options" Enum_Options_field_fullname protoreflect.FullName = "google.protobuf.Enum.options"
Enum_SourceContext_field_fullname protoreflect.FullName = "google.protobuf.Enum.source_context" Enum_SourceContext_field_fullname protoreflect.FullName = "google.protobuf.Enum.source_context"
Enum_Syntax_field_fullname protoreflect.FullName = "google.protobuf.Enum.syntax" Enum_Syntax_field_fullname protoreflect.FullName = "google.protobuf.Enum.syntax"
Enum_Edition_field_fullname protoreflect.FullName = "google.protobuf.Enum.edition"
) )
// Field numbers for google.protobuf.Enum. // Field numbers for google.protobuf.Enum.
@ -136,6 +179,7 @@ const (
Enum_Options_field_number protoreflect.FieldNumber = 3 Enum_Options_field_number protoreflect.FieldNumber = 3
Enum_SourceContext_field_number protoreflect.FieldNumber = 4 Enum_SourceContext_field_number protoreflect.FieldNumber = 4
Enum_Syntax_field_number protoreflect.FieldNumber = 5 Enum_Syntax_field_number protoreflect.FieldNumber = 5
Enum_Edition_field_number protoreflect.FieldNumber = 6
) )
// Names for google.protobuf.EnumValue. // Names for google.protobuf.EnumValue.

View File

@ -21,26 +21,18 @@ type extensionFieldInfo struct {
validation validationInfo validation validationInfo
} }
var legacyExtensionFieldInfoCache sync.Map // map[protoreflect.ExtensionType]*extensionFieldInfo
func getExtensionFieldInfo(xt protoreflect.ExtensionType) *extensionFieldInfo { func getExtensionFieldInfo(xt protoreflect.ExtensionType) *extensionFieldInfo {
if xi, ok := xt.(*ExtensionInfo); ok { if xi, ok := xt.(*ExtensionInfo); ok {
xi.lazyInit() xi.lazyInit()
return xi.info return xi.info
} }
return legacyLoadExtensionFieldInfo(xt) // Ideally we'd cache the resulting *extensionFieldInfo so we don't have to
} // recompute this metadata repeatedly. But without support for something like
// weak references, such a cache would pin temporary values (like dynamic
// legacyLoadExtensionFieldInfo dynamically loads a *ExtensionInfo for xt. // extension types, constructed for the duration of a user request) to the
func legacyLoadExtensionFieldInfo(xt protoreflect.ExtensionType) *extensionFieldInfo { // heap forever, causing memory usage of the cache to grow unbounded.
if xi, ok := legacyExtensionFieldInfoCache.Load(xt); ok { // See discussion in https://github.com/golang/protobuf/issues/1521.
return xi.(*extensionFieldInfo) return makeExtensionFieldInfo(xt.TypeDescriptor())
}
e := makeExtensionFieldInfo(xt.TypeDescriptor())
if e, ok := legacyMessageTypeCache.LoadOrStore(xt, e); ok {
return e.(*extensionFieldInfo)
}
return e
} }
func makeExtensionFieldInfo(xd protoreflect.ExtensionDescriptor) *extensionFieldInfo { func makeExtensionFieldInfo(xd protoreflect.ExtensionDescriptor) *extensionFieldInfo {

View File

@ -162,11 +162,20 @@ func appendBoolSlice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions
func consumeBoolSlice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeBoolSlice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.BoolSlice() sp := p.BoolSlice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := 0
for _, v := range b {
if v < 0x80 {
count++
}
}
if count > 0 {
p.growBoolSlice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
var v uint64 var v uint64
var n int var n int
@ -732,11 +741,20 @@ func appendInt32Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOption
func consumeInt32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeInt32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Int32Slice() sp := p.Int32Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := 0
for _, v := range b {
if v < 0x80 {
count++
}
}
if count > 0 {
p.growInt32Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
var v uint64 var v uint64
var n int var n int
@ -1138,11 +1156,20 @@ func appendSint32Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptio
func consumeSint32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeSint32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Int32Slice() sp := p.Int32Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := 0
for _, v := range b {
if v < 0x80 {
count++
}
}
if count > 0 {
p.growInt32Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
var v uint64 var v uint64
var n int var n int
@ -1544,11 +1571,20 @@ func appendUint32Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptio
func consumeUint32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeUint32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Uint32Slice() sp := p.Uint32Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := 0
for _, v := range b {
if v < 0x80 {
count++
}
}
if count > 0 {
p.growUint32Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
var v uint64 var v uint64
var n int var n int
@ -1950,11 +1986,20 @@ func appendInt64Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOption
func consumeInt64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeInt64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Int64Slice() sp := p.Int64Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := 0
for _, v := range b {
if v < 0x80 {
count++
}
}
if count > 0 {
p.growInt64Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
var v uint64 var v uint64
var n int var n int
@ -2356,11 +2401,20 @@ func appendSint64Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptio
func consumeSint64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeSint64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Int64Slice() sp := p.Int64Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := 0
for _, v := range b {
if v < 0x80 {
count++
}
}
if count > 0 {
p.growInt64Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
var v uint64 var v uint64
var n int var n int
@ -2762,11 +2816,20 @@ func appendUint64Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptio
func consumeUint64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeUint64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Uint64Slice() sp := p.Uint64Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := 0
for _, v := range b {
if v < 0x80 {
count++
}
}
if count > 0 {
p.growUint64Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
var v uint64 var v uint64
var n int var n int
@ -3145,11 +3208,15 @@ func appendSfixed32Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOpt
func consumeSfixed32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeSfixed32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Int32Slice() sp := p.Int32Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := len(b) / protowire.SizeFixed32()
if count > 0 {
p.growInt32Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
v, n := protowire.ConsumeFixed32(b) v, n := protowire.ConsumeFixed32(b)
if n < 0 { if n < 0 {
@ -3461,11 +3528,15 @@ func appendFixed32Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOpti
func consumeFixed32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeFixed32Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Uint32Slice() sp := p.Uint32Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := len(b) / protowire.SizeFixed32()
if count > 0 {
p.growUint32Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
v, n := protowire.ConsumeFixed32(b) v, n := protowire.ConsumeFixed32(b)
if n < 0 { if n < 0 {
@ -3777,11 +3848,15 @@ func appendFloatSlice(b []byte, p pointer, f *coderFieldInfo, opts marshalOption
func consumeFloatSlice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeFloatSlice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Float32Slice() sp := p.Float32Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := len(b) / protowire.SizeFixed32()
if count > 0 {
p.growFloat32Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
v, n := protowire.ConsumeFixed32(b) v, n := protowire.ConsumeFixed32(b)
if n < 0 { if n < 0 {
@ -4093,11 +4168,15 @@ func appendSfixed64Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOpt
func consumeSfixed64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeSfixed64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Int64Slice() sp := p.Int64Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := len(b) / protowire.SizeFixed64()
if count > 0 {
p.growInt64Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
v, n := protowire.ConsumeFixed64(b) v, n := protowire.ConsumeFixed64(b)
if n < 0 { if n < 0 {
@ -4409,11 +4488,15 @@ func appendFixed64Slice(b []byte, p pointer, f *coderFieldInfo, opts marshalOpti
func consumeFixed64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeFixed64Slice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Uint64Slice() sp := p.Uint64Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := len(b) / protowire.SizeFixed64()
if count > 0 {
p.growUint64Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
v, n := protowire.ConsumeFixed64(b) v, n := protowire.ConsumeFixed64(b)
if n < 0 { if n < 0 {
@ -4725,11 +4808,15 @@ func appendDoubleSlice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptio
func consumeDoubleSlice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { func consumeDoubleSlice(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
sp := p.Float64Slice() sp := p.Float64Slice()
if wtyp == protowire.BytesType { if wtyp == protowire.BytesType {
s := *sp
b, n := protowire.ConsumeBytes(b) b, n := protowire.ConsumeBytes(b)
if n < 0 { if n < 0 {
return out, errDecode return out, errDecode
} }
count := len(b) / protowire.SizeFixed64()
if count > 0 {
p.growFloat64Slice(count)
}
s := *sp
for len(b) > 0 { for len(b) > 0 {
v, n := protowire.ConsumeFixed64(b) v, n := protowire.ConsumeFixed64(b)
if n < 0 { if n < 0 {

View File

@ -197,7 +197,7 @@ func fieldCoder(fd protoreflect.FieldDescriptor, ft reflect.Type) (*MessageInfo,
return getMessageInfo(ft), makeMessageFieldCoder(fd, ft) return getMessageInfo(ft), makeMessageFieldCoder(fd, ft)
case fd.Kind() == protoreflect.GroupKind: case fd.Kind() == protoreflect.GroupKind:
return getMessageInfo(ft), makeGroupFieldCoder(fd, ft) return getMessageInfo(ft), makeGroupFieldCoder(fd, ft)
case fd.Syntax() == protoreflect.Proto3 && fd.ContainingOneof() == nil: case !fd.HasPresence() && fd.ContainingOneof() == nil:
// Populated oneof fields always encode even if set to the zero value, // Populated oneof fields always encode even if set to the zero value,
// which normally are not encoded in proto3. // which normally are not encoded in proto3.
switch fd.Kind() { switch fd.Kind() {

View File

@ -206,13 +206,18 @@ func aberrantLoadMessageDescReentrant(t reflect.Type, name protoreflect.FullName
// Obtain a list of oneof wrapper types. // Obtain a list of oneof wrapper types.
var oneofWrappers []reflect.Type var oneofWrappers []reflect.Type
for _, method := range []string{"XXX_OneofFuncs", "XXX_OneofWrappers"} { methods := make([]reflect.Method, 0, 2)
if fn, ok := t.MethodByName(method); ok { if m, ok := t.MethodByName("XXX_OneofFuncs"); ok {
for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) { methods = append(methods, m)
if vs, ok := v.Interface().([]interface{}); ok { }
for _, v := range vs { if m, ok := t.MethodByName("XXX_OneofWrappers"); ok {
oneofWrappers = append(oneofWrappers, reflect.TypeOf(v)) methods = append(methods, m)
} }
for _, fn := range methods {
for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) {
if vs, ok := v.Interface().([]interface{}); ok {
for _, v := range vs {
oneofWrappers = append(oneofWrappers, reflect.TypeOf(v))
} }
} }
} }

View File

@ -192,12 +192,17 @@ fieldLoop:
// Derive a mapping of oneof wrappers to fields. // Derive a mapping of oneof wrappers to fields.
oneofWrappers := mi.OneofWrappers oneofWrappers := mi.OneofWrappers
for _, method := range []string{"XXX_OneofFuncs", "XXX_OneofWrappers"} { methods := make([]reflect.Method, 0, 2)
if fn, ok := reflect.PtrTo(t).MethodByName(method); ok { if m, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok {
for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) { methods = append(methods, m)
if vs, ok := v.Interface().([]interface{}); ok { }
oneofWrappers = vs if m, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok {
} methods = append(methods, m)
}
for _, fn := range methods {
for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) {
if vs, ok := v.Interface().([]interface{}); ok {
oneofWrappers = vs
} }
} }
} }

View File

@ -538,6 +538,6 @@ func isZero(v reflect.Value) bool {
} }
return true return true
default: default:
panic(&reflect.ValueError{"reflect.Value.IsZero", v.Kind()}) panic(&reflect.ValueError{Method: "reflect.Value.IsZero", Kind: v.Kind()})
} }
} }

View File

@ -159,6 +159,42 @@ func (p pointer) SetPointer(v pointer) {
p.v.Elem().Set(v.v) p.v.Elem().Set(v.v)
} }
func growSlice(p pointer, addCap int) {
// TODO: Once we only support Go 1.20 and newer, use reflect.Grow.
in := p.v.Elem()
out := reflect.MakeSlice(in.Type(), in.Len(), in.Len()+addCap)
reflect.Copy(out, in)
p.v.Elem().Set(out)
}
func (p pointer) growBoolSlice(addCap int) {
growSlice(p, addCap)
}
func (p pointer) growInt32Slice(addCap int) {
growSlice(p, addCap)
}
func (p pointer) growUint32Slice(addCap int) {
growSlice(p, addCap)
}
func (p pointer) growInt64Slice(addCap int) {
growSlice(p, addCap)
}
func (p pointer) growUint64Slice(addCap int) {
growSlice(p, addCap)
}
func (p pointer) growFloat64Slice(addCap int) {
growSlice(p, addCap)
}
func (p pointer) growFloat32Slice(addCap int) {
growSlice(p, addCap)
}
func (Export) MessageStateOf(p Pointer) *messageState { panic("not supported") } func (Export) MessageStateOf(p Pointer) *messageState { panic("not supported") }
func (ms *messageState) pointer() pointer { panic("not supported") } func (ms *messageState) pointer() pointer { panic("not supported") }
func (ms *messageState) messageInfo() *MessageInfo { panic("not supported") } func (ms *messageState) messageInfo() *MessageInfo { panic("not supported") }

View File

@ -138,6 +138,46 @@ func (p pointer) SetPointer(v pointer) {
*(*unsafe.Pointer)(p.p) = (unsafe.Pointer)(v.p) *(*unsafe.Pointer)(p.p) = (unsafe.Pointer)(v.p)
} }
func (p pointer) growBoolSlice(addCap int) {
sp := p.BoolSlice()
s := make([]bool, 0, addCap+len(*sp))
s = s[:len(*sp)]
copy(s, *sp)
*sp = s
}
func (p pointer) growInt32Slice(addCap int) {
sp := p.Int32Slice()
s := make([]int32, 0, addCap+len(*sp))
s = s[:len(*sp)]
copy(s, *sp)
*sp = s
}
func (p pointer) growUint32Slice(addCap int) {
p.growInt32Slice(addCap)
}
func (p pointer) growFloat32Slice(addCap int) {
p.growInt32Slice(addCap)
}
func (p pointer) growInt64Slice(addCap int) {
sp := p.Int64Slice()
s := make([]int64, 0, addCap+len(*sp))
s = s[:len(*sp)]
copy(s, *sp)
*sp = s
}
func (p pointer) growUint64Slice(addCap int) {
p.growInt64Slice(addCap)
}
func (p pointer) growFloat64Slice(addCap int) {
p.growInt64Slice(addCap)
}
// Static check that MessageState does not exceed the size of a pointer. // Static check that MessageState does not exceed the size of a pointer.
const _ = uint(unsafe.Sizeof(unsafe.Pointer(nil)) - unsafe.Sizeof(MessageState{})) const _ = uint(unsafe.Sizeof(unsafe.Pointer(nil)) - unsafe.Sizeof(MessageState{}))

View File

@ -33,7 +33,7 @@ var (
return !inOneof(ox) && inOneof(oy) return !inOneof(ox) && inOneof(oy)
} }
// Fields in disjoint oneof sets are sorted by declaration index. // Fields in disjoint oneof sets are sorted by declaration index.
if ox != nil && oy != nil && ox != oy { if inOneof(ox) && inOneof(oy) && ox != oy {
return ox.Index() < oy.Index() return ox.Index() < oy.Index()
} }
// Fields sorted by field number. // Fields sorted by field number.

View File

@ -17,7 +17,7 @@ import (
// EnforceUTF8 reports whether to enforce strict UTF-8 validation. // EnforceUTF8 reports whether to enforce strict UTF-8 validation.
func EnforceUTF8(fd protoreflect.FieldDescriptor) bool { func EnforceUTF8(fd protoreflect.FieldDescriptor) bool {
if flags.ProtoLegacy { if flags.ProtoLegacy || fd.Syntax() == protoreflect.Editions {
if fd, ok := fd.(interface{ EnforceUTF8() bool }); ok { if fd, ok := fd.(interface{ EnforceUTF8() bool }); ok {
return fd.EnforceUTF8() return fd.EnforceUTF8()
} }

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !purego && !appengine //go:build !purego && !appengine && !go1.21
// +build !purego,!appengine // +build !purego,!appengine,!go1.21
package strs package strs

View File

@ -0,0 +1,74 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !purego && !appengine && go1.21
// +build !purego,!appengine,go1.21
package strs
import (
"unsafe"
"google.golang.org/protobuf/reflect/protoreflect"
)
// UnsafeString returns an unsafe string reference of b.
// The caller must treat the input slice as immutable.
//
// WARNING: Use carefully. The returned result must not leak to the end user
// unless the input slice is provably immutable.
func UnsafeString(b []byte) string {
return unsafe.String(unsafe.SliceData(b), len(b))
}
// UnsafeBytes returns an unsafe bytes slice reference of s.
// The caller must treat returned slice as immutable.
//
// WARNING: Use carefully. The returned result must not leak to the end user.
func UnsafeBytes(s string) []byte {
return unsafe.Slice(unsafe.StringData(s), len(s))
}
// Builder builds a set of strings with shared lifetime.
// This differs from strings.Builder, which is for building a single string.
type Builder struct {
buf []byte
}
// AppendFullName is equivalent to protoreflect.FullName.Append,
// but optimized for large batches where each name has a shared lifetime.
func (sb *Builder) AppendFullName(prefix protoreflect.FullName, name protoreflect.Name) protoreflect.FullName {
n := len(prefix) + len(".") + len(name)
if len(prefix) == 0 {
n -= len(".")
}
sb.grow(n)
sb.buf = append(sb.buf, prefix...)
sb.buf = append(sb.buf, '.')
sb.buf = append(sb.buf, name...)
return protoreflect.FullName(sb.last(n))
}
// MakeString is equivalent to string(b), but optimized for large batches
// with a shared lifetime.
func (sb *Builder) MakeString(b []byte) string {
sb.grow(len(b))
sb.buf = append(sb.buf, b...)
return sb.last(len(b))
}
func (sb *Builder) grow(n int) {
if cap(sb.buf)-len(sb.buf) >= n {
return
}
// Unlike strings.Builder, we do not need to copy over the contents
// of the old buffer since our builder provides no API for
// retrieving previously created strings.
sb.buf = make([]byte, 0, 2*(cap(sb.buf)+n))
}
func (sb *Builder) last(n int) string {
return UnsafeString(sb.buf[len(sb.buf)-n:])
}

View File

@ -51,7 +51,7 @@ import (
// 10. Send out the CL for review and submit it. // 10. Send out the CL for review and submit it.
const ( const (
Major = 1 Major = 1
Minor = 30 Minor = 33
Patch = 0 Patch = 0
PreRelease = "" PreRelease = ""
) )

View File

@ -69,7 +69,7 @@ func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error {
// UnmarshalState parses a wire-format message and places the result in m. // UnmarshalState parses a wire-format message and places the result in m.
// //
// This method permits fine-grained control over the unmarshaler. // This method permits fine-grained control over the unmarshaler.
// Most users should use Unmarshal instead. // Most users should use [Unmarshal] instead.
func (o UnmarshalOptions) UnmarshalState(in protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { func (o UnmarshalOptions) UnmarshalState(in protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) {
if o.RecursionLimit == 0 { if o.RecursionLimit == 0 {
o.RecursionLimit = protowire.DefaultRecursionLimit o.RecursionLimit = protowire.DefaultRecursionLimit

View File

@ -18,27 +18,27 @@
// This package contains functions to convert to and from the wire format, // This package contains functions to convert to and from the wire format,
// an efficient binary serialization of protocol buffers. // an efficient binary serialization of protocol buffers.
// //
// • Size reports the size of a message in the wire format. // - [Size] reports the size of a message in the wire format.
// //
// • Marshal converts a message to the wire format. // - [Marshal] converts a message to the wire format.
// The MarshalOptions type provides more control over wire marshaling. // The [MarshalOptions] type provides more control over wire marshaling.
// //
// • Unmarshal converts a message from the wire format. // - [Unmarshal] converts a message from the wire format.
// The UnmarshalOptions type provides more control over wire unmarshaling. // The [UnmarshalOptions] type provides more control over wire unmarshaling.
// //
// # Basic message operations // # Basic message operations
// //
// • Clone makes a deep copy of a message. // - [Clone] makes a deep copy of a message.
// //
// • Merge merges the content of a message into another. // - [Merge] merges the content of a message into another.
// //
// • Equal compares two messages. For more control over comparisons // - [Equal] compares two messages. For more control over comparisons
// and detailed reporting of differences, see package // and detailed reporting of differences, see package
// "google.golang.org/protobuf/testing/protocmp". // [google.golang.org/protobuf/testing/protocmp].
// //
// • Reset clears the content of a message. // - [Reset] clears the content of a message.
// //
// • CheckInitialized reports whether all required fields in a message are set. // - [CheckInitialized] reports whether all required fields in a message are set.
// //
// # Optional scalar constructors // # Optional scalar constructors
// //
@ -46,9 +46,9 @@
// as pointers to a value. For example, an optional string field has the // as pointers to a value. For example, an optional string field has the
// Go type *string. // Go type *string.
// //
// • Bool, Int32, Int64, Uint32, Uint64, Float32, Float64, and String // - [Bool], [Int32], [Int64], [Uint32], [Uint64], [Float32], [Float64], and [String]
// take a value and return a pointer to a new instance of it, // take a value and return a pointer to a new instance of it,
// to simplify construction of optional field values. // to simplify construction of optional field values.
// //
// Generated enum types usually have an Enum method which performs the // Generated enum types usually have an Enum method which performs the
// same operation. // same operation.
@ -57,29 +57,29 @@
// //
// # Extension accessors // # Extension accessors
// //
// • HasExtension, GetExtension, SetExtension, and ClearExtension // - [HasExtension], [GetExtension], [SetExtension], and [ClearExtension]
// access extension field values in a protocol buffer message. // access extension field values in a protocol buffer message.
// //
// Extension fields are only supported in proto2. // Extension fields are only supported in proto2.
// //
// # Related packages // # Related packages
// //
// • Package "google.golang.org/protobuf/encoding/protojson" converts messages to // - Package [google.golang.org/protobuf/encoding/protojson] converts messages to
// and from JSON. // and from JSON.
// //
// • Package "google.golang.org/protobuf/encoding/prototext" converts messages to // - Package [google.golang.org/protobuf/encoding/prototext] converts messages to
// and from the text format. // and from the text format.
// //
// • Package "google.golang.org/protobuf/reflect/protoreflect" provides a // - Package [google.golang.org/protobuf/reflect/protoreflect] provides a
// reflection interface for protocol buffer data types. // reflection interface for protocol buffer data types.
// //
// • Package "google.golang.org/protobuf/testing/protocmp" provides features // - Package [google.golang.org/protobuf/testing/protocmp] provides features
// to compare protocol buffer messages with the "github.com/google/go-cmp/cmp" // to compare protocol buffer messages with the [github.com/google/go-cmp/cmp]
// package. // package.
// //
// • Package "google.golang.org/protobuf/types/dynamicpb" provides a dynamic // - Package [google.golang.org/protobuf/types/dynamicpb] provides a dynamic
// message type, suitable for working with messages where the protocol buffer // message type, suitable for working with messages where the protocol buffer
// type is only known at runtime. // type is only known at runtime.
// //
// This module contains additional packages for more specialized use cases. // This module contains additional packages for more specialized use cases.
// Consult the individual package documentation for details. // Consult the individual package documentation for details.

View File

@ -129,7 +129,7 @@ func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) {
// MarshalState returns the wire-format encoding of a message. // MarshalState returns the wire-format encoding of a message.
// //
// This method permits fine-grained control over the marshaler. // This method permits fine-grained control over the marshaler.
// Most users should use Marshal instead. // Most users should use [Marshal] instead.
func (o MarshalOptions) MarshalState(in protoiface.MarshalInput) (protoiface.MarshalOutput, error) { func (o MarshalOptions) MarshalState(in protoiface.MarshalInput) (protoiface.MarshalOutput, error) {
return o.marshal(in.Buf, in.Message) return o.marshal(in.Buf, in.Message)
} }

View File

@ -26,7 +26,7 @@ func HasExtension(m Message, xt protoreflect.ExtensionType) bool {
} }
// ClearExtension clears an extension field such that subsequent // ClearExtension clears an extension field such that subsequent
// HasExtension calls return false. // [HasExtension] calls return false.
// It panics if m is invalid or if xt does not extend m. // It panics if m is invalid or if xt does not extend m.
func ClearExtension(m Message, xt protoreflect.ExtensionType) { func ClearExtension(m Message, xt protoreflect.ExtensionType) {
m.ProtoReflect().Clear(xt.TypeDescriptor()) m.ProtoReflect().Clear(xt.TypeDescriptor())

View File

@ -21,7 +21,7 @@ import (
// The unknown fields of src are appended to the unknown fields of dst. // The unknown fields of src are appended to the unknown fields of dst.
// //
// It is semantically equivalent to unmarshaling the encoded form of src // It is semantically equivalent to unmarshaling the encoded form of src
// into dst with the UnmarshalOptions.Merge option specified. // into dst with the [UnmarshalOptions.Merge] option specified.
func Merge(dst, src Message) { func Merge(dst, src Message) {
// TODO: Should nil src be treated as semantically equivalent to a // TODO: Should nil src be treated as semantically equivalent to a
// untyped, read-only, empty message? What about a nil dst? // untyped, read-only, empty message? What about a nil dst?

View File

@ -15,18 +15,20 @@ import (
// protobuf module that accept a Message, except where otherwise specified. // protobuf module that accept a Message, except where otherwise specified.
// //
// This is the v2 interface definition for protobuf messages. // This is the v2 interface definition for protobuf messages.
// The v1 interface definition is "github.com/golang/protobuf/proto".Message. // The v1 interface definition is [github.com/golang/protobuf/proto.Message].
// //
// To convert a v1 message to a v2 message, // - To convert a v1 message to a v2 message,
// use "github.com/golang/protobuf/proto".MessageV2. // use [google.golang.org/protobuf/protoadapt.MessageV2Of].
// To convert a v2 message to a v1 message, // - To convert a v2 message to a v1 message,
// use "github.com/golang/protobuf/proto".MessageV1. // use [google.golang.org/protobuf/protoadapt.MessageV1Of].
type Message = protoreflect.ProtoMessage type Message = protoreflect.ProtoMessage
// Error matches all errors produced by packages in the protobuf module. // Error matches all errors produced by packages in the protobuf module
// according to [errors.Is].
// //
// That is, errors.Is(err, Error) reports whether an error is produced // Example usage:
// by this module. //
// if errors.Is(err, proto.Error) { ... }
var Error error var Error error
func init() { func init() {

View File

@ -73,23 +73,27 @@ func (o MarshalOptions) sizeField(fd protoreflect.FieldDescriptor, value protore
} }
func (o MarshalOptions) sizeList(num protowire.Number, fd protoreflect.FieldDescriptor, list protoreflect.List) (size int) { func (o MarshalOptions) sizeList(num protowire.Number, fd protoreflect.FieldDescriptor, list protoreflect.List) (size int) {
sizeTag := protowire.SizeTag(num)
if fd.IsPacked() && list.Len() > 0 { if fd.IsPacked() && list.Len() > 0 {
content := 0 content := 0
for i, llen := 0, list.Len(); i < llen; i++ { for i, llen := 0, list.Len(); i < llen; i++ {
content += o.sizeSingular(num, fd.Kind(), list.Get(i)) content += o.sizeSingular(num, fd.Kind(), list.Get(i))
} }
return protowire.SizeTag(num) + protowire.SizeBytes(content) return sizeTag + protowire.SizeBytes(content)
} }
for i, llen := 0, list.Len(); i < llen; i++ { for i, llen := 0, list.Len(); i < llen; i++ {
size += protowire.SizeTag(num) + o.sizeSingular(num, fd.Kind(), list.Get(i)) size += sizeTag + o.sizeSingular(num, fd.Kind(), list.Get(i))
} }
return size return size
} }
func (o MarshalOptions) sizeMap(num protowire.Number, fd protoreflect.FieldDescriptor, mapv protoreflect.Map) (size int) { func (o MarshalOptions) sizeMap(num protowire.Number, fd protoreflect.FieldDescriptor, mapv protoreflect.Map) (size int) {
sizeTag := protowire.SizeTag(num)
mapv.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool { mapv.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool {
size += protowire.SizeTag(num) size += sizeTag
size += protowire.SizeBytes(o.sizeField(fd.MapKey(), key.Value()) + o.sizeField(fd.MapValue(), value)) size += protowire.SizeBytes(o.sizeField(fd.MapKey(), key.Value()) + o.sizeField(fd.MapValue(), value))
return true return true
}) })

View File

@ -3,11 +3,11 @@
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package protodesc provides functionality for converting // Package protodesc provides functionality for converting
// FileDescriptorProto messages to/from protoreflect.FileDescriptor values. // FileDescriptorProto messages to/from [protoreflect.FileDescriptor] values.
// //
// The google.protobuf.FileDescriptorProto is a protobuf message that describes // The google.protobuf.FileDescriptorProto is a protobuf message that describes
// the type information for a .proto file in a form that is easily serializable. // the type information for a .proto file in a form that is easily serializable.
// The protoreflect.FileDescriptor is a more structured representation of // The [protoreflect.FileDescriptor] is a more structured representation of
// the FileDescriptorProto message where references and remote dependencies // the FileDescriptorProto message where references and remote dependencies
// can be directly followed. // can be directly followed.
package protodesc package protodesc
@ -24,11 +24,11 @@ import (
"google.golang.org/protobuf/types/descriptorpb" "google.golang.org/protobuf/types/descriptorpb"
) )
// Resolver is the resolver used by NewFile to resolve dependencies. // Resolver is the resolver used by [NewFile] to resolve dependencies.
// The enums and messages provided must belong to some parent file, // The enums and messages provided must belong to some parent file,
// which is also registered. // which is also registered.
// //
// It is implemented by protoregistry.Files. // It is implemented by [protoregistry.Files].
type Resolver interface { type Resolver interface {
FindFileByPath(string) (protoreflect.FileDescriptor, error) FindFileByPath(string) (protoreflect.FileDescriptor, error)
FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error) FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error)
@ -61,19 +61,19 @@ type FileOptions struct {
AllowUnresolvable bool AllowUnresolvable bool
} }
// NewFile creates a new protoreflect.FileDescriptor from the provided // NewFile creates a new [protoreflect.FileDescriptor] from the provided
// file descriptor message. See FileOptions.New for more information. // file descriptor message. See [FileOptions.New] for more information.
func NewFile(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) { func NewFile(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error) {
return FileOptions{}.New(fd, r) return FileOptions{}.New(fd, r)
} }
// NewFiles creates a new protoregistry.Files from the provided // NewFiles creates a new [protoregistry.Files] from the provided
// FileDescriptorSet message. See FileOptions.NewFiles for more information. // FileDescriptorSet message. See [FileOptions.NewFiles] for more information.
func NewFiles(fd *descriptorpb.FileDescriptorSet) (*protoregistry.Files, error) { func NewFiles(fd *descriptorpb.FileDescriptorSet) (*protoregistry.Files, error) {
return FileOptions{}.NewFiles(fd) return FileOptions{}.NewFiles(fd)
} }
// New creates a new protoreflect.FileDescriptor from the provided // New creates a new [protoreflect.FileDescriptor] from the provided
// file descriptor message. The file must represent a valid proto file according // file descriptor message. The file must represent a valid proto file according
// to protobuf semantics. The returned descriptor is a deep copy of the input. // to protobuf semantics. The returned descriptor is a deep copy of the input.
// //
@ -93,9 +93,15 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot
f.L1.Syntax = protoreflect.Proto2 f.L1.Syntax = protoreflect.Proto2
case "proto3": case "proto3":
f.L1.Syntax = protoreflect.Proto3 f.L1.Syntax = protoreflect.Proto3
case "editions":
f.L1.Syntax = protoreflect.Editions
f.L1.Edition = fromEditionProto(fd.GetEdition())
default: default:
return nil, errors.New("invalid syntax: %q", fd.GetSyntax()) return nil, errors.New("invalid syntax: %q", fd.GetSyntax())
} }
if f.L1.Syntax == protoreflect.Editions && (fd.GetEdition() < SupportedEditionsMinimum || fd.GetEdition() > SupportedEditionsMaximum) {
return nil, errors.New("use of edition %v not yet supported by the Go Protobuf runtime", fd.GetEdition())
}
f.L1.Path = fd.GetName() f.L1.Path = fd.GetName()
if f.L1.Path == "" { if f.L1.Path == "" {
return nil, errors.New("file path must be populated") return nil, errors.New("file path must be populated")
@ -108,6 +114,9 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot
opts = proto.Clone(opts).(*descriptorpb.FileOptions) opts = proto.Clone(opts).(*descriptorpb.FileOptions)
f.L2.Options = func() protoreflect.ProtoMessage { return opts } f.L2.Options = func() protoreflect.ProtoMessage { return opts }
} }
if f.L1.Syntax == protoreflect.Editions {
initFileDescFromFeatureSet(f, fd.GetOptions().GetFeatures())
}
f.L2.Imports = make(filedesc.FileImports, len(fd.GetDependency())) f.L2.Imports = make(filedesc.FileImports, len(fd.GetDependency()))
for _, i := range fd.GetPublicDependency() { for _, i := range fd.GetPublicDependency() {
@ -231,7 +240,7 @@ func (is importSet) importPublic(imps protoreflect.FileImports) {
} }
} }
// NewFiles creates a new protoregistry.Files from the provided // NewFiles creates a new [protoregistry.Files] from the provided
// FileDescriptorSet message. The descriptor set must include only // FileDescriptorSet message. The descriptor set must include only
// valid files according to protobuf semantics. The returned descriptors // valid files according to protobuf semantics. The returned descriptors
// are a deep copy of the input. // are a deep copy of the input.

View File

@ -28,6 +28,7 @@ func (r descsByName) initEnumDeclarations(eds []*descriptorpb.EnumDescriptorProt
opts = proto.Clone(opts).(*descriptorpb.EnumOptions) opts = proto.Clone(opts).(*descriptorpb.EnumOptions)
e.L2.Options = func() protoreflect.ProtoMessage { return opts } e.L2.Options = func() protoreflect.ProtoMessage { return opts }
} }
e.L1.EditionFeatures = mergeEditionFeatures(parent, ed.GetOptions().GetFeatures())
for _, s := range ed.GetReservedName() { for _, s := range ed.GetReservedName() {
e.L2.ReservedNames.List = append(e.L2.ReservedNames.List, protoreflect.Name(s)) e.L2.ReservedNames.List = append(e.L2.ReservedNames.List, protoreflect.Name(s))
} }
@ -68,6 +69,9 @@ func (r descsByName) initMessagesDeclarations(mds []*descriptorpb.DescriptorProt
if m.L0, err = r.makeBase(m, parent, md.GetName(), i, sb); err != nil { if m.L0, err = r.makeBase(m, parent, md.GetName(), i, sb); err != nil {
return nil, err return nil, err
} }
if m.Base.L0.ParentFile.Syntax() == protoreflect.Editions {
m.L1.EditionFeatures = mergeEditionFeatures(parent, md.GetOptions().GetFeatures())
}
if opts := md.GetOptions(); opts != nil { if opts := md.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.MessageOptions) opts = proto.Clone(opts).(*descriptorpb.MessageOptions)
m.L2.Options = func() protoreflect.ProtoMessage { return opts } m.L2.Options = func() protoreflect.ProtoMessage { return opts }
@ -114,6 +118,27 @@ func (r descsByName) initMessagesDeclarations(mds []*descriptorpb.DescriptorProt
return ms, nil return ms, nil
} }
// canBePacked returns whether the field can use packed encoding:
// https://protobuf.dev/programming-guides/encoding/#packed
func canBePacked(fd *descriptorpb.FieldDescriptorProto) bool {
if fd.GetLabel() != descriptorpb.FieldDescriptorProto_LABEL_REPEATED {
return false // not a repeated field
}
switch protoreflect.Kind(fd.GetType()) {
case protoreflect.MessageKind, protoreflect.GroupKind:
return false // not a scalar type field
case protoreflect.StringKind, protoreflect.BytesKind:
// string and bytes can explicitly not be declared as packed,
// see https://protobuf.dev/programming-guides/encoding/#packed
return false
default:
return true
}
}
func (r descsByName) initFieldsFromDescriptorProto(fds []*descriptorpb.FieldDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (fs []filedesc.Field, err error) { func (r descsByName) initFieldsFromDescriptorProto(fds []*descriptorpb.FieldDescriptorProto, parent protoreflect.Descriptor, sb *strs.Builder) (fs []filedesc.Field, err error) {
fs = make([]filedesc.Field, len(fds)) // allocate up-front to ensure stable pointers fs = make([]filedesc.Field, len(fds)) // allocate up-front to ensure stable pointers
for i, fd := range fds { for i, fd := range fds {
@ -137,6 +162,34 @@ func (r descsByName) initFieldsFromDescriptorProto(fds []*descriptorpb.FieldDesc
if fd.JsonName != nil { if fd.JsonName != nil {
f.L1.StringName.InitJSON(fd.GetJsonName()) f.L1.StringName.InitJSON(fd.GetJsonName())
} }
if f.Base.L0.ParentFile.Syntax() == protoreflect.Editions {
f.L1.EditionFeatures = mergeEditionFeatures(parent, fd.GetOptions().GetFeatures())
if f.L1.EditionFeatures.IsLegacyRequired {
f.L1.Cardinality = protoreflect.Required
}
// We reuse the existing field because the old option `[packed =
// true]` is mutually exclusive with the editions feature.
if canBePacked(fd) {
f.L1.HasPacked = true
f.L1.IsPacked = f.L1.EditionFeatures.IsPacked
}
// We pretend this option is always explicitly set because the only
// use of HasEnforceUTF8 is to determine whether to use EnforceUTF8
// or to return the appropriate default.
// When using editions we either parse the option or resolve the
// appropriate default here (instead of later when this option is
// requested from the descriptor).
// In proto2/proto3 syntax HasEnforceUTF8 might be false.
f.L1.HasEnforceUTF8 = true
f.L1.EnforceUTF8 = f.L1.EditionFeatures.IsUTF8Validated
if f.L1.Kind == protoreflect.MessageKind && f.L1.EditionFeatures.IsDelimitedEncoded {
f.L1.Kind = protoreflect.GroupKind
}
}
} }
return fs, nil return fs, nil
} }
@ -151,6 +204,9 @@ func (r descsByName) initOneofsFromDescriptorProto(ods []*descriptorpb.OneofDesc
if opts := od.GetOptions(); opts != nil { if opts := od.GetOptions(); opts != nil {
opts = proto.Clone(opts).(*descriptorpb.OneofOptions) opts = proto.Clone(opts).(*descriptorpb.OneofOptions)
o.L1.Options = func() protoreflect.ProtoMessage { return opts } o.L1.Options = func() protoreflect.ProtoMessage { return opts }
if parent.Syntax() == protoreflect.Editions {
o.L1.EditionFeatures = mergeEditionFeatures(parent, opts.GetFeatures())
}
} }
} }
return os, nil return os, nil

View File

@ -276,8 +276,8 @@ func unmarshalDefault(s string, fd protoreflect.FieldDescriptor, allowUnresolvab
} else if err != nil { } else if err != nil {
return v, ev, err return v, ev, err
} }
if fd.Syntax() == protoreflect.Proto3 { if !fd.HasPresence() {
return v, ev, errors.New("cannot be specified under proto3 semantics") return v, ev, errors.New("cannot be specified with implicit field presence")
} }
if fd.Kind() == protoreflect.MessageKind || fd.Kind() == protoreflect.GroupKind || fd.Cardinality() == protoreflect.Repeated { if fd.Kind() == protoreflect.MessageKind || fd.Kind() == protoreflect.GroupKind || fd.Cardinality() == protoreflect.Repeated {
return v, ev, errors.New("cannot be specified on composite types") return v, ev, errors.New("cannot be specified on composite types")

View File

@ -107,7 +107,7 @@ func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.Desc
if isMessageSet && !flags.ProtoLegacy { if isMessageSet && !flags.ProtoLegacy {
return errors.New("message %q is a MessageSet, which is a legacy proto1 feature that is no longer supported", m.FullName()) return errors.New("message %q is a MessageSet, which is a legacy proto1 feature that is no longer supported", m.FullName())
} }
if isMessageSet && (m.Syntax() != protoreflect.Proto2 || m.Fields().Len() > 0 || m.ExtensionRanges().Len() == 0) { if isMessageSet && (m.Syntax() == protoreflect.Proto3 || m.Fields().Len() > 0 || m.ExtensionRanges().Len() == 0) {
return errors.New("message %q is an invalid proto1 MessageSet", m.FullName()) return errors.New("message %q is an invalid proto1 MessageSet", m.FullName())
} }
if m.Syntax() == protoreflect.Proto3 { if m.Syntax() == protoreflect.Proto3 {
@ -314,8 +314,8 @@ func checkValidGroup(fd protoreflect.FieldDescriptor) error {
switch { switch {
case fd.Kind() != protoreflect.GroupKind: case fd.Kind() != protoreflect.GroupKind:
return nil return nil
case fd.Syntax() != protoreflect.Proto2: case fd.Syntax() == protoreflect.Proto3:
return errors.New("invalid under proto2 semantics") return errors.New("invalid under proto3 semantics")
case md == nil || md.IsPlaceholder(): case md == nil || md.IsPlaceholder():
return errors.New("message must be resolvable") return errors.New("message must be resolvable")
case fd.FullName().Parent() != md.FullName().Parent(): case fd.FullName().Parent() != md.FullName().Parent():

View File

@ -0,0 +1,148 @@
// 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 protodesc
import (
"fmt"
"os"
"sync"
"google.golang.org/protobuf/internal/editiondefaults"
"google.golang.org/protobuf/internal/filedesc"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/descriptorpb"
gofeaturespb "google.golang.org/protobuf/types/gofeaturespb"
)
const (
SupportedEditionsMinimum = descriptorpb.Edition_EDITION_PROTO2
SupportedEditionsMaximum = descriptorpb.Edition_EDITION_2023
)
var defaults = &descriptorpb.FeatureSetDefaults{}
var defaultsCacheMu sync.Mutex
var defaultsCache = make(map[filedesc.Edition]*descriptorpb.FeatureSet)
func init() {
err := proto.Unmarshal(editiondefaults.Defaults, defaults)
if err != nil {
fmt.Fprintf(os.Stderr, "unmarshal editions defaults: %v\n", err)
os.Exit(1)
}
}
func fromEditionProto(epb descriptorpb.Edition) filedesc.Edition {
return filedesc.Edition(epb)
}
func toEditionProto(ed filedesc.Edition) descriptorpb.Edition {
switch ed {
case filedesc.EditionUnknown:
return descriptorpb.Edition_EDITION_UNKNOWN
case filedesc.EditionProto2:
return descriptorpb.Edition_EDITION_PROTO2
case filedesc.EditionProto3:
return descriptorpb.Edition_EDITION_PROTO3
case filedesc.Edition2023:
return descriptorpb.Edition_EDITION_2023
default:
panic(fmt.Sprintf("unknown value for edition: %v", ed))
}
}
func getFeatureSetFor(ed filedesc.Edition) *descriptorpb.FeatureSet {
defaultsCacheMu.Lock()
defer defaultsCacheMu.Unlock()
if def, ok := defaultsCache[ed]; ok {
return def
}
edpb := toEditionProto(ed)
if defaults.GetMinimumEdition() > edpb || defaults.GetMaximumEdition() < edpb {
// This should never happen protodesc.(FileOptions).New would fail when
// initializing the file descriptor.
// This most likely means the embedded defaults were not updated.
fmt.Fprintf(os.Stderr, "internal error: unsupported edition %v (did you forget to update the embedded defaults (i.e. the bootstrap descriptor proto)?)\n", edpb)
os.Exit(1)
}
fs := defaults.GetDefaults()[0].GetFeatures()
// Using a linear search for now.
// Editions are guaranteed to be sorted and thus we could use a binary search.
// Given that there are only a handful of editions (with one more per year)
// there is not much reason to use a binary search.
for _, def := range defaults.GetDefaults() {
if def.GetEdition() <= edpb {
fs = def.GetFeatures()
} else {
break
}
}
defaultsCache[ed] = fs
return fs
}
// mergeEditionFeatures merges the parent and child feature sets. This function
// should be used when initializing Go descriptors from descriptor protos which
// is why the parent is a filedesc.EditionsFeatures (Go representation) while
// the child is a descriptorproto.FeatureSet (protoc representation).
// Any feature set by the child overwrites what is set by the parent.
func mergeEditionFeatures(parentDesc protoreflect.Descriptor, child *descriptorpb.FeatureSet) filedesc.EditionFeatures {
var parentFS filedesc.EditionFeatures
switch p := parentDesc.(type) {
case *filedesc.File:
parentFS = p.L1.EditionFeatures
case *filedesc.Message:
parentFS = p.L1.EditionFeatures
default:
panic(fmt.Sprintf("unknown parent type %T", parentDesc))
}
if child == nil {
return parentFS
}
if fp := child.FieldPresence; fp != nil {
parentFS.IsFieldPresence = *fp == descriptorpb.FeatureSet_LEGACY_REQUIRED ||
*fp == descriptorpb.FeatureSet_EXPLICIT
parentFS.IsLegacyRequired = *fp == descriptorpb.FeatureSet_LEGACY_REQUIRED
}
if et := child.EnumType; et != nil {
parentFS.IsOpenEnum = *et == descriptorpb.FeatureSet_OPEN
}
if rfe := child.RepeatedFieldEncoding; rfe != nil {
parentFS.IsPacked = *rfe == descriptorpb.FeatureSet_PACKED
}
if utf8val := child.Utf8Validation; utf8val != nil {
parentFS.IsUTF8Validated = *utf8val == descriptorpb.FeatureSet_VERIFY
}
if me := child.MessageEncoding; me != nil {
parentFS.IsDelimitedEncoded = *me == descriptorpb.FeatureSet_DELIMITED
}
if jf := child.JsonFormat; jf != nil {
parentFS.IsJSONCompliant = *jf == descriptorpb.FeatureSet_ALLOW
}
if goFeatures, ok := proto.GetExtension(child, gofeaturespb.E_Go).(*gofeaturespb.GoFeatures); ok && goFeatures != nil {
if luje := goFeatures.LegacyUnmarshalJsonEnum; luje != nil {
parentFS.GenerateLegacyUnmarshalJSON = *luje
}
}
return parentFS
}
// initFileDescFromFeatureSet initializes editions related fields in fd based
// on fs. If fs is nil it is assumed to be an empty featureset and all fields
// will be initialized with the appropriate default. fd.L1.Edition must be set
// before calling this function.
func initFileDescFromFeatureSet(fd *filedesc.File, fs *descriptorpb.FeatureSet) {
dfs := getFeatureSetFor(fd.L1.Edition)
// initialize the featureset with the defaults
fd.L1.EditionFeatures = mergeEditionFeatures(fd, dfs)
// overwrite any options explicitly specified
fd.L1.EditionFeatures = mergeEditionFeatures(fd, fs)
}

View File

@ -16,7 +16,7 @@ import (
"google.golang.org/protobuf/types/descriptorpb" "google.golang.org/protobuf/types/descriptorpb"
) )
// ToFileDescriptorProto copies a protoreflect.FileDescriptor into a // ToFileDescriptorProto copies a [protoreflect.FileDescriptor] into a
// google.protobuf.FileDescriptorProto message. // google.protobuf.FileDescriptorProto message.
func ToFileDescriptorProto(file protoreflect.FileDescriptor) *descriptorpb.FileDescriptorProto { func ToFileDescriptorProto(file protoreflect.FileDescriptor) *descriptorpb.FileDescriptorProto {
p := &descriptorpb.FileDescriptorProto{ p := &descriptorpb.FileDescriptorProto{
@ -70,13 +70,13 @@ func ToFileDescriptorProto(file protoreflect.FileDescriptor) *descriptorpb.FileD
for i, exts := 0, file.Extensions(); i < exts.Len(); i++ { for i, exts := 0, file.Extensions(); i < exts.Len(); i++ {
p.Extension = append(p.Extension, ToFieldDescriptorProto(exts.Get(i))) p.Extension = append(p.Extension, ToFieldDescriptorProto(exts.Get(i)))
} }
if syntax := file.Syntax(); syntax != protoreflect.Proto2 { if syntax := file.Syntax(); syntax != protoreflect.Proto2 && syntax.IsValid() {
p.Syntax = proto.String(file.Syntax().String()) p.Syntax = proto.String(file.Syntax().String())
} }
return p return p
} }
// ToDescriptorProto copies a protoreflect.MessageDescriptor into a // ToDescriptorProto copies a [protoreflect.MessageDescriptor] into a
// google.protobuf.DescriptorProto message. // google.protobuf.DescriptorProto message.
func ToDescriptorProto(message protoreflect.MessageDescriptor) *descriptorpb.DescriptorProto { func ToDescriptorProto(message protoreflect.MessageDescriptor) *descriptorpb.DescriptorProto {
p := &descriptorpb.DescriptorProto{ p := &descriptorpb.DescriptorProto{
@ -119,7 +119,7 @@ func ToDescriptorProto(message protoreflect.MessageDescriptor) *descriptorpb.Des
return p return p
} }
// ToFieldDescriptorProto copies a protoreflect.FieldDescriptor into a // ToFieldDescriptorProto copies a [protoreflect.FieldDescriptor] into a
// google.protobuf.FieldDescriptorProto message. // google.protobuf.FieldDescriptorProto message.
func ToFieldDescriptorProto(field protoreflect.FieldDescriptor) *descriptorpb.FieldDescriptorProto { func ToFieldDescriptorProto(field protoreflect.FieldDescriptor) *descriptorpb.FieldDescriptorProto {
p := &descriptorpb.FieldDescriptorProto{ p := &descriptorpb.FieldDescriptorProto{
@ -168,7 +168,7 @@ func ToFieldDescriptorProto(field protoreflect.FieldDescriptor) *descriptorpb.Fi
return p return p
} }
// ToOneofDescriptorProto copies a protoreflect.OneofDescriptor into a // ToOneofDescriptorProto copies a [protoreflect.OneofDescriptor] into a
// google.protobuf.OneofDescriptorProto message. // google.protobuf.OneofDescriptorProto message.
func ToOneofDescriptorProto(oneof protoreflect.OneofDescriptor) *descriptorpb.OneofDescriptorProto { func ToOneofDescriptorProto(oneof protoreflect.OneofDescriptor) *descriptorpb.OneofDescriptorProto {
return &descriptorpb.OneofDescriptorProto{ return &descriptorpb.OneofDescriptorProto{
@ -177,7 +177,7 @@ func ToOneofDescriptorProto(oneof protoreflect.OneofDescriptor) *descriptorpb.On
} }
} }
// ToEnumDescriptorProto copies a protoreflect.EnumDescriptor into a // ToEnumDescriptorProto copies a [protoreflect.EnumDescriptor] into a
// google.protobuf.EnumDescriptorProto message. // google.protobuf.EnumDescriptorProto message.
func ToEnumDescriptorProto(enum protoreflect.EnumDescriptor) *descriptorpb.EnumDescriptorProto { func ToEnumDescriptorProto(enum protoreflect.EnumDescriptor) *descriptorpb.EnumDescriptorProto {
p := &descriptorpb.EnumDescriptorProto{ p := &descriptorpb.EnumDescriptorProto{
@ -200,7 +200,7 @@ func ToEnumDescriptorProto(enum protoreflect.EnumDescriptor) *descriptorpb.EnumD
return p return p
} }
// ToEnumValueDescriptorProto copies a protoreflect.EnumValueDescriptor into a // ToEnumValueDescriptorProto copies a [protoreflect.EnumValueDescriptor] into a
// google.protobuf.EnumValueDescriptorProto message. // google.protobuf.EnumValueDescriptorProto message.
func ToEnumValueDescriptorProto(value protoreflect.EnumValueDescriptor) *descriptorpb.EnumValueDescriptorProto { func ToEnumValueDescriptorProto(value protoreflect.EnumValueDescriptor) *descriptorpb.EnumValueDescriptorProto {
return &descriptorpb.EnumValueDescriptorProto{ return &descriptorpb.EnumValueDescriptorProto{
@ -210,7 +210,7 @@ func ToEnumValueDescriptorProto(value protoreflect.EnumValueDescriptor) *descrip
} }
} }
// ToServiceDescriptorProto copies a protoreflect.ServiceDescriptor into a // ToServiceDescriptorProto copies a [protoreflect.ServiceDescriptor] into a
// google.protobuf.ServiceDescriptorProto message. // google.protobuf.ServiceDescriptorProto message.
func ToServiceDescriptorProto(service protoreflect.ServiceDescriptor) *descriptorpb.ServiceDescriptorProto { func ToServiceDescriptorProto(service protoreflect.ServiceDescriptor) *descriptorpb.ServiceDescriptorProto {
p := &descriptorpb.ServiceDescriptorProto{ p := &descriptorpb.ServiceDescriptorProto{
@ -223,7 +223,7 @@ func ToServiceDescriptorProto(service protoreflect.ServiceDescriptor) *descripto
return p return p
} }
// ToMethodDescriptorProto copies a protoreflect.MethodDescriptor into a // ToMethodDescriptorProto copies a [protoreflect.MethodDescriptor] into a
// google.protobuf.MethodDescriptorProto message. // google.protobuf.MethodDescriptorProto message.
func ToMethodDescriptorProto(method protoreflect.MethodDescriptor) *descriptorpb.MethodDescriptorProto { func ToMethodDescriptorProto(method protoreflect.MethodDescriptor) *descriptorpb.MethodDescriptorProto {
p := &descriptorpb.MethodDescriptorProto{ p := &descriptorpb.MethodDescriptorProto{

View File

@ -10,46 +10,46 @@
// //
// # Protocol Buffer Descriptors // # Protocol Buffer Descriptors
// //
// Protobuf descriptors (e.g., EnumDescriptor or MessageDescriptor) // Protobuf descriptors (e.g., [EnumDescriptor] or [MessageDescriptor])
// are immutable objects that represent protobuf type information. // are immutable objects that represent protobuf type information.
// They are wrappers around the messages declared in descriptor.proto. // They are wrappers around the messages declared in descriptor.proto.
// Protobuf descriptors alone lack any information regarding Go types. // Protobuf descriptors alone lack any information regarding Go types.
// //
// Enums and messages generated by this module implement Enum and ProtoMessage, // Enums and messages generated by this module implement [Enum] and [ProtoMessage],
// where the Descriptor and ProtoReflect.Descriptor accessors respectively // where the Descriptor and ProtoReflect.Descriptor accessors respectively
// return the protobuf descriptor for the values. // return the protobuf descriptor for the values.
// //
// The protobuf descriptor interfaces are not meant to be implemented by // The protobuf descriptor interfaces are not meant to be implemented by
// user code since they might need to be extended in the future to support // user code since they might need to be extended in the future to support
// additions to the protobuf language. // additions to the protobuf language.
// The "google.golang.org/protobuf/reflect/protodesc" package converts between // The [google.golang.org/protobuf/reflect/protodesc] package converts between
// google.protobuf.DescriptorProto messages and protobuf descriptors. // google.protobuf.DescriptorProto messages and protobuf descriptors.
// //
// # Go Type Descriptors // # Go Type Descriptors
// //
// A type descriptor (e.g., EnumType or MessageType) is a constructor for // A type descriptor (e.g., [EnumType] or [MessageType]) is a constructor for
// a concrete Go type that represents the associated protobuf descriptor. // a concrete Go type that represents the associated protobuf descriptor.
// There is commonly a one-to-one relationship between protobuf descriptors and // There is commonly a one-to-one relationship between protobuf descriptors and
// Go type descriptors, but it can potentially be a one-to-many relationship. // Go type descriptors, but it can potentially be a one-to-many relationship.
// //
// Enums and messages generated by this module implement Enum and ProtoMessage, // Enums and messages generated by this module implement [Enum] and [ProtoMessage],
// where the Type and ProtoReflect.Type accessors respectively // where the Type and ProtoReflect.Type accessors respectively
// return the protobuf descriptor for the values. // return the protobuf descriptor for the values.
// //
// The "google.golang.org/protobuf/types/dynamicpb" package can be used to // The [google.golang.org/protobuf/types/dynamicpb] package can be used to
// create Go type descriptors from protobuf descriptors. // create Go type descriptors from protobuf descriptors.
// //
// # Value Interfaces // # Value Interfaces
// //
// The Enum and Message interfaces provide a reflective view over an // The [Enum] and [Message] interfaces provide a reflective view over an
// enum or message instance. For enums, it provides the ability to retrieve // enum or message instance. For enums, it provides the ability to retrieve
// the enum value number for any concrete enum type. For messages, it provides // the enum value number for any concrete enum type. For messages, it provides
// the ability to access or manipulate fields of the message. // the ability to access or manipulate fields of the message.
// //
// To convert a proto.Message to a protoreflect.Message, use the // To convert a [google.golang.org/protobuf/proto.Message] to a [protoreflect.Message], use the
// former's ProtoReflect method. Since the ProtoReflect method is new to the // former's ProtoReflect method. Since the ProtoReflect method is new to the
// v2 message interface, it may not be present on older message implementations. // v2 message interface, it may not be present on older message implementations.
// The "github.com/golang/protobuf/proto".MessageReflect function can be used // The [github.com/golang/protobuf/proto.MessageReflect] function can be used
// to obtain a reflective view on older messages. // to obtain a reflective view on older messages.
// //
// # Relationships // # Relationships
@ -71,12 +71,12 @@
// │ │ // │ │
// └────────────────── Type() ───────┘ // └────────────────── Type() ───────┘
// //
// • An EnumType describes a concrete Go enum type. // • An [EnumType] describes a concrete Go enum type.
// It has an EnumDescriptor and can construct an Enum instance. // It has an EnumDescriptor and can construct an Enum instance.
// //
// • An EnumDescriptor describes an abstract protobuf enum type. // • An [EnumDescriptor] describes an abstract protobuf enum type.
// //
// • An Enum is a concrete enum instance. Generated enums implement Enum. // • An [Enum] is a concrete enum instance. Generated enums implement Enum.
// //
// ┌──────────────── New() ─────────────────┐ // ┌──────────────── New() ─────────────────┐
// │ │ // │ │
@ -90,24 +90,26 @@
// │ │ // │ │
// └─────────────────── Type() ─────────┘ // └─────────────────── Type() ─────────┘
// //
// • A MessageType describes a concrete Go message type. // • A [MessageType] describes a concrete Go message type.
// It has a MessageDescriptor and can construct a Message instance. // It has a [MessageDescriptor] and can construct a [Message] instance.
// Just as how Go's reflect.Type is a reflective description of a Go type, // Just as how Go's [reflect.Type] is a reflective description of a Go type,
// a MessageType is a reflective description of a Go type for a protobuf message. // a [MessageType] is a reflective description of a Go type for a protobuf message.
// //
// • A MessageDescriptor describes an abstract protobuf message type. // • A [MessageDescriptor] describes an abstract protobuf message type.
// It has no understanding of Go types. In order to construct a MessageType // It has no understanding of Go types. In order to construct a [MessageType]
// from just a MessageDescriptor, you can consider looking up the message type // from just a [MessageDescriptor], you can consider looking up the message type
// in the global registry using protoregistry.GlobalTypes.FindMessageByName // in the global registry using the FindMessageByName method on
// or constructing a dynamic MessageType using dynamicpb.NewMessageType. // [google.golang.org/protobuf/reflect/protoregistry.GlobalTypes]
// or constructing a dynamic [MessageType] using
// [google.golang.org/protobuf/types/dynamicpb.NewMessageType].
// //
// • A Message is a reflective view over a concrete message instance. // • A [Message] is a reflective view over a concrete message instance.
// Generated messages implement ProtoMessage, which can convert to a Message. // Generated messages implement [ProtoMessage], which can convert to a [Message].
// Just as how Go's reflect.Value is a reflective view over a Go value, // Just as how Go's [reflect.Value] is a reflective view over a Go value,
// a Message is a reflective view over a concrete protobuf message instance. // a [Message] is a reflective view over a concrete protobuf message instance.
// Using Go reflection as an analogy, the ProtoReflect method is similar to // Using Go reflection as an analogy, the [ProtoMessage.ProtoReflect] method is similar to
// calling reflect.ValueOf, and the Message.Interface method is similar to // calling [reflect.ValueOf], and the [Message.Interface] method is similar to
// calling reflect.Value.Interface. // calling [reflect.Value.Interface].
// //
// ┌── TypeDescriptor() ──┐ ┌───── Descriptor() ─────┐ // ┌── TypeDescriptor() ──┐ ┌───── Descriptor() ─────┐
// │ V │ V // │ V │ V
@ -119,15 +121,15 @@
// │ │ // │ │
// └────── implements ────────┘ // └────── implements ────────┘
// //
// • An ExtensionType describes a concrete Go implementation of an extension. // • An [ExtensionType] describes a concrete Go implementation of an extension.
// It has an ExtensionTypeDescriptor and can convert to/from // It has an [ExtensionTypeDescriptor] and can convert to/from
// abstract Values and Go values. // an abstract [Value] and a Go value.
// //
// • An ExtensionTypeDescriptor is an ExtensionDescriptor // • An [ExtensionTypeDescriptor] is an [ExtensionDescriptor]
// which also has an ExtensionType. // which also has an [ExtensionType].
// //
// • An ExtensionDescriptor describes an abstract protobuf extension field and // • An [ExtensionDescriptor] describes an abstract protobuf extension field and
// may not always be an ExtensionTypeDescriptor. // may not always be an [ExtensionTypeDescriptor].
package protoreflect package protoreflect
import ( import (
@ -142,7 +144,7 @@ type doNotImplement pragma.DoNotImplement
// ProtoMessage is the top-level interface that all proto messages implement. // ProtoMessage is the top-level interface that all proto messages implement.
// This is declared in the protoreflect package to avoid a cyclic dependency; // This is declared in the protoreflect package to avoid a cyclic dependency;
// use the proto.Message type instead, which aliases this type. // use the [google.golang.org/protobuf/proto.Message] type instead, which aliases this type.
type ProtoMessage interface{ ProtoReflect() Message } type ProtoMessage interface{ ProtoReflect() Message }
// Syntax is the language version of the proto file. // Syntax is the language version of the proto file.
@ -151,8 +153,9 @@ type Syntax syntax
type syntax int8 // keep exact type opaque as the int type may change type syntax int8 // keep exact type opaque as the int type may change
const ( const (
Proto2 Syntax = 2 Proto2 Syntax = 2
Proto3 Syntax = 3 Proto3 Syntax = 3
Editions Syntax = 4
) )
// IsValid reports whether the syntax is valid. // IsValid reports whether the syntax is valid.
@ -172,6 +175,8 @@ func (s Syntax) String() string {
return "proto2" return "proto2"
case Proto3: case Proto3:
return "proto3" return "proto3"
case Editions:
return "editions"
default: default:
return fmt.Sprintf("<unknown:%d>", s) return fmt.Sprintf("<unknown:%d>", s)
} }
@ -436,7 +441,7 @@ type Names interface {
// FullName is a qualified name that uniquely identifies a proto declaration. // FullName is a qualified name that uniquely identifies a proto declaration.
// A qualified name is the concatenation of the proto package along with the // A qualified name is the concatenation of the proto package along with the
// fully-declared name (i.e., name of parent preceding the name of the child), // fully-declared name (i.e., name of parent preceding the name of the child),
// with a '.' delimiter placed between each Name. // with a '.' delimiter placed between each [Name].
// //
// This should not have any leading or trailing dots. // This should not have any leading or trailing dots.
type FullName string // e.g., "google.protobuf.Field.Kind" type FullName string // e.g., "google.protobuf.Field.Kind"
@ -480,7 +485,7 @@ func isLetterDigit(c byte) bool {
} }
// Name returns the short name, which is the last identifier segment. // Name returns the short name, which is the last identifier segment.
// A single segment FullName is the Name itself. // A single segment FullName is the [Name] itself.
func (n FullName) Name() Name { func (n FullName) Name() Name {
if i := strings.LastIndexByte(string(n), '.'); i >= 0 { if i := strings.LastIndexByte(string(n), '.'); i >= 0 {
return Name(n[i+1:]) return Name(n[i+1:])

View File

@ -35,7 +35,7 @@ func (p *SourcePath) appendFileDescriptorProto(b []byte) []byte {
b = p.appendSingularField(b, "source_code_info", (*SourcePath).appendSourceCodeInfo) b = p.appendSingularField(b, "source_code_info", (*SourcePath).appendSourceCodeInfo)
case 12: case 12:
b = p.appendSingularField(b, "syntax", nil) b = p.appendSingularField(b, "syntax", nil)
case 13: case 14:
b = p.appendSingularField(b, "edition", nil) b = p.appendSingularField(b, "edition", nil)
} }
return b return b
@ -160,8 +160,6 @@ func (p *SourcePath) appendFileOptions(b []byte) []byte {
b = p.appendSingularField(b, "java_generic_services", nil) b = p.appendSingularField(b, "java_generic_services", nil)
case 18: case 18:
b = p.appendSingularField(b, "py_generic_services", nil) b = p.appendSingularField(b, "py_generic_services", nil)
case 42:
b = p.appendSingularField(b, "php_generic_services", nil)
case 23: case 23:
b = p.appendSingularField(b, "deprecated", nil) b = p.appendSingularField(b, "deprecated", nil)
case 31: case 31:
@ -180,6 +178,8 @@ func (p *SourcePath) appendFileOptions(b []byte) []byte {
b = p.appendSingularField(b, "php_metadata_namespace", nil) b = p.appendSingularField(b, "php_metadata_namespace", nil)
case 45: case 45:
b = p.appendSingularField(b, "ruby_package", nil) b = p.appendSingularField(b, "ruby_package", nil)
case 50:
b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet)
case 999: case 999:
b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
} }
@ -240,6 +240,8 @@ func (p *SourcePath) appendMessageOptions(b []byte) []byte {
b = p.appendSingularField(b, "map_entry", nil) b = p.appendSingularField(b, "map_entry", nil)
case 11: case 11:
b = p.appendSingularField(b, "deprecated_legacy_json_field_conflicts", nil) b = p.appendSingularField(b, "deprecated_legacy_json_field_conflicts", nil)
case 12:
b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet)
case 999: case 999:
b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
} }
@ -285,6 +287,8 @@ func (p *SourcePath) appendEnumOptions(b []byte) []byte {
b = p.appendSingularField(b, "deprecated", nil) b = p.appendSingularField(b, "deprecated", nil)
case 6: case 6:
b = p.appendSingularField(b, "deprecated_legacy_json_field_conflicts", nil) b = p.appendSingularField(b, "deprecated_legacy_json_field_conflicts", nil)
case 7:
b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet)
case 999: case 999:
b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
} }
@ -330,6 +334,8 @@ func (p *SourcePath) appendServiceOptions(b []byte) []byte {
return b return b
} }
switch (*p)[0] { switch (*p)[0] {
case 34:
b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet)
case 33: case 33:
b = p.appendSingularField(b, "deprecated", nil) b = p.appendSingularField(b, "deprecated", nil)
case 999: case 999:
@ -361,14 +367,39 @@ func (p *SourcePath) appendFieldOptions(b []byte) []byte {
b = p.appendSingularField(b, "debug_redact", nil) b = p.appendSingularField(b, "debug_redact", nil)
case 17: case 17:
b = p.appendSingularField(b, "retention", nil) b = p.appendSingularField(b, "retention", nil)
case 18: case 19:
b = p.appendSingularField(b, "target", nil) b = p.appendRepeatedField(b, "targets", nil)
case 20:
b = p.appendRepeatedField(b, "edition_defaults", (*SourcePath).appendFieldOptions_EditionDefault)
case 21:
b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet)
case 999: case 999:
b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
} }
return b return b
} }
func (p *SourcePath) appendFeatureSet(b []byte) []byte {
if len(*p) == 0 {
return b
}
switch (*p)[0] {
case 1:
b = p.appendSingularField(b, "field_presence", nil)
case 2:
b = p.appendSingularField(b, "enum_type", nil)
case 3:
b = p.appendSingularField(b, "repeated_field_encoding", nil)
case 4:
b = p.appendSingularField(b, "utf8_validation", nil)
case 5:
b = p.appendSingularField(b, "message_encoding", nil)
case 6:
b = p.appendSingularField(b, "json_format", nil)
}
return b
}
func (p *SourcePath) appendUninterpretedOption(b []byte) []byte { func (p *SourcePath) appendUninterpretedOption(b []byte) []byte {
if len(*p) == 0 { if len(*p) == 0 {
return b return b
@ -418,6 +449,12 @@ func (p *SourcePath) appendExtensionRangeOptions(b []byte) []byte {
switch (*p)[0] { switch (*p)[0] {
case 999: case 999:
b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
case 2:
b = p.appendRepeatedField(b, "declaration", (*SourcePath).appendExtensionRangeOptions_Declaration)
case 50:
b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet)
case 3:
b = p.appendSingularField(b, "verification", nil)
} }
return b return b
} }
@ -427,6 +464,8 @@ func (p *SourcePath) appendOneofOptions(b []byte) []byte {
return b return b
} }
switch (*p)[0] { switch (*p)[0] {
case 1:
b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet)
case 999: case 999:
b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
} }
@ -440,6 +479,10 @@ func (p *SourcePath) appendEnumValueOptions(b []byte) []byte {
switch (*p)[0] { switch (*p)[0] {
case 1: case 1:
b = p.appendSingularField(b, "deprecated", nil) b = p.appendSingularField(b, "deprecated", nil)
case 2:
b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet)
case 3:
b = p.appendSingularField(b, "debug_redact", nil)
case 999: case 999:
b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
} }
@ -455,12 +498,27 @@ func (p *SourcePath) appendMethodOptions(b []byte) []byte {
b = p.appendSingularField(b, "deprecated", nil) b = p.appendSingularField(b, "deprecated", nil)
case 34: case 34:
b = p.appendSingularField(b, "idempotency_level", nil) b = p.appendSingularField(b, "idempotency_level", nil)
case 35:
b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet)
case 999: case 999:
b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
} }
return b return b
} }
func (p *SourcePath) appendFieldOptions_EditionDefault(b []byte) []byte {
if len(*p) == 0 {
return b
}
switch (*p)[0] {
case 3:
b = p.appendSingularField(b, "edition", nil)
case 2:
b = p.appendSingularField(b, "value", nil)
}
return b
}
func (p *SourcePath) appendUninterpretedOption_NamePart(b []byte) []byte { func (p *SourcePath) appendUninterpretedOption_NamePart(b []byte) []byte {
if len(*p) == 0 { if len(*p) == 0 {
return b return b
@ -473,3 +531,22 @@ func (p *SourcePath) appendUninterpretedOption_NamePart(b []byte) []byte {
} }
return b return b
} }
func (p *SourcePath) appendExtensionRangeOptions_Declaration(b []byte) []byte {
if len(*p) == 0 {
return b
}
switch (*p)[0] {
case 1:
b = p.appendSingularField(b, "number", nil)
case 2:
b = p.appendSingularField(b, "full_name", nil)
case 3:
b = p.appendSingularField(b, "type", nil)
case 5:
b = p.appendSingularField(b, "reserved", nil)
case 6:
b = p.appendSingularField(b, "repeated", nil)
}
return b
}

View File

@ -12,7 +12,7 @@ package protoreflect
// exactly identical. However, it is possible for the same semantically // exactly identical. However, it is possible for the same semantically
// identical proto type to be represented by multiple type descriptors. // identical proto type to be represented by multiple type descriptors.
// //
// For example, suppose we have t1 and t2 which are both MessageDescriptors. // For example, suppose we have t1 and t2 which are both an [MessageDescriptor].
// If t1 == t2, then the types are definitely equal and all accessors return // If t1 == t2, then the types are definitely equal and all accessors return
// the same information. However, if t1 != t2, then it is still possible that // the same information. However, if t1 != t2, then it is still possible that
// they still represent the same proto type (e.g., t1.FullName == t2.FullName). // they still represent the same proto type (e.g., t1.FullName == t2.FullName).
@ -115,7 +115,7 @@ type Descriptor interface {
// corresponds with the google.protobuf.FileDescriptorProto message. // corresponds with the google.protobuf.FileDescriptorProto message.
// //
// Top-level declarations: // Top-level declarations:
// EnumDescriptor, MessageDescriptor, FieldDescriptor, and/or ServiceDescriptor. // [EnumDescriptor], [MessageDescriptor], [FieldDescriptor], and/or [ServiceDescriptor].
type FileDescriptor interface { type FileDescriptor interface {
Descriptor // Descriptor.FullName is identical to Package Descriptor // Descriptor.FullName is identical to Package
@ -180,8 +180,8 @@ type FileImport struct {
// corresponds with the google.protobuf.DescriptorProto message. // corresponds with the google.protobuf.DescriptorProto message.
// //
// Nested declarations: // Nested declarations:
// FieldDescriptor, OneofDescriptor, FieldDescriptor, EnumDescriptor, // [FieldDescriptor], [OneofDescriptor], [FieldDescriptor], [EnumDescriptor],
// and/or MessageDescriptor. // and/or [MessageDescriptor].
type MessageDescriptor interface { type MessageDescriptor interface {
Descriptor Descriptor
@ -214,7 +214,7 @@ type MessageDescriptor interface {
ExtensionRanges() FieldRanges ExtensionRanges() FieldRanges
// ExtensionRangeOptions returns the ith extension range options. // ExtensionRangeOptions returns the ith extension range options.
// //
// To avoid a dependency cycle, this method returns a proto.Message value, // To avoid a dependency cycle, this method returns a proto.Message] value,
// which always contains a google.protobuf.ExtensionRangeOptions message. // which always contains a google.protobuf.ExtensionRangeOptions message.
// This method returns a typed nil-pointer if no options are present. // This method returns a typed nil-pointer if no options are present.
// The caller must import the descriptorpb package to use this. // The caller must import the descriptorpb package to use this.
@ -231,9 +231,9 @@ type MessageDescriptor interface {
} }
type isMessageDescriptor interface{ ProtoType(MessageDescriptor) } type isMessageDescriptor interface{ ProtoType(MessageDescriptor) }
// MessageType encapsulates a MessageDescriptor with a concrete Go implementation. // MessageType encapsulates a [MessageDescriptor] with a concrete Go implementation.
// It is recommended that implementations of this interface also implement the // It is recommended that implementations of this interface also implement the
// MessageFieldTypes interface. // [MessageFieldTypes] interface.
type MessageType interface { type MessageType interface {
// New returns a newly allocated empty message. // New returns a newly allocated empty message.
// It may return nil for synthetic messages representing a map entry. // It may return nil for synthetic messages representing a map entry.
@ -249,19 +249,19 @@ type MessageType interface {
Descriptor() MessageDescriptor Descriptor() MessageDescriptor
} }
// MessageFieldTypes extends a MessageType by providing type information // MessageFieldTypes extends a [MessageType] by providing type information
// regarding enums and messages referenced by the message fields. // regarding enums and messages referenced by the message fields.
type MessageFieldTypes interface { type MessageFieldTypes interface {
MessageType MessageType
// Enum returns the EnumType for the ith field in Descriptor.Fields. // Enum returns the EnumType for the ith field in MessageDescriptor.Fields.
// It returns nil if the ith field is not an enum kind. // It returns nil if the ith field is not an enum kind.
// It panics if out of bounds. // It panics if out of bounds.
// //
// Invariant: mt.Enum(i).Descriptor() == mt.Descriptor().Fields(i).Enum() // Invariant: mt.Enum(i).Descriptor() == mt.Descriptor().Fields(i).Enum()
Enum(i int) EnumType Enum(i int) EnumType
// Message returns the MessageType for the ith field in Descriptor.Fields. // Message returns the MessageType for the ith field in MessageDescriptor.Fields.
// It returns nil if the ith field is not a message or group kind. // It returns nil if the ith field is not a message or group kind.
// It panics if out of bounds. // It panics if out of bounds.
// //
@ -286,8 +286,8 @@ type MessageDescriptors interface {
// corresponds with the google.protobuf.FieldDescriptorProto message. // corresponds with the google.protobuf.FieldDescriptorProto message.
// //
// It is used for both normal fields defined within the parent message // It is used for both normal fields defined within the parent message
// (e.g., MessageDescriptor.Fields) and fields that extend some remote message // (e.g., [MessageDescriptor.Fields]) and fields that extend some remote message
// (e.g., FileDescriptor.Extensions or MessageDescriptor.Extensions). // (e.g., [FileDescriptor.Extensions] or [MessageDescriptor.Extensions]).
type FieldDescriptor interface { type FieldDescriptor interface {
Descriptor Descriptor
@ -344,7 +344,7 @@ type FieldDescriptor interface {
// IsMap reports whether this field represents a map, // IsMap reports whether this field represents a map,
// where the value type for the associated field is a Map. // where the value type for the associated field is a Map.
// It is equivalent to checking whether Cardinality is Repeated, // It is equivalent to checking whether Cardinality is Repeated,
// that the Kind is MessageKind, and that Message.IsMapEntry reports true. // that the Kind is MessageKind, and that MessageDescriptor.IsMapEntry reports true.
IsMap() bool IsMap() bool
// MapKey returns the field descriptor for the key in the map entry. // MapKey returns the field descriptor for the key in the map entry.
@ -419,7 +419,7 @@ type OneofDescriptor interface {
// IsSynthetic reports whether this is a synthetic oneof created to support // IsSynthetic reports whether this is a synthetic oneof created to support
// proto3 optional semantics. If true, Fields contains exactly one field // proto3 optional semantics. If true, Fields contains exactly one field
// with HasOptionalKeyword specified. // with FieldDescriptor.HasOptionalKeyword specified.
IsSynthetic() bool IsSynthetic() bool
// Fields is a list of fields belonging to this oneof. // Fields is a list of fields belonging to this oneof.
@ -442,10 +442,10 @@ type OneofDescriptors interface {
doNotImplement doNotImplement
} }
// ExtensionDescriptor is an alias of FieldDescriptor for documentation. // ExtensionDescriptor is an alias of [FieldDescriptor] for documentation.
type ExtensionDescriptor = FieldDescriptor type ExtensionDescriptor = FieldDescriptor
// ExtensionTypeDescriptor is an ExtensionDescriptor with an associated ExtensionType. // ExtensionTypeDescriptor is an [ExtensionDescriptor] with an associated [ExtensionType].
type ExtensionTypeDescriptor interface { type ExtensionTypeDescriptor interface {
ExtensionDescriptor ExtensionDescriptor
@ -470,12 +470,12 @@ type ExtensionDescriptors interface {
doNotImplement doNotImplement
} }
// ExtensionType encapsulates an ExtensionDescriptor with a concrete // ExtensionType encapsulates an [ExtensionDescriptor] with a concrete
// Go implementation. The nested field descriptor must be for a extension field. // Go implementation. The nested field descriptor must be for a extension field.
// //
// While a normal field is a member of the parent message that it is declared // While a normal field is a member of the parent message that it is declared
// within (see Descriptor.Parent), an extension field is a member of some other // within (see [Descriptor.Parent]), an extension field is a member of some other
// target message (see ExtensionDescriptor.Extendee) and may have no // target message (see [FieldDescriptor.ContainingMessage]) and may have no
// relationship with the parent. However, the full name of an extension field is // relationship with the parent. However, the full name of an extension field is
// relative to the parent that it is declared within. // relative to the parent that it is declared within.
// //
@ -532,7 +532,7 @@ type ExtensionType interface {
// corresponds with the google.protobuf.EnumDescriptorProto message. // corresponds with the google.protobuf.EnumDescriptorProto message.
// //
// Nested declarations: // Nested declarations:
// EnumValueDescriptor. // [EnumValueDescriptor].
type EnumDescriptor interface { type EnumDescriptor interface {
Descriptor Descriptor
@ -548,7 +548,7 @@ type EnumDescriptor interface {
} }
type isEnumDescriptor interface{ ProtoType(EnumDescriptor) } type isEnumDescriptor interface{ ProtoType(EnumDescriptor) }
// EnumType encapsulates an EnumDescriptor with a concrete Go implementation. // EnumType encapsulates an [EnumDescriptor] with a concrete Go implementation.
type EnumType interface { type EnumType interface {
// New returns an instance of this enum type with its value set to n. // New returns an instance of this enum type with its value set to n.
New(n EnumNumber) Enum New(n EnumNumber) Enum
@ -610,7 +610,7 @@ type EnumValueDescriptors interface {
// ServiceDescriptor describes a service and // ServiceDescriptor describes a service and
// corresponds with the google.protobuf.ServiceDescriptorProto message. // corresponds with the google.protobuf.ServiceDescriptorProto message.
// //
// Nested declarations: MethodDescriptor. // Nested declarations: [MethodDescriptor].
type ServiceDescriptor interface { type ServiceDescriptor interface {
Descriptor Descriptor

View File

@ -27,16 +27,16 @@ type Enum interface {
// Message is a reflective interface for a concrete message value, // Message is a reflective interface for a concrete message value,
// encapsulating both type and value information for the message. // encapsulating both type and value information for the message.
// //
// Accessor/mutators for individual fields are keyed by FieldDescriptor. // Accessor/mutators for individual fields are keyed by [FieldDescriptor].
// For non-extension fields, the descriptor must exactly match the // For non-extension fields, the descriptor must exactly match the
// field known by the parent message. // field known by the parent message.
// For extension fields, the descriptor must implement ExtensionTypeDescriptor, // For extension fields, the descriptor must implement [ExtensionTypeDescriptor],
// extend the parent message (i.e., have the same message FullName), and // extend the parent message (i.e., have the same message [FullName]), and
// be within the parent's extension range. // be within the parent's extension range.
// //
// Each field Value can be a scalar or a composite type (Message, List, or Map). // Each field [Value] can be a scalar or a composite type ([Message], [List], or [Map]).
// See Value for the Go types associated with a FieldDescriptor. // See [Value] for the Go types associated with a [FieldDescriptor].
// Providing a Value that is invalid or of an incorrect type panics. // Providing a [Value] that is invalid or of an incorrect type panics.
type Message interface { type Message interface {
// Descriptor returns message descriptor, which contains only the protobuf // Descriptor returns message descriptor, which contains only the protobuf
// type information for the message. // type information for the message.
@ -152,7 +152,7 @@ type Message interface {
// This method may return nil. // This method may return nil.
// //
// The returned methods type is identical to // The returned methods type is identical to
// "google.golang.org/protobuf/runtime/protoiface".Methods. // google.golang.org/protobuf/runtime/protoiface.Methods.
// Consult the protoiface package documentation for details. // Consult the protoiface package documentation for details.
ProtoMethods() *methods ProtoMethods() *methods
} }
@ -175,8 +175,8 @@ func (b RawFields) IsValid() bool {
} }
// List is a zero-indexed, ordered list. // List is a zero-indexed, ordered list.
// The element Value type is determined by FieldDescriptor.Kind. // The element [Value] type is determined by [FieldDescriptor.Kind].
// Providing a Value that is invalid or of an incorrect type panics. // Providing a [Value] that is invalid or of an incorrect type panics.
type List interface { type List interface {
// Len reports the number of entries in the List. // Len reports the number of entries in the List.
// Get, Set, and Truncate panic with out of bound indexes. // Get, Set, and Truncate panic with out of bound indexes.
@ -226,9 +226,9 @@ type List interface {
} }
// Map is an unordered, associative map. // Map is an unordered, associative map.
// The entry MapKey type is determined by FieldDescriptor.MapKey.Kind. // The entry [MapKey] type is determined by [FieldDescriptor.MapKey].Kind.
// The entry Value type is determined by FieldDescriptor.MapValue.Kind. // The entry [Value] type is determined by [FieldDescriptor.MapValue].Kind.
// Providing a MapKey or Value that is invalid or of an incorrect type panics. // Providing a [MapKey] or [Value] that is invalid or of an incorrect type panics.
type Map interface { type Map interface {
// Len reports the number of elements in the map. // Len reports the number of elements in the map.
Len() int Len() int

View File

@ -24,19 +24,19 @@ import (
// Unlike the == operator, a NaN is equal to another NaN. // Unlike the == operator, a NaN is equal to another NaN.
// //
// - Enums are equal if they contain the same number. // - Enums are equal if they contain the same number.
// Since Value does not contain an enum descriptor, // Since [Value] does not contain an enum descriptor,
// enum values do not consider the type of the enum. // enum values do not consider the type of the enum.
// //
// - Other scalar values are equal if they contain the same value. // - Other scalar values are equal if they contain the same value.
// //
// - Message values are equal if they belong to the same message descriptor, // - [Message] values are equal if they belong to the same message descriptor,
// have the same set of populated known and extension field values, // have the same set of populated known and extension field values,
// and the same set of unknown fields values. // and the same set of unknown fields values.
// //
// - Lists are equal if they are the same length and // - [List] values are equal if they are the same length and
// each corresponding element is equal. // each corresponding element is equal.
// //
// - Maps are equal if they have the same set of keys and // - [Map] values are equal if they have the same set of keys and
// the corresponding value for each key is equal. // the corresponding value for each key is equal.
func (v1 Value) Equal(v2 Value) bool { func (v1 Value) Equal(v2 Value) bool {
return equalValue(v1, v2) return equalValue(v1, v2)

View File

@ -11,7 +11,7 @@ import (
// Value is a union where only one Go type may be set at a time. // Value is a union where only one Go type may be set at a time.
// The Value is used to represent all possible values a field may take. // The Value is used to represent all possible values a field may take.
// The following shows which Go type is used to represent each proto Kind: // The following shows which Go type is used to represent each proto [Kind]:
// //
// ╔════════════╤═════════════════════════════════════╗ // ╔════════════╤═════════════════════════════════════╗
// ║ Go type │ Protobuf kind ║ // ║ Go type │ Protobuf kind ║
@ -31,22 +31,22 @@ import (
// //
// Multiple protobuf Kinds may be represented by a single Go type if the type // Multiple protobuf Kinds may be represented by a single Go type if the type
// can losslessly represent the information for the proto kind. For example, // can losslessly represent the information for the proto kind. For example,
// Int64Kind, Sint64Kind, and Sfixed64Kind are all represented by int64, // [Int64Kind], [Sint64Kind], and [Sfixed64Kind] are all represented by int64,
// but use different integer encoding methods. // but use different integer encoding methods.
// //
// The List or Map types are used if the field cardinality is repeated. // The [List] or [Map] types are used if the field cardinality is repeated.
// A field is a List if FieldDescriptor.IsList reports true. // A field is a [List] if [FieldDescriptor.IsList] reports true.
// A field is a Map if FieldDescriptor.IsMap reports true. // A field is a [Map] if [FieldDescriptor.IsMap] reports true.
// //
// Converting to/from a Value and a concrete Go value panics on type mismatch. // Converting to/from a Value and a concrete Go value panics on type mismatch.
// For example, ValueOf("hello").Int() panics because this attempts to // For example, [ValueOf]("hello").Int() panics because this attempts to
// retrieve an int64 from a string. // retrieve an int64 from a string.
// //
// List, Map, and Message Values are called "composite" values. // [List], [Map], and [Message] Values are called "composite" values.
// //
// A composite Value may alias (reference) memory at some location, // A composite Value may alias (reference) memory at some location,
// such that changes to the Value updates the that location. // such that changes to the Value updates the that location.
// A composite value acquired with a Mutable method, such as Message.Mutable, // A composite value acquired with a Mutable method, such as [Message.Mutable],
// always references the source object. // always references the source object.
// //
// For example: // For example:
@ -65,7 +65,7 @@ import (
// // appending to the List here may or may not modify the message. // // appending to the List here may or may not modify the message.
// list.Append(protoreflect.ValueOfInt32(0)) // list.Append(protoreflect.ValueOfInt32(0))
// //
// Some operations, such as Message.Get, may return an "empty, read-only" // Some operations, such as [Message.Get], may return an "empty, read-only"
// composite Value. Modifying an empty, read-only value panics. // composite Value. Modifying an empty, read-only value panics.
type Value value type Value value
@ -306,7 +306,7 @@ func (v Value) Float() float64 {
} }
} }
// String returns v as a string. Since this method implements fmt.Stringer, // String returns v as a string. Since this method implements [fmt.Stringer],
// this returns the formatted string value for any non-string type. // this returns the formatted string value for any non-string type.
func (v Value) String() string { func (v Value) String() string {
switch v.typ { switch v.typ {
@ -327,7 +327,7 @@ func (v Value) Bytes() []byte {
} }
} }
// Enum returns v as a EnumNumber and panics if the type is not a EnumNumber. // Enum returns v as a [EnumNumber] and panics if the type is not a [EnumNumber].
func (v Value) Enum() EnumNumber { func (v Value) Enum() EnumNumber {
switch v.typ { switch v.typ {
case enumType: case enumType:
@ -337,7 +337,7 @@ func (v Value) Enum() EnumNumber {
} }
} }
// Message returns v as a Message and panics if the type is not a Message. // Message returns v as a [Message] and panics if the type is not a [Message].
func (v Value) Message() Message { func (v Value) Message() Message {
switch vi := v.getIface().(type) { switch vi := v.getIface().(type) {
case Message: case Message:
@ -347,7 +347,7 @@ func (v Value) Message() Message {
} }
} }
// List returns v as a List and panics if the type is not a List. // List returns v as a [List] and panics if the type is not a [List].
func (v Value) List() List { func (v Value) List() List {
switch vi := v.getIface().(type) { switch vi := v.getIface().(type) {
case List: case List:
@ -357,7 +357,7 @@ func (v Value) List() List {
} }
} }
// Map returns v as a Map and panics if the type is not a Map. // Map returns v as a [Map] and panics if the type is not a [Map].
func (v Value) Map() Map { func (v Value) Map() Map {
switch vi := v.getIface().(type) { switch vi := v.getIface().(type) {
case Map: case Map:
@ -367,7 +367,7 @@ func (v Value) Map() Map {
} }
} }
// MapKey returns v as a MapKey and panics for invalid MapKey types. // MapKey returns v as a [MapKey] and panics for invalid [MapKey] types.
func (v Value) MapKey() MapKey { func (v Value) MapKey() MapKey {
switch v.typ { switch v.typ {
case boolType, int32Type, int64Type, uint32Type, uint64Type, stringType: case boolType, int32Type, int64Type, uint32Type, uint64Type, stringType:
@ -378,8 +378,8 @@ func (v Value) MapKey() MapKey {
} }
// MapKey is used to index maps, where the Go type of the MapKey must match // MapKey is used to index maps, where the Go type of the MapKey must match
// the specified key Kind (see MessageDescriptor.IsMapEntry). // the specified key [Kind] (see [MessageDescriptor.IsMapEntry]).
// The following shows what Go type is used to represent each proto Kind: // The following shows what Go type is used to represent each proto [Kind]:
// //
// ╔═════════╤═════════════════════════════════════╗ // ╔═════════╤═════════════════════════════════════╗
// ║ Go type │ Protobuf kind ║ // ║ Go type │ Protobuf kind ║
@ -392,13 +392,13 @@ func (v Value) MapKey() MapKey {
// ║ string │ StringKind ║ // ║ string │ StringKind ║
// ╚═════════╧═════════════════════════════════════╝ // ╚═════════╧═════════════════════════════════════╝
// //
// A MapKey is constructed and accessed through a Value: // A MapKey is constructed and accessed through a [Value]:
// //
// k := ValueOf("hash").MapKey() // convert string to MapKey // k := ValueOf("hash").MapKey() // convert string to MapKey
// s := k.String() // convert MapKey to string // s := k.String() // convert MapKey to string
// //
// The MapKey is a strict subset of valid types used in Value; // The MapKey is a strict subset of valid types used in [Value];
// converting a Value to a MapKey with an invalid type panics. // converting a [Value] to a MapKey with an invalid type panics.
type MapKey value type MapKey value
// IsValid reports whether k is populated with a value. // IsValid reports whether k is populated with a value.
@ -426,13 +426,13 @@ func (k MapKey) Uint() uint64 {
return Value(k).Uint() return Value(k).Uint()
} }
// String returns k as a string. Since this method implements fmt.Stringer, // String returns k as a string. Since this method implements [fmt.Stringer],
// this returns the formatted string value for any non-string type. // this returns the formatted string value for any non-string type.
func (k MapKey) String() string { func (k MapKey) String() string {
return Value(k).String() return Value(k).String()
} }
// Value returns k as a Value. // Value returns k as a [Value].
func (k MapKey) Value() Value { func (k MapKey) Value() Value {
return Value(k) return Value(k)
} }

View File

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !purego && !appengine //go:build !purego && !appengine && !go1.21
// +build !purego,!appengine // +build !purego,!appengine,!go1.21
package protoreflect package protoreflect

View File

@ -0,0 +1,87 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !purego && !appengine && go1.21
// +build !purego,!appengine,go1.21
package protoreflect
import (
"unsafe"
"google.golang.org/protobuf/internal/pragma"
)
type (
ifaceHeader struct {
_ [0]interface{} // if interfaces have greater alignment than unsafe.Pointer, this will enforce it.
Type unsafe.Pointer
Data unsafe.Pointer
}
)
var (
nilType = typeOf(nil)
boolType = typeOf(*new(bool))
int32Type = typeOf(*new(int32))
int64Type = typeOf(*new(int64))
uint32Type = typeOf(*new(uint32))
uint64Type = typeOf(*new(uint64))
float32Type = typeOf(*new(float32))
float64Type = typeOf(*new(float64))
stringType = typeOf(*new(string))
bytesType = typeOf(*new([]byte))
enumType = typeOf(*new(EnumNumber))
)
// typeOf returns a pointer to the Go type information.
// The pointer is comparable and equal if and only if the types are identical.
func typeOf(t interface{}) unsafe.Pointer {
return (*ifaceHeader)(unsafe.Pointer(&t)).Type
}
// value is a union where only one type can be represented at a time.
// The struct is 24B large on 64-bit systems and requires the minimum storage
// necessary to represent each possible type.
//
// The Go GC needs to be able to scan variables containing pointers.
// As such, pointers and non-pointers cannot be intermixed.
type value struct {
pragma.DoNotCompare // 0B
// typ stores the type of the value as a pointer to the Go type.
typ unsafe.Pointer // 8B
// ptr stores the data pointer for a String, Bytes, or interface value.
ptr unsafe.Pointer // 8B
// num stores a Bool, Int32, Int64, Uint32, Uint64, Float32, Float64, or
// Enum value as a raw uint64.
//
// It is also used to store the length of a String or Bytes value;
// the capacity is ignored.
num uint64 // 8B
}
func valueOfString(v string) Value {
return Value{typ: stringType, ptr: unsafe.Pointer(unsafe.StringData(v)), num: uint64(len(v))}
}
func valueOfBytes(v []byte) Value {
return Value{typ: bytesType, ptr: unsafe.Pointer(unsafe.SliceData(v)), num: uint64(len(v))}
}
func valueOfIface(v interface{}) Value {
p := (*ifaceHeader)(unsafe.Pointer(&v))
return Value{typ: p.Type, ptr: p.Data}
}
func (v Value) getString() string {
return unsafe.String((*byte)(v.ptr), v.num)
}
func (v Value) getBytes() []byte {
return unsafe.Slice((*byte)(v.ptr), v.num)
}
func (v Value) getIface() (x interface{}) {
*(*ifaceHeader)(unsafe.Pointer(&x)) = ifaceHeader{Type: v.typ, Data: v.ptr}
return x
}

Some files were not shown because too many files have changed in this diff Show More